前言
学习DMA部分的时候,我们需要学习的是什么?
- 掌握DMA部分的作用;
- 了解DMA的工作原理;
- 掌握DMA的编程;
DMA介绍
DMA 全称Direct Memory Access,即直接存储器访问。 DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。 DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
作用:为CPU减负。
DMA工作流程
DMA是一个不需要CPU干预的独立硬件模块,可以进行外设和存储器以及存储器和存储器之间的数据传输,只需要CPU告诉它数据源在哪里,搬到哪里去,一次搬运多少字节,总共搬运多少个字节,然后启动DMA功能,那么DMA就会需要的时候自动进行数据的搬移。
STM32F4最多有2个DMA控制器,2个DMA控制器总共有16个数据流(每个控制器8个)。每个DMA控制器都用于管理一个或者多个外设的存储器访问请求。每个数据流总共可以有多达8个通道(或请求),每个通道都有一个仲裁器,用于处理DMA请求间的优先级。
DMA主要特性
DMA框架图
通道选择:每个数据流都与一个DMA请求相关联,此DMA请求可以从8个可能的通道请求中选出。次选择由DMA_SxCR寄存器中的CHSEL[2:0]位控制。
DMA请求映射
DMA数据流
8个DMA控制流数据流都能够提供源和目标之间的单向传输链路。
每个数据流配置后都可以执行:
- 常规类型事务:存储器到外设、外设到寄存器或者寄存器到寄存器的传输;
- 双缓冲区类型事务:使用寄存器的两个寄存器指针的双缓冲区传输;
要传输的数据量多大65536,可以进行传输工作,并与连接到外设的AHB1端口进行连接
外设的源宽度有关。每个事务完成以后,包含要传输的数据项总量的寄存器都会进行递减。
外设到寄存器模式
寄存器到外设模式
寄存器到寄存器模式
仲裁器
仲裁器为两个AHB主端口(存储器和外设端口)提供基于请求优先级的8个DMA数据流请
求管理,并启动外设/存储器访问序列。
优先级管理分为两个阶段:
●
软件:每个数据流优先级都可以在DMA_ SxCR寄存器中配置。分为四个级别:
-非常高优先级
高优先级
中优先级
低优先级
●
硬件:如果两个请求具有相同的软件优先级,则编号低的数据流优先于编号高的数据
流。例如,数据流2的优先级高于数据流4。
位17:16 PL[1:0]: 优先级(Priority level)
DMA配置参数
1、通道
2、优先级
3、数据传输方向
4、存储器/外设 数据宽度
5、存储器/外设
6、地址是否增量
7、循环模式
8、数据传输量
DMA配置程序过程(串口发送DMA)
① 使能DMA时钟
② 初始化DMA通道参数
③使能串口DMA发送,串口DMA使能函数
④查询DMA的EN位,确保数据流就绪,可以配置
⑤设置通道当前剩余数据量
⑥使能DMA1通道,启动传输。
⑦查询DMA传输状态
⑧ 获取/设置通道当前剩余数据量
初始化函数
1、 模块初始化(不使用DMA应该怎么初始还是得写)
2、模块初始化后面添加开启模块内DMA使能位。(每个外设模块都会有一个DMA使能位)
3、USART1->CR3|=(1<<7);
4、按照DMA通道配置方法进行DMA相关寄存器初始化配置 UART1的发送函数:
5、添加DMA的UART1的发送函数
完整代码源
dma.c
#include "dma.h" void usart3_dma_config(int n,char* sp) { DMA_InitTypeDef DMA_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); DMA_InitStruct.DMA_BufferSize=n; DMA_InitStruct.DMA_Channel=DMA_Channel_4; DMA_InitStruct.DMA_DIR=DMA_DIR_MemoryToPeripheral; // DMA_InitStruct.DMA_FIFOMode=DMA_FIFOMode_Disable; // DMA_InitStruct.DMA_FIFOThreshold=DMA_FIFOThreshold_Full; DMA_InitStruct.DMA_Memory0BaseAddr=(uint32_t)sp; // DMA_InitStruct.DMA_MemoryBurst=DMA_MemoryBurst_Single;//??????? DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable; DMA_InitStruct.DMA_Mode=DMA_Mode_Normal; DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t) &USART3->DR; // DMA_InitStruct.DMA_PeripheralBurst=DMA_PeripheralBurst_Single; DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_Priority=DMA_Priority_Medium; DMA_Init(DMA1_Stream3, &DMA_InitStruct); DMA_Cmd(DMA1_Stream3, ENABLE); } void usart3_dma_send() { while(DMA_GetFlagStatus(DMA1_Stream3, DMA_FLAG_TCIF3)==0); DMA_ClearFlag(DMA1_Stream3, DMA_FLAG_TCIF3); } void adc1_dma_config(int n,char* sp) { DMA_InitTypeDef DMA_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); DMA_InitStruct.DMA_BufferSize=n; DMA_InitStruct.DMA_Channel=DMA_Channel_6; DMA_InitStruct.DMA_DIR=DMA_DIR_MemoryToPeripheral; // DMA_InitStruct.DMA_FIFOMode=DMA_FIFOMode_Disable; // DMA_InitStruct.DMA_FIFOThreshold=DMA_FIFOThreshold_Full; DMA_InitStruct.DMA_Memory0BaseAddr=(uint32_t)sp; // DMA_InitStruct.DMA_MemoryBurst=DMA_MemoryBurst_Single;//??????? DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable; DMA_InitStruct.DMA_Mode=DMA_Mode_Normal; DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t) &ADC1->DR; // DMA_InitStruct.DMA_PeripheralBurst=DMA_PeripheralBurst_Single; DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_Priority=DMA_Priority_Medium; DMA_Init(DMA2_Stream0, &DMA_InitStruct); DMA_Cmd(DMA2_Stream0, ENABLE); } void adc1_dma_send() { while(DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF3)==0); DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF3); }
dma.h
#ifndef _DMA_H_//如果没有 #define _DMA_H_//定义 #include "stm32f4xx.h" void usart3_dma_config(int n,char* sp); void usart3_dma_send(void); void adc1_dma_config(int n,char* sp); void adc1_dma_send(void); #endif //结束定义