Modbus CRC字节序:分清线缆校验与寄存器端序

如果CRC计算覆盖的字节范围已经错误,修改浮点数的字序设置并不能修复问题。Modbus RTU同时存在多种顺序规则:字段内部字节序、厂商定义的跨寄存器字序、CRC计算状态,以及校验值在线上的两字节排列。有效排查必须分别命名、分别验证。

本文以串行链路规范V1.02和应用协议V1.1b3为边界。样例是可复现的软件计算,不是仪表抓包;它们只能验证校验行为,不能证明电气链路可靠或寄存器映射正确。

CRC覆盖RTU地址和PDU字节,结果按低字节先发排列;寄存器解析必须在帧校验之后进行。
原创校验边界图:CRC发送顺序与寄存器数据解释是独立操作。

先确认当前封装确实使用RTU CRC

RTU在串口ADU末尾使用两字节CRC;Modbus ASCII使用LRC,标准Modbus TCP则使用MBAP,不附加RTU CRC。厂商私有隧道可能完整携带RTU帧。因此,先确认工作模式,再决定尾部哪些字节属于校验。

RTU计算覆盖地址、功能码和数据,不包括正在生成的CRC本身。UART起始位、奇偶校验位和停止位也不参与计算,依据串行规范第2.5.1节与附录B。日志中的时间戳、空格和十六进制显示字符都要去掉:文本字符“零”“二”的编码,并不是单个0x02字节。

明确计算函数返回什么

直接的反射位运算实现从0xFFFF开始,每个输入字节经过八次右移,并按移出的最低位决定是否异或0xA001。若接口返回的是数值累加器,发送时应明确取低八位,再取右移八位后的高字节。不要把本机整数内存直接复制到发送缓冲区。

某些旧式查表函数或接口已经预先安排过字节表示。不能只看函数叫CRC16,就再加一次交换。应分别检查文档约定的返回数值和实际发出的两个字节。CPU内存端序、打印出的十六进制顺序和线序是三个不同观察对象,在某台开发机上偶然一致也不代表可移植。

用规范短例建立独立判据

附录B逐步计算了输入02 07。按上述数值累加器约定,结果为0x1241,线上应发送41 12,完整示例帧为02 07 41 12。这个样例较短,两个CRC字节又不同,适合固定为回归样本,并与规范中间步骤逐项对照。

第二个独立计算的样例是01 03 00 00 00 01,数值CRC为0x0A84,线上末尾为84 0A。测试记录应把六个输入字节、数值结果和完整八字节帧分开存储。仅比较格式化字符串,可能掩盖某个错误序列化步骤又被后续交换抵消的情况。

先校验,再解释寄存器

应用协议第4.2节规定,十六位寄存器数值的高字节先发送。跨两个寄存器的三十二位数据,还需另外确认设备文档中的寄存器字序。二者都不会改变RTU CRC低字节先发送的规则。

接收端应先验证原始帧,再把寄存器字节转成整数或浮点数。若先交换负载再计算CRC,校验的就不是实际收到的消息。反过来,CRC正确的报文仍可能因为映射、符号位或缩放错误而显示错误数值。应报告“校验通过,数据解释待确认”,而不是调整校验算法让数值看起来合理。

用字节证据定位错误范围

保存原始长度、参与计算的字节、收到的CRC字节对和计算结果。如果多组样例都只表现为CRC两字节颠倒,优先检查返回约定和发送序列化。若地址被漏算,或最后一个数据字节误当成CRC,问题在覆盖边界,不能直接诊断为串口噪声。

发送缓冲区的软件计算正确,而接收端重组后失败时,应比较两端字节数组和帧边界。漏字节、插入字节及多帧拼接都可能导致失败。CRC本身不能区分UART溢出、干扰、解析器错误或发送端错误,应保留底层证据,再逐项验证原因。

让回归样例揭示实现捷径

用简单位运算参考实现与生产实现分别计算前述两个样例。把输入在每一个可能位置拆成两段,第二段继续使用上一段累加器,结果应与一次性计算一致。这验证增量状态维护,不代表已经验证串口字符时序。

对这些短帧逐一翻转每个比特,检查接收函数拒绝;再加入缺首字节、缺尾字节和交换CRC的情况。每个错误帧之后都再输入一个正确帧,以发现累加器未重置的问题。空缓冲区可以测试函数初值,但不能被接收为有效Modbus消息。校验通过与协议长度合法应分别断言。

以明确边界结束验收

  • 实现写清适用帧格式及校验覆盖范围。
  • 数值CRC与低字节先发分别通过测试。
  • 规范样例和构造样例都在接入前固定为回归项。
  • 分段计算与一次性计算结果相同。
  • 损坏、截断和线序错误样本不会发布寄存器数据。

CRC用于发现传输错误,不是身份认证,也不能证明应用语义正确。发送方完全可能为错误命令生成正确CRC。覆盖范围和发送顺序确认后,剩余的数据映射或物理链路问题应转入相应测试,避免无限修改同一个校验函数。

类似文章