Modbus RTU · 串行链路协议百科
工控现场最普及的串行通信协议:RS-485 一主多从、轮询问答、帧格式、功能码、CRC 校验。写作用,边看边用。
⌕
输入关键词实时过滤下方章节。
★一句话总览
先记住最本质的东西。
- Modbus:1979 年 Modicon(现施耐德电气)发布的公开工业通信协议,应用层标准,物理层可跑串口(RTU/ASCII)或以太网(TCP)。
- RTU:二进制紧凑编码,一主多从、主站轮询、从站应答;帧 = 地址 + 功能码 + 数据 + CRC16。
- 工控里几乎遍地都是:PLC ↔ 触摸屏、变频器、仪表、电表、温控器,全靠它。
一是什么
协议出身与适用场景。
- 由 Modicon 公司 1979 年为 PLC 通信设计,后来交给 Modbus 组织维护,完全公开免费,无授权费用。
- 典型的主从(Master/Slave)模型:一台主站(PLC、上位机、触摸屏)管理最多 247 个从站(地址 1~247;0 是广播地址,只发不收)。
- 从站不会主动说话,主站问一句、从站答一句(轮询);从站也可以"无响应"表示出错或不在线。
- 变体:Modbus RTU(二进制,串口主流)、Modbus ASCII(文本,慢但可读)、Modbus TCP(以太网,见另一篇)。
二物理层与接线
RTU 通常跑在 RS-485 上,偶尔 RS-232。
RS-485 接线(工业主流)
主站 A ──────── A 从站1 从站2 从站3 ... 主站 B ──────── B (手拉手菊花链,最远 ~1200m) GND ────────── GND (共地,屏蔽层单端接地) 两端各并一个 120Ω 终端电阻(最远的两台设备处)
常用参数(必须全链路一致)
| 参数 | 常见值 | 说明 |
|---|---|---|
| 波特率 | 9600 / 19200 / 38400 / 115200 | 全链路必须一致,工控默认常为 9600 或 19200 |
| 数据位 | 8 | RTU 固定 8 位 |
| 校验位 | 无校验(N)/ 偶校验(E) | 8N1 或 8E1 最常用;奇校验少见 |
| 停止位 | 1 或 2 | 常见 1 位;部分仪表要求 2 位 |
RS-232(短距离点对点)
电脑串口/调试时用:TX、RX、GND 三线,交叉连接(主机 TX → 从机 RX),距离约 15m 以内,只支持一主一从。
三RTU 帧格式
一帧报文长什么样(十六进制字节流)。
请求帧(主站 → 从站): ┌────────┬────────┬──────────────┬──────────┐ │ 地址(1B) │ 功能码(1B) │ 数据区(NB) │ CRC16(2B) │ └────────┴────────┴──────────────┴──────────┘ 0x01 0x03 起始地址+数量 低字节在前
| 字段 | 长度 | 说明 |
|---|---|---|
| 从站地址 | 1 字节 | 1~247;0 = 广播(从站执行但不回复) |
| 功能码 | 1 字节 | 告诉从站做什么(读/写、线圈/寄存器) |
| 数据区 | N 字节 | 请求:起始地址 + 数量 / 写数据;响应:字节数 + 数据 |
| CRC16 | 2 字节 | 校验整个帧(地址+功能码+数据),低字节在前发送 |
帧间隔(RTU 特有的"分包"规则)
- 帧与帧之间必须静默 ≥3.5 个字符时间(9600bps 下约 4ms)——靠这个判断"上一帧结束、新帧开始"。
- 帧内相邻字节间隔必须 <1.5 个字符时间,超了会被当成拆帧丢弃。
- 所以 RTU 不能用"每个字节停顿"的串口助手上位机直发,必须整帧连续发出。
实例:读保持寄存器(功能码 03)
读从站 1 的保持寄存器 0x0000 起 2 个:
请求 01 03 00 00 00 02 C4 0B
│ │ │ │ └─ CRC16(低字节在前)
│ │ └─────┴─ 起始地址 0x0000,数量 2
│ └─ 功能码 03(读保持寄存器)
└─ 从站地址 1
响应 01 03 04 12 34 56 78 7A 4D
│ │ │ └─── 4 字节数据:0x1234、0x5678
│ │ └─ 数据字节数 = 4
│ └─ 功能码 03(正常应答,最高位为 0)
└─ 从站地址 1
四四种数据模型
Modbus 的世界里只有四种"格子",记住这张表就懂了寻址。
| 模型 | 位/字 | 读/写 | 功能码 | PLC 习惯地址 | 典型用途 |
|---|---|---|---|---|---|
| 线圈 Coil | 1 bit | 读+写 | 01 读 / 05 写单 / 0F 写多 | 00001 起 | 继电器输出、启停命令 |
| 离散输入 Discrete Input | 1 bit | 只读 | 02 | 10001 起 | 开关量状态、按钮、限位 |
| 输入寄存器 Input Register | 16 bit | 只读 | 04 | 30001 起 | 模拟量采集:温度、压力、电流 |
| 保持寄存器 Holding Register | 16 bit | 读+写 | 03 读 / 06 写单 / 10 写多 | 40001 起 | 设定值、参数、累计量(最常用) |
💡
地址换算。报文里用的是从 0 开始的十六进制地址(0x0000~0xFFFF),和 PLC 的 1 起十进制习惯差 1:保持寄存器"40001"在报文里是 0x0000,40002 是 0x0001。各厂家人机/组态软件会自动换算,但写协议时要分清。
💡
大端字节序。16 位寄存器数据一律高字节在前(如 0x1234 发送为 12 34);32 位浮点/长整型顺序看设备手册(AB-CD 或 CD-AB 两种都有,联不上先怀疑这个)。
五常用功能码
记住这 8 个就覆盖 95% 应用。
| 功能码 | 名称 | 请求数据 | 响应 |
|---|---|---|---|
| 01 (0x01) | 读线圈 | 起始地址 + 数量 | 字节数 + 位打包数据 |
| 02 (0x02) | 读离散输入 | 起始地址 + 数量 | 字节数 + 位打包数据 |
| 03 (0x03) | 读保持寄存器 | 起始地址 + 数量 | 字节数 + 寄存器数据 |
| 04 (0x04) | 读输入寄存器 | 起始地址 + 数量 | 字节数 + 寄存器数据 |
| 05 (0x05) | 写单个线圈 | 线圈地址 + 0xFF00(ON)/0x0000(OFF) | 原样回显请求帧 |
| 06 (0x06) | 写单个寄存器 | 寄存器地址 + 数据 | 原样回显请求帧 |
| 0F (15) | 写多个线圈 | 起始地址 + 数量 + 字节数 + 数据 | 起始地址 + 数量 |
| 10 (16) | 写多个寄存器 | 起始地址 + 数量 + 字节数 + 数据 | 起始地址 + 数量 |
异常响应(功能码最高位置 1 = 出错)
从站把功能码最高位置 1 并附异常码返回,例如请求 03 失败 → 回 83 + 异常码: 01 83 02 C0 F1 │ │ └─ 异常码 02 = 非法数据地址 └─ 功能码 0x83 = 0x03 | 0x80(出错标志)
| 异常码 | 含义 | 排查方向 |
|---|---|---|
| 01 | 非法功能码 | 设备不支持该功能码 |
| 02 | 非法数据地址 | 寄存器地址超出设备范围(最常见) |
| 03 | 非法数据值 | 数量/数值非法,或跨了不连续区域 |
| 04 | 从站设备故障 | 从站内部错误(如正在写 EEPROM) |
| 06 | 从站忙 | 稍后重试 |
六CRC16 校验算法
RTU 用 CRC-16/MODBUS(多项式 0xA001 反射式),发送时低字节在前。
// 标准实现(C 语言风格):逐字节查表/移位均可,这是逐位算法
uint16_t crc16_modbus(const uint8_t *buf, uint16_t len) {
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < len; i++) {
crc ^= buf[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001) crc = (crc >> 1) ^ 0xA001;
else crc >>= 1;
}
}
return crc; // 发送顺序:低字节在前(crc & 0xFF 先发,再发 crc >> 8)
}
- 初始值 0xFFFF,多项式(反射)0xA001——这是 Modbus 专用变体,别和通用 CRC16-CCITT(0x1021)搞混。
- 对地址+功能码+数据三部分计算,不包含 CRC 本身。
- 验证技巧:把"报文 + 它自己的 CRC 低字节 + 高字节"按同样算法再算一遍,结果应为 0x0000。
七通讯过程
主站如何管理一堆从站。
轮询节奏
- 主站按"点表"循环发请求:先读 A 仪表 → 等响应(超时)→ 再读 B 变频器 → ...
- 每轮询一个从站后等待响应或超时,再发下一帧;响应超时典型 100~500ms(按波特率、从站数调)。
- 从站多、波特率低时,一个完整轮询周期可能几百毫秒~几秒,实时性要求高就别用 Modbus RTU。
常见故障现象
| 现象 | 大概率原因 |
|---|---|
| 全部从站无响应 | 波特率/校验不一致、A/B 接反、共地丢失、主站没发出来 |
| 单个从站无响应 | 地址错误、120Ω 终端电阻缺失(远距离)、从站掉电 |
| 偶发丢帧/乱码 | 线缆过长无终端电阻、屏蔽层未接地、与变频器/电机强电走同一桥架 |
| 功能码 02 异常 | 寄存器地址超范围,查设备点表 |
| 数据能读但值不对 | 字节序/浮点序不对、寄存器地址差 1(0 起 vs 1 起) |
八注意事项
实战经验。
✅
调通顺序。先用串口助手+USB-485 直接对单个从站发帧(01 03 00 00 00 02 的 CRC 可用在线计算器),确认帧格式对,再上上位机/PLC 轮询,少走一半弯路。
✅
用示波器/逻辑分析仪看波形。485 是差分对,A-B 电压:空闲 >200mV、逻辑 1 为 B 高于 A;波形反相说明 A/B 接反。
⚠️
帧间隔是 RTU 的命根子。上位机软件发送时必须在两帧之间留 ≥3.5 字符静默;某些"485 转以太网"模块会拆帧,导致从站不响应——看模块说明是否支持透传/Modbus 网关模式。
⚠️
现场干扰。485 走屏蔽双绞线,屏蔽层单端接地(通常在主站端),远离变频器输出线;实在干扰大再考虑 Modbus TCP 或隔离器。
⚠️
广播地址 0 不回复。想"同时启停全部从站"用 05 功能码发广播,从站执行但不回帧——别傻等响应。
没有匹配的内容,换个关键词试试。