下午好🌅🌅🌅
本答案参考通义千问
根据你提供的代码和描述,问题出现在 TCP 和 RTU 协议下对数据的字节顺序处理不一致。具体来说,RTU 解析正确,而 TCP 解析错误,这表明在 TCP 模式下,程序对 字节顺序(Endianness) 的处理与实际数据不符。
一、问题分析
1. 数据结构定义
你使用了 Modbus_REG_EMS.bit.BYTE0 和 Modbus_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 是高字节 |
| 代码逻辑差异 | 根据协议类型调整 BYTE0 和 BYTE1 的赋值顺序 |
| 推荐做法 | 使用 ntohs() 或手动交换字节,确保跨平台一致性 |
五、最终建议
- 统一处理逻辑:根据协议类型(TCP/RTU)决定字节顺序。
- 测试用例验证:使用已知的 TCP/RTU 数据包进行测试,确保解析结果正确。
- 文档记录:明确每种协议的字节顺序要求,避免后续开发人员混淆。
如有更多关于 Modbus 协议或字节顺序的问题,欢迎继续提问!