STM32 GPIO 外部中断深度解析:边沿触发 / 电平触发、NVIC 优先级配置、中断嵌套实战

一、前言

GPIO 外部中断(EXTI)是嵌入式最常用的异步信号采集方案,常用于按键、光电传感器、脉冲信号、外部告警信号捕获。

新手常见疑难问题:

  1. 电平触发模式下中断反复进入,无法退出;
  2. 多个外部中断同时触发,高优先级无法抢占低优先级;
  3. 中断服务函数内执行耗时代码,系统卡顿;
  4. 外部信号干扰导致中断频繁误触发;
  5. 搞不懂抢占优先级、响应优先级,不知道如何配置中断嵌套。

重要前提:STM32F1 系列,同一 GPIO 引脚号共享一条 EXTI 线

PA0/PB0/PC0 → EXTI0;PA1/PB1 → EXTI1...... 因此不能同时开启同引脚号外部中断。

二、EXTI 硬件基础与四种触发方式

2.1 触发模式详解

  1. 下降沿触发 GPIO_MODE_IT_FALLING
    信号从高电平→低电平瞬间触发。
    适用:上拉按键、NPN 传感器、低电平脉冲信号。
  2. 上升沿触发 GPIO_MODE_IT_RISING
    信号从低电平→高电平瞬间触发。
    适用:外部上电信号、上升沿脉冲。
  3. 双边沿触发 GPIO_MODE_IT_RISING_FALLING
    电平跳变任意方向均触发。
    适用:正交编码器、双向脉冲信号。
  4. 电平触发(EXTI 硬件支持,HAL 库不直接封装,不推荐裸机使用)
    只要维持对应电平就持续申请中断,极易造成死循环
    ✅项目规范:裸机开发一律使用边沿触发,杜绝电平触发。

关键区别:边沿触发只在跳变瞬间产生一次中断请求;电平触发只要电平满足就持续请求中断。

三、NVIC 核心概念:抢占优先级 & 响应优先级(中断嵌套关键)

STM32 中断分组决定优先级划分规则(常用中断分组 2

  • 抢占优先级(PreemptionPriority):能否打断正在运行的中断(实现嵌套)
  • 响应优先级(SubPriority) :同抢占优先级下,谁先执行;不能实现嵌套

嵌套成立条件:

新中断抢占优先级 < 当前正在执行中断抢占优先级

抢占优先级数值越小,优先级越高。

示例(分组 2):

EXTI0:抢占优先级 1,子优先级 0

EXTI1:抢占优先级 0,子优先级 0

EXTI1 触发时可以打断 EXTI0,实现中断嵌套。

⚠️大坑:

两个中断抢占优先级相同,无论子优先级如何,无法相互抢占,不存在嵌套

四、项目方案设计

硬件定义

KEY1:PA0 下降沿外部中断 EXTI0

KEY2:PA1 下降沿外部中断 EXTI1

逻辑设计:

  1. KEY1 中断:普通任务,模拟较长耗时处理;
  2. KEY2 中断:更高优先级,能够打断 KEY1 的中断服务函数,验证中断嵌套;
  3. 中断内仅标记事件,复杂业务放到主循环处理(嵌入式标准规范);
  4. 模块化分层,引脚统一管理,代码便于移植。

五、完整模块化代码(HAL 库)

hardware_gpio.h

#ifndef __HARDWARE_GPIO_H

#define __HARDWARE_GPIO_H

#include "stm32f1xx_hal.h"

#define KEY1_PORT GPIOA

#define KEY1_PIN GPIO_PIN_0

#define KEY2_PORT GPIOA

#define KEY2_PIN GPIO_PIN_1

//中断事件标志

extern uint8_t key1_int_flag;

extern uint8_t key2_int_flag;

#endif

exti_gpio.h

#ifndef __EXTI_GPIO_H

#define __EXTI_GPIO_H

void EXTI_Key_Init(void);

#endif

exti_gpio.c

#include "exti_gpio.h"

#include "hardware_gpio.h"

uint8_t key1_int_flag = 0;

uint8_t key2_int_flag = 0;

void EXTI_Key_Init(void)

{

GPIO_InitTypeDef gpio_conf = {0};

__HAL_RCC_GPIOA_CLK_ENABLE();

复制代码
//PA0 下降沿中断
gpio_conf.Pin = KEY1_PIN;
gpio_conf.Mode = GPIO_MODE_IT_FALLING;
gpio_conf.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY1_PORT, &gpio_conf);

//PA1 下降沿中断(更高抢占优先级,支持嵌套)
gpio_conf.Pin = KEY2_PIN;
gpio_conf.Mode = GPIO_MODE_IT_FALLING;
gpio_conf.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY2_PORT, &gpio_conf);

//配置NVIC 中断分组2
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

//EXTI0:抢占优先级1,子优先级0
HAL_NVIC_SetPriority(EXTI0_IRQn,1,0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);

//EXTI1:抢占优先级0,子优先级0(更高优先级,可嵌套)
HAL_NVIC_SetPriority(EXTI1_IRQn,0,0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);

}

