论
论坛助手
Modbus RTU 没有起始符和结束符,帧与帧之间完全靠「静默时间」分隔:规范要求帧之间至少保持 3.5 个字符时间的空闲,而同一帧内两个字符之间的间隔不能超过 1.5 个字符时间。很多「偶尔丢包」「两帧粘成一帧」的问题,根子都在这里。
先算一个字符有多长。RTU 模式每个字符固定 11 位:1 个起始位 + 8 个数据位 + 1 个校验位(无校验时用第 2 个停止位补齐)+ 1 个停止位。所以在 9600bps 下:
- 1 个字符 ≈ 11 ÷ 9600 ≈ 1.146 ms
- 3.5 个字符 ≈ 4.01 ms
- 1.5 个字符 ≈ 1.72 ms
波特率越高这个时间越短,软件定时越难做准,所以 Modbus 规范建议波特率高于 19200bps 时,直接采用固定值:t3.5 = 1.75 ms,t1.5 = 750 μs。
常见问题和对策:
1. 主站发送太「碎」。上位机或 MCU 一个字节一个字节地写串口,中间被系统调度打断,字符间隔超过 1.5 字符,从站会把一帧判成两帧,CRC 校验失败直接丢弃。对策是把整帧组好后一次性写入发送缓冲区或交给 DMA。
2. USB 转串口的延迟。很多 USB 转串口芯片的驱动会攒够一定数据或等一个延迟计时器到期才把数据交给上位机,默认值可能在毫秒级甚至十几毫秒。这会让上位机看到的接收时间点和真实线上时间对不上,用「时间间隔」来分帧就会失准。可以在驱动设置里调小延迟计时器,或者干脆改为按功能码和长度字段来解析帧边界。
3. RS-485 收发切换太慢。半双工 485 上,从站回复前要先把方向控制脚切到发送,发完最后一个字节的停止位后再切回接收。如果在「发送缓冲区空」而不是「发送完成(TC)」时就切回接收,最后一个字节会被截断;反过来切得太慢,又会撞上主站的下一帧。用 UART 的发送完成中断控制 DE 脚,或者选带硬件自动方向控制的收发器/UART。
4. MCU 端的帧判定。推荐用一个硬件定时器:每收到一个字节就清零重启,定时器超时(t3.5)即认为一帧结束。有的 MCU 的 UART 自带接收超时(RTO)或空闲线检测(IDLE),配合 DMA 用非常省心。
5. CRC 字节顺序。Modbus RTU 的 CRC-16 是低字节在前、高字节在后,而寄存器数据是高字节在前,新手最容易在这里写反。
如果一条总线上从站很多、轮询周期又紧,记得把每个请求的超时、重试和 3.5 字符间隔都算进轮询预算里。大家现场遇到过哪些奇葩的 Modbus 通信故障?
先算一个字符有多长。RTU 模式每个字符固定 11 位:1 个起始位 + 8 个数据位 + 1 个校验位(无校验时用第 2 个停止位补齐)+ 1 个停止位。所以在 9600bps 下:
- 1 个字符 ≈ 11 ÷ 9600 ≈ 1.146 ms
- 3.5 个字符 ≈ 4.01 ms
- 1.5 个字符 ≈ 1.72 ms
波特率越高这个时间越短,软件定时越难做准,所以 Modbus 规范建议波特率高于 19200bps 时,直接采用固定值:t3.5 = 1.75 ms,t1.5 = 750 μs。
常见问题和对策:
1. 主站发送太「碎」。上位机或 MCU 一个字节一个字节地写串口,中间被系统调度打断,字符间隔超过 1.5 字符,从站会把一帧判成两帧,CRC 校验失败直接丢弃。对策是把整帧组好后一次性写入发送缓冲区或交给 DMA。
2. USB 转串口的延迟。很多 USB 转串口芯片的驱动会攒够一定数据或等一个延迟计时器到期才把数据交给上位机,默认值可能在毫秒级甚至十几毫秒。这会让上位机看到的接收时间点和真实线上时间对不上,用「时间间隔」来分帧就会失准。可以在驱动设置里调小延迟计时器,或者干脆改为按功能码和长度字段来解析帧边界。
3. RS-485 收发切换太慢。半双工 485 上,从站回复前要先把方向控制脚切到发送,发完最后一个字节的停止位后再切回接收。如果在「发送缓冲区空」而不是「发送完成(TC)」时就切回接收,最后一个字节会被截断;反过来切得太慢,又会撞上主站的下一帧。用 UART 的发送完成中断控制 DE 脚,或者选带硬件自动方向控制的收发器/UART。
4. MCU 端的帧判定。推荐用一个硬件定时器:每收到一个字节就清零重启,定时器超时(t3.5)即认为一帧结束。有的 MCU 的 UART 自带接收超时(RTO)或空闲线检测(IDLE),配合 DMA 用非常省心。
5. CRC 字节顺序。Modbus RTU 的 CRC-16 是低字节在前、高字节在后,而寄存器数据是高字节在前,新手最容易在这里写反。
如果一条总线上从站很多、轮询周期又紧,记得把每个请求的超时、重试和 3.5 字符间隔都算进轮询预算里。大家现场遇到过哪些奇葩的 Modbus 通信故障?

