1. 中断系统基础:中断的概念、中断的作用、中断的优先级、中断嵌套

各位同学,咱们今天正式进入中断系统的学习。说实话,中断这个概念,是嵌入式开发里最核心、也最容易让人迷糊的东西之一。我当年刚入行时,就因为没搞懂中断优先级,让一个电机控制项目在高速运行时频繁“抽风”,查了整整三天才找到原因。嗯,从那以后,我对中断就再也不敢马虎了。

1.1 中断到底是什么?

先问大家一个问题:你正在厨房炒菜,突然门铃响了,你会怎么做?

正常人都会——关小火、去开门、处理完门铃的事、再回来继续炒菜。对吧?

单片机里的中断,本质上就是这套逻辑。CPU 正在执行主程序(炒菜),突然某个外设或内部事件触发了信号(门铃响了),CPU 暂停当前工作,保存现场(关小火),跳去执行一个专门的服务程序(开门处理),处理完再回来接着干(继续炒菜)。

这个“暂停-处理-恢复”的机制,就是中断。

中断的核心三要素:

  • 触发源:谁发起的请求?比如定时器溢出、外部引脚电平变化、串口收到数据。
  • 中断服务函数(ISR):CPU 跳过去执行的那段专用代码。
  • 返回机制:执行完 ISR 后,CPU 能准确回到原来的位置继续跑。

你想想看,如果没有中断,CPU 就得不停地轮询检查每个外设的状态——“定时器到了没?按键按了没?数据来了没?”这得多浪费算力。中断让 CPU 从“主动问”变成了“被动等”,效率直接翻倍。

1.2 中断的作用——为什么非用不可?

我个人习惯把中断的作用归纳为四点,咱们用表格来看,更清楚:

作用 说明 实际场景举例
实时响应 对外部事件做出毫秒级甚至微秒级的反应 电机堵转检测、急停按钮触发
并行处理 CPU 可以“同时”处理多个任务(宏观并行) 一边采集传感器,一边输出 PWM
降低功耗 CPU 没事时可以休眠,有中断才唤醒 电池供电设备,按键唤醒系统
简化编程 不用在主循环里写一堆轮询代码 串口接收数据,中断自动处理

我记得有一次做无刷电机驱动,主循环里跑着 PID 运算和通信协议,如果不用中断去捕获霍尔传感器的跳变沿,电机换相时机根本跟不上转速。用了中断后,霍尔信号一来,CPU 立刻响应换相,主循环该干嘛干嘛,互不干扰。

小提示:中断服务函数要短、要快、不能阻塞。我见过有人把延时函数直接写在 ISR 里,结果整个系统卡死。记住:ISR 里只做最必要的事,比如置个标志位、读个寄存器,复杂处理放主循环。

1.3 中断的优先级——谁先谁后?

当多个中断同时发生时,CPU 该先处理谁?这就涉及到优先级了。

每个中断源都有一个优先级编号。数值越小,优先级越高(具体看芯片手册,有些是数值越大越高)。高优先级的中断可以打断低优先级的中断,但反过来不行。

咱们用一张图来理解:

中断优先级与嵌套示意图 主程序(Main Loop) 低优先级中断 ISR(优先级 2) 高优先级中断 ISR(优先级 1) 低优先级中断 ISR(继续执行) 高优先级中断可以打断低优先级中断,处理完后再返回低优先级继续执行

这张图展示的就是中断嵌套的典型场景。主程序跑得好好的,突然来了个低优先级中断,CPU 进去处理。处理到一半,一个更高优先级的中断来了——CPU 二话不说,先挂起当前 ISR,去处理高优先级的,处理完再回来把低优先级的收尾。

为什么会这样设计?说白了,有些事件就是等不得。比如电机过流保护,必须立即响应,晚一毫秒都可能烧 MOSFET。而串口发个数据,晚几毫秒完全没问题。

注意:中断优先级不是越多越好。有些低端单片机只有两级优先级(高/低),高端的有 8 级甚至 16 级。我建议你:能用两级解决的,别用四级。优先级层级越多,嵌套越深,出 bug 的概率越大。我曾经在一个项目里用了 6 级嵌套,结果某个极端条件下栈溢出了,系统直接死机。从那以后,我给自己定了个规矩——嵌套不超过 3 层。

1.4 中断嵌套——深入理解

中断嵌套,说白了就是“中断里套中断”。

要实现嵌套,硬件上需要满足两个条件:

  1. CPU 支持多级优先级——硬件能区分哪个中断更重要。
  2. 中断控制器允许嵌套——在处理低优先级中断时,还能响应更高优先级的中断请求。

咱们用一段伪代码来模拟嵌套过程:

// 主程序
void main() {
    while(1) {
        do_something();  // 正常干活
    }
}

// 低优先级中断(优先级 2)
void ISR_Low(void) {
    // 保存现场(硬件自动做)
    flag_low = 1;           // 标记:低优先级开始
    delay_ms(10);           // 模拟处理时间
    // 就在这 10ms 里,高优先级中断来了!
    flag_low_done = 1;      // 标记:低优先级完成
    // 恢复现场(硬件自动做)
}

// 高优先级中断(优先级 1)
void ISR_High(void) {
    // 保存现场
    flag_high = 1;          // 标记:高优先级开始
    // 紧急处理,比如关闭电机
    motor_stop();
    // 恢复现场
}

实际执行顺序是:

  1. 主程序跑 do_something()
  2. 低优先级中断触发 → CPU 跳进 ISR_Low
  3. 执行到 delay_ms(10) 时,高优先级中断触发
  4. CPU 暂停 ISR_Low,跳进 ISR_High
  5. ISR_High 执行完毕,返回 ISR_Low 继续
  6. ISR_Low 执行完毕,返回主程序

关键点:中断嵌套时,CPU 会自动把当前 ISR 的断点地址和寄存器状态压入栈中。嵌套越深,栈消耗越大。如果你的栈空间分配不够,就会出现“栈溢出”——程序跑飞,查都查不出来。我建议你:每个中断嵌套层级预留至少 64 字节的栈空间。

1.5 实际项目中的优先级分配策略

说了这么多理论,咱们来点实际的。在电机控制项目里,我一般这样分配中断优先级:

优先级 中断源 原因
最高(1) 过流/过温保护 硬件安全,必须立即响应,否则烧器件
高(2) 霍尔传感器跳变 / 编码器 Z 信号 电机换相时机,错过就丢步
中(3) PWM 定时器溢出 / 比较匹配 控制周期定时,允许微秒级延迟
低(4) 串口接收 / ADC 转换完成 数据通信和采样,延迟几毫秒无影响
最低(5) 按键扫描 / LED 刷新 人机交互,几十毫秒延迟用户根本感觉不到

这个分配原则我用了很多年,基本没出过问题。核心思路就一条:对时间敏感、影响安全的,优先级给高;对时间不敏感的,优先级给低。

避坑指南:我曾经在一个项目中,把串口接收中断优先级设得比 PWM 定时器还高。结果串口数据一多,PWM 周期被频繁打断,电机转速出现明显抖动。后来把优先级调回来,问题立刻解决。记住:中断优先级不是越高越好,合理分配才是关键。

1.6 本章小结

中断系统是单片机开发的基石。咱们这一章讲了四个核心概念:

  • 中断的概念——CPU 暂停当前任务,去处理紧急事件,处理完再回来。
  • 中断的作用——实时响应、并行处理、降低功耗、简化编程。
  • 中断的优先级——谁更重要谁先处理,高优先级可以打断低优先级。
  • 中断嵌套——中断里套中断,注意栈空间和嵌套深度。