计组与微机控制
9.2 抢占优先级和中断完整过程
9.2 抢占优先级和中断完整过程
6. 抢占优先级和子优先级
这是这一节最容易混的地方。
Cortex-M 的优先级可以分成两部分:
抢占优先级 preemption priority 子优先级 subpriority
6.1 抢占优先级
抢占优先级决定:一个中断能不能打断另一个正在执行的中断。
规则:抢占优先级数值越小,优先级越高。
例如:A 的抢占优先级 = 0,B 的抢占优先级 = 1则 A 可以抢占 B。
6.2 子优先级
子优先级只在:抢占优先级相同,多个中断同时等待响应时起作用。
它决定:谁先被处理。
但注意:子优先级不能决定抢占。
也就是说:
如果两个中断抢占优先级相同, 即使其中一个子优先级更高, 也不能打断另一个正在执行的中断。
6.3 优先级判断规则
可以这样记:
先看抢占优先级,决定能不能抢占。 抢占优先级相同,再看子优先级,决定谁先执行。 如果抢占优先级和子优先级都相同,再看中断编号,编号小的先执行。
逻辑可以整理为:
两个中断同时到来: 1. 抢占优先级高的先处理 2. 抢占优先级相同,看子优先级 3. 子优先级也相同,看中断编号
6.4 举例理解
假设 A、B 两个中断:
因为抢占优先级相同,所以不能互相抢占。
如果 A、B 同时挂起,则:
A 的子优先级更高,所以 A 先执行。
再看另一个例子:
虽然 B 的子优先级数值较大,但是 B 的抢占优先级更高。
所以:B 优先执行,并且 B 可以抢占 A。
7. AIRCR 和优先级分组
优先级分组由 SCB 里的 AIRCR 寄存器设置。
其中:AIRCR.PRIGROUP
决定优先级位如何分配给:抢占优先级,子优先级

7.1 为什么需要分组?
因为优先级寄存器有效位数量有限。
例如某芯片只实现 3 位优先级:
bit7 bit6 bit5 有效 bit4 ~ bit0 无效
这 3 位可以全部用于抢占优先级,也可以拆成:
一部分给抢占优先级 一部分给子优先级
7.2 例子:3 位优先级,分组为 5
如果实际可用优先级宽度是 3 位,且 AIRCR 设置为某种分组,使:
bit7 bit6:抢占优先级 bit5:子优先级
那么:抢占优先级有 2 位 → 4 组 子优先级有 1 位 → 每组 2 个子优先级
总共仍然是:4 × 2 = 8 个优先级
对应编码:
数值越小,优先级越高。
7.3 例子:3 位优先级,全部用于抢占
如果优先级宽度是 3 位,分组后没有子优先级位,则:
bit7 bit6 bit5 全部是抢占优先级
此时:共有 8 个抢占优先级,没有子优先级
对应:0x00、0x20、0x40、0x60、0x80、0xA0、0xC0、0xE0
这种情况下,比较简单:
谁的数值小,谁优先级高。
8. 中断向量表重定位
8.1 为什么需要重定位?
Cortex-M 启动时,向量表通常在:0x00000000
这个地址一般映射到 Flash 或 ROM。
但是 Flash 或 ROM 在运行时不方便修改。
如果想动态改变中断服务程序入口地址,可以把向量表复制到 RAM,然后让 CPU 使用 RAM 中的新向量表。
这就是:中断向量表重定位
8.2 VTOR 寄存器
Cortex-M 中用 SCB 的 VTOR 寄存器指定新的向量表基地址。
SCB->VTOR = 新向量表基地址
例如:SCB->VTOR = 0x20000000
表示把向量表放到 SRAM 的起始地址。
向量表重定位提供了一个可编程的向量偏移寄存器 SCB->VTOR。
8.3 向量表基地址对齐
VTOR 不是随便写一个地址都可以。
因为向量表大小要按 2 的整数次幂对齐。
计算步骤:
向量表大小 = (外部中断数量 + 16 个系统异常) × 4 字节 然后向上取 2 的整数次幂作为对齐要求


