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、数字信号路由 |
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; // 使能通道,立即启动传输
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参与,延迟极低。
4.6 四种模式的数据流对比图
下图展示了四种DMA传输模式在系统总线上的数据流向和触发关系:
4.7 模式选择决策表
在实际项目中选择DMA传输模式时,可参考以下决策流程:
- 判断数据源和目的地类型:两者均为内存 → M2M;源为外设 → P2M或P2P;目的为外设 → M2P或P2P。
- 检查外设DMA请求映射:查阅MCU参考手册,确认源外设是否能产生DMA请求,目的外设是否能接受DMA写入。
- 评估是否需要循环模式:连续数据流(如音频、波形输出)应使能循环模式;单次批量传输使用正常模式。
- 考虑数据宽度对齐:源和目的的数据宽度应一致,否则DMA会自动进行对齐转换(部分MCU不支持非对齐宽度)。