1. DMA 基础概念
1.1 DMA 定义
DMA(Direct Memory Access,直接存储器访问)是一种允许硬件子系统直接读写系统内存的机制,无需 CPU 参与数据传输的每个字节。DMA 控制器作为独立的总线主设备,在 CPU 初始化传输参数后,自动完成数据搬运,仅在传输完成时通过中断通知 CPU。
核心思想:将 CPU 从繁重的数据拷贝任务中解放出来,使其专注于计算与控制逻辑,从而提升系统整体吞吐量。
1.2 DMA 与 CPU 数据传输对比
传统 PIO(Programmed I/O)模式下,CPU 逐字搬运数据,占用大量指令周期;DMA 模式下,CPU 仅需配置传输描述符,后续工作由 DMA 引擎独立完成。下表从多个维度对比两种方式:
| 对比维度 | CPU 直接传输(PIO) | DMA 传输 |
|---|---|---|
| CPU 占用率 | 100%(逐字搬运) | 仅初始化 + 完成中断 |
| 传输速率 | 受限于 CPU 主频与指令吞吐 | 接近总线带宽,支持突发传输 |
| 适用场景 | 小数据量(< 64 字节) | 大数据块(≥ 1 KB) |
| CPU 可并行性 | 无法并行,CPU 被完全占用 | CPU 可执行其他任务或进入低功耗 |
| 硬件复杂度 | 无需额外控制器 | 需要 DMA 控制器 + 描述符内存 |
| 延迟 | 低(立即执行) | 中等(配置 + 仲裁延迟) |
工程经验:在嵌入式 Linux 驱动中,对于 SPI/I2C 等低速外设的小数据量传输(如寄存器读写),PIO 反而更高效;对于网络帧、音频流、视频帧等大块数据,必须使用 DMA 才能达到线速。
1.3 DMA 传输类型
根据数据流向和参与方,DMA 传输分为三种基本类型。理解这些类型是配置 DMA 引擎接口的前提。
- 内存到内存(Memory-to-Memory, M2M):源和目标均为系统内存,用于数据拷贝、缓冲区移动、内存清零等。典型场景:
memcpy的硬件加速、帧缓冲拷贝。 - 内存到外设(Memory-to-Device, M2D):数据从内存发送到外设 FIFO 或寄存器,用于发送数据。典型场景:以太网 TX、音频播放、LCD 显示刷新。
- 外设到内存(Device-to-Memory, D2M):数据从外设 FIFO 或寄存器读入内存,用于接收数据。典型场景:以太网 RX、音频录制、摄像头捕获。
1.4 传输类型选择依据
在实际驱动开发中,选择哪种传输类型取决于外设硬件设计、数据方向和性能需求。下表总结了典型场景与推荐类型:
| 应用场景 | 推荐传输类型 | 原因 |
|---|---|---|
| 音频播放(I2S 输出) | M2D | 内存中的 PCM 数据连续发送到 I2S TX FIFO |
| 音频录制(I2S 输入) | D2M | I2S RX FIFO 数据连续写入内存环形缓冲区 |
| 以太网数据包发送 | M2D | 描述符指向的内存数据搬运到 MAC TX 引擎 |
| 以太网数据包接收 | D2M | MAC RX 引擎将数据写入内存描述符缓冲区 |
| 帧缓冲拷贝 / 图像旋转 | M2M | 两个内存区域之间的批量数据搬运 |
| SPI Flash 读写 | M2D / D2M | SPI 控制器通常支持双向 DMA 通道 |
注意:并非所有 DMA 控制器都支持 M2M 传输。部分嵌入式 SoC 的 DMA 仅支持外设相关传输(M2D/D2M),M2M 需要通过外设触发或使用通用 DMA 通道模拟。在移植 Linux DMA 引擎驱动时,务必查阅芯片参考手册确认支持能力。
1.5 DMA 传输的关键参数
无论哪种传输类型,DMA 引擎接口都需要配置以下核心参数:
- 源地址:数据起始地址(内存地址或外设地址)。
- 目标地址:数据写入地址(内存地址或外设地址)。
- 传输宽度:单次传输的数据宽度(8/16/32/64 位)。
- 突发长度:连续传输的数据单元个数(如 4/8/16 次突发)。
- 地址增量模式:源/目标地址是否自动递增(内存侧通常递增,外设侧通常固定)。
- 传输总字节数:一次 DMA 请求搬运的数据总量。
优化提示:在 Linux DMA 引擎驱动中,上述参数通过
struct dma_slave_config 和 struct dma_async_tx_descriptor 传递给底层硬件。合理设置突发长度和传输宽度可以显著提高总线利用率。