例 1:32 个外部中断
向量表大小 = (32 + 16) × 4 = 192 字节
192 向上取 2 的整数次幂:256 字节
所以向量表基地址可以是:
0x00000000 0x00000100 0x00000200 ……
不能随便放在 0x00000080 这种没有满足对齐的位置。
例 2:75 个外部中断
向量表大小 = (75 + 16) × 4 = 364 字节
364 向上取 2 的整数次幂:512 字节
所以向量表基地址可以是:
0x00000000 0x00000200 0x00000400 ……
挂起状态和活跃状态
| 状态 | 含义 |
| 挂起 pending | 中断请求已经到来,等待 CPU 处理 |
| 活跃 active | CPU 正在执行该中断的服务程序 |
一个中断可以:
- 只挂起;- 正在活跃;- 活跃期间再次挂起;- 被高优先级中断抢占。
9. Cortex-M 中断过程
中断过程可以分成五步:
初始化 中断申请 中断响应 中断服务 中断返回
9.1 初始化
在使用中断之前,需要做好配置。
主要包括:
设置中断优先级 设置外设中断触发条件 使能外设中断请求 使能 NVIC 中对应中断 必要时设置向量表 必要时设置优先级分组
设置优先级 → 使能外设硬件中断 → 使能 NVIC 中断
注意,有些情况下不设置优先级也能用,因为默认优先级可能是 0,但实际工程里一般应该显式设置。

9.2 中断申请
中断申请就是中断源向 CPU 或 NVIC 发出请求。
外部设备可能发出:
电平信号,脉冲信号
软件也可以通过写寄存器产生中断请求。
例如:
定时器溢出 串口接收到数据 GPIO 检测到边沿 软件设置挂起位
9.3 中断判断与响应条件

Cortex-M 接受中断请求需要满足几个条件:
处理器正在运行,不处于复位或停止状态 中断处于使能状态 中断没有被 PRIMASK、FAULTMASK、BASEPRI 等屏蔽 中断优先级高于当前正在执行的异常或中断
其中 NMI 和 HardFault 比较特殊:NMI 基本不可屏蔽 HardFault 也通常不受 PRIMASK 屏蔽
9.4 中断响应
当 CPU 决定响应中断时,会自动完成一部分现场保护。

