在嵌入式开发中,外部中断是实现硬件事件实时响应的关键机制。STM32 的外部中断 / 事件控制器(EXTI)具备强大的中断管理能力,本文将围绕按键中断应用,详细拆解 EXTI 工作原理与配置流程,帮助大家掌握硬件中断的开发技巧。
一、外部中断控制器核心架构
(一)控制器特性概览
STM32 的外部中断 / 事件控制器支持 19 路独立输入线,每路可配置:
- 触发模式:上升沿、下降沿、双边沿触发
- 中断 / 事件类型:中断请求(触发 CPU 响应)或事件请求(触发外设联动,无 CPU 干预 )
- 独立屏蔽:可单独关闭某路中断,不影响其他通道
- 脉冲检测:支持捕获窄脉冲(宽度低于 APB2 时钟周期),满足高精度场景需求
核心特点:每个中断线独立配置、状态可查、支持软件触发,为复杂中断场景提供灵活控制。
(二)关键组件解析
- 边沿检测器:监测 GPIO 引脚电平变化,触发中断 / 事件的 “传感器”
- 挂起寄存器(EXTI->PR):记录中断请求状态,置 1 表示对应中断线有未处理请求
- 屏蔽寄存器(EXTI->IMR):控制中断是否上报 NVIC(置 1 允许,置 0 屏蔽 )
- 触发选择寄存器(EXTI->FTSR/RTSR):配置下降沿 / 上升沿触发
同一中断线仅能映射一个 GPIO 引脚,需通过 AFIO 外设配置引脚与中断线的映射关系。
二、按键中断需求分析(以 PE4 为例)
(一)硬件连接
按键一端接地,另一端接 PE4,默认状态下 PE4 经上拉电阻保持高电平;按键按下时,PE4 电平拉低(下降沿变化 )。需配置 PE4 下降沿触发中断,实现按键事件实时响应(如 LED 翻转 )。
(二)中断设计目标
- 检测 PE4 引脚下降沿(按键按下动作 )
- 触发中断后翻转 LED 状态
- 确保中断标志正确清除,避免重复触发
三、外部中断配置全流程
(一)1. 使能 APB2 时钟(关键外设使能)
// 使能 AFIO 时钟(必须!中断引脚映射依赖 AFIO)
RCC->APB2ENR |= (0x01 << 0);
AFIO 负责管理 GPIO 与 EXTI 中断线的映射关系,必须先使能其时钟才能配置引脚映射。
(二)2. 配置引脚 - 中断线映射(AFIO 寄存器操作)
// 将 PE4 映射到 EXTI4 中断线
AFIO->EXTICR[1] |= (0x04 << 0);
EXTICR[1]
对应EXTICR2
寄存器(数组索引 0 对应EXTICR1
,索引 1 对应EXTICR2
)0x04
表示选择 PORT E(PE),左移 0 位对应EXTI4
中断线- 作用:建立 PE4 与 EXTI4 的硬件关联,使 EXTI4 能感知 PE4 的电平变化
(三)3. 使能 EXTI4 中断请求
// 允许 EXTI4 中断线向 NVIC 发送请求
EXTI->IMR |= (0x01 << 4);
IMR
(Interrupt Mask Register)寄存器第 4 位置 1,解除 EXTI4 的中断屏蔽,中断事件可传递到 NVIC。
(四)4. 配置下降沿触发模式(定义中断触发条件)
// 配置 EXTI4 为下降沿触发
EXTI->FTSR |= (0x01 << 4);
FTSR
(Falling Trigger Selection Register)第 4 位置 1,当 PE4 检测到电平从高→低变化时,触发中断请求。
(五)5. 配置 NVIC 中断优先级与使能
// 配置中断优先级分组(假设 2 位抢占 + 2 位响应)
NVIC_SetPriorityGrouping(2);
// 设置 EXTI4 中断优先级(抢占优先级 1,响应优先级 0 → 编码为 0100B = 4?需根据分组调整,此处示例为 6 简化)
NVIC_SetPriority(EXTI4_IRQn, 6);
// 使能 EXTI4 中断
NVIC_EnableIRQ(EXTI4_IRQn);
- 优先级分组:通过
NVIC_SetPriorityGrouping
划分抢占优先级和响应优先级的位数,影响中断嵌套逻辑 - 优先级设置:
NVIC_SetPriority
定义中断响应优先级,数值越小优先级越高 - 中断使能:
NVIC_EnableIRQ
允许 NVIC 转发 EXTI4 中断到 CPU
(六)6. 实现中断处理函数(业务逻辑编写)
// 定义全局标志位(控制 LED 状态)
volatile uint8_t flag = 0;
void EXTI4_IRQHandler(void)
{
// 检查 EXTI4 中断标志(确认中断源)
if ((EXTI->PR & (0x01 << 4)) != 0)
{
// 清除中断标志(必须手动写 1 清除,否则会重复触发)
EXTI->PR |= (0x01 << 4);
// 翻转标志位
flag = !flag;
// 模拟 LED 控制(根据 flag 切换状态,需实现 LED1 函数)
LED1(flag);
}
}
- 中断标志检测:通过
EXTI->PR
寄存器判断中断是否由 EXTI4 触发 - 标志清除:写 1 清除
PR
寄存器对应位,否则中断会持续触发 - 业务逻辑:翻转标志位并控制 LED,实现 “按键按下→LED 翻转” 的交互
四、库函数版配置对比(简化开发流程)
若使用 STM32 标准外设库,配置流程更简洁:
void EXTI4_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 1. 使能 AFIO 和 GPIOE 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE, ENABLE);
// 2. 配置 PE4 为输入模式(上拉/下拉,根据硬件决定)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入(匹配按键硬件)
GPIO_Init(GPIOE, &GPIO_InitStruct);
// 3. 配置引脚-中断线映射(PE4 → EXTI4)
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
// 4. 配置 EXTI4 中断模式
EXTI_InitStruct.EXTI_Line = EXTI_Line4;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
// 5. 配置 NVIC 中断
NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; // 响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
// 中断处理函数(与寄存器版逻辑一致)
void EXTI4_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line4) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line4); // 清除中断标志
flag = !flag;
LED1(flag);
}
}
库函数通过 GPIO_EXTILineConfig
简化引脚映射,EXTI_InitTypeDef
封装触发模式、中断使能等配置,降低了寄存器操作的复杂度。