STM32F1-DMA

发布于 2022-11-23  298 次阅读


DMA简介

DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据, 但是不需要占用CPU,即在传输数据的时候,CPU可以干其他的事情,好像是多线程一样。 数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是SRAM或者是FLASH。 DMA控制器包含了DMA1和DMA2,其中DMA1有7个通道,DMA2有5个通道,这里的通道可以理解为传输数据的一种管道。要注意的是DMA2只存在于大容量的单片机中。

DMA框图

DMA请求:DMA传输数据,先向DMA控制器发送请求

DMA通道:不同外设向DMA的不同通道发送请求,DMA1有7个通道,DMA2有5个通道

DMA1通道

DMA优先级:多个DMA通道同时发来请求时,就有先后响应处理的顺序问题,这个由仲裁器管理(优先级管理也分软件阶段和硬件阶段)

第一阶段(软件阶段):每个通道的优先级可在DMA_CCRx寄存器中设置,有四个等级:最高、高、中和低优先级。

第二阶段(硬件阶段):如果两个请求有相同软件优先级,较低编号的通道比较高编号的通道有较高的优先级。

(大容量芯片中,DMA1控制器拥有高于DMA2控制器的优先级)

注意:多个请求通过逻辑或输入到DMA控制器,只能有一个请求有效。

DMA处理过程

  • 1.外设想通过DMA发送数据,先发送请求
  • 2.DMA控制器收到请求后,给外设一个ack
  • 3.外设收到ack后,释放请求
  • 4.外设启动DMA数据传输,直至传输结束

DMA相关寄存器介绍

寄存器名称作用
DMA_CCRxDMA通道x配置寄存器用于配置DMA(核心控制寄存器)
DMA_ISRDMA中断状态寄存器用于查询当前DMA传输状态
DMA_IFCRDMA中断标志清除寄存器用来清除DMA_ISR对应位
DMA_CNDTRxDMA通道x传输数量寄存器用于控制DMA通道x每次传输的数据量
DMA_CPARxDMA通道x外设地址寄存器用于存储STM32外设地址
DMA_CMARxDMA通道x存储器地址寄存器用于存放存储器的地址
USART_CR3USART控制寄存器3用于使能串口DMA发送

DMA中断状态寄存器(DMA_ISR)

DMA中断标志清除寄存器(DMA_IFCR)

DMA通道x传输数量寄存器(DMA_CNDTR)

最大数据传输数目:65535

为0时不传输数据

储存器到储存器模式

相关HAL库驱动

驱动函数关联寄存器功能描述
__HAL_RCC_DMAx_CLK_ENABLE(…)RCC_AHBENR使能DMAx时钟
HAL_DMA_Init(…)DMA_CCR初始化DMA
HAL_DMA_Start_IT(…)DMA_CCR/CPAR/CMAR/CNDTR开始DMA传输
__HAL_LINKDMA(…)用来连接DMA和外设句柄
HAL_UART_Transmit_DMA(…)CCR/CPAR/CMAR/CNDTR/USART_CR3使能DMA发送,启动传输
__HAL_DMA_GET_FLAG(…)DMA_ISR查询DMA传输通道的状态
__HAL_DMA_ENABLE(…)DMA_CCR(EN)使能DMA外设
__HAL_DMA_DISABLE(…)DMA_CCR(EN)失能DMA外设

 相关定义

/* 定义aSRC_Const_Buffer数组作为DMA传输数据源
 const关键字将aSRC_Const_Buffer数组变量定义为常量类型 */
 const uint32_t aSRC_Const_Buffer[32]= {
     0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
     0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
     0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
     0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
     0x41424344,0x44564748,0x494A4B4C,0x4D4E4F50,
     0x51525345,0x55565758,0x595A5B5C,0x5D5E5F60,
     0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
     0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80
 };
 /* 定义DMA传输目标存储器 */
 uint32_t aDST_Buffer[32];

aSRC_Const_Buffer[BUFFER_SIZE]定义用来存放源数据,并且使用了const关键字修饰,即常量类型,使得变量是存储在内部flash空间上

