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 中断分组全局唯一,项目内统一配置,避免多处冲突。
相关推荐
码上上岸2 小时前
基于单片机的老人跌倒监测报警器设计与实现
stm32·嵌入式硬件·物联网·智能硬件·硬件
恒锐丰科技林技术员2 小时前
SS6209 半桥驱动器:小型半桥电源方案的优选器件
经验分享·嵌入式硬件
别催小唐敲代码5 小时前
Pixhawk 6C — Mission Planner 烧录 & 全套校准教程
stm32·单片机·嵌入式硬件
wuyk5557 小时前
从零吃透Modbus通信|第7章:终极工程整合(模块化架构、双模式主机从机、RTOS适配、量产级模板)
c语言·stm32·学习·架构
Freak嵌入式7 小时前
Pico UART 数据收发实战:硬件配置、MicroPython 编程
java·开发语言·单片机·嵌入式硬件·生活
一条破秋裤8 小时前
STM32 学习笔记:PWM 驱动 LED 呼吸灯与舵机代码
笔记·stm32·学习
Freak嵌入式8 小时前
Pico PIO 汇编语法深度详解:标识符、指令、伪指令全解析
汇编·stm32·单片机·嵌入式硬件·fpga开发·硬件架构
自小吃多9 小时前
PCB Editor软件差分对添加与设置笔记
笔记·嵌入式硬件
逐步前行10 小时前
ESP32-S3-工程管理
单片机
智购科技无人售货机厂家11 小时前
2026自动售货机连接器选型指南:从IP等级到插拔寿命的工程实践~YH
单片机·嵌入式硬件