常见CRC算法介绍:CRC-8、CRC-16、CRC-32、CRC-CCITT的适用场景与参数

做嵌入式通信这些年,我几乎每天都在跟CRC打交道。你想想看,一条I2C总线上一堆传感器,万一数据被噪声干扰了,芯片还在傻傻地用错误数据做运算——那可就麻烦了。所以选对CRC算法,跟选对通信协议一样重要。

今天咱们就把四种最常见的CRC算法掰开了讲。CRC-8、CRC-16、CRC-32,还有那个有点特殊的CRC-CCITT。它们各有各的脾气,用错了地方,轻则性能浪费,重则检错失效。

避坑警告:我之前在一个项目中偷懒,把所有通信都用CRC-8,结果3.3V电平的I2C总线偶尔被电机噪声干扰,CRC-8根本检不出双比特翻转,数据被污染了三天才发现——从那以后我再也不瞎选了。

1. CRC算法核心参数速查

在讲具体算法之前,咱们先统一一下术语。每种CRC都由这几个参数决定:多项式(Poly)、初始值(Init)、结果异或值(XorOut)、是否输入反转(RefIn)、是否输出反转(RefOut)。它们就像密钥,一丁点不一样,算出来的校验码就完全不同。

参数含义常见值示例(CRC-16-Modbus)
多项式(Poly)生成CRC的除数0x8005
初始值(Init)移位寄存器的起始值0xFFFF
结果异或值(XorOut)计算完成后与输出做异或0x0000
输入反转(RefIn)字节是否按位反转输入True
输出反转(RefOut)结果是否按位反转输出True

习惯分享:我一般会在代码注释里把参数表直接复制粘贴一份,不然半年以后你自己都搞不清当时用的是哪个变体。

2. CRC-8 —— 小数据包的轻量卫士

CRC-8只有8位校验码,计算速度极快。适合校验长度不超过10~20字节的数据包,比如单传感器读数、按键状态。但它的汉明距离有限,碰上长数据包或强噪声环境基本就废了。

  • 典型多项式:0x07(CRC-8-ATM)、0x9B(CRC-8-Dallas)
  • 适用场景:I2C单字节读写、1-Wire温度传感器、短报文遥控
  • 我的经验:有一次做智能锁,锁体上报状态只用3个字节,我用了CRC-8-ATM,速度极快,占空间也小。后来升级加了一个4字节的UID,CRC-8就开始漏检测了——所以记住,数据长度小于16字节时我才会考虑它。

关键参数表:CRC-8常用变体

变体PolyInitRefIn/RefOutXorOut
CRC-8-ATM0x070x00False0x00
CRC-8-Dallas0x310x00True0x00

3. CRC-16 —— I2C总线的黄金搭档

这是最常用的CRC算法之一。16位校验,检错能力适中,计算量也不大。我个人在I2C长数据包(比如128字节的传感器融合数据)里几乎必用CRC-16-Modbus。你想想看,Modbus的CRC-16连19~30位的突发错误都能100%检出,对10KHz~400KHz的I2C总线噪声来说够用了。

  • 典型多项式:0x8005(Modbus)、0x1021(CCITT)
  • 适用场景:I2C批量读写、SPI长帧、工业总线
  • 避坑指南:我曾经遇到过一件事,某厂家的PMBus电源芯片规定用CRC-16-IBM(多项式0x8005),而我的代码里写的是CRC-16-Modbus——看着多项式一样,但Modbus的初始值是0xFFFF,IBM是0x0000,结果我调了三个小时才找到原因。

4. CRC-CCITT —— 通信老兵的常青树

CRC-CCITT严格来说是CRC-16的一个变种,多项式固定为0x1021。但因为它被CCITT(现ITU-T)标准采用,很多X.25、HDLC、蓝牙协议里都在用。它的多项式经过精心选取,对同步通信中的单比特和双比特错误尤其敏感。

注意:CRC-CCITT还有个兄弟叫CRC-16-CCITT-FALSE(初始值0xFFFF,无反转)和CRC-16-CCITT-TRUE(初始值0x0000,反转)。千万别搞混。

变体PolyInitRefInRefOutXorOut
CRC-CCITT (XMODEM)0x10210x0000FalseFalse0x0000
CRC-16-CCITT (Kermit)0x10210x0000TrueTrue0x0000

小技巧:如果你在做蓝牙LE或串口透传,直接选CRC-CCITT(Kermit)准没错。蓝牙协议栈内部用的也是这个变体。

5. CRC-32 —— 大块数据的看门狗

CRC-32生成32位校验码,计算量大了不少,但对错误检测能力几乎接近极限。它用在文件校验、网络包(以太网FCS)、固件升级等场景。我当年移植RTOS固件升级模块时,整包CRC-32耗时大约5ms(80MHz Cortex-M4),对于几百KB的固件完全可以接受。

  • 典型多项式:0x04C11DB7(最通用)
  • 适用场景:固件完整性校验、文件系统元数据、长距离通信
  • 我的看法:如果数据长度超过512字节,我建议直接上CRC-32。不要为了省那几百微秒的时间而牺牲可靠性——你难道想看到现场设备升级一半变砖吗?

6. 四种算法选择决策 —— 一张SVG图说明

下面这个图是我平时给团队培训用的参考。它从数据长度、误码率要求和硬件资源三个维度帮你快速定位该用哪个算法。嗯,你可以把它贴在工作台旁边。

CRC算法选择决策图 开始:需要CRC校验 数据长度 ≤ 32字节? CRC-8 传感器读数 开关状态等短数据 噪声环境强? CRC-32 固件升级 文件校验 遵循标准协议? CRC-CCITT Modbus / HDLC / 蓝牙 XMODEM / PPP CRC-16(通用) I2C长数据包 SPI批量传输

7. 实战中的参数选择清单

别光看理论,落实到代码里还得注意这些点。我列一个核对清单,每次选CRC时对照一遍:

  1. 数据包最大长度:超过200字节直接放弃CRC-8。
  2. 最小误码率要求:汽车级应用(比如CAN)最好用CRC-16以上。
  3. MCU性能:8位单片机就别跑软件CRC-32了,除非你用硬件模块。
  4. 协议兼容性:如果对接现成芯片,查手册里明确的算法变体(包括Init和XorOut)。
  5. 字节序:I2C发数据一般是MSB first,但CRC计算里RefIn可能要求LSB first,别搞反了。

终极踩坑:我见过最惨的事故是,有人在循环里把CRC计算和字节发送混在一起,结果每次发送前都重新初始化寄存器。整包校验码全乱了。记住:CRC计算一定是原子操作,先收完整个包,再算CRC。

好了,四种常见CRC算法就讲到这里。你学会怎么选了吗?下次在I2C总线上加CRC校验时,心里有数没?