一、 概述
在单片机的使用过程中,中断系统几乎是不可绕过的,中断一般有向量号,优先级和异常向量表指向的函数入口,本章主要讲述中断优先级(主要 armv7m 为代表)。
二、优先级
对于系统来说,优先级有异常优先级和执行优先级。
(1)异常优先级:有三个是固定的,其他都是可配置的。三个固定的分别是
异常 |
异常优先级 |
Reset |
-3 |
NMI |
-2 |
HardFault |
-1 |
Other |
0~IMPLEMENTATION DEFINED^2(IMPLEMENTATION DEFINED取值范围:3~8) |
(2)执行优先级:一般是当前执行的异常优先级,但对某些寄存器的设置就会导致执行优先级的改变。
2.1 异常优先级分组
一般而言,一个异常的异常优先级(三个固定优先级的异常除外)是一个 8位的值,对于除外部中断而言的其他异常,异常优先级有寄存器 SCS->SHPR1~SHPR3 指定,外部异常优先级(中断)由 NVIC->IPR0~IPR123 指定。然而,根据 IMPLEMENTATION DEFINED 的值,可能会将一部分位数固定为 0,如下:
优先级可配置位数 |
异常优先级数量 |
最大异常优先级值(低位固定,高位可调) |
3 |
8 |
0b11100000(224) |
4 |
16 |
0b11110000(240) |
5 |
32 |
0b11111000(248) |
6 |
64 |
0b11111100(252) |
7 |
128 |
0b11111110(254) |
8 |
256 |
0b11111111(255) |
其中,优先级值越低,异常优先级就越高。
这 8 位的异常优先级,也会分为组优先级(抢占优先级)和子优先级。其中,AIRCR.PRIGROUP 就决定了多少位指定为组优先级,多少位指定为子优先级,如下:
AIRCR.PRIGROUP 值 |
异常优先级段[7:0] | |
组优先级段 |
子优先级段 | |
0 |
[7:1] |
[0] |
1 |
[7:2] |
[1:0] |
2 |
[7:3] |
[2:0] |
3 |
[7:4] |
[3:0] |
4 |
[7:5] |
[4:0] |
5 |
[7:6] |
[5:0] |
6 |
[7] |
[6:0] |
7 |
|
[7:0] |
如果多个挂起异常具有相同的组优先级,那么将使用子优先级比对同组内异常的优先级。
其实也存在一种情况,比如多个异常的组优先级和子优先级有一致的情况下,刚好这些异常同时发生且异常优先级高于执行优先级,那么处理器会先处理异常号更低的异常(注意:同等组优先级不可抢占),异常号如下:
异常号 |
异常 |
1 |
Reset |
2 |
NMI |
3 |
HardFault |
4 |
MemMange |
5 |
BusFault |
6 |
UsageFault |
7~10 |
Reserved |
11 |
SVCall |
12 |
DebugMonitor |
13 |
Reserved |
14 |
PendSV |
15 |
SysTick |
16 |
External interrupt0 |
… |
|
16+N(N最大为495) |
External interruptN |
2.2 执行优先级以及通过寄存器的提升
当没有活动异常的时候,那么,软件实际以优先级(最大异常优先级值+1)执行,这个优先级也称为执行优先级的基础级别(base level)
执行优先级是以下3个方面综合的最高级别:
(1)执行优先级的基础级别
(2)活动异常的最高优先级
(3)PRIMASK、FAULTMASK、BASEPRI 的值的影响(非特权级别对这三个寄存器访问是 RAZ/WI,也就是不管这三个寄存器的实际值,读出来的是0,写入寄存器操作被忽略)
在这里面,先忽略第(3)个,如果执行优先级是第(1)个,说明当前没有活动异常,如果有任何活动异常,那么执行优先级就是第(2)。
现在看第(3),对执行优先级的影响是:异常处理程序可以以高于相应异常优先级的优先级执行,这里需要注意一点,降低当前异常的优先级是永远不被许可的,这会导致高优先级被低优先级抢占。
下面看如何通过这些寄存器提升异常的执行优先级(复位将会使得这三个寄存器为 0)。
(1)通过对 PRIMASK 寄存器第 0位 PM 写入 1,那么执行优先级就会变为 0。防止任何具有可配置优先级的异常变为活动状态。
将这个寄存器值置1 使用指令 CPSID i
将这个寄存器值清0 使用指令 CPSIE i
(2)通过对FAULTMASK 寄存器第 0 位 FM 置为 1,那么执行优先级为 -1(只有异常优先级低于 -1,也就是异常优先级最高只能为 0 的异常处理程序才能将此置为 1,Hardfault 处理程序都不行,NMI 会将此寄存器清零)
将这个寄存器值置1 使用指令 CPSID f
将这个寄存器值清0 使用指令 CPSIE f
(3)通过设置 BASEPRI 低8 位,这个寄存器改变了异常抢占所需要的优先级,需要注意的是,只有当 当前正在执行的软件的未屏蔽优先级低于 BASEPRI 值时,此时才会生效,将执行优先级提升到 BASEPRI(BASEPRI 能配置的位数与异常优先级能配置的位数一致,组优先级和子优先级设置也一样)。如果BASEPRI 值全为0,则disable BASEPRI
注意:优先级提升机制只影响组优先级,不影响子优先级,子优先级仅用于对挂起的异常优先级进行排序,但不影响活动异常。
这三个寄存器对执行优先级的影响不仅局限于异常处理程序,只要是特权权限就可以提升执行优先级,那么利用这种特性也可在关键区域上做到只屏蔽某一优先级及以下异常。
2.3 其余升级的情况
在当前执行优先级低于 Hardfault 的时候,处理器在以下情况会将异常优先级升级为 Hardfault。
(1)待处理的同步故障或 SVCall (所有的同步异常)组优先级低于或等于当前执行优先级时
(2)一个可配置优先级的故障被禁用了,但这个故障产生了
(3)对于浮点异常,不遵循(1)(2),而是:如果存在 MemManage 且 FPCCR.MMRDY==0 或者存在 BusFault 且 FPCCR.BFRDY==0,则故障升级为 HardFault
2.4 总结
总体而言,执行优先级可以认为是这样判断:
(1)查询除了复位异常外其他的异常是否是活动异常,从这些产生的异常里面,找到组优先级最高的
(2)依次按照 BASEPRI、PRIMASK、FAULTMASK 这个次序访问寄存器,看是否这些寄存器有效,取出这三个寄存器定义有组优先级最高的值,都没有那就是 按照优先级 256 取组优先级
(3)比对(1)、(2)的优先级,哪个最高,哪个就是当前执行优先级
异常之间比对是这样的:
(1)挂起的异常之间,先比对组优先级,组优先级高的优先,组优先级一致就比对子优先级,子优先级一致就比对异常号,异常号越低,则越优先
(2)在跟执行优先级比对的时候,只比对组优先级,组优先级高则可以抢占
三、参考文档
ARM:《DDI0403E_d_armv7m_arm.pdf》
评论