H.Sean.N 2025-11-25 14:15 采纳率: 40%
浏览 5

数据处理字序,联合体


uint8_t* l_prev = data;//指向接收到的数据                          
case 0x10for(indexi=0; indexi<ems_ReqMsg->Num; indexi++)
            {
                if (ems_ReqMsg->Protocol_Type == 1)//TCP
                {    
                    Modbus_REG_EMS.bit.BYTE1 = *(l_prev+indexi*2+13);
                    Modbus_REG_EMS.bit.BYTE0 = *(l_prev+indexi*2+14);
                }else if (ems_ReqMsg->Protocol_Type == 0)//RTU
                {
                    Modbus_REG_EMS.bit.BYTE1 = *(l_prev+indexi*2+7);
                    Modbus_REG_EMS.bit.BYTE0 = *(l_prev+indexi*2+8);
                }
                Data[indexi] = Modbus_REG_EMS.all;
            }
            break;   

这个程序分别对 01 10 9D B1 00 02 04 00 00 13 88 C0 D7 与 00 00 00 00 00 0B 01 10 9D B1 00 02 04 00 00 13 88 处理,
为什么RTU解析正确, 00 00 13 88 ,TCP解析错误 13 88 00 00 ?

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2025-11-25 14:24
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    根据你提供的代码和描述,问题出现在 TCP 和 RTU 协议下对数据的字节顺序处理不一致。具体来说,RTU 解析正确,而 TCP 解析错误,这表明在 TCP 模式下,程序对 字节顺序(Endianness) 的处理与实际数据不符。


    一、问题分析

    1. 数据结构定义

    你使用了 Modbus_REG_EMS.bit.BYTE0Modbus_REG_EMS.bit.BYTE1 来表示一个 16 位寄存器的两个字节,说明这是一个联合体(union),用于将两个字节组合成一个 16 位整数。

    typedef union {
        uint16_t all;
        struct {
            uint8_t BYTE0 : 8;
            uint8_t BYTE1 : 8;
        } bit;
    } Modbus_REG_EMS;
    

    这种结构中:

    • BYTE0 是低字节(LSB)
    • BYTE1 是高字节(MSB)

    也就是说,Modbus_REG_EMS.all = (BYTE1 << 8) | BYTE0


    2. RTU 模式下的处理逻辑

    Modbus_REG_EMS.bit.BYTE1 = *(l_prev + indexi*2 + 7);
    Modbus_REG_EMS.bit.BYTE0 = *(l_prev + indexi*2 + 8);
    
    • BYTE1 是高位字节
    • BYTE0 是低位字节
    • 这种方式是正确的,因为 RTU 协议中寄存器是以大端序(Big Endian)传输的

    3. TCP 模式下的处理逻辑

    Modbus_REG_EMS.bit.BYTE1 = *(l_prev + indexi*2 + 13);
    Modbus_REG_EMS.bit.BYTE0 = *(l_prev + indexi*2 + 14);
    
    • 同样是 BYTE1 在前,BYTE0 在后
    • TCP 协议中的寄存器是以小端序(Little Endian)传输的,即低位字节在前,高位字节在后

    因此,在 TCP 模式下,如果仍然按照 大端序 处理,就会导致 字节顺序错误,从而得到错误的数据值。


    二、具体示例分析

    示例 1:RTU 数据(正确解析)

    原始数据(RTU):00 00 13 88

    • 每个寄存器占 2 字节
    • 第一个寄存器:13 88
    • 按照大端序(RTU):0x8813 → 正确值为 35347

    示例 2:TCP 数据(错误解析)

    原始数据(TCP):13 88 00 00

    • 按照大端序处理:0x8813 → 错误地认为是 0x8813,但实际应为 0x00001388(即 3464
    • 如果按小端序处理:0x8813 → 正确值为 0x1388(即 5000

    三、解决方案

    解决方案 1:统一处理方式,根据协议类型调整字节顺序

    修改后的代码如下:

    case 0x10:
        for (indexi = 0; indexi < ems_ReqMsg->Num; indexi++) {
            if (ems_ReqMsg->Protocol_Type == 1) { // TCP: Little Endian
                Modbus_REG_EMS.bit.BYTE0 = *(l_prev + indexi * 2 + 13); // 低字节
                Modbus_REG_EMS.bit.BYTE1 = *(l_prev + indexi * 2 + 14); // 高字节
            } else if (ems_ReqMsg->Protocol_Type == 0) { // RTU: Big Endian
                Modbus_REG_EMS.bit.BYTE1 = *(l_prev + indexi * 2 + 7);  // 高字节
                Modbus_REG_EMS.bit.BYTE0 = *(l_prev + indexi * 2 + 8);  // 低字节
            }
            Data[indexi] = Modbus_REG_EMS.all;
        }
        break;
    

    注意: 在 TCP 模式下,将 BYTE0 放在前面,BYTE1 放在后面,以符合 小端序 的规则。


    解决方案 2:使用标准函数进行字节顺序转换

    如果你希望更通用,可以使用标准库函数如 ntohs() 或手动实现字节交换:

    // 使用 ntohs 函数(网络字节序转主机字节序)
    uint16_t value = ntohs(*((uint16_t*)(l_prev + offset)));
    
    // 或者手动交换字节
    uint16_t value = ((*(l_prev + offset + 1)) << 8) | (*(l_prev + offset));
    

    四、重点总结

    | 问题点 | 解决方案 | |--------|----------| | 字节顺序错误 | TCP 应使用小端序,RTU 使用大端序 | | 联合体结构定义 | 确保 BYTE0 是低字节,BYTE1 是高字节 | | 代码逻辑差异 | 根据协议类型调整 BYTE0BYTE1 的赋值顺序 | | 推荐做法 | 使用 ntohs() 或手动交换字节,确保跨平台一致性 |


    五、最终建议

    • 统一处理逻辑:根据协议类型(TCP/RTU)决定字节顺序。
    • 测试用例验证:使用已知的 TCP/RTU 数据包进行测试,确保解析结果正确。
    • 文档记录:明确每种协议的字节顺序要求,避免后续开发人员混淆。

    如有更多关于 Modbus 协议或字节顺序的问题,欢迎继续提问!

    评论

报告相同问题?

问题事件

  • 修改了问题 11月25日
  • 创建了问题 11月25日