1. DMA 基础概念

1.1 DMA 工作原理

直接存储器访问(Direct Memory Access, DMA)是一种允许硬件子系统直接读写系统内存的机制,无需 CPU 参与数据传输的每个字节。DMA 控制器作为独立的总线主控设备,在系统总线上与 CPU 并行工作,显著提升数据搬运效率。

核心思想:CPU 只需配置传输参数(源地址、目的地址、传输长度、触发条件),DMA 控制器自动完成剩余数据搬运,传输完成后通过中断通知 CPU。
DMA 工作原理框图 CPU DMA 控制器 系统内存 外设(SPI/UART) 系统总线(AHB/AXI) 配置传输参数 数据搬运 数据搬运 DMA 请求 传输完成中断 工作流程 ① CPU 配置 DMA 寄存器 ② 外设发起 DMA 请求 ③ DMA 自动搬运数据 ④ 传输完成触发中断

1.2 DMA 与 CPU 数据搬运对比

传统 CPU 使用 PIO(Programmed I/O)模式逐字节搬运数据,而 DMA 将搬运任务卸载给专用硬件。下表从多个维度对比两种方式:

对比维度 CPU 直接搬运(PIO) DMA 搬运
CPU 占用率 100%(搬运期间无法执行其他任务) 仅配置和中断处理阶段占用(通常 <5%)
吞吐量 受限于 CPU 主频和指令执行速度 可达数 Gbps(取决于总线带宽)
延迟 低(无额外硬件调度开销) 中等(需 DMA 请求-响应握手)
适用场景 小数据量(<64 字节)、控制寄存器访问 大数据块(≥256 字节)、流式外设
硬件复杂度 无需额外 DMA 控制器 需要 DMA 引擎和中断控制器
功耗 高(CPU 持续活跃) 低(CPU 可进入低功耗模式)
工程经验:在嵌入式 Linux 系统中,当单次传输量超过 256 字节时,DMA 的收益开始显著。对于小于 64 字节的短数据,PIO 模式因省去 DMA 配置开销反而更快。
CPU vs DMA 数据搬运对比 CPU 搬运(PIO) 1. CPU 读取源数据 → 寄存器 2. CPU 写入寄存器 → 目的地址 3. 重复 1-2 直到传输完成 DMA 搬运 1. CPU 配置 DMA 描述符 2. DMA 引擎自动搬运数据 3. CPU 处理完成中断 CPU 占用 ~100% CPU 占用 ~5% 吞吐量 低 吞吐量 高 功耗 高 功耗 低

1.3 DMA 传输类型

根据数据流向和参与方,DMA 传输分为三种基本类型。每种类型在 Linux 内核中有不同的驱动实现路径和硬件配置方式。

1.3.1 内存到内存(Memory-to-Memory)

数据在系统内存的两个区域之间搬运,不涉及外设。典型应用包括:帧缓冲拷贝、加密/校验前的数据整理、用户态与内核态缓冲区同步。

/* Linux 内核 dmaengine API 示例:内存到内存传输 */
struct dma_chan *chan;
struct dma_async_tx_descriptor *tx;
dma_cookie_t cookie;

chan = dma_request_chan(dev, "memcpy");
tx = dmaengine_prep_dma_memcpy(chan, dst_phys, src_phys, size, DMA_CTRL_ACK);
cookie = dmaengine_submit(tx);
dma_async_issue_pending(chan);
注意:内存到内存 DMA 需要硬件支持。部分低端 DMA 控制器仅支持外设触发传输,无法发起内存到内存搬运。在申请通道前应通过 dma_get_slave_caps() 检查能力位。

1.3.2 内存到外设(Memory-to-Device)

数据从系统内存传输到外设的 FIFO 或寄存器。典型场景:LCD 显示数据刷新、音频播放 DMA 输出、网络 TX 描述符填充。

参数 说明 典型值
源地址 内存缓冲区物理地址 0x4800_0000
目的地址 外设数据寄存器地址 0x01C2_0000 (SPI_TX)
传输宽度 单次总线传输的数据位宽 8/16/32 位
突发长度 连续传输次数(提高总线利用率) 4/8/16
流控方式 由 DMA 还是外设控制传输节奏 外设流控(推荐)

1.3.3 外设到内存(Device-to-Memory)

数据从外设 FIFO 读取到系统内存。典型场景:摄像头传感器数据采集、音频录音 DMA 输入、网络 RX 数据接收、UART 批量接收。

三种 DMA 传输类型 内存 → 内存 源: 内存缓冲区 A 目的: 内存缓冲区 B 内存 → 外设 源: 内存数据缓冲区 目的: 外设 TX 寄存器 外设 → 内存 源: 外设 RX 寄存器 目的: 内存接收缓冲区 典型应用场景 • 内存→内存:帧缓冲拷贝、数据校验、加密预处理、用户态/内核态数据交换 • 内存→外设:LCD 显示刷新、音频播放、SPI/I2C 发送、网络 TX • 外设→内存:摄像头采集、音频录音、UART 批量接收、网络 RX

1.4 传输类型选择决策

在实际驱动开发中,选择哪种 DMA 传输类型取决于外设硬件设计、系统带宽需求和实时性要求。以下决策树可辅助判断:

  1. 数据是否涉及外设?
    • 否 → 使用内存到内存(memcpy 通道)
    • 是 → 进入下一步
  2. 数据流向?
    • 内存 → 外设 → 使用内存到外设(slave TX 通道)
    • 外设 → 内存 → 使用外设到内存(slave RX 通道)
  3. 是否需要循环传输?
    • 是(如音频环形缓冲区)→ 配置 DMA 循环模式(DMA_CYCLIC)
    • 否 → 配置单次传输模式
关键结论:DMA 的本质是将 CPU 从繁重的数据搬运中解放出来。理解三种传输类型的硬件特性和 Linux dmaengine 框架的对应关系,是后续章节编写 DMA 驱动的基石。在嵌入式 Linux 系统中,dmaengine 子系统提供了统一的 API 抽象,屏蔽了不同 SoC DMA 控制器的硬件差异。