EXTI外部中断

EXTI外部中断

简介

对于互联型产品,外部中断/事件控制器由20个产生事件/中断请求的边沿检测器组成,对于其它产品,则有19个能产生事件/中断请求的边沿检测器。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。

通用I/O端口以下图的方式连接到16个外部中断/事件线上:

image

端口0-15分别对应EXTI线0-15,通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟。。

另外四个EXTI线的连接方式如下:

● EXTI线16连接到PVD输出

● EXTI线17连接到RTC闹钟事件

● EXTI线18连接到USB唤醒事件

● EXTI线19连接到以太网唤醒事件(只适用于互联型产品)

EXTI寄存器介绍

image

事件屏蔽寄存器(EXTI_EMR)

用于控制是否允许 EXTI 线上的事件请求传递到事件通道,是 EXTI 事件模式的核心控制位。

image

位域范围 含义说明
位 31:20 保留,必须始终保持为复位状态 (0)。
位 19:0 MRx:线 x 上的事件屏蔽 (Event Mask on line x)
0:屏蔽来自线 x 上的事件请求;
1:开放来自线 x 上的事件请求。
注:位 19 只适用于互联型产品,对于其它产品为保留位。

上升沿触发选择寄存器(EXTI_RTSR)

image

位域范围 含义说明
位 31:19 保留,必须始终保持为复位状态 (0)。
位 18:0 TRx:线 x 上的上升沿触发事件配置位 (Rising trigger event configuration bit of line x)
0:禁止输入线 x 上的上升沿触发 (中断和事件)
1:允许输入线 x 上的上升沿触发 (中断和事件)
注:位 19 只适用于互联型产品,对于其它产品为保留位。

外部唤醒线是边沿触发的,这些线上不能出现毛刺信号。

在写EXTI_RTSR寄存器时,在外部中断线上的上升沿信号不能被识别,挂起位也不会被置位。

在同一中断线上,可以同时设置上升沿和下降沿触发。即任一边沿都可触发中断

下降沿触发选择寄存器(EXTI_FTSR)

这个功能同上。

软件中断事件寄存器(EXTI_SWIER)

用于通过软件方式触发 EXTI 中断或事件,常用于中断测试或模拟外部触发。

image-20260218102638661

位域范围 含义说明
位 31:19 保留,必须始终保持为复位状态 (0)。
位 18:0 SWIERx:线 x 上的软件中断 (Software interrupt on line x)
当该位为 '0' 时,写 '1' 将设置 EXTI_PR中相应的挂起位。
如果在 EXTI_IMR 和 EXTI_EMR 中允许产生该中断,则此时将产生一个中断。
注:通过清除 EXTI_PR 的对应位 (写入 '1'),可以清除该位为 '0'。
注:位 19 只适用于互联型产品,对于其它产品为保留位。

挂起寄存器(EXTI_PR)

用于记录 EXTI 线是否发生了触发请求。在中断服务函数中,必须通过向对应位写入 '1' 来清除挂起位,否则中断会被持续触发。rc代码可读可清除,w1代表只有写1有效。

image

位域范围 含义说明
位 31:19 保留,必须始终保持为复位状态 (0)。
位 18:0 PRx:挂起位 (Pending bit)
0:没有发生触发请求
1:发生了选择的触发请求当在外部中断线上发生了选择的边沿事件,该位被置 '1'。
在该位中写入 '1' 可以清除它,也可以通过改变边沿检测的极性清除。
注:位 19 只适用于互联型产品,对于其它产品为保留位。

标准库的使用

一、EXTI 标准库配置核心概念

STM32 标准库把 EXTI 的配置封装成了结构化的函数和宏定义,核心是通过 EXTI_InitTypeDef 结构体定义中断线参数,再调用 EXTI_Init() 函数完成配置。

1. 核心结构体:EXTI_InitTypeDef

