Skip to content

STM32及ARM中断详解

· 8 min

ARM体系的中断和异常#

当一个中断或者异常触发时,先看到这个中断源有没有被启动,以及它的优先级如何,然后进入中断向量表,如果允许进行中断或者异常的处理,就会根据中断向量表进入中断服务程序。

在中断服务程序中进行关中断、保存现场、开中断、处理中断和异常、关中断、恢复现场、开中断

在ARM体系中和异常统一由中断向量表管理。

中断向量表#

中断向量表有低端和高端向量表两种,低端向量表就是从0x00000000开始,高端向量表处在0xFFFF0000位置。

下图中的中断向量表基本对应着ArmV7-A架构,而CortexM架构都可以使用自定义的中断向量表,不用遵循下表。如果是更新的Arm指令集版本,和下表也有较大区别

中断向量表和ResetHandler#

ARM中断向量表的位置默认为0x00000000,中断向量表如下。

_start:
ldr pc, =Reset_Handler /* 复位中断 */
ldr pc, =Undefined_Handler /* 未定义指令中断 */
ldr pc, =SVC_Handler /* SVC(Supervisor)中断*/
ldr pc, =PrefAbort_Handler /* 预取终止中断 */
ldr pc, =DataAbort_Handler /* 数据终止中断 */
ldr pc, =NotUsed_Handler /* 保留中断 */
ldr pc, =IRQ_Handler /* IRQ 中断 */
ldr pc, =FIQ_Handler /* FIQ(快速中断) */

当机器上电启动时会从这个代码开始,先进入ResetHandler进行系统的初始化。当进入中断时,会从中断向量表的起始地址(也就是上面_start)开始,根据中断的类型进行偏移,例如如果偏移量为0x08则会执行SVC中断的语句,pc寄存器直接被装入中断向量的地址,跳转到对应的中断处理。

如果要防止中断嵌套,需要在中断服务程序中加入屏蔽中断的代码。

ResetHandler中是机器上电启动后或者复位后进行初始化的代码,初始化后会进入_main函数最后才进入main函数。

可以手动设置中断向量表的地址,一般的做法是在ResetHandler中设置。

STM32(Cortex-M)的中断服务程序#

对Cortex-M系列来说,并不遵循arm所规定的普通的中断向量表。

__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler

可以看到,中断向量表的第一行规定着栈初始化的地址,也就是说开机后会先初始化栈,然后才进行ResetHandler。

中断和异常触发流程(Cortex-M)#

1. 保存现场#

不管是触发中断还是异常,硬件都会自动保存R0到R3、R12、LR、xPSR寄存器。

剩下的寄存器将在中断服务程序中进行保存。

根据ATPCS标准,被调用的函数如果要是用R4到R11寄存器,则需要在被调用函数的内部进行压栈。

2. 根据中断号进入中断#

寻找中断向量表,再根据中断号,在中断向量表中进行偏移。

3. 恢复现场。。。。#

NVIC中断优先级#

不同系列的芯片有不同位数的中断优先级,这个中断优先级一般是在BASEPRI寄存器的最高几位。比如CortexM系列的CPU,中断优先级位数有4位,就是BASEPRI寄存器的高四位。

优先级数字越小,中断越优先。

中断优先级又被NVIC分成抢占优先级和响应优先级,可以配置对应的优先级分组。比如4位优先级里面,有两位是抢占优先级,有两位是响应优先级。我们发现如果中断优先级的设置只有4位的话,也就是只有16个不同的优先级,注定了一些中断必须要在同一个优先级中。对抢占优先级和响应优先级的处理是NVIC负责的,抢占优先级更高的可以中断嵌套其他更低的中断,而相同的抢占优先级的任务里,顺序执行响应优先级更高的。而对于整个优先级都完全一样的中断,则会通过自然优先级,也就是在中断向量表中的位置来判断。越靠前的自然优先级就越高。

本身抢占优先级和响应优先级这两个东西配合在一起,才是BASEPRI看到的真正的任务优先级,但是NVIC把他们拆成了两部分来使用。对BASEPRI寄存器来说,对优先级进行屏蔽,不在乎抢占还是响应,会直接屏蔽掉比寄存器的数值更大的那些中断。

NVIC可以管理的是中断,一般来源于外设或者内核异常。而有一些异常,比如HardFault或者NMI,他们在中断向量表的最前面,但是不通过NVIC进行中断的管理,并且其优先级也不能够被配置。他们的优先级默认被配置为最高,任何情况下发生这几个异常都是最先进行处理。而在这之后的MemManage和BusFault等等异常,包括操作系统常用到的SysTick和PenSV都是NVIC管理的内核异常。再往后,就是NVIC管理的外设的中断。

在操作系统中,一般使用到的中断就是SysTick和PendSV。SysTick用于提供操作系统时钟,并且在合适的时候进行唤起任务的切换。PendSV则用于进行任务的切换。PendSV的可配置优先级一般为最低,然后SysTick为其次。