Cortex-M 硬件通常自动压栈:
R0 R1 R2 R3 R12 LR PC xPSR
然后根据中断编号查向量表,取出中断服务程序入口地址,跳转执行 ISR。
所以中断响应可以记成:保存现场 → 查向量表 → 跳转 ISR
9.5 中断服务
中断服务就是执行对应的中断服务程序 ISR。
例如:
void TIM2_IRQHandler(void) { // 清除中断标志 // 处理定时器事件 }
中断服务程序里通常要做两件事:
处理事件 清除中断标志位
如果不清除外设中断标志,退出中断后可能会立刻再次进入中断。
9.6 中断返回

Cortex-M 中断返回时,会通过特殊的异常返回机制恢复现场。
CPU 会从栈中恢复:
R0、R1、R2、R3、R12、LR、PC、xPSR
然后回到被中断的程序继续执行。
所以中断返回可以记成:
弹栈恢复现场 → 回到断点继续执行
由于堆栈具有“先进后出”的特点,容易实现中断嵌套。
10. 中断嵌套
Cortex-M 支持中断嵌套。
所谓中断嵌套,就是:
正在执行一个中断服务程序时, 来了一个更高优先级的中断, CPU 暂停当前 ISR, 先去执行更高优先级 ISR。
例如:
正在执行 USART 中断 突然来了优先级更高的 TIM 中断 CPU 先处理 TIM TIM 结束后再回到 USART
能不能嵌套,主要看:抢占优先级
子优先级不能决定嵌套。
中断响应流程
一般流程:
1. 外设或异常事件产生中断请求;2. NVIC 判断是否使能、是否被屏蔽;
3. 比较优先级;4. CPU 自动压栈保存现场;
5. 根据向量表取 ISR 地址;6. 跳转执行中断服务程序;
7. ISR 清除外设中断标志;8. 异常返回,恢复现场;
9. 回到原程序继续执行。
向量地址计算
公式:向量地址 = VTOR + 异常编号 × 4
外部中断:异常编号 = IRQn + 16
所以:IRQn 的向量地址 = VTOR + (IRQn + 16) × 4
例子:
VTOR = 0x00000000 IRQ0 向量地址 = 0x00000000 + 16 × 4 = 0x00000040 IRQ3 向量地址 = 0x00000000 + 19 × 4 = 0x0000004C HardFault 向量地址 = 0x00000000 + 3 × 4 = 0x0000000C
11.2 优先级数值大小
优先级数值越小,实际优先级越高。
例如:0x00 最高 0x20 次之 0x40 更低 0xE0 最低
11.3 抢占优先级和子优先级
抢占优先级:决定能不能打断别人 子优先级:决定同组同时挂起时谁先执行
先比抢占,再比响应,最后比编号。
11.4 三个屏蔽寄存器
PRIMASK:屏蔽普通可屏蔽中断,NMI 和 HardFault 除外 FAULTMASK:只留 NMI BASEPRI:按优先级门槛屏蔽
12. 简答题模板
1. 什么是中断向量表?
中断向量表是存放中断或异常入口信息的一块连续存储区域。CPU 响应中断时,根据中断编号在中断向量表中查找对应的表项,从而获得中断服务程序的入口地址或跳转指令,并转去执行相应的中断服务程序。在 Cortex-M 处理器中,中断向量表为矢量型中断向量表,每个表项占 4 字节,表项内容通常是中断服务程序的入口地址。
2. Cortex-M 中 IRQn 和异常编号有什么关系?
Cortex-M 中异常编号 1~15 通常对应系统异常,异常编号 16 及以后对应外部中断。外部中断 IRQ0 对应异常编号 16,IRQ1 对应异常编号 17,因此外部中断的异常编号等于 IRQn + 16。若向量表基地址为 VTOR,则 IRQn 的向量地址为 VTOR + (IRQn + 16) × 4。
3. 简述 NVIC 的作用。
NVIC 是 Cortex-M 处理器中的嵌套向量中断控制器,用于管理外部中断和部分异常。它可以接收多个中断请求,控制中断的使能、挂起和清除,设置中断优先级,并根据优先级决定是否响应中断。NVIC 还支持中断嵌套和向量化中断响应,使 CPU 能够根据中断编号快速查找中断服务程序入口。
4. PRIMASK、FAULTMASK 和 BASEPRI 有什么区别?
PRIMASK 用于屏蔽所有可屏蔽异常,但不能屏蔽 NMI 和 HardFault;FAULTMASK 的屏蔽能力更强,置位后除 NMI 外的所有异常都被屏蔽;BASEPRI 用于按优先级阈值屏蔽中断,当 BASEPRI 设置为某个优先级值时,该优先级及更低优先级的中断会被屏蔽,而更高优先级的中断仍可响应。
5. 抢占优先级和子优先级有什么区别?
抢占优先级用于判断一个中断能否打断另一个正在执行的中断,抢占优先级数值越小,实际优先级越高。子优先级只在抢占优先级相同且多个中断同时挂起时用于决定响应顺序,子优先级本身不能引起中断嵌套。判断中断响应顺序时,通常先比较抢占优先级,再比较子优先级,若二者都相同,则按中断编号决定。
6. 什么是中断向量表重定位?
中断向量表重定位是指通过修改 SCB 中的 VTOR 寄存器,把中断向量表的基地址从默认位置改到新的存储区域。Cortex-M 启动时向量表通常位于 0x00000000 地址处,一般对应 Flash 或 ROM。为了在运行过程中灵活修改中断服务程序入口地址,可以把向量表复制到 RAM 中,然后修改 SCB->VTOR 指向 RAM 中的新向量表。
7. 简述 Cortex-M 的中断响应过程。
Cortex-M 中断过程包括初始化、中断申请、中断响应、中断服务和中断返回。初始化阶段需要设置中断优先级、外设触发方式,并使能外设和 NVIC 中断。中断源产生请求后,NVIC 根据使能状态、屏蔽状态和优先级判断是否响应。若满足响应条件,CPU 自动保存现场,并通过中断向量表取得中断服务程序入口地址,转去执行 ISR。中断服务完成后,CPU 自动从栈中恢复现场,并返回到被中断程序继续执行。
