一:常见的校验方式知识分享:
在工业控制中,通信之间为了保证数据传输的准确性或者是完整性,都会存在校验。
常见的校验方式有以下几种方式:
1. 奇偶校验(Parity Check)
原理:在数据后添加一个校验位,使数据中1的个数为奇数或偶数。
类型:奇校验:1的个数为奇数。偶校验:1的个数为偶数。
特点:简单,但只能检测奇数个错误。常用于串口之间的通讯。
2. 校验和(Checksum)
原理:将数据分段求和,结果作为校验和附加在数据后。
特点:实现简单,适合低错误率环境,但检测能力有限。
一般用于PC机与下位机之间的通讯方式。
3. 循环冗余校验(CRC, Cyclic Redundancy Check)
原理:通过多项式除法生成校验码,附加在数据后。
特点:检测能力强,适合高可靠性要求的场合。
4. MD5 和 SHA 校验
原理:使用哈希算法生成固定长度的校验值。
特点:主要用于验证数据完整性,抗碰撞性强。
5. Reed-Solomon 码
原理:通过添加冗余数据,检测并纠正多个错误。
特点:广泛应用于通信和存储系统,纠错能力强。
6. 前向纠错(FEC, Forward Error Correction)
原理:发送冗余信息,接收方直接纠正错误。
特点:无需重传,适合实时性要求高的场景。
7. 异或校验(XOR Check)
原理:对数据逐字节异或运算,结果作为校验值。
特点:简单,但检测能力有限。
二:STM32的硬件CRC 知识分享:
今天我们讨论一下,STM32的硬件CRC校验方式:
2.1:我们查看一下官方的手册:
CRC(循环冗余校验)计算单元用于使用可配置的生成器多项式值和大小来获取CRC代码。
在其他应用中,基于CRC的技术用于验证数据传输或存储的完整性。在EN/IEC60335-1标准的范围内,它们提供了一种验证闪存完整性的方法。CRC计算单元有助于在运行时计算软件的签名,以便与在链接时生成并存储在给定存储器位置的引用名进行比较。
STM32 微控制器内置了 CRC(循环冗余校验)模块,用于计算数据的 CRC 校验值。CRC 是一种常用的数据校验方法,用于检测数据传输或存储过程中的错误。STM32 的 CRC 模块支持多种 CRC 标准,并且可以通过硬件加速计算,提高效率
2.2:STM32 CRC 模块的特点
支持多种 CRC 标准:STM32 的 CRC 模块支持多种 CRC 多项式,包括 CRC-32、CRC-16 等。
硬件加速:CRC 计算由硬件完成,速度快,不占用 CPU 资源。
可配置的初始值和输入输出反转:用户可以根据需要配置 CRC 计算的初始值,以及是否对输入和输出数据进行反转。
32 位数据宽度:STM32 的 CRC 模块通常支持 32 位数据宽度的计算。
2.3:STM32 CRC 模块的使用步骤
启用 CRC 时钟:在使用 CRC 模块之前,需要先启用 CRC 模块的时钟。
配置 CRC 模块(可选):根据需要配置 CRC 的初始值、多项式、输入输出反转等参数。
计算 CRC:将数据写入 CRC 数据寄存器,CRC 模块会自动计算 CRC 值。
读取 CRC 结果:计算完成后,从 CRC 数据寄存器中读取 CRC 值。
三:STM32 cubeMX 软件配置如下:
硬件CRC校验代码如下:
hcrc.Instance = CRC; //CRC示例 hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE; //失能默认的多项式 hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE; //使用默认的初始值 hcrc.Init.GeneratingPolynomial = 101; hcrc.Init.CRCLength = CRC_POLYLENGTH_7B; //数据长度为7个字节 hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE; //输入数据不反转 hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE; //输出数据不反转 hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES; //输入数据格式为32位字 if (HAL_CRC_Init(&hcrc) != HAL_OK) { Error_Handler(); }
CRC (循环冗余校验) 计算单元计算从 8 位数据(字节)缓冲区派生的 7 位 CRC 码。用户定义的生成多项式被手动设置为 0x65,即 X^7 + X^6 + X^5 + X^2 + 1,如列车通信网络 IEC 60870-5[17] 中使用的那样。
宏定义一些变量:
static const uint8_t CRC7_DATA8_TEST5[5] = {0x12, 0x34, 0xBA, 0x71, 0xAD}; static const uint8_t CRC7_DATA8_TEST17[17] = {0x12, 0x34, 0xBA, 0x71, 0xAD, 0x11, 0x56, 0xDC, 0x88, 0x1B, 0xEE, 0x4D, 0x82, 0x93, 0xA6, 0x7F, 0xC3 }; static const uint8_t CRC7_DATA8_TEST1[1] = {0x19}; static const uint8_t CRC7_DATA8_TEST2[2] = {0xAB, 0xCD}; uint32_t * CRC7_DATA8_PTR_TEST1 = (uint32_t *)CRC7_DATA8_TEST1; uint32_t * CRC7_DATA8_PTR_TEST2 = (uint32_t *)CRC7_DATA8_TEST2; /* Expected CRC Values */ /* The 7 LSB bits are the 7-bit long CRC */ uint32_t uwExpectedCRCValue_1 = 0x00000057; /* First byte stream CRC */ uint32_t uwExpectedCRCValue_2 = 0x0000006E; /* Second byte stream CRC */ uint32_t uwExpectedCRCValue_3 = 0x0000004B; /* Third byte stream CRC */ uint32_t uwExpectedCRCValue_4 = 0x00000027; /* Fourth byte stream CRC */
测试代码如下所示:
uwCRCValue = HAL_CRC_Accumulate(&hcrc, (uint32_t *)&CRC7_DATA8_TEST5, BUFFER_SIZE_5); /* Compare the CRC value to the expected one */ if (uwCRCValue != uwExpectedCRCValue_1) { /* Wrong CRC value: enter Error_Handler */ Error_Handler(); } uwCRCValue = HAL_CRC_Accumulate(&hcrc, (uint32_t *)&CRC7_DATA8_TEST17, BUFFER_SIZE_17); /* Compare the CRC value to the expected one */ if (uwCRCValue != uwExpectedCRCValue_2) { /* Wrong CRC value: enter Error_Handler */ Error_Handler(); } uwCRCValue = HAL_CRC_Accumulate(&hcrc, (uint32_t *)CRC7_DATA8_PTR_TEST1, BUFFER_SIZE_1); /* Compare the CRC value to the expected one */ if (uwCRCValue != uwExpectedCRCValue_3) { /* Wrong CRC value: enter Error_Handler */ Error_Handler(); } uwCRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)CRC7_DATA8_PTR_TEST2, BUFFER_SIZE_2); /* Compare the CRC value to the expected one */ if (uwCRCValue != uwExpectedCRCValue_4) { /* Wrong CRC value: enter Error_Handler */ Error_Handler(); } else { /* Right CRC value: Turn LED4 on */ BSP_LED_On(LED4); }
代码测试图如下所示:
程序执行的过程如下所示:
首先,处理一个 5 字节长的缓冲区以产生第一个 CRC。
接下来,从 17 字节长的缓冲区计算第二个 CRC。对于后者,CRC 计算器不会重新初始化,而是使用先前计算的 CRC 作为初始值。
然后,从 1 字节长的缓冲区计算第三个 CRC。同样,CRC 计算器不会重新初始化,而是将先前计算的 CRC 用作初始值。
最后,从 2 字节长的缓冲区计算第四个 CRC。这一次,CRC 计算器使用为 7 位 CRC 0x7F的 IP 默认值重新初始化。这是通过调用 HAL_CRC_Calculate() 而不是 HAL_CRC_Accumulate() 来完成的。
后记:CRC校验在使用过程中的注意事项
数据对齐:STM32 的 CRC 模块通常要求输入数据是 32 位对齐的。如果数据不是 32 位对齐的,可能需要手动处理。
多项式选择:默认情况下,STM32 使用 CRC-32 多项式(0x04C11DB7)。如果需要使用其他多项式,可以通过配置寄存器来实现。
初始值:默认初始值为 0xFFFFFFFF,可以根据需要修改。
第二部分:软件CRC-8校验方式:
/** * @brief 计算CRC8校验值 * @param *pdat:数据首地址 * @param len:计算数据长度 * @retval 无 */ unsigned char calcCRC(unsigned char *pdat, unsigned int len) { unsigned char i; unsigned char crc=0x00; /* 计算的初始crc值 */ while(len--) { crc ^= *pdat++; /* 每次先与需要计算的数据异或,计算完指向下一数据 */ for (i=8; i>0; --i) /* 下面这段计算过程与计算一个字节crc一样 */ { if (crc & 0x80) crc = (crc << 1) ^ 0xD5; else crc = (crc << 1); } } return (crc); }
到这里:STM32硬件CRC校验方式和软件CRC-8校验方式,基本上就是这样,大家可以根据自己项目需求,自行取舍。