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、音频录制、摄像头捕获。
DMA 三种传输类型架构图 DMA 控制器 系统内存 外设 FIFO 目标内存 外设数据源 M2M:内存→DMA→目标内存 | M2D:内存→DMA→外设 | D2M:外设→DMA→内存 箭头方向表示数据流,DMA 控制器作为总线主设备驱动传输

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 传递给底层硬件。合理设置突发长度和传输宽度可以显著提高总线利用率。