Modbus CRC-16 校验:计算校验码与检查完整 RTU 报文
从一条读取请求算出 Modbus CRC-16,说明 0BC4 与 C4 0B 的区别,以及怎样检查已经带有 CRC 的 RTU 报文。
Modbus CRC-16 校验:计算校验码与检查完整 RTU 报文
同一条 RTU 请求,CRC 计算器显示 0BC4,串口记录的末尾却是 C4 0B。这两个结果可以同时正确:前者把校验码写成一个 16 位数值,后者按实际传输顺序列出两个字节。核对 CRC 时,除了看数值,还要确认计算器用了哪些输入字节。
下面这条 FC03 请求读取设备 01 从偏移 0000 开始的两个寄存器。把计算前后的内容放在一起,就能看清 CRC 应该加在哪里。
| 内容 | 字节或数值 |
|---|---|
| 参与计算的六个字节 | 01 03 00 00 00 02 |
| 16 位 CRC 数值 | 0BC4 |
| 报文末尾的 CRC 字节 | C4 0B |
| 带 CRC 的完整请求 | 01 03 00 00 00 02 C4 0B |
计算器里应该填哪一段
打开 RTU CRC 计算器,填入 01 03 00 00 00 02,结果会显示为 C4 0B。这里采用的就是追加到报文末尾的字节顺序。
CRC 要覆盖校验码前面的全部报文字节。开头的设备地址 01 和功能码 03 都在计算范围内,不能只取寄存器数据,也不能只取不含设备地址的 PDU。如果从日志中复制的是完整请求,应先分离末尾已有的两个 CRC 字节;如果还在组装请求,则直接使用尚未添加 CRC 的内容。
空格只是字节分隔符,时间戳、TX: 标签和其他日志列都不属于输入。保留一份原始记录再整理文本,比较容易发现漏字节,或把请求和响应连在一起的情况。改过设备地址、起始偏移或读取数量,也要根据修改后的内容重新计算 CRC。
0BC4 为什么写成 C4 0B
0BC4 的高字节是 0B,低字节是 C4。RTU 规定 CRC 先传低字节,再传高字节,所以报文尾部依次写 C4 0B。这个规则见串行线路指南第 2.5.1.2 节。它只说明 CRC 字段的发送顺序,不能据此把整条报文中每两个字节都交换一次。
对照两个工具的结果时,先看它们显示的是 CRC 数值,还是已经排好顺序的报文字节。一个显示 0BC4,另一个显示 C4 0B,并不足以判断其中一个算错了。
手工计算或核对代码时,CRC 寄存器从 FFFF 开始,依次与输入字节异或;每个字节处理八次右移,移出的最低位为一时,与反向多项式 A001 异或。处理完本例的六个字节后,寄存器值为 0BC4。串行线路指南第 6.2.2 节给出了完整算法。
已经带 CRC 的报文怎样检查
对于 01 03 00 00 00 02 C4 0B 这样的完整记录,可以直接粘贴到 RTU 报文解析器,一起查看请求字段和校验结果。若要手工核对,就取前六个字节重新计算,再把结果按传输顺序与末尾的 C4 0B 比较。
把这八个字节全部送入同一 CRC 算法,会得到余数 0000,按两个字节显示就是 00 00。这可以用于整帧余数检查,但不能把结果再次追加到原报文后面,否则八字节请求就成了十字节。计算器只负责处理输入字节,报文长度和字段是否合理还需要另外检查。
CRC 匹配也不能证明寄存器地址选对了。一个读错寄存器的请求,同样可以带有正确的 CRC。功能码、起始偏移和数量仍要对照设备手册;如果疑问在于 40001 这样的编号如何填写,可接着看寄存器地址指南。如果响应通过校验但读出的值不合理,则先确认它对应哪条请求,再检查数据类型、寄存器顺序、缩放和单位,RTU 读取实操有相应的只读示例。