这是配置 EXTI 的核心结构体,所有中断线参数都通过它设置

typedef struct
{
    uint32_t EXTI_Line;               // 选择要配置的EXTI线(如EXTI_Line0~EXTI_Line19)
    EXTIMode_TypeDef EXTI_Mode;       // 中断/事件模式
    EXTITrigger_TypeDef EXTI_Trigger; // 触发边沿(上升沿/下降沿/双边沿)
    FunctionalState EXTI_LineCmd;     // ENABLE(使能)/DISABLE(禁用)该EXTI线C
}EXTI_InitTypeDef;

2. 关键参数枚举说明

枚举类型 可选值(常用) 含义说明
EXTI_Line EXTI_Line0 ~ EXTI_Line15 对应 GPIO 引脚的中断线(如 EXTI_Line0 对应 PA0/PB0/PC0 等)
EXTI_Line16 ~ EXTI_Line19 对应片内外设(PVD/RTC 闹钟 / USB 唤醒 / 以太网唤醒)
EXTI_Mode EXTI_Mode_Interrupt 中断模式(触发后进入中断服务函数)
EXTI_Mode_Event 事件模式(仅触发硬件信号,不进入中断)
EXTI_Trigger EXTI_Trigger_Rising 上升沿触发(低→高)
EXTI_Trigger_Falling 下降沿触发(高→低)
EXTI_Trigger_Rising_Falling 上升沿 + 下降沿触发(双边沿)

二、标准库 EXTI 配置完整步骤

以 “配置 PA0 为下降沿触发中断,触发后翻转 PB0 连接的 LED” 为例

#include "stm32f10x.h"

// 初始化 LED(PB0 推挽输出)
void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    // 使能 GPIOB 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_SetBits(GPIOB, GPIO_Pin_0); // 默认 LED 灭
}

// 初始化 EXTI 中断(PA0 下降沿触发)
void EXTI_Key_Init(void)
{
    // 1. 使能 GPIOA 和 AFIO 时钟(必须!EXTI映射依赖AFIO)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    // 2. 配置 PA0 为上拉输入(假设按键低电平有效)
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 3. 配置 AFIO:将 PA0 映射到 EXTI_Line0
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

    // 4. 配置 EXTI_Line0:下降沿触发、中断模式、使能
    EXTI_InitTypeDef EXTI_InitStruct;
    EXTI_InitStruct.EXTI_Line = EXTI_Line0;          // 选择 EXTI_Line0
    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_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;    // EXTI_Line0 对应 EXTI0_IRQn
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;  // 子优先级
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;     // 使能中断通道
    NVIC_Init(&NVIC_InitStruct);
}

// 6. 编写 EXTI0 中断服务函数(函数名固定,对应启动文件)
void EXTI0_IRQHandler(void)
{
    // 检查 EXTI_Line0 的挂起位,确认是该线触发的中断
    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 执行中断逻辑:翻转 PB0 LED
        GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0)));

        // 清除 EXTI_Line0 的挂起位(必须!写1清除,否则中断会重复触发)
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

int main(void)
{
    LED_Init();       // 初始化 LED
    EXTI_Key_Init();  // 初始化 EXTI 中断

    while (1)
    {
        // 主循环空转,等待中断触发
    }
}

三、常用 EXTI 操作函数

函数名 功能说明 常用场景
EXTI_DeInit() 复位 EXTI 所有配置为默认状态 重新配置 EXTI 前清空旧配置
EXTI_Init() 根据结构体配置 EXTI 线(核心初始化函数) 配置中断 / 事件的触发规则
EXTI_StructInit() EXTI_InitTypeDef 结构体填充为默认值 快速初始化结构体
EXTI_GenerateSWInterrupt() 软件触发指定 EXTI 线中断(无需硬件触发) 测试中断服务函数
EXTI_GetFlagStatus() 获取 EXTI 线的标志位状态(中断 / 事件通用) 事件模式下判断触发状态
EXTI_ClearFlag() 清除 EXTI 线的标志位 事件模式下清除触发标志
EXTI_GetITStatus() 获取 EXTI 线的中断标志位状态 中断服务函数中判断中断来源
EXTI_ClearITPendingBit() 清除 EXTI 线的中断挂起位(核心!) 中断服务函数末尾必须调用

