做 PLC、仪表或传感器的数据采集时,经常会遇到一个问题:通信正常,也能读到数据,但数值就是不对。
温度显示成几千,电压变成小数,甚至同一个地址换个工具就读出了不同结果。排查这些问题,要先弄清楚寄存器地址、数据类型和数值换算之间的关系。
本文使用虚构的温度测点作为示例,实际配置应以设备寄存器表为准。
一、寄存器是什么?
可以把寄存器理解为设备对外提供的“带编号的数据格子”。采集软件通过地址找到对应格子,读取其中的数据。
Modbus 有四类常用数据对象:
| 数据对象 | 单个对象大小 | 常用读取功能码 |
|---|---|---|
| 线圈 | 1 位 | 01 |
| 离散输入 | 1 位 | 02 |
| 保持寄存器 | 16 位 | 03 |
| 输入寄存器 | 16 位 | 04 |
线圈和离散输入常用于开关状态;寄存器常用于温度、电压、计数等数值。一个 16 位寄存器占两个字节,但它代表什么物理量,由设备定义。
保持寄存器在协议模型中支持读写,具体地址是否允许写入,还要看设备说明。参考:Modbus 协议规范
二、拿到寄存器表,先看哪些字段?
假设设备手册提供了以下测点:
| 参数 | 示例值 |
|---|---|
| 测点名称 | 温度 |
| 寄存器参考编号 | 40001 |
| 读取功能码 | 03 |
| 数据类型 | INT16,有符号整数 |
| 寄存器数量 | 1 |
| 换算关系 | 原始值 × 0.1 |
| 单位 | ℃ |
这张表告诉我们:读取一个保持寄存器,将结果按有符号整数解析,再乘以 0.1,才是实际温度。
因此,只知道寄存器地址,还不足以正确读取测点。
另外,设备地址和寄存器地址是两个概念。例如在 RS485 总线上,设备地址用于选择哪台仪表,寄存器地址用于选择该仪表中的哪项数据。
三、40001、地址 0 和地址 1 有什么关系?
这是入门时最容易混淆的地方。
在传统参考编号方式中,40001 表示第一个保持寄存器,对应报文中的地址 0。Modbus 报文中的寄存器地址从 0 开始。参考:协议规范第 6.3 节
但采集工具的输入方式不一定相同:
- 使用零起始地址的工具,可能填写
0。 - 使用一起始编号的工具,可能填写
1。 - 支持完整参考编号的工具,可能填写
40001。
三种写法可能指向同一位置,关键在于工具如何转换。
不要看到地址就习惯性减 1。先确认手册给的是参考编号还是报文地址,再确认工具使用哪种规则。还要留意十进制与十六进制,例如0x0010对应十进制 16。
四、一个数值为什么占两个寄存器?
寄存器的大小固定,但数据类型的长度不同:
| 数据类型 | 位数 | 占用寄存器数 |
|---|---|---|
| INT16 / UINT16 | 16 | 1 |
| INT32 / UINT32 | 32 | 2 |
| FLOAT32 | 32 | 2 |
例如一个 FLOAT32 测点从地址 10 开始,通常需要一起读取地址 10 和 11,再组合解析。只读其中一个,无法得到完整数值。
同样的原始位模式,按不同类型解释也会得到不同结果。例如0xFFFF按 UINT16 解析是 65535,按 INT16 解析则是 -1。
对于跨两个寄存器的数据,还要确认高低字的排列顺序。单个寄存器在标准报文中高字节先传,但跨寄存器的组合方式需要查看设备说明。工具中的“字节序”“字序”设置也应核对具体含义,不能只凭选项名称判断。
五、把原始值转换为实际温度
回到前面的温度示例。假设读回的数据字节为:
00 EB0x00EB转为十进制是 235,按 INT16 解析仍为 235。根据点表的换算关系:
实际温度 = 235 × 0.1 = 23.5℃如果直接显示 235℃,可能只是漏掉了倍率,并非通信故障。
也要避免重复换算:如果采集软件已经乘过 0.1,上层程序再次乘以 0.1,就会错误地显示成 2.35℃。
六、数值异常时,按什么顺序排查?
| 现象 | 优先检查 |
|---|---|
| 完全没有响应 | 接线、串口参数、设备地址、网络连接 |
| 返回非法地址异常 | 起始地址、读取数量、地址是否实现 |
| 有数据但像是其他测点 | 功能码、地址起始规则、地址偏移 |
| 正负号不对或数值离谱 | 有符号/无符号、整数/浮点、字节序与字序 |
| 恰好相差 10 倍或 100 倍 | 倍率、单位、是否重复换算 |
调试时,先选一个能与设备屏幕对照的测点,保存原始报文,每次只修改一项参数。确认单个测点正确后,再扩展到批量采集,会更容易定位问题。