STM32出现HardFault_Handler故障的原因
STM32系统中,HardFault_Handler故障主要有两个方面的原因:
- 内存溢出或访问越界
- 堆栈溢出
最近我遇到的问题是栈溢出,情况如下:
static char data[10000];
void fun1(unsigned char *buf) {
int i = 0;
for (i = 0; i < 5000; i++) {
data = buf;
}
}
void fun2(void) {
unsigned char buf[5000];
// 其他代码
fun1(buf); // 执行完毕此函数后出现硬件错误 HardFault_Handler
printf("data: %s\r\n", buf);
}
int main() {
// 其他代码
fun2();
// 其他代码
while (1);
}
问题分析
通过断点调试,在进入fun1(buf)
函数时发现SP指向了数组data
所开辟的空间,同时PC和其他寄存器的值也压入了栈。在循环执行data = buf
时,修改了压入栈的数据,导致在退出fun1(buf)
时PC指向了错误的位置。
问题一:为什么SP会指向数组data所开辟的空间?
原因是发生了栈溢出。
问题二:是什么导致了堆栈溢出?
我们可以查看相关资料,了解堆栈的概念。
在startup_stm32f10x_md.s
文件中,可以看到如下定义:
Stack_Size EQU 0x00000400
Heap_Size EQU 0x00000200
堆和栈的区别
- 栈区(stack):由编译器自动分配和释放,存放函数的参数值、局部变量等,操作方式类似于数据结构中的栈。
- 堆区(heap):一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收,分配方式类似于数据结构中的链表。
- 全局区(静态区)(static):全局变量和静态变量的存储,初始化和未初始化的变量分别存放在不同的区域,程序结束后由系统自动释放。
- 文字常量区:存放常量字符串等数据。
- 程序代码区:存放函数体的二进制代码。
示例:
int a = 0; // 全局初始化区
char *p1; // 全局未初始化区
int main() {
int b; // 栈
char s[] = "abc"; // 栈
char *p3 = "1234567"; // 文字常量区
static int c = 0; // 静态初始化区
p1 = (char *)malloc(10); // 堆区
strcpy(p1, "123456"); // "123456" 放在常量区
}
堆栈溢出原因分析
明白堆栈的分配原理后,我们可以确认是栈溢出而非堆溢出。导致栈溢出的原因在于unsigned char buf[5000];
的定义,buf的开辟占用了较大的栈空间,超出了在startup_stm32f10x_md.s
文件中定义的栈大小,从而导致栈溢出。
问题总结
- 当函数内部变量占用空间较大时,建议将其定义为全局变量或静态变量,以减少堆栈的占用。
- 通过使用指针解决数据复制问题,进一步降低内存占用。