3. Modbus数据模型:理解PLC的“记忆宫殿”
各位同学,今天我们来聊聊Modbus通信里最核心的概念——数据模型。说实话,我刚开始接触Modbus时,也被线圈、寄存器这些名词搞得晕头转向。但后来我发现,只要理解了PLC内部的数据存储方式,一切就豁然开朗了。
Modbus协议定义了一套标准的数据访问方式。它把设备内部的数据分成了四个区域。每个区域都有特定的用途和访问规则。这就像我们整理房间,把书放在书架上,衣服放在衣柜里,找起来才方便。
核心要点:Modbus数据模型是主站和从站之间通信的“共同语言”。不理解它,你写的代码就是瞎蒙。
3.1 四种数据对象:从“开关”到“数值”
Modbus定义了四种基本数据对象。我习惯把它们分成两类:位对象和字对象。
| 数据对象 | 对象类型 | 访问权限 | 数据大小 | 通俗理解 |
|---|---|---|---|---|
| 线圈 (Coil) | 位 | 可读可写 | 1 bit (0或1) | 继电器输出,可以控制 |
| 离散输入 (Discrete Input) | 位 | 只读 | 1 bit (0或1) | 按钮、限位开关状态 |
| 保持寄存器 (Holding Register) | 字 | 可读可写 | 16 bits (0-65535) | 设定值、参数配置 |
| 输入寄存器 (Input Register) | 字 | 只读 | 16 bits (0-65535) | 传感器采集的数值 |
嗯,这里要注意:线圈和离散输入都是位数据,但线圈是“输出”,你可以改变它;离散输入是“输入”,你只能读取它。同样的道理,保持寄存器和输入寄存器都是16位数据,但保持寄存器可读可写,输入寄存器只读。
我在项目中遇到过这样一个坑:有个同事把温度传感器的值写到了保持寄存器里,结果上位机怎么读都读不到。为什么?因为温度传感器是输入寄存器,只读!你写进去的数据,设备根本不认。
3.2 地址映射规则:从“0”开始的哲学
Modbus的地址映射规则,说白了就是给每个数据对象编个号。但这个编号方式有点特别。
Modbus协议规定,地址从0开始编号。比如线圈的地址范围是00001到09999,但实际通信时发送的是0到9998。为什么?因为协议设计者觉得,从0开始更符合计算机的思维习惯。
我的经验:在写代码时,一定要搞清楚你用的库是“协议地址”还是“PLC地址”。有些库直接使用0-65535的协议地址,有些库则使用1-65536的PLC地址。差一个数,通信就全乱了。
常见的地址映射规则如下:
- 线圈 (0x区):地址范围 00001 - 09999,对应协议地址 0x0000 - 0x270E
- 离散输入 (1x区):地址范围 10001 - 19999,对应协议地址 0x0000 - 0x270E
- 输入寄存器 (3x区):地址范围 30001 - 39999,对应协议地址 0x0000 - 0x270E
- 保持寄存器 (4x区):地址范围 40001 - 49999,对应协议地址 0x0000 - 0x270E
你想想看,为什么地址范围只到49999?因为Modbus协议最初设计时,只规划了这么点地址空间。后来扩展了,但老设备还是遵循这个规则。
3.3 功能码分类:主站如何“发号施令”
功能码是Modbus通信的“指令集”。主站通过发送不同的功能码,告诉从站要做什么操作。
功能码分为三类:位操作、字操作和诊断操作。我重点讲前两类,因为日常开发中90%的场景都用它们。
3.3.1 位操作功能码
| 功能码 | 名称 | 作用 |
|---|---|---|
| 0x01 | 读取线圈 | 读取一组线圈的状态 |
| 0x02 | 读取离散输入 | 读取一组离散输入的状态 |
| 0x05 | 写单个线圈 | 控制一个线圈的通断 |
| 0x0F | 写多个线圈 | 批量控制多个线圈 |
3.3.2 字操作功能码
| 功能码 | 名称 | 作用 |
|---|---|---|
| 0x03 | 读取保持寄存器 | 读取一组保持寄存器的值 |
| 0x04 | 读取输入寄存器 | 读取一组输入寄存器的值 |
| 0x06 | 写单个寄存器 | 修改一个保持寄存器的值 |
| 0x10 | 写多个寄存器 | 批量修改多个保持寄存器 |
避坑指南:我曾经在调试一个变频器时,用0x06功能码写单个寄存器,结果变频器没反应。后来发现,这个变频器只支持0x10功能码(写多个寄存器)。即使只写一个寄存器,也必须用0x10。所以,一定要先看设备的Modbus手册,确认它支持哪些功能码。
3.4 知识体系结构图
下面这张图,是我自己总结的Modbus数据模型知识体系。它把四个数据对象、地址映射和功能码串在了一起。你把它记在脑子里,写代码时就不会迷路。
3.5 实战中的地址换算
在实际开发中,你经常会遇到地址换算的问题。比如设备手册上说“保持寄存器地址是40001”,但你的代码里要填0x0000。怎么换算?
我教你一个简单的方法:PLC地址减去起始偏移量,再减1。
- 40001 → 协议地址 = 40001 - 40001 = 0
- 40002 → 协议地址 = 40002 - 40001 = 1
- 30001 → 协议地址 = 30001 - 30001 = 0
说白了,就是去掉前缀数字(4或3),然后减1。因为PLC地址从1开始,协议地址从0开始。
重要提醒:有些国产设备不按套路出牌。我遇到过一台温控器,它的保持寄存器地址直接写成了“0x0001”,而不是“40001”。所以,一定要以设备手册为准,不要想当然。
3.6 小结
这一章我们讲了Modbus数据模型的四个核心概念:线圈、离散输入、保持寄存器、输入寄存器。它们分别对应了设备的不同数据类型和访问权限。地址映射规则告诉我们如何把PLC地址转换成协议地址。功能码则决定了主站能对从站做什么操作。
我个人觉得,理解数据模型是掌握Modbus通信的第一步。你只有知道数据存在哪里、怎么访问,才能写出正确的通信代码。下一章我们会深入Python实现,到时候你会看到这些概念是如何在代码中落地的。