STM32CubeMX学习笔记(22)——CRC接口使用

发布者:WhisperingSoul最新更新时间:2025-02-14 来源: jianshu关键字:STM32CubeMX  CRC 手机看文章 扫描二维码
随时随地手机看文章

一、CRC简介

CRC(Cyclic Redundancy Check),即循环冗余校验,是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。

在数据传输过程中,无论传输系统的设计再怎么完美,差错总会存在,这种差错可能会导致在链路上传输的一个或者多个帧被破坏(出现比特差错,0变为1,或者1变为0),从而接受方接收到错误的数据。为尽量提高接受方收到数据的正确率,在接收方接收数据之前需要对数据进行差错检测,当且仅当检测的结果为正确时接收方才真正收下数据。检测的方式有多种,常见的有奇偶校验、因特网校验和循环冗余校验等。循环冗余校验是一种用于校验通信链路上数字传输准确性的计算方法(通过某种数学运算来建立数据位和校验位的约定关系的 )。发送方计算机使用某公式计算出被传送数据所含信息的一个值,并将此值 附在被传送数据后,接收方计算机则对同一数据进行 相同的计算,应该得到相同的结果。如果这两个 CRC结果不一致,则说明发送中出现了差错,接收方计算机可要求发送方计算机重新发送该数据。

1.1 基本原理

CRC检验原理实际上就是在一个p位二进制数据序列之后附加一个r位二进制检验码(序列),从而构成一个总长为n=p+r位的二进制序列;附加在数据序列之后的这个检验码与数据序列的内容之间存在着某种特定的关系。如果因干扰等原因使数据序列中的某一位或某些位发生错误,这种特定关系就会被破坏。因此,通过检查这一关系,就可以实现对数据正确性的检验。

几个基本概念

  1. 帧检验序列FCS(Frame Check Sequence):为了进行差错检验而添加的冗余码。

  2. 多项式模2运行:实际上是按位异或(Exclusive OR)运算,即相同为0,相异为1,也就是不考虑进位、借位的二进制加减运算。如:10011011 + 11001010 = 01010001。

  3. 生成多项式(generator polynomial):当进行CRC检验时,发送方与接收方需要事先约定一个除数,即生成多项式,一般记作G(x)。生成多项式的最高位与最低位必须是1。常用的CRC码的生成多项式有:



    每一个生成多项式都可以与一个代码相对应,如CRC8对应代码:100110001。

1.2 CRC检验码的计算

设信息字段为K位,校验字段为R位,则码字长度为N(N=K+R)。设双方事先约定了一个R次多项式g(x),则CRC码:

V ( x ) = A ( x ) g ( x ) = x R m ( x ) + r ( x )
其中: m(x)为K次信息多项式, r(x)为R-1次校验多项式。

这里r(x)对应的代码即为冗余码,加在原信息字段后即形成CRC码。

r(x)的计算方法为:在K位信息字段的后面添加R个0,再除以g(x)对应的代码序列,得到的余数即为r(x)对应的代码(应为R-1位;若不足,而在高位补0)。

计算示例:
设需要发送的信息为M = 1010001101,产生多项式对应的代码为P = 110101,R=5。在M后加5个0,然后对P做模2除法运算,得余数r(x)对应的代码:01110。故实际需要发送的数据是101000110101110。

1.3 错误检测

当接收方收到数据后,用收到的数据对P(事先约定的)进行模2除法,若余数为0,则认为数据传输无差错;若余数不为0,则认为数据传输出现了错误,由于不知道错误发生在什么地方,因而不能进行自动纠正,一般的做法是丢弃接收的数据。

【注】几点说明:

  1. CRC是一种常用的检错码,并不能用于自动纠错。

  2. 只要经过严格的挑选,并使用位数足够多的除数 P,那么出现检测不到的差错的概率就很小很小。

  3. 仅用循环冗余检验 CRC 差错检测技术只能做到无差错接受(只是非常近似的认为是无差错的),并不能保证可靠传输。

1.4 STM32中的CRC

