前面几章,我们搭建好了开发环境,配置好了基础工程,也学会了用GPIO点灯和读取传感器信号。但不知道大家有没有想过一个问题:
如果CPU一直在main函数里不停地循环读取某个引脚的状态,那它还有时间干别的吗?
GPIO输入那篇里,我们读取声音传感器的信号用的是轮询方式------CPU在循环里一遍又一遍地问"引脚变高了没?变低了没?"这种方式在只检测一个信号的时候还能应付,但如果系统里有多个信号需要同时检测、多个任务需要同时处理,轮询就力不从心了。
这时候就需要中断 登场了。这篇咱们就来聊聊:中断是什么、它怎么工作、以及我们怎么用按键中断来替代轮询检测。中断也是我们接触到的第一个比较重要而且有点难理解的功能,这里尽量讲得详细一点。

一、什么是中断?一个生活化的比喻
先别急着看寄存器,咱们用一个生活中最常见的场景来理解中断。
假设你正在书房里专心写代码(这是你当前执行的主程序 )。这时候------叮咚 ------门铃响了(这是一个中断事件 )。你停下手中的工作(打断当前任务 ),起身去开门(执行中断服务程序 )。发现是快递员,签收完包裹之后,你回到书房,继续接着刚才的代码往下写(恢复主程序执行)。

整个过程有几个关键点:
-
中断源
:门铃(触发中断的外部事件)
-
中断响应
:你停下当前工作去开门(CPU暂停主程序,跳转执行中断服务函数)
-
中断服务函数
:开门、签收这个具体动作(处理中断事件的代码)
-
恢复执行
:处理完后回到书房继续写代码(CPU回到被打断的位置继续执行)
中断的本质就是这样------让CPU能够在处理主要任务的同时,及时响应突发的、更紧急的事件。
如果用GPIO轮询的方式来处理门铃,那就相当于:你写代码的时候每隔一秒钟就抬头看一眼门口有没有人------效率极其低下,而且如果正好在两次抬头的间隙有人敲门,你就错过了。
二、STM32的中断系统长什么样?
STM32F103内部有一套完整的中断管理系统,主要由两个核心部件组成:
2.1 NVIC------中断的"调度中心"
NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)是Cortex-M3内核内置的中断管理单元。它的职责是:
-
管理所有中断
:STM32F103一共有60个可屏蔽中断,加上16个内核中断(异常),总共76个中断源
-
决定中断优先级
:当多个中断同时发生时,谁先处理、谁能打断谁
-
嵌套处理
:高优先级的中断可以打断正在执行的低优先级中断
简单理解:NVIC就是CPU门口的保安------所有中断请求都要经过它,它根据优先级决定让谁先进去。
2.2 中断向量表------中断的"通讯录"
中断向量表是一个存放在Flash起始地址的表,里面按顺序记录了每个中断对应的服务函数的入口地址。
当某个中断发生时,CPU会去这个表里查找:这个中断对应的函数在哪个地址?然后跳转过去执行。
这个表在STM32的启动文件 (如startup_stm32f103xb.s)里已经预先定义好了,比如外部中断0对应的服务函数叫EXTI0_IRQHandler,外部中断5~9共用EXTI9_5_IRQHandler,外部中断10~15共用EXTI15_10_IRQHandler。我们只需要在代码里实现这些函数就可以了,不需要在main函数里调用它们------中断发生后CPU会自动跳转过去。
三、外部中断(EXTI)------GPIO引脚的中断能力
如果上面这些简单的介绍还是不能完全理解,没关系,可以先跟着做,动手操作之后再思考,有时候更容易理解。这里我们用最常用的一种中断类型来介绍如何使用中断------通过GPIO的外部中断来控制LED。
STM32F103的每一个GPIO引脚都可以配置为外部中断输入 ,EXTI(External Interrupt/Event Controller)支持19个外部中断/事件线。这19条线分别是:
-
线0~15
:对应外部IO口的输入中断
-
线16
:连接到PVD(可编程电压监测器)输出
-
线17
:连接到RTC闹钟事件
-
线18
:连接到USB唤醒事件
EXTI的核心功能是:
-
边沿检测
:可以检测输入信号的上升沿 、下降沿 或者双边沿触发
-
独立配置
:每个中断线可以单独配置触发方式和使能/屏蔽
这意味着:当按键被按下(电平从低变高,产生上升沿)时,EXTI会检测到这个变化,向NVIC发出中断请求,CPU就会暂停当前任务去执行对应的中断服务函数。
四、按键中断------我们这篇要做的实验
回到我们室内环境检测系统的项目进度。前一篇GPIO篇里,我们实现了两个功能:
-
PC13输出
:控制LED以1秒间隔闪烁,表示系统正常运行
-
PB12输入
:读取声音传感器的数字输出
这一篇我们要增加第三个功能------按键中断:
-
硬件连接
:将一个按键模块连接到单片机的PB3引脚,然后就是经典组合------仿真器和单片机

