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)。

在启动了中断返回序列后,下述的处理就将进行:
  1. 出栈:先前压入栈中的寄存器在这里恢复。内部的出栈顺序与入栈时的相对应,堆栈指针的值也改回去。

  2. 更新NVIC寄存器:伴随着异常的返回,它的活动位也被硬件清除。对于外部中断,倘若中断输入再次被置为有效, 悬起位也将再次置位,新一次的中断响应序列也可随之再次开始。