第三章:手工计算——手算一个4位CRC校验码的完整过程

说实话,很多工程师学CRC,上来就啃数学公式,结果越看越懵。

我个人习惯是——先动手算一遍。哪怕是最简单的4位CRC,你亲手走一遍流程,比看十遍理论都管用。

这一章,我就带你手算一个完整的4位CRC校验码。你跟着我一步步来,保证能搞明白。

3.1 准备材料:我们需要什么?

先明确一下我们要用的参数:

  • 原始数据:二进制 1101 0011(8位)
  • 生成多项式10011(5位,对应CRC-4)
  • CRC宽度:4位
  • 初始值0000
  • 结果异或值0000

这里要提醒一句:生成多项式的位数 = CRC宽度 + 1。4位CRC,多项式就是5位。这个关系千万别搞反了,我在项目里见过有人拿4位多项式去算4位CRC,结果怎么都对不上。

3.2 第一步:数据补零

为什么要补零?

因为CRC校验码要放在数据末尾,相当于给数据腾出4个空位。补零的个数 = CRC宽度。

原始数据:1101 0011
补4个零后:1101 0011 0000

嗯,这一步很简单,但容易忘。我刚开始学的时候,就老忘记补零,算出来的结果自然不对。

3.3 第二步:模2除法(核心步骤)

模2除法说白了就是不带进位的二进制除法。你只需要记住一个规则:异或运算

我们拿补零后的数据 110100110000 除以多项式 10011

来,我写给你看:

        1101011010  ← 商(我们不关心)
        ---------
10011 ) 110100110000
        10011       ← 第一次异或
        -----
         10010
         10011      ← 第二次异或
         -----
          00011     ← 注意:这里不够5位,直接拉下一位
          00000     ← 不够除,商0
          -----
           00111
           00000    ← 不够除,商0
           -----
            01110
            00000   ← 不够除,商0
            -----
             11100
             10011  ← 第三次异或
             -----
              11110
              10011 ← 第四次异或
              -----
               11010
               10011
               -----
                10010
                10011
                -----
                 00010
                 00000
                 -----
                  00100
                  00000
                  -----
                   01000
                   00000
                   -----
                    10000
                    10011
                    -----
                     0011  ← 余数,这就是CRC校验码!

最终余数:0011

这就是我们手工算出来的4位CRC校验码。

3.4 第三步:结果处理

我们算出来的余数是 0011,但有些协议会要求对结果进行异或处理。本例中结果异或值为 0000,所以最终校验码就是 0011

发送方发送的数据就是:原始数据 + CRC校验码 = 1101 0011 0011

3.5 验证:接收方怎么做?

接收方拿到 110100110011,用同样的多项式 10011 再做一次模2除法。

如果余数为0,说明数据没问题。如果不为0,说明数据被篡改了。

我曾经在一个Modbus项目中遇到过这种情况:明明CRC算对了,但接收方总报错。后来发现是字节序搞反了——高位字节和低位字节的顺序不对。这种坑,你早晚也会遇到。

3.6 手算过程中的常见坑

避坑指南(我踩过的雷)

  • 补零个数搞错:4位CRC补4个零,不是5个。多项式位数是5,但补零看的是CRC宽度。
  • 异或时对齐位置:每次异或,多项式最高位必须对齐当前被除数的最高位。我刚开始手算时经常对歪,结果全错。
  • 余数位数:最终余数不足4位时,前面补0。比如余数是 11,要写成 0011
  • 初始值不为0的情况:有些协议初始值是 1111,那就要先把数据跟初始值异或,再做除法。这个我们后面章节会讲。

3.7 手算的意义

你可能会问:现在都有现成的库函数,为什么还要手算?

我的回答是:手算能帮你理解CRC的本质。当你遇到CRC校验不通过、需要调试协议栈时,手算能力就是你的救命稻草。

我记得有一次调试一个老旧的PLC设备,它的CRC实现跟标准不一样。我硬是拿笔在纸上算了一遍,才找到问题出在多项式顺序上。如果我不会手算,那天可能就卡死了。

3.8 小结

这一章我们手算了一个4位CRC校验码。核心就三步:

  1. 数据补零(补CRC宽度个零)
  2. 模2除法(用异或运算)
  3. 取余数作为校验码

下一章,我们会把这个手算过程用Python代码实现。到时候你会发现,代码其实就是把手工步骤翻译成计算机语言。

小提示:如果你手算时总出错,可以先用Python的 bin() 函数打印每一步的中间结果,对照着检查。我就是这么干的。

好了,这一章就到这里。拿起笔,找个数据自己算一遍,比看十遍都管用。