第4章 中断系统编程:中断优先级、嵌套中断、临界区保护
中断系统,说白了就是嵌入式系统的"紧急通道"。POS机这种实时性要求高的设备,中断用不好,交易就可能卡死。我刚开始做POS机开发时,就吃过中断嵌套的亏——刷卡后屏幕卡住不动,查了半天才发现是低优先级中断把高优先级中断给堵了。
4.1 中断优先级——谁先谁后得排好队
中断优先级,就是给每个中断源排个号。号小的先处理,号大的等着。你想想看,POS机上电瞬间,电源管理中断肯定比按键中断重要——没电了还处理什么按键?
核心原则:实时性要求越高的中断,优先级越高。
我在项目中遇到过这样一个场景:POS机的磁条卡读卡器中断和打印机中断冲突了。读卡器中断优先级设低了,结果打印时读卡数据丢失。后来我把读卡器中断优先级调到最高,问题就解决了。
常见的优先级分配策略:
- 时钟中断——最高优先级(系统心跳,丢了就乱套)
- 外部触发中断——次高(比如刷卡、按键)
- 通信中断——中等(SPI、I2C、UART)
- 定时器中断——较低(非关键定时任务)
嗯,这里要注意:不同MCU的优先级设置方式不一样。STM32用NVIC,NXP的LPC系列用VIC。但思路是一样的——把最重要的中断放在最前面。
4.2 嵌套中断——中断里还能再进中断?
嵌套中断,就是正在处理一个中断时,来了个更高优先级的中断,CPU先跑去处理新的,处理完再回来继续旧的。说白了就是"紧急事件插队"。
为什么会这样?因为有些中断真的等不起。比如POS机的EMV交易超时中断,如果被低优先级的打印中断堵着,交易就可能失败。这时候嵌套中断就派上用场了。
我的经验:嵌套深度不要超过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——死机!中断里别用标准库
- 忘记清除中断标志位——中断一直触发,系统卡死
- 中断优先级设成一样——不知道谁先执行,行为不可预测
- 临界区保护时间太长——系统响应变慢,像死机一样
我的建议是:
- 中断服务函数只做最必要的事——设置标志位、清除中断标志
- 复杂处理放到主循环或任务中做
- 每个中断都要有超时保护——防止硬件故障导致中断卡死
- 调试时用GPIO翻转来测量中断响应时间——简单有效
小技巧:我习惯在中断服务函数开头和结尾各翻转一个GPIO,用示波器一看就知道中断响应时间和执行时间。这比任何调试器都直观。
4.5 总结
中断系统编程,说白了就是三个字:优先级、嵌套、保护。优先级排好队,嵌套别太深,临界区保护好。做到这三点,POS机的中断系统基本就稳了。
我记得刚入行时,带我的老工程师说:"中断用得好,系统跑得稳;中断用不好,调试调到老。"现在想想,真是至理名言。你们在实际项目中遇到中断问题,不妨从这三个方面入手排查,大概率能找到问题所在。
下一章我们聊聊定时器与PWM输出,这也是POS机里常用的功能——比如蜂鸣器控制、LED呼吸灯效果。到时候见。