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个通道
DMA优先级:多个DMA通道同时发来请求时,就有先后响应处理的顺序问题,这个由仲裁器管理(优先级管理也分软件阶段和硬件阶段)
第一阶段(软件阶段):每个通道的优先级可在DMA_CCRx寄存器中设置,有四个等级:最高、高、中和低优先级。
第二阶段(硬件阶段):如果两个请求有相同软件优先级,较低编号的通道比较高编号的通道有较高的优先级。
(大容量芯片中,DMA1控制器拥有高于DMA2控制器的优先级)
注意:多个请求通过逻辑或输入到DMA控制器,只能有一个请求有效。
DMA处理过程
- 1.外设想通过DMA发送数据,先发送请求
- 2.DMA控制器收到请求后,给外设一个ack
- 3.外设收到ack后,释放请求
- 4.外设启动DMA数据传输,直至传输结束
DMA相关寄存器介绍
寄存器 | 名称 | 作用 |
DMA_CCRx | DMA通道x配置寄存器 | 用于配置DMA(核心控制寄存器) |
DMA_ISR | DMA中断状态寄存器 | 用于查询当前DMA传输状态 |
DMA_IFCR | DMA中断标志清除寄存器 | 用来清除DMA_ISR对应位 |
DMA_CNDTRx | DMA通道x传输数量寄存器 | 用于控制DMA通道x每次传输的数据量 |
DMA_CPARx | DMA通道x外设地址寄存器 | 用于存储STM32外设地址 |
DMA_CMARx | DMA通道x存储器地址寄存器 | 用于存放存储器的地址 |
USART_CR3 | USART控制寄存器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);
}
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循环发送数据。
Comments NOTHING