文章目录
- 一、DMA寄存器
-
- [ 1、DMA中断状态寄存器(DMA_ISR)](# 1、DMA中断状态寄存器(DMA_ISR))
- [ 2、DMA中断标志清除寄存器(DMA_IFCR)](# 2、DMA中断标志清除寄存器(DMA_IFCR))
- [ 3、DMA通道x配置寄存器(DMA_CCRx)(x = 1...7)](# 3、DMA通道x配置寄存器(DMA_CCRx)(x = 1…7))
- [ 4、DMA通道x传输数量寄存器(DMA_CNDTRx)(x = 1...7)](# 4、DMA通道x传输数量寄存器(DMA_CNDTRx)(x = 1…7))
- [ 5、DMA通道x外设地址寄存器(DMA_CPARx)(x = 1...7)](# 5、DMA通道x外设地址寄存器(DMA_CPARx)(x = 1…7))
- [ 6、DMA通道x存储器地址寄存器(DMA_CMARx)(x = 1...7)](# 6、DMA通道x存储器地址寄存器(DMA_CMARx)(x = 1…7))
- [ 7、DMA寄存器映像](# 7、DMA寄存器映像)
- 二、代码实例
-
- [ 1、USART1 DMA 发送(批量发送字符串 / 数组)](# 1、USART1 DMA 发送(批量发送字符串 / 数组))
- [ 2、ADC1 DMA 采集(PA0 通道,连续采集多组数据)](# 2、ADC1 DMA 采集(PA0 通道,连续采集多组数据))
- [ 3、DMA 内存到内存传输(高速复制数组)](# 3、DMA 内存到内存传输(高速复制数组))
- 三、总结
-
- [ 1、配置DMA通道的过程:](# 1、配置DMA通道的过程:)
- [ 2、核心寄存器](# 2、核心寄存器)
- [ 3、注意事项](# 3、注意事项)
一、DMA寄存器
1、DMA中断状态寄存器(DMA_ISR)
偏移地址:0x00
复位值:0x0000 0000


2、DMA中断标志清除寄存器(DMA_IFCR)
偏移地址:0x04
复位值:0x0000 0000

3、DMA通道x配置寄存器(DMA_CCRx)(x = 1...7)
偏移地址:0x08 + 20 x (通道编号 -- 1)
复位值:0x0000 0000



4、DMA通道x传输数量寄存器(DMA_CNDTRx)(x = 1...7)
偏移地址:0x0C + 20 x (通道编号 -- 1)
复位值:0x0000 0000

5、DMA通道x外设地址寄存器(DMA_CPARx)(x = 1...7)
偏移地址:0x10 + 20 x (通道编号 -- 1)
复位值:0x0000 0000
当开启通道(DMA_CCRx的EN=1)时不能写该寄存器。

6、DMA通道x存储器地址寄存器(DMA_CMARx)(x = 1...7)
偏移地址:0x14 + 20 x (通道编号 -- 1)
复位值:0x0000 0000
当开启通道(DMA_CCRx的EN=1)时不能写该寄存器。

7、DMA寄存器映像


二、代码实例
1、USART1 DMA 发送(批量发送字符串 / 数组)
实现串口 DMA 批量发送数据,CPU 无需逐字节等待,适合大数据量输出。
c
#include "stm32f10x.h"
#include <string.h>
// 发送缓冲区
uint8_t USART1_Tx_Buf[] = "STM32 USART1 DMA Test\r\n";
uint16_t USART1_Tx_Len = sizeof(USART1_Tx_Buf) - 1;
/**
* @brief 系统时钟初始化(72MHz)
*/
void SystemClock_Init(void)
{
RCC->CR |= RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));
FLASH->ACR |= FLASH_ACR_LATENCY_2;
RCC->CFGR &= ~RCC_CFGR_HPRE;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
RCC->CFGR &= ~RCC_CFGR_PPRE2;
RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
RCC->CFGR |= RCC_CFGR_PLLMULL9;
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
/**
* @brief USART1初始化(9600波特率,开启DMA发送)
*/
void USART1_Init(void)
{
// 1. 开启时钟:GPIOA、USART1、AFIO
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_AFIOEN;
// 开启DMA1时钟
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// 2. 配置GPIO:PA9(TX)复用推挽,PA10(RX)浮空输入
GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9);
GPIOA->CRH |= GPIO_CRH_MODE9_0 | GPIO_CRH_CNF9_1;
GPIOA->CRH &= ~(GPIO_CRH_MODE10 | GPIO_CRH_CNF10);
GPIOA->CRH |= GPIO_CRH_CNF10_0;
// 3. 配置USART1:9600波特率,8N1,开启DMA发送
USART1->CR1 &= ~USART_CR1_UE;
USART1->BRR = 72000000 / 9600;
USART1->CR1 &= ~(USART_CR1_M | USART_CR1_PCE);
USART1->CR2 &= ~USART_CR2_STOP;
USART1->CR3 |= USART_CR3_DMAT; // 开启USART1 DMA发送模式
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
/**
* @brief DMA1_Channel4初始化(USART1_TX映射通道)
* @param buf:发送缓冲区地址
* @param len:发送长度
*/
void DMA1_Channel4_Init(uint8_t *buf, uint16_t len)
{
// 1. 关闭DMA通道,清空配置
DMA1_Channel4->CCR &= ~DMA_CCR_EN;
// 2. 配置DMA核心参数
DMA1_Channel4->CCR = 0; // 清空CCR寄存器
DMA1_Channel4->CCR |= DMA_CCR_DIR; // 方向:内存→外设
DMA1_Channel4->CCR |= DMA_CCR_MINC; // 内存地址自增
DMA1_Channel4->CCR &= ~DMA_CCR_PINC; // 外设地址不自增
DMA1_Channel4->CCR &= ~DMA_CCR_PSIZE; // 外设数据宽度:8位
DMA1_Channel4->CCR &= ~DMA_CCR_MSIZE; // 内存数据宽度:8位
DMA1_Channel4->CCR &= ~DMA_CCR_CIRC; // 关闭循环模式
DMA1_Channel4->CCR |= DMA_CCR_PL_0; // 优先级:中
// 3. 配置外设地址(USART1_DR)、内存地址、传输长度
DMA1_Channel4->CPAR = (uint32_t)&USART1->DR; // 外设地址
DMA1_Channel4->CMAR = (uint32_t)buf; // 内存地址
DMA1_Channel4->CNDTR = len; // 传输长度
// 4. 开启DMA通道
DMA1_Channel4->CCR |= DMA_CCR_EN;
}
/**
* @brief 等待DMA发送完成
*/
void USART1_DMA_Wait_Tx(void)
{
// 等待DMA传输完成标志(TCIF4=1)
while(!(DMA1->ISR & DMA_ISR_TCIF4));
// 清除传输完成标志(写1清除)
DMA1->IFCR |= DMA_IFCR_CTCIF4;
}
// 主函数
int main(void)
{
SystemClock_Init();
USART1_Init();
while(1)
{
// 初始化DMA并发送数据
DMA1_Channel4_Init(USART1_Tx_Buf, USART1_Tx_Len);
// 等待发送完成
USART1_DMA_Wait_Tx();
// 延时1s(简单延时,可替换为SysTick)
for(uint32_t i=0; i<72000000/20; i++);
}
}
2、ADC1 DMA 采集(PA0 通道,连续采集多组数据)
实现 ADC1 通道 0(PA0)的连续 DMA 采集,将数据批量存入内存,适合模拟量高速采样。
c
#include "stm32f10x.h"
// ADC采集缓冲区(存储100组采样值)
uint16_t ADC1_Rx_Buf[100];
#define ADC1_BUF_LEN 100
/**
* @brief ADC1初始化(PA0,单次转换,开启DMA)
*/
void ADC1_Init(void)
{
// 1. 开启时钟:GPIOA、ADC1、DMA1
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// ADC时钟分频:6分频(72MHz/6=12MHz,≤14MHz)
RCC->CFGR &= ~RCC_CFGR_ADCPRE;
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;
// 2. 配置GPIO:PA0模拟输入
GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
// 3. 配置ADC1:单次转换,通道0,开启DMA
ADC1->CR1 &= ~ADC_CR1_SCAN; // 单通道模式
ADC1->CR2 |= ADC_CR2_CONT; // 连续转换模式
ADC1->CR2 |= ADC_CR2_DMA; // 开启ADC DMA模式
ADC1->SQR1 &= ~ADC_SQR1_L; // 转换序列长度:1
ADC1->SQR3 &= ~ADC_SQR3_SQ1;
ADC1->SQR3 |= 0; // 转换序列第1位:通道0
ADC1->SMPR2 &= ~ADC_SMPR2_SMP0;
ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; // 采样时间:239.5周期
ADC1->CR2 |= ADC_CR2_ADON; // 开启ADC
// 校准ADC
ADC1->CR2 |= ADC_CR2_CAL;
while(ADC1->CR2 & ADC_CR2_CAL);
}
/**
* @brief DMA1_Channel1初始化(ADC1映射通道)
*/
void DMA1_Channel1_Init(void)
{
// 1. 关闭DMA通道
DMA1_Channel1->CCR &= ~DMA_CCR_EN;
// 2. 配置DMA参数
DMA1_Channel1->CCR = 0;
DMA1_Channel1->CCR &= ~DMA_CCR_DIR; // 方向:外设→内存
DMA1_Channel1->CCR |= DMA_CCR_MINC; // 内存地址自增
DMA1_Channel1->CCR &= ~DMA_CCR_PINC;// 外设地址不自增
DMA1_Channel1->CCR |= DMA_CCR_PSIZE_0; // 外设数据宽度:16位
DMA1_Channel1->CCR |= DMA_CCR_MSIZE_0; // 内存数据宽度:16位
DMA1_Channel1->CCR |= DMA_CCR_CIRC; // 开启循环模式
DMA1_Channel1->CCR |= DMA_CCR_PL_1; // 优先级:高
// 3. 配置地址和长度
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR; // 外设地址(ADC_DR)
DMA1_Channel1->CMAR = (uint32_t)ADC1_Rx_Buf; // 内存地址
DMA1_Channel1->CNDTR = ADC1_BUF_LEN; // 传输长度
// 4. 开启DMA通道
DMA1_Channel1->CCR |= DMA_CCR_EN;
// 启动ADC转换
ADC1->CR2 |= ADC_CR2_SWSTART;
}
// 主函数
int main(void)
{
SystemClock_Init();
ADC1_Init();
DMA1_Channel1_Init();
while(1)
{
// ADC1_Rx_Buf中实时存储最新的100组采样值
// 可在此处理采样数据(如求平均值、滤波等)
}
}
3、DMA 内存到内存传输(高速复制数组)
实现内存块之间的 DMA 高速传输,比 CPU 循环复制效率高(适合大数据量)。
c
#include "stm32f10x.h"
// 源缓冲区和目标缓冲区
uint32_t DMA_Src_Buf[1024] = {0x12345678, 0x87654321, /* 省略其他数据 */};
uint32_t DMA_Dst_Buf[1024] = {0};
#define DMA_MEM_LEN 1024
/**
* @brief DMA1_Channel7初始化(内存到内存传输,优先选该通道)
*/
void DMA1_Channel7_Mem2Mem_Init(void)
{
// 1. 开启DMA1时钟
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// 2. 关闭DMA通道
DMA1_Channel7->CCR &= ~DMA_CCR_EN;
// 3. 配置内存到内存参数
DMA1_Channel7->CCR = 0;
DMA1_Channel7->CCR |= DMA_CCR_DIR; // 方向:内存→内存(源→目标)
DMA1_Channel7->CCR |= DMA_CCR_MINC; // 内存地址自增
DMA1_Channel7->CCR |= DMA_CCR_PINC; // 外设地址自增(内存到内存时,外设=源内存)
DMA1_Channel7->CCR |= DMA_CCR_PSIZE_1; // 数据宽度:32位
DMA1_Channel7->CCR |= DMA_CCR_MSIZE_1; // 数据宽度:32位
DMA1_Channel7->CCR &= ~DMA_CCR_CIRC; // 关闭循环
DMA1_Channel7->CCR |= DMA_CCR_PL_1 | DMA_CCR_PL_0; // 优先级:最高
// 4. 配置源地址、目标地址、传输长度
DMA1_Channel7->CPAR = (uint32_t)DMA_Src_Buf; // 源内存(外设地址)
DMA1_Channel7->CMAR = (uint32_t)DMA_Dst_Buf; // 目标内存
DMA1_Channel7->CNDTR = DMA_MEM_LEN; // 传输长度
// 5. 开启DMA通道,启动传输
DMA1_Channel7->CCR |= DMA_CCR_EN;
}
/**
* @brief 等待内存到内存传输完成
*/
void DMA_Mem2Mem_Wait(void)
{
while(!(DMA1->ISR & DMA_ISR_TCIF7));
DMA1->IFCR |= DMA_IFCR_CTCIF7;
}
// 主函数
int main(void)
{
SystemClock_Init();
// 启动DMA内存到内存传输
DMA1_Channel7_Mem2Mem_Init();
// 等待传输完成
DMA_Mem2Mem_Wait();
while(1)
{
// DMA_Dst_Buf已完成与DMA_Src_Buf的复制
// 可验证数据:if(DMA_Dst_Buf[0] == 0x12345678) 表示传输成功
}
}
三、总结
1、配置DMA通道的过程:
1. 在DMA_CPARx寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将是数据传输的源或目标。
2. 在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数据将从这个地址读出或写入这个地址。
3. 在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。
4. 在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级。
5. 在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外设和存储器的数据宽度、传输一半产生中断或传输完成产生中断。
6. 设置DMA_CCRx寄存器的ENABLE位,启动该通道。
2、核心寄存器

3、注意事项
DMA 寄存器编程核心流程:
开时钟→关通道→配 CCR 参数→设地址 / 长度→开通道→等待 / 处理完成;
不同场景的核心差异是DIR(方向)、MINC/PINC(地址自增)、CIRC(循环模式)的配置;
外设 DMA 需先开启外设的 DMA 模式(如 USART_CR3_DMAT、ADC_CR2_DMA),再配置 DMA 通道。