4. DMA传输模式:内存到内存、内存到外设、外设到内存、外设到外设

DMA控制器支持四种基本的数据传输模式,分别对应系统中不同的数据通路。理解这四种模式的触发机制、数据流向和适用场景,是配置DMA传输链路的基础。每种模式在总线仲裁、地址增减方式和传输完成标志上存在显著差异。

4.1 四种传输模式总览

下表从数据源、目的地、典型触发源和常用外设四个维度,对比四种DMA传输模式的核心特征:

传输模式 数据源 目的地 典型触发源 常用场景
内存→内存 SRAM / Flash SRAM 软件请求(立即触发) 数据搬移、缓冲区拷贝、CRC计算
内存→外设 SRAM 外设数据寄存器 外设发送请求(TXE / TXFIFO空) DAC波形输出、SPI发送、UART发送
外设→内存 外设数据寄存器 SRAM 外设接收请求(RXNE / RXFIFO非空) ADC采样、SPI接收、UART接收
外设→外设 外设A数据寄存器 外设B数据寄存器 源外设请求 定时器触发ADC、数字信号路由
关键概念:“内存→内存”模式不需要外设触发信号,DMA在配置完成后立即启动传输,适合批量数据预处理。其余三种模式均依赖外设硬件请求信号启动单次或突发传输。

4.2 内存到内存(M2M)

M2M模式是唯一由软件直接启动的传输模式。DMA控制器在使能通道后,立即从源地址读取数据并写入目的地址,无需等待外设请求。该模式常用于在SRAM不同区域之间搬运数据,或从Flash常量区拷贝初始化表到SRAM。

典型配置要点:

  • 源地址和目的地址均为内存地址,必须位于DMA可访问的总线矩阵内。
  • 地址增量通常设置为递增(Increment),源和目的均可独立配置。
  • 传输完成中断可用于通知CPU数据就绪。
  • 部分MCU要求M2M模式下必须使用字(Word)对齐访问。
// 伪代码:内存到内存搬运配置示例
DMA_Channel->CPAR  = (uint32_t)srcBuffer;   // 源地址:SRAM源缓冲区
DMA_Channel->CMAR  = (uint32_t)dstBuffer;   // 目的地址:SRAM目的缓冲区
DMA_Channel->CNDTR = 256;                   // 传输数据量:256个半字
DMA_Channel->CCR   = DMA_CCR_MEM2MEM       // 内存到内存模式
                   | DMA_CCR_MINC           // 目的地址递增
                   | DMA_CCR_PINC           // 源地址递增
                   | DMA_CCR_PSIZE_1        // 外设数据宽度:16位
                   | DMA_CCR_MSIZE_1;       // 内存数据宽度:16位
DMA_Channel->CCR  |= DMA_CCR_EN;           // 使能通道,立即启动传输
避坑提示:M2M模式下,DMA会连续占用总线直到传输完成。如果传输数据量较大(如超过1KB),CPU可能因总线延迟而出现响应滞后。建议将大块M2M传输拆分为多次小批量,或在传输期间使能CPU的指令缓存(I-Cache)和数据缓存(D-Cache)。

4.3 内存到外设(M2P)

M2P模式将内存中的预置数据发送到外设的数据寄存器。外设的发送请求信号(如UART的TXE、SPI的TXFIFO空、DAC的转换完成)作为DMA的触发源。每次外设请求到来,DMA从内存读取一个数据单元并写入外设寄存器。

典型应用:DAC波形输出

将波形表存放在SRAM中,DMA在定时器触发下逐点搬运到DAC的数据寄存器,实现无CPU干预的连续波形生成。

// 伪代码:DMA内存到外设(DAC波形输出)
#define WAVEFORM_SIZE  512
uint16_t waveTable[WAVEFORM_SIZE];  // 波形表,由CPU预填充

DMA_Channel->CPAR  = (uint32_t)&DAC->DHR12R1;  // 目的:DAC通道1数据保持寄存器
DMA_Channel->CMAR  = (uint32_t)waveTable;       // 源:波形表
DMA_Channel->CNDTR = WAVEFORM_SIZE;             // 传输点数
DMA_Channel->CCR   = DMA_CCR_CIRC              // 循环模式,波形连续输出
                   | DMA_CCR_MINC               // 源地址递增
                   | DMA_CCR_PSIZE_0            // 外设数据宽度:16位
                   | DMA_CCR_MSIZE_0;           // 内存数据宽度:16位
