中断与外部中断 EXTI
基于 STM32F103 · 零基础讲解中断概念、NVIC 嵌套向量控制器、抢占/响应优先级、EXTI 外部中断与按键中断点灯。
🎯学习目标
- 理解和掌握中断的概念和中断处理过程;
- 掌握 STM32F103 的中断类型、优先级概念和中断向量表;
- 了解 NVIC 嵌套向量中断控制器的结构和特点;
- 掌握 EXTI 的内部结构、工作原理和特性;
- 熟悉 NVIC 和 EXTI 的标准外设库与 HAL 库函数;
- 掌握 STM32 中断配置过程及外部中断开发方法。
1中断的相关概念
中断:计算机在执行程序过程中,当出现异常情况(如断电)或特殊请求(如数据传输)时,CPU 暂停现行程序的运行,转向处理这些异常或请求,处理完毕后再返回到原程序的中断处继续执行。
中断是单片机实时处理内部或外部事件的一种机制。中断和异常本质都是对主程序的"中断"。
为什么使用中断?
CPU 速度快、外设速度慢。若用"等待/查询"方式会降低 CPU 效率;用中断方式则:
提高效率
CPU 无需轮询等待,外设就绪时主动通知。
实时处理
对实时事件及时响应。
异常处理
对突发故障及时处理。
2中断处理流程
中断处理流程分为四步:中断请求 → 中断响应 → 中断服务 → 中断返回。
| 步骤 | 说明 | 由谁完成 |
|---|---|---|
| 中断请求 | 中断源向 CPU 发请求信号,中断请求寄存器被置位 | 硬件 |
| 中断响应 | CPU 判断是否符合响应条件,符合则暂停当前程序跳转到 ISR | 硬件 |
| 中断服务 | 执行为处理中断而编写的程序(ISR) | 开发人员编写 |
| 中断返回 | 退出 ISR,返回被中止的位置继续执行主程序 | 硬件 |
3STM32 中断与异常向量表
中断数量
- Cortex-M3 内核最多支持 256 个中断:15 个内核中断 + 240 个外部中断,具有 256 级可编程优先级;
- STM32F10x 系列有 84 个中断通道:16 个内核中断 + 68 个可屏蔽中断;
- STM32F103 只有 60 个可屏蔽中断,STM32F107 有 68 个可屏蔽中断。
中断向量表
当发生异常或中断,内核需要知道中断服务程序的入口地址,由这些入口地址组成的表称为中断向量表。入口地址存放在程序存储器(ROM),默认从零地址开始,每个向量占 4 个字节。
| 向量编号 | 入口地址 | 说明 |
|---|---|---|
| — | 0x00000000 | MSP 的初始值 |
| 1 | 0x00000004 | 复位向量(PC 初始值) |
| 2 | 0x00000008 | NMI 异常服务程序入口 |
| 3 | 0x0000000C | 硬 Fault 异常服务程序入口 |
| … | … | 其它中断服务程序入口 |
STM32 在标准库 stm32f10x.h 中通过 IRQn_Type 枚举将中断号与宏名联系起来,使用时直接引用宏名(如 EXTI3_IRQn)。
typedef enum IRQn {
NonMaskableInt_IRQn = -14, // 不可屏蔽中断
MemoryManagement_IRQn= -12, // 存储器管理
BusFault_IRQn = -11, // 总线错误
...
EXTI0_IRQn = 6, EXTI1_IRQn = 7, ...
} IRQn_Type;
4NVIC 与中断优先级 ⭐(核心考点)
NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)负责管理 STM32 的中断。STM32 使用 Cortex-M3 的 8 位优先级寄存器中的 4 位来配置优先级,即只支持 16 级中断优先级管理。
优先级分组(5 组)
用 NVIC_PriorityGroupConfig() 设置,将 4 位在抢占和响应之间分配:
| 分组 | 抢占优先级 | 响应优先级 | 位分配 |
|---|---|---|---|
| Group_0 | 0 | 0~15 | 抢占 0 位 / 响应 4 位 |
| Group_1 | 0~1 | 0~7 | 抢占 1 位 / 响应 3 位 |
| Group_2 | 0~3 | 0~3 | 抢占 2 位 / 响应 2 位 |
| Group_3 | 0~7 | 0~1 | 抢占 3 位 / 响应 1 位 |
| Group_4 | 0~15 | 0 | 抢占 4 位 / 响应 0 位 |
NVIC 初始化结构体
typedef struct {
uint8_t NVIC_IRQChannel; // 中断源(IRQ通道)
uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级
uint8_t NVIC_IRQChannelSubPriority; // 响应优先级
FunctionalState NVIC_IRQChannelCmd; // 使能中断通道
} NVIC_InitTypeDef;
NVIC 相关 5 个函数定义在标准库 misc.c / misc.h 中。
5中断服务程序 ISR
STM32 将所有中断服务程序统一放在标准库 stm32f10x_it.c 文件中。其中每个中断服务函数只有函数名、函数体为空,需要用户自己编写函数体。
一个典型的 ISR 结构包括:检测中断标志 → 处理逻辑 → 清除中断标志。
void EXTI2_IRQHandler(void) {
if(EXTI_GetITStatus(EXTI_Line2) != RESET) { // ① 判断中断是否发生
/* 中断处理逻辑 */ // ② 处理
EXTI_ClearITPendingBit(EXTI_Line2); // ③ 清除中断标志
}
}
6STM32 外部中断 EXTI
EXTI(External Interrupt/Event Controller,外部中断/事件控制器)支持 19 个外部中断/事件请求,每个都有独立的触发和屏蔽设置,具有中断模式和事件模式两种。STM32 芯片之外的外设中断(I/O 端口)由 EXTI 和 NVIC 共同负责,每一个 GPIO 引脚都可配置成外部中断触发源。
中断模式
Interrupt事件模式
EventGPIO 与中断线映射(重要)
EXTI 中断服务函数对应关系
| 中断线 | 中断服务函数 |
|---|---|
| EXTI0~EXTI4 | 各自独立,如 EXTI0_IRQHandler … EXTI4_IRQHandler |
| EXTI5~EXTI9 | 共用 EXTI9_5_IRQHandler(void) |
| EXTI10~EXTI15 | 共用 EXTI15_10_IRQHandler(void) |
EXTI 标准库函数定义在 stm32f10x_exti.c,头文件声明共 8 种函数,常用的有:
| 函数 | 功能 |
|---|---|
EXTI_Init() | 按结构体初始化 EXTI |
EXTI_GetITStatus() | 检查 EXTI 线触发请求是否发生(返回 SET/RESET) |
EXTI_ClearITPendingBit() | 清除 EXTI 线挂起位 |
GPIO_EXTILineConfig() | 选择 GPIO 引脚用作外部中断线路 |
typedef struct {
uint32_t EXTI_Line; // 中断/事件线,如 EXTI_Line0
EXTIMode_TypeDef EXTI_Mode; // EXTI_Mode_Interrupt / EXTI_Mode_Event
EXTITrigger_TypeDef EXTI_Trigger; // 上升沿/下降沿/双沿触发
FunctionalState EXTI_LineCmd; // ENABLE / DISABLE
} EXTI_InitTypeDef;
7EXTI 标准库中断配置步骤
使用 STM32 外部中断的完整配置流程(必考):
| 步骤 | 操作 | 关键函数 |
|---|---|---|
| ① | 开启 GPIO 时钟和 AFIO 复用时钟 | RCC_APB2PeriphClockCmd(...|RCC_APB2Periph_AFIO,ENABLE) |
| ② | 配置 GPIO 为输入模式(如浮空输入) | GPIO_Init() |
| ③ | 建立 GPIO 引脚与中断线的映射 | GPIO_EXTILineConfig() |
| ④ | 初始化 EXTI(线/模式/触发/使能) | EXTI_Init() |
| ⑤ | 配置 NVIC(分组/抢占/响应/使能) | NVIC_Init() |
| ⑥ | 编写中断服务程序 ISR | EXTIx_IRQHandler() |
while(1) 无限循环负责日常事务。纯轮询架构简单但响应不及时。8标准库 vs HAL 库
| 对比 | 标准外设库 | HAL 库 |
|---|---|---|
| EXTI 配置 | 手动 EXTI_Init()+NVIC_Init() | STM32CubeMX 图形化生成 |
| 源文件 | stm32f10x_exti.c | stm32f1xx_hal_exti.c |
| 中断处理 | 直接在 EXTIx_IRQHandler 写逻辑 | ISR 调用 HAL_GPIO_EXTI_IRQHandler() |
| 用户回调 | 无 | HAL_GPIO_EXTI_Callback()(虚函数,用户重写) |
| 清标志 | EXTI_ClearITPendingBit() | HAL 自动处理 |
HAL 库 EXTI 常用函数:HAL_EXTI_SetConfigLine()(配置中断线)、HAL_EXTI_IRQHandler()(中断处理)、HAL_EXTI_GetPending() / HAL_EXTI_ClearPending()(获取/清除挂起)、HAL_EXTI_GenerateSWI()(软件中断)。
⭐重点例题:按键外部中断翻转 LED
void EXTI_Key_Init(void) {
GPIO_InitTypeDef g; NVIC_InitTypeDef n; EXTI_InitTypeDef e;
// ① 开时钟(GPIOE + AFIO 复用,缺一不可)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO, ENABLE);
// ② PE3 浮空输入
g.GPIO_Pin=GPIO_Pin_3; g.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOE,&g);
// ⑤ NVIC:分组2,抢占0、响应1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
n.NVIC_IRQChannel=EXTI3_IRQn;
n.NVIC_IRQChannelPreemptionPriority=0;
n.NVIC_IRQChannelSubPriority=1;
n.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&n);
// ③ 映射 PE3 → EXTI3
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
// ④ EXTI3 下降沿触发、中断模式、使能
e.EXTI_Line=EXTI_Line3; e.EXTI_Mode=EXTI_Mode_Interrupt;
e.EXTI_Trigger=EXTI_Trigger_Falling; e.EXTI_LineCmd=ENABLE;
EXTI_Init(&e);
}
// ⑥ 中断服务程序(函数名不可改)
void EXTI3_IRQHandler(void) {
if(EXTI_GetITStatus(EXTI_Line3) != RESET) {
// 读 PB5 输出取反,实现翻转
GPIO_WriteBit(GPIOB,GPIO_Pin_5,
(BitAction)(1-GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5)));
EXTI_ClearITPendingBit(EXTI_Line3); // 清标志,否则反复进中断
}
}
关键点:① 必须开 AFIO 时钟;② 中断函数名固定 EXTI3_IRQHandler;③ ISR 末尾必须清中断标志,否则会反复进入。
/* CubeMX 生成 PE3=GPIO_EXTI3 下降沿、使能 NVIC */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if(GPIO_Pin == GPIO_PIN_3)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); // 翻转 LED
}
HAL 库中 EXTI3_IRQHandler → 自动调用 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3) → 回调 HAL_GPIO_EXTI_Callback(),用户只需重写回调函数。
🎯自测(点击展开)
中断处理流程的四个步骤是什么?
抢占优先级和响应优先级有什么区别?
STM32F103 支持几级中断优先级?为什么?
PA0 已用作外部中断,PB0 还能作外部中断吗?
GPIO 用作 EXTI 中断为什么要开 AFIO 时钟?
中断服务程序末尾为什么要清中断标志?
📝强化题库
选择题点选即时判分;填空题输入后"检查"或"显示答案"。