第4章 中断系统编程:中断优先级、嵌套中断、临界区保护

中断系统,说白了就是嵌入式系统的"紧急通道"。POS机这种实时性要求高的设备,中断用不好,交易就可能卡死。我刚开始做POS机开发时,就吃过中断嵌套的亏——刷卡后屏幕卡住不动,查了半天才发现是低优先级中断把高优先级中断给堵了。

4.1 中断优先级——谁先谁后得排好队

中断优先级,就是给每个中断源排个号。号小的先处理,号大的等着。你想想看,POS机上电瞬间,电源管理中断肯定比按键中断重要——没电了还处理什么按键?

核心原则:实时性要求越高的中断,优先级越高。

我在项目中遇到过这样一个场景:POS机的磁条卡读卡器中断和打印机中断冲突了。读卡器中断优先级设低了,结果打印时读卡数据丢失。后来我把读卡器中断优先级调到最高,问题就解决了。

常见的优先级分配策略:

  • 时钟中断——最高优先级(系统心跳,丢了就乱套)
  • 外部触发中断——次高(比如刷卡、按键)
  • 通信中断——中等(SPI、I2C、UART)
  • 定时器中断——较低(非关键定时任务)

嗯,这里要注意:不同MCU的优先级设置方式不一样。STM32用NVIC,NXP的LPC系列用VIC。但思路是一样的——把最重要的中断放在最前面。

4.2 嵌套中断——中断里还能再进中断?

嵌套中断,就是正在处理一个中断时,来了个更高优先级的中断,CPU先跑去处理新的,处理完再回来继续旧的。说白了就是"紧急事件插队"。

为什么会这样?因为有些中断真的等不起。比如POS机的EMV交易超时中断,如果被低优先级的打印中断堵着,交易就可能失败。这时候嵌套中断就派上用场了。

我的经验:嵌套深度不要超过3层。太深了,栈空间容易爆。我曾经在调试时发现程序莫名其妙复位,查了三天才发现是嵌套太深,栈溢出了。

嵌套中断的实现要点:

  1. 中断服务函数要尽量短——别在里面做复杂运算
  2. 高优先级中断不能调用可能被低优先级中断占用的资源
  3. 注意中断栈的大小——每个嵌套层都要消耗栈空间
// 嵌套中断示例(伪代码)
void TIM2_IRQHandler(void)      // 优先级=2
{
    // 处理定时器事件
    // 此时来了个优先级=1的外部中断
    // CPU自动保存现场,去处理外部中断
    // 外部中断处理完,回来继续执行
    clear_TIM2_flag();
}

void EXTI0_IRQHandler(void)     // 优先级=1
{
    // 高优先级中断处理
    // 处理完返回低优先级中断
    clear_EXTI0_flag();
}

4.3 临界区保护——别让中断搞乱共享数据

临界区,就是一段不能被中断打断的代码。为什么?因为中断可能修改共享变量,导致主程序读到不一致的数据。

我举个例子:POS机的交易金额变量,主程序正在更新它,突然来了个中断也要修改它。结果呢?数据乱了,交易金额变成天文数字。嗯,这种事我见过不止一次。

警告:临界区保护不是万能的。保护时间过长,会影响系统实时性。我曾经见过有人把整个中断服务函数都包在临界区里——那还要中断干嘛?

常用的临界区保护方法:

方法 适用场景 缺点
关中断 短小临界区(<10μs) 影响实时性
信号量 RTOS环境 实现复杂
原子操作 单变量操作 仅限特定指令
// 关中断保护临界区(Cortex-M3示例)
void critical_section_enter(void)
{
    // 保存当前中断状态并关中断
    __disable_irq();
}

void critical_section_exit(void)
{
    // 恢复中断状态
    __enable_irq();
}

// 使用示例
void update_transaction_amount(uint32_t new_amount)
{
    critical_section_enter();
    // 临界区开始
    g_transaction_amount = new_amount;
    g_amount_updated = 1;
    // 临界区结束
    critical_section_exit();
}

4.4 避坑指南——我踩过的那些坑

做中断系统编程,坑真的不少。我把自己踩过的坑整理一下,你们少走弯路。

我曾经犯过的错误:

  • 中断服务函数里调用printf——死机!中断里别用标准库
  • 忘记清除中断标志位——中断一直触发,系统卡死
  • 中断优先级设成一样——不知道谁先执行,行为不可预测
  • 临界区保护时间太长——系统响应变慢,像死机一样

我的建议是:

  1. 中断服务函数只做最必要的事——设置标志位、清除中断标志
  2. 复杂处理放到主循环或任务中做
  3. 每个中断都要有超时保护——防止硬件故障导致中断卡死
  4. 调试时用GPIO翻转来测量中断响应时间——简单有效

小技巧:我习惯在中断服务函数开头和结尾各翻转一个GPIO,用示波器一看就知道中断响应时间和执行时间。这比任何调试器都直观。

4.5 总结

中断系统编程,说白了就是三个字:优先级、嵌套、保护。优先级排好队,嵌套别太深,临界区保护好。做到这三点,POS机的中断系统基本就稳了。

我记得刚入行时,带我的老工程师说:"中断用得好,系统跑得稳;中断用不好,调试调到老。"现在想想,真是至理名言。你们在实际项目中遇到中断问题,不妨从这三个方面入手排查,大概率能找到问题所在。

下一章我们聊聊定时器与PWM输出,这也是POS机里常用的功能——比如蜂鸣器控制、LED呼吸灯效果。到时候见。