🎓 总站 🏠 本课目录 05 GPIO 06 中断 07 串口通信
智能嵌入式系统设计 · 第6章

中断与外部中断 EXTI

基于 STM32F103 · 零基础讲解中断概念、NVIC 嵌套向量控制器、抢占/响应优先级、EXTI 外部中断与按键中断点灯。

📚 学习进度
0%

🎯学习目标

  • 理解和掌握中断的概念和中断处理过程;
  • 掌握 STM32F103 的中断类型、优先级概念和中断向量表;
  • 了解 NVIC 嵌套向量中断控制器的结构和特点;
  • 掌握 EXTI 的内部结构、工作原理和特性;
  • 熟悉 NVIC 和 EXTI 的标准外设库与 HAL 库函数;
  • 掌握 STM32 中断配置过程及外部中断开发方法。

1中断的相关概念

中断:计算机在执行程序过程中,当出现异常情况(如断电)或特殊请求(如数据传输)时,CPU 暂停现行程序的运行,转向处理这些异常或请求,处理完毕后再返回到原程序的中断处继续执行。

💡 一句话理解就像你正在看书(主程序),快递电话响了(中断源)→ 暂停看书去取快递(中断服务程序)→ 取完回来接着看书(中断返回)。

中断是单片机实时处理内部或外部事件的一种机制。中断和异常本质都是对主程序的"中断"。

为什么使用中断?

CPU 速度快、外设速度慢。若用"等待/查询"方式会降低 CPU 效率;用中断方式则:

提高效率

CPU 无需轮询等待,外设就绪时主动通知。

⏱️

实时处理

对实时事件及时响应。

🛡️

异常处理

对突发故障及时处理。

⭐ 三个关键名词中断源:发出中断请求的事件(如按键、定时器到时);中断服务程序(ISR):处理中断的程序;主程序:被中断的正常程序。

2中断处理流程

中断处理流程分为四步:中断请求 → 中断响应 → 中断服务 → 中断返回

① 中断请求中断源置位请求 ② 中断响应判断条件→跳转 ③ 中断服务执行 ISR ④ 中断返回回到中断处
图1 · 中断处理四步流程
步骤说明由谁完成
中断请求中断源向 CPU 发请求信号,中断请求寄存器被置位硬件
中断响应CPU 判断是否符合响应条件,符合则暂停当前程序跳转到 ISR硬件
中断服务执行为处理中断而编写的程序(ISR)开发人员编写
中断返回退出 ISR,返回被中止的位置继续执行主程序硬件
💡 单重 vs 多重单重中断:ISR 执行期间不响应新中断;多重(嵌套)中断:高优先级中断可打断正在执行的低优先级 ISR。

3STM32 中断与异常向量表

中断数量

  • Cortex-M3 内核最多支持 256 个中断:15 个内核中断 + 240 个外部中断,具有 256 级可编程优先级;
  • STM32F10x 系列有 84 个中断通道:16 个内核中断 + 68 个可屏蔽中断;
  • STM32F103 只有 60 个可屏蔽中断,STM32F107 有 68 个可屏蔽中断。

中断向量表

当发生异常或中断,内核需要知道中断服务程序的入口地址,由这些入口地址组成的表称为中断向量表。入口地址存放在程序存储器(ROM),默认从零地址开始,每个向量占 4 个字节

向量编号入口地址说明
0x00000000MSP 的初始值
10x00000004复位向量(PC 初始值)
20x00000008NMI 异常服务程序入口
30x0000000C硬 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 级中断优先级管理。

⭐ 两种优先级(必考)抢占优先级(Preemption):决定能否中断嵌套——高抢占可打断低抢占的 ISR;响应优先级(Sub/子优先级):抢占相同时谁先执行,但不能互相打断。

优先级分组(5 组)

NVIC_PriorityGroupConfig() 设置,将 4 位在抢占和响应之间分配:

分组抢占优先级响应优先级位分配
Group_000~15抢占 0 位 / 响应 4 位
Group_10~10~7抢占 1 位 / 响应 3 位
Group_20~30~3抢占 2 位 / 响应 2 位
Group_30~70~1抢占 3 位 / 响应 1 位
Group_40~150抢占 4 位 / 响应 0 位
优先级寄存器高 4 位(Group_2 为例) bit7抢占 bit6抢占 bit5响应 bit4响应 紫=抢占优先级(2位→0~3) 绿=响应优先级(2位→0~3)
图2 · NVIC 优先级分组:4 位在抢占与响应间划分

