python3 实现 CRC16 校简码生成

发布时间: 更新时间: 总字数:2018 阅读时间:5m 作者: IP上海 分享 网址

使用 Python 3 实现 CRC16 校验码的生成,主要涉及到位操作和对多项式的理解。下面我将提供一个通用的 CRC16 实现,并解释其中的关键概念。

CRC16 校验码生成原理

CRC(Cyclic Redundancy Check,循环冗余校验)是一种广泛使用的差错控制技术。CRC16 校验码的生成基于一个预设的生成多项式(Polynomial)。其基本思想是将要校验的数据看作一个二进制多项式,然后用生成多项式去除以这个数据多项式,得到的余数就是 CRC 校验码。

核心步骤:

  1. 初始化: CRC 寄存器通常初始化为 0x0000 或 0xFFFF,具体取决于所使用的 CRC16 变种。
  2. 数据处理: 每次处理一个字节的数据。
  3. 异或操作: 将当前字节与 CRC 寄存器的高位字节进行异或操作。
  4. 移位和条件异或: 将 CRC 寄存器左移一位。如果最高位(MSB)为 1,则将 CRC 寄存器与生成多项式进行异或操作。这个过程重复 8 次(对于每个字节的 8 位)。
  5. 循环: 对所有数据字节重复上述步骤。
  6. 结果处理: 最后得到的 CRC 寄存器中的值就是校验码。有些 CRC16 变种还会对最终结果进行异或或字节翻转。

常见的 CRC16 多项式和初始化值

CRC16 有多种变种,它们的主要区别在于使用的生成多项式、初始值、以及最终结果是否进行异或或字节翻转。以下是一些常见的 CRC16 变种:

  • CRC-16-CCITT (Kermit, X.25, V.41, IBM) (Most common):

    • 多项式:0x1021
    • 初始值:0x00000xFFFF
    • XOR out:0x0000
    • Reflect In/Out: 通常是 False / FalseTrue / True (视具体实现而定)
  • CRC-16-MODBUS:

    • 多项式:0x8005 (反转后是 0xA001)
    • 初始值:0xFFFF
    • XOR out:0x0000
    • Reflect In/Out: True / True
  • CRC-16-ARC (or CRC-16-IBM):

    • 多项式:0x8005
    • 初始值:0x0000
    • XOR out:0x0000
    • Reflect In/Out: True / True

Python3 实现 CRC16

下面我将提供一个通用的 CRC16 实现,你可以通过修改 POLYNOMIALINITIAL_VALUE 来适应不同的 CRC16 变种。这个实现是基于位操作,模拟了硬件层面的 CRC 计算。

def crc16(data: bytes, polynomial: int = 0x8005, initial_value: int = 0xFFFF, reflect_in: bool = True, reflect_out: bool = True, xor_out: int = 0x0000) -> int:
    """
    计算给定数据的 CRC16 校验码。

    Args:
        data: 要计算校验码的字节数据。
        polynomial: CRC16 生成多项式。例如,0x8005 (CRC-16-MODBUS) 或 0x1021 (CRC-16-CCITT)。
        initial_value: CRC 寄存器的初始值。例如,0xFFFF (CRC-16-MODBUS) 或 0x0000 (CRC-16-CCITT)。
        reflect_in: 输入字节是否反转。
        reflect_out: 最终结果是否反转。
        xor_out: 最终结果是否与此值进行异或。

    Returns:
        计算出的 16 位 CRC 校验码。
    """

    crc = initial_value

    for byte in data:
        if reflect_in:
            byte = reflect_byte(byte)  # 反转字节的位顺序

        crc ^= (byte << 8)  # 将当前字节与 CRC 寄存器的高位字节异或

        for _ in range(8):
            if (crc & 0x8000) != 0:  # 如果最高位是 1
                crc = (crc << 1) ^ polynomial
            else:
                crc = crc << 1
            crc &= 0xFFFF  # 确保 CRC 保持 16 位

    if reflect_out:
        crc = reflect_word(crc)  # 反转最终 CRC 结果的位顺序

    crc ^= xor_out
    return crc

def reflect_byte(byte: int) -> int:
    """反转一个字节的位顺序。"""
    reflected_byte = 0
    for i in range(8):
        if (byte >> i) & 1:
            reflected_byte |= (1 << (7 - i))
    return reflected_byte

def reflect_word(word: int) -> int:
    """反转一个 16 位字的位顺序。"""
    reflected_word = 0
    for i in range(16):
        if (word >> i) & 1:
            reflected_word |= (1 << (15 - i))
    return reflected_word

# --- 示例用法 ---

