3. Modbus RTU详解
Modbus RTU 是工业现场最常见的串行通信协议之一,它运行在 RS-485 或 RS-232 物理层之上,以紧凑的二进制帧、明确的地址域和可靠的 CRC 校验,支撑起 PLC、仪表、变频器、网关之间的数据交换。理解 RTU 的关键,不是死记功能码,而是把“串行链路—帧结构—校验—寻址—功能码—异常响应”串成一条完整的通信链。下面从通信基础开始,逐层拆解 RTU 帧的组成与交互逻辑。
一、串行通信基础:RTU 的物理与链路前提
Modbus RTU 本身是应用层协议,它依赖串行链路完成比特传输。现场最常用的是 RS-485 两线半双工总线:主站发送请求,从站应答,同一时刻总线上只能有一个发送方。通信参数必须主从一致,包括波特率、数据位、校验位、停止位,常见组合为 9600/8/N/1、19200/8/E/1 等。若参数不一致,表现为收不到数据、乱码或 CRC 错误。
RTU 与 ASCII 模式的最大区别在于:RTU 用二进制字节传输,帧与帧之间依靠至少 3.5 个字符时间的静默间隔来界定边界;ASCII 模式则用冒号开始、回车换行结束。因此 RTU 的帧同步依赖时间间隔,若主站连续发送过快、从站处理慢,就可能出现帧粘连或截断。实际工程中,网关或串口服务器常通过“帧间隔超时”参数来模拟这一静默时间。
二、RTU 帧结构:地址域、功能码、数据域与 CRC
一个标准 RTU 帧由四部分组成:从站地址(1 字节)、功能码(1 字节)、数据域(N 字节)、CRC 校验(2 字节,低字节在前)。地址域决定“谁应答”,功能码决定“做什么”,数据域携带寄存器地址、数量或写入值,CRC 负责检错。帧长度不固定,最短 4 字节(地址+功能码+CRC),最长 256 字节左右。
以读取保持寄存器为例,主站请求帧为:01 03 00 00 00 02 C4 0B。其中 01 是从站地址,03 是功能码,00 00 是起始寄存器地址,00 02 是读取数量,C4 0B 是 CRC。从站正常应答为:01 03 04 00 0A 00 0B XX XX,其中 04 表示后续数据字节数,00 0A 与 00 0B 是两个寄存器的值。
三、CRC 校验:RTU 的检错核心
CRC(循环冗余校验)是 RTU 帧可靠性的最后一道防线。发送方对“地址+功能码+数据域”逐字节计算 CRC-16,得到 2 字节校验值附加在帧尾;接收方用同样算法重新计算,若结果不一致则丢弃整帧。RTU 使用的 CRC-16 多项式为 0xA001(反向表示),初始值 0xFFFF,不进行最终异或。
下面是一段常用的 CRC 计算代码,可用于嵌入式或网关脚本中:
uint16_t modbus_crc16(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 本身;发送时低字节先发。若现场频繁出现 CRC 错误,优先排查波特率偏差、线缆质量、终端电阻和电磁干扰,而不是先怀疑从站程序。
四、地址域、功能码与异常响应
地址域是 RTU 的寻址基础。主站发送的地址决定哪个从站应答,从站地址通常为 1~247,0 为广播地址,广播写操作所有从站执行但不返回应答。地址冲突是现场常见故障:两个从站设为同一地址时,总线上会出现数据碰撞或应答混乱,表现为“有时通、有时不通”。
功能码定义操作类型,常用功能码如下表所示:
| 功能码 | 名称 | 作用 | 典型数据域 |
|---|---|---|---|
| 0x01 | 读线圈 | 读取开关量输出状态 | 起始地址 + 数量 |
| 0x02 | 读离散输入 | 读取开关量输入状态 | 起始地址 + 数量 |
| 0x03 | 读保持寄存器 | 读取模拟量/参数值 | 起始地址 + 数量 |
| 0x04 | 读输入寄存器 | 读取只读测量值 | 起始地址 + 数量 |
| 0x05 | 写单个线圈 | 控制单路开关 | 线圈地址 + FF00/0000 |
| 0x06 | 写单个寄存器 | 写入单个参数 | 寄存器地址 + 值 |
| 0x0F | 写多个线圈 | 批量控制开关量 | 地址 + 数量 + 字节数 + 数据 |
| 0x10 | 写多个寄存器 | 批量写入参数 | 地址 + 数量 + 字节数 + 数据 |
当从站无法完成请求时,会返回异常响应:功能码最高位置 1(即原功能码 + 0x80),随后跟一个异常码。例如请求功能码 0x03,异常应答功能码为 0x83。常见异常码包括:01 非法功能、02 非法数据地址、03 非法数据值、04 从站设备故障。网关集成时,必须把异常码映射为上层可读的错误信息,而不是简单丢弃。
五、案例与复盘:一次典型的 RTU 轮询故障
某项目网关轮询 8 台电表,前 3 台正常,第 4 台开始频繁超时。排查发现:第 4 台电表地址被误设为 3,与第 3 台冲突;同时总线末端未接终端电阻,CRC 错误率偏高。修正地址并加装 120Ω 电阻后,轮询恢复稳定。这个案例说明:RTU 故障往往不是单一原因,地址冲突、CRC 错误、超时设置需要联合排查。
建议的排查顺序为:先确认物理层(接线、终端电阻、屏蔽接地),再核对通信参数(波特率、校验位、停止位),然后检查地址唯一性,最后用串口助手抓取原始帧,逐字节比对地址、功能码、数据域和 CRC。只有把帧结构看透,才能在网关集成中快速定位问题。