【STM32】外部中断
STM32 外部中断(EXTI)概述
这篇文章结合示例代码,系统性地讲述 STM32 外部中断(EXTI)实验的原理、以及配置流程。目的在于辅助读者掌握STM32F1 外部中断机制。
STM32F1xx官方资料:《STM32中文参考手册V10》-第9章 中断和事件
STM32的每个IO都可以作为外部中断输入。
STM32的中断控制器支持19个外部中断/事件请求:
线0~15:对应外部IO口的输入中断。
线16:连接到PVD输出。
线17:连接到RTC闹钟事件。
线18:连接到USB唤醒事件。每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。从上面可以看出,STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),
STM32F103RCT6(51),那么中断线怎么跟io口对应呢?
如图所示,
GPIOx.0映射到EXTI0
GPIOx.1映射到EXTI1
…
GPIOx.15映射到EXTI15图20:AFIO_EXTICR 映射机制图:
每个 EXTI 线可通过 AFIO 寄存器选择映射到 GPIOA~GPIOG 的某一个引脚。
例如:EXTI3 可以绑定 PE3、PC3、PB3 等,只能绑定一个。
特性 | 说明 |
---|---|
可用中断线 | EXTI0 ~ EXTI15(共16条 IO 线) + EXTI16(PVD)、EXTI17(RTC)、EXTI18(USB) |
可触发方式 | 上升沿、下降沿、双边沿 |
可配置功能 | 中断 / 事件 |
IO与EXTI映射 | 通过 AFIO_EXTICR 寄存器设置 |
中断向量数量 | 仅有 7 个中断服务函数(EXTI0~4,EXTI9_5,EXTI15_10) |
中断线与中断函数映射关系
对于每个中断线,我们可以设置相应的触发方式(上升沿触发,下降沿触发,边沿触发)以及使能。
那么,是不是16个中断线就可以分配16个中断服务函数呢?
IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数。
从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数,
外部中断线10~15分配一个中断向量,共用一个中断服务函数。
中断线 | 中断服务函数 | 是否共享中断 |
---|---|---|
EXTI0 | EXTI0_IRQHandler | 否 |
EXTI1 | EXTI1_IRQHandler | 否 |
EXTI2 | EXTI2_IRQHandler | 否 |
EXTI3 | EXTI3_IRQHandler | 否 |
EXTI4 | EXTI4_IRQHandler | 否 |
EXTI5~9 | EXTI9_5_IRQHandler | 是,需区分 |
EXTI10~15 | EXTI15_10_IRQHandler | 是,需区分 |
另外,补充一个知识点,线16、线17、线18 是 STM32 外部中断控制器(EXTI)中,与特定内部外设连接的特殊中断线。 它们不再对应 GPIO 引脚,而是与 STM32 内部某些模块直接相关。
EXTI 特殊中断线(EXTI16 ~ EXTI18)
EXTI16 — PVD(电源电压监测器)
EXTI17 — RTC Alarm(实时时钟闹钟)
EXTI18 — USB Wakeup(USB 唤醒)
中断线编号 | 来源模块 | 名称/用途 | 中断触发条件 | 常用用途 |
---|---|---|---|---|
EXTI16 | PVD(电源监测) | 电压检测中断 | 电压低于/高于设定阈值 | 电源掉电、低电压预警 |
EXTI17 | RTC(实时时钟) | RTC闹钟中断 | RTC 到达设定闹钟时间 | 定时唤醒、定时事件处理 |
EXTI18 | USB(唤醒模块) | USB 唤醒中断 | USB 活动信号唤醒 MCU | USB 唤醒低功耗模式 |
外部中断库函数设置
中断服务函数列表:
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
EXTI_Init函数
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
typedef struct
{uint32_t EXTI_Line; //指定要配置的中断线 EXTIMode_TypeDef EXTI_Mode; //模式:事件 OR中断EXTITrigger_TypeDef EXTI_Trigger;//触发方式:上升沿/下降沿/双沿触发FunctionalState EXTI_LineCmd; //使能 OR失能
}EXTI_InitTypeDef;EXTI_InitStructure.EXTI_Line=EXTI_Line2; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);
EXTI 外部中断配置步骤(官方流程)
外部中断的一般配置步骤:
① 初始化IO口为输入。
GPIO_Init();
② 开启IO口复用时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
③ 设置IO口与中断线的映射关系。
void GPIO_EXTILineConfig();
④ 初始化线上中断,设置触发条件等。
EXTI_Init();
⑤ 配置中断分组(NVIC),并使能中断。
NVIC_Init();
⑥ 编写中断服务函数。
EXTIx_IRQHandler();
⑦ 清除中断标志位
EXTI_ClearITPendingBit();
EXTI线通过 AFIO_EXTICR1~4 映射 GPIO 引脚
EXTI0
可映射PA0 ~ PG0
EXTI15
可映射PA15 ~ PG15
中断向量表图说明:仅为 EXTI0~4、EXTI9_5、EXTI15_10 提供中断服务函数。多线共享的服务函数需在函数内判断是哪条线路触发。
🌟 配置步骤详解(以 KEY1 按键连接 PE3 → EXTI3 为例):
① 配置 GPIO 为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); // 使能GPIOE时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // PE3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);
② 使能 AFIO(复用功能模块)时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
③ 配置 EXTI 线映射(将 PE3 映射到 EXTI3)
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
④ 配置 EXTI 中断线
EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line3; // EXTI3
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能
EXTI_Init(&EXTI_InitStructure);
⑤ 配置 NVIC 中断优先级并使能中断
NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能
NVIC_Init(&NVIC_InitStructure);
⑥ 编写中断服务函数 EXTI3_IRQHandler
void EXTI3_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line3) != RESET){// 中断处理逻辑,比如点亮 LEDLED1_TOGGLE(); // 示例函数,用户自定义EXTI_ClearITPendingBit(EXTI_Line3); // 清除中断标志位}
}
按键的硬件连接
战舰:
精英:
mini:
原理图说明(KEY0 ~ KEY2 与 PA0、PE3、PE4)(对照图解说明)
按键名称 | 接口引脚 | 对应 EXTI 线 | 中断函数 |
---|---|---|---|
KEY0 | PE4 | EXTI4 | EXTI4_IRQHandler |
KEY1 | PE3 | EXTI3 | EXTI3_IRQHandler |
KEY2 | PE2 | EXTI2 | EXTI2_IRQHandler |
WK_UP | PA0 | EXTI0 | EXTI0_IRQHandler |
多个引脚使用同一个中断线时的处理(共享中断)
如图所示:
EXTI9_5_IRQHandler 处理 EXTI5EXTI9
EXTI15_10_IRQHandler 处理 EXTI10EXTI15
处理多个中断线:
void EXTI9_5_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line7) != RESET){// 处理 EXTI7 对应按键EXTI_ClearITPendingBit(EXTI_Line7);}if (EXTI_GetITStatus(EXTI_Line6) != RESET){// 处理 EXTI6 对应按键EXTI_ClearITPendingBit(EXTI_Line6);}
}
完整按键中断实验总结
📋 总体代码结构:
void KEY_Interrupt_Init(void)
{// 1. 初始化 GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOE, &GPIO_InitStructure);// 2. 映射 EXTI 线GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);// 3. 配置 EXTIEXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line3;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);// 4. 配置 NVICNVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}
综上。
步骤
1️⃣ 配置 GPIO 为输入模式(上拉/下拉)
2️⃣ 使能 AFIO 时钟
3️⃣ 使用GPIO_EXTILineConfig()
设置 EXTI 映射
4️⃣ 使用EXTI_Init()
配置中断线触发方式
5️⃣ 使用NVIC_Init()
设置中断优先级与使能
6️⃣ 编写EXTIx_IRQHandler()
并清除中断标志位
以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!