一.问题环境
最近在调试STM32F030K6单片机,用的STM32F0xx_StdPeriph_Lib_V1.5.0标准外设固件库。搭建了BSP+SYSTEM+HARDWARE+APP框架,同样的框架在STM32F103单片机及其BSP下,APP正常流畅运行,可更换为配置好的STM32F030K6的BSP及单片机板后,总是出现各种各样的问题。具体问题如下:
二.问题现象
-
1.程序没有进入while循环就卡死,跑飞,进入硬件错误中断(HardFault_Handler)。
-
开启仿真调试,发现程序卡在含有结构体变量的初始化部分。结构体通过传递指针地址作为参数,并传递给HARDWARE层
-
在进入HARDWARE的函数后,将结构体指针的BSP配置部分继续传递给BSP的时候,还没有进入BSP层的函数的调用前,程序就跑飞了。进入了硬件错误中断。
-
单片机采用8MHz的外部晶振,并通过采用6倍频的方式,使时钟频率达到48MHz,并且配置FLASH_SetLatency(FLASH_Latency_1)仍没有效果。
-
-
2.将APP的结构体包含的成员结构体变量调下位置,可以运行到下一个语句,但仍然卡死。
- APP的HARDWARE结构体成员定义方式由变量改为指针,并为每个HARDWARE结构体单独创建实例,把其地址传给APP的HARDWARE结构体成员指针。
- 能够进入while循环。
- 当运行while循环到达一定周期后,再次跑飞卡死,进入HardFault_Handler
- 发现问题仍然是访问了结构体某个成员造成错误
三.原因分析
-
通过以上的结构体问题,我发现问题多半是由于结构体的大小不确定造成的。而结构体的大小通常由其对齐方式决定。
-
编译器通常对结构体按默认的4字节对齐,并填充相应的字节。
-
结构体的对齐是指在内存中为结构体的成员分配存储位置时,按照特定规则调整其地址,以确保每个成员都在满足其对齐要求的地址上。这种对齐要求通常基于数据类型的大小。例如:
char
类型的对齐为 1 字节short
类型的对齐为 2 字节int
和float
类型的对齐为 4 字节double
类型的对齐为 8 字节
-
为什么使用F0系列的单片机,需要对结构体进行单字节对齐
F0系列的单片机可能会对内存访问有更严格的对齐要求,尤其是在访问某些外设寄存器或特定内存区域时。如果结构体中的数据成员没有按照其对齐要求存放,可能会导致以下问题:
- 硬件异常:某些寄存器在读取时,如果地址未对齐,可能导致硬件异常,甚至系统崩溃。
- 性能影响:未对齐访问可能导致CPU执行多次内存读取操作,降低性能。
因此,在F0系列中,为了确保数据访问的正确性和高效性,通常需要对结构体进行单字节对齐。
- 为什么使用F1系列的单片机,同样的程序没有跑飞呢?
F1系列的单片机对内存访问的要求可能较为宽松,允许未对齐访问,因此在相同的程序中,虽然结构体未严格遵循对齐规则,仍能正常运行。原因可能包括:
- 容忍度更高:F1系列的处理器可能对未对齐访问有更好的容错能力,能在一定程度上自动处理未对齐的数据访问。
- 软件优化:可能在编译器或程序设计上进行了优化,能够避免因未对齐访问导致的问题。
四.问题解决
-
对所有结构体进行单字节对齐。
结构体代码统一加入:
#pragma pack(1) //1字节对齐 #pragma anon_unions //开启匿名结构、联合 union Frame { uint32_t buf[FRAME_BUF_SIZE]; struct { uint8_t head; uint8_t length; uint8_t cmd; uint8_t data[FRAME_BUF_SIZE - 4]; uint8_t check; }; }; #pragma no_anon_unions //关闭匿名结构、联合 #pragma pack() //取消字节对齐
-
加入对齐后,程序稳定运行。