所有的STM32芯片都内置了一个硬件的CRC计算模块,可以很方便地应用到需要进行通信的程序中,这个CRC计算模块使用常见的、在以太网中使用的计算多项式:



写成16进制就是:0x04C11DB7

使用这个内置CRC模块的方法非常简单,既首先复位CRC模块(设置CRC_CR=0x01),这个操作把CRC计算的余数初始化为0xFFFFFFFF;然后把要计算的数据按每32位分割为一组数据字,并逐个地把这组数据字写入CRC_DR寄存器(既下图中的绿色框),写完所有的数据字后,就可以从CRC_DR寄存器(既下图中的兰色框)读出计算的结果。



下面是用C语言描述的这个计算模块的算法,大家可以把它放在通信的另一端,对通信的正确性进行验证:

DWORD dwPolynomial = 0x04c11db7;DWORD cal_crc(DWORD *ptr, int len){
    DWORD    xbit;
    DWORD    data;
    DWORD    CRC = 0xFFFFFFFF;    // init
    while (len--) {
        xbit = 1 << 31;

        data = *ptr++;
        for (int bits = 0; bits < 32; bits++) {
            if (CRC & 0x80000000) {
                CRC <<= 1;
                CRC ^= dwPolynomial;
            }
            else
                CRC <<= 1;
            if (data & xbit)
                CRC ^= dwPolynomial;

            xbit >>= 1;
        }
    }
    return CRC;}

有几点需要说明:

  1. 上述算法中变量CRC,在每次循环结束包含了计算的余数,它始终是向左移位(既从最低位向最高位移动),溢出的数据位被丢弃。

  2. 输入的数据始终是以32位为单位,如果原始数据少于32位,需要在低位补0,当然也可以高位补0。

  3. 假定输入的DWORD数组中每个分量是按小端存储。

  4. 输入数据是按照最高位最先计算,最低位最后计算的顺序进行。

例如:
如果输入0x44434241,内存中按字节存放的顺序是:0x41, 0x42, 0x43, 0x44。计算的结果是:0xCF534AE1
如果输入0x41424344,内存中按字节存放的顺序是:0x44, 0x43, 0x42, 0x41。计算的结果是:0xABCF9A63

二、新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”


2. 选择 MCU 和封装


3. 配置时钟
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)


选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置


4. 配置调试模式
非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire


三、CRC

3.1 参数配置

在 Computing 中选择 CRC 设置,并勾选 Activated 激活


3.2 生成代码

输入项目名和项目路径


选择应用的 IDE 开发环境 MDK-ARM V5


每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。


点击 GENERATE CODE 生成代码

四、库函数

HAL_CRC_Calculate() 函数在每次计算时,对DR寄存器进行了复位,而HAL_CRC_Accumulate() 函数没有,因此在使用时就要根据需求来选择相应的函数了。

/**

  * @brief  Compute the 32-bit CRC value of a 32-bit data buffer

  *         starting with the previously computed CRC as initialization value.

  * @param  hcrc CRC handle

  * @param  pBuffer pointer to the input data buffer.

  * @param  BufferLength input data buffer length (number of uint32_t words).

  * @retval uint32_t CRC (returned value LSBs for CRC shorter than 32 bits)

  */

uint32_t HAL_CRC_Accumulate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[], uint32_t BufferLength)

{

  uint32_t index;      /* CRC input data buffer index */

  uint32_t temp = 0U;  /* CRC output (read from hcrc->Instance->DR register) */


  /* Change CRC peripheral state */

  hcrc->State = HAL_CRC_STATE_BUSY;


  /* Enter Data to the CRC calculator */

  for (index = 0U; index < BufferLength; index++)

  {

    hcrc->Instance->DR = pBuffer[index];

  }

  temp = hcrc->Instance->DR;


  /* Change CRC peripheral state */

  hcrc->State = HAL_CRC_STATE_READY;


  /* Return the CRC computed value */

  return temp;

}


