2. 单片机中断源:外部中断、定时器中断、串口中断、ADC中断

中断源,说白了就是「谁可以打断CPU当前的工作」。我刚开始学单片机时,总觉得中断是个很玄乎的东西——CPU怎么知道该响应谁?后来调了几个项目才明白,其实每个中断源都对应一个硬件事件,就像你家门铃、电话、闹钟同时响了,你得先判断哪个最紧急。

这一章咱们就把单片机里最常见的四类中断源掰开揉碎了讲。你想想看,一个电机控制系统里,可能同时要用到外部中断检测编码器、定时器中断产生PWM、串口中断接收指令、ADC中断采集电流——它们怎么共存?优先级怎么定?嗯,这就是本章要解决的问题。

2.1 外部中断:最直接的「敲门」信号

外部中断,就是通过芯片的引脚触发的。我习惯把它理解成「硬件敲门」——外部设备拉高或拉低某个引脚,CPU就得停下来看看怎么回事。

核心要点:外部中断通常支持上升沿触发、下降沿触发、低电平触发、高电平触发。实际项目中,我建议优先用边沿触发,避免电平触发带来的抖动误判。

举个例子,你在做一个电机堵转检测。电机堵转时电流会飙升,你用一个比较器把电流信号转成数字电平,接到单片机的外部中断引脚上。一旦电流超标,电平跳变,中断服务程序立刻执行——这比轮询快得多。

触发方式 适用场景 注意事项
上升沿触发 按键、脉冲计数 防抖处理,否则一次按键可能触发多次
下降沿触发 编码器Z信号、故障信号 信号质量差时加施密特触发器
低电平触发 紧急停止、中断共享 电平持续期间会反复触发,需硬件配合
高电平触发 唤醒休眠模式 功耗敏感场景常用

我曾经踩过的坑:有一次做工业控制器,外部中断引脚没加滤波电容,现场强电磁干扰导致每秒触发上千次假中断。从那以后,我所有外部中断引脚都串一个100Ω电阻再加10nF电容到地——成本几分钱,省心一大截。

2.2 定时器中断:最常用的「节拍器」

定时器中断,是单片机内部定时器溢出或比较匹配时产生的中断。我个人觉得,这是嵌入式开发里最重要的中断源——没有之一。PWM输出、周期采样、任务调度、延时控制,全得靠它。

定时器中断的核心参数就三个:预分频值、自动重装载值、中断优先级。预分频决定时钟源分频倍数,重装载值决定定时周期。公式很简单:

定时周期 = (预分频值 + 1) × (自动重装载值 + 1) / 定时器时钟频率

举个例子,STM32的定时器时钟是72MHz,你想产生1ms的定时中断:

预分频值 = 71  → 时钟变成 72MHz / (71+1) = 1MHz
重装载值 = 999 → 定时周期 = 1000 × 1µs = 1ms

小技巧:我习惯把定时器中断频率设计成10的整数倍(比如1ms、10ms),这样调试时用示波器看引脚翻转,一眼就能判断定时器是否准确。别问我为什么,问就是被坑过——有一次设了个7.53ms的周期,示波器上怎么看怎么别扭。

2.3 串口中断:异步通信的「邮差」

串口中断,就是UART收发数据时产生的中断。你发一个字节,或者收到一个字节,硬件都会通知CPU。我刚开始做串口通信时,用的是轮询方式——CPU死等数据,啥也干不了。后来换成中断驱动,CPU利用率直接从30%飙到95%以上。

串口中断通常分三种:

  • 发送完成中断:数据从移位寄存器送出去了,可以继续发下一个字节
  • 接收完成中断:收到一个字节,赶紧去读数据寄存器
  • 错误中断:帧错误、溢出错误、校验错误——我建议一定要使能这个中断,否则数据错了你都不知道

实战建议:串口中断服务程序里只做数据搬运——把收到的字节扔进环形缓冲区,把要发的字节从发送缓冲区取出来。千万别在中断里做协议解析、字符串处理这些耗时操作。我曾经见过一个同事在串口中断里调用了sprintf(),结果系统直接卡死。

2.4 ADC中断:模拟世界的「翻译官」

ADC中断,是模数转换完成时产生的中断。单片机要测量电压、电流、温度这些模拟量,就得靠ADC。转换需要时间——几微秒到几十微秒不等——你总不能CPU干等着吧?所以ADC中断就派上用场了。

ADC中断的典型用法:

  1. 启动ADC转换(软件触发或定时器触发)
  2. CPU继续干别的事
  3. 转换完成,ADC中断触发
  4. 中断服务程序里读取转换结果,存入缓冲区
  5. 主循环或定时器任务处理数据

我做过一个电机电流环控制,ADC采样频率是20kHz,每个PWM周期采样一次电流。如果用轮询,CPU基本就废了。换成ADC中断+DMA,CPU占用率不到5%。

注意:ADC中断频率不能太高。假设你的ADC转换时间是1µs,中断服务程序执行需要2µs,那中断频率就不能超过333kHz(1/3µs),否则CPU永远在响应中断,主循环跑不了。我一般把ADC中断占用的CPU时间控制在总处理能力的10%以内。

2.5 中断源优先级与嵌套

多个中断源同时触发怎么办?谁先响应?这就涉及到中断优先级了。大多数单片机支持抢占优先级和子优先级两级配置:

优先级类型 作用 典型配置
抢占优先级 决定能否打断正在执行的中断 数值越小,优先级越高
子优先级 同抢占优先级时,决定谁先执行 数值越小,优先级越高

我个人的习惯是:

  • 定时器中断(尤其是PWM相关)给最高优先级——丢了周期就丢了控制
  • 外部中断给次高——通常是紧急事件
  • ADC中断给中等——数据晚几个周期问题不大
  • 串口中断给最低——有缓冲区兜底,不怕丢

避坑指南:我曾经把串口中断优先级设得比定时器还高,结果串口大量收发时,定时器中断被频繁打断,PWM波形出现了肉眼可见的抖动。从那以后,我定了一条铁律:周期严格的中断,优先级必须最高。

2.6 中断源知识体系总览

下面这张图把四种中断源的核心关系画出来了。你看一眼,心里就有谱了。

单片机中断源知识体系 CPU中断控制器 NVIC / 中断向量表 外部中断 GPIO边沿/电平触发 紧急事件、编码器 定时器中断 溢出/比较匹配 PWM、周期采样 串口中断 TX/RX完成、错误 通信、指令收发 ADC中断 转换完成 电流、电压采样 优先级配置建议 定时器 > 外部 > ADC > 串口 每个中断源都有独立的触发条件、标志位和向量地址

这张图你看懂了吗?四个中断源各自独立,但最终都汇聚到CPU的中断控制器。控制器根据优先级决定先处理谁。实际项目中,你可能会同时使能多个中断源——比如用定时器中断驱动PWM,用ADC中断采集电流,用串口中断接收上位机指令——这时候优先级配置就至关重要了。