中断执行流程

一、中断触发阶段

1. 中断源触发

  • 硬件事件GPIO输入、定时器溢出、ADC转换完成等。
  • 软件事件:软件中断指令(如SWI - SoftWare Interrupt)。

2. 中断请求(IRQ)发送

  • 外设将中断标志位置1,并向NVIC(嵌套向量中断控制器)发送请求。
  • NVIC根据优先级决定是否立即响应。

二、中断响应阶段

1. 处理器上下文保存

  • 自动压栈(硬件完成):
    • 当前程序计数器(PC)、程序状态寄存器(xPSR)、通用寄存器(R0-R3, R12, LR)依次压入当前堆栈(MSP/PSP)。
    • 若中断嵌套发生,自动切换到主堆栈(MSP)。
  • LR值更新LR被设置为特殊值(如0xFFFFFFF1),标记中断返回模式。

2. 中断向量表查找

  • 处理器根据中断号(IRQn)计算向量表偏移:

    1
    中断服务函数地址 = VTOR(向量表基址) + 4 * (IRQn + 16)
    • 例如:IRQn=10,其地址 = 0x8000000 + 4 * (10 + 16) = 0x8000068

3. 跳转到中断服务函数(ISR

  • 处理器从向量表中读取ISR地址,并跳转执行。

注:

  • ISR不能返回一个值。

  • ISR不能传递参数。

  • 在许多处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额外的寄存器入栈。有些处理器/编译器就不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。

  • 与第三点一脉相承,printf()经常有重入和性能上的问题,所以一般不使用printf()

三、中断处理阶段

1. ISR执行流程

1
2
3
4
5
6
7
8
9
void TIM1_IRQHandler(void) {
// 1. 清除中断标志(防止重复进入)
TIM1->SR &= ~TIM_SR_CC1IF;

// 2. 用户处理逻辑(如读取ADC数据、翻转GPIO等)
do_something();

// 3. 若有更高优先级中断,可触发上下文切换(如PendSV)
}

2. 关键注意事项

  • 快速响应ISR应尽量简短,避免长时间阻塞。
  • 共享资源保护:若需访问全局变量,使用临界区(关中断)或原子操作。

四、中断返回阶段

1. 上下文恢复

  • 出栈操作(硬件自动完成):
    • 从堆栈中依次恢复R0-R3, R12, LR, PC, xPSR
  • LR值检查:若LR=0xFFFFFFF1,表示返回线程模式并使用MSP

2. 返回原程序

  • 处理器恢复之前的PCxPSR,继续执行被中断的代码。

五、完整流程示意图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌───────────────────────┐
│ 主程序运行 │
└──────────┬───────────┘
│ 中断触发
┌──────────▼───────────┐
│ 硬件自动保存上下文 │
│ (PC, xPSR, R0-R3等) │
└──────────┬───────────┘

┌──────────▼───────────┐
│ 从向量表获取ISR地址 │
└──────────┬───────────┘

┌──────────▼───────────┐
│ 执行ISR │
│ 1. 清除中断标志 │
│ 2. 处理中断任务 │
└──────────┬───────────┘

┌──────────▼───────────┐
│ 硬件自动恢复上下文 │
└──────────┬───────────┘

┌──────────▼───────────┐
│ 返回主程序继续执行 │
└───────────────────────┘

中断嵌套与优先级

1. 优先级规则

  • 抢占优先级:高优先级可打断低优先级。
  • 子优先级:相同抢占优先级时,按子优先级顺序执行。

2. 嵌套中断流程

1
2
3
4
5
6
7
8
9
10
11
低优先级ISR执行中

高优先级中断触发

硬件保存低优先级ISR的上下文

执行高优先级ISR

高优先级ISR返回,恢复低优先级ISR上下文

低优先级ISR继续执行