在嵌入式开发中,外部中断是实现硬件事件实时响应的关键机制。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 时钟(关键外设使能)

            
            
              cpp
              
              
            
          
          // 使能 AFIO 时钟(必须!中断引脚映射依赖 AFIO)
RCC->APB2ENR |= (0x01 << 0);  AFIO 负责管理 GPIO 与 EXTI 中断线的映射关系,必须先使能其时钟才能配置引脚映射。
(二)2. 配置引脚 - 中断线映射(AFIO 寄存器操作)

            
            
              cpp
              
              
            
          
          // 将 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 中断请求

            
            
              cpp
              
              
            
          
          // 允许 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 中断优先级与使能
            
            
              cpp
              
              
            
          
          // 配置中断优先级分组(假设 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. 实现中断处理函数(业务逻辑编写)

            
            
              cpp
              
              
            
          
          // 定义全局标志位(控制 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 标准外设库,配置流程更简洁:
            
            
              cs
              
              
            
          
          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 封装触发模式、中断使能等配置,降低了寄存器操作的复杂度。