DMA配置

DMA_HandleTypeDef  g_dma_handle;            /* DMA句柄 */
void Dma_Init(void)
{
	__HAL_RCC_DMA1_CLK_ENABLE();
	
    /*DMA配置 */
    g_dma_handle.Instance = DMA1_Channel1;                               /* DMA通道*/
    g_dma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;             /* DIR = 1 , 存储器到存储器模式 */
    g_dma_handle.Init.PeriphInc = DMA_PINC_ENABLE;                 /* 外设非增量模式 */
    g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;                     /* 存储器增量模式 */
    g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;    /* 外设数据长度:32位,1WORD = 4 BYTE */
    g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;       /* 存储器数据长度:32位 */
    g_dma_handle.Init.Mode = DMA_NORMAL;                            /* 外设流控模式,内存到内存不支持循环模式 */
    g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;               /* 中等优先级 */
	HAL_DMA_Init(&g_dma_handle);
    HAL_DMA_Start(&g_dma_handle,(uint32_t)aSRC_Const_Buffer,(uint32_t)aDST_Buffer,0);//长度为零,CNDTR为零,即先不传输,后面可以自己控制传输触发条件
}

传输数据

void Dma_Enable_Transmit(uint16_t cndtr)
{
	__HAL_DMA_DISABLE(&g_dma_handle);//从新给CNDTR写值之前,需要将DAM失能
	g_dma_handle.Instance -> CNDTR = cndtr;//重装CNDTR的值,使能传输
	__HAL_DMA_ENABLE(&g_dma_handle);//使能DMA
	
}

mian函数

memset(aDST_Buffer,0,32);//memset将目标数组清零
Dma_Enable_Transmit(32);//重装CNDTR的值,使能发送
while(1)
{
	if(__HAL_DMA_GET_FLAG(&g_dma_handle,DMA_FLAG_TC1))//获取TC位,即发送完成位的状态
	{
	__HAL_DMA_CLEAR_FLAG(&g_dma_handle,DMA_FLAG_TC1);//清除TC标志位
		break;
	}
}

储存器到外设模式

 DMA配置

DMA_HandleTypeDef  g_dma_handle;            /* DMA句柄 */
void Dma_Init(void)
{
	__HAL_RCC_DMA1_CLK_ENABLE();
	
		/* 关联 DMA 句柄,hdmatx 是外设句柄结构体的成员变量,在这里实际就是 UART1_Handler,如下图1.1所示*/
	__HAL_LINKDMA(&g_uart1_handle, hdmatx, g_dma_handle); 
    /*DMA配置 */
    g_dma_handle.Instance = DMA1_Channel4;                               /* DMA通道*/
    g_dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;             /* DIR = 1 , 存储器到外设模式 */
    g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;                 /* 外设非增量模式 */
    g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;                     /* 存储器增量模式 */
    g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;    /* 外设数据长度:32位,1WORD = 4 BYTE */
    g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;       /* 存储器数据长度:32位 */
    g_dma_handle.Init.Mode = DMA_CIRCULAR;                            /* 循环模式 */
    g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;               /* 中等优先级 */
	HAL_DMA_Init(&g_dma_handle);
}
1.1

USART1 向 DMA发出TX请求

uint8_t str[] = "123\n";

// 采用DMA发送 str,按照str实际大小发送,不发送字符串末尾的'0'
    HAL_UART_Transmit_DMA(&g_uart1_handle, str, sizeof(str) - 1);

if(__HAL_DMA_GET_FLAG(&g_dma_handle,DMA_FLAG_TC1))//获取TC位,即发送完成位的状态
	{
	__HAL_DMA_CLEAR_FLAG(&g_dma_handle,DMA_FLAG_TC1);//清除TC标志位
		break;
	}

HAL_UART_Transmit_DMA函数用于启动USART的DMA传输。只需要指定源数据地址及长度,运行该函数后USART的DMA发送传输就开始了,根据配置它会通过USART循环发送数据。

  • alipay_img
  • wechat_img
想法不去做终究就只是想法
最后更新于 2023-05-07