4、中断处理流程:中断请求、中断响应、中断服务程序(ISR)、中断返回

中断处理,说白了就是CPU被“打断”后,怎么去处理紧急事件,再回来继续干活。这个流程我拆成四个环节来讲:中断请求、中断响应、中断服务程序、中断返回。每个环节都有坑,咱们一个一个说。

核心一句话:中断处理 = 硬件自动完成 + 软件手动处理。硬件负责“快速响应”,软件负责“具体干活”。

4.1 中断请求:谁在“敲门”?

中断请求,就是外设或内部事件向CPU发信号:“嘿,我有事找你!”

这个信号可以是电平触发,也可以是边沿触发。我个人习惯用边沿触发,因为它不容易被误判。我在项目中遇到过一个问题:按键按下时,电平抖动导致中断被触发了十几次。后来改成下降沿触发,再加个20ms的软件消抖,问题就解决了。

中断请求的来源主要有三类:

  • 外部中断:GPIO引脚上的电平变化,比如按键、传感器信号。
  • 内部中断:定时器溢出、ADC转换完成、UART接收数据等。
  • 软件中断:程序自己触发的中断,比如系统调用、异常处理。

小技巧:中断请求信号要尽量短。我曾经见过一个同事,把中断引脚拉低了500ms,结果CPU一直在处理中断,主循环根本跑不动。记住:中断请求是“敲门”,不是“踹门”。

4.2 中断响应:CPU怎么“开门”?

CPU收到中断请求后,不会立刻响应。它得先把手头的事做完——至少要把当前指令执行完。然后,硬件自动完成以下动作:

  1. 保存现场:把PC(程序计数器)和状态寄存器压入堆栈。
  2. 关中断:防止在响应过程中被更高优先级的中断打断。
  3. 查找中断向量:根据中断号,从中断向量表中找到对应的ISR入口地址。
  4. 跳转执行:PC指向ISR的第一条指令。

嗯,这里要注意:中断响应时间是硬实时系统的关键指标。我做过一个电机控制项目,要求中断响应必须在5微秒以内。当时选了个Cortex-M4内核的MCU,硬件自动压栈只需要12个时钟周期,完全够用。如果你用51单片机,那响应时间可能要到几十微秒,就得掂量掂量了。

避坑指南:我曾经在STM32上犯过一个低级错误——中断向量表没对齐。结果中断响应时,CPU跳到了一个错误的地址,系统直接跑飞。后来查手册才发现,Cortex-M系列要求中断向量表必须按256字节对齐。血的教训啊!

4.3 中断服务程序(ISR):怎么“干活”?

ISR就是中断处理的核心代码。但这里有个原则:ISR要短、要快、要干净。

你想想看,ISR执行期间,其他中断可能被屏蔽,或者至少低优先级的中断进不来。如果你在ISR里搞个延时、跑个循环,整个系统的实时性就毁了。

我建议ISR只做三件事:

  • 读取硬件状态:比如读取UART接收寄存器、ADC转换结果。
  • 设置标志位:通知主循环“数据来了,赶紧处理”。
  • 清除中断标志:告诉硬件“我已经处理完了,你可以发下一个请求了”。

下面是一个典型的UART接收中断ISR示例:

void USART1_IRQHandler(void)
{
    // 1. 检查中断源
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        // 2. 读取数据
        uint8_t data = USART_ReceiveData(USART1);
        
        // 3. 存入缓冲区(环形缓冲区)
        rx_buffer[rx_write_index] = data;
        rx_write_index = (rx_write_index + 1) % RX_BUFFER_SIZE;
        
        // 4. 设置标志位
        rx_data_ready = 1;
        
        // 5. 清除中断标志
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

个人经验:ISR里尽量不要调用函数,尤其是printf这种慢速函数。我见过有人直接在ISR里打印调试信息,结果串口输出占了几毫秒,系统直接卡死。真要调试,用GPIO翻转加示波器,比printf靠谱多了。

4.4 中断返回:怎么“关门”?

ISR执行完后,CPU要回到被中断的地方继续执行。这个过程叫中断返回,硬件会自动完成:

  1. 恢复现场:从堆栈中弹出之前保存的PC和状态寄存器。
  2. 开中断:重新允许中断响应。
  3. 继续执行:CPU回到被中断的指令处,继续往下跑。

这里有个容易忽略的点:堆栈平衡。如果你在ISR里手动压栈了,返回前一定要手动出栈。否则堆栈指针错位,程序必崩。

避坑指南:我曾经在ARM汇编里写ISR,忘了在返回前恢复LR寄存器。结果中断返回时,CPU跳到了一个随机地址,系统直接HardFault。查了两天才发现,是LR被ISR里的子函数改写了。记住:ISR里调用的函数,必须保证不破坏现场。

4.5 完整流程:一张图看懂

下面我用SVG画了一张中断处理流程图,把四个环节串起来。你看完应该就全明白了。

中断处理完整流程图 主程序执行 中断请求到来 中断响应(硬件自动完成) 保存现场 → 关中断 → 查向量表 → 跳转ISR 中断服务程序(ISR) 读状态 → 设标志 → 清中断标志 中断返回:恢复现场 → 开中断 → 继续执行

4.6 总结:记住这三点

中断处理流程,说白了就是“请求-响应-处理-返回”四个步骤。我总结三个要点:

环节 谁负责 关键动作 常见坑
中断请求 外设/软件 发送中断信号 信号抖动、电平持续时间过长
中断响应 硬件自动 保存现场、查向量表 向量表未对齐、响应时间超限
ISR 软件编写 读数据、设标志、清标志 ISR过长、调用阻塞函数
中断返回 硬件自动 恢复现场、开中断 堆栈不平衡、LR被破坏

最后说一句:中断处理是嵌入式系统的“心脏”。你写得好,系统就稳如老狗;你写得烂,系统就三天两头死机。我建议你每写一个ISR,都问自己三个问题:够短吗?够快吗?够安全吗?

可可精品资料库