STM32 EXTI(外部中断)详解

一、EXTI是什么?

EXTI = External Interrupt/Event Controller(外部中断/事件控制器)

简单说:EXTI是STM32的"门卫",专门管引脚上的电平变化。当引脚电平变化时,EXTI会通知CPU:"有情况!"

二、EXTI能干什么?

两种工作模式:

  1. 中断模式:电平变化 → EXTI → CPU → 执行中断函数

  2. 事件模式:电平变化 → EXTI → 直接触发其他外设(不打扰CPU)

常见用途:

  • 按键检测(按一下触发一次)

  • 限位开关(碰到就停)

  • 外部信号计数(来一个脉冲记一次)

三、EXTI的硬件连接(重要!)

复制代码
外部引脚 → GPIO端口 → EXTI线路 → NVIC → CPU
      ↑          ↑          ↑        ↑      ↑
    电平变化   端口选择   线路选择  优先级  执行中断
复制代码

关键规则:

  1. 每个EXTI线路对应多个GPIO引脚(但有规矩!)

  2. 同一时间,一个EXTI线路只能连一个GPIO引脚

四、EXTI线路分配表(必须记住!)

EXTI线路 可以连接的GPIO引脚
EXTI0 PA0, PB0, PC0, PD0, PE0, ... PI0
EXTI1 PA1, PB1, PC1, PD1, PE1, ... PI1
EXTI2 PA2, PB2, PC2, PD2, PE2, ... PI2
... ...(按数字对应)
EXTI15 PA15, PB15, PC15, PD15, ... PI15

重要规律:EXTI0可以连所有端口的引脚0,EXTI1连所有引脚1,以此类推。

五、完整配置步骤(按键为例)

第1步:初始化按键引脚(PA0为例)

复制代码
void KEY_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 1. 开启GPIOA时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    
    // 2. 配置PA0为上拉输入(按键按下接地)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;      // 输入模式
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      // 上拉电阻
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}
复制代码

第2步:配置EXTI中断(核心!)

复制代码
void EXTI0_Init(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 1. 开启SYSCFG时钟(必须!很多人忘记)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    
    // 2. 将PA0连接到EXTI0线路
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
    
    // 3. 配置EXTI0线路
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;        // 线路0
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;         // 使能
    EXTI_Init(&EXTI_InitStructure);
    
    // 4. 配置NVIC中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;  // 中断源
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
复制代码

第3步:编写中断服务函数

复制代码
// EXTI0中断服务函数(函数名固定,不能改!)
void EXTI0_IRQHandler(void)
{
    // 1. 检查是否是EXTI0触发的中断
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 2. 你的处理代码
        if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
        {
            // 按键按下的处理
            LED_Toggle();  // 切换LED状态
        }
        
        // 3. 清除中断标志(必须!不然会一直进中断)
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}
复制代码

六、触发方式详解

触发方式 说明 适用场景
上升沿触发 低电平→高电平时触发 按键释放检测
下降沿触发 高电平→低电平时触发 按键按下检测
双边沿触发 上升和下降都触发 电平变化检测
复制代码
// 选择触发方式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;   // 上升沿
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  // 下降沿  
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 双边沿
复制代码

七、多按键配置示例(多个EXTI线路)

复制代码
// 配置PA0(EXTI0)和PC13(EXTI13)两个按键
void EXTI_Multi_Init(void)
{
    // ... 开启时钟、GPIO初始化 ...
    
    // PA0 -> EXTI0(按键1)
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&EXTI_InitStructure);
    
    // PC13 -> EXTI13(按键2)
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource13);
    EXTI_InitStructure.EXTI_Line = EXTI_Line13;
    EXTI_Init(&EXTI_InitStructure);
    
    // 配置两个中断通道
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_Init(&NVIC_InitStructure);
    
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;  // 注意!10-15共用
    NVIC_Init(&NVIC_InitStructure);
}

// 中断服务函数
void EXTI0_IRQHandler(void)   // 处理EXTI0
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 处理按键1
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

void EXTI15_10_IRQHandler(void)  // 处理EXTI10-15
{
    if(EXTI_GetITStatus(EXTI_Line13) != RESET)
    {
        // 处理按键2(PC13)
        EXTI_ClearITPendingBit(EXTI_Line13);
    }
}
复制代码

八、注意事项(避坑指南)

常见错误1:忘记开SYSCFG时钟

复制代码
// 必须加!
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
常见错误2:中断函数名写错
  • EXTI0-4:EXTI0_IRQHandlerEXTI1_IRQHandler...

  • EXTI5-9:共用 EXTI9_5_IRQHandler

  • EXTI10-15:共用 EXTI15_10_IRQHandler

常见错误3:没清除中断标志

会导致重复进入中断,程序卡死!

常见错误4:GPIO模式设错

必须设置为输入模式:GPIO_Mode_IN

九、事件模式 vs 中断模式

复制代码
// 中断模式:需要CPU参与
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

// 事件模式:直接触发其他外设(如DMA、ADC)
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
// 事件模式不触发中断,不需要NVIC配置
复制代码

十、软件触发中断(特殊用途)

复制代码
// 有时候需要软件模拟一个中断
EXTI_GenerateSWInterrupt(EXTI_Line0);  // 软件触发EXTI0中断
复制代码

十一、消抖处理(实际应用)

复制代码
void EXTI0_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 简单延时消抖
        delay_ms(20);  // 延时20ms
        
        // 再次检测
        if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
        {
            // 确实按下了
            LED_Toggle();
        }
        
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

十二、EXTI配置速查表

步骤 做什么 关键函数/配置
1 开SYSCFG时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG)
2 GPIO初始化 模式=输入,上下拉按需
3 引脚连EXTI SYSCFG_EXTILineConfig(端口源, 引脚源)
4 配置EXTI 选线路、模式、触发方式
5 配置NVIC 设优先级
6 写中断函数 函数名固定,记得清标志

总结口诀

复制代码
EXTI是门卫,管脚电平变。
先开时钟后连线,SYSCFG别忘记。
引脚线路要对齐,0对0来1对1。
触发方式有三种,上升下降和双边。
中断函数名固定,清除标志要牢记。
按键记得要消抖,实际应用才稳定。
相关推荐
简单中的复杂2 分钟前
【避坑指南】RK3576 Linux SDK 编译:解决 Buildroot 卡死在 host-gcc-final 的终极方案
linux·嵌入式硬件
上海合宙LuatOS28 分钟前
LuatOS核心库API——【audio 】
java·网络·单片机·嵌入式硬件·物联网·音视频·硬件工程
Hhh __灏39 分钟前
stm32的SRAM内存不足如何分析和优化?堆栈空间如何优化?
单片机
LS_learner1 小时前
Snapd和Apt—Linux 上两种完全不同的软件包管理系统
嵌入式硬件
点灯小铭1 小时前
基于51单片机的双档交流电压表设计与实现
单片机·嵌入式硬件·毕业设计·51单片机·课程设计·期末大作业
厦门辰迈智慧科技有限公司2 小时前
全功能数据采集仪mcu主要用途
单片机·嵌入式硬件·水库大坝安全监测·数据采集仪mcu
jl48638212 小时前
变比测试仪显示屏的“标杆“配置!如何兼顾30000小时寿命与六角矢量图精准显示?
人工智能·经验分享·嵌入式硬件·物联网·人机交互
清风6666662 小时前
基于单片机的智能电热水壶设计与温度控制系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
Balabala噗3 小时前
JLink-Cortex-M Error报错-No Cortex-M SW Device Found-解决办法整理
单片机·嵌入式硬件