嵌入式Linux驱动开发:DMA与硬件加速器驱动实现 - 第1节

1.1 DMA 工作原理

直接存储器访问(Direct Memory Access, DMA)是一种允许硬件子系统(外设或内存)直接读写系统内存而无需 CPU 干预的数据传输技术。在嵌入式 Linux 系统中,DMA 是提升数据吞吐量、降低 CPU 负载的关键机制。

核心思想:DMA 控制器接管总线控制权,在源地址与目的地址之间建立一条硬件直通路径,由硬件完成数据搬运,CPU 仅在传输开始和结束时参与配置与中断处理。

DMA 传输的基本流程

  1. CPU 配置 DMA 控制器:设置源地址、目的地址、传输长度、传输方向、触发源等参数。
  2. DMA 控制器请求总线控制权:通过总线仲裁获得对系统总线的访问权限。
  3. DMA 执行数据传输:按配置的传输模式逐字节或逐字搬运数据,每传输一个单元自动更新地址计数器。
  4. 传输完成中断:当传输计数器归零时,DMA 控制器向 CPU 发送中断信号,通知传输结束。
  5. CPU 处理后续数据:CPU 在中断服务程序中处理已完成的数据缓冲区。
DMA 传输工作流程 CPU 配置 DMA DMA 请求总线 DMA 搬运数据 传输完成中断 CPU 处理数据 关键说明: • 整个传输过程中,CPU 不参与数据搬运,可执行其他任务。 • DMA 传输粒度可以是字节、半字(16位)或字(32位),取决于硬件设计。 • 总线仲裁机制确保 DMA 与 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 直接搬运更合适。
CPU 搬运 vs DMA 搬运 时序对比 CPU 搬运: CPU 100% 占用:搬运数据 → 无其他任务可执行 DMA 搬运: 配置 DMA DMA 硬件搬运数据(CPU 可执行其他任务或休眠) 中断处理 性能差异根源: • CPU 搬运:每传输一个字节都需要取指、译码、执行、访存,指令开销大。 • DMA 搬运:硬件状态机直接驱动总线,每个时钟周期可完成一次传输,无指令开销。

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 控制器自身具备总线主控能力且能持续驱动总线。

传输类型选择决策流程

  1. 判断数据源和目标:如果两端都是内存地址 → 选择 M2M;如果一端是外设地址 → 进入下一步。
  2. 判断数据流向:外设 → 内存 → P2M;内存 → 外设 → M2P。
  3. 判断触发方式:M2M 使用软件触发;P2M/M2P 使用硬件触发(外设请求信号)。
  4. 配置 DMA 通道:设置源地址递增/递减/固定、目的地址递增/递减/固定、传输宽度、突发传输大小等参数。
三种 DMA 传输类型架构图 内存 A(源) DMA 控制器 内存 B(目的) ① 内存到内存 (M2M) — 软件触发 外设(源) DMA 控制器 内存(目的) 外设请求信号(硬件触发) ② 外设到内存 (P2M) — 硬件触发 内存(源) DMA 控制器 外设(目的) 外设请求信号(硬件触发) ③ 内存到外设 (M2P) — 硬件触发

传输类型在 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 接收数据寄存器地址。