4. DMA传输类型:内存到内存、内存到外设、外设到内存、循环传输

DMA(Direct Memory Access)控制器作为系统总线上的主设备,其核心价值在于无需CPU干预即可完成数据搬运。根据数据源与目的地的不同组合,DMA传输可划分为四种基本类型。理解每种类型的触发机制、传输方向、适用场景及配置要点,是驱动开发者设计高效数据通路的基础。

4.1 传输类型总览

下表从传输方向、典型触发源、硬件路径和常见应用四个维度,对四种DMA传输类型进行对比:

传输类型 数据方向 典型触发源 硬件路径 典型应用
内存到内存 (M2M) 内存 → 内存 软件请求 系统总线 帧缓冲拷贝、数据搬移、内存清零
内存到外设 (M2P) 内存 → 外设 DMA请求或外设就绪 系统总线 → 外设总线 LCD显示刷新、DAC输出、SPI发送
外设到内存 (P2M) 外设 → 内存 外设硬件请求 外设总线 → 系统总线 ADC采样、UART接收、摄像头数据捕获
循环传输 (Circular) 双向或单向循环 硬件事件或定时器 系统总线 / 外设总线 音频流、连续数据采集、环形缓冲区
驱动开发提示: 在Linux DMA引擎框架中,dma_transfer_type枚举定义了DMA_MEM_TO_MEM、DMA_MEM_TO_DEV、DMA_DEV_TO_MEM和DMA_CYCLIC四种类型。循环传输在音频子系统中使用最为广泛,通常与dmaengine_prep_dma_cyclic()接口配合使用。

4.2 内存到内存传输 (M2M)

内存到内存传输是最纯粹的DMA搬运模式,源地址和目标地址均位于系统内存空间。该模式通常由软件显式启动,不需要外设硬件信号的参与。

4.2.1 工作原理

M2M传输的典型流程如下:

  1. CPU配置DMA控制器:源地址、目标地址、传输宽度、传输计数。
  2. CPU写入DMA使能寄存器或触发寄存器,启动传输。
  3. DMA控制器通过系统总线读取源内存数据,写入目标内存。
  4. 传输完成后,DMA控制器产生中断通知CPU。
关键特性: M2M传输不需要外设硬件握手信号,完全由软件控制。在Linux中,dma_async_memcpy_buf_to_buf()是典型的M2M封装接口。该模式常用于帧缓冲的拷贝、内存区域清零或数据格式转换前的搬移。

4.2.2 内存到内存传输结构图

内存到内存 (M2M) DMA传输结构 源内存 数据缓冲区 A 地址: 0x8000_0000 目标内存 数据缓冲区 B 地址: 0x9000_0000 DMA 控制器 源: 0x8000_0000 → 目标: 0x9000_0000 CPU 发起传输请求 传输完成中断

4.3 内存到外设传输 (M2P)

内存到外设传输将系统内存中的数据发送到外设设备,例如将帧缓冲数据发送到LCD控制器,或将音频样本发送到I2S DAC。该模式需要外设提供就绪信号或DMA请求信号来协调传输节奏。

4.3.1 硬件握手与流控

M2P传输的典型硬件连接包括:

  • DMA请求线 (DMA_REQ): 外设向DMA控制器发出的传输请求信号。
  • DMA应答线 (DMA_ACK): DMA控制器向外设发出的应答信号。
  • 外设就绪信号: 部分外设通过状态寄存器指示是否可接收数据。
常见陷阱: 在M2P传输中,如果外设的FIFO深度较小且DMA响应延迟较大,可能导致外设FIFO下溢(Underflow)。驱动开发时应根据外设FIFO大小和DMA延迟合理设置突发传输大小(Burst Size)和阈值(Threshold)。

4.3.2 内存到外设传输流程图

内存到外设 (M2P) DMA传输流程 系统内存 待发送数据 DMA 控制器 读取 → 转发 外设 (LCD/SPI) FIFO → 输出 DMA_REQ / DMA_ACK 外设就绪信号 传输完成中断

4.4 外设到内存传输 (P2M)

外设到内存传输是数据采集系统的核心,外设(如ADC、UART、摄像头接口)产生数据后通过DMA直接存入内存,无需CPU逐字节读取。该模式对实时性和数据完整性要求较高。

4.4.1 配置要点