/**

  * @brief  Compute the 32-bit CRC value of a 32-bit data buffer

  *         starting with hcrc->Instance->INIT as initialization value.

  * @param  hcrc CRC handle

  * @param  pBuffer pointer to the input data buffer.

  * @param  BufferLength input data buffer length (number of uint32_t words).

  * @retval uint32_t CRC (returned value LSBs for CRC shorter than 32 bits)

  */

uint32_t HAL_CRC_Calculate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[], uint32_t BufferLength)

{

  uint32_t index;      /* CRC input data buffer index */

  uint32_t temp = 0U;  /* CRC output (read from hcrc->Instance->DR register) */


  /* Change CRC peripheral state */

  hcrc->State = HAL_CRC_STATE_BUSY;


  /* Reset CRC Calculation Unit (hcrc->Instance->INIT is

  *  written in hcrc->Instance->DR) */

  __HAL_CRC_DR_RESET(hcrc);


  /* Enter 32-bit input data to the CRC calculator */

  for (index = 0U; index < BufferLength; index++)

  {

    hcrc->Instance->DR = pBuffer[index];

  }

  temp = hcrc->Instance->DR;


  /* Change CRC peripheral state */

  hcrc->State = HAL_CRC_STATE_READY;


  /* Return the CRC computed value */

  return temp;

}


五、修改main函数

添加测试数据

/* USER CODE BEGIN PV */

/* Private variables ---------------------------------------------------------*/

#define BUFFER_SIZE    114

static const uint32_t dataBuffer[BUFFER_SIZE] =

{

    0x00001021, 0x20423063, 0x408450a5, 0x60c670e7, 0x9129a14a, 0xb16bc18c,

    0xd1ade1ce, 0xf1ef1231, 0x32732252, 0x52b54294, 0x72f762d6, 0x93398318,

    0xa35ad3bd, 0xc39cf3ff, 0xe3de2462, 0x34430420, 0x64e674c7, 0x44a45485,

    0xa56ab54b, 0x85289509, 0xf5cfc5ac, 0xd58d3653, 0x26721611, 0x063076d7,

    0x569546b4, 0xb75ba77a, 0x97198738, 0xf7dfe7fe, 0xc7bc48c4, 0x58e56886,

    0x78a70840, 0x18612802, 0xc9ccd9ed, 0xe98ef9af, 0x89489969, 0xa90ab92b,

    0x4ad47ab7, 0x6a961a71, 0x0a503a33, 0x2a12dbfd, 0xfbbfeb9e, 0x9b798b58,

    0xbb3bab1a, 0x6ca67c87, 0x5cc52c22, 0x3c030c60, 0x1c41edae, 0xfd8fcdec,

    0xad2abd0b, 0x8d689d49, 0x7e976eb6, 0x5ed54ef4, 0x2e321e51, 0x0e70ff9f,

    0xefbedfdd, 0xcffcbf1b, 0x9f598f78, 0x918881a9, 0xb1caa1eb, 0xd10cc12d,

    0xe16f1080, 0x00a130c2, 0x20e35004, 0x40257046, 0x83b99398, 0xa3fbb3da,

    0xc33dd31c, 0xe37ff35e, 0x129022f3, 0x32d24235, 0x52146277, 0x7256b5ea,

    0x95a88589, 0xf56ee54f, 0xd52cc50d, 0x34e224c3, 0x04817466, 0x64475424,

    0x4405a7db, 0xb7fa8799, 0xe75ff77e, 0xc71dd73c, 0x26d336f2, 0x069116b0,

    0x76764615, 0x5634d94c, 0xc96df90e, 0xe92f99c8, 0xb98aa9ab, 0x58444865,

    0x78066827, 0x18c008e1, 0x28a3cb7d, 0xdb5ceb3f, 0xfb1e8bf9, 0x9bd8abbb,

    0x4a755a54, 0x6a377a16, 0x0af11ad0, 0x2ab33a92, 0xed0fdd6c, 0xcd4dbdaa,

    0xad8b9de8, 0x8dc97c26, 0x5c644c45, 0x3ca22c83, 0x1ce00cc1, 0xef1fff3e,

    0xdf7caf9b, 0xbfba8fd9, 0x9ff86e17, 0x7e364e55, 0x2e933eb2, 0x0ed11ef0

};

 

