Cortex M3中断的具体行为#
中断或异常响应序列
入栈:自动把8个寄存器的值压入栈
取向量:从向量表中找出对应的服务程序入口地址
选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC
中断程序处理完成后返回
出栈
- 入栈:
响应异常的第一个行动,就是自动保存现场的必要部分:依次把xPSR, PC, LR, R12以及 R3-R0由硬件自动压入适当的堆栈中:如果当响应异常时,当前的代码正在使用PSP,则压入 PSP,即使用线程堆栈;否则压入MSP,使用主堆栈。 一旦进入了服务例程,就将一直使用 主堆栈MSP 。
地址
寄存器
被保存的顺序
旧SP(N-0)
原先已压入栈的内容
(N-4)
xPSR
2
(N-8)
PC
1
(N-12)
LR
8
(N-16)
R12
7
(N-20)
R3
6
(N-24)
R2
5
(N-28)
R1
4
新SP(N-32)
R0
3
CM3在看不见的内部打乱了入栈的顺序,这是有深层次的原因的。先把PC与xPSR的值保存, 就可以更早地启动服务例程指令的预取——因为这需要修改PC; 同时,也做到了在早期就可以更新xPSR中IPSR位段的值。
- 取向量:
当数据总线(系统总线)正在为入栈操作而忙得团团转时, 指令总线(I-Code总线)可不是凉快地坐着看热闹——它正在为响应中断紧张有序地执行另一项重要的任务: 从向量表中找出正确的异常向量,然后在服务程序的入口处预取指。 由此可以看到各自都有专用总线的好处:入栈与取指这两个工作能同时进行。
- 更新寄存器:
在入栈和取向量的工作都完毕之后,执行服务例程之前,还要更新一系列的寄存器:
SP: 在入栈中会把堆栈指针(PSP或MSP)更新到新的位置。在执行服务例程后,将由MSP负责对堆栈的访问。
PSR: IPSR位段(地处PSR的最低部分)会被更新为新响应的异常编号。
PC: 在向量取出完毕后,PC将指向服务例程的入口地址。
LR: LR的用法将被重新解释,其值也被更新成一种特殊的值,称为“EXC_RETURN”,并且在异常返回时使用。 EXC_RETURN的二进制值除了最低4位外全为1,而其最低4位则有另外的含义
EXC_RETURN数值
功能
0xFFFF FFF1
返回handler模式
0xFFFF FFF9
返回线程模式,并使用主堆栈(SP=MSP)
0xFFFF FFFD
返回线程模式,并使用线程堆栈(SP=PSP)
- 异常返回:
当异常服务例程执行完毕后,需要很正式地做一个“异常返回”动作序列,从而恢复先前的系统状态,才能使被中断的程序得以继续执行。 从形式上看,有3种途径可以触发异常返回序列,不管使用哪一种,都需要用到先前储的LR的值。
返回指令
工作原理
BX <reg>
当LR存储EXC_RETURN时,使用BX LR即可返回
POP {PC}
在服务例程中,LR的值常常会被压入栈。 此时即可使用POP指令把LR存储的EXC_RETURN往PC里弹,从而激起处理器做中断返回
LDR与LDM
把PC作为目的寄存器,亦可启动中断返回序列
有些处理器使用特殊的返回指令来标示中断返回,例如8051就使用reti。 但是在CM3中,是通过把EXC_RETURN往PC里写来识别返回动作的。因此,可以使用上述的常规返回指令, 从而为使用C语言编写服务例程扫清了最后的障碍(无需特殊的编译器命令,如__interrupt)。
- 在启动了中断返回序列后,下述的处理就将进行:
出栈:先前压入栈中的寄存器在这里恢复。内部的出栈顺序与入栈时的相对应,堆栈指针的值也改回去。
更新NVIC寄存器:伴随着异常的返回,它的活动位也被硬件清除。对于外部中断,倘若中断输入再次被置为有效, 悬起位也将再次置位,新一次的中断响应序列也可随之再次开始。