1. 中断基础概念
中断(Interrupt)是嵌入式系统中最重要的硬件-软件通信机制之一。它允许外设或处理器内部事件在发生时立即通知CPU,而无需CPU持续轮询(Polling)设备状态。理解中断的基础概念是编写正确、高效Linux设备驱动的前提。
1.1 中断的定义
中断是一种硬件机制,当某个事件(如按键按下、数据接收完成、定时器溢出)发生时,硬件会向CPU发送一个电信号(中断请求线,IRQ Line)。CPU在完成当前指令后,会暂停正在执行的程序,保存上下文,跳转到一个预先定义好的中断服务程序(Interrupt Service Routine, ISR)去处理该事件。处理完成后,CPU恢复之前被中断的程序继续执行。
中断处理流程可概括为以下步骤:
- 事件触发: 外设或内部模块产生中断事件。
- 信号发送: 中断控制器(如GIC、NVIC)将中断信号路由到CPU核心。
- CPU响应: CPU保存当前执行现场(PC、寄存器等),进入中断模式。
- 向量查找: CPU根据中断号查找中断向量表,获取ISR入口地址。
- ISR执行: 执行中断服务程序,处理事件(如读取数据、清除中断标志)。
- 恢复现场: 执行中断返回指令,恢复被中断的程序继续运行。
1.2 中断类型:IRQ 与 FIQ
在ARM架构(特别是Cortex-A系列)中,中断分为两种类型:IRQ(Interrupt Request) 和 FIQ(Fast Interrupt Request)。两者在优先级、响应速度和资源占用上存在显著差异。
| 特性 | IRQ(普通中断) | FIQ(快速中断) |
|---|---|---|
| 优先级 | 较低(FIQ优先级高于IRQ) | 较高(可抢占IRQ) |
| 向量表位置 | 0x00000018(ARM模式) | 0x0000001C(ARM模式) |
| 寄存器保存 | 需保存/恢复所有被破坏的寄存器 | 有专用寄存器(r8~r12),减少压栈开销 |
| 响应延迟 | 相对较长 | 更短(快速响应) |
| 典型用途 | 普通外设中断(UART、I2C、SPI等) | 高实时性需求(DMA、定时器、紧急事件) |
| 中断嵌套 | 可被FIQ抢占 | 不可被IRQ抢占 |
1.3 中断在嵌入式系统中的作用
中断机制是嵌入式系统实时性和多任务处理能力的基石。在Linux驱动的上下文中,中断的作用体现在以下几个方面:
- 提高CPU利用率: 无需CPU轮询等待外设状态,CPU可以在等待期间执行其他任务或进入低功耗模式。
- 实时响应: 对时间敏感的事件(如电机编码器脉冲、网络数据包到达)可以立即得到处理。
- 事件驱动编程: 驱动代码以“事件触发-处理”的模式组织,逻辑清晰,耦合度低。
- 低功耗支持: 系统可以在空闲时进入休眠,中断作为唤醒源将CPU从休眠中唤醒。
- 硬件抽象: 中断控制器和中断号在设备树中描述,驱动通过标准API(如
request_irq)申请中断,与具体硬件解耦。
interrupts 属性和 interrupt-parent 属性描述。驱动通过 platform_get_irq() 或 of_irq_get() 获取中断号,然后调用 request_irq() 注册中断处理函数。这是后续章节的核心内容。
1.4 中断上下文与进程上下文
理解中断上下文(Interrupt Context)与进程上下文(Process Context)的区别,对于编写安全的驱动代码至关重要。
| 特性 | 进程上下文 | 中断上下文 |
|---|---|---|
| 可睡眠 | 是(可以调用 msleep、wait_event 等) |
否(禁止睡眠,不能调用可能阻塞的函数) |
| 持有锁 | 可以使用互斥锁(mutex) | 只能使用自旋锁(spinlock) |
| 访问用户空间 | 可以(copy_from_user / copy_to_user) |
禁止(无法访问用户空间) |
| 调度 | 可以被调度器抢占 | 不可被调度(除非中断嵌套) |
| 典型操作 | 文件读写、系统调用、内存分配 | 清除中断标志、拷贝FIFO数据、唤醒等待队列 |
kmalloc(GFP_KERNEL)、mutex_lock()、copy_from_user())。违反此规则会导致内核崩溃或死锁。如果需要执行耗时操作,应使用底半部机制(Tasklet、Workqueue、 threaded IRQ)。
1.5 中断号与中断控制器
在嵌入式系统中,中断号(IRQ Number)是标识每个中断源的唯一编号。中断控制器(如ARM GIC、TI OMAP INTC)负责管理多个中断源的路由、优先级和使能控制。
在设备树中,中断控制器通过以下属性描述:
interrupt-controller:空属性,标记该节点为中断控制器。#interrupt-cells:指定中断描述符需要的单元数(通常为2:中断号 + 触发类型)。interrupt-parent:指向父中断控制器的phandle。
设备节点通过 interrupts 属性声明自己使用的中断:
// 设备树示例:UART设备中断描述
uart0: serial@10000000 {
compatible = "vendor,uart";
reg = <0x10000000 0x1000>;
interrupt-parent = <&gic>; // 指向GIC中断控制器
interrupts = <0 33 4>; // SPI类型,中断号33,高电平触发
};
通过本节的学习,你已经掌握了中断的基本概念、IRQ/FIQ的区别、中断在系统中的作用以及中断上下文的约束。这些知识是后续学习中断申请和设备树中断属性解析的基础。