3. 常见CRC标准:CRC-8, CRC-16, CRC-32, CRC-CCITT
CRC 标准多了去了。你打开个芯片手册,光校验方式就能列七八种。说白了,不同标准就是不同多项式、不同初始值、不同输出异或的组合。选哪个?看你的通信场景和性能要求。
我做了十几年通信诊断,见过不少工程师随便挑个CRC标准就往上怼。结果呢?CRC 误判率高、或者计算太慢拖累吞吐。今天我把最常见的几个标准拆开讲,附带我自己踩过的坑。
3.1 标准速查表
先给你一个总表,心里有个底。后面再逐个细聊。
| 标准名称 | 位宽 | 常用多项式 | 主要应用 | 我的选择建议 |
|---|---|---|---|---|
| CRC-8 | 8 位 | 0x07(x⁸ + x² + x + 1) | 小数据块、传感器、1-Wire | 嵌入式内存紧张时首选 |
| CRC-16 | 16 位 | 0x8005(x¹⁶ + x¹⁵ + x² + 1) | Modbus、USB、PPP | 工业通信首推 |
| CRC-32 | 32 位 | 0x04C11DB7(标准以太网) | 以太网、ZIP、文件校验 | 大包传输,别犹豫 |
| CRC-CCITT | 16 位 | 0x1021(x¹⁶ + x¹² + x⁵ + 1) | XMODEM、蓝牙、老旧协议 | 兼容旧系统时的选择 |
注:多项式通常用倒序表示(比如 0x8005 实际是 0xA001 反转)。你算 CRC 时一定要和协议文档里的多项式表对上,否则全是错。
3.2 CRC-8:轻量级选手
CRC-8 只有 8 位。说白了,它占用资源最少,8 位微控制器都能轻松跑。
我在做无线传感器节点时用过它。电池供电,MCU 主频只有 16 MHz,每次发 20 个字节以内。CRC-8 误判率大约 1/256,够用了。但千万别拿它保护超过 256 字节的包——碰撞概率会飙高。
参数一览:
- 多项式:0x07(标准),也有 0x9B(自动校验)
- 初始值:0x00
- 输出异或:0x00
- 典型应用:1-Wire 协议、SMBus
3.3 CRC-16 双雄:标准 16 与 CCITT
CRC-16 有两个常见变体,你肯定碰到过。一个叫 CRC-16-IBM(0x8005),一个叫 CRC-16-CCITT(0x1021)。
我记得有一次排查 Modbus 通信失败,现场说“CRC 算出来不对”。我查了三天才发现——对端用的是 CCITT,我这头写了 IBM 标准。两个多项式不同,算出来当然驴唇不对马嘴。后来我在代码里统一用可配置的多项式表,再没出过这种低级错误。
| 特性 | CRC-16-IBM | CRC-16-CCITT |
|---|---|---|
| 多项式(标准) | 0x8005 | 0x1021 |
| 初始值 | 0x0000 | 0x0000(或 0xFFFF) |
| 输出异或 | 0x0000 | 0x0000 |
| 常用领域 | Modbus RTU、USB | XMODEM、蓝牙 |
| 误判率(16 位) | 约 1/65536 | 约 1/65536 |
注意:CCITT 还有另一种变体用 0xFFFF 作为初始值。如果发现你的 CRC 计算结果总差固定值,多半是初始值没对上。
3.4 CRC-32:重型武器
CRC-32 是业界标杆。以太网、ZIP 文件、PNG 图片……几乎处处都有它。多项式 0x04C11DB7,32 位空间,误判率低至 1/4.3×10⁹。你想想看,现在网卡每天收发几百兆数据,靠的就是它。
我在调试某个电信设备的 VLAN 转发问题时,切出原始报文自己算 CRC。用 Wireshark 标了 CRC-32,核对多项式发现没问题。结果发现是硬件校验 enable 和 disable 逻辑反了……嗯,这是另一个故事了。
CRC-32 标准参数:
多项式:0x04C11DB7(正向)
初始值:0xFFFFFFFF
输出异或:0xFFFFFFFF
字节序:小端(LSB first)
3.5 核心参数对比表
挑选 CRC 标准时,我建议你盯着这四个参数看:多项式、宽度、初始值、输出异或。你只要把参数表按下面格式写清楚,移植起来非常简单。
| 标准 | 宽度 | 多项式(十六进制) | 初始值 | 输出异或 | 典型 poly(反转后) |
|---|---|---|---|---|---|
| CRC-8 | 8 | 0x07 | 0x00 | 0x00 | 0xE0 |
| CRC-16-IBM | 16 | 0x8005 | 0x0000 | 0x0000 | 0xA001 |
| CRC-16-CCITT | 16 | 0x1021 | 0x0000 | 0x0000 | 0x8408 |
| CRC-32 | 32 | 0x04C11DB7 | 0xFFFFFFFF | 0xFFFFFFFF | 0xEDB88320 |
一项个人小技巧:我习惯把多项式表放在 ROM 里预计算好,然后直接查表算 CRC。512 字节的表,速度比逐位计算快几十倍。特别是 CRC-32,没事别用逐位法——慢到崩溃。
3.6 选型思路与避坑
给你一个简单的判定树,帮我选了好多年不出错:
- 包长 ≤ 64 字节、MCU 资源极少 → CRC-8
- 工业总线、Modbus、串口通信 → CRC-16-IBM(0x8005)
- 需要兼容老协议(XMODEM、蓝牙) → CRC-16-CCITT
- 网络传输、文件校验、大包 → CRC-32
避坑指南——我曾经犯过的错:
- 我曾经直接抄网上别人写的 CRC 表,结果多项式表是反转前还是反转后没分清。算出来的 CRC 永远和别人对不上。解决方案:先找个已知 test vector 验证。比如输入 "123456789" 看输出是不是标准值。
- 我曾经用 CRC-32 保护一个只有 4 字节的短包。误判率反而比 CRC-16 还差?因为 32 位多项式用 4 字节数据算出来,有效检错范围根本发挥不出来。短包用小宽度就行了。
- 注意端序问题。很多通信协议用 LSB first(小端),但有些嵌入式内部寄存器是 MSB first。如果你发现 CRC 一半对一半错,先检查字节序。
3.7 CRC 标准关系图
下面这张图帮你理清常见标准之间的层级。注意:不同标准共享相同的数学原理,只是参数不同。
3.8 小结
选 CRC 标准,别盲目追求位宽大。我反而建议你根据数据包长度和平台资源先列三个候选,再用测试向量跑一遍。很多时候 CRC-16 就能满足需求,没必要上 CRC-32 拖累计算。