EXTI配置流程
一、EXTI 配置流程(HAL,F1 专用要点)
开时钟
对应 GPIO 端口:
__HAL_RCC_GPIOx_CLK_ENABLE()
AFIO 时钟(F1 必须):
__HAL_RCC_AFIO_CLK_ENABLE()
(HAL 在配置 EXTI 线时用 AFIO->EXTICR 做端口映射)
配置引脚为外部中断模式(GPIO_Init)
Mode
选一个:GPIO_MODE_IT_RISING
(上升沿)GPIO_MODE_IT_FALLING
(下降沿)GPIO_MODE_IT_RISING_FALLING
(双边沿)
Pull
:GPIO_PULLUP
/GPIO_PULLDOWN
/GPIO_NOPULL
(根据电路)Pin
:对应的GPIO_PIN_x
在 F1 HAL 中,调用
HAL_GPIO_Init()
且 Mode 为 IT_xxx 时,会自动完成 EXTI 线与端口的 AFIO 映射(前提是 AFIO 时钟已开)。NVIC 配置(中断优先级与使能)
找到该引脚对应的 EXTI IRQ 号(见下表)
HAL_NVIC_SetPriority(IRQn, preempt, sub)
、HAL_NVIC_EnableIRQ(IRQn)
编写 IRQHandler 与回调
在对应的
EXTIxx_IRQHandler()
中调用:HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x);
实现回调(弱符号,可在用户文件中重写):
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {if (GPIO_Pin == GPIO_PIN_x) { /* 你的处理 */ }
}
清除中断标志:已在 HAL_GPIO_EXTI_IRQHandler()
内完成;若手动操作可用 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_x)
。
5.(可选)释放调试引脚
若用到 PA15 / PB3 / PB4 做 EXTI,需要关闭 JTAG(保留 SWD):
__HAL_AFIO_REMAP_SWJ_NOJTAG();
6.设置优先级分组
HAL_Init()中的 /* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
EXTI 线号与 IRQ 对应关系(记住这张表)
EXTI 线 | 触发引脚范围 | 对应 NVIC 中断名 |
---|---|---|
0 | Px0 | EXTI0_IRQn |
1 | Px1 | EXTI1_IRQn |
2 | Px2 | EXTI2_IRQn |
3 | Px3 | EXTI3_IRQn |
4 | Px4 | EXTI4_IRQn |
5~9 | Px5…Px9 | EXTI9_5_IRQn (共享) |
10~15 | Px10…Px15 | EXTI15_10_IRQn (共享) |
同一时刻 一个 EXTI 线只能映射到一个端口的某一脚(比如 EXTI13 只能选 PC13 或 PA13 或 PB13 中的一个)。
例子:使用中断的方法,按下 KEY1 翻转 LED1 状态,而 LED2 一直保持 500ms 的频率闪烁。
如果采用中断法,由于软件消抖需要在中断服务函数加入延时函数,这种做法一般是不被允许的。
exti.c:
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "stm32f1xx_hal.h" // 建议显式包含,确保 HAL API 与宏可用/*** @brief 外部中断初始化:PA0 -> EXTI0,下降沿触发,上拉输入* @note F1 系列使用 AFIO 完成 EXTI 端口映射,所以必须打开 AFIO 时钟。* 这里选择“下降沿触发”,常见接法是按键按下把 PA0 拉低。*/
void exti_init(void)
{GPIO_InitTypeDef gpio_initstruct;/* 1) 开启相关时钟:- GPIOA:因为我们要用 PA0- AFIO :F1 通过 AFIO->EXTICR 做 EXTI 端口映射(HAL 初始化 IT 模式时会用到)*/__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟__HAL_RCC_AFIO_CLK_ENABLE(); // ⚠ F1 必须打开 AFIO 时钟/* 2) 配置 PA0 为“外部中断输入-下降沿触发”,并上拉HAL_GPIO_Init 在 Mode=GPIO_MODE_IT_xxx 时,会自动完成“EXTI0 线 <- PA0”的 AFIO 映射与 EXTI 边沿配置。*/gpio_initstruct.Pin = GPIO_PIN_0; // 选择 PA0gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 外部中断:下降沿触发gpio_initstruct.Pull = GPIO_PULLUP; // 上拉:空闲=高,按下=低// 输入模式下 Speed 无意义,不设置也行,这里保持默认HAL_GPIO_Init(GPIOA, &gpio_initstruct);/* (可选)3) 先清一下挂起位,避免上电毛刺导致的误进中断 */__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);/* 4) 配置 NVIC:打开 EXTI0 的中断入口,并设置优先级- EXTI0_IRQn:专门对应“线0”的中断入口- 优先级:根据你的系统统一规划。下面示例:抢占优先级=2,子优先级=0- 默认 HAL_Init() 会设置优先级分组为 4(4 bits preemption,0 bits sub)*/HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}/*** @brief EXTI0 中断入口(硬件向量表指到这里)* @note 这里不要直接清标志;统一交给 HAL 去做:* HAL_TIM/GPIO 的 IRQHandler 会完成:读取/确认/清除挂起位,* 最后再回调 HAL_GPIO_EXTI_Callback() 交给用户层处理。*/
void EXTI0_IRQHandler(void)
{/* 把“这根线”的中断交给 HAL 做通用处理(含清旗标、调用回调) */HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}/*** @brief HAL 的 GPIO EXTI 通用回调* @param GPIO_Pin: 指示到底是哪一根 EXTI 线触发(例如 GPIO_PIN_0)* @note 任何 EXTI 线(0..15)若经 HAL 处理,最终都会走到这个回调;* 用户可依据 GPIO_Pin 做分发。** 这里做了“简单阻塞式消抖”:delay_ms(20) 后再次确认按下再翻转 LED。* 这种办法直观,但会阻塞中断(不推荐在复杂系统中使用)。* 更优雅的做法是:在回调里“暂时屏蔽这根 EXTI 线”,启动一个 20ms 的* 定时器(软定时或硬件定时器),定时到期后在主循环/定时器回调中再读脚、* 处理业务,并重新使能 EXTI —— 这样就是“非阻塞消抖”。*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if (GPIO_Pin == GPIO_PIN_0) // 只处理 PA0 的情形{/* === 简单阻塞式消抖(与原代码一致):等待20ms再确认 ===注意:1) 若你的 delay_ms() 基于 SysTick,在中断上下文中可能不可用或影响系统节拍;通常建议用“关中断的简单忙等延时”,或避免在 ISR 中做延时。2) 阻塞式延时会延长中断占用时间,可能影响系统实时性。*/delay_ms(20);/* 再次确认仍为“按下”(低电平),才认为有效触发 */if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){/* 用户业务:翻转 LED(示例) */led1_toggle();}}
}
EXTI 中断实现流程(HAL,代码实现流程)
开时钟
__HAL_RCC_GPIOx_CLK_ENABLE()
打开目标端口;
F1 额外:__HAL_RCC_AFIO_CLK_ENABLE()
打开 AFIO 时钟(EXTI 端口映射要用)。GPIO 配置为 EXTI 模式
GPIO_InitTypeDef.Mode = GPIO_MODE_IT_FALLING / RISING / RISING_FALLING
;GPIO_InitTypeDef.Pull = GPIO_PULLUP / PULLDOWN / NOPULL
;HAL_GPIO_Init()
会同时完成 AFIO 的 EXTICR 映射(把 EXTI 线挂到对应端口的那根脚上)以及触发边沿设置。NVIC 配置
根据线号选择 IRQ:0..4 →
EXTI0_IRQn
..EXTI4_IRQn
(独占入口)5..9 →
EXTI9_5_IRQn
(共享入口)10..15 →
EXTI15_10_IRQn
(共享入口)HAL_NVIC_SetPriority()
+HAL_NVIC_EnableIRQ()
使能。
触发时序
引脚满足设定边沿 → EXTI 寄存器置挂起位(PR) → NVIC 进对应 IRQ。IRQ 入口
在EXTIxx_IRQHandler()
内调用HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x)
,把通用操作交给 HAL。HAL IRQ Handler 做的事
读取并清除 EXTI 挂起位;
调用弱符号回调
HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
。
用户回调
在HAL_GPIO_EXTI_Callback()
里根据GPIO_Pin
判断来源并处理业务(比如翻转 LED、发送消息、唤醒任务等)。返回
中断退出。如果需要“非阻塞消抖”,建议在回调里屏蔽该线、启动 X ms 定时器,到期后在线程上下文再读取脚并恢复中断。