REAL4 的两层字节序
Modbus RTU 的寄存器是 16 位的。当传输 32 位浮点数(REAL4)时,需要两个寄存器。问题来了:
- 字序(Word Order):高 16 位在前还是低 16 位在前?
- 字节序(Byte Order):每个 16 位寄存器内部,高字节在前还是低字节在前?
不同厂商的习惯不同,联调时最常见的问题就是”流量计读出来是乱码”。
四种组合
| 字序 | 字节序 | 寄存器 [0x01, 0x02] 的解释 |
|---|---|---|
| 高字在前 | 大端 | reg0=高16位, reg1=低16位, 寄存器内高字节在前 |
| 高字在前 | 小端 | reg0=高16位, reg1=低16位, 寄存器内低字节在前 |
| 低字在前 | 大端 | reg0=低16位, reg1=高16位, 寄存器内高字节在前 |
| 低字在前 | 小端 | reg0=低16位, reg1=高16位, 寄存器内低字节在前 |
可配置的解析策略
PipeMonitor 用一个全局配置来控制解析方式:
typedef enum
{
FLOWMETER_WORD_ORDER_HIGH_LOW = 0U,
FLOWMETER_WORD_ORDER_LOW_HIGH
} FlowmeterWordOrder;
typedef enum
{
FLOWMETER_BYTE_ORDER_BIG_ENDIAN = 0U,
FLOWMETER_BYTE_ORDER_LITTLE_ENDIAN
} FlowmeterByteOrder;
typedef struct
{
FlowmeterWordOrder word_order;
FlowmeterByteOrder byte_order;
} FlowmeterEndianConfig;
默认配置是”高字在前 + 大端”,联调时可以动态切换。
解析过程
static uint32_t Flowmeter_CombineU32(uint16_t reg0, uint16_t reg1)
{
/* 第一步:处理每个寄存器内部的字节序 */
uint16_t word0 = Flowmeter_ApplyByteOrder(reg0);
uint16_t word1 = Flowmeter_ApplyByteOrder(reg1);
/* 第二步:处理两个寄存器之间的字序 */
if (g_flowmeter_endian.word_order == FLOWMETER_WORD_ORDER_LOW_HIGH)
return ((uint32_t)word1 << 16) | (uint32_t)word0;
return ((uint32_t)word0 << 16) | (uint32_t)word1;
}
static float Flowmeter_RegsToFloat(uint16_t reg0, uint16_t reg1)
{
float value;
uint32_t raw = Flowmeter_CombineU32(reg0, reg1);
memcpy(&value, &raw, sizeof(value)); /* IEEE754 位模式转换 */
return value;
}
联调时的典型症状
症状 1:读出来的流量是天文数字
比如应该读到 12.345 m³/h,实际读到 1.2345e+30。
原因:字序反了,高 16 位和低 16 位对调。
症状 2:读出来的流量是 NaN 或 Inf
原因:字节序反了,IEEE754 的指数和尾数被打乱。
症状 3:偶尔读到正常值,偶尔读到乱码
原因:可能是寄存器地址偏移了一位,读到的是相邻的数据。
调试方法
用 msh 命令读取原始寄存器值,手动验证:
/* 假设读到 reg0=0x4148, reg1=0x0000 */
/* 高字在前+大端:0x41480000 → 12.5 */
/* 低字在前+大端:0x00004148 → 1.5e-40 */
/* 高字在前+小端:0x48410000 → 2048.0 */
/* 低字在前+小端:0x00004841 → 1.5e-40 */
对比流量计手册的寄存器定义,确定正确的组合。
运行时切换
提供 API 允许在运行时切换字节序配置:
FlowmeterEndianConfig config;
config.word_order = FLOWMETER_WORD_ORDER_LOW_HIGH;
config.byte_order = FLOWMETER_BYTE_ORDER_BIG_ENDIAN;
Flowmeter_SetEndianConfig(&config);
联调时可以在 msh 里切换配置,立刻看到解析结果的变化,快速定位正确的组合。
PT100 和 PXW 为什么没这个问题
PT100 返回的是 16 位有符号整数,不存在字序问题。字节序由 Modbus 协议固定为大端(高字节在前),所以直接读取即可。
PXW 的压力和温度也是 16 位整数,同样没有字序问题。
只有流量计的 REAL4 是 32 位,才需要处理字节序。
常见厂商的字节序习惯
| 厂商 | 字序 | 字节序 |
|---|---|---|
| 西门子 | 高字在前 | 大端 |
| 施耐德 | 低字在前 | 大端 |
| ABB | 高字在前 | 大端 |
| 国产多数 | 低字在前 | 大端 |
| 个别国产 | 低字在前 | 小端 |
没有统一标准,联调时一定要看手册或者试出来。
小结
字节序问题是 Modbus 联调的经典坑。PipeMonitor 的解决方案是:
- 可配置:字序和字节序独立配置,覆盖所有组合
- 可运行时切换:联调时快速试错
- 集中管理:一个全局配置,所有 32 位解析都走同一个路径
踩过这个坑之后,建议在项目文档里明确记录每个设备的字节序配置,避免下次换人维护时再踩一次。