本文介绍CRC算法的基本原理,并使用Python实现CRC16算法。
1 CRC基本概念
1.1 基本原理
CRC(Cyclic Redundancy Check),即循环冗余校验算法,可以校验数据在传输的过程中是否发送错误。其根本思想是在要发送的帧后面附加CRC校验码,生成一个新帧发送给接收端。校验码不是随意的,它要使所生成的新帧能与发送端和接收端共同选定的某个特定数整除(模2除法,相当于异或)。新帧到达接收端后,同样采用模2除法除以选定的特定数,如果余数为0,则表明该帧数据在传输过程中没有出现错误,否则表明数据在发送的过程中出现了错误。相当于原来的数无法被特定数整除,将其加上被特定数整除后的余数后,新生成的数据就应该能被特定数整除了。
1.2 计算过程
首先确定校验码的位数,比如4位,则生成多项式的X4和X0的系数必须为1,其它系数随意,此处选择生成多项式为X4+X3+1,所以特定数为11001。假设需要校验的数据为10110011,在数据的最后添加4个0,形成101100110000。将101100110000以模2除法的方式除以生成多项式11001,得到的余数0100即为CRC校验码(最高位的0不可省略),具体计算过程如下图所示。
2 Python实现CRC16算法
2.1 计算流程
实际使用的CRC算法与上述流程不同,CRC计算网站可以选择不同的生成多项式计算校验码,如下图所示,对应的计算流程如下:
- (1) 预置1个16位的寄存器为十六进制FFFF,称此寄存器为CRC寄存器。
- (2) 把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低8位相异或,把结果放于CRC寄存器,高八位数据不变。
- (3) 把CRC寄存器的内容右移一位,用0填补最高位,并检查右移后的移出位。
- (4) 如果移出位为0,重复步骤 (3);如果移出位为1,CRC寄存器与多项式A001(A001是8005按位颠倒后的结果)进行异或。
- (5) 重复步骤 (3) 和 (4),直到右移8次,这样整个8位数据全部进行了处理。
- (6) 重复步骤 (2) 到步骤 (5),进行通讯信息帧下一个字节的处理。
- (7) 将该通讯信息帧所有字节按上述步骤计算完成后,最后得到的CRC寄存器内容即为CRC码。
2.2 Python实现
# 输入数据和想要数据反转的长度,输出反转后的数据
# 如0x8005,二进制数为1000_0000_0000_0101,反转后的二进制数为1010_0000_0000_0001,对应0xa001
def reverse(din,len_din):
din_bin=bin(din) #整形转换为二进制数
din_bin_str=str(din_bin) #二进制数转换为字符串
dout_bin_str='' #输出的初始值为空
for i in range(len_din):
if (i < len(din_bin_str)-2): #去除0b后实际二进制数的长度,从后往前拼接,实现反转
dout_bin_str = dout_bin_str + (din_bin_str[len(din_bin_str)-i-1])
else: #不够长度补0
dout_bin_str = dout_bin_str + '0'
dout = int(dout_bin_str,2) #字符串转换为整形
return dout
crc16=0xffff #初始值
poly=0x8005 #多项式
poly=reverse(poly,16)
refin = 1 #输入数据反转
refout = 1 #输出数据反转
datas_str = "be a8 ce 15"
# ls=input().split()
ls = datas_str.split() # 以空格分割字符串
datas=list(ls)
for data_str in datas:
data=int(data_str,16)
if (refin == 0):
data = reverse(data,8)
crc16 = data ^ crc16
for i in range(8):
if 1&(crc16) == 1: #最低位为1
crc16 = crc16 >> 1
crc16 = crc16^poly
else:
crc16 = crc16 >> 1
if (refout == 0):
result = hex(reverse(crc16,16))
else:
result = hex(crc16)
print("CRC结果为", result)