笔记来源-STM32嵌入式开发公众号(分析ARM Cortex-M内核复位过程)
复位序列
大部分CPU复位后都是从0x0000 0000处取得第一条指令开始运行的,然而在ARM Cortex-M内核中的复位序列不同。
ARM Cortex-M内核中的复位序列过程:
![](https://i-blog.csdnimg.cn/blog_migrate/587e926ff77fa891e635420797c214a4.png)
中断向量表默认是在复位向量处,但是中断向量表的位置也可以改变。
在ARM Cortex-M内核中,发送异常后,并不是执行中断向量表对应的代码,而是将对应处的数据存入PC中,然后去此地址处进行取指。也就是,在ARM Cortex-M的中断向量表存放是ISR程序的入口地址。复位相当于发生了一次Reset异常,地址0x0000 0004处存放的正是Reset异常对应的中断处理函数入口地址。
需要注意的是:
0x0000 0000处的MSP初始值最低三位需要是0。因为手册上规定栈任何时候都必须4字节对齐,在调入入口需要2字节对齐,而且SP的最低两位在硬件上就被置为0了。
0x0000 0004处存放的地址最低位必须为1。这与ARM模式和Thumb模式有关。ARM中PC的地址必须是32位对齐,其最低两位也被硬件置0,所以写入PC的数据最低两位并不代表真实的取值地址。ARM中使用最低一位判断这条指令是ARM指令还是Thumb指令(0-ARM、1-Thumb)。在Cortex-M内核中,并不支持ARM模式,若强行切换到ARM模式会引起HardFault。
HardFault_Handler问题查找方法
出现问题的现象:在对Keil对STM32程序调试时意外跑飞,停止调试时停在HardFault_Handler函数的while(1)里,说明STM32出现了硬件错误。
有两种原因:
内存溢出或访问越界。
堆栈溢出。
查找问题方法1:
发生异常后先查看LR寄存器的值,确定当时使用的堆栈是MSP还是PSP。
在Keil里view-Registers Window打开寄存器窗口,查看R14(LR)的值。
若R14(LR) = 0xFFFF FFE9,查看MSP(主堆栈指针)的值;
若R14(LR) = 0xFFFF FFFD,查看PSP(进程堆栈指针)的值。
![](https://i-blog.csdnimg.cn/blog_migrate/65cfd4cb4974e972a0cb92d013b9bb16.png)
上图的R14(LR) = 0xFFFF FFFD,则查看PSP(进程堆栈指针)的值。
找到相应堆栈的指针,在内存中查看相应堆栈的内容。
由于异常发生时,内核将R0~R3、R12、R14(LR)、PC、XPRS寄存器依次入栈,其中R14(LR)即为发送异常前PC将要执行的下一条指令地址。
注意:寄存器均是32位、且STM32是小端模式。
当CM3开始相应一个中断/异常时,会有三个动作:
入栈:把xPSR、PC、LR、R12、R3~R0寄存器的值压入栈。
取向量:从中断向量表中找出对应的服务程序入口地址。
选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC。
响应异常的第一个动作,就是自动保存现场的必要部分:依次把xPSR、PC、LR、R12、R3~R0由硬件自动压入适当的堆栈中;如果当响应异常时,当前的代码正在使用PSP,则压入PSP,否则压入MSP。一旦退出服务例程,就将一直使用主堆栈。
上图中可看出,SP的值为0x2000 19A8,keil里点击view-Memory Windows-Memory1,在Address地址栏输入SP的值,然后查看堆栈里的值,依次是R0~R3、R12,R14(LR)、PC、XPRS。
![](https://i-blog.csdnimg.cn/blog_migrate/6d2f0db806d41fda008ac1293075589b.png)
即地址为0x0800 8263为异常前PC将要执行的下一条指令地址。
找到将要执行下一条指令的位置
keil里点击view-Disassembly Window-右击-Show Disassembly at Address...。在弹出框Show Code at Address的地址框输入地址0x0800 8263进行搜索,然后就可以找到对应的代码了。
![](https://i-blog.csdnimg.cn/blog_migrate/0ec0f3e7449af6fc623d5a99391b5aac.png)
查找问题方法2:
在中断函数HardFault_Handler里的while(1)打个断点。
运行到断点时。返回到出错的函数位置。keil里点击View-Call Stack Window,弹出Call Stack + Locals对话框,右击对话框的HardFault_Handler,选择Show Caller Code,就会跳到出错的函数位置。
![](https://i-blog.csdnimg.cn/blog_migrate/2ca1315c1e8676d7bd53c830b1a4f002.png)