出现问题的现象
在用Keil对STM32的程序进行仿真时,程序有时候回跑飞,停止仿真程序会停在HardFault_Handler函数里的死循环while(1)中。这说明STM32出现了硬件错误。
------------------------------------- 图 1 -------------------------------------
STM32出现HardFault_Handler故障的原因主要有两个方面:
- 内存溢出或者访问越界。
- 堆栈溢出。
出现问题后查找问题点的方法
方法1:
① 发生异常后先查看LR寄存器中的值,确定当时使用堆栈为MSP还是PSP。
寄存器组介绍
处理器拥有R0-R15的寄存器组,其中R13为堆栈指针SP,SP有两个,但是同一时刻只能有一个可以看到,这就是所谓的“banked”(影子)寄存器。
a、R0-R12都是 32位通用寄存器,用于数据操作。但是注意:绝大多数 16位Thumb指令只能访问R0-R7,而 32位 Thumb-2指令可以访问所有寄存器,函数调用时的形参和局部变量被压入在其中。b、Cortex-M3拥有两个堆栈指针,然而它们是 banked,因此任一时刻只能使用其中的一个。
主堆栈指针(MSP):复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包括中断服务例程)
进程堆栈指针(PSP):由用户的应用程序代码使用。堆栈指针的最低两位永远是0,这意味着堆栈总是4字节对齐的。c、R14:连接寄存器(LR)当呼叫一个子程序时,由R14存储返回地址
d、R15:程序计数寄存器(PC)指向当前的程序地址,如果修改它的值,就能改变程序的执行流(这里有很多高级技巧)
e、Cortex-M3还在内核水平上搭载了若干特殊功能寄存器,包括程序状态字寄存器组(PSRs)中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI)
控制寄存器(CONTROL)
在Keil菜单栏点击【view】->【Registers Window】打开寄存器窗口,查看 R14(LR) 的值。
若R14(LR) = 0xFFFFFFE9, 查看 MSP(主堆栈指针)的值;
若R14(LR) = 0xFFFFFFFD, 查看 PSP(进程栈指针)的值。
------------------------------------- 图 2 -------------------------------------
上图的R14(LR) = 0xFFFF FFFD,则查看 PSP(进程栈指针)的值。
② 找到相应堆栈的指针,在内存中查看相应堆栈里的内容。
由于异常发生时,内核将R0~R3、R12、R14(LR)、PC、XPRS寄存器依次入栈,其中R14(LR)即为发生异常前PC将要执行的下一条指令地址。
注意:寄存器均是32位、且STM32是小端模式(高位在后面,逆序读)。(参考Cortex_M3权威指南)
------------------------------------- 图 3 -------------------------------------
从 图2 可看到SP的值为 0x20000B2C,在Keil菜单栏点击【view】->【Memory Windows】->【Memory1】,在【Address】地址栏中输入SP的值 0x20000B2C,然后查看堆栈里面的值依次是 R0~R3、R12、R14(LR)、PC、XPRS。
------------------------------------- 图 4 -------------------------------------
显然堆栈后第21个字节到24字节即为LR,图4显示该地址为0x08005907 即为异常前PC将要执行的下一条指令地址。
③ 找到将要执行下一条指令的位置
在Keil菜单栏中点击【view】->【Disassembly Window】,在【Disassembly Window】窗口中右击,在下拉菜单中选择【Show Disassembly at Address...】。在弹出框【Show Code at Address】的地址框中输入地址 0x08005907 进行搜索,然后就会找到相对应的代码。
------------------------------------- 图 5 -------------------------------------
方法2
① 在中断函数HardFault_Handler里的while(1)处打个断点
------------------------------------- 图 6 -------------------------------------
② 返回到出错的函数位置
在Keil菜单栏点击【view】->【Call Stack Window】,弹出【Call Stack + Locals】对话框,右键点击对话框中的【HardFault_Handler】,选择【Show Caller Code】,就会跳到出错的函数位置。
------------------------------------- 图 5 -------------------------------------