REAL4 的两层字节序

Modbus RTU 的寄存器是 16 位的。当传输 32 位浮点数(REAL4)时,需要两个寄存器。问题来了:

  1. 字序(Word Order):高 16 位在前还是低 16 位在前?
  2. 字节序(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 位解析都走同一个路径

踩过这个坑之后,建议在项目文档里明确记录每个设备的字节序配置,避免下次换人维护时再踩一次。

后续阅读