# 一对反了的字节记一次 ModBus 通信调试## 现象AF1026 振动监测仪上位机调试现场Qt波形图显示 4 通道振动值在 0~5 mm/s 之间无规律满幅跳动。按理说振动传感器在不动的时候值很小且稳定摇晃传感器时设备报警灯亮但上位机数值纹丝不动。因为报警是mcu单独处理的。折腾了大半天改滤波、改量程、硬编码测试……最后一查ModBus 数据区的字节序反了。---## 链路背景- **MCU**TI TM4C123ARM Cortex-M4F**小端**字节序- **通信**ModBus RTU over RS-232115200-8-N-1- **上位机**Qt 6.10.3CMinGWMCU 每 200ms 通过功能码 0xA2 返回 17 字节实时数据| 偏移 | 长度 | 含义 | 类型 ||------|------|------|------|| 0~7 | 8B | DetectResultScaled[4] | uint16_t × 4 || 8~15 | 8B | AfterRAWScaled[4] | int16_t × 4 || 16 | 1B | AlarmStatus | uint8_t |---## 代码怎么写错了**MCU 端**User/main.c 0xA2 处理// 直接在 ARM 小端内存上 memcpy原样发出 memcpy((ModBus_RStruct.Data[0]), (DetectResultScaled[0]), 8); memcpy((ModBus_RStruct.Data[8]), (AfterRAWScaled[0]), 8);ARM 是小端uint16_t 800 0x0320内存里是 [0x20] [0x03]。memcpy 走 ModBus 原样发送 20 03。**PC 端**serialworker.cpp原来的代码cpp// 当成大端解析高字节在前detectValues[i] (int16_t)(((uint16_t)p[i * 2] 8) | p[i * 2 1]);把收到的 20 03 当成大端(0x20 8) | 0x03 0x2003 8195。**实际值应该是** (0x03 8) | 0x20 0x0320 800。**差了多少倍** 安静时的 800 变成了 8195×10剧烈振动时的 31278 变成了 11902÷2.6。小信号被放大大信号被压缩。---## 用数据说话取两帧实测数据对比修前后真实 MCU 发送值: Detect[3] 800 (0x0320) PC旧(大端)读到 8195RAW[2] 734 (0x02DE) PC旧(大端)读到 56894→int16→-8641摇晃时:真实 MCU: Detect[2] 31278 (0x7A2E) PC旧读到 11902Detect[0] 12307 (0x3013) PC旧读到 4915换算成上位机显示的 mm/s量程5.0| 场景 | 通道 | 真实值 | 真实mm/s | 旧PC值 | 旧显示mm/s ||------|------|--------|----------|--------|-----------|| 静止 | CH3 RAW | 734 | 0.11 | 56894→-8641 | 1.32 || 摇晃 | CH3 Detect | 31278 | 4.77 | 11902 | 1.82 || 摇晃 | CH1 Detect | 12307 | 1.88 | 4915 | 0.75 |**静止时 0.11mm/s 被放大到 1.32摇晃时 4.77mm/s 被压缩到 1.82。** 所以上位机看着永远在差不多的区间跳不管怎么晃都不超过5——不是真的噪声大是字节反了之后分布被扭了。---## 为什么 ModBus 帧头没问题但数据区反了看一下 ModBus 响应帧的构造MCU Hardware/Src/modbus.ctmp[2] (char)((StructTmp.Address) 8); // 地址高字节 tmp[3] (char)(StructTmp.Address); // 地址低字节// 帧头手动填大端// 数据区直接 memcpymemcpy(tmp[6], (StructTmp.Data[0]), StructTmp.Number);**帧头地址/数量是手动按大端填的数据区是直接 memcpy ARM 小端字节。** 同一个响应帧里前半截大端、后半截小端。PC 端解析时全按大端处理——帧头碰巧对了数据区全错了。---## 修复**方案AMCU端**在 0xA2 处理中手动转大端再填 Data 数组。要改 MCU、重编译、烧录。**方案BPC端**改解析代码大端改成小端。一行代码重编 Qt 即可。选了方案 B// 修复前大端错 detectValues[i] (int16_t)(((uint16_t)p[i * 2] 8) | p[i * 2 1]); // 修复后小端对 detectValues[i] (int16_t)(((uint16_t)p[i * 2 1] 8) | p[i * 2]); ---## 爬坑心得1. ModBus 标准只规定帧头大端**数据区的字节序是应用层自己的事**——谁写谁负责2. ARM Cortex-M 默认小端memcpy 直接发送就是小端字节流3. PC 端 Qt 的 QSerialPort 只是透传字节字节序不会自动转换4. 跨平台通信先约定好字节序不确定就 htonl/ntohl 或手动 8 0xFF全程忙活最久的滤波代码改动全部打回真正修 bug 只改了一行比特序。