一.DMA简介
DMA,全称Direct Memory Access,即直接存储器访问;
DMA传输:将数据从一个地址空间复制到另一个地址空间,即数据搬运工。
传输方式:
- 内存--->外设
- 外设--->内存
- 内存--->内存
DMA传输无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
作用:为CPU减负。
二.DMA结构框图介绍
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发送 |
数据传输方向:位14、位4;
通道优先级:位13:12;
循环模式:位5;
外设/存储器增量模式:位7、位6;
外设/存储器数据宽度:位11:10、位9:8;
中断使能:位3、位2、位1;
通道开启:位0
查询当前DMA传输状态;
最大数据传输数目:65535;
非循环模式下传输结束后,要开始新的DMA传输,需要在关闭DMA通道情况下, 在该寄存器下重新写入传输数目。
从哪里传?传哪里去?
四.DMA相关HAL库驱动介绍
驱动函数 | 关联寄存器 | 功能描述 |
__HAL_RCC_DMAx_CLK_ENABLE() | RCC_AHBENR | 使能DMAx时钟 |
HAL_DMA_Iinit() | 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外设 |
DMA外设相关结构体:DMA_HandleTypeDef 和 DMA_InitTypeDef
五.DMA配置步骤(串口)
- 使能DMA时钟:__HAL_RCC_DMA1_CLK_ENABLE;
- 初始化DMA:HAL_DMA_Init函数初始化DMA相关参数,__HAL_LINKDMA函数连接DMA和外设;
- 使能串口的DMA发送,启动传输:HAL_UART_Transmit_DMA;
- 查询DMA传输状态:__HAL_DMA_GET_FLAG查询通道传输状态;__HAL_DMA_GET_COUNTER获取当前传输剩余数据量;
- DMA中断使用:HAL_NVIC_EnableIRQ,HAL_NVIC_SetPriority,编写中断服务函数xxx_IRQHandler
六.编程实战
实验目标:实现内部RAM中内存到内存的数据传输。
dma.c
#include "dma.h"
uint8_t src_buf[10] = {0x0a,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
uint8_t dest_buf[10] = {0};
DMA_HandleTypeDef dma_handler;
void dma_init(void)/*内存到内存*/
{
__HAL_RCC_DMA1_CLK_ENABLE();/*使能DMA1时钟*/
dma_handler.Instance = DMA1_Channel1;/*选择DMA通道*/
dma_handler.Init.Direction = DMA_MEMORY_TO_MEMORY;/*选择DMA传输方向*/
dma_handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;/*数据传输宽度*/
dma_handler.Init.MemInc = DMA_MINC_ENABLE;/*选择是否增量模式*/
dma_handler.Init.Mode = DMA_NORMAL;/*是否循环,内存到内存是不支持的*/
dma_handler.Init.PeriphDataAlignment =DMA_MDATAALIGN_BYTE; /*外设数据宽度*/
dma_handler.Init.PeriphInc = DMA_MINC_ENABLE;/*外设地址是否增量*/
dma_handler.Init.Priority = DMA_PRIORITY_HIGH; /*DMA通道优先级*/
HAL_DMA_Init(&dma_handler);/*初始化DMA*/
HAL_DMA_Start(&dma_handler,(uint32_t)src_buf,(uint32_t)dest_buf,0);/*确定源地址与目标地址*/
}
void dma_transmit(uint16_t cndtr)/*DMA发送数据函数*/
{
__HAL_DMA_DISABLE(&dma_handler);/*失能DMA通道*/
dma_handler.Instance->CNDTR = cndtr;/*传输数据量*/
__HAL_DMA_ENABLE(&dma_handler);/*使能DMA通道*/
}
dma.h
#ifndef __DMA_H
#define __DMA_H
#include "stm32f1xx.h"
extern DMA_HandleTypeDef dma_handler;
extern uint8_t src_buf[10];
extern uint8_t dest_buf[10];
void dma_init(void);/*内存到内存*/
void dma_transmit(uint16_t cndtr);/*DMA发送数据函数*/
#endif
main.c
#include "stm32f1xx_hal.h"
#include "sys.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "dma.h"
#include "led.h"
#include "key.h"
#include "string.h"
int main()
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
usart_init(115200);
delay_init();
dma_init();
led_init();
key_init();
while(1)
{
delay_us(50000);
memset(dest_buf,0,10);/*对目的进行初始化*/
dma_transmit(10);
while(1)
{
if(__HAL_DMA_GET_FLAG(&dma_handler,DMA_FLAG_GL1))
{
__HAL_DMA_CLEAR_FLAG(&dma_handler,DMA_FLAG_GL1);
printf("传输完成!\r\n");
break;
}
}
}
}