一般中断问题分析
ARM CPU 在上电启动之后会自动进入SVC模式,也是ARM上电后的默认工作模式,如果发生了中断,ARM会自动切换到外部中断模式(IRQ为例),如果是FIQ那么就切换到FIQ模式下面进行处理。
在每一个模式下面都有一组可以访问的寄存器,SVC和IRQ模式下的R0~R12是共用的,这就涉及到寄存器Rx内容的保存:压栈,恢复:出栈操作。在IRQ处理中用到了哪些Rx就要进行相应的保护与恢复,否则当处理退出IRQ时就会出错误。
发生中断时候,ARM是要首先完成当前正在执行的指令,然后再进行为IRQ的必要处理。具体的内容如下:
- 将当前CPSR,保存到IRQ模式下的的SPSR_irq,进行备份。
- 把PC-4所指向的地址放到LR。LR = PC-4。为中断返回是有个退出点。
- 强制PC= 0X0000'0018,指向IRQ的中断向量的地址,通常地址0X0000'0018放置一条跳命令,跳转到IRQ的中断服务函数的入口地址。B HANDLERIRQ.
补充一点:ARM的正在执行的指令地址x,和取指令的地址PC,地址y,之间是差x=y-8.
难点:中断服务函数退出地址的计算
PC-8 --------------->|正在执行的指令的地址|0x120 如果此时发生中断了
PC-4 --------------->|真正的中断返回点地址|0x124
PC ---------------->|取指令的地址 |0x128
通过上述分析可以得出:真正的中断返回点地址是0X124,真正应该存入LR的地址是0X124.但是实际情况是:0X128 ?????
分析:ARM 总是执行完当前的指令才会处理其他事情,问题就出在这里,当ARM执行完当前的指令的时候PC已经更新了,如下
PC = PC+4;PC = 0X128+4 = 0X12C,存放到LR=PC-4; LR = 0X12C-4=0X128;这个0X128不是真正的中断函数的返回点。真正的函数返回点是0X124.怎么办????
在给PC恢复数值是进行这样的处理下:LR = LR-4,PC=LR. ==>SUBS PC,LR,#4. 并且自动恢复CPSR=SPSR_irq
SWI软中断问题分析
软中断和一般的中断处理流程有不同的地方:CPU在执行指令时候,遇见一般的中断,先要执行完当前的指令,更新PC,然后把LR=PC-4.SPSR_irq=CPSR.CPSR=XXX.进入IRQ中断处理过程。真正的断点返回地址是:LR-4。在返回时需要进行地址的调整。 ==>SUBS PC,LR,#4.并且自动恢复CPSR=SPSR_irq。
swi软中断还是很特别的玩意,软中断发生的时候,CPU切换到SVC工作模式,PC的数值不进行更新,即:不是等到把当前的指令执行完毕之后在执行其他的处理。而是立刻处理。CPU立即把软中断的返回地址写入LR中。LR=PC-4,这是真正的返回地址,PC的数据不更新。!!!!。SWI中断返回地址不需要调整就是正确的地址。
然后就是软中断编号的计算,方法:发生软中断时那条指令地址里面数据的低24位放的就是软中断标号,从中取出来放到R0里面,R0是函数的第一个参数,也是存放函数返回结果的地方。
HandlerSWI
STMFD SP!,{R0-R3,R12,LR},LR存放的是swi真正的软中断要返回的中断地址点。
LDR R0,[LR,#-4];找到到底在哪一条指令时发生了软中断,很明显:就是LR真正断点返回地址的上面一条地址。LR-4或者PC-8。找个这个指令的地址,取出其中的内容。软中断的编号就是存放在发生软中断那条指令地址的低24位数据里面。
BIC R0,R0,#0xFF00'0000,低24位是软中断号
BL my_swi_handler;
LDMFD SP! ,{R0-R3,R12,PC}^,其中^表示CPSR=SPSR_svc.只能手动恢复。
使用方法
extern void my_swi_handler(unsigned int num);
__swi(0x1)led_one(void);
__swi(0x2)led_two(void);
main()
{
led_one();
delay(100ms);
led_two();
}
void my_swi_handler(unsigned int num)
{
case 0x1:{ do_some_thing}break;
case 0x2:{do_another_thing}break;
default: break;
}