咱们在调试时,最惧怕就是出现HardFault错误了。由于咱们不知道是从哪一个地方跳到这里的?单步调试起来太过于麻烦,特别在代码量大的时候,更是费时间。函数
那么有没有一种方法,能够快速定位到发生HardFault错误的代码位置(函数)呢?工具
咱们知道,HardFault实质上是一个中断,中断的过程以下图:指针
FUNC1->IRQ_Handler:发生中断 Note right of IRQ_Handler:中断处理函数 IRQ_Handler->FUNC1:中断返回
在这里我就有一个疑问了:既然FUNC1和IRQ_Handler之间不存在调用关系,那IRQ_Handler运行完以后怎么返回到FUNC1中去的呢?调试
中断/异常响应原理:当Cortex-M3响应一个中断时,会在它体内奔涌起三股暗流:code
1.入栈:吧8个寄存器(R0~R3,R12,LR,PC,xPSR)的值压入栈中;blog
2.取向量:从向量表中找出对应的中断服务程序入口地址(函数指针的原理);开发
3.更新PC指针,堆栈指针SP,链接寄存器LR的值。编译器
摘自《ARM Cortex M3权威指南》编译
明白原理后,就好办了。那么咱们在HardFault函数中打一個断点,在他刚进入中断时,内核寄存器的值,就是保存发生中断那一刻的值。咱们就能够经过这些值,获得发送错误的位置。table
下图是出现错误时状态:
咱们关注SP寄存器的值:0x2000FFAC,咱们再查看SP地址附近所对应的值。
address | value |
---|---|
0x2000FFAC | 0x2000FFD0 |
0x2000FFB0 | 0x0000000D |
0x2000FFB4 | 0x00000000 |
0x2000FFB8 | 0x00000000 |
0x2000FFBC | 0x0001F204 |
0x2000FFC0 | 0xA5A5A5A5 |
0x2000FFC4 | 0x0001F239 |
0x2000FFC8 | 0x0001F204 |
0x2000FFCC | 0x00000000 |
0x2000FFD0 | 0x8000000C |
咱们须要的是PC和LR的值,由于这两个寄存器表明的是:PC表明当前执行位置,LR表明返回地址。
从栈空间咱们能够得知:PC的值为0x0001F204, LR的值为0x1F239。
怎么找的?
很简单,咱们知道进入中断处理函数后,内核依次将xPSR,PC,LR,R12,R3,R2,R1,R0的顺序入栈。(至于为何是这个顺序,请看Cortex-M3权威指南)。而后咱们须要知道栈的操做方式(满减,空减,满增,空增),而Cortex-M3属于满减栈,就是堆栈指针指向最后一个被压入堆栈的32位数值;PUSH压栈时,SP先自减4,再存入新值,POP出栈时相反,先从SP指针处读出上次被压入堆栈的值,SP再自增4。
因此咱们要从0x2000FFD0开始看起,PC跟LR是第二跟第三入栈的。因此就是0x2000FFC8和0x2000FFC4对应的值。
发生HardFault时,PC正在执行0x1F204地址的指令,LR是准备返回的地址0x1F239。
怎么看这两个地址所在的函数?
有两个方法:
1.看编译器生成的map文件。
2.使用编译工具链的addr2line,以下:
arm-none-eabi-addr2line 1f204 -f -e xxxx.elf (烧录文件) >fp_test (函数名) >..../main.c:129 (对应文件及行号) arm-none-eabi-addr2line 1f239 -f -e xxxx.elf >test >..../main.c:142