python crc-16 crc-16校验码 crc-16校验算法 ppp(MAC)帧检验序列FCS
想弄明白这里要看多几遍,配合下面的例子能更快理解。
第一、CRC-16校验码计算方法:
常用查表法和计算法。计算方法一般都是:
(1)、预置1个16位的寄存器为十六进制FFFF(即全为1),称此寄存器为CRC寄存器;
(2)、把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低
8位相异或,把结果放于CRC寄存器,高八位数据不变;
(3)、把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位;
(4)、如果移出位为0:重复第3步(再次右移一位);如果移出位为1,CRC寄存器与多
项式A001(1010 0000 0000 0001)进行异或;
(5)、重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
(6)、重复步骤2到步骤5,进行通讯信息帧下一个字节的处理;
(7)、将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低
字节进行交换;
(8)、最后得到的CRC寄存器内容即为:CRC码。
以上计算步骤中的多项式A001是8005按位颠倒后的结果。
8005(1000 0000 0000 0101)二进制旋转180度(按位颠倒)即为A001(1010 0000 0000 0001)
因为这是被除数右移,左移的话不用颠倒为8005。(相对来说就是除数左移颠倒,右移不用颠倒为8005)
除数也就是生成多项式,是有标准的。
为什么要设置crc寄存器初始值为FFFF?因为某些数据在前面加0但结果不变,所以加了点改动,CRC-16具体细分也有不同标准,就是CRC寄存器初始值,结果异或值,高低位在前在后不同等等。
我们这用MODBUS
前8位不动,后8位和传过来的数据的两位16进制数异或成新的crc值。
右移两位,因为移一位移出去的是0,第二位是1,然后异或
只要移出去的是0就继续移,直到是有个1移出去。下面是完整过程
移动8位结束,重新下一轮,结束时的crc值作为新一轮的初始值,下两位16进制数为:00
结果为F807
接下来是Python写的代码,这里是直接求值法,另外一种是查表法。
def calc_crc(string):
data = bytearray.fromhex(string)
crc = 0xFFFF#初始值
for pos in data:
crc ^= pos#每次两位16进制和crc低8位异或
for i in range(8):#右移八次
if ((crc & 1) != 0):#如果移出去的不是0(即移出去的是1)则右移1位且和A001异或。否则为0直接右移1位不作其他处理
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))[2:]#高低8位对调。
crc = calc_crc('3D00030007')
print('crc:'+crc)
bytearray.fromhex(string)
不好说,可以看下面两个链接自行体会。简单来说就是把传进来的16进制数string转化为数组,每两位16进制数作为一组数据,有些似乎因为ASCII码超出范围而不能用\x表示,用其ASCII值对应的字符表示,它们实际值是一样的。
crc ^= pos
即 crc= crc ^ pos 异或运算
crc >>= 1
即 crc = crc >>1 右移1位
if ((crc & 1) != 0):
& 在Python里(如果是数值的话)是位运算,都是1则为1,其他情况为0。crc & 1其实是crc & 0000 0001。crc末位为1则为1,意为移出去的是1;crc末位为0则为0,意为移出去的是0
最后一行hex(((crc & 0xff) << 8) + (crc >> 8))[2:]
[2:]意为选取结果的第2位到最后,从0开始,不写则结果为0x…
hex()返回16进制数
(crc & 0xff) << 8) 和0000 0000 1111 1111 并运算,高8位结果为0,低八位为原值,再左移8位。
结果由原来的0000 0000 xxxx xxxx变为xxxx xxxx 0000 0000
(crc >> 8) 右移8位,高八位变低八位,高八位可看成自动补0。
结果由原来的xxxx xxxx xxxx xxxx变为0000 0000 xxxx xxxx
相加得高低八位互换的效果。