1、中断基础概念:什么是中断、中断的作用、中断与轮询的对比

1.1 到底什么是中断?

先问大家一个问题:你正在写代码,突然有人拍你肩膀,你会怎么做?

正常人会先停下手里的活,转头看看是谁,处理完这件事,再回头继续写代码。对吧?

中断,说白了就是这个道理。

中断是CPU处理异步事件的一种硬件机制。当外设或内部模块需要CPU关注时,它会发送一个电信号——我们叫它“中断请求”。CPU收到这个信号后,会暂停当前正在执行的程序,保存好现场(就是各种寄存器的值),然后跳到一个专门的处理函数里去干活。干完了,再回来接着刚才断掉的地方继续执行。

我刚开始做嵌入式开发时,总觉得中断很神秘。后来自己写了一个按键中断的程序,按下按键,LED灯亮,松开就灭。那一刻我才真正理解——中断就是CPU的“临时插队”机制

核心要点:

  • 中断是硬件触发的,不是软件轮询的
  • CPU会暂停当前任务,去处理紧急事件
  • 处理完后,CPU会回到原来的任务继续执行

1.2 中断到底有什么用?

你想想看,如果没有中断,CPU怎么知道外设发生了什么事?

只能不停地去问:“嘿,按键你按了吗?”“嘿,串口你收到数据了吗?”“嘿,定时器你到时间了吗?”

这就是轮询。CPU像个焦虑症患者,每隔几微秒就去检查一次。大部分时候,外设根本没动静,CPU白白浪费了时间。

中断的作用,就是让外设主动通知CPU。

我做过一个项目,需要同时处理串口接收、按键检测、定时器溢出三个任务。如果用轮询,主循环里全是if判断,代码又臭又长,还容易漏掉事件。改用中断后,每个外设都有自己的中断服务函数,代码清晰多了,响应也快。

总结一下中断的几个核心作用:

  • 提高CPU利用率:不用傻等,该干嘛干嘛
  • 实时响应:紧急事件来了,立刻处理
  • 支持多任务:多个外设可以“同时”工作
  • 降低功耗:CPU可以休眠,有事件再唤醒

我的经验:在低功耗项目中,中断几乎是必须的。让CPU大部分时间处于睡眠模式,只有中断来了才醒来干活。这样电池能用很久。

1.3 中断 vs 轮询:到底选哪个?

很多新手会纠结这个问题。我直接说结论:没有绝对的好坏,看场景

咱们来做个对比:

对比项 中断 轮询
CPU利用率 高,有事才处理 低,一直在检查
响应速度 快,事件触发立即响应 慢,取决于轮询周期
编程复杂度 较高,需处理临界区、嵌套等 简单,就是循环加判断
适用场景 事件不频繁、要求实时性 事件频繁、对实时性要求不高
功耗 低,可休眠 高,CPU一直跑

举个例子:

  • 按键检测:用中断好。人按一下,几百毫秒才松开,CPU没必要一直盯着。
  • ADC连续采样:用轮询好。每秒采样几千次,中断进进出出反而浪费开销。
  • 串口接收:看情况。数据来得慢用中断,数据流很大用DMA+中断。

避坑指南:我曾经在一个项目中,把所有的外设都开了中断。结果中断嵌套太多,CPU大部分时间都在处理中断,主循环反而跑不动了。后来我把一些不紧急的事件改成了轮询,系统才稳定下来。

记住:中断不是越多越好。每个中断都有开销——保存现场、跳转、恢复现场。中断太频繁,系统性能反而会下降。

1.4 中断的基本流程

搞懂了概念,咱们看看中断到底是怎么跑的。我习惯把它分成五步:

  1. 中断请求:外设拉高中断引脚,或者写中断标志位
  2. 中断响应:CPU检测到中断信号,判断优先级,决定是否响应
  3. 保护现场:CPU自动把PC、状态寄存器等压栈
  4. 中断处理:跳转到中断向量表,执行对应的ISR(中断服务函数)
  5. 恢复现场:执行完ISR,出栈恢复寄存器,继续执行原程序

这里有个细节:中断向量表。每个中断源都有一个固定的入口地址。CPU根据中断号,去向量表里找到对应的ISR地址,然后跳过去。

以ARM Cortex-M系列为例,中断向量表长这样:

; 中断向量表示例(简化版)
__Vectors       DCD     __initial_sp          ; 栈顶指针
                DCD     Reset_Handler         ; 复位中断
                DCD     NMI_Handler           ; 不可屏蔽中断
                DCD     HardFault_Handler     ; 硬错误
                DCD     MemManage_Handler     ; 内存管理
                DCD     BusFault_Handler      ; 总线错误
                DCD     UsageFault_Handler    ; 用法错误
                ...
                DCD     TIM2_IRQHandler       ; 定时器2中断
                DCD     USART1_IRQHandler     ; 串口1中断

小技巧:写ISR时,一定要快进快出。我一般只在ISR里设置标志位,真正的处理逻辑放在主循环里。这样能减少中断占用时间,避免影响其他中断的响应。

1.5 中断的优先级与嵌套

多个中断同时来了怎么办?谁先处理?

这就涉及到中断优先级了。每个中断源都有一个优先级数值。数值越小,优先级越高(具体看芯片手册,有的芯片是反的)。

高优先级的中断可以打断低优先级的中断,这叫中断嵌套

举个例子:

  • 系统正在处理串口中断(优先级3)
  • 突然来了一个定时器中断(优先级1)
  • CPU会暂停串口中断,先去处理定时器中断
  • 定时器中断处理完,再回来继续处理串口中断

听起来很合理对吧?但这里有个坑:中断嵌套太深,栈空间会爆炸

我曾经踩过的坑:在一个项目中,我设置了5级中断嵌套。结果某个极端情况下,中断一层套一层,栈空间直接溢出,系统跑飞了。后来我把中断嵌套限制在3级以内,并且给每个任务分配了独立的栈空间,问题才解决。

建议:中断嵌套不要超过3层。能用轮询解决的,就别用中断。

1.6 小结

中断是嵌入式系统的核心机制。它让CPU从“傻等”变成了“随叫随到”。

记住几个关键点:

  • 中断是硬件事件触发的,不是软件轮询的
  • 中断能提高CPU利用率,但也要付出代价(保存现场、嵌套风险)
  • 中断和轮询各有适用场景,别盲目追求中断
  • ISR要短小精悍,别在里面干重活

下一章,咱们聊聊中断的具体实现——中断控制器、中断向量表、以及如何配置一个中断。到时候我会拿STM32和RISC-V两个平台做对比,看看它们的中断机制有什么不同。