嵌入式Linux驱动开发:DMA与硬件加速器驱动实现 - 第1节
1.1 DMA 工作原理
直接存储器访问(Direct Memory Access, DMA)是一种允许硬件子系统(外设或内存)直接读写系统内存而无需 CPU 干预的数据传输技术。在嵌入式 Linux 系统中,DMA 是提升数据吞吐量、降低 CPU 负载的关键机制。
核心思想:DMA 控制器接管总线控制权,在源地址与目的地址之间建立一条硬件直通路径,由硬件完成数据搬运,CPU 仅在传输开始和结束时参与配置与中断处理。
DMA 传输的基本流程
- CPU 配置 DMA 控制器:设置源地址、目的地址、传输长度、传输方向、触发源等参数。
- DMA 控制器请求总线控制权:通过总线仲裁获得对系统总线的访问权限。
- DMA 执行数据传输:按配置的传输模式逐字节或逐字搬运数据,每传输一个单元自动更新地址计数器。
- 传输完成中断:当传输计数器归零时,DMA 控制器向 CPU 发送中断信号,通知传输结束。
- CPU 处理后续数据:CPU 在中断服务程序中处理已完成的数据缓冲区。
1.2 DMA 与 CPU 数据搬运对比
理解 DMA 与 CPU 直接搬运数据的差异,是选择数据传输方式的基础。下表从多个维度对比两种方式:
| 对比维度 | CPU 直接搬运 | DMA 搬运 |
|---|---|---|
| 执行主体 | CPU 核心(软件循环) | DMA 控制器(硬件状态机) |
| CPU 占用率 | 100%(搬运期间无法执行其他任务) | 仅配置和中断处理阶段占用(通常 <5%) |
| 传输速率 | 受限于 CPU 指令执行速度 | 接近总线带宽,通常快 2~10 倍 |
| 适用场景 | 小数据量(<64 字节)、非实时 | 大数据块(≥256 字节)、高速外设 |
| 功耗 | 较高(CPU 持续活跃) | 较低(CPU 可进入低功耗模式) |
| 实现复杂度 | 简单,memcpy() 即可 | 需要驱动配置 DMA 引擎、处理描述符链 |
| 实时性 | 受中断和调度影响 | 硬件级确定性延迟 |
工程经验:在嵌入式 Linux 驱动中,对于 SPI、I2S、UART 等外设的批量数据传输,优先使用 DMA 方式。对于 GPIO 模拟时序或寄存器级别的短数据交换,CPU 直接搬运更合适。
1.3 DMA 传输类型
根据数据传输的源端和目的端类型,DMA 传输分为三种基本类型。理解这些类型是配置 DMA 引擎的前提。
三种传输类型详解
| 传输类型 | 源端 | 目的端 | 典型应用场景 | 是否需要外设触发 |
|---|---|---|---|---|
| 内存到内存 (M2M) | 内存缓冲区 A | 内存缓冲区 B | 数据拷贝、图像帧复制、协议栈缓冲区移动 | 否(软件触发启动) |
| 外设到内存 (P2M) | 外设数据寄存器(如 SPI RX FIFO) | 内存缓冲区 | ADC 采样数据读取、网络数据包接收、音频录音 | 是(外设请求信号触发) |
| 内存到外设 (M2P) | 内存缓冲区 | 外设数据寄存器(如 SPI TX FIFO) | 音频播放、LCD 显示数据刷新、PWM 波形数据输出 | 是(外设请求信号触发) |
注意:并非所有 DMA 控制器都支持内存到内存传输。部分低端 DMA 控制器仅支持外设相关传输(P2M 和 M2P),因为 M2M 传输需要 DMA 控制器自身具备总线主控能力且能持续驱动总线。
传输类型选择决策流程
- 判断数据源和目标:如果两端都是内存地址 → 选择 M2M;如果一端是外设地址 → 进入下一步。
- 判断数据流向:外设 → 内存 → P2M;内存 → 外设 → M2P。
- 判断触发方式:M2M 使用软件触发;P2M/M2P 使用硬件触发(外设请求信号)。
- 配置 DMA 通道:设置源地址递增/递减/固定、目的地址递增/递减/固定、传输宽度、突发传输大小等参数。
传输类型在 Linux 驱动中的体现
在 Linux DMA 引擎子系统中,传输类型通过 dma_transfer_direction 枚举表示:
enum dma_transfer_direction {
DMA_MEM_TO_MEM, /* 内存到内存 */
DMA_MEM_TO_DEV, /* 内存到外设 */
DMA_DEV_TO_MEM, /* 外设到内存 */
DMA_DEV_TO_DEV, /* 外设到外设(较少见) */
};
驱动开发要点:在申请 DMA 通道时,需要向 DMA 引擎指定传输方向和支持的传输类型。例如,一个 SPI 接收驱动应申请支持
DMA_DEV_TO_MEM 的通道,并配置外设地址为 SPI 接收数据寄存器地址。