如何针对非4字节倍数数据使用STM32硬件CRC32计算
问题说明
我有一台设备采用查表法实现CRC32校验和,代码如下:
uint32_t CrcFromBuffer(uint8_t* buffer, uint16_t length) { uint32_t crc32 = 0xffffffff; if (buffer == NULL) { return 0; } else { while (length != 0) { uint8_t crc32_table_index = ((uint8_t)(crc32) ^ (*buffer)); crc32 = crc32 >> 8; crc32 = crc32_table[crc32_table_index] ^ crc32; buffer++; length--; } } return ~crc32; }
使用STM32F401的硬件CRC模块时,多项式与上述一致,但仅支持32位输入。当数据长度为4字节倍数时,可通过反转位序的方式匹配软件计算结果,示例代码:
bool eight_bytes_random_crc() { uint8_t array8[8] = {0xAB, 0x21, 0x32, 0x47, 0x01, 0xFF, 0x00, 0x99}; uint32_t array32[2] = {__RBIT(0x473221AB), __RBIT(0x9900FF01)}; // 反转位序 uint32_t hard_crc = __RBIT(~HAL_CRC_Calculate(&hcrc, array32, 2)); // 再次反转位序并取反 uint32_t soft_crc = CrcFromBuffer(array8, 8); return hard_crc == soft_crc; }
现需解决:如何用STM32硬件CRC处理非4字节倍数的数据数组?曾尝试暴力搜索单字节输入的等效32位值,但未找到通用方法。
解决方案
核心思路
STM32硬件CRC与软件查表法的多项式一致(IEEE CRC32,0x04C11DB7),差异仅在于位序处理和数据对齐要求。采用以下策略实现任意长度数据的硬件CRC计算:
- 优先处理所有完整的4字节块,得到中间CRC结果;
- 对剩余的1/2/3字节,将其填充为4字节并转换为硬件兼容的位序,输入硬件CRC完成累加计算;
- 最后统一反转位序并取反,匹配软件输出格式。
完整实现代码
#include "stm32f4xx_hal.h" #include <stdlib.h> // 通用硬件CRC计算函数,支持任意长度数据 uint32_t HardwareCrcFromBuffer(uint8_t* buffer, uint16_t length) { if (buffer == NULL || length == 0) { return 0; } uint32_t crc_result = 0xFFFFFFFF; uint16_t full_blocks = length / 4; uint16_t remainder = length % 4; // 处理所有完整的4字节块 if (full_blocks > 0) { uint32_t* buf32 = (uint32_t*)buffer; uint32_t* temp_buf = (uint32_t*)malloc(full_blocks * sizeof(uint32_t)); for (uint16_t i = 0; i < full_blocks; i++) { // 将小端字节序转为大端,再反转每一位以匹配硬件CRC的位处理顺序 uint32_t big_endian = __REV(buf32[i]); temp_buf[i] = __RBIT(big_endian); } // 硬件计算完整块的CRC,初始值默认0xFFFFFFFF crc_result = HAL_CRC_Calculate(&hcrc, temp_buf, full_blocks); free(temp_buf); } // 处理剩余的1/2/3字节 if (remainder > 0) { uint32_t remainder_val = 0; // 将剩余字节填充到32位变量的低位,高位补0 for (uint16_t i = 0; i < remainder; i++) { remainder_val |= ((uint32_t)buffer[full_blocks*4 + i]) << (8*i); } // 同样转换为大端并反转位序,输入硬件CRC累加计算 uint32_t rbit_val = __RBIT(__REV(remainder_val)); crc_result = HAL_CRC_Calculate(&hcrc, &rbit_val, 1); } // 匹配软件输出格式:反转位序后取反 return __RBIT(~crc_result); } // 验证函数:测试不同长度数据的CRC一致性 bool test_any_length_crc() { // 测试1字节数据 uint8_t test1[1] = {0xAB}; uint32_t soft1 = CrcFromBuffer(test1, 1); uint32_t hard1 = HardwareCrcFromBuffer(test1, 1); if (soft1 != hard1) return false; // 测试2字节数据 uint8_t test2[2] = {0xAB, 0xCD}; uint32_t soft2 = CrcFromBuffer(test2, 2); uint32_t hard2 = HardwareCrcFromBuffer(test2, 2); if (soft2 != hard2) return false; // 测试3字节数据 uint8_t test3[3] = {0xAB, 0xCD, 0xEF}; uint32_t soft3 = CrcFromBuffer(test3, 3); uint32_t hard3 = HardwareCrcFromBuffer(test3, 3); if (soft3 != hard3) return false; return true; }
关键细节说明
- 字节序与位序转换:软件查表法按字节流从低到高处理(每个字节的LSB先参与运算),而STM32硬件CRC按32位数据的MSB到LSB处理,因此需要通过
__REV将小端4字节块转为大端,再用__RBIT反转每一位,匹配硬件输入要求。 - 剩余字节处理:将剩余字节填充到32位变量的低位,同样做字节序和位序转换后输入硬件CRC,利用硬件CRC的线性特性自动延续之前的中间计算结果,无需暴力搜索等效值。
- 结果对齐:最终对硬件输出的CRC值反转位序并取反,完全匹配软件
CrcFromBuffer的输出格式。
内容的提问来源于stack exchange,提问作者mactro
相关产品推荐
相关产品推荐

