1. 中断基础概念:什么是中断、中断向量表、中断服务函数(ISR)的执行流程
中断机制是嵌入式系统响应外部或内部异步事件的基石。理解中断的底层原理,是后续配置中断优先级、提升外设驱动稳定性的前提。本节将从三个核心维度展开:中断的本质、中断向量表的组织方式、以及中断服务函数(ISR)的完整执行流程。
1.1 什么是中断
中断(Interrupt)是一种硬件机制,允许外设或内核内部事件暂停当前正在执行的程序,转而执行一段预先定义好的特殊函数(ISR),处理完后再恢复原程序的执行。它解决了CPU轮询(Polling)方式下的低效问题——CPU无需一直检查外设状态,只有在事件发生时才会被“通知”。
核心要点:中断是CPU与外设之间的“事件驱动”通信方式,而非“查询驱动”。
| 对比维度 | 轮询方式 | 中断方式 |
|---|---|---|
| CPU利用率 | 低(大量时间浪费在查询) | 高(无事时执行主任务) |
| 实时性 | 差(响应延迟取决于查询周期) | 强(事件触发立即响应) |
| 代码复杂度 | 简单(循环+条件判断) | 中等(需配置中断向量、ISR) |
| 功耗 | 高(CPU持续运行) | 低(可进入休眠,中断唤醒) |
实战提示:在低功耗IoT设备中,中断是必选方案。例如,UART接收中断可以让CPU在无数据时进入睡眠模式,数据到达时自动唤醒。
1.2 中断向量表
中断向量表(Interrupt Vector Table, IVT)是MCU内部一张地址映射表,表中每一项(向量)存放着对应中断服务函数(ISR)的入口地址。当某个中断被触发时,CPU硬件自动从向量表中取出对应的地址,跳转执行。
- 固定基址:大多数ARM Cortex-M系列MCU的向量表默认位于Flash起始地址(如0x00000000),但可通过VTOR寄存器重定位到SRAM或Flash的其他位置。
- 向量编号:每个中断源对应一个唯一的向量编号(IRQn),编号越小优先级越高(部分架构相反)。
- 异常与中断:复位、NMI、HardFault等系统异常也占用向量表的前几个位置,与外部中断统一管理。
常见陷阱:向量表地址必须按4字节对齐(Cortex-M要求)。如果使用IAP(在应用编程)升级固件,务必在跳转前重新设置VTOR,否则中断将指向旧向量表,导致程序跑飞。
1.3 中断服务函数(ISR)的执行流程
一个完整的中断响应过程分为三个阶段:中断请求 → 硬件自动压栈与向量查找 → ISR执行与出栈返回。以ARM Cortex-M为例,具体步骤如下:
- 中断请求:外设(如UART、定时器)产生中断信号,NVIC(嵌套向量中断控制器)检测到挂起的中断。
- 优先级裁决:NVIC比较当前中断与正在执行的中断优先级,若新中断优先级更高,则触发抢占。
- 硬件自动压栈:CPU自动将R0-R3、R12、LR、PC、xPSR等8个寄存器压入当前栈(MSP或PSP)。
- 向量表查找:CPU根据中断编号从向量表中取出ISR入口地址,加载到PC寄存器。
- ISR执行:CPU跳转到ISR函数,执行用户编写的处理代码(如读取数据、清除中断标志)。
- 出栈与返回:ISR执行完毕后,执行BX LR指令(LR在进入ISR时被硬件更新为EXC_RETURN),CPU自动出栈恢复现场,继续执行被中断的程序。
1.4 ISR 编写规范与注意事项
- 快速执行:ISR中不应包含延时、轮询、复杂计算等阻塞操作,应尽快清除中断标志并退出。
- 避免重入:同一中断在ISR执行期间再次触发,可能导致栈溢出或数据竞争。通常通过硬件屏蔽或软件标志防止重入。
- 使用 volatile:ISR与主程序共享的全局变量必须声明为
volatile,防止编译器优化导致数据不一致。 - 中断嵌套:高优先级中断可以抢占低优先级ISR,但嵌套深度应受控,避免栈溢出。
// 典型 UART 接收中断 ISR 示例 (STM32 HAL 风格)
volatile uint8_t rx_buffer[256];
volatile uint16_t rx_index = 0;
void USART1_IRQHandler(void)
{
// 1. 检查中断标志
if (USART1->SR & USART_SR_RXNE)
{
// 2. 读取数据
uint8_t data = USART1->DR;
// 3. 存入缓冲区(非阻塞)
if (rx_index < 256)
{
rx_buffer[rx_index++] = data;
}
// 4. 清除标志(读DR自动清除RXNE)
// 某些外设需手动写标志位清除
}
}
进阶思考:在RTOS环境下,ISR通常只做“标记-通知”工作(如发送信号量或消息队列),实际数据处理交给任务上下文,以缩短关中断时间。