4、通信协议解析(上):Modbus RTU/TCP协议原理、数据帧结构、地址映射
各位同学,咱们今天聊聊Modbus。说实话,这玩意儿在工业现场太常见了。我做了十几年SCADA集成,几乎每个项目都绕不开它。你想想看,从PLC到变频器,从温控表到智能电表,Modbus就像工业界的“普通话”。
为什么先讲Modbus?因为它是工业物联网网关最基础、最通用的协议。你搞懂了Modbus,其他协议上手会快很多。今天咱们重点讲RTU和TCP两种模式,把原理、帧结构、地址映射这些核心东西掰开揉碎了说清楚。
4.1 Modbus协议的前世今生
Modbus是1979年Modicon公司(就是现在的施耐德电气)发明的。最初就是为了让PLC和智能设备之间能通信。说白了,它就是个主从架构的协议——一个主站(Master)问,多个从站(Slave)答。
我个人习惯把Modbus分成三类:
- Modbus RTU:串行通信,二进制编码,效率高。现场最常见。
- Modbus ASCII:串行通信,ASCII编码,可读性好但效率低。现在用得少了。
- Modbus TCP:基于以太网,封装在TCP/IP包里。现代工厂越来越普及。
嗯,这里要注意:RTU和TCP虽然名字不同,但核心的数据模型和功能码是完全一样的。区别只在于传输层和封装方式。我在项目中遇到过不少新手,以为RTU和TCP是两套完全不同的协议,其实不然。
4.2 Modbus RTU协议原理与数据帧结构
先讲RTU。它跑在RS-232或RS-485物理层上。RS-485用得最多,因为支持多站、抗干扰能力强。我建议你在现场布线时,尽量用双绞屏蔽线,接地要做好。有一次我在一个钢厂项目里,就因为485线没屏蔽,数据乱得一塌糊涂。
4.2.1 RTU数据帧结构
RTU的帧结构非常紧凑。一个完整的RTU报文长这样:
| 地址码(1字节) | 功能码(1字节) | 数据区(N字节) | CRC校验(2字节) |
咱们拆开来看:
- 地址码:从站地址,范围1-247。0是广播地址,248-255保留。
- 功能码:告诉从站要干什么。比如03是读保持寄存器,06是写单个寄存器。
- 数据区:具体参数,比如起始地址、寄存器数量、要写入的数据等。
- CRC校验:循环冗余校验,确保数据在传输过程中没被干扰。
关键点:RTU帧里没有帧头和帧尾标记。它靠“静默时间”来区分报文。也就是说,如果总线上超过3.5个字符时间没有数据传输,就认为上一帧结束了。这个时间参数在网关配置时一定要设对,否则容易粘包。
4.2.2 常用功能码
功能码是Modbus的灵魂。我整理了一张表,这些是你在项目中几乎天天要用的:
| 功能码 | 名称 | 作用 | 数据模型 |
|---|---|---|---|
| 01 | 读线圈 | 读取DO(数字量输出) | 线圈 |
| 02 | 读离散输入 | 读取DI(数字量输入) | 离散输入 |
| 03 | 读保持寄存器 | 读取AO(模拟量输出)或参数 | 保持寄存器 |
| 04 | 读输入寄存器 | 读取AI(模拟量输入) | 输入寄存器 |
| 05 | 写单个线圈 | 控制单个DO | 线圈 |
| 06 | 写单个寄存器 | 设置单个AO或参数 | 保持寄存器 |
| 15 | 写多个线圈 | 批量控制DO | 线圈 |
| 16 | 写多个寄存器 | 批量设置参数 | 保持寄存器 |
你想想看,03和04有什么区别?03读的是保持寄存器,可以读写;04读的是输入寄存器,只能读不能写。我在调试一个水处理项目时,就遇到过有人用04去读PLC里设的参数,结果死活读不到——因为参数存在保持寄存器里。
4.2.3 RTU报文示例
咱们看个实际例子。假设你要读取地址为01的从站,从寄存器地址100开始读2个寄存器:
主站发送:01 03 00 64 00 02 C4 0B
从站回复:01 03 04 00 0A 00 14 7A 3B
拆解一下:
- 发送帧:01(地址) 03(功能码) 00 64(起始地址100) 00 02(读2个) C4 0B(CRC)
- 回复帧:01(地址) 03(功能码) 04(数据长度4字节) 00 0A(寄存器1的值=10) 00 14(寄存器2的值=20) 7A 3B(CRC)
注意,Modbus寄存器是16位的,所以每个寄存器占2字节。数据采用大端模式,高位在前。这个细节容易搞错,我曾经在对接一个进口设备时,因为字节序没搞对,读出来的数据全是乱的。
4.3 Modbus TCP协议原理与数据帧结构
Modbus TCP就是把Modbus报文装进TCP/IP包里。它去掉了CRC校验(因为TCP本身有校验),加了一个MBAP报文头。说白了,就是给Modbus报文穿了个“网络外套”。
4.3.1 TCP数据帧结构
Modbus TCP的帧结构:
| MBAP头(7字节) | 功能码(1字节) | 数据区(N字节) |
MBAP头包含:
- 事务处理标识符(2字节):用于匹配请求和响应。我习惯从0开始递增。
- 协议标识符(2字节):固定为0x0000,表示Modbus协议。
- 长度(2字节):后续字节数(包括功能码和数据区)。
- 单元标识符(1字节):相当于RTU里的从站地址。在TCP里,这个字段用于区分网关后面的设备。
实战技巧:在工业物联网网关里,Modbus TCP的单元标识符特别有用。你可以用一个网关IP,通过不同的单元标识符去访问后面挂载的多个RTU设备。我做过一个项目,一个网关接了32个串口设备,就是靠单元标识符来区分的。
4.3.2 TCP报文示例
同样的需求——读地址01的从站,寄存器100开始读2个:
主站发送:00 01 00 00 00 06 01 03 00 64 00 02
从站回复:00 01 00 00 00 07 01 03 04 00 0A 00 14
拆解:
- 发送帧:00 01(事务ID) 00 00(协议ID) 00 06(长度=6) 01(单元ID) 03(功能码) 00 64(地址) 00 02(数量)
- 回复帧:00 01(事务ID) 00 00(协议ID) 00 07(长度=7) 01(单元ID) 03(功能码) 04(数据长度) 00 0A(值1) 00 14(值2)
看到没?TCP帧里没有CRC,但多了MBAP头。事务ID必须对应,否则从站不会认。我调试时经常用Wireshark抓包,一看事务ID不对,就知道是哪里出问题了。
4.4 地址映射——网关的核心工作
好了,协议原理讲完了。现在说说地址映射。这是工业物联网网关最核心的功能之一。说白了,就是把Modbus的寄存器地址,映射到SCADA系统能理解的标签(Tag)上。
4.4.1 地址映射的三种方式
我总结了一下,常见的映射方式有三种:
- 直接映射:Modbus地址直接对应SCADA标签。比如保持寄存器40001对应“温度1”。简单粗暴,适合小系统。
- 偏移映射:在Modbus地址上加一个偏移量。比如PLC里地址从0开始,SCADA里从40001开始。很多PLC就是这么干的。
- 计算映射:对原始数据进行运算后再映射。比如把两个16位寄存器拼成一个32位浮点数,或者把原始值乘以系数得到工程值。
注意:地址映射最容易出问题的地方是“地址偏移”。不同厂家的设备,地址起始可能不一样。有的从0开始,有的从1开始,有的从40001开始。我曾经在一个项目里,就因为地址偏移搞错了1,导致所有数据都错位了。排查了整整一天才找到原因。
4.4.2 实际映射案例
咱们看个实际案例。一个水处理项目,PLC里存了这些数据:
| PLC内部地址 | 数据类型 | 含义 | Modbus地址 | SCADA标签 |
|---|---|---|---|---|
| 40001 | 16位无符号 | 进水流量 | 0x0000 | FLOW_IN |
| 40002 | 16位无符号 | 出水流量 | 0x0001 | FLOW_OUT |
| 40003-40004 | 32位浮点数 | pH值 | 0x0002 | PH_VALUE |
| 40005 | 16位无符号 | 水泵状态(位映射) | 0x0004 | PUMP_STATUS |
在网关里配置时,你需要告诉它:
- 从站地址是多少(比如01)
- 功能码是多少(03读保持寄存器)
- 起始地址是多少(0x0000)
- 读取多少个寄存器(比如5个)
- 每个寄存器怎么解析(16位、32位、浮点数、位映射)
嗯,这里有个坑:32位浮点数在Modbus里怎么存?不同厂家可能不一样。有的用ABCD字节序,有的用CDAB。我建议你在配置网关时,先读一个已知值验证一下字节序,免得后面数据全错。
4.4.3 地址映射的优化策略
做SCADA集成时,地址映射不是简单的一对一。我分享几个优化策略:
- 批量读取:尽量用功能码03一次读多个连续的寄存器,而不是一个一个读。这样效率高很多。我见过有人读100个点,发了100次请求,其实一次就能搞定。
- 缓存机制:网关内部可以缓存最近读到的数据。如果SCADA系统频繁请求同一个点,直接从缓存返回,不用再去问PLC。这个在实时性要求不高的场景下特别有用。
- 异常处理:如果某个从站没响应,网关要有超时重试机制。我一般设3次重试,间隔500ms。如果3次都失败,就报错并记录日志。
避坑指南:我曾经在一个项目中,把网关的轮询周期设得太快(10ms),结果PLC的CPU负载飙到90%,差点死机。后来改成100ms轮询,一切正常。记住,不是越快越好,要留有余量。
4.5 小结
今天咱们把Modbus RTU和TCP的核心原理、帧结构、地址映射都过了一遍。说白了,Modbus就是个“问-答”协议,主站问什么,从站答什么。RTU适合串口通信,TCP适合网络通信。地址映射是网关的翻译工作,把设备侧的寄存器地址转成SCADA系统能理解的标签。
下一节咱们继续讲Modbus的高级话题——功能码的深入应用、异常码处理、以及多主站冲突的解决方案。到时候我会分享一些实际项目中的调试技巧,保证让你少走弯路。
好,今天就到这里。有什么问题,咱们课后交流。