起因

工控和嵌入式项目里选通信协议,十有八九绕不开 Modbus:下位机一边可能是 RS485 总线上的仪表/变频器/PLC,另一边可能是走以太网的运动控制卡或上位机软件,同一个项目里 RTU 和 TCP 混着用是常态。要正确选型、写对接代码,就得先把两者的关系和差别讲清楚。

需求

  • 弄清 Modbus RTU 和 Modbus TCP 的本质关系:哪些东西是一样的,哪些不一样
  • 弄清各自怎么定界、怎么校验,为什么 TCP 不要 CRC
  • 顺带知道 ASCII 模式是什么位置,以及协议转换器为什么能轻松互转

技术实现原理

本质是同一个协议:应用层的寄存器模型不变

Modbus rtu和 Modbus tcp两个协议的本质都是 MODBUS协议,都是靠MODBUS寄存器地址来交换数据。也就是说,应用层的东西完全一样:0x01 读线圈、0x03 读保持寄存器、0x10 写多个寄存器……这些功能码和寄存器编址(线圈/离散输入/输入寄存器/保持寄存器四个空间)在两种模式下毫无差别。

变的只是"信封":所用的硬件接口不一样,Modbus RTU一般采用串口RS232C或RS485/422,而Modbus TCP一般采用以太网口。实际上Modbus协议包括ASCII、RTU、TCP三种模式,前两种跑串口,第三种跑 TCP。

串口侧:主从问答 + 字节流定界的难题

标准的Modicon控制器使用RS232C实现串行的Modbus。Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和应答的方式,数据通讯采用Master/Slave方式——同一时刻只有一个主机,主机发一问、从机回一答,从机永远不主动说话(这条规则是 RS485 半双工总线决定的:两方同时开口就撞车)。

串口是纯字节流,天然没有"帧边界",两种模式用了两种办法解决:

  • RTU:二进制帧,靠静默时间定界——总线上空闲至少 3.5 个字符时间(3.5T)算一帧结束,帧内字符间隔不得超过 1.5T。这也是 RTU 实现里最常见的坑的来源(见文末);
  • ASCII:把每个字节拆成两个可打印的十六进制字符发送,帧头 :、帧尾 CRLF,靠这两个可见字符定界。调试时肉眼能读是它唯一的优点,效率减半是代价。

校验:LRC、CRC16,以及 TCP 为什么不要

Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验。原因很直接:RS232/RS485 是裸电气链路,没有任何底层协议帮你查错,错一个 bit 就是错一个寄存器值,应用层必须自己兜底(RTU 的 CRC-16 检错能力远强于 LRC,这也是二进制模式敢用、还成为主流的原因)。

ModbusTCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议——TCP 首部自带校验和,丢包、错序、重复都由传输层重传机制兜住,底下以太网帧还有 FCS(CRC32)再查一道。链路和应用之间已经有两层校验,再在 Modbus 帧里加 CRC 就是重复劳动。

TCP 侧:MBAP 头替换了"地址 + CRC"

Modbus TCP 的帧 = MBAP 头(7 字节)+ PDU(功能码+数据,与 RTU 的 PDU 完全一致):

字段 字节数 作用
事务标识 Transaction ID 2 请求响应配对编号,客户端自增
协议标识 Protocol ID 2 恒为 0(留给扩展)
长度 Length 2 后面还有多少字节
单元标识 Unit ID 1 对应 RTU 的从站地址

TCP 是字节流同样没有边界,但 MBAP 里有长度域,接收方读定长头、再按长度读体即可精确切帧——不再需要 3.5T 静默那套靠时间定界的做法。

网络上流传的"TCP和RTU协议非常类似,只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可"就是这么来的:PDU 部分原封不动,RTU 尾上的 2 字节 CRC 去掉,头上加的 MBAP 前 6 字节在事务标识从 0 开始时正好是 00 00 00 00 00 06——“5 个 0 和一个 6”。其中 0006 是长度域(Unit ID 1 字节 + 典型 PDU 5 字节)。这是速记口诀,写代码时还是应该老老实实按 MBAP 七字节结构拼,事务标识要用响应里的值核对配对。

另外两个实际差别值得知道:

  • TCP 模式官方叫 Client/Server(不再叫主从),但问答逻辑不变:Client 发请求、Server 应答;
  • RTU 从站地址 1~247 有效(0 是广播);到了 TCP 侧这个地址变成 Unit ID。直连设备时它常被忽略,但经过 TCP↔RTU 网关时,Unit ID 就是网关转发到串口侧的从站地址,填 0 广播、填错路由就到不了目标从机。

协议转换为什么容易

正因为 PDU 层完全一致,转换器只需要换信封:拆掉 MBAP、补上从站地址和 CRC、控制串口收发时序,反之亦然。现在市场上有很多协议转换器,可以轻松的将这些不同的协议相互转换,如:Intesisbox可以把 modbus rtu转换成 Modbus tcp。

踩坑记录与注意事项

  • RTU 发送必须"一气呵成":帧内字符间隔超过 1.5T 会被接收方判为帧结束,剩下半截当成新帧解析直接丢。用 OS 或高负载 MCU 发送时,中途被调度打断就会触发,这也是"偶发丢帧"的头号根因;
  • 3.5T 在高波特率下极短:115200bps 时 3.5T 约 0.4ms,RTOS 的定时粒度不够细就定不准帧边界;反之低波特率时 3.5T 又很长(9600bps 约 4ms),收发节奏要留够;
  • CRC 字节序是低字节在前:与很多协议相反,拼错字节序后校验永远不过,抓包一眼就能看出来;
  • TCP 模式要核对事务标识:TCP 允许并发挂多个请求,响应靠 Transaction ID 配对,只发不核对在并发场景会张冠李戴;
  • Unit ID 过网关时别乱填:见上文,它是串口侧从站地址的映射;
  • 单帧读寄存器上限 125 个:PDU 最大 253 字节限制,超了要分次读,库封装好的一般看不到这条,手写帧就会踩到;
  • RTU 没有连接概念:从机不在线只能靠主机的超时判断,超时时间要大于从机的最长处理时间,否则会重复请求。