ARTICLE · 1165409
对 Modbus 协议文档里的倍率,乘还是除?

在模拟器集成设备之后,出现了一个现象,模拟器页面设置频率的值为 19,但 EMS 端读取到的值是 20。为什么会出现这个问题?先看协议内容。
原因要从对协议里倍率的理解说起。协议文档里的倍率写法(10 或 0.1)是约定俗成的,但不同人读到这个数字,直觉上的理解可能不一样。
我当时的理解是,19 是实际值,应该先除以 10 才是原始值(也就是寄存器里存的值),也就是 1.9。但 16 位无符号数不能存小数,1.9 四舍五入就变成了 2。
其实仔细一想,EMS 端读出 20 本身也搞反了。
按照协议倍率是 10,EMS 端拿到原始值之后原本应该除以 10。如果它真的除了 10,面对原始值 2,读出来的应该是 0.2,大家一眼就能发现异常。偏偏 EMS 也理解错了,拿原始值 2 乘以了 10,碰巧算出了 20。
模拟器把存入当成了除法,EMS 把读取当成了乘法,两边全搞反了。结果 19 变成 20,反而让人误以为“只是差了 1 的四舍五入误差”。
只能说,两端各错各的,阴差阳错掩盖了真正的问题。
在这个现象里,主要研究的问题有下面这些:
16 位无符号数为什么不能存小数?为什么默认四舍五入? 为什么倍率两种写法 10、0.1 实际上表达的是同一个意思?
第一个问题,其实也解释了为什么要设计“倍率”这个东西。
正是因为 Modbus 通信中寄存器只能存储整数(16 位 / 32 位),而实际物理量,比如电压、频率、功率,通常都带有小数,所以才用“倍率”把实际值放大成整数(原始值)存入寄存器,读取时再除以倍率还原出来。
至于四舍五入,是因为整数只能取最近的整数值,这是数据类型本身的约束。
第二个问题,看表格里两行数据,频率的倍率是 10,电压的倍率是 0.1,看上去方向相反,其实只是视角不同。
倍率为 10:意思是“原始值 = 实际值 × 10”,读取时除以 10; 倍率为 0.1:意思是“实际值 = 原始值 × 0.1”,也是除以 10。
两种写法描述的是同一个放大关系,只是一个从“存入”的角度写,另一个从“读出”的角度写。
那现在应该怎么处理呢?
不管怎么说,让模拟器把实际值除以 10 再存入寄存器,总是会碰到小数截断问题的。除非末位恰好是 0 才能整除,但这样一来就限制了模拟器的使用。
很多场景不一定末位是 0,如果真遇到 999、3333.3 这类值,末尾的 0.9 和 0.33 就必然丢失了。而倍率设计的初衷本来就是为了解决小数点问题,这样做反而和初衷相悖了。
所以,选择的处理方案就是,模拟器端永远乘以放大倍数,EMS 端永远除以放大倍数。
把这些理解梳理成公式和示意图,那就是:
放大倍数 = 倍率 > 1 ? 倍率 : 1 / 倍率 模拟器:原始值 = 实际值 × 放大倍数 EMS:实际值 = 原始值 ÷ 放大倍数
