3. DMA 传输模式详解
DMA 控制器支持多种传输模式,以适应不同硬件场景下的数据搬运需求。理解每种模式的触发条件、数据流向和适用场景,是进行驱动性能调优的基础。本章将逐一剖析四种核心模式:内存到内存(Mem2Mem)、内存到外设(Mem2Periph)、外设到内存(Periph2Mem)以及循环模式(Circular Mode)。
3.1 模式分类与核心差异
下表从数据源、目的地、触发方式与典型用途四个维度,对四种模式进行横向对比:
| 传输模式 | 数据源 | 目的地 | 触发方式 | 典型用途 |
|---|---|---|---|---|
| Mem2Mem | 内存(SRAM/DRAM) | 内存(SRAM/DRAM) | 软件请求(立即启动) | 内存拷贝、数据搬移、帧缓冲复制 |
| Mem2Periph | 内存(SRAM/DRAM) | 外设(UART/SPI/DAC) | 外设请求(TX 请求) | 发送数据、波形输出、显示刷新 |
| Periph2Mem | 外设(ADC/I2S/Camera) | 内存(SRAM/DRAM) | 外设请求(RX 请求) | 数据采集、音频录制、图像捕获 |
| Circular | 外设 ↔ 内存 | 内存 ↔ 外设 | 硬件自动循环 | 连续采样、环形缓冲、流式处理 |
调优要点: Mem2Mem 模式通常占用总线带宽最高,因为它需要同时从源地址读、向目的地址写,且无外设等待间隙。在系统负载较高时,应优先保证外设相关 DMA 通道的优先级。
3.2 内存到内存(Mem2Mem)模式
Mem2Mem 模式是最纯粹的 DMA 数据搬运模式。它不需要外设请求信号,一旦软件配置完成并启动,DMA 控制器会立即开始从源地址读取数据,并写入目的地址,直到传输计数归零。
关键特征:
- 软件触发: 无需等待硬件事件,启动即执行。
- 双总线占用: 读总线和写总线同时工作,对系统总线压力最大。
- 常用于: 内存拷贝(类似 memcpy)、图像数据格式转换、协议栈数据搬移。
驱动配置示例(STM32 HAL 风格):
// 配置 Mem2Mem 传输
DMA_HandleTypeDef hdma_mem2mem;
hdma_mem2mem.Instance = DMA1_Channel1;
hdma_mem2mem.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_mem2mem.Init.PeriphInc = DMA_PINC_ENABLE; // 源地址递增
hdma_mem2mem.Init.MemInc = DMA_MINC_ENABLE; // 目的地址递增
hdma_mem2mem.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_mem2mem.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_mem2mem.Init.Mode = DMA_NORMAL; // 单次传输
HAL_DMA_Init(&hdma_mem2mem);
// 启动传输(阻塞等待完成)
HAL_DMA_Start(&hdma_mem2mem, (uint32_t)src_buf, (uint32_t)dst_buf, 1024);
HAL_DMA_PollForTransfer(&hdma_mem2mem, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
3.3 内存到外设(Mem2Periph)模式
Mem2Periph 模式用于将内存中的数据发送到外设。DMA 控制器等待外设发出的“发送请求”(TX Request)信号,每次请求到来时,从内存读取一个数据单元并写入外设的数据寄存器。
关键特征:
- 外设触发: 传输节奏由外设的发送能力决定。
- 单总线占用: 读内存占用总线,写外设通常不占用系统总线(通过外设总线桥)。
- 常用于: UART 发送、SPI 主模式发送、DAC 波形输出。
常见陷阱: 当外设 FIFO 较浅(如 UART 只有 1 字节 FIFO)时,DMA 请求频率会非常高。如果 DMA 响应延迟过大,可能导致外设 FIFO 下溢(Underrun),产生发送错误。此时应启用 DMA 的“突发传输”模式或增大外设 FIFO 深度。
3.4 外设到内存(Periph2Mem)模式
Periph2Mem 是数据采集场景的核心模式。外设(如 ADC、I2S、Camera 接口)在准备好数据后,发出“接收请求”(RX Request),DMA 控制器将外设数据寄存器中的值读取并写入内存缓冲区。
关键特征:
- 外设触发: 数据到达即触发传输,无需 CPU 轮询。
- 单总线占用: 读外设不占用系统总线,写内存占用写总线。
- 常用于: ADC 连续采样、音频录制、摄像头数据捕获。
性能优化技巧: 对于高速外设(如 ADC 采样率 > 1Msps),建议使用 DMA 的“双缓冲”(Double Buffer)模式。当 DMA 正在填充缓冲区 A 时,CPU 可以安全处理缓冲区 B 的数据,避免数据覆盖或 Cache 一致性问题。
3.5 循环模式(Circular Mode)
循环模式是 Periph2Mem 或 Mem2Periph 的增强变体。当 DMA 传输完设定的数据量后,不会产生完成中断并停止,而是自动将地址指针重置到缓冲区起始位置,继续下一轮传输。这使得缓冲区形成一个逻辑上的环形队列。
关键特征:
- 自动回绕: 传输计数器到达零后自动重载,地址指针回到起点。
- 连续流式: 无需 CPU 干预即可实现无限连续传输。
- 常用于: 音频流、连续 ADC 采样、数字麦克风数据读取。
数据一致性风险: 在循环模式下,CPU 和 DMA 同时访问同一块缓冲区。必须使用“半传输中断”或“水位线中断”来区分当前 DMA 正在写入的区域和 CPU 可以安全读取的区域。否则可能读到不完整或脏数据。
3.6 模式选择决策流程
在实际驱动开发中,选择哪种 DMA 模式取决于数据流向和触发源。以下决策流程可以帮助快速确定模式:
- 判断数据源和目标:
- 如果源和目标都是内存 → Mem2Mem
- 如果源是内存,目标是外设 → Mem2Periph
- 如果源是外设,目标是内存 → Periph2Mem
- 判断是否需要连续流式传输:
- 如果是连续采样或流式数据 → 在 Periph2Mem 或 Mem2Periph 基础上启用 Circular Mode
- 如果是单次传输或批量处理 → 使用 Normal Mode
- 判断是否需要双缓冲:
- 如果 CPU 处理速度跟不上 DMA 填充速度 → 启用 Double Buffer(部分 DMA 控制器支持)
- 如果缓冲区足够大或处理速度足够快 → 单缓冲即可
性能调优建议: 在 Mem2Periph 和 Periph2Mem 模式下,合理设置 DMA 的“数据宽度”和“突发传输大小”可以显著降低总线占用率。例如,对于 16 位外设,使用半字传输(16-bit)而非字节传输(8-bit),可以减少一半的请求次数。