1. 中断基础概念
中断(Interrupt)是嵌入式系统中处理器与外部设备交互的核心机制。它允许外设或软件在需要时“打断”CPU当前正在执行的程序流,转而执行一个预先定义好的中断服务程序(ISR, Interrupt Service Routine),处理完成后自动返回被中断的断点继续执行。这一机制避免了CPU轮询外设状态带来的性能浪费,是实现高效驱动响应的基石。
核心理解:中断的本质是“事件驱动的异步通知”。CPU无需主动查询外设状态,外设通过中断线主动告知CPU“我有事需要处理”,从而大幅提升系统实时性与吞吐量。
1.1 中断定义与分类
中断根据触发源和触发方式可分为以下几类:
| 分类维度 | 类型 | 说明 | 典型示例 |
|---|---|---|---|
| 触发源 | 硬件中断 | 由外部设备(如GPIO、定时器、UART)通过中断控制器向CPU发送的电平或边沿信号触发 | 按键按下、DMA传输完成 |
| 软件中断 | 由CPU执行特定指令(如ARM的SWI、x86的INT n)主动触发,常用于系统调用 | Linux系统调用、任务切换 | |
| 触发方式 | 电平触发 | 中断信号保持有效电平(高或低)直到CPU响应并清除 | 传统PCI设备中断 |
| 边沿触发 | 中断信号在电平跳变(上升沿或下降沿)时触发,脉冲宽度极短 | GPIO按键中断 | |
| 可屏蔽性 | 可屏蔽中断 | CPU可通过中断屏蔽寄存器(IMR)暂时忽略该中断请求 | 大部分外设中断 |
| 不可屏蔽中断 | CPU必须立即响应,无法通过软件屏蔽,用于紧急事件 | 看门狗超时、电源故障 |
驱动开发要点:在Linux内核中,硬件中断对应的处理函数注册在
request_irq()中,软件中断则通过trigger_softirq()或tasklet机制实现。理解中断分类有助于选择正确的注册方式和处理策略。
1.2 中断向量表
中断向量表(Interrupt Vector Table, IVT)是存储在固定内存地址的一张表格,表中每一项存放着对应中断号的中断服务程序入口地址。当CPU接收到中断请求时,硬件自动根据中断号查表,跳转到对应的ISR执行。
中断向量表的结构与寻址方式因架构而异:
- x86架构:IVT位于内存地址0x0000~0x03FF(实模式),共256个中断向量,每个向量4字节(段地址+偏移地址)。保护模式下使用中断描述符表(IDT)。
- ARM Cortex-M:中断向量表位于Flash起始地址(0x00000000),前16个为系统异常向量,后续为外设中断向量,每个向量4字节(函数地址)。
- RISC-V:使用mtvec/stvec寄存器指向中断向量表基址,支持直接模式和向量模式。
常见陷阱:在ARM Cortex-M上,向量表第0项必须是有效的栈指针,否则复位后CPU无法正确初始化栈空间。驱动开发中若需重定位向量表(如使用RTOS),必须确保新向量表对齐到512字节或1024字节边界(具体见芯片手册)。
1.3 中断优先级
中断优先级决定了当多个中断同时发生时,CPU优先响应哪一个。优先级管理是中断控制器(如ARM GIC、NVIC、x86 APIC)的核心功能。不同架构的优先级实现方式有所差异:
| 架构/控制器 | 优先级位数 | 优先级值含义 | 分组方式 |
|---|---|---|---|
| ARM Cortex-M NVIC | 8位(实际使用3~8位) | 数值越小,优先级越高 | 支持抢占优先级与子优先级分组(PRIGROUP) |
| ARM GICv2/v3 | 8位 | 数值越小,优先级越高 | 支持安全/非安全世界独立优先级配置 |
| x86 APIC | 8位 | 数值越大,优先级越高 | 固定优先级与轮转优先级模式 |
| RISC-V PLIC | 可配置(通常3~7位) | 数值越大,优先级越高 | 支持阈值中断,低于阈值的中断被屏蔽 |
驱动开发关键:在Linux内核中,中断优先级通常在硬件初始化阶段由板级代码或设备树配置。驱动开发者一般通过
irq_set_priority()(RTOS环境)或设备树interrupts属性中的优先级字段进行设置。高优先级中断应分配给时间敏感的外设(如定时器、DMA),低优先级中断分配给非实时外设(如按键、LED)。
1.4 中断嵌套
中断嵌套(Interrupt Nesting)是指CPU在处理一个中断服务程序(ISR)的过程中,又被另一个更高优先级的中断打断,转而执行更高优先级的ISR,处理完成后再返回被中断的低优先级ISR继续执行。中断嵌套是提升系统实时响应能力的重要机制。
中断嵌套的实现依赖以下硬件与软件条件:
- 硬件支持:中断控制器必须支持优先级仲裁,且CPU在ISR执行期间能够重新使能中断(通常通过自动或手动清除中断屏蔽位实现)。
- 栈管理:每次中断嵌套都需要保存当前ISR的上下文(寄存器、返回地址),嵌套深度受限于栈空间大小。
- 可重入性:ISR必须可重入(reentrant),即不能使用全局静态变量而不加保护,否则嵌套可能导致数据竞争。
嵌套风险提示:中断嵌套深度过大会导致栈溢出,尤其在资源受限的MCU上(如STM32F103默认栈大小仅1KB)。驱动开发中应遵循以下原则:
1. ISR尽量短小精悍,避免在ISR中执行耗时操作。
2. 合理分配中断优先级,避免高优先级ISR频繁打断低优先级ISR导致“优先级反转”。
3. 在RTOS环境下,ISR中禁止调用可能引起任务阻塞的API(如
1. ISR尽量短小精悍,避免在ISR中执行耗时操作。
2. 合理分配中断优先级,避免高优先级ISR频繁打断低优先级ISR导致“优先级反转”。
3. 在RTOS环境下,ISR中禁止调用可能引起任务阻塞的API(如
vTaskDelay())。
1.5 中断处理流程总结
一个完整的中断处理流程包含以下关键步骤,驱动开发者需要深刻理解每个环节的硬件与软件行为:
- 中断请求(IRQ):外设检测到事件(如数据到达、传输完成),通过中断线向中断控制器发送请求信号。
- 中断仲裁:中断控制器根据优先级规则,选择当前最高优先级的中断请求,向CPU发送中断信号。
- CPU响应:CPU完成当前指令后,保存现场(压栈PC、xPSR、LR、R0-R3、R12),查询中断向量表获取ISR入口地址。
- ISR执行:CPU跳转到ISR执行。在ISR中,驱动开发者需要完成:清除中断标志、读取/写入数据、通知内核或应用层。
- 中断返回:ISR执行完毕后,执行中断返回指令(如ARM的BX LR),CPU从栈中恢复现场,继续执行被中断的程序。
Linux内核视角:在Linux中,中断处理被分为上半部(Top Half)和下半部(Bottom Half)。上半部在ISR中执行,要求快速完成(通常只做寄存器操作和标志清除);下半部通过tasklet、工作队列或软中断延后执行耗时操作。这正是本课程后续章节的核心内容——tasklet机制正是下半部的一种高效实现。