目录

前言

数据校验概念

CRC校验算法

CRC计算原理

CRC算法种类

代码实现CRC算法

python实现算法①

python实现算法②

总结


前言

在二次开发eCan上位机应用时,遇到了采用CRC(全称是循环冗余校验)32算法校验文件传输完整性的场景,浅浅地记录一下使用心得.

数据校验概念

数据在传输的过程中,会受到各种干扰的影响,如脉冲干扰,随机噪声干扰和人为干扰等,这会使数据产生差错。为了能够控制传输过程的差错,通信系统必须采用有效的检错方案。因此产生了数据校验。

数据校验是为保证数据的完整性进行的一种验证操作。通常用一种指定的算法对原始数据计算出的一个校验值,接收方用同样的算法计算一次校验值,如果两次计算得到的检验值相同,则说明数据是完整的。

CRC校验算法

CRC计算原理

被校验的数据 除以 多项式,得到的余数就是CRC数值。不过这里的除法是模2除法,也就是异或。多项式是通信中双方约定数,可以自己定义,不过目前有各个领域定义好的,可直接用。

CRC算法种类

{    //CRC算法名称宽度  多项式      初始值      结果异或值  输入反转    输出反转    {"CRC4_ITU", 4,     0x03,       0x00,       0x00,       E_TRUE,   E_TRUE},    {"CRC5_EPC", 5,     0x09,       0x09,       0x00,       E_FALSE,  E_FALSE},    {"CRC5_ITU", 5,     0x15,       0x00,       0x00,       E_TRUE,   E_TRUE},    {"CRC5_USB", 5,     0x05,       0x1F,       0x1F,       E_TRUE,   E_TRUE},    {"CRC6_ITU", 6,     0x03,       0x00,       0x00,       E_TRUE,   E_TRUE},    {"CRC7_MMC", 7,     0x09,       0x00,       0x00,       E_FALSE,  E_FALSE},    {"CRC8", 8,     0x07,       0x00,       0x00,       E_FALSE,  E_FALSE},    {"CRC8_ITU", 8,     0x07,       0x00,       0x55,       E_FALSE,  E_FALSE},    {"CRC8_ROHC", 8,     0x07,       0xFF,       0x00,       E_TRUE,   E_TRUE},    {"CRC8_MAXIM", 8,     0x31,       0x00,       0x00,       E_TRUE,   E_TRUE},    {"CRC16_IBM", 16,    0x8005,     0x0000,     0x0000,     E_TRUE,   E_TRUE},    {"CRC16_MAXIM", 16,    0x8005,     0x0000,     0xFFFF,     E_TRUE,   E_TRUE},    {"CRC16_USB", 16,    0x8005,     0xFFFF,     0xFFFF,     E_TRUE,   E_TRUE},    {"CRC16_MODBUS",16,    0x8005,     0xFFFF,     0x0000,     E_TRUE,   E_TRUE},    {"CRC16_CCITT", 16,    0x1021,     0x0000,     0x0000,     E_TRUE,   E_TRUE},    {"CRC16_CCITT_FALSE",16,    0x1021,     0xFFFF,     0x0000,     E_FALSE,  E_FALSE},    {"CRC16_X25", 16,    0x1021,     0xFFFF,     0xFFFF,     E_TRUE,   E_TRUE},    {"CRC16_XMODEM", 16,    0x1021,     0x0000,     0x0000,     E_FALSE,  E_FALSE},    {"CRC16_DNP", 16,    0x3D65,     0x0000,     0xFFFF,     E_TRUE,   E_TRUE},    {"CRC32", 32,    0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, E_TRUE,   E_TRUE},    {"CRC32_MPEG2", 32,    0x04C11DB7, 0xFFFFFFFF, 0x00000000, E_FALSE,  E_FALSE}};

代码实现CRC算法

要求:在这里,在IEEE 802.3 帧中采用的是CRC-32校验码,输出4个字节的校验码.

x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1

多项式按正常写法是0x04C11DB7 ,翻转则为 0xEDB88320

通常的CRC算法在计算一个数据段的CRC值时,其CRC值是由求解每个数值的CRC值的和对CRC寄存器的值反复更新而得到的。

python实现算法①

binascii刚好有现成的可以直接调用.输入要求的是二进制,那就用binascii.a2b_hex将16进制的字符串转换成二进制.

import binasciidef crc2hex(crc):    """    crc : str 从hex文件或bin文件中获取的有效数据           func a2b_hex 16进制字符串 -> 二进制           func crc32 二进制 计算得到crc值 -> int           最后和 0xffffffff 相乘得正值    return str 4个字节    """    # return '%08x' % (binascii.crc32(binascii.a2b_hex(crc)) & 0xffffffff)    # 增加一个 取反    return '%08x' % (binascii.crc32(binascii.a2b_hex(crc)) & 0xffffffff ^ 0xffffffff)

python实现算法②

CRC- 32的算法的多项式是0x04C11DB7 , 初始值默认是0xFFFFFFFF.

有多项式和初始值,就可以事先构造表.这里用查表法来减少计算量,

import re# 定义一个256个元素的全0数组custom_crc32_table = [0 for x in range(0, 256)]# 定义一个256个元素的全0数组reversal_crc32_table = [0 for x in range(0, 256)]# 一个8位数据加到16位累加器中去,只有累加器的高8位或低8位与数据相作用,# 其结果仅有256种可能的组合值。def generate_crc32_table():    for i in range(256):        c = i << 24        for j in range(8):            if (c & 0x80000000):                c = (c << 1) ^ 0x04C11DB7            else:                c = c << 1        custom_crc32_table[i] = c & 0xffffffffdef get_crc32_val(bytes_arr):    length = len(bytes_arr)    # print(f"data length {length}")    if bytes_arr != None:        crc = 0xffffffff        for i in range(0, length):            crc = (crc <> 24)) & 0xff]    else:        crc = 0xffffffff    # - 返回计算的CRC值    crc = getReverse(crc ^ 0xffffffff, 32)    return crc# 反转def getReverse(tempData, byte_length):    reverseData = 0    for i in range(0, byte_length):        reverseData += ((tempData >> i) & 1) <> 1) ^ 0xEDB88320            else:                c = c >> 1        reversal_crc32_table[i] = c & 0xffffffffdef reversal_getCrc32(bytes_arr):    length = len(bytes_arr)    if bytes_arr != None:        crc = 0xffffffff        for i in range(0, length):            crc = (crc >> 8) ^ reversal_crc32_table[(bytes_arr[i] ^ crc) & 0xff]    else:        crc = 0xffffffff    crc = crc ^ 0xffffffff    return crcif __name__ == '__main__':    import struct    import zlib    import binascii    s = struct.pack('>i', 400)    print('当前CRC输入初始值:', (s, type(s)))    test = binascii.crc32(s) & 0xffffffff    print('算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % test)))    test = zlib.crc32(s) & 0xffffffff    print('算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % test)))    buf_s = [0x00, 0x00, 0x01, 0x90]    generate_crc32_table()    crc_stm = get_crc32_val(bytearray(buf_s)) & 0xffffffff    print('算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % crc_stm)))    reversal_init_crc32_table()    crc_stm = reversal_getCrc32(bytearray(buf_s)) & 0xffffffff    print('反转算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % crc_stm)))    """    当前CRC输入初始值: (b'\x00\x00\x01\x90', )    算出来的CRC值: 0xc8507d19    算出来的CRC值: 0xc8507d19        算出来的CRC值: 0xc8507d19    反转算出来的CRC值: 0xc8507d19        """

总结

在数据传输前,CRC校验得到一个4字节的校验码.接收方对获取的有效数据进行同样的校验,若校验码一致,说明传输的数据是完整的.

CRC算法的种类多样,在算法上的差别在于多项式和初始值,这两个也可以自行定义.