文章目录

Modbus RTU的CRC校验计算

发布于 2026-03-26 12:50:42 · 浏览 6 次 · 评论 0 条

Modbus RTU的CRC校验计算

Modbus RTU 协议依赖 CRC-16 校验来检测数据传输过程中的错误。只有当接收方计算出的 CRC 值与报文附带的 CRC 值完全一致时,才会处理该数据包。


基础参数准备

在开始计算前,明确以下核心参数,这是 Modbus RTU 标准规定的固定值:

  • 多项式0xA001(这是 $x^{16} + x^{15} + x^2 + 1$ 的反转二进制表示)。
  • 初始值0xFFFF
  • 输入反转:是。
  • 输出反转:是。
  • 结果异或值0x0000

计算逻辑流程

CRC 计算本质上是一个二进制除法过程的变种,通过异或和移位操作实现。为了直观理解,请参考以下处理流程:

graph TD A[Start: Init CRC = 0xFFFF] --> B[Input Next Byte] B --> C["CRC = CRC XOR Byte"] C --> D{Loop 8 times?} D -- No --> H{More Bytes?} D -- Yes --> E["Check LSB (Bit 0)"] E -- 1 --> F["Shift & XOR: (CRC >> 1) ^ 0xA001"] E -- 0 --> G["Shift Right: CRC >> 1"] F --> D G --> D H -- Yes --> B H -- No --> I["Swap Bytes: High/Low Exchange"] I --> J[End: Get CRC Value]

手把手计算步骤

假设我们要发送的数据帧为:01 03 00 00 00 01(从站地址1,功能码3,起始地址0,数量1)。下面演示如何计算该帧的 CRC 校验码。

  1. 初始化 CRC 寄存器为一个全 1 的 16 位二进制数,即 0xFFFF

  2. 第一个数据字节 0x01,将其与当前的 CRC 寄存器值进行异或(XOR)。

    • 0xFFFF XOR 0x01 = 0xFFFE
  3. 执行“移位与异或”循环,共重复 8 次(对应 8 个 bit):

    • 检查 CRC 寄存器的最低位(LSB,Bit 0)是否为 1。

    • 如果 LSB 为 1:

      1. CRC 寄存器向右移动一位(Shift Right)。
      2. 结果与多项式 0xA001 进行异或。
    • 如果 LSB 为 0:

      1. 仅将 CRC 寄存器向右移动一位。
    • 以第一步为例(0xFFFE):

      • LSB 是 0。
      • 右移一位:0x7FFF
      • (继续后续 7 次循环处理...)
  4. 处理完第一个字节的所有 8 次循环后,下一个数据字节 0x03,再次与当前的 CRC 寄存器值异或,并重复第 3 步的 8 次循环操作。

  5. 依次处理剩余的所有字节:0x000x000x000x01。每个字节都要经历“异或 -> 8次右移判断”的过程。

  6. 最终处理完所有数据字节后,CRC 寄存器中的值即为计算结果。假设此时内部计算值为 0x3401

  7. 交换高低字节位置。

    • 原结果:34 01(高字节 34,低字节 01)。
    • 交换后:01 34
    • 这是 Modbus RTU 协议特有的“低字节在前,高字节在后”传输规则。
  8. 追加到原报文末尾。

    • 最终发送帧:01 03 00 00 00 01 31 34(注:0x34 对应 ASCII 40x01 对应 ASCII 1,但在十六进制下通常写成 0x31 0x34 是十进制显示,十六进制显示为 0x01 0x34)。

Python 代码实现

为了在实际项目中快速应用,以下提供一段标准的 Python 脚本,可直接计算 Modbus RTU 的 CRC。

def calculate_crc(data_bytes):
    """
    计算 Modbus RTU 的 CRC-16 校验码
    :param data_bytes: 字节列表或字节对象,例如: b'\x01\x03\x00\x00\x00\x01'
    :return: 两个字节的 CRC 值 (低字节在前, 高字节在后)
    """
    crc_register = 0xFFFF
    polynomial = 0xA001

    for byte in data_bytes:
        # 将当前字节与 CRC 寄存器低 8 位异或
        crc_register ^= byte

        # 对该字节的 8 个 bit 逐位处理
        for _ in range(8):
            # 检查最低位是否为 1
            if (crc_register & 0x0001) != 0:
                # 右移一位后与多项式异或
                crc_register = (crc_register >> 1) ^ polynomial
            else:
                # 仅右移一位
                crc_register = crc_register >> 1

    # 返回两个字节,先低字节后高字节
    return bytes([crc_register & 0xFF, (crc_register >> 8) & 0xFF])

# 示例数据帧
frame_data = bytes([0x01, 0x03, 0x00, 0x00, 0x00, 01])

# 计算并获取 CRC
crc_result = calculate_crc(frame_data)

# 拼接完整报文
full_message = frame_data + crc_result

print(f"原始数据: {frame_data.hex(' ').upper()}")
print(f"CRC 校验码: {crc_result.hex(' ').upper()}")
print(f"完整报文: {full_message.hex(' ').upper()}")

验证与调试技巧

在实际开发中,如果 CRC 计算结果与设备要求不符,排查以下几点:

  1. 核对字节序:Modbus RTU 要求最终输出时“低字节在前”。如果你的计算结果是 0x34 0x1,发送时必须是 0x1 0x34

  2. 检查多项式:某些变种协议可能使用 0x8005,但标准 Modbus 必须使用 0xA001

  3. 确认数据类型:确保输入的是“原始数值”,而不是 ASCII 字符串。例如发送数字 1,是 0x01,而不是字符 '1' (0x31)。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文