计组与微机控制
9.1 中断向量表和中断源以及优先级
9.1 中断向量表和中断源以及优先级
二. 中断向量表
1.中断向量表定义
定义: 中断向量表是存放异常服务程序入口地址的一张表,CPU 通过它找到对应异常/中断的处理函数。
Cortex-M 采用矢量中断:
向量表中存放的不是跳转指令,而是 ISR 入口地址。
2 指令型向量表与矢量型向量表
指令型中断向量表: 表中存放跳转指令,常见于部分早期处理器。
矢量中断向量表: 表中存放中断服务程序入口地址,Cortex-M 采用这种方式。
6.3 Cortex-M 向量表地址计算
每个向量表项占 4 字节,因此:
向量地址 = 向量表基地址 + 异常编号 × 4


例子:HardFault 异常编号 = 3
向量表偏移 = 3 × 4 = 0x0C
HardFault 向量地址 = 向量表基地址 + 0x0C
6.4 向量表前几个固定项
Cortex-M 向量表开头通常为:
0x00:初始 MSP 值
0x04:Reset_Handler
0x08:NMI_Handler
0x0C:HardFault_Handler
...
注意:第 0 项不是中断函数地址,而是主栈指针 MSP 初值。

中断流程:



7. 向量表重定位
7.1 向量表重定位定义
定义:向量表重定位是把向量表基地址从默认位置改到新的存储区域,使 CPU 按新的向量表响应异常。
Cortex-M 通过 SCB 的 VTOR 寄存器实现:
SCB->VTOR
7.2 为什么需要重定位
默认向量表常位于 Flash 起始地址,运行时不方便修改。若系统需要动态修改中断入口,常把向量表复制到 RAM,再修改 VTOR 指向 RAM 中的新向量表。
常见用途:
BootLoader 跳转应用程序
操作系统切换任务或应用
动态修改中断服务入口
7.3 对齐要求
向量表基地址不是任意地址,必须满足对齐要求。向量表大小通常要扩展到下一个 2 的整数次方。
例子:32 个外部中断 + 16 个系统异常 = 48 个向量
48 × 4 = 192 字节
扩展到 256 字节对齐
75 个外部中断 + 16 个系统异常 = 91 个向量
91 × 4 = 364 字节
扩展到 512 字节对齐
8. NVIC
8.1 NVIC 定义
定义: NVIC 是 Nested Vectored Interrupt Controller,嵌套向量中断控制器,用于管理 Cortex-M 的外部中断请求。

作用:
接收多个外部中断请求,管理中断使能/禁止
管理中断挂起状态,管理中断优先级
支持中断嵌套,支持矢量化跳转
Cortex-M 中断与异常
1. 中断向量表
中断发生后,CPU 必须知道:
发生了哪个中断?这个中断对应的服务程序 ISR 在哪里?
这个“中断号 → 中断服务程序入口地址”的对应关系,就由中断向量表完成。
1.1 中断向量表的本质
中断向量表是系统 ROM 或 RAM 中的一块区域,里面按顺序存放每个异常或中断对应的入口信息。
可以理解成:中断编号 → 查中断向量表 → 得到中断服务程序入口地址 → 跳转执行 ISR
中断向量表里通常不是完整的中断服务程序代码, 而是服务程序的入口地址,或者是一条跳转指令。
异常编号 1 ~ 15 是系统异常
其中几个固定优先级很重要:
注意:
Reset、NMI、HardFault 优先级固定,不能像普通中断那样随便改。
2.2 外部中断
外部中断一般来自外设,例如:
定时器GPIO,串口 SPI,I2C,ADC,DMA EXTI 外部中断线
在 Cortex-M 中:异常编号 16 及以后是外部中断

所以:
外部中断 IRQ0 对应异常编号 16 外部中断 IRQ1 对应异常编号 17 外部中断 IRQ2 对应异常编号 18
关系可以记成:
异常编号 = IRQn + 16 IRQn = 异常编号 - 16
例如:IRQ0 的向量地址 = VTOR + (0 + 16) × 4 IRQ5 的向量地址 = VTOR + (5 + 16) × 4
2.3 Cortex-M 常见中断源分类
NVIC 接收的中断源分成几类,可以整理为:
外部中断请求 IRQ 不可屏蔽中断 NMI SysTick 系统节拍中断 内核系统异常 软件中断源
外部中断 IRQ
由外设产生,例如:
定时器 I/O 端口 通信接口 ADC DMA EXTI
不可屏蔽中断 NMI
NMI 不能被普通屏蔽寄存器屏蔽。
典型来源:
看门狗 掉电检测 严重外部故障
NMI 的优先级仅低于 Reset。
SysTick 中断
SysTick 来自 Cortex-M 内核的系统定时器。
常用于:
操作系统节拍 定时任务 延时计数 系统调度
软件中断源
课件中提到一些软件触发方式,例如:
SVC 指令触发 SVCall NVIC_STIR 触发外部中断 NVIC_ISPR 设置挂起位 EXTI_SWIER 软件触发外部中断事件
可以理解为:
有些中断不是外设真的拉了一根线, 而是软件主动写寄存器制造一个中断请求。
NVIC 和 SCB 的位置

