3、设备接口协议详解(一):Modbus RTU/TCP协议原理、数据模型(线圈、寄存器)、功能码、报文结构解析。
各位工程师朋友,咱们今天来啃一块硬骨头——Modbus协议。说实话,这玩意儿在工业现场太常见了。我做了十几年MES集成,跟设备打交道,十台里有七八台都支持Modbus。你想想看,一个1999年就发布的协议,到现在还能活跃在一线,说明它确实有两把刷子。
Modbus本质上是个“主从”通信协议。什么意思呢?就是一台主设备(比如你的上位机、PLC或者MES接口机)发起请求,从设备(比如传感器、变频器、智能仪表)响应。主设备问,从设备答,就这么简单。但简单归简单,里面的门道可不少。
核心要点:Modbus协议只定义了应用层的数据格式,底层走串口还是走以太网,它不管。这就衍生出了两个最常用的变种:Modbus RTU(串口)和Modbus TCP(以太网)。
3.1 数据模型:线圈、离散输入、输入寄存器、保持寄存器
我第一次接触Modbus时,最晕的就是这四个数据对象。说白了,Modbus把设备里的数据分成了四类,每一类都有不同的读写权限和用途。
| 数据模型 | 对象类型 | 读写属性 | 数据大小 | 典型用途 |
|---|---|---|---|---|
| 线圈 (Coil) | 位 | 可读可写 | 1 bit | 开关量输出(继电器、指示灯) |
| 离散输入 (Discrete Input) | 位 | 只读 | 1 bit | 开关量输入(按钮、限位开关) |
| 输入寄存器 (Input Register) | 字 | 只读 | 16 bit | 模拟量输入(温度、压力传感器) |
| 保持寄存器 (Holding Register) | 字 | 可读可写 | 16 bit | 参数设置、控制指令、累计值 |
嗯,这里要注意。很多设备手册里写的“寄存器地址”,其实指的是保持寄存器。比如你看到“地址40001”,这个4开头就代表保持寄存器区。我个人习惯,在MES对接时,最常用的就是保持寄存器和线圈。因为你要读数据、写参数,基本都落在这两个区。
我的经验:曾经对接一台温控仪表,手册上写温度值在“地址40003”。我一开始直接用功能码03去读,结果死活读不到。后来才发现,它用的是输入寄存器,得用功能码04。所以,拿到设备手册第一件事,先确认数据模型是哪一类。
3.2 功能码:Modbus的“指令集”
功能码就是主设备告诉从设备“你要干什么”。常用的功能码其实没几个,我列个表,你一看就明白。
| 功能码 | 名称 | 作用 | 操作对象 |
|---|---|---|---|
| 01 (0x01) | 读线圈 | 读取一组线圈的状态 | 线圈 |
| 02 (0x02) | 读离散输入 | 读取一组离散输入的状态 | 离散输入 |
| 03 (0x03) | 读保持寄存器 | 读取一个或多个保持寄存器的值 | 保持寄存器 |
| 04 (0x04) | 读输入寄存器 | 读取一个或多个输入寄存器的值 | 输入寄存器 |
| 05 (0x05) | 写单个线圈 | 强制一个线圈的通断 | 线圈 |
| 06 (0x06) | 写单个寄存器 | 写入一个保持寄存器的值 | 保持寄存器 |
| 15 (0x0F) | 写多个线圈 | 连续写入多个线圈 | 线圈 |
| 16 (0x10) | 写多个寄存器 | 连续写入多个保持寄存器 | 保持寄存器 |
你看,是不是很清晰?读用01-04,写用05、06,批量读写用15、16。我在项目中遇到过最坑的事,就是有人用功能码03去读线圈,结果从设备返回异常码。说白了,功能码和操作对象是绑定的,不能乱用。
3.3 报文结构解析:RTU vs TCP
报文结构是Modbus的核心。咱们先看RTU,再看TCP。两者最大的区别,就是RTU多了一个CRC校验,而TCP没有,因为TCP/IP协议栈自己会做校验。
3.3.1 Modbus RTU 报文
RTU报文是二进制格式,没有起始位和停止位,靠时间间隔来区分帧。结构如下:
| 从站地址 (1字节) | 功能码 (1字节) | 数据区 (N字节) | CRC校验 (2字节) |
举个例子,我要读取从站地址为1的设备,从保持寄存器地址0开始,读2个寄存器。请求报文就是:
01 03 00 00 00 02 C4 0B
拆解一下:
- 01:从站地址,表示我要找1号设备
- 03:功能码,读保持寄存器
- 00 00:起始地址,从0号寄存器开始
- 00 02:读取数量,读2个寄存器
- C4 0B:CRC校验,低字节在前
如果设备正常响应,会返回:
01 03 04 00 0A 00 14 7A 5B
- 01:从站地址
- 03:功能码
- 04:数据字节数,表示后面有4个字节
- 00 0A:第一个寄存器的值,十进制10
- 00 14:第二个寄存器的值,十进制20
- 7A 5B:CRC校验
避坑指南:我曾经调试一个RTU设备,报文发出去总是没响应。查了半天,发现是CRC算错了。很多初学者会忽略CRC的字节序——Modbus RTU的CRC是低字节在前,高字节在后。比如CRC值0x0BC4,在报文里要写成C4 0B。
3.3.2 Modbus TCP 报文
TCP报文去掉了CRC,加了一个MBAP报文头。结构如下:
| 事务处理标识 (2字节) | 协议标识 (2字节) | 长度 (2字节) | 单元标识 (1字节) | 功能码 (1字节) | 数据区 (N字节) |
同样读保持寄存器的例子,TCP请求报文:
00 01 00 00 00 06 01 03 00 00 00 02
拆解:
- 00 01:事务处理标识,用于匹配请求和响应
- 00 00:协议标识,固定为0
- 00 06:长度,表示从单元标识开始到结束共有6个字节
- 01:单元标识,相当于RTU里的从站地址
- 03:功能码
- 00 00:起始地址
- 00 02:读取数量
响应报文:
00 01 00 00 00 07 01 03 04 00 0A 00 14
注意看,TCP响应里没有CRC,但多了一个字节的长度字段。嗯,这里要记住,TCP报文的最大长度是260字节,超过这个数就要分包了。
3.4 知识体系图:Modbus协议全景
下面这张图,是我自己总结的Modbus协议知识体系。你可以把它当成一张地图,以后遇到Modbus相关的问题,先看这张图,心里就有数了。
3.5 实战要点:MES对接时的注意事项
最后,我结合自己的项目经验,给你几个实实在在的建议:
- 先看设备手册:确定设备支持RTU还是TCP,支持哪些功能码,数据模型是哪种。别上来就写代码,八成要返工。
- 注意字节序:Modbus默认是大端模式(高字节在前),但有些设备厂商会搞小端。我遇到过一台国产仪表,手册上写大端,实际是小端,折腾了我一整天。
- 超时和重试:工业现场干扰多,报文丢包很正常。我建议超时时间设500ms,重试3次。别设太短,也别无限重试。
- 地址偏移:很多设备手册里的地址是PLC地址(如40001),实际Modbus报文里要减1。比如40001对应报文地址0x0000。这个坑,我踩过不止一次。
- 用工具先测试:推荐ModScan(主站模拟)和ModSim(从站模拟)。先拿工具把报文调通,再写代码,效率高得多。
我的习惯:每次对接新设备,我都会先用ModScan发一条功能码03的报文,读几个寄存器看看。如果返回的数据能看懂,说明协议没问题。如果返回异常码或乱码,那就得回头查手册了。
好了,Modbus RTU和TCP的核心内容就这些。数据模型、功能码、报文结构,这三样你吃透了,Modbus就算入门了。下一节咱们接着聊Modbus的异常处理和多设备轮询策略,那才是真正考验工程能力的地方。
公众号:蓝海资料掘金营,微信deep3321