NVIC 初始化结构体

typedef struct {
  uint8_t NVIC_IRQChannel;                   // 中断源(IRQ通道)
  uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级
  uint8_t NVIC_IRQChannelSubPriority;        // 响应优先级
  FunctionalState NVIC_IRQChannelCmd;        // 使能中断通道
} NVIC_InitTypeDef;
⚠️ 优先级判断五原则① 数值越小,优先级越高;② 先比抢占优先级;③ 抢占相同则比响应优先级;④ 两者都相同则按向量表顺序;⑤ Reset/NMI/HardFault 优先级为负且不可改,永远最高。

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
通过外部信号边沿产生中断信号传给 NVIC,触发中断,最终执行中断服务程序。软件实现功能
📡

事件模式

Event
产生脉冲将系统从睡眠/停止模式唤醒,产生触发信号供外设电路使用。硬件触发执行

GPIO 与中断线映射(重要)

⭐ 以组为单位(必考)GPIO 的中断以组为单位,同组(末端序号相同)的引脚共用一条外部中断线。例如 PA0、PB0、PC0…PG0 共用 EXTI0,若用了 PA0,PB0 就不能再作外部中断,只能选末端序号不同的(如 PB1、PC2)。
PA0 PB0 …PG0 EXTI0(共用) NVIC ISR
图3 · 同末端序号引脚共用一条 EXTI 线 → NVIC → 中断服务程序

EXTI 中断服务函数对应关系

中断线中断服务函数
EXTI0~EXTI4各自独立,如 EXTI0_IRQHandlerEXTI4_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()
编写中断服务程序 ISREXTIx_IRQHandler()
⚠️ 易错点GPIO 用作 EXTI 外部中断必须开启 AFIO 复用时钟,否则映射不生效!
💡 程序架构嵌入式程序常用"前后台系统"架构:前台用中断处理实时事件,后台while(1) 无限循环负责日常事务。纯轮询架构简单但响应不及时。

8标准库 vs HAL 库

对比标准外设库HAL 库
EXTI 配置手动 EXTI_Init()+NVIC_Init()STM32CubeMX 图形化生成
源文件stm32f10x_exti.cstm32f1xx_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

例题:按键 K1(PE3) 触发外部中断,每按一次翻转 PB5 上的 LED 思路:① 开 GPIOE+AFIO 时钟 → ② PE3 浮空输入 → ③ 映射 PE3→EXTI3 → ④ EXTI3 下降沿触发 → ⑤ 配 NVIC → ⑥ ISR 中翻转 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 末尾必须清中断标志,否则会反复进入。
HAL 库等价写法(回调方式)
/* 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(),用户只需重写回调函数。

🎯自测(点击展开)

中断处理流程的四个步骤是什么?
中断请求 → 中断响应 → 中断服务 → 中断返回。其中只有"中断服务(ISR)"需要开发人员编写。
抢占优先级和响应优先级有什么区别?
抢占优先级决定能否中断嵌套(高抢占可打断低抢占的 ISR);响应优先级在抢占相同时决定先后,但不能互相打断。数值越小优先级越高。
STM32F103 支持几级中断优先级?为什么?
16 级。因为 STM32 只用 Cortex-M3 优先级寄存器的高 4 位,2^4=16。
PA0 已用作外部中断,PB0 还能作外部中断吗?
不能。同末端序号的引脚(PA0~PG0)共用 EXTI0 中断线,只能选末端序号不同的(如 PB1)。
GPIO 用作 EXTI 中断为什么要开 AFIO 时钟?
外部中断映射通过复用功能 I/O 控制器(AFIO)实现,不开 AFIO 时钟映射不生效。
中断服务程序末尾为什么要清中断标志?
若不清除挂起标志(Pending bit),中断退出后标志仍置位,会立即再次进入中断,造成死循环。

📝强化题库

选择题点选即时判分;填空题输入后"检查"或"显示答案"。

已答 0/0答对 0正确率
已答 0/0答对 0