-
功能需求
:按下按键时,触发中断,在中断服务函数中翻转LED的状态(亮→灭,或灭→亮)
为什么用中断而不用轮询?
如果用轮询方式检测按键,CPU需要在主循环里不断地读取引脚电平------这有两个问题:
-
CPU被占用
:一直在做"读引脚"这件事,没法专心干别的
-
可能漏检
:如果按键按下刚好发生在两次读取之间,程序就"看不见"这次按键
而中断方式则是事件驱动 的------按键按下才触发处理,平时CPU可以安心执行主程序,真正做到了有事件才响应,没事件不打扰。
消抖问题------中断的"坑"
不过中断方式也有一个需要注意的地方:按键的机械抖动。
机械按键在按下和释放的瞬间,触点会快速弹跳几次,产生一连串的电平变化。如果用中断直接检测边沿,按一次键可能会触发多次中断------明明只想翻转一次LED,结果可能翻转了好几次。
解决方式通常有:
-
软件延时消抖
:在中断服务函数中延时10~20ms再读取电平确认
-
定时器消抖
:用定时器中断来做延时检测(更精确,不阻塞)
-
状态机消抖
:用状态机管理按键状态,滤除抖动
本篇我们利用AI,直接使用HAL_GetTick()配合时间戳来实现100ms软件消抖。
五、配置步骤一览
结合我们之前几篇的配置流程,按键中断的配置大致需要这几步:
-
CubeMX引脚配置
:找到按键对应的引脚(PB3),在Pinout视图中选择
GPIO_EXTI3模式
-
触发方式配置
:在GPIO配置中,选择中断触发方式------上升沿触发(按键按下时电平从低变高)

-
NVIC中断使能
:在NVIC选项卡中,勾选对应的EXTI中断线,使能中断

-
生成代码
:点击GENERATE CODE,CubeMX会自动生成中断初始化代码
-
编写按键功能
:让AI编写按键功能并生成按键功能文件,实现按键反转IO的功能

六、小结
这一篇我们从"为什么需要中断"出发,梳理了中断的基本概念、STM32的中断体系架构(NVIC和中断向量表),以及外部中断EXTI的工作原理。
核心要记住的是:
-
中断 = 让CPU从"主动轮询"变成"被动响应"
,效率更高、响应更及时
-
NVIC是中断的调度中心
,负责管理优先级和嵌套
-
EXTI让每个GPIO引脚都能产生中断
,是GPIO输入的高级形态
-
按键中断要注意消抖
,否则一次按下可能触发多次
刚开始接触中断,基本都是从外部中断开始的,这导致很长一段时间里我都混淆了"外部中断"和"中断"的关系。实际上,外部中断只是中断功能中的一种类型,还有很多其他中断类型,比如串口接收中断、DMA发送完成中断、定时器溢出中断等等。感兴趣的朋友可以利用AI去深入了解一下。