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数据模型知识体系。它把四个数据对象、地址映射和功能码串在了一起。你把它记在脑子里,写代码时就不会迷路。

Modbus数据模型知识体系 Modbus数据模型 线圈 (Coil) 离散输入 保持寄存器 输入寄存器 地址映射规则:0x区(线圈) | 1x区(离散输入) | 3x区(输入寄存器) | 4x区(保持寄存器) 位操作功能码 (01,02,05,0F) 字操作功能码 (03,04,06,10) 核心原则:位对象控制开关,字对象传输数值 地址从0开始,功能码决定操作类型

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实现,到时候你会看到这些概念是如何在代码中落地的。


专注资料整理