第4章 中断管理优化:中断优先级设置、ISR瘦身、中断延迟控制

中断管理,说白了就是嵌入式系统的“应急响应机制”。麻醉机这种生命支持设备,中断处理的好坏直接关系到病人安全。我见过不少同行,代码写得挺漂亮,但中断管理一塌糊涂——该响应的没响应,不该抢的乱抢,最后系统跑飞了都不知道怎么死的。

这一章,咱们就聊聊中断优化的三个核心点:优先级怎么设、ISR怎么写才够瘦、延迟怎么控得住。

4.1 中断优先级设置——别让“抢断”变成灾难

中断优先级,不是随便排个123就完事的。你得想清楚:哪些中断是“生死攸关”的,哪些可以等一等。

我个人习惯,把麻醉机的中断分成三个梯队:

优先级梯队 典型中断源 响应要求
第一梯队(最高) 气道压力报警、氧浓度异常、心跳丢失 μs级响应,不可被屏蔽
第二梯队(中等) 流量传感器采样、按键扫描、通信接收 ms级响应,可被第一梯队打断
第三梯队(最低) LCD刷新、日志记录、自检任务 可延迟,允许被高优先级打断

我在项目中遇到过一个问题:某款麻醉机的氧浓度报警偶尔会延迟几百毫秒。查到最后,发现是LCD刷新中断和报警中断用了同一优先级,结果LCD刷新占用了太多时间。嗯,这里要注意——同优先级的中断,硬件上通常是按顺序响应的,谁先来谁先处理,但如果你在ISR里耗太久,后面的中断就只能排队等着。

避坑指南:我曾经见过一个团队,把所有中断都设成同一优先级。结果一个串口接收中断里做了大量数据处理,导致定时器中断被堵死,系统时钟直接跑偏。记住:高优先级中断的ISR必须短小精悍,否则你设了优先级也没用。

设置优先级时,我建议遵循两个原则:

  • 时间敏感型 > 数据敏感型:报警、控制类中断优先于数据采集、通信类中断
  • 不可重入型 > 可重入型:如果某个中断服务程序本身不可重入,那就给它高优先级,避免被多次打断导致状态混乱

4.2 ISR瘦身——别让中断服务程序变成“胖子”

ISR瘦身,是我在面试新人时必问的问题。很多人觉得ISR里写点逻辑没什么,但你想过没有——ISR执行期间,其他中断是进不来的(除非你开了嵌套)。你拖得越久,系统响应越差。

ISR瘦身的核心思想就一句话:ISR里只做最必要的事,其余全部丢到任务级去处理

具体怎么做?我总结了一个“三做三不做”原则:

应该做 不应该做
读取硬件寄存器,清除中断标志 进行浮点运算或复杂数学计算
将数据放入队列或缓冲区 直接操作全局变量(除非用原子操作)
设置事件标志或信号量 调用printf、malloc等不可重入函数

举个例子,一个典型的“胖ISR”长这样:

// ❌ 错误示范:ISR里做了太多事
void TIMER_IRQHandler(void) {
    uint32_t raw = TIM->SR;
    TIM->SR = 0;  // 清标志
    
    // 计算流量——用了浮点!
    float flow = (float)adc_value * 0.123f;
    
    // 更新显示——直接操作LCD!
    LCD_DisplayNumber(flow);
    
    // 写日志——用了文件系统!
    Log_Write("flow = %f", flow);
    
    // 检查报警——做了大量逻辑判断!
    if (flow > ALARM_THRESHOLD) {
        Buzzer_On();
        LED_Red();
    }
}

这个ISR执行一次可能要几百微秒。如果系统里同时有多个这样的中断,后果不堪设想。

瘦身后的版本应该是这样:

// ✅ 正确示范:ISR只做最必要的事
void TIMER_IRQHandler(void) {
    uint32_t raw = TIM->SR;
    TIM->SR = 0;  // 清标志
    
    // 只做一件事:把数据塞进队列
    Queue_Send(&flow_queue, adc_value);
    
    // 设置事件标志,通知任务层处理
    Event_Set(EVENT_FLOW_READY);
}
小技巧:我个人习惯在ISR里用一个“临界区保护”的环形缓冲区。这样既保证了数据不丢失,又不会阻塞其他中断。你想想看,如果每个ISR都只花几个微秒,系统的实时性自然就上去了。

4.3 中断延迟控制——别让“迟到”变成事故

中断延迟,就是从硬件触发中断到CPU开始执行ISR第一条指令的时间。这个时间如果太长,麻醉机的报警可能就“迟到”了——病人气道压力都爆表了,你这边还没反应过来,这能行吗?

影响中断延迟的因素主要有三个:

  • 关中断时间:你在临界区里关了中断,外面的中断就只能等着
  • 高优先级ISR执行时间:高优先级ISR不退出,低优先级的中断就永远进不去
  • 硬件响应时间:CPU本身的中断采样、压栈也需要时间

我在项目中遇到过最典型的问题:某次系统升级后,麻醉机的按键响应变得特别迟钝。按下去要等一两秒才有反应。查了半天,发现是新增的一个通信模块在ISR里做了大量数据解析,而且这个通信中断优先级设得比按键还高。结果按键中断一直被堵着,直到通信处理完才能响应。

怎么控制延迟?我建议从这几个方面入手:

  1. 最小化关中断的临界区:能用原子操作就别关中断,非要关的话,时间控制在10μs以内
  2. 高优先级ISR必须“快进快出”:第一梯队的中断ISR,执行时间不要超过50μs
  3. 合理使用中断嵌套:如果硬件支持,可以允许高优先级中断打断低优先级ISR
  4. 避免在ISR里调用复杂函数:比如printf、malloc、甚至一些库函数,都可能引入不可预测的延迟

核心指标:对于麻醉机这类设备,我通常要求:

  • 最高优先级中断延迟 < 5μs
  • 普通中断延迟 < 50μs
  • 关中断时间 < 10μs(单次)

如果达不到,说明你的中断管理还有优化空间。

最后说一个我踩过的坑。有一次,我在ISR里用了一个全局变量做状态标志,结果主循环和ISR同时读写这个变量,导致数据错乱。后来加了volatile关键字,又用原子操作保护,才解决问题。记住:ISR和主循环共享的变量,一定要用volatile声明,并且保证访问的原子性

嗯,中断管理这块,说白了就是“该快的快,该等的等”。优先级设对了,ISR写瘦了,延迟控住了,你的系统才能稳稳地跑在病人身边。下一章咱们聊聊任务调度优化,到时候再细说怎么把任务和中断配合好。