1. 中断系统基础:中断的概念、中断的作用、中断优先级、中断嵌套
各位同学,咱们今天正式进入中断系统的学习。说实话,中断是嵌入式开发里最核心、也最容易出坑的概念之一。我当年刚入行时,就因为没搞懂中断优先级,把一个电机控制项目搞出了飞车事故——嗯,从那以后我再也不敢轻视中断了。
1.1 中断到底是什么?
先打个比方。你正在写代码,突然有人敲门——你放下键盘去开门,处理完事情回来继续写。这个“敲门”就是中断。CPU 正在执行主程序,突然收到一个信号,暂停当前任务,跳去处理紧急事件,处理完再回来接着干。
中断的本质,就是让 CPU 能够响应异步事件。什么叫异步?就是事件发生的时间不可预测。比如按键按下、串口收到数据、定时器溢出——你没法提前知道它们什么时候来。
中断的三要素:
- 中断源:谁发出的中断信号?比如外部引脚、定时器、串口模块。
- 中断向量:中断发生后,CPU 跳转到哪个地址执行代码?每个中断源对应一个固定的入口地址。
- 中断服务函数(ISR):处理中断事件的代码块。执行完必须返回主程序。
我习惯把中断服务函数叫做“紧急处理程序”。它要短、要快、不能阻塞。你想想看,如果有人在敲门,你开门后开始做饭、洗衣服、看电视——那敲门的人不得急死?
1.2 中断的作用——为什么非用不可?
没有中断的世界是什么样的?CPU 只能轮询。轮询就是不停地问:“按键按了吗?”“数据到了吗?”“定时到了吗?”——大部分时间都在空转,浪费算力,还容易丢事件。
中断解决了三个核心问题:
| 问题 | 轮询方式 | 中断方式 |
|---|---|---|
| CPU 利用率 | 低,大量时间在空转查询 | 高,有事件才响应 |
| 实时性 | 差,响应延迟取决于查询周期 | 好,事件发生立即响应 |
| 多任务处理 | 困难,需要手动调度 | 自然,硬件自动切换 |
| 代码复杂度 | 简单但低效 | 稍复杂但高效 |
我在做工业传感器项目时遇到过一个问题:用轮询方式读取编码器脉冲,结果 CPU 被占得死死的,连显示刷新都卡顿了。换成中断后,CPU 利用率从 85% 降到了 15%——这就是中断的价值。
1.3 中断优先级——谁更重要?
多个中断同时来了怎么办?CPU 得有个判断标准:先处理谁?这就是优先级的作用。
优先级分两种:
- 抢占优先级:高优先级可以打断低优先级的中断服务。比如定时器中断正在执行,外部紧急中断来了,CPU 会暂停定时器 ISR,先去处理紧急中断。
- 响应优先级:当两个中断同时到达时,响应优先级高的先执行。但响应优先级不能打断正在执行的中断。
我的经验:优先级分配有个基本原则——时间敏感的中断优先级高,数据量大的中断优先级低。比如电机过流保护中断必须最高优先级,串口接收中断可以中等,按键中断可以最低。我曾经把按键优先级设得比定时器还高,结果按键按下时定时器丢了一拍,电机转速直接跳变——教训深刻。
以 STM32 为例,NVIC(嵌套向量中断控制器)支持最多 16 级抢占优先级。配置时要注意:
// STM32 中断优先级配置示例
NVIC_InitTypeDef NVIC_InitStructure;
// 配置 USART1 中断:抢占优先级 1,响应优先级 2
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 配置 TIM2 中断:抢占优先级 2,响应优先级 0
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
这里 USART1 的抢占优先级是 1,TIM2 是 2。所以 USART1 中断可以打断 TIM2 中断。如果两个中断同时到达,USART1 先执行。
1.4 中断嵌套——中断里还有中断
中断嵌套就是:一个中断正在执行,另一个更高优先级的中断来了,CPU 再次暂停,去处理新的中断。处理完再回来继续原来的中断,最后返回主程序。
这个过程有点像俄罗斯套娃。但要注意:
中断嵌套的陷阱:
- 栈溢出:每嵌套一层,栈空间就多占一份。嵌套太深,栈可能爆掉。
- 优先级反转:低优先级中断占用了高优先级中断需要的资源,导致高优先级中断被阻塞。
- 中断延迟:嵌套层数越多,最外层中断的响应延迟越大。
我曾经在一个项目中,把中断嵌套搞到了 4 层。结果系统偶尔死机,查了三天才发现是栈溢出。从那以后,我给自己定了个规矩:中断嵌套不超过 2 层,ISR 里尽量只设标志位,具体处理放到主循环。
下面这张图展示了中断嵌套的完整流程:
1.5 中断系统的实际应用要点
说了这么多理论,咱们看看实际开发中要注意什么:
- ISR 要短小精悍:不要在中断里做复杂运算、延时、打印。设个标志位就返回,具体处理交给主循环。
- 临界区保护:如果主程序和中断都访问同一个变量,要用关中断或信号量保护。否则会出现“读了一半被中断修改”的问题。
- 中断频率要可控:中断太频繁,CPU 大部分时间都在进中断出中断,主程序反而没时间跑。我见过有人把定时器中断设成 1μs 一次,结果 CPU 利用率直接 100%。
- 优先级分配要合理:紧急事件高优先级,批量数据低优先级。别把按键和电机保护设成同一优先级。
避坑指南:我曾经在一个项目中,把全局变量 flag 在主程序和中断里同时读写,没做保护。结果程序运行几个小时后就随机崩溃。查了两天才发现是中断修改了 flag 的中间值。从那以后,所有中断和主程序共享的变量,我都加上了 volatile 关键字和临界区保护。
1.6 本章小结
中断系统是单片机的“紧急响应机制”。它让 CPU 能够高效处理异步事件,而不是傻傻地轮询。理解中断的概念、优先级和嵌套机制,是写好嵌入式程序的基础。
记住三个核心点:
- 中断是异步事件驱动的,不是轮询
- 优先级决定了中断的响应顺序和嵌套能力
- ISR 要短、要快、不能阻塞