Cortex-M 中,NVIC 和 SCB 都位于系统控制空间 SCS:
SCS 基地址:0xE000E000 大小:4 KB
其中:NVIC:主要管理外部中断 SCB:系统控制块,管理系统异常、向量表重定位、优先级分组等
NVIC 和 SCB 位于系统控制空间 SCS 地址从 0xE000E000 开始 大小为 4 KB
4. Cortex-M 中断管理寄存器

4.1 PRIMASK
PRIMASK 用于快速屏蔽大多数中断。
PRIMASK = 0:不屏蔽 PRIMASK = 1:屏蔽所有可屏蔽异常
但是它不能屏蔽:NMI,HardFault
所以可以记成:PRIMASK 屏蔽普通中断和大多数异常, 但 NMI 和 HardFault 仍然可以响应。
PRIMASK 本质上是把当前优先级提高到 0, 因此优先级不高于 0 的可配置异常都进不来。
4.2 FAULTMASK
FAULTMASK 更强。
FAULTMASK = 1:屏蔽除 NMI 以外的所有异常
也就是说:FAULTMASK 置 1 后,只有 NMI 可以响应。
它甚至可以屏蔽 HardFault。
所以它比 PRIMASK 更“狠”。一般用于非常关键的保护段。
另外:除了 NMI 异常处理之外,异常返回时通常会自动清除 FAULTMASK。
4.3 BASEPRI

BASEPRI 是按优先级阈值屏蔽中断。
BASEPRI = 0:不屏蔽 BASEPRI = 某个优先级值:屏蔽该优先级及更低优先级的中断
注意 Cortex-M 里:优先级数值越小,优先级越高。
所以如果:BASEPRI = 0x40
那么会屏蔽:优先级数值 >= 0x40 的中断
也就是屏蔽:0x40、0x60、0x80、0xA0、0xC0、0xE0……
但不会屏蔽:0x00、0x20
可以记成:BASEPRI 屏蔽“低于或等于某门槛”的中断, 高优先级中断仍然能进来。
4.4 三个屏蔽寄存器对比
PRIMASK:屏蔽大多数 FAULTMASK:只留 NMI BASEPRI:按优先级门槛屏蔽
5. Cortex-M 中断优先级
5.1 优先级基本规则
Cortex-M 的优先级规则:
优先级数值越小,实际优先级越高。
例如:优先级 0 高于优先级 1 优先级 1 高于优先级 2 0x00 高于 0x20 0x20 高于 0x40
这点和生活中“数字越大越厉害”的直觉相反,要特别注意。
5.2 固定优先级
几个异常优先级固定:
Reset -3 NMI -2 HardFault -1
它们高于普通可编程中断。
普通外部中断和部分系统异常的优先级是可编程的。
5.3 NVIC_IPRx 优先级寄存器

每个外部中断都有一个优先级配置。
Cortex-M 中优先级寄存器通常是 8 位宽。
每个中断占 8 位,4 个中断共用一个 32 位 NVIC_IPRx 寄存器
所以:NVIC_IPR0 管理 IRQ0 ~ IRQ3,NVIC_IPR1 管理 IRQ4 ~ IRQ7……
每个优先级用 8 bit 管理,60 个中断需要 60 个 8 bit = 60 字节
对应 15 个 32 位寄存器
5.4 不是 8 位都有效
虽然优先级寄存器是 8 位,但实际芯片不一定实现全部 8 位。
很多 Cortex-M 只实现高几位。
例如 Cortex-M3/M4 常见实现 3 位优先级,则有效位是:bit7 bit6 bit5
低位:bit4 ~ bit0通常没有实现,写了也无效,读出来为 0。
所以可用优先级只有:
0x00 0x20 0x40 0x60 0x80 0xA0 0xC0 0xE0
低位不用,置 0,优先级是高位有效
5.5 Cortex-M0+ 的优先级
Cortex-M0+:
NVIC_IPR 只有 8 个寄存器 每个寄存器管理 4 个 IRQ 最多支持 32 个 IRQ 中断源
如果只实现最高 2 位,则可用优先级只有:0x00 0x40 0x80 0xC0也就是 4 个优先级。