四、配置注意事项

  1. AFIO 时钟必须使能:EXTI 线映射依赖 AFIO 外设,未使能会导致中断无法触发;
  2. GPIO 必须为输入模式:EXTI 仅能检测 GPIO 输入引脚的电平变化,输出模式引脚无法触发;
  3. 中断挂起位必须写 1 清除:仅读取 EXTI_GetITStatus() 无法清除挂起位,必须调用 EXTI_ClearITPendingBit() 写 1 清除;
  4. 同一 EXTI 线同一时间只能映射一个 GPIO 引脚:例如 EXTI_Line0 同一时刻只能映射 PA0、PB0、PC0 等中的一个;
  5. 中断服务函数名必须固定:需与启动文件(startup_stm32f10x_xx.s)中的中断向量表一致(如 EXTI0 对应 EXTI0_IRQHandler);
  6. 多中断线共用服务函数的处理:共用同一个中断服务函数的 EXTI 线(如 EXTI9_5_IRQHandler 对应 Line5~Line9),必须分别检查每个线的 PR 位,分别清除。

总结

  1. STM32F10x 标准库 EXTI 配置核心是 “时钟使能 → GPIO 输入配置 → AFIO 映射 → EXTI 初始化 → NVIC 配置 → 中断服务函数”6 步;
  2. 中断服务函数中,“检查挂起位 → 执行逻辑 → 清挂起位” 是固定流程,清挂起位是最核心的易错点;

Hal库的使用

要使用外部中断要先配置GPIO口为对应的外部中断模式,然后配置GPIO的参数,比如默认上下拉模式,和是软件还是事件模式,上升沿还是下降沿触发。

image

image

配置好初始化参数后生成文件还需要进行如下配置。

HAL 库采用回调机制处理中断,不再像标准库那样直接编写 EXTIx_IRQHandler()。你需要重写弱定义的回调函数 HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin),该函数会在 EXTI 中断触发后自动被调用。

/* 重写 EXTI 中断回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_3)
    {
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // 翻转 PB0 LED
    }
}

常用函数

函数名 功能说明 使用场景
HAL_GPIO_EXTI_Callback() EXTI 中断回调函数(需用户重写) 处理中断触发后的逻辑
HAL_GPIO_EXTI_IRQHandler() EXTI 中断处理入口函数(CubeMX 自动调用) 底层清除挂起位并调用回调函数

回调函数的参数判断:回调函数的参数 GPIO_Pin 对应触发中断的引脚(如 GPIO_PIN_3),多个 EXTI 线共用回调时必须分别判断;

无需手动清除挂起位:HAL 库的 HAL_GPIO_EXTI_IRQHandler() 会自动清除 EXTI_PR 挂起位,用户只需关注回调函数中的逻辑;

NVIC 优先级配置:在 CubeMX 的 “NVIC Settings” 中提前配置好 EXTI 中断的抢占优先级和子优先级,生成代码后无需手动修改;

回调函数的位置:将 HAL_GPIO_EXTI_Callback() 放在 main.cmain 函数之前,或单独放在 gpio.c 中。

总结

  1. CubeMX 配置并生成代码后,核心工作是重写 HAL_GPIO_EXTI_Callback() 回调函数
  2. HAL 库自动处理 EXTI 挂起位的清除,无需手动操作;
  3. 回调函数中通过参数 GPIO_Pin 判断中断来源,执行对应的逻辑。
posted @ 2026-02-18 11:09  野云闲出眠  阅读(0)  评论(0)    收藏  举报