4. CRC校验实现
任务目标
在Bootloader升级流程中,固件包在传输或存储过程中可能发生位翻转、丢包等错误。本案例实现标准CRC32(IEEE 802.3,多项式0x04C11DB7)校验算法,为后续固件包完整性验证提供基础校验函数。
实现方案
采用查表法实现CRC32,以字节为单位处理数据。查表法预先计算256个CRC余式,运行时每个字节仅需一次查表与异或操作,相比逐位计算速度提升约8倍,适合在Bootloader中对整个固件包进行校验。代码不依赖任何硬件外设,可直接在主机编译验证,也可移植到MCU环境。
完整代码
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
/* CRC32表(IEEE 802.3,多项式0x04C11DB7,初值0xFFFFFFFF,结果异或0xFFFFFFFF) */
static uint32_t crc_table[256];
/* 初始化CRC32查找表 */
static void crc32_init_table(void) {
for (uint32_t i = 0; i < 256; i++) {
uint32_t crc = i;
for (int j = 0; j < 8; j++) {
crc = (crc & 1) ? (crc >> 1) ^ 0xEDB88320 : (crc >> 1);
}
crc_table[i] = crc;
}
}
/* 计算CRC32校验值
* data: 输入数据指针
* len: 数据长度(字节)
* 返回: 32位CRC校验值
*/
uint32_t crc32_compute(const uint8_t *data, size_t len) {
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < len; i++) {
uint8_t index = (uint8_t)(crc ^ data[i]);
crc = (crc >> 8) ^ crc_table[index];
}
return crc ^ 0xFFFFFFFF;
}
/* 最小验证入口 */
int main(void) {
crc32_init_table();
/* 测试向量1:空数据 */
uint32_t crc_empty = crc32_compute(NULL, 0);
printf("空数据 CRC32: 0x%08X\n", crc_empty);
/* 测试向量2:字符串 "123456789"(标准CRC32校验向量) */
const char *test_str = "123456789";
uint32_t crc_str = crc32_compute((const uint8_t *)test_str, 9);
printf("\"123456789\" CRC32: 0x%08X\n", crc_str);
/* 测试向量3:固件模拟数据(含帧头/地址/长度/负载) */
uint8_t firmware_packet[] = {
0xAA, 0x55, /* 帧头 */
0x00, 0x10, /* 目标地址 */
0x04, 0x00, /* 数据长度=4 */
0xDE, 0xAD, 0xBE, 0xEF /* 负载数据 */
};
uint32_t crc_pkt = crc32_compute(firmware_packet, sizeof(firmware_packet));
printf("固件包 CRC32: 0x%08X\n", crc_pkt);
return 0;
}
编译与运行
# 主机编译验证(需支持C99或更高版本)
gcc -std=c99 -Wall -o crc32_test crc32.c
./crc32_test
验证方法
程序运行后观察输出:
- 空数据:CRC32值为
0x00000000(初值0xFFFFFFFF经最终异或后为0) - "123456789":CRC32值必须为
0xCBF43926,这是CRC32算法的标准校验向量,用于确认算法实现正确 - 固件模拟包:记录输出值,在后续Bootloader案例中作为固件包完整性校验的基准值
若"123456789"的输出不是0xCBF43926,说明查表生成或计算逻辑有误,需检查多项式方向(0xEDB88320为0x04C11DB7的反射形式)及初值/异或处理。
MCU移植说明
在Bootloader工程中使用时,将crc32_init_table()在启动时调用一次,之后对接收到的固件包调用crc32_compute()即可。若MCU的Flash资源紧张,可将crc_table声明为const并预生成常量表,省去运行时初始化。