STM32_DMA_寄存器操作

文章目录

  • 一、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 通道。

相关推荐
Funing73 小时前
无法打开 源 文件 “esp_err.h“
嵌入式硬件·esp32
Hello World . .3 小时前
51单片机基础外设:中断、定时器/计数器(PWM控制蜂鸣器、电机)
单片机·嵌入式硬件·51单片机
FakeOccupational4 小时前
【电路笔记 STM32】Cortex-M7 内核上的数据缓存结构图 + MPU内存保护单元 + Cache基本操作 + Cache&DMA 时序图
笔记·stm32·缓存
WangLanguager4 小时前
foc最终要求的是相电压,还是线电压
单片机
LCG元4 小时前
基于STM32CubeMX的HAL库串口通信与DMA传输深度优化
stm32·单片机·嵌入式硬件
嵌入小生0075 小时前
硬件 --- GPIO/中断/定时器/蜂鸣器
单片机·嵌入式硬件·定时器·pwm·gpio·蜂鸣器·中断
forAllforMe5 小时前
LAN9252 从机模式寄存器的配置代码示例
stm32·单片机·嵌入式硬件
不想起床&5 小时前
51单片机
单片机·嵌入式硬件·51单片机
我在人间贩卖青春6 小时前
单片机复位源
单片机·嵌入式硬件·复位源