之前在使用中断的时候,对内部的机制不是很了解。特别是相关寄存器的自动保存与恢复。在调试HardFault的时候,(见帖子https://www.stmcu.org.cn/module/forum/thread-604503-1-1.html )不明白出错的地址为什么是从PC处(更准确的说法应该是返回地址)开始找,而不是LR。因此,这里做个简单的实验分享一下。实验相关的代码在附件中可以下载。使用arm gcc进行编译,arm gdb进行调试。keil mdk虽然有不同的地方,但是原理还是相同的。 ' |7 n3 X3 d0 l/ A9 a3 H% H1 W7 K 在《嵌入式软件设计基础,基于ARM cortex-m3》一书中,介绍了进入异常处理程序和从异常处理程序返回的步骤:3 v+ b1 Z" u+ f( j1 [5 V! H0 p) s
2. 从异常处理程序返回
. W3 N" ?/ `9 B$ ]0 @* }2 U# n 看下部分源代码:代码中添加的nop指令没有什么作用。主要是为了方便观察自动入栈的LR和PC值的不同。往0xE000ED04处写0x10000000是为了触发PendSV中断。在触发PendSV中断之后,会发生中断的自动保存。 1. 使用arm gdb进行调试。在PendSV_Handler中断处理函数上点一个断点。之后输入continue继续运行。直到断点处停止。 2.这时候进入了PendSV中断,并且相关的寄存器已经得到入栈保存。可以使用disassemble查看当前的汇编代码。可以发现在自动保存R0-R3,R12,LR,返回地址,PSR之后,编译器还push 了R7和LR。 4 v" N6 ], h* T- ` 3.通过info reg查看相关的寄存器。LR的值是0xFFFFFFF9。这个数就是EXC_RETURN。更加具体的解释可以参考ARM cortex-m3权威指南。这里看到的一个比较重要的地址是sp。发生中断之后,自动保存的寄存器就在他里面。0 |. p! l* n; C. c 使用x/10xw 0x2000ffd0查看sp处连续的四个字。因为进入PendSV之后,编译器自动压入了R7和LR。所以前两个数是当前的R7和LR(数据正好和我们使用info reg读出来的吻合)。0x0000000a是R0 ... 中断发生时,自动保存的LR是0x08008637,返回地址是0x08002646。因为ARM CM3/4只能执行THUMB指令,所以LR的实际地址应该是0x08008636。(具体为什么是这样,可以查看ARM CM3权威指南)4 H0 V& x/ O: M' J$ C+ `$ I / G+ m5 `' ~) @5 \% [ 4.使用disassemble查看main的汇编代码:(PS:使用disassemble查看汇编代码的时候,他会一些ldr的数据当成指令来翻译。准确的翻译可以使用arm-none-eabi-objdump -d main.o来查看)。& f2 n0 y& y. u& N 使用objdump查看的代码,明显的不同是对ldr数据的翻译上。9 p/ H. a" n% F, E: r5 E! t 实际触发PendSV的代码是0x08002644的那一行代码。因此从中断返回的代码也就是他的下一条指令。也就是0x08002646。这个值也就是自动保存在栈中的返回地址。9 x0 B0 M' ~. u+ { 在触发中断之前,最后一条能够更新LR的指令是bl hello。他将LR的位置更新到bl hello的下一条指令。也就是0x08002636。由于是thumb指令,所以将最低位置1。因此自动保存在栈中的数值也就是0x08002637。/ G, S! i, U& K- \) w 8 r, P% a" Y5 `" `- t6 H9 f 好了,暂时就解释这么多。更加具体的解释请参考ARM cortex-m3权威指南。! X7 V2 L8 d! A2 r1 ~8 B: m& K5 q 之前有朋友在社区群中问,ARM CM3权威指南中的汇编部分是不是要看。我觉得还是要看的。虽然不一定要会使用汇编写程序,但是简单的汇编代码一定要懂。这对你写程序帮助不是很大。但是对你debug确实受益匪浅。/ J9 D @ S( H: p' P |
interrupt.zip
下载523.46 KB, 下载次数: 21
多谢分享,大师出手不凡