1、DMA简介

这里我们以STM32F10x系列芯片为例

  • 直接存储器存取(DMA):在外设和存储器之间 或者 存储器和存储器 之间实现高速数据传输的一种技术,无需CPU参与就能传递数据。

  • 两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。

  • 一个仲裁器来协调各个DMA请求的优先权。

2、DMA硬件电路

在这里插入图片描述
其中,DMA2仅存在于大容量产品和互联型产品。

3、DMA请求

  • DMA1控制器
    在这里插入图片描述
  1. 从外设(TIMx[x=1, 2, 3, 4]、ADC1、SPI1、SPI/I2S2、I2Cx[x=1, 2]和USARTx[x=1, 2, 3])产生的7个请求,通过逻辑或输入到DMA1控制器,这意味着同时只能有一个请求有效。
  2. 外设的DMA请求,可以通过设置相应外设寄存器中的DMA控制位,被独立地开启或关闭。
  • DMA2控制器
    存在于DMA2仅存在于大容量产品和互联型产品。
    在这里插入图片描述
  1. 从外设(TIMx[5, 6, 7, 8]、ADC3、SPI/I2S3、UART4、DAC通道1、2和SDIO)产生的5个请求,经逻辑或输入到DMA2控制器,这意味着同时只能有一个请求有效。
  2. 外设的DMA请求,可以通过设置相应外设寄存器中的DMA控制位,被独立地开启或关闭。

4、常用寄存器及标志位

1) 常用寄存器

  • DMA 控制寄存器(CCR)
    1. 方向(DIR):设置数据传输方向(外设→内存 或 内存→外设)。
    2. 循环模式(CIRC):使能循环传输模式。
    3. 优先级(PL):设置 DMA 通道优先级(高 / 中 / 低)。
    4. 数据宽度(PSIZE、MSIZE):外设和内存的数据宽度(字节、半字、字)。
    5. 增量模式(PINC、MINC):使能外设或内存地址自动递增。
  • DMA 外设地址寄存器(CPAR):存储外设数据传输的起始地址(如 USART_DR、ADC_DR 等)。
  • DMA 内存地址寄存器(CMAR):存储内存缓冲区的起始地址。
  • DMA 数据计数器寄存器(CNDTR):设置要传输的数据量(最大值为 65535)。
  • DMA 中断使能寄存器(DIER):使能 / 禁用中断(传输完成、半传输完成、错误中断等)。
  • DMA 状态寄存器(ISR):包含传输状态标志位(如传输完成、半传输完成、错误标志)。

2) 常用标志位(位于 ISR 寄存器)

  • 传输完成标志(TCIFx):当 DMA 传输完成时置 1,需软件清除。
  • 半传输完成标志(HTIFx):当传输数据量达到总数据量的一半时置 1。
  • 传输错误标志(TEIFx):传输过程中出现错误时置 1。
  • 直接模式错误标志(DMEIFx):直接模式下出现错误时置 1(仅特定型号支持)。

5、基本DMA配置流程

  1. 初始化 DMA 通道 / 数据流:
    • 选择 DMA 通道和外设。
    • 配置 CPAR、CMAR、CNDTR。
  2. 配置 CCR 寄存器
    • 设置传输方向、数据宽度、增量模式等。
  3. 使能中断(可选):
    • 在 DIER 中使能对应的中断类型。
  4. 启动 DMA 传输:
    • 通过 CCR 的 EN 位使能 DMA 通道。
  5. 处理中断(可选):
    • 在中断服务函数中检查 ISR 标志位并清除。

6、代码

  • 使用DMA进行内存与串口之间的数据发送
#include "stm32f10x.h"                  // Device header

#define BUFFER_SIZE 14
uint8_t TxBuffer[BUFFER_SIZE] = "Hello World!\r\n"; 

void DMA1_Configuration(void);
void USART1_Configuration(void);
 

int main(void)
{

	DMA1_Configuration();
	USART1_Configuration();	 
	while(1)
	{
		
	}
}


void DMA1_Configuration(void)
{
	//开启DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	//配置DMA通道
	DMA_InitTypeDef  DMA_InitStruct;
	DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE;  //传输数据的大小
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;			//传输方向
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;		//这是一个布尔型参数,用于启用或禁用内存到内存的传输模式
	DMA_InitStruct.DMA_MemoryBaseAddr =(uint32_t)TxBuffer;    //DMA 传输数据的源或目标内存地址,具体取决于 DMA_DIR 的设置
	DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;   //它定义了内存中数据项单位,字节,半字,字
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;         //这是一个布尔型参数,用于决定在每次传输后内存地址是否自动递增
	DMA_InitStruct.DMA_Mode =  DMA_Mode_Circular;            //设置 DMA 的工作模式
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;      //外设的基地址,也就是 DMA 传输数据的源或目标外设地址,具体取决于 DMA_DIR 的设置。
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;         //外设数据项单位,字节,半字,字
	DMA_InitStruct.DMA_PeripheralInc   =  DMA_PeripheralInc_Disable;          //这是一个布尔型参数,用于决定在每次传输后外设地址是否自动递增。
	DMA_InitStruct.DMA_Priority   =  DMA_Priority_High;              //DMA通道的优先级。	
	DMA_Init(DMA1_Channel4, &DMA_InitStruct);	
	//开启DMA中断
	DMA_ITConfig(DMA1_Channel4,DMA_IT_TC|DMA_IT_HT|DMA_IT_TE,ENABLE);
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel4_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	DMA_Cmd(DMA1_Channel4,ENABLE);
}

void USART1_Configuration(void)
{
	//开启USART1总线的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    // 初始化 GPIOA Pin_9作为 USART1 的 TX 引脚
    GPIO_InitTypeDef GPIO_InitStructure;
	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);   
	
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Tx ;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1, &USART_InitStruct);

	//开启USART_DMA的硬件触发功能
	USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //当 USART1 的数据寄存器DR为空时,会自动触发 DMA 将内存中的数据传输到USART1外设。	
	//开启USART1
	USART_Cmd(USART1,ENABLE);
}

void DMA1_Channel4_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC4))   
        DMA_ClearITPendingBit(DMA1_IT_TC4); 	
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