第三章 CRC常用多项式:CRC-8, CRC-16, CRC-32
说实在的,刚接触CRC的时候,最容易让人眼花的就是那些多项式到底用哪个。CRC-8、CRC-16、CRC-32,光看名字你可能觉得是位数不同,但实际用起来,坑还挺多。我最早在一款温度传感器项目里,就因为选错了多项式,导致数据在传输中偶尔出错,排查了整整两天才回过神。
多项式是CRC算法的核心。它决定了一个校验码能检出的错误类型和覆盖范围。选对了,95%的误码都能拦住;选错了,再长的校验码也是摆设。你想想看,数据链路层、存储系统、无线通信,各有各的偏好。我们得根据不同场景来挑。
3.1 什么是CRC多项式?
多项式就是一组二进制数,用它来做除法,余数就是CRC码。这个多项式必须是一个“本原多项式”,说白了就是它能尽可能地检测出位翻转和突发错误。常见的多项式已经经过几十年的验证,业内直接拿来用就行。
我个人习惯把多项式看作一个“筛子”。筛子的孔位决定了能拦住的错误类型。选错筛子,等于白做校验。
| 多项式名称 | 位数 | 十六进制表达式(常用) | 典型应用 |
|---|---|---|---|
| CRC-8 | 8 | 0x07 | 1-Wire, SMBus |
| CRC-16-IBM | 16 | 0x8005 | Modbus, USB |
| CRC-16-CCITT | 16 | 0x1021 | XMODEM, Bluetooth |
| CRC-32-IEEE | 32 | 0x04C11DB7 | Ethernet, ZIP, Gzip |
小提示: 同一个位数下也有不同变体,比如CRC-16就有IBM和CCITT两种。选哪个,最好先查查你的通信协议手册,别拍脑袋乱用。
3.2 CRC-8 —— 小巧玲珑的低成本选择
CRC-8适合对可靠性要求不是极端苛刻、但希望节省存储和带宽的场景。比如我做过的一个智能家居传感器,用SPI通信,每次只发32字节数据。这时候用CRC-8就够用了,它能检测出所有单个位错误和大部分双位错误。
为什么选它? 因为它只占1字节的附加空间,计算也快,适合MCU资源紧张的情况。但它有个缺陷:对长数据包(超过100字节)的检测能力会下降。
重点: CRC-8的最大检测能力是:所有1-2位错误,所有奇数位错误,以及长度不超过8位的突发错误。超过这个范围,漏检率会上升。
3.3 CRC-16 —— 工业界的平衡之王
CRC-16是目前最常用的CRC多项式之一,尤其是CRC-16-CCITT(0x1021)。它在检测能力和开销之间取得了很好平衡。我之前在QSPI Flash驱动里就是用CRC-16,每256字节数据生成2字节校验码,效果相当稳。
我个人习惯在以下场景首选CRC-16:
- 数据包长度在128-1024字节之间
- 传输速率中等(几Mbps到几十Mbps)
- 需要兼容Modbus、XMODEM等标准协议
避坑指南: 我曾经在一个项目中用了CRC-16-IBM(0x8005),结果与对方设备不兼容,因为对方用的是CCITT。记住,多项式一样,但初始值、结果异或值、输入输出反射都可能不同。选错了就是“鸡同鸭讲”。
| 参数 | CRC-16-IBM | CRC-16-CCITT |
|---|---|---|
| 多项式 | 0x8005 | 0x1021 |
| 初始值 | 0x0000 | 0xFFFF |
| 结果异或值 | 0x0000 | 0x0000 |
| 输入反射 | True | True |
| 输出反射 | True | True |
// CRC-16-CCITT 参考实现(C语言)
uint16_t crc16_ccitt(uint8_t *data, uint16_t len) {
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001)
crc = (crc >> 1) ^ 0xA001; // 注意这里用了反转多项式
else
crc >>= 1;
}
}
crc ^= 0x0000; // 结果异或,实际可以不写
return crc;
}
3.4 CRC-32 —— 高可靠场景的标配
CRC-32,尤其是IEEE 802.3版本,几乎成了网络传输和文件完整性校验的事实标准。你电脑上解压一个ZIP文件,或者通过以太网收发数据,背后都是CRC-32在把关。
我参与过一个工业相机项目,要求每秒传输上百兆图像数据,且不能有错。我们最后选了CRC-32,配合QSPI的DMA传输,做到了“几乎零漏检”。为什么会这样?因为CRC-32可以检测出:
- 所有单个和双个位错误
- 所有奇数位错误
- 所有长度不超过32位的突发错误
- 99.9999999%以上的其他错误模式
警告: CRC-32的计算量比CRC-16大不少。在低端MCU上(如Cortex-M0),软件计算一帧1024字节的CRC-32可能需要几百微秒。如果你需要高速QSPI通信,建议用硬件CRC模块或查表法加速。
3.5 如何选择适合你项目的多项式?
嗯,这里要注意,选多项式不只是看位数。给你三个实际建议:
- 查协议文档: 项目如果依赖某个标准协议(如Modbus、BLE),直接用协议规定的多项式,别自己发明。
- 考虑数据长度: 每包数据小于64字节,CRC-8够用;64-512字节,CRC-16稳妥;超过512字节或要求极高可靠性,上CRC-32。
- 看硬件支持: 很多MCU内置CRC-32硬件加速器(比如STM32),用硬件算几乎不占CPU时间。这时候就算数据不长,用CRC-32也无妨。
我的经验: 在QSPI应用中,如果数据可靠性是第一优先级(比如存储固件、关键配置),哪怕数据只有16字节,我也建议用CRC-32。硬件算一下也就几个时钟周期,不亏。如果只是传传感器读数,CRC-16足矣。