Modbus C# 第五天:数据类型详解与问题排查

这是系列第五篇。前四天你已经学会了读、写、轮询、处理浮点数、做 WinForms 界面。

但你可能一直有个困惑:Modbux 里那些 INT16、UINT16、FLOAT 到底是什么意思?为什么有的读 1 个寄存器,有的读 2 个?

今天专门把这件事讲清楚,并且把遇到问题时怎么排查也一并写全。

一、先理解一个核心事实

Modbus 协议本身,只认识一种东西:16 位的寄存器。

它不知道什么是整数、小数、正数、负数。它只知道“这里有一个 16 位的格子”。

所有的类型,都是人给它赋予的含义。

二、Modbux 里的类型 vs C# 里的类型

你在 Modbux 里看到的类型,和 C# 里写的类型,是一一对应的,只是叫法不同。

Modbux 里选 C# 里写 位数 占几个寄存器 范围
UINT16 ushort 16 位 1 个 0 ~ 65535
INT16 short 16 位 1 个 -32768 ~ 32767
UINT32 uint 32 位 2 个 0 ~ 42 亿
INT32 int 32 位 2 个 -21 亿 ~ 21 亿
FLOAT float 32 位 2 个 小数
DOUBLE double 64 位 4 个 小数
INT64 long 64 位 4 个 很大
UINT64 ulong 64 位 4 个 很大

核心规律:

  1. 16 位的类型,占 1 个寄存器
  2. 32 位的类型,占 2 个寄存器
  3. 64 位的类型,占 4 个寄存器

一个寄存器 = 16 位 = 2 个字节。

三、为什么有 INT 和 UINT

INT 和 UINT 的区别,是“能不能存负数”。

类型 全称 能存负数吗
INT16 有符号 16 位整数 ✅ 能
UINT16 无符号 16 位整数 ❌ 不能

例子:

  • UINT16 的范围是 0 ~ 65535
  • INT16 的范围是 -32768 ~ 32767

同样是 16 位,UINT16 把一半的范围分给了正数,INT16 把一半分给了负数。

所以:

  • 数据不可能为负(比如温度、数量)→ 用 UINT16
  • 数据可能为负(比如偏移量、温差)→ 用 INT16

四、16 位 vs 32 位

4.1 16 位能存多大

UINT16 最大 65535。超过这个数,就装不下。

比如你要存 100000:

  • UINT16 最大 65535,装不下
  • 必须用 UINT32(32 位),它最大能到 42 亿

4.2 32 位为什么占 2 个寄存器

因为一个寄存器只有 16 位,32 位需要 2 个寄存器拼起来。

  • 寄存器 1:前 16 位
  • 寄存器 2:后 16 位

拼起来,才能表示一个 32 位数。

4.3 64 位占 4 个寄存器

同理,64 位需要 4 个寄存器。

实际项目中,64 位很少用。 大部分数据 16 位或 32 位就够了。

五、FLOAT 是什么

FLOAT 就是小数。

  • 占 32 位 = 2 个寄存器
  • 用 IEEE 754 标准编码
  • 能存 25.6、101.325 这种小数

在 C# 里对应 float 类型。

为什么叫浮点数: 因为小数点可以“浮动”,能表示很大或很小的数。

六、怎么知道用哪个类型

看设备手册。

设备手册会写清楚:

手册写什么 C# 里用什么 读几个寄存器
温度是 16 位无符号整数 ushort 1 个
温度是 16 位有符号整数 short 1 个
累计产量是 32 位无符号整数 uint 2 个
温度是浮点数 float 2 个

没有手册,就问厂家,或者试。

七、读出来不对,怎么排查

这是实际工作中一定会遇到的。按下面四步来。

第一步:打印原始寄存器值

不要直接看拼出来的结果,先看寄存器里的原始值。

ushort[] registers = master.ReadHoldingRegisters(2, 0, 2);
Console.WriteLine("寄存器 0:" + registers[0]);
Console.WriteLine("寄存器 1:" + registers[1]);

对比:

  • 打印的值和 Modbux(或设备)里设置的一样 → 读取没问题,问题在拼装
  • 打印的值不一样 → 读取有问题,检查 Unit ID、地址

第二步:确认类型对不对

  • 你代码里用的类型,和设备手册说的一致吗?
  • float 对应 FLOAT,ushort 对应 UINT16
  • 如果手册说是 UINT32,你却用 ushort 去读,只有一个寄存器,肯定不对

类型错了,读出来的值一定不对。

第三步:试四种字节顺序

如果寄存器值对了,类型也对了,但拼出来还是不对,那就是字节顺序问题。

把 ConvertToFloat 里的两个寄存器,按四种顺序都试一遍:

顺序 A(大端):

bytes[0] = (byte)(registers[0] >> 8);
bytes[1] = (byte)(registers[0] & 0xFF);
bytes[2] = (byte)(registers[1] >> 8);
bytes[3] = (byte)(registers[1] & 0xFF);

顺序 B(小端):

bytes[0] = (byte)(registers[1] >> 8);
bytes[1] = (byte)(registers[1] & 0xFF);
bytes[2] = (byte)(registers[0] >> 8);
bytes[3] = (byte)(registers[0] & 0xFF);

顺序 C(字节交换):

bytes[0] = (byte)(registers[0] & 0xFF);
bytes[1] = (byte)(registers[0] >> 8);
bytes[2] = (byte)(registers[1] & 0xFF);
bytes[3] = (byte)(registers[1] >> 8);

顺序 D(全交换):

bytes[0] = (byte)(registers[1] & 0xFF);
bytes[1] = (byte)(registers[1] >> 8);
bytes[2] = (byte)(registers[0] & 0xFF);
bytes[3] = (byte)(registers[0] >> 8);

四种都试一遍,总有一个对的。

第四步:确认比例系数

如果拼出来的值合理,但单位不对(比如温度显示 256 而不是 25.6),检查设备手册的比例系数。

常见情况:

手册说 转换方法
单位是 0.1℃ 值 × 0.1
单位是 0.01℃ 值 × 0.01
单位是 1℃ 值 × 1

这一步很容易漏,但很关键。

八、一个提醒:REAL 是什么

有些设备手册会用 REAL 这个词。

REAL 和 FLOAT 是一个意思,都是 32 位浮点数,占 2 个寄存器。

九、完整排查流程

遇到读出来不对,按这个顺序走:

1. 打印原始寄存器值
   ↓
   和 Modbux / 设备里设置的一样吗?
   ├── 不一样 → 检查 Unit ID、地址
   └── 一样 → 继续
   ↓
2. 检查类型
   ↓
   代码里的类型,和手册一致吗?
   ├── 不一致 → 改类型
   └── 一致 → 继续
   ↓
3. 试四种字节顺序
   ↓
   哪个拼出来合理,就用哪个
   ↓
4. 检查比例系数
   ↓
   值合理但单位不对,就乘比例

十、核心总结

你只需要记住 说明
16 位 = 1 个寄存器 UINT16 / INT16
32 位 = 2 个寄存器 UINT32 / INT32 / FLOAT
64 位 = 4 个寄存器 DOUBLE / INT64 / UINT64
INT 能存负数,UINT 不能 看数据会不会为负
用哪个类型 看设备手册
读出来不对 按四步排查:寄存器值 → 类型 → 字节顺序 → 比例
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容