/* Expected CRC Value */

uint32_t uwExpectedCRCValue = 0x379E9F06;

/* USER CODE END PV */


在 main 函数中添加用程序计算数据的 CRC 值并校验是否和正确的 CRC 值相等,打印输出校验信息。

/**

  * @brief  The application entry point.

  * @retval int

  */

int main(void)

{

  /* USER CODE BEGIN 1 */

  __IO uint32_t CRCValue = 0;

  /* USER CODE END 1 */


  /* MCU Configuration--------------------------------------------------------*/


  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

  HAL_Init();


  /* USER CODE BEGIN Init */


  /* USER CODE END Init */


  /* Configure the system clock */

  SystemClock_Config();


  /* USER CODE BEGIN SysInit */


  /* USER CODE END SysInit */


  /* Initialize all configured peripherals */

  MX_USART1_UART_Init();

  MX_CRC_Init();

  /* USER CODE BEGIN 2 */

  printf('nr ****** CRC Test Example *****nr');

  /* USER CODE END 2 */


  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

    CRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)dataBuffer, BUFFER_SIZE);

    printf('rn32-bit CRC:0x%Xn', CRCValue);

[1] [2]
关键字:STM32CubeMX  CRC 引用地址:STM32CubeMX学习笔记(22)——CRC接口使用

上一篇:STM32CubeMX学习笔记(23)——通用定时器接口使用(输入捕获测量脉宽)
下一篇:STM32CubeMX学习笔记(21)——DAC接口使用(输出模拟音频波形)

推荐阅读最新更新时间:2026-03-25 11:23

