CRC 校验概述:定义、历史、应用场景
大家好,我是你们这堂课的老同事。今天咱们聊聊 CRC —— 循环冗余校验。说实话,这玩意儿听起来有点吓人,但说白了就是给数据算个“指纹”,用来检查它有没有被弄脏。
我最早接触 CRC 是在一个 I2C 传感器项目上。当时板子上的干扰很厉害,数据动不动就错一位。加了个奇偶校验?根本没用。后来改用 CRC,才算把问题按住。
那 CRC 到底是什么?我们从头捋一遍。
1.1 CRC 的定义
CRC(Cyclic Redundancy Check,循环冗余校验)是一种基于二进制多项式的检错码。发送方用约定的多项式去除数据帧,得到的余数就是 CRC 校验码。接收方用同样的多项式再做一次除法,如果余数为 0,说明数据大概率没坏。
核心公式:M(x) · x^n ÷ G(x) = Q(x) … R(x)
- M(x):原始数据多项式
- G(x):生成多项式(固定,双方约定)
- R(x):余数 → CRC 校验值
举个例子吧。假设你要发一个字节 0x83,生成多项式用最简单的 0x07(CRC‑3)。计算过程就像小学除法,只不过用的是模 2 除法(不进位,不借位)。最后得到 3 位余数,拼在数据后面发出去。
我的经验:刚开始我老算错模 2 除法,后来发现直接查表比手算快十倍。但理解原理很重要,要不然遇到“多项式选错了”这类坑,你根本不知道错在哪。
1.2 历史小故事
CRC 的发明要追溯到 1961 年。当时 IBM 的 W. Wesley Peterson 发表了一篇叫《Cyclic Codes for Error Detection》。他老人家把纠错码里的循环码思想搬到了检错上,于是 CRC 诞生了。
为什么叫“循环”?因为如果一个码字左移一位再处理,结果还是合法码字——这个性质让硬件实现特别简单。你想想看,用几个移位寄存器和异或门就能搞定,这在 60 年代可太值钱了。
我记得上世纪 90 年代,很多串行协议(比如 HDLC、PPP)都拿 CRC 当家。直到现在,以太网帧尾部还挂着 CRC‑32 呢。这玩意儿真的很耐用。
1.3 应用场景:I2C 通信中的 CRC
回到咱们的主线——I2C。I2C 本身没有强制要求校验,主从设备之间只能靠应答位(ACK/NACK)确认字节层面有没有收到。但应答位只能告诉你“我看没看到数据”,不能告诉你“我看到的数据对不对”。
举个实际场景:我从一个温度传感器读 16 位温度值。如果总线受到串扰,最低位从 1 变成了 0,温度直接差 0.5°C。传感器会照常发送 NACK 吗?不会,因为字节数是对的,它以为自己发对了。这时候就需要 CRC 来抓这种“静默错误”。
I2C 里常用 CRC‑8(生成多项式 0x07 或 0x31)。一些高端传感器(如 SHT30、MPU‑9250)会在读数据帧的最后带一个字节 CRC,让你验证整包数据。
注意:I2C 上做 CRC 时,一定要把从机地址和读写位也算进校验范围。我曾经吃过亏,只算了数据区,结果地址被篡改了却查不出来。
1.4 常见校验方式对比
| 校验方式 | 检测能力 | 实现复杂度 | 典型长度 | I2C 常用性 |
|---|---|---|---|---|
| 奇偶校验 | 只能检测奇数个位错误 | 极低(1 位 XOR) | 1 bit | 很少用 |
| 和校验(Checksum) | 能检测多数单字节错误 | 低(累加) | 8/16 bit | 部分协议 |
| CRC | 能检测突发错误、连续错误 | 中等(查表或硬件) | 3~32 bit | 强烈推荐 |
你看,CRC 在检错能力上碾压其他方案。虽然实现比奇偶校验复杂一点,但用查表法也就几十条指令。在单片机资源紧张的年代,这点开销完全值得。
1.5 CRC 的工作流程
下面是 CRC 在 I2C 通信中的典型流程。我画了个图,一目了然。
从图里你就能看出,发送方先把数据算一遍,余数挂尾巴上。接收方收到后,把整个包(数据+CRC)再除一次。余数刚好是 0 的话,多半没问题;不是 0 那肯定出错了。
经验之谈:我一开始图省事,让接收方只对数据算 CRC,然后跟发过来的 CRC 比对。结果发现算法有个小 bug,两边的移位方向不一样。后来我才知道,一定要用一模一样的多项式、初始值和输出异或值。推荐直接用查表法,省得自己折腾。
1.6 为什么选 CRC 而不是其他校验?
你可能会问:奇偶校验这么简单,为什么不能用?
- 奇偶校验:只能抓单数位错误。如果两个 bit 同时翻转,它就觉得数据没问题。I2C 总线上的噪声可不管你是奇数还是偶数。
- 和校验:把所有字节加起来。缺点也非常明显——如果同一个位置的两个字节互相抵消(比如 +1 和 -1),和校验就以为数据没变。
- CRC:靠多项式除法,任何连续的、分散的、甚至是大量的比特翻转,它都能以极高的概率检测出来。CRC‑16 的漏检率只有
1/65536,CRC‑32 更是低到1/4.3×10^9。
一句话总结:在实时性要求不极端的 I2C 场景下,CRC 是“性价比”最高的校验方案。付出几十个字节的代码空间,换来几乎完美的错误检测。