1. DMA 基础概念

1.1 DMA 的定义

DMA(Direct Memory Access,直接存储器访问) 是一种允许硬件子系统(外设或内存控制器)在不经过 CPU 的情况下,直接与系统内存进行数据读写的技术。在嵌入式 Linux 系统中,DMA 控制器作为总线上的独立主设备,能够发起总线事务,完成数据搬运任务。

核心要点:DMA 的本质是“CPU 授权下的数据旁路传输”——CPU 只需配置传输参数,后续数据流动由 DMA 控制器自主完成,传输结束后通过中断通知 CPU。
对比维度 CPU 直接搬运 DMA 搬运
数据路径 外设 → CPU 寄存器 → 内存 外设 → 总线 → 内存(旁路 CPU)
CPU 占用 全程占用,无法执行其他任务 仅配置和收尾阶段介入
传输速率 受限于 CPU 指令执行速度 接近总线带宽,适合高速外设
适用场景 小数据量、低频操作 大数据块、流式数据、实时采集

1.2 DMA 与 CPU 的交互模式

DMA 与 CPU 的协同工作遵循“配置-启动-传输-通知”的四步模型。整个交互过程可抽象为以下阶段:

  1. CPU 配置阶段:CPU 设置 DMA 控制器的源地址、目的地址、传输长度、传输宽度、突发大小等参数。
  2. CPU 启动阶段:CPU 写 DMA 控制器的启动寄存器(或使能通道),DMA 控制器获得总线控制权。
  3. DMA 自主传输阶段:DMA 控制器在总线仲裁器授权下,逐字或逐块搬运数据,CPU 可并行执行其他任务。
  4. DMA 完成通知阶段:传输完成或出错时,DMA 控制器触发中断,CPU 响应中断进行后续处理(如数据校验、缓冲区切换)。
设计要点:在嵌入式 Linux 驱动中,DMA 传输通常与 CPU 的 cache 一致性管理紧密配合。DMA 直接操作物理内存,而 CPU 可能使用 cache 缓存同一区域,驱动需要通过 dma_map_single() / dma_sync_single_for_cpu() 等 API 保证数据一致性。
DMA 与 CPU 交互时序图 CPU DMA 控制器 内存/外设 ① 配置源/目的/长度 ② 写启动寄存器 ③ 读源数据(突发) ④ 写目的数据(突发) ⑤ 传输完成中断 CPU 在阶段③④可并行执行其他任务,仅在阶段①⑤参与 DMA 交互

1.3 DMA 在嵌入式系统中的作用

在嵌入式 Linux 系统中,DMA 承担着数据搬运加速器和CPU 负载卸载器的双重角色。其核心价值体现在以下方面:

  • 降低 CPU 占用率:高速外设(如千兆以太网、USB 3.0、MIPI CSI 摄像头)的数据吞吐可达数百 MB/s,若由 CPU 逐字搬运,CPU 将完全被数据移动占据,无法处理协议栈、控制逻辑等关键任务。DMA 将 CPU 从数据搬运中解放出来。
  • 提升数据传输吞吐量:DMA 控制器通常支持突发传输(burst transfer),一次总线事务可传输多个数据字,总线利用率远高于 CPU 单次 load/store 指令。
  • 支持后台数据流动:DMA 可在 CPU 执行中断服务程序或上下文切换时继续传输,实现“传输与计算重叠”。
  • 降低实时任务延迟:音频编解码、软件定义无线电等实时应用要求数据以固定间隔送达,DMA 的硬件定时触发模式可保证确定性延迟。
常见误区:DMA 并非“零开销”——每次 DMA 传输仍需 CPU 进行配置和中断处理,且 DMA 描述符的维护、cache 刷新操作都会消耗 CPU 周期。对于小于 64 字节的极短传输,CPU 直接搬运反而更快。
DMA 在嵌入式系统中的作用架构 CPU 核心 控制、协议栈、应用 DMA 控制器 数据搬运、通道管理 系统内存 DDR / SRAM 高速外设 以太网 / USB / 摄像头 低速外设 UART / SPI / I²C 存储设备 eMMC / NAND Flash 配置/中断 DMA 读写 数据请求 数据请求 数据请求 DMA 在嵌入式系统中的三大作用 ① CPU 负载卸载:将数据搬运从 CPU 转移到 DMA 控制器 ② 吞吐量提升:突发传输 + 总线级并行,接近硬件极限速率 ③ 实时性保障:硬件触发 + 描述符链,确定性延迟

1.4 DMA 传输的基本要素

一次完整的 DMA 传输由以下五个基本要素构成,驱动开发者在配置 DMA 时必须明确指定每一项:

要素 说明 典型配置值
源地址 数据读取的起始物理地址,可以是外设 FIFO 地址或内存缓冲区地址 0x5000_0000(外设基址)
目的地址 数据写入的目标物理地址 0x8000_0000(DDR 地址)
传输宽度 每次总线事务传输的数据位宽 8/16/32/64 位
突发长度 一次总线仲裁中连续传输的数据单元个数 4/8/16(AHB 总线典型值)
传输模式 单次传输、循环传输、描述符链传输 循环模式(音频流常用)
驱动开发提示:在 Linux DMA Engine 框架中,上述要素通过 struct dma_slave_config 和 struct dma_async_tx_descriptor 描述。配置时需特别注意源/目的地址的递增属性——外设 FIFO 通常地址固定(不递增),而内存缓冲区地址递增。
/* Linux DMA Engine 配置示例(伪代码) */
struct dma_slave_config config = {
    .direction = DMA_DEV_TO_MEM,        // 外设到内存方向
    .src_addr = 0x50000000,             // 外设 FIFO 地址
    .dst_addr = 0x80000000,             // 内存缓冲区地址
    .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
    .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
    .src_maxburst = 8,                  // 突发长度 8
    .dst_maxburst = 8,
};
dmaengine_slave_config(chan, &config);