一、中断基础概念
各位同学好,我是老张。做嵌入式这行快二十年了,今天咱们聊聊中断。说实话,中断这玩意儿,是嵌入式系统的灵魂。没有中断,你的CPU就得像个傻子一样轮询——那效率,想想都头疼。
1.1 什么是中断
中断,说白了就是“CPU正在干活,突然被叫去处理更重要的事”。
我习惯这么跟新人解释:你正在写代码,突然电话响了。你放下键盘接电话,接完再回来继续写。这个电话铃声,就是中断信号。你接电话的动作,就是中断服务程序。
中断的核心价值在于——让CPU从“主动查询”变成“被动响应”。你想想看,如果没有中断,CPU得不停地问“有按键按下吗?”“有数据到了吗?”——这不累死?
中断的本质:一种硬件机制,允许外部设备或内部事件“打断”CPU当前执行的主程序,转而去执行一个特定的处理函数。
我在项目中遇到过最典型的例子:一个串口通信模块,如果不使用中断,CPU每毫秒都要去检查接收寄存器。用了中断后,数据到了才通知CPU。同样的CPU,处理能力提升了至少5倍。
1.2 中断向量表
中断向量表,嗯,这个名字听起来挺唬人。其实它就是一张“地址映射表”。
每个中断源都有一个编号,比如外部中断0、定时器1溢出中断等等。CPU收到中断信号后,根据这个编号去查表,找到对应的中断服务程序入口地址,然后跳过去执行。
我画了个图,帮你理解这个过程:
这张图你看懂了吗?CPU收到中断请求后,先去查向量表,找到对应的ISR地址,跳过去执行,执行完再回来。
我的经验:写启动代码时,中断向量表一定要放在正确的位置。ARM Cortex-M系列要求向量表必须从0地址开始,或者通过VTOR寄存器重映射。我曾经见过一个同事,把向量表放错了位置,结果一进中断就跑飞了——查了两天才找到原因。
1.3 中断优先级
多个中断同时来了怎么办?谁先处理?这就涉及到优先级了。
优先级,就是给每个中断源排个队。数字越小优先级越高(有些芯片反过来,看手册)。
我习惯把优先级分成几个等级:
| 优先级等级 | 典型应用 | 说明 |
|---|---|---|
| 最高(0-1) | 系统时钟、看门狗 | 系统稳定性相关,不能丢 |
| 高(2-3) | 外部紧急事件 | 比如急停按钮、过流保护 |
| 中(4-5) | 通信接口 | UART、SPI、I2C等 |
| 低(6-7) | 普通外设 | 按键扫描、LED刷新 |
为什么会这样分?你想想看,系统时钟丢了,整个系统就乱了。按键扫描慢个几十毫秒,用户根本感觉不到。这就是优先级设计的核心思想——把紧急的和不紧急的分开。
注意:优先级不是越高越好。我曾经把一个GPIO中断设成了最高优先级,结果它频繁触发,导致系统时钟中断被阻塞——整个系统时间都乱了。优先级设置要权衡,别让“小事”耽误了“大事”。
1.4 中断嵌套
中断嵌套,说白了就是“中断里还能再进中断”。
处理高优先级中断时,来了个更高优先级的中断——CPU会暂停当前的中断服务程序,先去处理更紧急的那个。处理完再回来继续。
这就像你正在接一个重要电话,突然另一个更重要的电话打进来了——你会说“不好意思,我先接个更急的电话”,然后切换过去。
中断嵌套的规则其实很简单:
- 高优先级可以打断低优先级——这是允许的
- 低优先级不能打断高优先级——这是被禁止的
- 同级中断不能互相打断——得排队
我举个例子:
// 伪代码:中断嵌套场景
主程序正在执行
↓
外部中断0(优先级2)触发 → 进入ISR_External0
↓
定时器1(优先级1)触发 → 暂停ISR_External0
↓
进入ISR_Timer1(优先级更高,先处理)
↓
ISR_Timer1执行完毕 → 返回ISR_External0继续
↓
ISR_External0执行完毕 → 返回主程序
关键点:中断嵌套虽然灵活,但嵌套层数越多,出问题的概率越大。我个人的经验是——嵌套不要超过3层。超过3层,栈空间容易爆,调试起来也头疼。
嗯,这里要注意一点:有些单片机支持“中断优先级分组”,可以把优先级分成“抢占优先级”和“子优先级”。抢占优先级决定能否打断,子优先级决定同抢占级时的处理顺序。这个细节,不同芯片实现不一样,用的时候一定要看芯片手册。
避坑指南:我曾经在一个项目中,把中断嵌套设了5层。结果系统跑着跑着就死机了。查了半天,发现是栈溢出了。从那以后,我给自己定了个规矩——中断服务程序里尽量少做事,把耗时操作放到主循环或者任务队列里。中断里只做“标记”,不干“重活”。
好了,中断的基础概念就聊到这儿。这些是后面所有内容的地基,一定要理解透。下一节咱们聊聊中断的响应过程,到时候我会结合具体的芯片架构来讲。