1. 中断基础概念:中断的定义、中断向量表、中断优先级与嵌套
1.1 中断的定义与核心机制
中断(Interrupt)是嵌入式系统中一种硬件机制,允许外设或软件事件暂停当前CPU正在执行的主程序流,转而执行一个预先定义好的特殊服务程序(中断服务程序,ISR),处理完成后自动返回原程序断点继续执行。中断机制是实时响应外部事件的基础,也是GPIO边沿/电平触发调试的前提。
中断的本质: 中断是一种异步事件处理机制。CPU在每执行完一条指令后,都会检查是否有中断请求(IRQ)挂起。若有,则自动保存当前上下文(PC、PSR、寄存器等),跳转到中断向量表中对应的入口地址执行ISR。
关键术语: 中断源(Interrupt Source)、中断请求(IRQ)、中断服务程序(ISR)、中断向量表(IVT)、中断优先级、中断嵌套、临界区(Critical Section)。
1.2 中断向量表(Interrupt Vector Table, IVT)
中断向量表是一张存储在固定内存地址的表格,表中每一项(向量)存放着对应中断服务程序的入口地址。当CPU响应某个中断时,硬件自动根据中断号从向量表中取出地址并跳转执行。
| 中断向量号 | 向量地址偏移 | 典型用途 | 优先级(默认) |
|---|---|---|---|
| 0 | 0x0000_0000 | 复位向量(Reset) | 最高(-3,不可屏蔽) |
| 1 | 0x0000_0004 | NMI(不可屏蔽中断) | -2 |
| 2 | 0x0000_0008 | 硬错误(Hard Fault) | -1 |
| 16 | 0x0000_0040 | SysTick定时器 | 可配置 |
| 32 | 0x0000_0080 | GPIO Port A(典型) | 可配置 |
| 33 | 0x0000_0084 | GPIO Port B(典型) | 可配置 |
调试注意: 在GPIO中断调试中,若中断未触发,首先检查中断向量表是否已正确映射到对应的GPIO中断号。常见错误:未在启动文件中定义ISR函数名,或ISR函数名与向量表符号不匹配。
以下SVG图展示了ARM Cortex-M内核的中断向量表布局与GPIO中断映射关系:
1.3 中断优先级与嵌套
中断优先级决定了当多个中断同时发生时,CPU优先响应哪一个。优先级数值越小,优先级越高(在ARM Cortex-M系列中)。中断嵌套(Interrupt Nesting)是指一个高优先级中断可以打断正在执行的低优先级ISR。
优先级分组模型(ARM Cortex-M)
ARM Cortex-M3/M4支持可编程的优先级分组,将优先级寄存器分为抢占优先级(Preemption Priority)和子优先级(Subpriority)两部分:
| 分组编号 | 抢占优先级位数 | 子优先级位数 | 抢占优先级数量 | 子优先级数量 |
|---|---|---|---|---|
| 0 | 0 | 4 | 1 | 16 |
| 1 | 1 | 3 | 2 | 8 |
| 2 | 2 | 2 | 4 | 4 |
| 3 | 3 | 1 | 8 | 2 |
| 4 | 4 | 0 | 16 | 1 |
调试建议: GPIO中断通常配置为中等优先级(如5~10),避免与系统定时器(SysTick,通常优先级较高)冲突。若出现中断丢失或响应延迟,优先检查优先级分组设置是否一致。
以下SVG图展示了中断嵌套的执行流程:
1.4 中断响应流程(硬件自动完成)
- 中断采样: CPU在每条指令结束时采样中断输入引脚或NVIC挂起标志。
- 优先级裁决: NVIC比较所有挂起中断的优先级,选择最高优先级中断。
- 入栈(Stacking): 硬件自动将PC、PSR、R0-R3、R12、LR压入当前栈(MSP或PSP)。
- 取向量: 从向量表中读取对应中断号的ISR入口地址。
- 更新寄存器: 更新PC为ISR入口地址,更新LR为特殊返回地址(EXC_RETURN)。
- 执行ISR: CPU开始执行中断服务程序。
- 出栈(Unstacking): ISR执行完毕后,硬件自动恢复寄存器,返回主程序。
GPIO中断调试要点:
- 确认GPIO中断在NVIC中已使能(NVIC_EnableIRQ)。
- 确认中断优先级分组一致(NVIC_SetPriorityGrouping)。
- 确认ISR函数名与中断向量表符号完全匹配(如 EXTI0_IRQHandler)。
- 确认中断标志位在ISR中被清除(否则会反复触发)。
1.5 中断优先级配置示例代码
/* 设置优先级分组:3位抢占优先级 + 1位子优先级 */
NVIC_SetPriorityGrouping(5); // 分组3:3位抢占,1位子优先级
/* 配置GPIO中断优先级 */
NVIC_SetPriority(EXTI0_IRQn, 5); // 抢占优先级=2(高3位),子优先级=1(低1位)
NVIC_SetPriority(EXTI1_IRQn, 9); // 抢占优先级=4,子优先级=1
/* 使能中断 */
NVIC_EnableIRQ(EXTI0_IRQn);
NVIC_EnableIRQ(EXTI1_IRQn);
/* ISR实现 */
void EXTI0_IRQHandler(void)
{
// 检查中断标志
if (EXTI->PR & (1 << 0))
{
// 处理GPIO中断事件
// ...
// 清除中断标志位(必须)
EXTI->PR = (1 << 0);
}
}
常见陷阱: 在ISR中执行耗时操作(如printf、长循环)会导致低优先级中断被长时间阻塞,甚至触发看门狗复位。ISR应遵循“快进快出”原则,仅设置标志位或传递数据,具体处理放在主循环中。