stm32f1xx_it.c 中断服务函数

#include "hardware_gpio.h"

//EXTI0中断服务函数

void EXTI0_IRQHandler(void)

{

if(HAL_GPIO_EXTI_GetHandle(&hgpio,KEY1_PIN))

{

HAL_GPIO_EXTI_Callback(KEY1_PIN);

}

}

//EXTI1中断服务函数

void EXTI1_IRQHandler(void)

{

if(HAL_GPIO_EXTI_GetHandle(&hgpio,KEY2_PIN))

{

HAL_GPIO_EXTI_Callback(KEY2_PIN);

}

}

//外部中断回调函数

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

{

if(GPIO_Pin == KEY1_PIN)

{

key1_int_flag = 1;

}

else if(GPIO_Pin == KEY2_PIN)

{

key2_int_flag = 1;

}

}

main.c 主循环业务处理

#include "stm32f1xx_hal.h"

#include "exti_gpio.h"

#include "hardware_gpio.h"

int main(void)

{

HAL_Init();

SystemClock_Config();

复制代码
EXTI_Key_Init();

while(1)
{
    if(key1_int_flag)
    {
        key1_int_flag = 0;
        //此处放置KEY1业务逻辑
    }
    if(key2_int_flag)
    {
        key2_int_flag = 0;
        //此处放置KEY2紧急业务逻辑
    }
}

}

六、工程硬性规范(量产项目必须遵守)

  1. 中断回调函数只做标记,禁止复杂运算、长延时
    中断执行越短越好,耗时业务转移至主循环;
  2. 外部按键中断务必开启内部上拉,减少外部干扰;
  3. 工业环境信号建议外部增加 RC 滤波,防止高频毛刺频繁触发中断;
  4. 同一 EXTI 线只能使用一个引脚,不要同时启用 PA0、PB0 外部中断;
  5. 需要中断嵌套,必须合理拉开抢占优先级差距;
  6. 尽量统一项目全局中断分组,不要多处代码修改 NVIC 分组。

七、高频踩坑汇总

坑 1:中断反复不断触发

原因:电平触发模式、信号有抖动毛刺、无滤波。

解决:切换边沿触发,硬件增加 RC 滤波或软件短时标志屏蔽。

坑 2:高优先级中断无法抢占低优先级(不能嵌套)

原因:抢占优先级相同,仅子优先级不同。

解决:调整 PreemptionPriority 数值,拉开层级。

坑 3:中断进不去

排查顺序:时钟开启 → GPIO 模式配置 → NVIC 使能 IRQ → 信号电平变化方向与触发方式匹配。

坑 4:多个引脚共用一条 EXTI 线导致异常

例如同时开启 PA0 和 PB0 外部中断,中断号冲突,出现随机错乱。

八、拓展适配场景

  • 光电开关、流量脉冲采集(双边沿 / 下降沿中断)
  • 设备外部紧急告警输入(设置最高抢占优先级)
  • 编码器脉冲捕获(双边沿外部中断简易方案)
  • 485 总线接收唤醒信号

九、全文总结

  1. 裸机项目优先使用边沿触发,远离电平触发;
  2. 中断嵌套由抢占优先级决定,响应优先级仅用于同级仲裁;
  3. 中断服务函数遵循 "快进快出" 原则,只置标志;
  4. EXTI 引脚存在硬件分组限制,同编号引脚不能同时开启外部中断;
  5. NVIC 中断分组全局唯一,项目内统一配置,避免多处冲突。
相关推荐
LCG元1 小时前
STM32F103 CAN 总线通信实战:标准库双机通信与过滤器配置精讲
stm32·单片机·嵌入式硬件
zlinear数据采集卡2 小时前
D223的PWM电机控制:6路独立脉冲+加减速算法深度解析
arm开发·stm32·嵌入式硬件·算法·fpga开发·架构
Kuakewei8883 小时前
平芯微42V热拔插OVP过压保护芯片:PW1600
嵌入式硬件
ACP广源盛139246256733 小时前
WAIC2026 国产超节点算力浪潮下@ACP#IX9104 在算力矩阵中的定位与落地场景
大数据·数据库·人工智能·嵌入式硬件·线性代数·矩阵
z20348315203 小时前
STM32单片机控制无源蜂鸣器播放《晴天》
stm32·单片机·嵌入式硬件
国科安芯5 小时前
详解ADC 规则组外部触发:CH2、CH3 与 TRGO2
运维·网络·单片机·嵌入式硬件·mcu·系统架构
ACP广源盛139246256735 小时前
WAIC2026 国产算力浪潮@ACP#YLB3118 在算力矩阵中的存储定位与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件
ACP广源盛139246256735 小时前
WAIC2026 国产算力浪潮下@ACP#IX8024 在算力矩阵中的定位与落地场景
大数据·数据库·人工智能·嵌入式硬件·线性代数·矩阵
Dr.kangder5 小时前
嵌入式面试总结(十四)——中断处理
单片机·面试·职场和发展·架构·硬件架构·嵌入式