4. 中断系统原理:ARM Cortex-M NVIC中断控制器,中断优先级分组,中断向量表
在蓝牙芯片适配中,中断系统是连接硬件事件与软件处理的桥梁。ARM Cortex-M 系列内核(如 M4、M33 等)是当前主流蓝牙 SoC 的标配,其嵌套向量中断控制器(NVIC)提供了灵活、低延迟的中断管理机制。理解 NVIC 的工作原理、优先级分组策略以及中断向量表的布局,是编写可靠蓝牙驱动的基础。
核心要点:NVIC 支持最多 240 个中断源,每个中断可独立配置使能、挂起和优先级。优先级分组决定了抢占优先级与子优先级的位数分配,直接影响中断嵌套行为。
4.1 NVIC 中断控制器架构
NVIC 是 Cortex-M 内核的一部分,与 CPU 紧耦合,无需外部中断控制器即可处理所有异常和中断。其关键特性包括:
- 可嵌套中断:高优先级中断可以抢占低优先级中断,支持硬件自动压栈和出栈。
- 向量化中断:每个中断对应一个固定的中断向量地址,CPU 直接从向量表加载入口地址,无需软件查询。
- 尾链技术:当两个中断连续发生时,跳过不必要的出栈/入栈操作,降低延迟。
- 中断屏蔽:通过 PRIMASK、FAULTMASK、BASEPRI 寄存器实现全局或基于优先级的中断屏蔽。
蓝牙适配提示:蓝牙协议栈通常要求中断响应时间在微秒级。NVIC 的硬件尾链和自动压栈特性,使得在 Cortex-M 上实现低功耗蓝牙(BLE)协议栈成为可能。
4.2 中断优先级分组
Cortex-M 使用 8 位优先级寄存器(实际有效位数由芯片厂商决定),通过 优先级分组 将优先级分为抢占优先级和子优先级两部分。分组由 SCB->AIRCR 寄存器的 PRIGROUP 字段控制。
| 分组编号 | 抢占优先级位数 | 子优先级位数 | 抢占优先级等级数 | 子优先级等级数 |
|---|---|---|---|---|
| 0 | 7 | 1 | 128 | 2 |
| 1 | 6 | 2 | 64 | 4 |
| 2 | 5 | 3 | 32 | 8 |
| 3 | 4 | 4 | 16 | 16 |
| 4 | 3 | 5 | 8 | 32 |
| 5 | 2 | 6 | 4 | 64 |
| 6 | 1 | 7 | 2 | 128 |
| 7 | 0 | 8 | 1 | 256 |
常见陷阱:蓝牙芯片厂商(如 Nordic、TI、Dialog)通常只使用 3~5 位有效优先级。配置分组时,务必查阅芯片参考手册,避免使用无效位导致优先级配置失效。
优先级比较规则:
- 先比较抢占优先级:数值越小,优先级越高。
- 若抢占优先级相同,再比较子优先级:数值越小,优先级越高。
- 若两者均相同,则比较中断编号:编号越小,优先级越高(硬件固定)。
4.3 中断向量表
中断向量表是存储在固定地址(通常为 Flash 起始地址 0x00000000)的一张函数指针表。每个中断源对应一个表项,CPU 在响应中断时自动从表中加载入口地址。
向量表结构:
| 偏移地址 | 异常/中断号 | 描述 |
|---|---|---|
| 0x00 | - | 主堆栈指针(MSP)初始值 |
| 0x04 | 1 | 复位异常(Reset_Handler) |
| 0x08 | 2 | 不可屏蔽中断(NMI_Handler) |
| 0x0C | 3 | 硬错误异常(HardFault_Handler) |
| 0x10 | 4 | 内存管理异常(MemManage_Handler) |
| 0x14 | 5 | 总线错误异常(BusFault_Handler) |
| 0x18 | 6 | 用法错误异常(UsageFault_Handler) |
| ... | ... | ... |
| 0x40 + (n-16)*4 | n | 外部中断 n(如 UART、BLE 射频中断) |
蓝牙芯片适配要点:BLE 协议栈通常需要将中断向量表重定位到 RAM 或特定 Flash 区域,以便运行时动态修改。通过 SCB->VTOR 寄存器设置新的向量表基址即可实现。
4.4 中断配置实战代码
以下代码展示了在 Cortex-M4 上配置 NVIC 中断优先级分组、使能外部中断并设置优先级的典型流程:
/* 中断优先级分组配置:4位抢占 + 4位子优先级 */
#define NVIC_PRIORITYGROUP_4 ((uint32_t)0x00000003) /* 分组 3 */
void NVIC_Configuration(void)
{
/* 1. 设置优先级分组 */
SCB->AIRCR = NVIC_PRIORITYGROUP_4 | (0x5FA << SCB_AIRCR_VECTKEY_Pos);
/* 2. 使能 UART 中断(假设中断号为 42) */
NVIC_EnableIRQ((IRQn_Type)42);
/* 3. 设置 UART 中断优先级:抢占优先级 2,子优先级 1 */
/* 优先级值 = (抢占优先级 << 子优先级位数) | 子优先级 */
/* 此处子优先级位数为 4,所以优先级值 = (2 << 4) | 1 = 33 */
NVIC_SetPriority((IRQn_Type)42, 33);
/* 4. 设置 BLE 射频中断优先级更高:抢占优先级 0,子优先级 0 */
NVIC_SetPriority((IRQn_Type)BLE_IRQn, 0);
}
/* UART 中断服务函数 */
void UART_IRQHandler(void)
{
/* 检查中断标志 */
if (USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR;
/* 处理接收数据 */
}
}
关键点:
- 优先级值越小,优先级越高。抢占优先级 0 为最高。
- BLE 射频中断通常设置为最高优先级,以确保协议栈时序。
- 中断服务函数名必须与启动文件中的向量表名称一致,否则链接器无法正确绑定。
4.5 中断延迟与蓝牙实时性
蓝牙协议栈对中断延迟有严格要求,尤其是 BLE 连接事件和广播事件。NVIC 的硬件特性直接影响延迟指标:
| 延迟类型 | 典型值(Cortex-M4 @ 64MHz) | 说明 |
|---|---|---|
| 中断响应延迟 | 12~16 个时钟周期 | 从中断请求到执行第一条 ISR 指令 |
| 中断抢占延迟 | 6~10 个时钟周期 | 高优先级中断抢占低优先级时的额外开销 |
| 尾链切换延迟 | 6~8 个时钟周期 | 连续中断之间的切换,无需完整压栈 |
| 中断退出延迟 | 10~14 个时钟周期 | 从 ISR 返回主程序 |
蓝牙适配避坑:如果蓝牙射频中断被低优先级中断长时间阻塞,可能导致连接超时或丢包。建议将射频中断优先级设为最高,并尽量减少 ISR 内的执行时间,将耗时操作放到任务级处理。
4.6 中断嵌套与优先级设计原则
在蓝牙芯片适配中,合理设计中断优先级是系统稳定性的关键。以下原则可供参考:
- 射频中断最高:BLE 射频中断(如 RADIO_IRQ)应设置为最高优先级,确保协议栈时序。
- 定时器中断次高:协议栈使用的定时器中断(如 RTC、TIMER)优先级次之,用于链路层调度。
- 外设中断中等:UART、SPI、I2C 等外设中断设置为中等优先级。
- 非实时中断最低:GPIO 按键、调试接口等非实时中断设置为最低优先级。
- 避免同优先级嵌套:同优先级中断不会互相抢占,可能导致响应延迟不可控。
实战建议:在蓝牙协议栈初始化前,调用 NVIC_PriorityGroupConfig() 设置分组,之后不要再修改。中断优先级一旦配置,在整个系统运行期间保持不变。