Post

STM32 外部中断

STM32 外部中断。

STM32 外部中断

1. 中断流程

信号路径为:GPIO Pin (PB12) -> Edge Detect Circuit -> NVIC -> CPU

  1. 中断触发:外设(如 GPIO、定时器、UART 等)产生中断请求(Interrupt Request, IRQ),通过硬件线路发送到NVIC。
  2. 中断检测:NVIC 持续监测各外设的中断请求信号,判断是否满足触发条件(如电平触发、边沿触发等)。
  3. 优先级判断:若多个中断同时请求,NVIC 根据抢占优先级→响应优先级→中断向量表顺序的规则确定执行顺序。
  4. 中断响应:NVIC 暂停当前正在执行的程序(若允许被抢占),保存当前程序状态(PC、寄存器等)到栈中。
  5. 向量跳转:根据中断向量表中的地址,跳转到对应的中断服务程序(Interrupt Service Routine, ISR)执行。
  6. 中断处理:执行 ISR 中的用户代码(如数据处理、状态清除等)。
  7. 中断返回:ISR 执行完毕后,从栈中恢复现场,返回到被中断的程序继续执行。

2. 中断配置

当将STM32的引脚 PB12 配置为 “外部中断模式,下降沿触发” 时,芯片内部的外部中断/事件控制器 会进行一系列设置。

2.1 下降沿触发选择寄存器(Falling trigger selection register)

配置后第一个直接受到影响的寄存器。

  • 作用:这个寄存器用于启用对应引脚线的下降沿检测功能。
  • 将该寄存器的第12位(对应EXTI12)设置为‘1’,EXTI 控制器正式“监听” PB12 引脚上的下降沿信号。(0000 0000 0000 0000 0000 0001 0000 0000 0000 = 0x00001000)

2.2 上升沿触发寄存器(Rising trigger selection register)

上升沿触发寄存器与下降沿触发寄存器是互斥的,用于配置上升沿触发。

  • 作用:启用对应引脚线的上升沿检测功能。
  • 将该寄存器的第12位(对应EXTI12)明确地设置为‘0’,EXTI控制器忽略 PB12 引脚上的任何上升沿信号。这确保了中断触发条件的纯粹性。(0000 0000 0000 0000 0000 0000 0000 0000 = 0x00000000 )

2.3 边沿检测电路(Edge detect circuit)

此为硬件电路,而不是一个软件可配置的寄存器。它负责实际检测引脚上的电平变化。

  • 作用:实时监控 GPIO 引脚的电平。当它检测到符合 Rising/Falling trigger selection register 所设置条件的边沿事件时,就会产生一个信号。
    1. 该电路会持续采样PB12引脚的电平。
    2. 当 PB12 引脚的电平从高电平(1) 变为低电平(0) 时,电路会检测到这个下降沿。
    3. 由于 Falling trigger selection register 已经使能,这个下降沿事件被认为是有效的。
    4. 检测到有效下降沿后,该电路会做两件事:
    • 置位中断挂起寄存器:它将 Pending request register 的第12位置‘1’。这个标志位表示“有一个EXTI12 的中断正在等待处理”。这个寄存器就像是一个标志,告诉 NVIC 有中断来了。
    • 向NVIC发送中断请求:如果中断没有被屏蔽,这个请求会继续向上传递到 NVIC。

2.4 中断屏蔽寄存器(Interrupt mask register)

将其第 12 位 设置为 1。这相当于打开了一个允许开关,让 EXTI12 的中断请求能够通过,并继续向 NVIC 传递。(0000 0000 0000 0000 0001 0000 0000 0000 = 0x00001000)

3. 中断触发

当 PB12 被设置为下降沿触发后,PB12 由高电平变为低电平时,芯片内部发生的具体事件。

3.1 Edge detect circuit (边沿检测电路)

这个硬件电路持续监测着 PB12(即 EXTI12 线)上的电平。

当它检测到电平从高 变为低 时,它立即识别到一个下降沿。

3.2 Falling trigger selection register (下降沿触发选择寄存器)

边沿检测电路会来“查询”这个寄存器的配置。它发现该寄存器的第12位是1。

这意味着刚才检测到的下降沿是一个有效事件,需要被处理。

3.3 Pending request register (挂起请求寄存器)

一旦边沿检测电路确认了有效事件,它会自动将 Pending request register 的第12位置为1。

这个动作至关重要! 这一位就像一个“标志旗”,升起来表示:“有一个EXTI12的中断正在等待处理”。只要这一位是1,EXTI控制器就会认为有一个中断请求持续存在。

3.4 Interrupt mask register (中断屏蔽寄存器)

