1. 中断系统概述

中断这玩意儿,说白了就是CPU干活干到一半,突然被一个紧急事件叫停,转去处理那个事件,处理完再回来接着干。我刚开始学单片机时,总觉得中断很神秘,后来才发现——它就是嵌入式系统的灵魂。

核心理解:中断是CPU对外部或内部紧急事件的实时响应机制。没有中断,单片机就只能轮询,效率低得可怜。

1.1 中断的概念

中断的本质是什么?我习惯用一个比喻来解释:你正在写代码,突然电话响了。你停下手中的活,接电话,处理完,挂断,继续写代码。这个电话就是中断源,接电话的过程就是中断响应。

在单片机里,中断源可以是外部引脚电平变化、定时器溢出、串口收到数据、ADC转换完成等等。每个中断源都有一个唯一的中断号,CPU通过中断向量表找到对应的处理函数。

我的经验:中断服务函数(ISR)一定要短小精悍。我在项目中见过有人把整个业务逻辑都塞进ISR里,结果系统卡死。记住:ISR只做最紧急的事,比如置个标志位、读个数据,剩下的交给主循环。

1.2 中断的作用

中断到底解决了什么问题?你想想看,如果没有中断,CPU就得不停地去查某个事件有没有发生——这叫轮询。轮询的坏处很明显:CPU大部分时间都在空转,浪费功耗,而且响应不及时。

中断带来的好处,我总结为三点:

  • 实时响应:事件一来,CPU立刻响应,延迟可控。比如按键按下,中断方式能在微秒级响应,轮询可能得等几十毫秒。
  • 资源解放:CPU不用一直盯着某个外设,可以去做其他事。说白了就是提高了CPU利用率。
  • 低功耗:这是我最看重的。没有中断时,CPU可以进入睡眠模式,等中断唤醒。我在做电池供电产品时,全靠中断来省电。

注意:中断不是万能的。中断过多会导致CPU频繁进出ISR,上下文切换开销反而拖慢系统。我曾经在一个项目中开了8个中断,结果系统响应比轮询还慢——这就是中断风暴。

1.3 中断的优先级与嵌套

多个中断同时来了怎么办?谁先处理?这就涉及到优先级。每个中断源都有一个优先级,数值越小优先级越高(具体看芯片手册)。

中断嵌套,就是高优先级中断可以打断低优先级中断的处理。举个例子:

// 伪代码示意中断嵌套流程
1. CPU正在执行主循环
2. 定时器中断发生(优先级2)→ CPU进入定时器ISR
3. 定时器ISR执行到一半,外部中断发生(优先级1)
4. CPU暂停定时器ISR,进入外部中断ISR
5. 外部中断ISR执行完毕,返回定时器ISR
6. 定时器ISR执行完毕,返回主循环

嗯,这里要注意:不是所有单片机都支持硬件中断嵌套。有些低端MCU只有两级优先级,不支持嵌套。我在项目中吃过这个亏——以为能嵌套,结果高优先级中断被低优先级阻塞了。

特性 轮询方式 中断方式
CPU利用率 低(大量空转) 高(有事才处理)
响应延迟 不可控(取决于轮询周期) 可控(微秒级)
功耗 高(CPU持续运行) 低(可进入睡眠)
编程复杂度 简单 中等(需处理共享数据保护)
适用场景 低速、非实时任务 高速、实时性要求高的任务

避坑指南:我曾经在一个项目中,把两个中断的优先级设成了相同值。结果它们同时触发时,CPU不知道先处理谁,导致系统随机死机。后来我养成了习惯:每个中断优先级必须明确分配,绝不重复。

1.4 中断系统知识结构

我把中断系统的核心逻辑画成了一张图,方便你理解整体脉络:

中断系统知识结构图 中断系统 中断概念 中断作用 优先级与嵌套 中断源 中断向量表 实时响应 资源解放 优先级分配 嵌套机制 关键要点 • 中断是CPU响应紧急事件的机制,比轮询更高效 • 中断优先级决定多个中断同时发生时的处理顺序 • 中断嵌套允许高优先级中断打断低优先级中断处理

1.5 中断优先级分配实战建议

优先级怎么分配?我有一套自己的原则:

  1. 时间敏感的中断优先级最高——比如电机控制中的PWM中断,错过一个周期电机就抖了。
  2. 数据量小的中断优先级次之——比如外部按键中断,处理快,不会阻塞太久。
  3. 数据量大但可以缓冲的中断优先级最低——比如串口接收,有FIFO撑着,晚一点处理没关系。

注意:千万别把所有中断都设成最高优先级。我见过有人图省事,把所有中断优先级都设成0(最高),结果低优先级中断永远抢不过高优先级,系统直接崩溃。优先级是资源,要合理分配。

1.6 中断嵌套的陷阱

中断嵌套听起来很美好,但实际用起来坑不少。我踩过最深的坑是——嵌套导致栈溢出。单片机的中断栈通常只有几十到几百字节,嵌套层数一多,栈就爆了。

举个例子:

// 中断嵌套导致栈溢出示例
void TIM2_IRQHandler(void) {  // 优先级2
    // 处理定时器中断
    // 执行过程中,外部中断来了
    // CPU保存现场,进入EXTI_IRQHandler
    // 如果EXTI_IRQHandler又嵌套了其他中断...
    // 栈空间被层层消耗,最终溢出
}

void EXTI_IRQHandler(void) {  // 优先级1
    // 处理外部中断
}

我的建议:除非你非常清楚芯片的栈大小和中断嵌套深度,否则尽量少用嵌套。我现在的习惯是:中断优先级只分两级——紧急和非紧急。紧急的不能被打断,非紧急的可以被紧急的打断。这样嵌套深度最多2层,安全得多。