1. Modbus CRC16 算法规范
Modbus 用的 CRC 算法标准称为 "Modbus CRC-16" 或 "CRC-16-IBM",也叫 CRC-16-ANSI,多项式为:
多项式 = x 16 + x 15 + x 2 + 1 多项式=x^{16} + x^{15} + x^{2} + 1 多项式=x16+x15+x2+1
用 16 进制表示时,多项式是 0x8005(但注意,Modbus 是高位在前运算,这里 Modbus 采用的是位逆序后的 0xA001 进行右移计算)。
算法初始值和结果异或:
初始 CRC 值 = 0xFFFF
实际上,在 Modbus RTU 中,计算时每个字节先与 CRC 低字节异或,然后 CRC 右移 1 位,如果移出位是 1 则与多项式 0xA001 异或(这是 0x8005 的位逆序多项式)。计算完所有字节后,得到的 CRC 值在传输时是低字节在前(Little-endian 字节顺序)附加在帧末尾。
简单概括 :
取当前数据的 1 位,与 CRC 的低位(bit 0)异或,结果记为 xorBit,然后 CRC 右移 1 位,如果 xorBit为 0,继续取数据的下一位;如果 xorBit为 1,则 CRC 与多项式 0xA001 异或。
2. 对 01 03 00 00 00 01进行 CRC 计算
用这个算法来算。
字节 0x01:
crc = 0xFFFF ^ 0x01 = 0xFFFE (1111 1111 1111 1110)
循环 8 次(一次一位):
位 0:crc & 1 = 0 → 右移 1 位:0x7FFF
位 1:0x7FFF & 1 = 1 → 右移 1 位得 0x3FFF,然后异或 0xA001:
0x3FFF ^ 0xA001 = 0x9FFE
位 2:0x9FFE & 1 = 0 → 0x4FFF
位 3:0x4FFF & 1 = 1 → 右移:0x27FF ^ 0xA001 = 0x87FE
位 4:0x87FE & 1 = 0 → 0x43FF
位 5:0x43FF & 1 = 1 → 右移:0x21FF ^ 0xA001 = 0x81FE
位 6:0x81FE & 1 = 0 → 0x40FF
位 7:0x40FF & 1 = 1 → 右移:0x207F ^ 0xA001 = 0x807E
处理完字节 0x01 后,CRC = 0x807E。
字节 0x03:
crc ^= 0x03 → 0x807E ^ 0x03 = 0x807D
循环 8 次:
0x807D & 1 = 1 → 右移:0x403E ^ 0xA001 = 0xE03F
0xE03F & 1 = 1 → 右移:0x701F ^ 0xA001 = 0xD01E
0xD01E & 1 = 0 → 0x680F
0x680F & 1 = 1 → 右移:0x3407 ^ 0xA001 = 0x9406
0x9406 & 1 = 0 → 0x4A03
0x4A03 & 1 = 1 → 右移:0x2501 ^ 0xA001 = 0x8500
0x8500 & 1 = 0 → 0x4280
0x4280 & 1 = 0 → 0x2140
CRC = 0x2140
字节 0x00: (跟0异或结果是本身)
crc ^= 0x00 = 0x2140
循环 8 次(每次 crc & 1 都是 0,因为 0x2140 二进制 0010 0001 0100 0000,最低位是 0,但右移过程中最低位会变化,我们仔细算):
0x2140 & 1 = 0 → 0x10A0
0x10A0 & 1 = 0 → 0x0850
0x0850 & 1 = 0 → 0x0428
0x0428 & 1 = 0 → 0x0214
0x0214 & 1 = 0 → 0x010A
0x010A & 1 = 0 → 0x0085
0x0085 & 1 = 1 → 右移:0x0042 ^ 0xA001 = 0xA043
0xA043 & 1 = 1 → 右移:0x5021 ^ 0xA001 = 0xF020
CRC = 0xF020
字节 0x00:
crc ^= 0x00 = 0xF020
循环:
0xF020 & 1 = 0 → 0x7810
0x7810 & 1 = 0 → 0x3C08
0x3C08 & 1 = 0 → 0x1E04
0x1E04 & 1 = 0 → 0x0F02
0x0F02 & 1 = 0 → 0x0781
0x0781 & 1 = 1 → 右移:0x03C0 ^ 0xA001 = 0xA3C1
0xA3C1 & 1 = 1 → 右移:0x51E0 ^ 0xA001 = 0xF1E1
0xF1E1 & 1 = 1 → 右移:0x78F0 ^ 0xA001 = 0xD8F1
CRC = 0xD8F1
字节 0x00:
crc ^= 0x00 = 0xD8F1
循环:
0xD8F1 & 1 = 1 → 右移:0x6C78 ^ 0xA001 = 0xCC79
0xCC79 & 1 = 1 → 右移:0x663C ^ 0xA001 = 0xC63D
0xC63D & 1 = 1 → 右移:0x631E ^ 0xA001 = 0xC31F
0xC31F & 1 = 1 → 右移:0x618F ^ 0xA001 = 0xC18E
0xC18E & 1 = 0 → 0x60C7
0x60C7 & 1 = 1 → 右移:0x3063 ^ 0xA001 = 0x9062
0x9062 & 1 = 0 → 0x4831
0x4831 & 1 = 1 → 右移:0x2418 ^ 0xA001 = 0x8419
CRC = 0x8419
字节 0x01:
crc ^= 0x01 = 0x8418
循环:
0x8418 & 1 = 0 → 0x420C
0x420C & 1 = 0 → 0x2106
0x2106 & 1 = 0 → 0x1083
0x1083 & 1 = 1 → 右移:0x0841 ^ 0xA001 = 0xA840
0xA840 & 1 = 0 → 0x5420
0x5420 & 1 = 0 → 0x2A10
0x2A10 & 1 = 0 → 0x1508
0x1508 & 1 = 0 → 0x0A84
CRC = 0x0A84
最终 CRC = 0x0A84
3. 传输顺序
CRC 计算结果 0x0A84 在传输时,低字节在前,即先发 0x84,再发 0x0A。
所以整帧为:
yaml
01 03 00 00 00 01 84 0A
4. CRC(循环冗余校验)在线计算
