Modbus RTU的CRC校验计算
Modbus RTU 协议依赖 CRC-16 校验来检测数据传输过程中的错误。只有当接收方计算出的 CRC 值与报文附带的 CRC 值完全一致时,才会处理该数据包。
基础参数准备
在开始计算前,明确以下核心参数,这是 Modbus RTU 标准规定的固定值:
- 多项式:
0xA001(这是 $x^{16} + x^{15} + x^2 + 1$ 的反转二进制表示)。 - 初始值:
0xFFFF。 - 输入反转:是。
- 输出反转:是。
- 结果异或值:
0x0000。
计算逻辑流程
CRC 计算本质上是一个二进制除法过程的变种,通过异或和移位操作实现。为了直观理解,请参考以下处理流程:
手把手计算步骤
假设我们要发送的数据帧为:01 03 00 00 00 01(从站地址1,功能码3,起始地址0,数量1)。下面演示如何计算该帧的 CRC 校验码。
-
初始化 CRC 寄存器为一个全 1 的 16 位二进制数,即
0xFFFF。 -
取第一个数据字节
0x01,将其与当前的 CRC 寄存器值进行异或(XOR)。0xFFFF XOR 0x01 = 0xFFFE。
-
执行“移位与异或”循环,共重复 8 次(对应 8 个 bit):
-
检查 CRC 寄存器的最低位(LSB,Bit 0)是否为 1。
-
如果 LSB 为 1:
- 将 CRC 寄存器向右移动一位(Shift Right)。
- 将结果与多项式
0xA001进行异或。
-
如果 LSB 为 0:
- 仅将 CRC 寄存器向右移动一位。
-
以第一步为例(
0xFFFE):- LSB 是 0。
- 右移一位:
0x7FFF。 - (继续后续 7 次循环处理...)
-
-
处理完第一个字节的所有 8 次循环后,取下一个数据字节
0x03,再次与当前的 CRC 寄存器值异或,并重复第 3 步的 8 次循环操作。 -
依次处理剩余的所有字节:
0x00、0x00、0x00、0x01。每个字节都要经历“异或 -> 8次右移判断”的过程。 -
最终处理完所有数据字节后,CRC 寄存器中的值即为计算结果。假设此时内部计算值为
0x3401。 -
交换高低字节位置。
- 原结果:
34 01(高字节34,低字节01)。 - 交换后:
01 34。 - 这是 Modbus RTU 协议特有的“低字节在前,高字节在后”传输规则。
- 原结果:
-
追加到原报文末尾。
- 最终发送帧:
01 03 00 00 00 01 31 34(注:0x34对应 ASCII4,0x01对应 ASCII1,但在十六进制下通常写成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 计算结果与设备要求不符,排查以下几点:
-
核对字节序:Modbus RTU 要求最终输出时“低字节在前”。如果你的计算结果是
0x34 0x1,发送时必须是0x1 0x34。 -
检查多项式:某些变种协议可能使用
0x8005,但标准 Modbus 必须使用0xA001。 -
确认数据类型:确保输入的是“原始数值”,而不是 ASCII 字符串。例如发送数字 1,是
0x01,而不是字符'1'(0x31)。

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