中断信号在继续向上传递之前,会经过一个“门卫”,也就是中断屏蔽寄存器。

因为之前已经将它的第12位设置为1,这个“门卫”会放行这个中断请求。

3.5 至 NVIC 中断控制器

中断请求成功通过“门卫”,被发送到NVIC。

NVIC 是 CPU 的“中断调度中心”。它根据优先级等因素,最终决定在何时让 CPU 来响应这个中断。

CPU 会跳转到事先写好的中断服务函数(EXTI15_10_IRQHandler)中执行代码。

3.6 清理阶段(在中断服务函数中)

当处理完中断后,必须手动清除挂起标志位,否则 CPU 会认为中断一直存在,导致无限重复进入中断函数。

需要在代码中向 Pending request register 的第12位写入一个‘1’(HAL_GPIO_EXTI_IRQHandler),以此来清除它(将其清零)。这个“写1清零”的机制非常常见。

4. 中断执行

4.1 优先级配置

中断优先级分为抢占优先级与响应优先级。

STM32 使用 4 位来表示优先级,这4位可以通过NVIC_PriorityGroupConfig()函数分成以下5种组合:

优先级分组抢占优先级位数响应优先级位数说明
NVIC_PriorityGroup_00位4位16个响应,无抢占
NVIC_PriorityGroup_11位3位2级抢占,8个响应
NVIC_PriorityGroup_22位2位4级抢占,4个响应
NVIC_PriorityGroup_33位1位8级抢占,2个响应
NVIC_PriorityGroup_44位0位16级抢占,无响应

当选择 NVIC_PriorityGroup_4 时,4 位都用来设置抢占优先级,每个中断向量的抢占优先级都可以设置为 0~15,但响应优先级都只能是 0。

当选择 NVIC_PriorityGroup_2 时,抢占优先级和响应优先级都可以设置为 0~3。

1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 2位抢占,2位响应

4.2 中断向量表

某 STM32 的中断向量表。

中断名称处理函数抢断优先级响应优先级
EXTI0EXTI0_IRQHandler--
EXTI1EXTI1_IRQHandler--
EXTI2EXTI2_IRQHandler--
EXTI3EXTI3_IRQHandler--
EXTI4EXTI4_IRQHandler--
EXTI9_5EXTI9_5_IRQHandler--
EXTI15_10EXTI15_10_IRQHandler--

4.3 中断优先级

  • 抢占优先级:决定中断能否“打断”正在执行的其他中断。
  • 响应优先级:仅当抢占优先级相同时,决定多个中断的执行顺序(无法抢占,仅排队)。

优先级数字越小优先级越高。

  • 两个中断同时发生时:
    • 先比较抢占优先级,当抢占优先级相同时比较响应优先级。
    • 若二者响应优先级也相同,则取决于二者在中断向量表中的顺序。
  • A 中断正在执行,另一中断到达:只比较抢占优先级。

4.4 EXTI9_5 如何区分中断

由于 EXTI5~EXTI9 共享同一个中断服务程序,因此必须在程序中通过中断标志位判断具体是哪条线触发了中断,再执行对应逻辑。

核心步骤:

  • 检查某条中断线的标志位(EXTI_GetITStatus);
  • 若标志位有效,执行对应处理逻辑;
  • 清除中断标志位(EXTI_ClearITPendingBit),避免重复响应。

示例:

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
void EXTI9_5_IRQHandler(void) {
    // 检查是否是 EXTI5 触发中断
    if (EXTI_GetITStatus(EXTI_Line5) != RESET) {
        // 处理 EXTI5 中断(例如:读取 GPIOE5 的状态)
        GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_5);
        // 清除 EXTI5 中断标志位
        EXTI_ClearITPendingBit(EXTI_Line5);
    }

    // 检查是否是 EXTI6 触发中断
    if (EXTI_GetITStatus(EXTI_Line6) != RESET) {
        // 处理 EXTI6 中断(例如:控制 LED 翻转)
        GPIO_ToggleBits(GPIOB, GPIO_Pin_6);
        // 清除 EXTI6 中断标志位
        EXTI_ClearITPendingBit(EXTI_Line6);
    }

    // 同理处理 EXTI7、EXTI8、EXTI9
    if (EXTI_GetITStatus(EXTI_Line7) != RESET) {
        // 处理逻辑...
        EXTI_ClearITPendingBit(EXTI_Line7);
    }

    // ...(EXTI8、EXTI9 类似)
}

5. REFERENCES

  1. STM32 参考手册
  2. 【keysking的STM32教程】 第7集 深入讲解STM32中断】
This post is licensed under CC BY 4.0 by the author.