一、EXTI简介
什么是 EXTI?(类比说明)
我们把 CPU 比作一个正在办公室专心写代码的程序员。
- 普通 GPIO(轮询模式,Polling) :就像程序员每隔 1 秒钟,就要跑到门口看一眼有没有人敲门。如果在没看的这 1 秒里刚好有人敲门,他就错过了。
- EXTI(外部中断模式,Interrupt) :就像程序员在门口装了一个 "门铃(EXTI)" 。他安心在屋里写代码(执行主程序)。一旦有人按下门铃,门铃立刻响,CPU 会立刻把当前写的字保存一下,瞬间冲过去开门,处理完敲门的人,再回来继续写代码。
总结 :EXTI 就是让单片机能**"瞬间响应"**外部电平变化的机制。它不需要 CPU 一直盯着引脚看。
二、EXTI 的工作模式
1. 中断模式
- 触发 CPU 中断执行 ISR(中断服务程序)
- 用于需要立即处理的事件
2. 事件模式 - 唤醒 CPU 但不执行 ISR
- 用于低功耗场景
三、EXTI 的触发方式
上升沿触发:信号从低电平到高电平
下降沿触发:信号从高电平到低电平
双边沿触发:上升沿和下降沿都触发

四、EXTI 系统架构
掌握了系统架构,对 EXTI 就有一个整体的把握,在编程时就思路就非常清晰。
1. EXTI 工作框图
触发信号由输入线进行输入:

2. EXTI 线路映射
EXTI有20个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0至EXTI15。
值得注意的是:
- 不同端口号,相同编号的引脚不能同时使用同一根输入线
- 引脚编号决定连接哪根中断输入线,例如PA0只能连接到EXTI0、PB12只能连接到EXTI12。
- 通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟

另外 4 个 EXTI 线的连接方式如下:
|-------------|-----------------|---------------------------------------|
| EXTI 线 | 连接到的外设事件 | 主要作用 |
| 线 16 EXTI16 | PVD 输出 | 电源电压监控。当供电电压低于或高于预设阈值时触发,用于掉电预警和数据保护。 |
| 线 17 EXTI17 | RTC 闹钟事件 | 绝对时间定点唤醒。在设定的具体日期和时间触发,用于执行定时任务。 |
| 线 18 EXTI18 | USB OTG FS 唤醒事件 | 全速USB远程唤醒。当检测到USB总线活动时,将系统从低功耗模式唤醒。 |
| 线 19 EXTI19 | 以太网唤醒事件 | 网络远程唤醒。当以太网收到唤醒数据包时触发,用于网络应用。 |
| 线 20 EXTI20 | USB OTG HS 唤醒事件 | 高速USB远程唤醒。功能同线18,但用于高速USB接口。 |
3. 关于中断标志
**在 STM32 中,中断标志是用于标识中断事件是否发生的状态位。当发生某中断时,对应的中断标志位则被置 1。**对于不同的中断线触发的中断,会将请求挂起寄存器对应的位置1,我们可以把这些对应的位理解成 EXTI 的中断标志位。

当发生中断时,运行中断服务函数后,需要手动清除中断标志位,不及时清除标志会产生严重后果:
- 重复进入中断:导致系统卡死在同一个中断中
- 系统崩溃:无法响应其他中断
- 性能下降:CPU 时间被无效中断占用
五、EXTI 配置
以检测 KEY1 为例(当 KEY1 按下,会产生下降沿信号,因此我们要设置 EXTI 下降沿检测,触发中断):



2. 分析生成的代码


3. 中断服务程序
STM32CubeMX生成的中断服务程序是统一放到文件 stm32f1xx_it.c 的,PC12对应的中断服务程序为 EXTI15_10_IRQHandler() :

4. 定义自己的EXTI中断调用的函数

六、上述中断服务函数里运行延时函数说明
原则上,中断服务程序应该尽可能短小,避免任何阻塞操作,包括延时。
1. 为什么这里可以使用延时?
- 系统简单性
如果这是一个单任务系统,没有其他重要中断 。
这是一个学习/演示项目,对实时性要求不高 - 按键消抖的特殊需求
机械按键会产生抖动,需要延时来确认稳定状态。 - 短时延时的可接受性
15ms 在很多人机交互场景中是可接受的
2. 更好的解决方案
- 定时器中断消抖
- 硬件消抖(使用RC电路过滤抖动)
3. 建议
在学习阶段理解这种权衡,在实际项目中应采用更好的消抖方案。
七、EXTI 外部中断(hal_gpio.c 中断回调 + hal_exti.c)
1. HAL_GPIO_EXTI_IRQHandler
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
- 功能:EXTI中断服务函数入口,清除中断标志、调用中断回调
- 参数:触发中断的引脚编号
- 返回值:无
使用:在stm32f1xx_it.c 对应EXTI中断函数内调用
2. HAL_GPIO_EXTI_Callback
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
- 功能:弱函数,用户重写,外部中断触发后的业务处理逻辑
- 参数:产生中断的引脚
- 返回值:无
重写示例:检测按键引脚执行对应操作
3. HAL_NVIC_EnableIRQ / HAL_NVIC_DisableIRQ
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn); void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);
- 功能:开启/关闭EXTI对应通道的NVIC中断
- 参数:中断号,如EXTI0_IRQn、EXTI1_IRQn等
- 返回值:无
4. HAL_NVIC_SetPriority
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
- 功能:配置EXTI中断抢占优先级、子优先级(F1仅2位抢占,2位子优先级)
- 参数:IRQn:中断通道;PreemptPriority抢占优先级;SubPriority子优先级
- 返回值:无