寄存器分类与功能码:线圈、离散输入、保持寄存器、输入寄存器的定义与对应功能码(01-06,15,16)
说实话,Modbus 协议里最绕人的就是这四种寄存器。
我刚入行那会儿,也经常搞混。线圈和离散输入有啥区别?保持寄存器和输入寄存器又差在哪?
后来我在一个污水处理项目里,因为把保持寄存器当成了输入寄存器来读,结果数据全是乱的。折腾了一下午才找到原因。从那以后,我就把这四种寄存器刻在脑子里了。
今天咱们就把它们彻底理清楚。
一、四种寄存器的本质区别
先记住一个核心原则:能不能写 和 是不是掉电保存。
你想想看,Modbus 协议把设备里的数据分成了四类。为啥要分?因为有些数据你只能看,不能改。比如传感器采集的温度值。有些数据你既要读也要写,比如变频器的频率设定值。
| 寄存器类型 | 位/字 | 可读 | 可写 | 掉电保存 | 典型用途 |
|---|---|---|---|---|---|
| 线圈 (Coil) | 1位 | ✓ | ✓ | 通常不保存 | 开关、继电器、指示灯 |
| 离散输入 (Discrete Input) | 1位 | ✓ | ✗ | — | 按钮、限位开关、传感器状态 |
| 保持寄存器 (Holding Register) | 16位 | ✓ | ✓ | 通常保存 | 设定值、参数、累计值 |
| 输入寄存器 (Input Register) | 16位 | ✓ | ✗ | — | 模拟量输入、温度、压力 |
这张表我建议你截图保存。每次做地址规划前,先看一眼,不容易出错。
二、线圈与离散输入:位操作的世界
这两种都是位数据,一个 bit 代表一个状态。但它们的区别在于:线圈是输出,离散输入是输入。
1. 线圈(Coil)
说白了,线圈就是你可以控制的开关。PLC 里的 DO(数字量输出)点,通常就映射到线圈上。
我记得有个项目,客户要求远程控制 50 个阀门。每个阀门对应一个线圈。我用功能码 05 写单个线圈,用功能码 15 批量写。调试的时候,发现有个阀门怎么都打不开。查了半天,原来是地址偏移算错了。嗯,这里要注意:线圈地址从 0 开始,但很多组态软件显示从 1 开始。
- 01 (0x01) — 读取线圈状态。一次最多读 2000 个线圈。
- 05 (0x05) — 写单个线圈。数据 0xFF00 表示 ON,0x0000 表示 OFF。
- 15 (0x0F) — 写多个线圈。批量操作时效率高。
2. 离散输入(Discrete Input)
离散输入是只读的。你只能读它的状态,不能改。PLC 里的 DI(数字量输入)点,比如按钮、限位开关,就属于这一类。
我曾经在一个包装线上,用离散输入来检测每个工位有没有物料。功能码 02 读取,一次读 64 个点,轮询一遍只要几十毫秒。
- 02 (0x02) — 读取离散输入状态。用法和 01 类似,但只能读不能写。
三、保持寄存器与输入寄存器:字操作的世界
这两种是 16 位数据,一个寄存器可以存 0~65535 或者 -32768~32767。它们也是按读写权限来区分的。
1. 保持寄存器(Holding Register)
保持寄存器是最常用的。因为它既能读又能写,而且掉电后数据还在。变频器的频率、PID 的设定值、累计流量,我都习惯放在保持寄存器里。
我个人习惯,把需要远程修改的参数都规划到保持寄存器区域。比如一个温控仪表,目标温度用保持寄存器 40001,实际温度用输入寄存器 30001。这样逻辑清晰,不容易搞混。
- 03 (0x03) — 读取保持寄存器。一次最多读 125 个寄存器。
- 06 (0x06) — 写单个保持寄存器。
- 16 (0x10) — 写多个保持寄存器。批量修改参数时必用。
2. 输入寄存器(Input Register)
输入寄存器是只读的。它专门用来存放传感器采集的数据。比如 4-20mA 模拟量输入、PT100 温度值、压力变送器的数值。
为什么不用保持寄存器来存?因为输入寄存器是只读的,从硬件层面就保证了数据不会被误写。我在一个水处理项目中,就遇到过上位机误写了保持寄存器,导致变频器频率突变。从那以后,所有只读的模拟量我都强制用输入寄存器。
- 04 (0x04) — 读取输入寄存器。一次最多读 125 个寄存器。
四、功能码速查表
为了方便你现场查阅,我把这 8 个功能码整理成了一张表。建议你打印出来贴在工位上。
| 功能码 | 名称 | 操作对象 | 最大数量 | 我常用的场景 |
|---|---|---|---|---|
| 01 (0x01) | 读线圈 | 线圈 | 2000 个 | 读取继电器状态 |
| 02 (0x02) | 读离散输入 | 离散输入 | 2000 个 | 读取按钮、限位开关 |
| 03 (0x03) | 读保持寄存器 | 保持寄存器 | 125 个 | 读取设定值、参数 |
| 04 (0x04) | 读输入寄存器 | 输入寄存器 | 125 个 | 读取模拟量输入 |
| 05 (0x05) | 写单个线圈 | 线圈 | 1 个 | 控制单个开关 |
| 06 (0x06) | 写单个保持寄存器 | 保持寄存器 | 1 个 | 修改单个参数 |
| 15 (0x0F) | 写多个线圈 | 线圈 | 1968 个 | 批量控制输出点 |
| 16 (0x10) | 写多个保持寄存器 | 保持寄存器 | 123 个 | 批量下载参数表 |
五、知识体系结构图
下面这张图,是我自己画的一个结构图。它把四种寄存器和对应的功能码串在了一起。你看完应该能有个整体印象。
六、避坑指南
- 地址偏移问题:很多设备手册里写的是 40001,但实际协议里地址是 0。你发 03 功能码读地址 0,对应的是手册里的 40001。千万别直接发 40001。
- 线圈写 0xFF00:写单个线圈时,数据必须是 0xFF00(ON)或 0x0000(OFF)。写 0x0001 是无效的。我见过有人写 0x0001,结果线圈没反应。
- 批量读写数量限制:功能码 03 一次最多读 125 个寄存器,功能码 16 一次最多写 123 个。超过这个数,设备会返回异常码。我习惯一次读 120 个,留点余量。
- 寄存器数据类型:32 位浮点数或整数,需要占用两个连续的保持寄存器。规划地址时,记得留好对齐。我曾经因为没对齐,读出来的温度值全是乱码。
做地址规划时,我会把四种寄存器分开写在 Excel 里。线圈从 0 开始,离散输入从 1000 开始,保持寄存器从 40001 开始,输入寄存器从 30001 开始。这样一眼就能看出哪个地址对应哪种寄存器。
好了,这四种寄存器和对应的功能码,咱们就聊到这儿。你只要记住:线圈和保持寄存器能读能写,离散输入和输入寄存器只能读。这个原则抓住了,后面做地址规划就不会乱。