第1章:中断管理——实时性的第一道防线

做麻醉机嵌入式开发这些年,我最大的体会就是:中断管理做不好,实时性就是一句空话。你想想看,病人正在手术中,呼吸参数突然报警,CPU却还在处理一个无关紧要的任务——这种场景光是想想就后背发凉。

今天咱们就聊聊中断管理的三个核心问题:中断延迟的组成、中断嵌套与可重入性、以及ISR的设计原则。这些都是我踩过坑之后才真正理解的东西。

1.1 中断延迟的组成

什么叫中断延迟?说白了就是从硬件发出中断请求,到CPU开始执行你的ISR第一行代码,这中间到底花了多少时间。

我习惯把中断延迟拆成三部分来看:

延迟阶段 时间来源 典型时长(ARM Cortex-M3 @72MHz)
硬件延迟 中断信号传递、CPU当前指令完成 3~12个时钟周期
中断响应 压栈、取向量、跳转 12~16个时钟周期
软件延迟 关中断时间、高优先级ISR执行 不确定,看你怎么写代码

前两部分基本是硬件决定的,你改不了。但软件延迟这部分,完全取决于你的代码质量。我在项目中遇到过最夸张的情况:一个同事在ISR里调用了printf,结果中断延迟直接飙到毫秒级。麻醉机的压力采样周期才1ms,这还得了?

⚠️ 避坑指南:我曾经在一个项目中,因为某个外设的中断优先级设得太高,导致定时器中断被频繁抢占。结果呼吸频率计算值忽高忽低,差点把测试工程师搞疯。后来我把所有中断优先级重新梳理了一遍,才解决问题。

1.2 中断嵌套与可重入性

中断嵌套,就是高优先级中断可以打断低优先级中断。听起来很美好对吧?但这里有个大坑——可重入性

什么叫可重入?就是一个函数在被调用执行的过程中,又被再次调用了。如果这个函数使用了全局变量或者静态变量,那第二次调用就可能把第一次的数据给覆盖掉。

我举个例子:

// 不可重入的例子
static uint32_t counter = 0;

void increment_counter(void) {
    counter++;  // 如果被中断打断,再次调用,counter就乱了
}

// 可重入的例子
void increment_counter_safe(uint32_t *p_counter) {
    (*p_counter)++;  // 只操作传入的指针,不碰全局变量
}

在麻醉机里,这个问题特别要命。我记得有一次,压力传感器中断和流量传感器中断共享了一个全局缓冲区。结果两个中断嵌套了,缓冲区里的数据全乱了套。那台机器在测试时出现了压力波形抖动,排查了两天才找到原因。

我的建议:ISR里尽量别用全局变量。实在要用,就用volatile关键字声明,并且考虑用关中断或者原子操作来保护。但说实话,最好的办法就是——ISR里只做最必要的事

1.3 ISR设计原则

这部分是我最想跟你分享的。做了十年嵌入式,我总结了几条ISR设计原则,每一条都是用教训换来的。

原则一:ISR要短小精悍

ISR不是让你写业务逻辑的地方。它的任务只有一个:快速接收事件,然后通知后台任务去处理

怎么做?我习惯用这两种方式:

  • 设置标志位:ISR里只置一个全局标志,主循环里轮询处理
  • 使用消息队列:ISR把数据塞进队列,任务线程从队列里取

你想想看,如果ISR里做了一堆浮点运算,那其他中断得等到什么时候?

原则二:避免在ISR中调用不可重入函数

这个前面已经说过了。再强调一遍:printf、malloc、甚至是某些库函数,在ISR里调用都可能出问题

我个人的习惯是:ISR里只用两种函数——要么是硬件寄存器操作,要么是自己写的、明确标注了可重入的函数。

原则三:合理设置中断优先级

麻醉机里,中断优先级不是随便设的。我一般这样排:

  1. 最高优先级:系统心跳定时器(1ms中断)——这是整个系统的节拍
  2. 次高优先级:压力/流量传感器中断——实时性要求极高
  3. 中等优先级:通信中断(SPI、I2C)——数据不能丢,但可以等一等
  4. 最低优先级:按键、显示刷新——慢一点没关系

💡 小技巧:我习惯在项目初期就画一张中断优先级表格,贴在工位上。每次新增中断,先看看这张表,避免优先级冲突。这个习惯帮我避免了好几次潜在的bug。

原则四:注意中断延迟的累积效应

单个中断延迟可能只有几微秒,但多个中断嵌套起来,延迟就可能累积到几十甚至上百微秒。在麻醉机这种生命支持设备里,最坏情况下的中断延迟才是你真正需要关心的。

怎么算?我一般这样估算:

最坏中断延迟 = 硬件延迟 + 最长关中断时间 + 所有高优先级ISR执行时间之和

如果这个值超过了你的实时性要求,那就得优化了。要么缩短关中断时间,要么降低某些ISR的执行时间。

小结

中断管理这件事,说难不难,说简单也不简单。关键是要有系统思维——别只盯着一个中断看,要看到整个中断系统的相互作用。

最后分享一个我自己的习惯:每次写完ISR,我都会问自己三个问题:

  • 这个ISR能在10微秒内执行完吗?
  • 如果被更高优先级中断打断,会出问题吗?
  • 这个ISR里有没有调用不可重入函数?

三个问题都回答「是」,才算合格。嗯,这标准可能有点严,但做医疗设备嘛,严一点总没错。