做 Modbus 主站、网关、透传、电表 / 采集器开发时,最容易踩坑:一次读写寄存器数量超限,直接报异常码 03(非法数据值)。很多人只记得 03H 读寄存器,但容易混淆 03H、04H、10H 的协议上限,并且分清【Modbus 协议标准上限】和【设备固件实际限制】。
一、03H(读保持寄存器)
✅ 协议标准单次最大:125 个寄存器(Word)
- 1 个寄存器 = 2 字节
- 125 寄存器 = 250 字节数据
原理(PDU 长度限制)
Modbus RTU PDU 最大长度 253 字节 (不包含从站地址、CRC 校验) 应答 PDU 结构:1字节功能码 + 1字节数据长度 + 寄存器数据 可用数据字节 = 253 - 2 = 251,必须取偶数,最大 250 字节 250 / 2 = 125,请求报文寄存器数量字段最大值 0x007D。
RTU 报文示例
读从站 1,起始地址 0,读取 125 个保持寄存器 请求:01 03 00 00 00 7D CRC16 应答:01 03 FA [250字节寄存器数据] CRC16 FA = 250,代表后续 250 字节数据。
同规则:04H(读输入寄存器)单次标准上限同样 125 个寄存器。
超限行为
请求寄存器数量>125,合规从站返回异常响应:0x83 03
- 0x83:功能码最高位置 1,表示异常响应
- 03:异常码 = 非法数据值
⚠️工程坑:协议上限是 125,但大量电表、仪表、PLC 固件做了裁剪,限制 32、60、100 个不等。优先以设备手册为准,协议上限只是理论最大值。
二、10H(写多个保持寄存器)
✅ 协议标准单次最大:123 个寄存器(Word)
高频踩坑点!写多寄存器比读少 2 个,不要记成 125。
原理
10H 应答 PDU 很短(只返回从站地址、功能码、起始地址、写入数量),限制来自请求 PDU : 请求 PDU 结构: 1B功能码 + 2B起始地址 + 2B寄存器数量 + 1B字节长度 + 寄存器数据 固定头部占用:1+2+2+1 = 6字节 剩余可放数据:253 - 6 = 247字节 数据必须偶数,最大 246 字节 246 / 2 = 123 寄存器。 寄存器数量字段最大值 0x007B。
RTU 报文示例
从站 1,起始地址 0,写入 2 个寄存器 请求:01 10 00 00 00 02 04 00 01 00 02 CRC16 应答:01 10 00 00 00 02 CRC16
超限行为
请求写入寄存器数量>123,合规从站返回异常响应:0x90 03
- 0x90:10H 异常功能码
- 03:异常码 = 非法数据值
三、常用 Modbus 功能码单次读写上限汇总表
表格
| 功能码 | 名称 | 协议单次最大数量 | 单位 |
|---|---|---|---|
| 01H | 读线圈 | 2000 个线圈 | 位 |
| 02H | 读离散输入 | 2000 个离散输入 | 位 |
| 03H | 读保持寄存器 | 125 个寄存器 | 字 (Word,2B) |
| 04H | 读输入寄存器 | 125 个寄存器 | 字 (Word,2B) |
| 05H | 写单线圈 | 1 个线圈 | 位 |
| 06H | 写单保持寄存器 | 1 个寄存器 | 字 |
| 0FH | 写多线圈 | 1968 个线圈 | 位 |
| 10H | 写多保持寄存器 | 123 个寄存器 | 字 |
四、网关 / 主站开发必看要点
- 两层限制判断: 第一层:Modbus 协议硬上限(表内数值,超过直接返回异常 03) 第二层:从站设备自身限制(电表经常 32/60,优先读设备手册)
- 网关转发逻辑:
- 上位机下发请求数量 > 协议上限 → 网关直接返回异常 03,不要转发给从站
- 上位机请求 ≤协议上限,但 > 后端从站支持最大值 → 同样返回异常 03
- 分包策略: 如果上位机需要一次性读取超过设备支持数量寄存器,主站程序需要自动分包轮询 ,不要一次性下发超限请求。

五、一句话总结
03H 读保持寄存器:协议最大 125 个寄存器 ; 10H 写多保持寄存器:协议最大 123 个寄存器; 实际项目以从站设备手册为准。