// 使能DMA请求映射,等待定时器触发

4.4 外设到内存(P2M)

P2M模式是最常用的数据采集模式。外设的接收请求信号(如UART的RXNE、SPI的RXFIFO非空、ADC的转换结束)触发DMA,将外设数据寄存器中的值搬运到内存缓冲区。该模式可大幅降低数据采集时的CPU中断频率。

典型应用:多通道ADC连续采样

  • ADC配置为扫描模式+连续转换,每次转换结束产生DMA请求。
  • DMA将ADC转换结果依次搬运到内存数组,数组大小等于通道数×采样次数。
  • 传输过半或完成时触发中断,CPU处理数据。
// 伪代码:DMA外设到内存(ADC多通道采样)
#define ADC_CHANNELS    4
#define SAMPLE_COUNT    128
uint16_t adcBuffer[ADC_CHANNELS * SAMPLE_COUNT];

DMA_Channel->CPAR  = (uint32_t)&ADC->DR;          // 源:ADC数据寄存器
DMA_Channel->CMAR  = (uint32_t)adcBuffer;          // 目的:采样缓冲区
DMA_Channel->CNDTR = ADC_CHANNELS * SAMPLE_COUNT;  // 总采样点数
DMA_Channel->CCR   = DMA_CCR_MINC                  // 目的地址递增
                   | DMA_CCR_CIRC                  // 循环模式,持续采集
                   | DMA_CCR_PSIZE_0               // 外设数据宽度:16位
                   | DMA_CCR_MSIZE_0               // 内存数据宽度:16位
                   | DMA_CCR_HTIE                  // 传输过半中断使能
                   | DMA_CCR_TCIE;                 // 传输完成中断使能

4.5 外设到外设(P2P)

P2P模式在两个外设的数据寄存器之间直接传输数据,不经过内存。该模式在部分MCU中受硬件限制(如DMA请求映射仅支持单向),但在支持双缓冲或硬件握手的外设组合中非常高效。

典型应用:定时器触发ADC + DMA直接路由到DAC

定时器产生PWM触发信号 → ADC启动转换 → ADC转换结束产生DMA请求 → DMA将ADC结果直接写入DAC数据寄存器 → DAC输出模拟电压。整个链路无需CPU参与,延迟极低。

硬件限制说明:并非所有MCU都支持P2P模式。部分DMA控制器要求源外设必须能产生DMA请求,且目的外设必须能接受DMA写入。实际使用前需查阅参考手册中“DMA request mapping”章节,确认外设对是否在支持的映射表中。

4.6 四种模式的数据流对比图

下图展示了四种DMA传输模式在系统总线上的数据流向和触发关系:

DMA四种传输模式数据流对比 DMA四种传输模式数据流对比 DMA 控制器 内存 (SRAM/Flash) 外设A (源) 外设B (目的) ① M2M:内存→内存 ② M2P:内存→外设 ③ P2M:外设→内存 ④ P2P:外设→外设 触发源说明 ① M2M:软件触发(配置后立即启动) ②③④:外设硬件请求触发(TXE/RXNE/转换结束等)

4.7 模式选择决策表

在实际项目中选择DMA传输模式时,可参考以下决策流程:

  1. 判断数据源和目的地类型:两者均为内存 → M2M;源为外设 → P2M或P2P;目的为外设 → M2P或P2P。
  2. 检查外设DMA请求映射:查阅MCU参考手册,确认源外设是否能产生DMA请求,目的外设是否能接受DMA写入。
  3. 评估是否需要循环模式:连续数据流(如音频、波形输出)应使能循环模式;单次批量传输使用正常模式。
  4. 考虑数据宽度对齐:源和目的的数据宽度应一致,否则DMA会自动进行对齐转换(部分MCU不支持非对齐宽度)。
工程经验:在调试初期,建议先用M2M模式验证DMA通道配置是否正确(如地址增量、数据宽度、传输完成中断),再切换到外设相关模式。这样可以隔离外设配置问题与DMA配置问题,降低调试复杂度。