DMA传输与I2C驱动移植适配工程实践 - 第1节
1.1 DMA 工作原理
直接存储器访问(Direct Memory Access, DMA)是一种允许硬件设备直接与系统内存交换数据而无需 CPU 逐字节干预的机制。在嵌入式系统中,DMA 控制器作为总线上的独立主设备,接管数据搬运任务,使 CPU 能够并行处理计算与控制逻辑。
1.1.1 传统 PIO 模式 vs DMA 模式
| 对比维度 | PIO(Programmed I/O) | DMA(Direct Memory Access) |
|---|---|---|
| 数据搬运主体 | CPU 逐字/逐字节搬运 | DMA 控制器独立搬运 |
| CPU 占用率 | 高(传输期间被完全占用) | 低(仅配置和收尾阶段介入) |
| 传输速率 | 受限于 CPU 指令执行速度 | 接近总线带宽,可达数百 MB/s |
| 适用场景 | 小数据量、低频外设 | 大数据块、高速外设(I2C/SPI/UART/ADC) |
| 实时性影响 | 阻塞 CPU,影响中断响应 | 释放 CPU,保障系统实时性 |
1.1.2 DMA 传输基本流程
- 请求阶段:外设(如 I2C 控制器)产生 DMA 请求信号,通知 DMA 控制器需要数据传输。
- 仲裁阶段:DMA 控制器向总线仲裁器申请总线控制权,获得授权后成为总线主设备。
- 传输阶段:DMA 控制器直接在外设寄存器与内存之间搬运数据,每次传输后更新地址和计数器。
- 终止阶段:当传输计数器归零或遇到错误条件,DMA 控制器释放总线,并向 CPU 发送完成中断。
1.2 DMA 传输模式
根据应用场景的不同,DMA 控制器支持多种传输模式。嵌入式 Linux 驱动开发中,最常用的三种模式为:单次传输(Single Transfer)、循环传输(Circular / Auto-Reload)和链表传输(Scatter-Gather / Linked List)。
1.2.1 单次传输模式
单次传输是最基本的 DMA 模式。配置完成后,DMA 控制器执行一次指定长度的数据传输,传输结束后自动停止,并产生完成中断。若需再次传输,必须由 CPU 重新配置。
- 特点:一次性、确定性、低资源占用。
- 适用场景:单次 ADC 采样、单帧图像捕获、一次性内存拷贝。
- 驱动接口:dmaengine 中的
dma_async_issue_pending()触发一次。
1.2.2 循环传输模式
循环传输模式下,DMA 控制器在完成一次传输后自动重新加载初始配置,立即开始下一次传输,形成连续的数据流。该模式通常用于需要持续数据采集或输出的场景。
- 特点:自动重载、无中断开销、持续传输。
- 适用场景:音频流播放/录制、连续 ADC 数据采集、PWM 波形生成。
- 驱动接口:设置
DMA_PREP_CIRCULAR_LIST标志位。
1.2.3 链表传输模式(Scatter-Gather DMA)
链表传输模式通过硬件链表结构描述多个不连续的内存块,DMA 控制器自动遍历链表,依次完成各块的传输。这是最灵活、最强大的 DMA 模式,也是 Linux DMA Engine 框架的核心能力之一。
| 特性 | 单次传输 | 循环传输 | 链表传输 |
|---|---|---|---|
| 传输次数 | 1 次 | 无限次(自动重载) | N 次(链表节点数) |
| 内存连续性 | 要求连续 | 要求连续 | 支持不连续(Scatter-Gather) |
| CPU 干预频率 | 每次传输后需重新配置 | 仅初始配置 | 仅初始配置链表 |
| 中断次数 | 1 次 | 0 次(或按需) | 1 次(整个链表完成) |
| 典型应用 | 单次 SPI 读写 | 音频环形缓冲区 | 网络数据包、文件系统 I/O |
1.3 DMA 在嵌入式系统中的角色
在嵌入式 Linux 系统中,DMA 已从“可选加速器”演变为“核心基础设施”。以下从三个维度阐述 DMA 的关键作用:
1.3.1 性能维度:释放 CPU 算力
以 I2C 总线传输 4KB 数据为例:
- 无 DMA(PIO 模式):CPU 每字节需执行 10~15 条指令,4KB 数据约需 40,000~60,000 条指令,期间 CPU 完全被占用。
- 有 DMA:CPU 仅需约 50 条指令完成配置,随后可执行其他任务,传输由 DMA 控制器独立完成。
- 实测数据:在 400kHz I2C 总线上,DMA 模式可将 CPU 占用率从 85% 降至 5% 以下。
1.3.2 实时性维度:降低中断延迟
DMA 通过以下机制改善系统实时性:
- 批量中断:DMA 完成整个数据块后才产生一次中断,而非每字节中断一次。
- 优先级管理:DMA 请求优先级可独立配置,高优先级外设(如音频)可抢占低优先级传输。
- 零拷贝:结合 MMU,DMA 可直接在用户空间缓冲区与设备之间传输,避免数据拷贝。
1.3.3 功耗维度:降低系统能耗
在电池供电的嵌入式设备中,DMA 对功耗的影响尤为显著:
| 场景 | CPU 活跃时间 | 系统功耗 | 续航影响 |
|---|---|---|---|
| PIO 模式传输 1MB 数据 | ~50 ms | ~250 mW | 高 |
| DMA 模式传输 1MB 数据 | ~0.5 ms(配置+中断) | ~80 mW | 低 |
| CPU 深度睡眠 + DMA 后台传输 | 0 ms(CPU 休眠) | ~15 mW | 极低 |
1.3.4 软件架构中的 DMA 抽象层
在 Linux 内核中,DMA 通过分层架构为驱动开发者提供统一接口:
驱动开发者通常工作在“内核子系统”和“DMA Engine 核心层”之间,通过标准 API 完成 DMA 通道申请、配置、提交和回收,无需关心底层 DMA 控制器的具体寄存器操作。