中断丢失问题:中断丢失的常见原因

中断丢失,说白了就是CPU该响应中断的时候没响应。我刚开始做嵌入式系统时,总觉得中断嘛,优先级设好就行了。直到有一次产品在现场频繁死机,查了三天才发现是中断丢了。嗯,这问题其实比想象中常见。

中断丢失的核心原因,我归纳为三类:屏蔽、优先级反转、处理耗时过长。咱们一个一个拆开看。

3.1 屏蔽导致的中断丢失

屏蔽是最直接的原因。你想想看,中断被关了,CPU自然收不到信号。但问题往往出在「不该关的时候关了」或者「关了忘开」。

典型场景:

  • 全局关中断(cli / disable_irq)后,长时间不恢复
  • 在中断处理函数里又调了关中断函数,导致嵌套丢失
  • 外设的中断使能位被意外清零

我记得有个项目,用的是某款ARM Cortex-M3芯片。驱动库里有个函数,进去就关了总中断,出来时忘了开。结果系统跑着跑着,定时器中断就丢了,看门狗复位。查代码查了两天,最后发现是那个函数在异常路径上漏了enable_irq。

我的习惯:关中断操作一定要成对出现。写代码时,我习惯用宏封装:

#define ENTER_CRITICAL()  do { old_level = disable_irq_save(); } while(0)
#define EXIT_CRITICAL()   do { restore_irq(old_level); } while(0)

这样能保证每个出口都恢复中断状态。

3.2 优先级反转导致的中断丢失

优先级反转这个词,做RTOS的人都不陌生。但很多人以为它只影响任务调度,其实中断也会被它坑。

举个例子:

  • 高优先级中断A(比如定时器,优先级0)
  • 低优先级中断B(比如串口,优先级2)
  • 中优先级中断C(比如GPIO,优先级1)

如果B正在处理,A来了,按理说A应该抢占B。但如果B在临界区里关了高优先级中断,A就被堵住了。等B出来,A可能已经错过了采样窗口——中断丢失。

场景 是否丢失 原因
B在临界区,A到达 可能丢失 A被屏蔽,B退出后A的脉冲已过
B在临界区,C到达 不丢失 C优先级高于B,但低于A,不影响A
B在临界区,A和C同时到达 A可能丢失 C先响应,A被屏蔽时间更长

我曾经踩过的坑:在一个多中断系统中,串口中断里用了spin_lock_irqsave,锁住了所有中断。结果高优先级的ADC中断来了,被硬生生堵了200微秒。ADC采样周期是100微秒,直接丢了两个采样点。后来我把锁改成了spin_lock,只锁当前CPU,不关其他中断,问题解决。

3.3 处理耗时过长导致的中断丢失

这个原因最隐蔽。你想想看,中断处理函数执行时间太长,后面的中断排队等着。如果中断源是边沿触发或者单脉冲,等CPU腾出手来,信号已经没了。

我一般把中断处理分成两部分:

  • 上半部(Top Half):只做最紧急的事,比如读寄存器、清标志、记录时间
  • 下半部(Bottom Half):把耗时操作丢给任务或工作队列

经验数据:

  • 中断处理函数执行时间 < 中断源最小间隔的 1/3
  • 如果中断源间隔是 100μs,处理函数最好控制在 30μs 以内
  • 超过这个阈值,丢失概率急剧上升

我做过一个电机控制项目,霍尔传感器中断每50微秒来一次。一开始我在中断里做了PID计算和PWM更新,算下来要80微秒。结果电机转起来一顿一顿的——中断丢了。后来我把PID计算挪到任务里,中断里只读霍尔值和清标志,处理时间降到5微秒,问题消失。

3.4 三种原因的对比与诊断

原因 典型表现 诊断方法 修复方向
屏蔽 中断完全不响应 检查中断使能寄存器 成对操作、避免长临界区
优先级反转 高优先级中断偶尔丢失 加GPIO翻转测延迟 缩小锁范围、使用优先级继承
处理耗时过长 周期性中断丢失 测量中断处理时间 拆分为上下半部

3.5 核心逻辑图

下面这张图,我把三种原因和它们的关系画出来了。你看一眼就能明白:

中断丢失核心原因与关系 中断丢失 屏蔽 优先级反转 处理耗时过长 全局关中断未恢复 外设使能位意外清零 低优先级中断锁高优先级 临界区嵌套导致延迟 中断处理函数执行超时 未拆分上下半部 诊断建议 先查中断使能寄存器 → 再测中断处理时间 → 最后分析优先级反转

我的诊断流程:

  1. 先看中断使能寄存器——确认中断没被关
  2. 用逻辑分析仪抓中断引脚和CPU响应引脚——测延迟
  3. 如果延迟稳定但丢中断,查处理时间
  4. 如果延迟忽大忽小,查优先级反转

中断丢失这个问题,说白了就是「该响应的时候没响应」。屏蔽、优先级反转、处理耗时过长,这三兄弟经常一起出现。我建议你在设计阶段就把它们考虑进去,别等到现场出问题了再查——那滋味,我尝过,不好受。