Modbus RTU协议详解:从帧格式到功能码示例,一篇就够了
一、什么是Modbus RTU?
Modbus RTU(Remote Terminal Unit)是Modbus协议在串行链路上最常用的一种传输方式,采用二进制编码和CRC校验,具有帧结构紧凑、传输效率高的特点。它广泛应用于工业自动化、PLC、仪表、传感器等设备之间的通信。
对比:Modbus ASCII使用ASCII字符传输,可读性好但效率低;Modbus TCP基于以太网,去掉了CRC校验,增加了MBAP报文头。
二、物理层与通信参数
Modbus RTU通常运行于RS-232或RS-485串行接口上,常用参数配置:
| 参数 | 典型值 |
|---|---|
| 波特率 | 9600、19200、38400、115200 |
| 数据位 | 8 |
| 停止位 | 1(或2) |
| 校验位 | 无校验(N)、奇校验(O)、偶校验(E) |
| 总线拓扑 | RS-485:半双工,菊花链手拉手 |
| 最大从站数 | RS-485:32或128(加中继) |
关键点:通信双方必须完全一致才能正常通信。
三、RTU帧格式(核心重点)
一个完整的Modbus RTU帧由以下4个字段组成,没有帧头或帧尾,通过时间间隔区分帧边界。
| 字段 | 长度 | 说明 |
|---|---|---|
| 地址码 | 1字节 | 从站地址1~247,0为广播地址 |
| 功能码 | 1字节 | 指示操作类型(读/写) |
| 数据 | N字节 | 根据功能码不同,长度和内容不同 |
| CRC校验 | 2字节 | 循环冗余校验,低字节在前 |
3.1 帧间隔时间(极易出错考点)
帧内间隔:一帧内,字节与字节之间的间隔不能超过1.5个字符时间,否则从站会认为帧不完整而丢弃。
帧间间隔:两帧之间的空闲时间必须 ≥3.5个字符时间,用以区分不同的帧。
例如:波特率9600,1个字符时间 ≈ 1/9600 * 11位(8数据+1起始+1停止+1校验? 实际按位计算),通常1字符≈1ms。3.5字符≈3.5ms。
四、功能码详解(带示例)
Modbus定义了四种数据对象:线圈(Coil)、离散输入(Discrete Input)、输入寄存器(Input Register)、保持寄存器(Holding Register)。常用功能码如下:
| 功能码(HEX) | 名称 | 操作对象 | 权限 |
|---|---|---|---|
| 0x01 | 读线圈 | 线圈 | 读 |
| 0x02 | 读离散输入 | 离散输入 | 读 |
| 0x03 | 读保持寄存器 | 保持寄存器 | 读 |
| 0x04 | 读输入寄存器 | 输入寄存器 | 读 |
| 0x05 | 写单个线圈 | 线圈 | 写 |
| 0x06 | 写单个寄存器 | 保持寄存器 | 写 |
| 0x0F (15) | 写多个线圈 | 线圈 | 写 |
| 0x10 (16) | 写多个寄存器 | 保持寄存器 | 写 |
下面给出最常用的几个功能码的请求/响应示例(假设从站地址=01)。
4.1 功能码 0x03 – 读保持寄存器
请求:01 03 00 01 00 01 D5 CA
解析:
01:从站地址03:功能码00 01:起始寄存器地址(协议地址,对应PLC地址40002)00 01:读取寄存器数量(1个)D5 CA:CRC校验(低字节CA在前,D5在后)
正常响应:01 03 02 00 17 F8 4A
01:从站地址03:功能码02:数据字节数(2字节)00 17:寄存器值 = 0x0017 = 23F8 4A:CRC
4.2 功能码 0x06 – 写单个寄存器
请求(向地址0x0001的寄存器写入0x001F):01 06 00 01 00 1F 19 9E
00 01:寄存器地址00 1F:写入的值(31)19 9E:CRC
正常响应:回显相同的请求报文(原样返回)。
4.3 功能码 0x10 – 写多个寄存器
请求(从地址0x0002开始写入2个寄存器的值:0x000A和0x000B):01 10 00 02 00 02 04 00 0A 00 0B 60 71
01:地址10:功能码(16进制10 = 16)00 02:起始地址00 02:寄存器数量04:后续数据字节数(2个寄存器 × 2字节 = 4)00 0A:第1个寄存器值00 0B:第2个寄存器值60 71:CRC
正常响应:01 10 00 02 00 02 E0 78
回显地址、功能码、起始地址、数量,CRC重新计算。
4.4 功能码 0x01 – 读线圈
请求(从线圈地址0x0000开始读取2个线圈状态):01 01 00 00 00 02 3D C9
00 00:起始线圈地址(PLC地址00001)00 02:读取2个线圈
响应:01 01 01 03 91 90
01:字节数(1字节)03:二进制 0000 0011,表示第0位=1,第1位=1(两个线圈均为ON)
五、CRC校验计算与字节序
CRC-16-Modbus算法:
多项式:
0x8005(反转后为0xA001)初始值:
0xFFFF计算范围:地址 + 功能码 + 数据(不包含CRC本身)
结果发送顺序:低字节在前,高字节在后(小端序)
示例计算(手动简单理解)
以01 03 00 01 00 01为例:
CRC初始值 0xFFFF
异或 0x01 → 0xFFFE
右移8次,每次若移出1则异或0xA001,得到临时CRC...
处理完所有字节后,最终的CRC = 0xD5CA
发送时先发
CA,再发D5
常见面试题:CRC校验的两个字节,发送时谁在前?答:低字节在前。
六、异常码与异常响应
当从站无法处理请求时,返回异常响应:功能码的最高位置1,数据字节为异常码。
例如:请求读保持寄存器(0x03)时地址越界,从站返回:01 83 02 C1 91
01:地址83=0x80 | 0x03,表示读寄存器出错02:异常码(非法数据地址)
常见异常码表
| 异常码 | 名称 | 含义 |
|---|---|---|
| 0x01 | 非法功能码 | 从站不支持该功能码 |
| 0x02 | 非法数据地址 | 地址超出范围或不存在 |
| 0x03 | 非法数据值 | 数据值无效(如写入只读寄存器) |
| 0x04 | 从站设备故障 | 执行时发生不可恢复错误 |
| 0x06 | 从站忙 | 正在处理上一个命令,稍后再试 |
七、数据模型与地址映射
在实际工程中,我们常听到PLC地址如40001、00001,它们与协议地址的对应关系如下:
| 数据类型 | PLC地址范围 | 协议地址(十六进制) | 功能码示例 |
|---|---|---|---|
| 线圈 | 00001~09999 | 0x0000~0x270E | 01(读) / 05(写) |
| 离散输入 | 10001~19999 | 0x0000~0x270E | 02(读) |
| 输入寄存器 | 30001~39999 | 0x0000~0x270E | 04(读) |
| 保持寄存器 | 40001~49999 | 0x0000~0x270E | 03(读) / 06(写) |
注意:PLC地址
40001对应的协议地址是0x0000,40002→0x0001,依此类推。在报文中必须使用协议地址。
八、常见故障排查指南
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 无响应 | 地址错误、波特率不一致、接线A/B反、终端电阻缺失 | 检查配置,用示波器或串口助手抓包 |
| 返回异常码0x02 | 请求的地址超出设备范围 | 确认设备地址映射表 |
| 返回异常码0x03 | 写入值非法(如写入只读区域) | 检查功能码与数据类型是否匹配 |
| CRC错误 | 帧内字节间隔超过1.5字符,或线路干扰 | 检查软件发送连续性,增加屏蔽,降低波特率 |
| 读取数据值错乱(大端) | 32位数据或浮点数的字节顺序不对 | 交换寄存器高低字节(如使用Modbus Poll软件) |
九、调试工具推荐
串口助手:格西烽火、SSCOM,可捕获原始字节。
Modbus调试软件:Modbus Poll(主站模拟)、Modbus Slave(从站模拟)。
逻辑分析仪:查看总线实际波形和字符间隔。
十、总结
Modbus RTU简单、稳定、开放,是工业通信的必修课。掌握以下重点,即可轻松应对开发与面试:
帧结构:地址、功能码、数据、CRC(低字节在前)。
时间间隔:帧内<1.5字符,帧间≥3.5字符。
常用功能码:03读寄存器、06写单寄存器、16写多寄存器、01读线圈。
CRC算法:多项式0xA001,初始0xFFFF,发送小端序。
异常码:01~04的含义。
物理层参数:9600-8-N-1最常见。
最后留一道思考题:如果一帧数