if __name__ == "__main__":
    # 示例 1: CRC-16-MODBUS
    # 多项式: 0x8005, 初始值: 0xFFFF, Reflect In/Out: True, XOR Out: 0x0000
    # 测试数据: 0x01 0x02 0x03 0x04 0x05 (ASCII 'abcde')
    # 预期结果: 0x9B10 (根据在线工具验证)
    data_modbus = b'\x01\x02\x03\x04\x05'
    crc_modbus = crc16(data_modbus, polynomial=0x8005, initial_value=0xFFFF, reflect_in=True, reflect_out=True, xor_out=0x0000)
    print(f"CRC-16-MODBUS for '{data_modbus.hex()}': 0x{crc_modbus:04X}")

    # 示例 2: CRC-16-CCITT (初始值 0x0000, 不反转)
    # 多项式: 0x1021, 初始值: 0x0000, Reflect In/Out: False, XOR Out: 0x0000
    # 测试数据: b"123456789"
    # 预期结果: 0x29B1 (根据在线工具验证)
    data_ccitt_0000 = b"123456789"
    crc_ccitt_0000 = crc16(data_ccitt_0000, polynomial=0x1021, initial_value=0x0000, reflect_in=False, reflect_out=False, xor_out=0x0000)
    print(f"CRC-16-CCITT (init 0x0000) for '{data_ccitt_0000.decode()}': 0x{crc_ccitt_0000:04X}")

    # 示例 3: CRC-16-CCITT (初始值 0xFFFF, 反转) ( often called CRC-16-X.25)
    # 多项式: 0x1021, 初始值: 0xFFFF, Reflect In/Out: True, XOR Out: 0xFFFF
    # 测试数据: b"123456789"
    # 预期结果: 0x906E (根据在线工具验证)
    data_ccitt_ffff_x25 = b"123456789"
    crc_ccitt_ffff_x25 = crc16(data_ccitt_ffff_x25, polynomial=0x1021, initial_value=0xFFFF, reflect_in=True, reflect_out=True, xor_out=0xFFFF)
    print(f"CRC-16-CCITT (init 0xFFFF, X.25) for '{data_ccitt_ffff_x25.decode()}': 0x{crc_ccitt_ffff_x25:04X}")

    # 示例 4: CRC-16-ARC (or CRC-16-IBM)
    # 多项式: 0x8005, 初始值: 0x0000, Reflect In/Out: True, XOR Out: 0x0000
    # 测试数据: b"123456789"
    # 预期结果: 0xBB3D (根据在线工具验证)
    data_arc = b"123456789"
    crc_arc = crc16(data_arc, polynomial=0x8005, initial_value=0x0000, reflect_in=True, reflect_out=True, xor_out=0x0000)
    print(f"CRC-16-ARC for '{data_arc.decode()}': 0x{crc_arc:04X}")

代码解释

  • crc16(data, polynomial, initial_value, reflect_in, reflect_out, xor_out) 函数:

    • data: 输入字节序列。
    • polynomial: CRC16 的生成多项式。
    • initial_value: CRC 寄存器的起始值。
    • reflect_in: 布尔值,指示每个输入字节的位是否需要反转。
    • reflect_out: 布尔值,指示最终的 CRC 结果的位是否需要反转。
    • xor_out: 最终 CRC 结果与此值进行异或。
    • 核心循环: 遍历输入数据的每个字节。
      • crc ^= (byte << 8): 将当前字节左移 8 位(使其与 CRC 寄存器的高位对齐),然后与 CRC 寄存器进行异或。
      • 内层循环(8 次): 模拟多项式除法的过程。
        • if (crc & 0x8000) != 0:: 检查 CRC 寄存器的最高位(第 15 位)是否为 1。如果为 1,则表示当前多项式除法有余数。
        • crc = (crc << 1) ^ polynomial: 将 CRC 寄存器左移一位,并与生成多项式进行异或。
        • else: crc = crc << 1: 如果最高位为 0,则直接左移一位。
        • crc &= 0xFFFF: 确保 CRC 寄存器始终保持在 16 位。
    • 反射和异或: 根据 reflect_outxor_out 参数对最终的 CRC 结果进行处理。
  • reflect_byte(byte)reflect_word(word) 函数:

    • 这两个辅助函数用于反转字节或字的位顺序。例如,0b10000000 反转后是 0b00000001。这在一些 CRC 变种中是必需的。

如何选择参数

在实际应用中,你需要根据所使用的 CRC16 协议(例如,MODBUS RTU、蓝牙、SD 卡等)来确定正确的 polynomialinitial_valuereflect_inreflect_outxor_out 参数。通常这些参数都会在协议规范中明确指出。

如果你不确定参数,可以尝试在网上搜索 “CRC-16 [协议名称]” 或 “CRC-16 [多项式] 参数” 来查找对应的配置。有许多在线 CRC 计算器也可以帮助你验证参数是否正确。

通过调整这些参数,你可以使用同一个 crc16 函数来实现各种 CRC16 校验码的生成。

在线工具

Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数