STM32CubeMX学习笔记(9)——I2C接口使用(读写EEPROM AT24C02)
一、I2C简介 I2C(Inter-Integrated Circuit ,内部集成电路) 总线是一种由飞利浦 Philip 公司开发的串行总线。是两条串行的总线,它由一根数据线(SDA)和一根 时钟线(SCL)组成。I2C 总线上可以接多个 I2C 设备,每个器件都有一个唯一的地址识别。同一时间只能有一个主设备,其他为从设备。通常 MCU 作为主设备控制,外设作为从设备。 STM32 的 I2C 外设可用作通讯的主机及从机,支持 100Kbit/s 和 400Kbit/s 的速率,支持 7 位、10 位设备地址,支持 DMA 数据传输,并具有数据校验功能。它的 I2C 外设还支持 SMBus2.0 协议,SMBus 协议与 I
[单片机]
STM32CubeMX学习笔记(43)——USB接口使用(CDC虚拟串口)
一、USB简介 USB(Universal Serial BUS)通用串行总线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、Microsoft 等多家公司联合提出的。 USB 发展到现在已经有 USB1.0/1.1/2.0/3.0 等多个版本。目前用的最多的就是 USB1.1 和 USB2.0,USB3.0 目前已经开始普及。STM32F103 自带的 USB 符合 USB2.0 规范,不过 STM32F103 的 USB 都只能用来做设备,而不能用作主机。 标准 USB 共四根线组成,除
[单片机]
ESP32学习笔记(2)——GPIO接口使用
一、简介 ESP32 芯片有 40 个物理 GPIO pad。每个 pad 都可用作一个通用 IO,或连接一个内部的外设信号。IO_MUX、RTC IO_MUX 和 GPIO 交换矩阵用于将信号从外设传输至 GPIO pad。这些模块共同组成了芯片的 IO 控制。 注意:其中 GPIO 34-­39 仅用作输入管脚,其他的既可以作为输入又可以作为输出管脚。 GPIO6-11通常用于SPI闪存。 1.1 官方资料 ESP-IDF 编程指南——GPIO&RTC GPIO ESP32 技术参考手册——4 IO_MUX 和 GPIO 交换矩阵 (GPIO, IO_MUX) gpio_example 1.2 包含头文件 #includ
[单片机]
ESP32学习笔记(7)——SmartConfig接口使用(ESP-Touch和AirKiss)
一、概述 SmartConfig是TI开发的一种配置技术,用于将新的Wi-Fi设备连接到Wi-Fi网络。它使用移动应用程序将网络凭据从智能手机或平板电脑广播到未配置的Wi-Fi设备。 该技术的优点是设备不需要直接知道接入点(AP)的SSID或密码。此信息是使用智能手机提供的。 ESP-IDF 编程指南——SmartConfig 二、API说明 以下 SmartConfig 接口位于 esp_wifi/include/esp_smartconfig.h。 2.1 esp_smartconfig_set_type 2.2 esp_smartconfig_start 2.3 esp_smartconfig_sto
[单片机]
ESP32学习笔记(20)——SPI(从机)接口使用
一、SPI简介 SPI(Serial Peripheral Interface) 协议是由摩托罗拉公司提出的通讯协议,即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。 芯片的管脚上只占用四根线。 MISO: 主器件数据输出,从器件数据输入。 MOSI:主器件数据输入,从器件数据输出。 SCK: 时钟信号,由主设备控制发出。 NSS(CS): 从设备选择信号,由主设备控制。当NSS为低电平则选中从器件。 1.1 ESP32中SPI ESP32集成了两个通用SPI控制器,可用作片外SPI主设备驱动的从节点 SPI2,有时也称为HSPI SPI
[单片机]
ESP32学习笔记(36)——BluFi(蓝牙配网)接口使用
一、简介 ESP32 的 BluFi 是通过蓝牙通道的 Wi-Fi 网络配置功能。它提供了一个安全协议来将 Wi-Fi 配置和凭据传递给 ESP32。使用这些信息,ESP32 可以连接到一个 AP 或建立一个 SoftAP。 BluFi 层中的分片、数据加密、校验和验证是此过程的关键要素。 您可以自定义对称加密、非对称加密和校验和支持自定义。这里我们使用DH算法进行密钥协商,128-AES算法进行数据加密,CRC16算法进行校验和验证。 二、BluFi流程 将 ESP32 设置为 GATT Server 模式,然后它将发送带有特定广告数据的广播。您可以根据需要自定义此广播,这不是 BluFi 配置文件的一部分。 使用安
[单片机]
ESP32学习笔记(45)——DAC接口使用
一、概述 ESP32 有两个 8 位 DAC(数模转换器) 通道,分别连接 GPIO25(通道 1) 和 GPIO26(通道 2)。 DAC 驱动器允许将这些通道设置为任意电压。 ESP-IDF 编程指南——DAC 二、API说明 以下 DAC 接口位于 driver/include/driver/dac_common.h 。 2.1 dac_output_voltage 2.2 dac_output_enable 2.3 dac_cw_generator_config 2.4 dac_cw_generator_enable 三、通道引脚 两个 8 位 DAC(数模转换器) 通道,分别连接 GPIO25(通道
[单片机]
使用3.3V CAN收发器简化汽车接口设计
随着汽车的不断发展,配备的先进功能越来越多,旨在增强安全性、舒适性和便利性。更多的功能意味着需要更复杂的电子器件,这凸显了电源效率的重要性。高能效有助于延长行驶里程并降低运营成本,使半导体制造商可以将微控制器 (MCU) 等电气元件的典型电源电压从 5V 降低到 3.3V。在许多汽车系统中,现在只需要为 5V 控制器局域网 (CAN) 收发器提供 5V 电源轨,而所有其他元件可以使用由 12V、24V 或 48V 电池提供的 3.3V 或更低电源轨。使用 3.3V 电源运行的 CAN 收发器将不再需要 5V 电源轨,便于与 MCU 无缝连接。 对于目前正在生产的汽车 CAN 网络,唯一符合电磁兼容性 (EMC) 标准的收发器也
[嵌入式]
<font color='red'>使用</font>3.3V CAN收发器简化汽车<font color='red'>接口</font>设计
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved