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

端口0-15分别对应EXTI线0-15,通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟。。
另外四个EXTI线的连接方式如下:
● EXTI线16连接到PVD输出
● EXTI线17连接到RTC闹钟事件
● EXTI线18连接到USB唤醒事件
● EXTI线19连接到以太网唤醒事件(只适用于互联型产品)
EXTI寄存器介绍

事件屏蔽寄存器(EXTI_EMR)
用于控制是否允许 EXTI 线上的事件请求传递到事件通道,是 EXTI 事件模式的核心控制位。

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

| 位域范围 | 含义说明 |
|---|---|
| 位 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 中断或事件,常用于中断测试或模拟外部触发。
| 位域范围 | 含义说明 |
|---|---|
| 位 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有效。

| 位域范围 | 含义说明 |
|---|---|
| 位 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 线的中断挂起位(核心!) | 中断服务函数末尾必须调用 |
四、配置注意事项
- AFIO 时钟必须使能:EXTI 线映射依赖 AFIO 外设,未使能会导致中断无法触发;
- GPIO 必须为输入模式:EXTI 仅能检测 GPIO 输入引脚的电平变化,输出模式引脚无法触发;
- 中断挂起位必须写 1 清除:仅读取
EXTI_GetITStatus()无法清除挂起位,必须调用EXTI_ClearITPendingBit()写 1 清除; - 同一 EXTI 线同一时间只能映射一个 GPIO 引脚:例如 EXTI_Line0 同一时刻只能映射 PA0、PB0、PC0 等中的一个;
- 中断服务函数名必须固定:需与启动文件(
startup_stm32f10x_xx.s)中的中断向量表一致(如 EXTI0 对应EXTI0_IRQHandler); - 多中断线共用服务函数的处理:共用同一个中断服务函数的 EXTI 线(如 EXTI9_5_IRQHandler 对应 Line5~Line9),必须分别检查每个线的 PR 位,分别清除。
总结
- STM32F10x 标准库 EXTI 配置核心是 “时钟使能 → GPIO 输入配置 → AFIO 映射 → EXTI 初始化 → NVIC 配置 → 中断服务函数”6 步;
- 中断服务函数中,“检查挂起位 → 执行逻辑 → 清挂起位” 是固定流程,清挂起位是最核心的易错点;
Hal库的使用
要使用外部中断要先配置GPIO口为对应的外部中断模式,然后配置GPIO的参数,比如默认上下拉模式,和是软件还是事件模式,上升沿还是下降沿触发。


配置好初始化参数后生成文件还需要进行如下配置。
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.c 中 main 函数之前,或单独放在 gpio.c 中。
总结
- CubeMX 配置并生成代码后,核心工作是重写
HAL_GPIO_EXTI_Callback()回调函数; - HAL 库自动处理 EXTI 挂起位的清除,无需手动操作;
- 回调函数中通过参数
GPIO_Pin判断中断来源,执行对应的逻辑。

浙公网安备 33010602011771号