尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

当0x1234变成0x3412:嵌入式通信里那个“反着来”的魔鬼细节

当0x1234变成0x3412:嵌入式通信里那个“反着来”的魔鬼细节 1. 什么是大端序和小端序假设一个 16 位数值0x1234它由两个字节组成高字节0x12低字节0x34。它们在内存或通信流中的排列顺序有两种完全相反的约定。模式英文名核心原则0x1234在内存/通信流中的存储顺序常见阵营大端序Big-Endian高位字节存/发在低地址前面低地址/先发0x12然后0x34网络字节序TCP/IP、I2C 总线、SPI 总线、Motorola 处理器小端序Little-Endian低位字节存/发在低地址前面低地址/先发0x34然后0x12x86/AMD 处理器、STM32Cortex-M、龙芯 1C102MIPS记忆口诀大端序高高在上像我们读数字的习惯先读高位一百再读低位二十三。小端序低前矮后像反转的数字流。2. 你学过的实例中哪里会碰到实例一MPU6050 的加速度数据大端序通信MPU6050 的加速度 X 轴数据存于ACCEL_XOUT_H (0x3B)和ACCEL_XOUT_L (0x3C)两个 8 位寄存器。它们是大端序传输先读高字节再读低字节。你通过 I2C 连续读 0x3B 和 0x3C会收到两个字节先收到的字节0x12 后收到的字节0x34这两个字节拼成 16 位值0x1234。因为先收到的0x12是高位后收到的是低位。你代码里的拼接必须是int16_t accel_x (data_high 8) | data_low; // 结果accel_x 0x1234这就是大端序解码。如果这是一台小端序机器你正好在把通信流里的大端序数据转为处理器的小端序变量。实例二龙芯 1C102 的内存存储小端序处理器你的龙芯 1C102 是 MIPS 架构通常是小端序。当你在代码里定义一个 32 位变量uint32_t val 0x12345678;时它在内存中的实际存储顺序是内存地址 (低→高)存储的内容0x10000x78最低位字节0x10010x560x10020x340x10030x12最高位字节这是小端序低地址存低位字节。3. 当大端序设备遇上小端序处理器这就是嵌入式通信里端序冲突的根源。设备端MPU6050、RC522、网络数据包几乎统一用大端序这叫网络字节序Network Byte Order。处理器端龙芯 1C102、STM32、x86大多用小端序这叫主机字节序Host Byte Order。当你把从 MPU6050 读到的两个字节0x12 0x34用(high 8) | low拼成0x1234时你实际上在执行一次从大端序通信流到小端序处理器变量的隐式转换。因为你的 CPU 认为0x1234在内存里应该是0x34 0x12但数据拼出来直接存入寄存器或变量时编译器会帮你处理好你不必手动翻转字节——只要你拼的顺序是高位在左、低位在右。所以你的代码(data_high 8) | data_low是通用的无论 CPU 是大小端只要通信协议是先发高字节大端就能正确还原成处理器期望的数值。4. 端序问题的实战陷阱面试中一个经典的“下马威”问题是这个题目你在一个小端序的 MCU 上从 I2C 设备读到了一个 16 位值两个字节分别是0x12先收到和0x34后收到。你用memcpy直接把它拷贝到一个uint16_t变量里。这个变量的值是多少分析通信流里先收到0x12放在数组低地址buf[0]后收到0x34放在buf[1]。memcpy到uint16_t val小端序 CPU 把低地址buf[0]的值当成低位。所以val的内存布局是低地址0x12低位高地址0x34高位。结果val 0x3412和你想表达的0x1234完全颠倒了。正确做法永远手动拼接或用htons()/ntohs()这类字节序转换函数不要直接 memcpy。
返回列表