一、DMA简介
DMA传输实现高速数据移动过程无需任何CPU操作控制。
DMA控制器是独立于Cortex_M4内核的。
STM32F407共有2个DMA控制器,DMA1只有外设到存储器和存储器到外设的传输模式,DMA2具有外设到存储器、存储器到外设以及存储器到存储器的传输模式。
传输模式:
1》外设到存储器(P--->M):把外设数据寄存器内容转移到指定的内存空间。
2》存储器到外设(M--->P):把特定存储区内容转移到外设的数据寄存器中。
3》存储器到存储器(M--->M):把一个指定的存储区内容拷贝到另一个存储区空间。
二、功能框图
(1)外设通道选择
每个DMA控制器具有8个数据流,每个数据流对应8个外设通道,每个通道对应不同的DMA请求。
外设通道选择要解决的主要问题是决定哪一个外设作为该数据流的源地址或者目标地址。
每个外设请求都占用一个数据流通道,相同外设请求可以占用不同数据流通道。
(2)仲裁器
仲裁器用来管理判断哪个数据流的优先级高。
仲裁器管理数据流方法分为两个阶段:第一阶段数据软件阶段,在配置数据流时可以通过寄存器设定它的优先级别,可以设置为非常高、高、中和低四个级别。第二阶段数据硬件阶段,如果两个或两个以上数据流软件设置优先级一样,则它们优先级取决于数据流编号,编号越低优先级越高,比如数据流2优先级高于数据流3。
(3)FIFO
FIFO是源和目标之间的一个中转站。
每个数据流都独立拥有四级32位FIFO(先进先出存储器缓冲区)。DMA传输具有FIFO模式和直接模式。
FIFO模式:用于在源数据传输到目标地址之前临时存放这些数据,只有当数据存储量达到阈值级别(1/4、2/4、3/4和满)时,才会将FIFO中的内容传输到目标中。
直接模式 :将源数据放在FIFO后会立即将该数据传输到目标地址中,无须满足任何条件即可传输。
三、初始化结构体
typedef struct
{
uint32_t DMA_Channel; //通道选择,可选通道0至通道7,每个外设对应固定的通道。
DMA_Channel_0 ~ DMA_Channel_7
uint32_t DMA_PeripheralBaseAddr; //外设地址
uint32_t DMA_Memory0BaseAddr; //存储器0地址
uint32_t DMA_DIR; //传输方向选择,可选P-->M;M-->P;M-->M。
DMA_DIR_PeripheralToMemory
DMA_DIR_MemoryToPeripheral
DMA_DIR_MemoryToMemory
uint32_t DMA_BufferSize; //设定一次传输的数据个数
uint32_t DMA_PeripheralInc; //外设地址是否递增
DMA_PeripheralInc_Enable
DMA_PeripheralInc_Disable
uint32_t DMA_MemoryInc; //存储器地址是否递增
DMA_MemoryInc_Enable
DMA_MemoryInc_Disable
uint32_t DMA_PeripheralDataSize; //外设数据宽度,可选字节(8位)、半字(16位)和字(32位)。
DMA_PeripheralDataSize_Byte
DMA_PeripheralDataSize_HalfWord
DMA_PeripheralDataSize_Word
uint32_t DMA_MemoryDataSize; //存储器数据宽度,可选字节(8位)、半字(16位)和字(32位)。
DMA_MemoryDataSize_Byte
DMA_MemoryDataSize_HalfWord
DMA_MemoryDataSize_Word
uint32_t DMA_Mode; //DMA传输模式选择,可选一次传输或者循环传输,在M--->M模式时,只能是一次传输。
DMA_Mode_Normal
DMA_Mode_Circular
uint32_t DMA_Priority; //优先级选择,可选非常高、高、中和低。
DMA_Priority_Low
DMA_Priority_Medium
DMA_Priority_High
DMA_Priority_VeryHigh
uint32_t DMA_FIFOMode; //FIFO模式使能,在M-->M传输时,FIFO自动开启,软件禁止不了。
DMA_FIFOMode_Disable
DMA_FIFOMode_Enable
uint32_t DMA_FIFOThreshold; //FIFO阈值选择,可选1/4、1/2、3/4和满。
DMA_FIFOThreshold_1QuarterFull
DMA_FIFOThreshold_HalfFull
DMA_FIFOThreshold_3QuartersFull
DMA_FIFOThreshold_Full
uint32_t DMA_MemoryBurst; //存储器突发模式选择,可选单次模式、4节拍、8节拍和16节拍。
DMA_MemoryBurst_Single
DMA_MemoryBurst_INC4
DMA_MemoryBurst_INC8
DMA_MemoryBurst_INC16
uint32_t DMA_PeripheralBurst; //外设突发模式选择,可选单次模式、4节拍、8节拍和16节拍。
DMA_PeripheralBurst_Single
DMA_PeripheralBurst_INC4
DMA_PeripheralBurst_INC8
DMA_PeripheralBurst_INC16
}DMA_InitTypeDef;
四、常用固件库函数
(1)初始化DMA的寄存器到复位状态
void DMA_DeInit(DMA_Stream_TypeDef* DMAy_Streamx); //y = 1、2 ; x = 0 ~ 7
(2)DMA初始化函数
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct); //y = 1、2 ; x = 0 ~ 7
(3)DMA使能函数
void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState); //y = 1、2 ; x = 0 ~ 7
四、程序
(1)存储器到存储器实验(FLASH to SRAM)
bsp_dma.h文件
#ifndef __BSP_DMA_H__
#define __BSP_DMA_H__
#include "stm32f4xx_conf.h"
#define BUFFER_SIZE 32
extern void DMA_Config(void);
extern uint8_t Buffercmp(const uint32_t* pBuffer1,uint32_t* pBuffer2, uint16_t BufferLength);
#endif
bsp_dma.c文件
#include "./dma/bsp_dma.h"
extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];;
/*
存储器到存储器传输,必须使用DMA2,数据流任意,通道任意。
*/
/**********************
功能:配置DMA
参数:无
返回值:无
***********************/
void DMA_Config(void)
{
DMA_InitTypeDef dmaInitValue;
/*1、打开时钟(DMA)*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
/*2、复位初始化DMA数据流*/
DMA_DeInit(DMA2_Stream0);
/*3、等待复位完成*/
while(DMA_GetCmdStatus(DMA2_Stream0) != DISABLE)
{
}
/*4、初始化DMA*/
dmaInitValue.DMA_BufferSize = BUFFER_SIZE;
dmaInitValue.DMA_Channel = DMA_Channel_0;
dmaInitValue.DMA_DIR = DMA_DIR_MemoryToMemory;
dmaInitValue.DMA_FIFOMode = DMA_FIFOMode_Enable;
dmaInitValue.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
dmaInitValue.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer;
dmaInitValue.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dmaInitValue.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
dmaInitValue.DMA_MemoryInc = DMA_MemoryInc_Enable;
dmaInitValue.DMA_Mode = DMA_Mode_Normal;
dmaInitValue.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
dmaInitValue.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
dmaInitValue.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
dmaInitValue.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
dmaInitValue.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA2_Stream0,&dmaInitValue);
/*5、清除DMA数据流传输完成标志位(DMA_FLAG_TCIF0表示数据流0传输完成)*/
DMA_ClearFlag(DMA2_Stream0,DMA_FLAG_TCIF0);
/*6、使能DMA*/
DMA_Cmd(DMA2_Stream0,ENABLE);
/*7、等待DMA数据流有效*/
while ((DMA_GetCmdStatus(DMA2_Stream0) != ENABLE))
{
}
}
/**********************************************
功能:判断指定长度的两个数据源是否完全相同
参数:
pBuffer1---数据源1
pBuffer2---数据源2
BufferLength---比较长度
返回值:
完全相同返回1;
只要有一对数据不相同返回0
***********************************************/
uint8_t Buffercmp(const uint32_t* pBuffer1,
uint32_t* pBuffer2, uint16_t BufferLength)
{
/* 数据长度递减 */
while(BufferLength--)
{
/* 判断两个数据源是否对应相等 */
if(*pBuffer1 != *pBuffer2)
{
/* 对应数据源不相等马上退出函数,并返回0 */
return 0;
}
/* 递增两个数据源的地址指针 */
pBuffer1++;
pBuffer2++;
}
/* 完成判断并且对应数据相对 */
return 1;
}
main.c文件
#include "./usart/bsp_usart.h"
#include "./dma/bsp_dma.h"
#include "stdio.h"
/* 定义aSRC_Const_Buffer数组作为DMA传输数据源
const关键字将aSRC_Const_Buffer数组变量定义为常量类型(存储在FLASH中) */
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
/* 定义DMA传输目标存储器(存储在SRAM中) */
uint32_t aDST_Buffer[BUFFER_SIZE];
int main(void)
{
uint16_t TransferStatus;
USART_Config();
DMA_Config();
//等待DMA传输完成
while(DMA_GetFlagStatus(DMA2_Stream0,DMA_FLAG_TCIF0) == DISABLE)
{
}
//比较源数据和目标数据
TransferStatus = Buffercmp(aSRC_Const_Buffer,aDST_Buffer,BUFFER_SIZE);
if(TransferStatus == 0)//数据不相同
{
printf("\r\n 源数据和目标数据不相同");
}
else//数据完全相同
{
printf("\r\n 源数据和目标数据完全相同");
}
while(1)
{
}
}
(2)存储器到外设实验(SRAM to 串口)
bsp_dma.h文件
#ifndef __BSP_DMA_H__
#define __BSP_DMA_H__
#include "stm32f4xx_conf.h"
#define SENDBUFF_SIZE 5000
extern void USART_DMA_Config(void);
#endif
bsp_dma.c文件
#include "./dma/bsp_dma.h"
uint8_t SendBuff[SENDBUFF_SIZE];
/****************************************************
功能:USART1 TX DMA 配置,内存到外设(USART1->DR)
参数:无
返回值:无
*****************************************************/
void USART_DMA_Config(void)
{
DMA_InitTypeDef damInitValue;
/*1、打开时钟DMA*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
/*2、复位初始化DMA数据流*/
DMA_DeInit(DMA2_Stream7);
/*3、确保DMA数据流复位完成*/
while(DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
{
}
/*4、初始化DMA*/
damInitValue.DMA_BufferSize = SENDBUFF_SIZE;
damInitValue.DMA_Channel = DMA_Channel_4;
damInitValue.DMA_DIR = DMA_DIR_MemoryToPeripheral;
damInitValue.DMA_FIFOMode = DMA_FIFOMode_Disable;
damInitValue.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
damInitValue.DMA_Memory0BaseAddr = (uint32_t)SendBuff;
damInitValue.DMA_MemoryBurst = DMA_MemoryBurst_Single;
damInitValue.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
damInitValue.DMA_MemoryInc = DMA_MemoryInc_Enable;
damInitValue.DMA_Mode = DMA_Mode_Normal;
damInitValue.DMA_PeripheralBaseAddr = USART1_BASE+0x04;//USART1的数据寄存器地址
damInitValue.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
damInitValue.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
damInitValue.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
damInitValue.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA2_Stream7,&damInitValue);
/*5、使能DMA*/
DMA_Cmd(DMA2_Stream7,ENABLE);
/*6、等待DMA数据流有效*/
while(DMA_GetCmdStatus(DMA2_Stream7) != ENABLE)
{
}
}
main.c文件
#include "./usart/bsp_usart.h"
#include "./dma/bsp_dma.h"
#include "stdio.h"
extern uint8_t SendBuff[SENDBUFF_SIZE];
int main(void)
{
uint16_t i;
USART_Config();
USART_DMA_Config();
printf("\r\n USART1 DMA TX 测试 \r\n");
//填充将要发送的数据
for(i = 0;i<SENDBUFF_SIZE;i++)
{
SendBuff[i] = 'A';
}
//USART1向DMA发出TX请求
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
while(1)
{
}
}
来源:CSDN
作者:泪无痕z
链接:https://blog.csdn.net/m0_37671794/article/details/104529743