单片机 STM32 大小端对程序的影响
更多
好的,STM32作为基于ARM Cortex-M内核的微控制器,其字节序(大小端)会对程序产生几方面显著的影响。了解这些影响对于编写正确、稳定和兼容的嵌入式代码至关重要。
核心概念回顾:
- 小端模式(Little-Endian): 低地址存储数据的最低有效字节(Least Significant Byte - LSB)。
- 大端模式(Big-Endian): 低地址存储数据的最高有效字节(Most Significant Byte - MSB)。
STM32的大小端配置:
- ARM Cortex-M内核默认配置为小端模式(Little-Endian)。
- 绝大多数STM32应用程序都使用小端模式。
- Cortex-M内核提供了在系统启动时(在加载栈指针和调用
main之前)通过配置控制寄存器(如AIRCR的ENDIANNESS位)来切换为大端模式的能力。但这非常罕见,通常只在需要严格兼容特定外部大端设备或协议时才会这么做。
大小端对STM32程序的具体影响:
-
数据访问(尤其是强制类型转换和结构体/联合体):
- 问题: 这是最常见的影响点。当你使用强制类型转换(
(uint16_t*)、(float*))或通过结构体/联合体访问不同数据类型的数据时,大小端决定了内存中字节的排列顺序。 - 例子: 假设你有一个
uint32_t变量value = 0xAABBCCDD;,存储在地址0x20000000开始的内存中。- 小端模式下:
0x20000000:0xDD(LSB)0x20000001:0xCC0x20000002:0xBB0x20000003:0xAA(MSB) 如果程序中使用uint16_t *ptr = (uint16_t*)&value;,那么*ptr在地址0x20000000处读取到的将是0xDDCC。
- 大端模式下:
0x20000000:0xAA(MSB)0x20000001:0xBB0x20000002:0xCC0x20000003:0xDD(LSB) 同样的指针操作,*ptr(在0x20000000)读取到的将是0xAABB。
- 小端模式下:
- 影响: 如果代码期望以特定方式解释内存中的数据(比如,通过
uint16_t指针读取uint32_t的低16位),而在另一种大小端模式下编译运行,结果会完全错误。
- 问题: 这是最常见的影响点。当你使用强制类型转换(
-
外设寄存器访问:
- 问题: STM32的外设寄存器有自己固定的物理字节顺序(通常按位域在寄存器中的位置定义,这种顺序与CPU的大小端模式无关)。
- ST的硬件库(HAL/LL, 标准外设库): 这些库已经为你处理了大小端问题。当库函数使用
uint32_t *指针访问一个32位寄存器时,编译器会根据STM32当前的大小端模式进行正确的内存访问。只要寄存器被定义为特定的uint32_t类型,库函数就能正确读写寄存器的值。 - 手动寄存器操作: 如果你自己直接通过绝对地址指针访问寄存器(不推荐,但有时需要),你需要非常小心:
- 即使在小端STM32上,物理寄存器的位
[31:24]仍然对应最高字节。当你用uint32_t *reg = (uint32_t*)0x40000000; *reg = 0xAABBCCDD;写入时,内核会自动根据当前大小端模式将0xDD写到低地址(小端)或将0xAA写到低地址(大端)。在另一端读取该寄存器的设备必须知道STM32当前的大小端模式才能正确解释数据。 - 使用
__packed结构体访问寄存器位域需要极其小心,因为__packed会强制不同字节/半字的位域使用紧密内存排列,其解释也依赖于大小端。这容易出错,应尽量避免。建议使用库提供的位域宏或直接使用位操作(&, |, ~, <<, >>)。 - 以字节(
uint8_t *)或半字(uint16_t *)方式访问寄存器的一部分时,访问到的内容也取决于STM32当前的大小端模式,这可能与外设寄存器定义的内部位域顺序不一致。
- 即使在小端STM32上,物理寄存器的位
-
外设数据传输:
- 问题: 通过外设(如UART, SPI, I2C, CAN, USB, Ethernet, DMA)发送和接收的数据流(字节序列)通常依赖于应用层的协议定义。
- 数据打包/解包: 当协议要求将多字节数据(如
int16_t,int32_t,float)传输为一个字节序列时,发送方和接收方必须约定这些多字节数据的字节序。 - 影响: 例如,UART协议本身没有定义字节序。如果STM32用小端方式存储一个
uint32_t sensorValue,然后简单地将&sensorValue的地址交给DMA或直接用指针按字节循环发送,它会从低地址开始发送,也就是先发LSB。如果接收方期望的是大端格式(先发MSB),那么接收方解析到的将是错误的值。解决方案通常是在发送前或接收后进行显式的字节序转换(Byte Swapping),使用htons,htonl,ntohs,ntohl函数或自定义的位操作来实现。 - 使用DMA: DMA控制器在执行外设<->内存或内存<->内存传输时,它会按字节或设定的字长进行复制,但不会自动改变字节顺序。如果外设的字节序与内存中的期望不一致(比如内存中变量是小端,而外设的数据线要求特定顺序),就需要在数据传输前或后进行软件转换。
-
与外部系统的兼容性:
- 问题: 当STM32与其他系统(可能是大端的,如某些DSP、PowerPC处理器、网络协议、文件格式)通信或共享数据(如通过共享内存、文件系统)时,字节序必须匹配。
- 影响: 如果不处理差异,双方无法正确解读交换的数据。网络通信中通常使用网络字节序(即大端序) 作为标准,所以STM32(小端)在发送网络数据前需要用
hton*()系列函数转换为大端序,接收后需要用ntoh*()函数转换回小端序。
最佳实践和建议:
- 默认为小端模式: 除非有强制性的特定需求(如兼容外部大端芯片或协议),否则应坚持使用STM32的默认小端模式。更改需要谨慎配置内核寄存器。
- 避免依赖字节序的代码: 尽量避免代码逻辑依赖于特定的内存字节排列。尤其是:
- 慎用强制类型转换读取内存的不同部分,除非你非常清楚当前大小端模式。
- 避免依赖结构体或联合体内存布局进行跨字节边界的位域访问(尤其使用
__packed时)。优先使用位操作(&, |, ~, <<, >>)来访问寄存器的位。
- 使用字节序转换函数:
- 在与外部系统通信或处理协议数据时,显式进行字节序转换是最可靠的做法。
- 标准做法是定义并使用类似网络字节序转换的函数:
uint16_t swap_bytes_u16(uint16_t x) { return ((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8); } uint32_t swap_bytes_u32(uint32_t x) { return ((x & 0xFF000000) >> 24) | ((x & 0x00FF0000) >> 8) | ((x & 0x0000FF00) << 8) | ((x & 0x000000FF) << 24); } - 许多编译器(如GCC)也提供内置函数:
__builtin_bswap16,__builtin_bswap32。 - 对于符合POSIX标准的嵌入式系统(可能基于Linux),可以使用标准库中的
htons,htonl,ntohs,ntohl。
- 清晰定义通信协议: 在跨系统通信中,明确协议中所有多字节整数、浮点数等的字节序。统一约定为网络字节序(大端)是一种常见选择。
- 使用标准整数类型: 使用
stdint.h中的固定宽度类型(uint8_t,int16_t,uint32_t等)代替int,long等,避免因平台字长不同带来的混淆(尽管字长变化通常比字节序问题更少)。 - 关注数据流的字节序: 当通过外设(UART, SPI, I2C, DMA等)读写多字节数据到内存时,思考数据流的物理顺序(MSB/LSB First?)以及它如何映射到内存变量的期望顺序,必要时在软件中及时进行转换。
总结:
STM32默认的小端模式本身在代码内部通常不会引起问题。问题主要发生在:
- 涉及强制类型转换、直接指针操作访问不同数据类型或结构体/联合体内部布局时。
- 手动操作外设寄存器字节/半字时。
- 与外部系统通信或交换多字节数据而未正确处理字节序差异时。
- 与外设进行多字节数据传输(包括DMA)但未考虑外设期望的字节序时。
因此,编写健壮的STM32代码,关键在于:
- 理解大小端概念和影响范围。
- 默认用小端,避免依赖字节序的内部操作。
- 在所有跨系统、跨设备或涉及协议的数据传输边界处,显式处理字节序转换。
遵循这些实践,大小端就不会成为程序中的隐藏陷阱。
使用STM32单片机和51单片机实现HC04超声波测距模块的程序免费下载
本文档的主要内容详细介绍的是使用STM32单片机和51单片机实现HC04超声波模块测距的
资料下载
佚名
2019-08-21 17:31:00
stm32单片机引脚介绍及功能
和I/O引脚。stm32的引脚一般有GPIO和AFIO两种用途。 比如有的引脚是电源正极和黑色标记的电源负极,这是来给单片机提供电源的,要把电流引到对应的引脚才能正常工作。 还有晶体震荡电路反向输入
2021-08-19 16:50:15
7天热门专题
换一换
换一换
- 如何分清usb-c和type-c的区别
- 中国芯片现状怎样?芯片发展分析
- vga接口接线图及vga接口定义
- 芯片的工作原理是什么?
- 华为harmonyos是什么意思,看懂鸿蒙OS系统!
- 什么是蓝牙?它的主要作用是什么?
- ssd是什么意思
- 汽车电子包含哪些领域?
- TWS蓝牙耳机是什么意思?你真的了解吗
- 什么是单片机?有什么用?
- 升压电路图汇总解析
- plc的工作原理是什么?
- 再次免费公开一肖一吗
- 充电桩一般是如何收费的?有哪些收费标准?
- ADC是什么?高精度ADC是什么意思?
- EDA是什么?有什么作用?
- dtmb信号覆盖城市查询
- 苹果手机哪几个支持无线充电的?
- type-c四根线接法图解
- 华为芯片为什么受制于美国?
- 怎样挑选路由器?
- 元宇宙概念股龙头一览
- 锂电池和铅酸电池哪个好?
- 什么是场效应管?它的作用是什么?
- 如何进行编码器的正确接线?接线方法介绍
- 虚短与虚断的概念介绍及区别
- 晶振的作用是什么?
- 大疆无人机的价格贵吗?大约在什么价位?
- 苹果nfc功能怎么复制门禁卡
- amoled屏幕和oled区别
- 单片机和嵌入式的区别是什么
- 复位电路的原理及作用
- BLDC电机技术分析
- dsp是什么意思?有什么作用?
- 苹果无线充电器怎么使用?
- iphone13promax电池容量是多少毫安
- 芯片的组成材料有什么
- 特斯拉充电桩充电是如何收费的?收费标准是什么?
- 直流电机驱动电路及原理图
- 传感器常见类型有哪些?
- 自举电路图
- 通讯隔离作用
- 苹果笔记本macbookpro18款与19款区别
- 新斯的指纹芯片供哪些客户
- 伺服电机是如何进行工作的?它的原理是什么?
- 无人机价钱多少?为什么说无人机烧钱?
- 以太网VPN技术概述
- 手机nfc功能打开好还是关闭好
- 十大公认音质好的无线蓝牙耳机
- 元宇宙概念龙头股一览