电子说
在进行CortexM系列MCU开发时,大家应该都或多或少触发过不可屏蔽中断,例如人见人爱的Hardfault。
对于嵌入式工程师来说,能够稳定复现的Bug最好解决,再不济挨个打断点也能定位到问题代码位置。
可是异常如果是不定时,无规律的发生在日常的运行中,那打断点的方式无异于守株待兔。
本篇将提供小技巧简述CortexM的异常压栈机制,教大家如何写出一个现场保存函数以及如何调用该函数。
如下图所示,触发中断的时候,CortexM系列芯片会按照该排列将相关现场数据进行地址偏移压栈,N即此时的SP数值,通过SP中存放的地址,我们可以获取到如图所示的数据。
而要知道触发异常的地址则需要关注LR(Link Register)的数值,它存放着异常触发点的函数地址。
那么按照压栈的排列,我们可以编写一个结构体以及相关的记录现场函数,如下图所示,结构体的成员完全是按照上图的压栈顺序排列,那下一步就是在哪里调用这个接口来获取到案发时的第一手资料
要想获得第一手数据,就要去中断函数的执行处,很多厂商的SDK都会在名为XXX_it.c文件里为开发者们写好各个中断的Handler,由于S文件中都是弱定义(弱定义即在C文件中有同名的函数,则以C文件中的函数优先编译,则可能造成触发中断时并不会运行S文件处的函数)所以建议可以将其屏蔽或者自己重新编写一个新的函数名。
由下图可见,通过IMPORT的形式调用外部现场保存函数,将SP寄存器赋值给R0寄存器是因为R0通常时作为函数的第一个参数进行调用的,可看作是将SP数值作为参数传入StackSave函数,BL的意思是带返回的跳转,即使跳转运行完成StackSave函数后还返回此处。
完成上述函数编写以及调用后,即可进行测试了。本次范例通过SPI初始化时传入错误的外设寄存器地址来触发HardFault中断,由下图可看出,此时记录到的LR数值为0x8000e83
查询后可知,0x8000e83的位置为SPI初始化函数中,符合预设的异常触发位置。在非仿真环境下可通过map文件找到大致的函数地址。
综上,我们就知道了如何获取到触发异常中断时的程序运行地址,通过将获取到的信息存入到片上Flash指定地址的形式将此时的现场信息进行保存,并通过固件和map一一对应管控。
这样在漫长运行过程中,如果不慎触发异常异常中断,通过对应的map文件即可迅速定位BUG所在大致位置
全部0条评论
快来发表一下你的评论吧 !