4、正如我们所猜测,的确是由于程序跑飞导致。程序停在了void HardFault_Handler(void) 。通过查看 SP 以及回溯栈里面的内容,找到了对应的LR,具体方法如下:
当中断产生时,按照上图所示的顺序进行压栈,同时栈指针SP--,即: R0, R1, R2, R3, R12, LR, PC,xPSR。
如上图所示,当产生异常时,如果call stack窗口显示不出来的话,只能根据core的寄存器手动回溯栈,以找到出错时的指针。根据ARM core的说明,SP+6,即红框的部分,为中断处理后LR和PC,据此可以追溯函数异常时的位置!
5、根据出错时的PC和LR,发现是浮点运算的函数,初步判断是因为浮点运算导致,比如没有对齐导致的Hardfault,但实际检查发现,并不是浮点运算的问题!
6、问题一时陷入了僵局。但有一点是确定的,是因为栈的区域被异常覆盖或者改写导致产生hard fault,
7、由于问题可以稳定复现,采取逐个排除法最终发现了问题的所在:当把一个局部数组变量改为全局数组时,问题消失!由于局部数组变量是保存在栈当中,所以怀疑是对这个局部数组变量使用不当导致了栈被覆盖或者改写!追查这个局部变量数组:
经检查发现,这个原先是8bit的局部变量的数组,在最后被强制转换成了uint32_t *类型的指针,由于是指针, 在对其进行++或--操作时,都是按照4字节宽带操作的,这就相当于扩大了4倍,覆盖了后面的栈的内容, 导致了程序跑飞!
小结
当芯片异常复位或者进入异常处理(如Hard fault, Mem Manage, Bus fault等)时,首先考虑的是,如何快速的复现这个问题,当问题被稳定复现的时候,可以通过调试工具在异常处理的地方打上断点停留,这样就可以获取到栈指针SP,通过SP去看栈里面的内容去回溯栈。当然,如果栈的内容被无端改写时,栈里面的内容,如保存的LR就没有太大的参考意义。不过,可以通过观察栈里面的内容,去估测是哪个模块或者函数异常修改了栈的内容,进而定位最终的问题源!