2. DMA 基础概念

直接存储器访问(Direct Memory Access, DMA)是嵌入式系统中用于在无需 CPU 持续干预的情况下,高效搬运数据的硬件引擎。理解 DMA 的工作原理、传输类型及其与 CPU 的协同机制,是掌握 RAW 格式输出与缓冲区管理的前提。

2.1 DMA 工作原理

DMA 控制器作为总线上的独立主设备,在 CPU 配置完成后接管总线控制权,自动完成数据从源地址到目的地址的搬运。其核心工作流程如下:

  1. CPU 配置 DMA 通道:设置源地址、目的地址、传输长度、传输宽度及触发源。
  2. DMA 请求触发:外设事件(如 ADC 转换完成、UART 接收非空)或软件触发启动传输。
  3. 总线仲裁与传输:DMA 向总线仲裁器申请总线控制权,获得授权后执行单次或突发传输。
  4. 地址递增与计数:每传输一个单元,源/目的地址按设定模式递增或保持不变,传输计数器递减。
  5. 传输完成中断:计数器归零时,DMA 向 CPU 发出完成中断,或触发链式传输。
关键指标: DMA 传输效率取决于总线带宽、突发传输大小(Burst Size)以及通道优先级仲裁策略。在 RAW 数据流场景中,通常配置为循环模式(Circular Mode)以避免频繁重配。
DMA 单次传输工作流程 CPU 配置 DMA 请求触发 总线仲裁 数据传输 计数递减 完成中断 循环模式(Circular Mode)下,计数器归零后自动重载初始值,无需 CPU 重新配置

2.2 DMA 传输类型

根据源和目的的设备类型,DMA 传输分为三种基本模式。在 RAW 格式输出场景中,外设到内存和内存到外设是最常用的两种类型。

传输类型 源设备 目的设备 典型应用 RAW 场景相关性
内存到内存 SRAM / Flash SRAM 数据拷贝、缓冲区整理、CRC 计算 低:用于 RAW 数据预处理(如像素重排)
外设到内存 ADC / SPI / I2S / DCMI SRAM 传感器数据采集、音频录制、图像捕获 高:RAW 数据从传感器搬入缓冲区
内存到外设 SRAM DAC / SPI / I2S / LCD 波形输出、音频播放、显示刷新 高:RAW 数据从缓冲区输出到外设
实战提示: 在 RAW 图像传感器驱动中,通常使用双 DMA 通道:通道 1 负责将传感器数据(外设到内存)搬入 ping-pong 缓冲区,通道 2 负责将处理后的 RAW 数据(内存到外设)输出到显示或存储接口。两个通道可并行工作,最大化吞吐量。
DMA 三种传输类型架构 内存 → 内存 源内存 目的内存 DMA 控制器 DMA 外设 → 内存 外设 (ADC/SPI) 内存 DMA 控制器 DMA 内存 → 外设 内存 外设 (DAC/SPI) DMA 控制器 DMA RAW 输出场景:外设→内存 用于采集,内存→外设 用于输出,内存→内存 用于像素格式转换

2.3 DMA 与 CPU 的协同

DMA 并非完全取代 CPU,而是将数据搬运的重复性工作卸载到专用硬件上,使 CPU 能够专注于控制逻辑、协议解析和算法处理。协同工作的核心原则如下:

  • 配置阶段:CPU 负责初始化 DMA 通道、设置传输参数、注册完成回调。
  • 传输阶段:DMA 独立搬运数据,CPU 可执行其他任务或进入低功耗模式。
  • 同步阶段:通过中断或轮询标志位,CPU 感知传输完成并处理数据。
  • 冲突避免:CPU 和 DMA 同时访问同一内存区域时,需通过互斥机制(如关中断、乒乓缓冲区)防止数据竞争。
常见陷阱: 当 CPU 和 DMA 同时访问同一外设 FIFO 或内存地址时,可能产生总线冲突或数据一致性问题。解决方案包括:使用 DMA 的循环模式配合双缓冲区(Ping-Pong Buffer),或启用 CPU 缓存一致性协议(如 Cortex-M 的 D-Cache 清洁/无效操作)。
DMA 与 CPU 协同工作时序 CPU 配置 DMA 执行其他任务 / 休眠 中断处理 数据处理 DMA 等待触发 数据传输中 空闲 等待触发 DMA 完成中断 协同要点 1. CPU 配置完成后立即释放总线,DMA 独立传输,CPU 可进入休眠或执行其他任务。 2. DMA 传输完成触发中断,CPU 响应中断并处理数据,DMA 进入空闲或等待下一轮触发。 3. 在 RAW 数据流中,建议使用 DMA 循环模式 + 双缓冲区,减少中断频率,提升吞吐量。

2.4 关键配置参数

在实际驱动开发中,DMA 初始化涉及以下核心参数,这些参数直接影响 RAW 数据流的稳定性和效率:

参数 选项 RAW 场景推荐 说明
传输宽度 8-bit / 16-bit / 32-bit 匹配传感器位宽(如 10-bit 用 16-bit) 宽度不匹配时需填充或截断
突发大小 1 / 4 / 8 / 16 拍 4 或 8 拍(平衡总线占用与延迟) 突发越大,总线占用时间越长,但传输效率越高
地址模式 递增 / 递减 / 固定 源固定(外设 FIFO),目的递增(内存) 外设 FIFO 地址固定,内存缓冲区地址递增
循环模式 启用 / 禁用 启用(连续 RAW 流) 计数器归零后自动重载,无需 CPU 重配
中断使能 完成中断 / 半传输中断 / 错误中断 完成中断 + 半传输中断(乒乓切换) 半传输中断用于切换缓冲区,避免数据覆盖
实战代码片段: 以下为 STM32 HAL 库中配置 DMA 用于 RAW 数据采集的典型初始化序列(外设到内存,循环模式):
// DMA 句柄定义
DMA_HandleTypeDef hdma_raw_rx;

// 配置 DMA 通道
hdma_raw_rx.Instance = DMA1_Channel1;
hdma_raw_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;      // 外设到内存
hdma_raw_rx.Init.PeriphInc = DMA_PINC_DISABLE;           // 外设地址固定
hdma_raw_rx.Init.MemInc = DMA_MINC_ENABLE;               // 内存地址递增
hdma_raw_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; // 16-bit
hdma_raw_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_raw_rx.Init.Mode = DMA_CIRCULAR;                    // 循环模式
hdma_raw_rx.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_raw_rx);

// 链接 DMA 到外设(如 ADC 或 DCMI)
__HAL_LINKDMA(&hraw_sensor, hdma_rx, hdma_raw_rx);