1. 什么是循环冗余校验

循环冗余校验,简称 CRC,是一种通过多项式除法来判断数据是否被篡改的校验方法。说白了,就是在发送数据时额外塞几个校验位,接收端用同样的算法算一遍,算出来不一样就说明数据出问题了。

我第一次接触CRC,是刚工作那会儿。老大丢给我一份要写到Flash里的固件升级程序,让我检查数据对不对。我心想直接算个累加和不就完了?结果跑了一轮,换个地址再读,数据全对了,但某几个bit被毛刺打翻了,累加和愣是没发现。折腾了一下午,后来改成CRC16,问题就抓出来了。那之后,我对CRC就特别上道。

1.1 核心思想——用除法做核对

CRC 的原理其实不复杂。它把原始数据看作一个很长的二进制数,除以一个事先约好的多项式(也就是“除数”),得到余数,这个余数就是 CRC 校验值。接收方用同样的多项式去除收到的数据,如果余数为0,说明大概率没出错。

  • 数据:你要保护的消息,比如一串字节
  • 多项式:双方约定的除数,比如 0x1021(CRC-CCITT)
  • 余数:CRC 校验码,通常是 8、16、32 位

小提示:CRC 里除法和小学长除法很像,但用的是异或(XOR)代替减法,所以硬件实现起来特别快。

1.2 多项式——CRC的“身份证”

每个 CRC 算法都对应一个多项式。比如 CRC-32 的多项式是 0x04C11DB7,CRC-16-IBM 是 0x8005。我见过不少同学以为随便选个多项式就行,实际不然。多项式选得不好,检错能力差到吓人。

算法名称多项式(十六进制)校验宽度常见应用
CRC-80x078位小数据包、I²C
CRC-16-IBM0x800516位Modbus、USB
CRC-16-CCITT0x102116位XMODEM、蓝牙
CRC-320x04C11DB732位以太网、ZIP、PNG

我个人习惯,在项目开始前先确定通信或存储协议里用的是什么多项式。别小看这一步,曾经有个同事在QSPI Flash驱动里用了多项式0x8005做CRC8,结果8位多项式实际只有5位有效,等于白干。后来我帮他改成0x07,误检率立刻降下来。

避坑指南:我曾经在批量烧录产线上遇到过一批Flash芯片读回来的CRC老对不上。排查到最后发现,代码里多项式初始化那行被编译器优化掉了——因为我没把多项式常量声明为 volatile。记住,CRC 算法的实现一定要确保每次调用都重新加载初始值,尤其是嵌入式环境。

1.3 应用场景——哪些地方离不开CRC?

CRC的应用比你想象的广。你可以把数据看作在一条嘈杂的道路上奔跑,CRC就是那个检查轮胎是不是跑偏的传感器。

  • 存储系统:NAND Flash、SD卡、固态硬盘——每次写入都生成CRC,读取时重新校验。我遇到过几次Flash读出来的数据有比特翻转,全靠CRC32把坏块揪出来。
  • 通信协议:以太网帧尾部就附着4字节CRC32;USB、CAN、SPI也常用CRC。在QSPI(四线SPI)上,如果用的是 Mode 0 时序,数据线翻转可能引入隐性错误,加CRC能救你一命。
  • 嵌入式固件更新:OTA升级时,下载包都会附带CRC校验值。我记得做物联网模块那会儿,服务器发下来的固件包里自带CRC32,单片机读完先校验,不对就请求重传,客户从来没抱怨过升级变砖。
  • 工业现场总线:Modbus、PROFIBUS 里CRC16是标配。环境恶劣,电磁干扰重,没有CRC,数据错一个bit就可能造成设备误动作。

1.4 为什么选CRC而非其他校验?

  1. 检错能力强:可以检测出多位突发错误。相比之下,奇偶校验只能检奇数位错误,累加和性能更差。
  2. 硬件实现简单:用几个移位寄存器和异或门就能搞定,甚至不需要CPU参与。我在FPGA上用过纯硬件的CRC32,跑1Gbps毫无压力。
  3. 计算速度中等:软件查表法也很快,每字节只要几个周期。Cortex-M0上用查表法做CRC16,几十兆字节也就几十毫秒。
  4. 标准统一:协议里直接用现成多项式就行,不用自己发明轮子。

注意:CRC不是加密。坏人可以恶意篡改数据同时重算CRC,所以CRC只适合检测随机错误,不适合防篡改。需要防篡改,请用HMAC或数字签名。

下面这张图简单展示了CRC在通信链路中的位置。你想想看,数据从发送端出发,先经过CRC生成器,带上校验码,然后穿过信道,接收端再用同样的多项式做除法。余数为0 -> 数据可靠;不为0 -> 丢包或重传。

CRC 在数据通信中的应用流程 原始数据 CRC 生成器 待发送的数据+CRC 噪声信道 收到数据+CRC CRC 校验 余数=0 → 可靠 发送端 计算余数附加 接收端 重新除法判余数

从这张图你能看出,CRC 就像数据链路层的“安检员”。我习惯在QSPI传输的数据包的每个256字节块后面附加2字节CRC16,这样如果传输过程中某条数据线被干扰,CRC立刻能发现,然后我就能触发重传。测试过几千次,误检率几乎为零。

1.5 关于QSPI与CRC的特殊关系

QSPI 支持四线同时传输,带宽是普通SPI的4倍,但随之而来的是更复杂的信号完整性挑战。四条数据线之间的串扰、同板其他走线的耦合、甚至Flash芯片内部电源抖动,都可能造成单bit或多bit错误。而这些错误用简单的奇偶校验根本抓不住。

我在一个高速数据记录仪项目里,把QSPI时钟拉到80MHz,结果数据率上去了,偶尔出现读回来的几个字节全错——但求和校验居然通过了!换成CRC16后,问题暴露无遗,原来是电源纹波导致某条数据线上的电平判决出错。从此我的所有QSPI数据帧里都固定带上CRC。

一句话总结:CRC 用多项式除法把数据“压缩”成几个字节的指纹,指纹对得上,数据大概率没坏。在 QSPI 这类高速接口里,它是你防数据异常的护身符。