配置参数 说明 典型值
源地址 外设数据寄存器地址(固定) 0x01C2_6000 (UART_RX)
目标地址 内存缓冲区地址(递增) 0x7000_0000
传输宽度 每次传输的数据位宽 8/16/32 bit
突发长度 连续传输的数据单元数 4/8/16
FIFO阈值 触发DMA请求的FIFO水位 半满 / 1/4满
驱动实现示例: 在Linux中,P2M传输通常通过dmaengine_prep_slave_sg()或dmaengine_prep_dma_cyclic()接口配置。对于UART接收,典型的DMA描述符配置如下:
struct dma_async_tx_descriptor *txd;
struct dma_slave_config config;

config.direction = DMA_DEV_TO_MEM;
config.src_addr = uart_base + UART_RX_REG;
config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
config.src_maxburst = 4;  /* 每次请求传输4字节 */

dmaengine_slave_config(chan, &config);
txd = dmaengine_prep_slave_sg(chan, sgt, nents, DMA_DEV_TO_MEM, flags);

4.4.2 外设到内存传输结构图

外设到内存 (P2M) DMA传输结构 外设 (ADC/UART) 数据寄存器 FIFO → 触发DMA_REQ DMA 控制器 接收 → 写入内存 地址递增模式 系统内存 数据缓冲区 地址: 0x7000_0000 DMA_REQ 硬件触发 传输完成中断

4.5 循环传输 (Circular / Cyclic)

循环传输是DMA的一种特殊模式,传输完成后自动重新加载初始配置,形成无限循环的数据流。该模式特别适合音频播放/录音、连续数据采集等需要稳定、无中断数据流的场景。

4.5.1 循环传输的核心机制

  • 环形缓冲区: 内存中预先分配一块连续区域,DMA在传输到末尾后自动回绕到起始地址。
  • 双缓冲/乒乓缓冲: 将环形缓冲区分为两个半区,CPU处理一个半区时DMA填充另一个半区。
  • 周期中断: 每完成一个半区或指定大小的传输,DMA产生中断通知CPU处理数据。
  • 无CPU干预: 配置完成后,DMA独立运行,CPU仅在周期中断时处理数据。
音频驱动中的循环传输: 在ALSA音频驱动中,snd_pcm_period_elapsed()回调由DMA周期中断触发。典型的配置是:缓冲区大小=64KB,周期大小=8KB,DMA每传输8KB产生一次中断,CPU在中断中处理音频数据并更新指针。

4.5.2 循环传输环形缓冲区结构图

循环传输 (Circular) 环形缓冲区结构 环形缓冲区 (Ring Buffer) — 64KB 半区 A (32KB) DMA正在写入 / CPU正在处理 半区 B (32KB) CPU正在处理 / DMA正在写入 DMA 控制器 (循环模式) 传输完成 → 自动重载 → 继续传输 每完成一个半区 → 产生周期中断 → CPU处理数据 → 更新指针

4.6 四种传输类型的对比与选型

在实际驱动开发中,选择合适的DMA传输类型直接影响系统性能和资源利用率。下表从多个维度进行对比:

对比维度 M2M M2P P2M Circular
触发方式 软件触发 外设请求 外设请求 硬件事件 / 定时器
地址模式 源递增 → 目标递增 源固定 → 目标递增 源固定 → 目标递增 源/目标循环回绕
中断频率 传输完成一次 传输完成一次 传输完成一次 每个周期中断
CPU负载 低(仅启动和完成) 低 低 极低(仅周期处理)
典型延迟 取决于内存带宽 取决于外设速度 取决于外设速度 取决于周期大小
适用场景 数据搬移、拷贝 显示、音频输出 数据采集、接收 音频流、连续采样
选型注意事项:
  • M2M传输虽然简单,但会占用系统总线带宽,在高速系统中应避免与关键外设争抢总线。
  • M2P和P2M传输需要正确配置外设的DMA请求信号极性、FIFO阈值和突发大小,否则可能导致数据丢失。
  • 循环传输的周期大小选择至关重要:周期太小会导致频繁中断,增加CPU负载;周期太大会增加数据延迟。
  • 部分DMA控制器不支持硬件循环模式,此时需要在驱动中通过软件重载描述符来模拟循环传输。

4.7 本章小结

DMA的四种基本传输类型构成了嵌入式系统中数据搬运的基础框架:

  • 内存到内存 (M2M) 提供纯软件触发的数据搬移能力,适用于帧缓冲拷贝和内存初始化。
  • 内存到外设 (M2P) 实现从系统内存到外设的数据输出,是显示和音频输出的核心。
  • 外设到内存 (P2M) 完成从外设到系统内存的数据采集,是传感器和通信接口的基础。
  • 循环传输 (Circular) 通过环形缓冲区和自动重载机制,实现无中断的连续数据流。