DMA功能概述:
DMA用于数据传输,绕过CPU进行数据大量传输,保证系统运行速度。
DMA框图:
DMA包含有: REQ_STR0_CH0为通道, REQ_STREAM0为数据流, 外设端口,储存器端口,仲裁器、通道选择器、数据流选择器。
MDA数据流表功能
STM32F4最多有2个DMA控制器,2个DMA控制器总共有16个数据流(一个DMA8个数据流)。每个数据流8个通道,每个通道只特定传输固定的外设端口。
DMA原理配置原理概述
源、目标传输地址和传输模式确定和配置
——》数据流和通道的确定和配置
----》仲裁器的优先级
-----》传输时指针递增确定,包含源和目标的指针。
------》循环模式,双缓冲区模式的确定和配置
----》数据宽度确定
–》单次传输或者突发增量传输
----》FIFO启用确定
值得注意的是在配置DMA之前要禁止掉数据流,并等待数据流被禁止成功。
每次去触发DMA之前,根据通道的特定功能,去功能文件库找使能DMA使能功能。
比如DMA2数据流7的通道四,是串口一的发送功能。(可以看DMA数据流的表)。DMA触发前应该在串口库函数找到函数
DMACmd(USART1,USART_DMAReq_Tx, ENABLE);
然后使能相应功能,再使能DMA,函数如:
MYDMA_Enable(DMA2_Stream7,SEND_BUF_SIZE);//此时可以执行其他任务
DMA驱动程序:
#include"dma.h"
// DMA_Streamx is sach as DMA1_Stream0
/* DMA配置
DMA_Streamx DMAx的数据流
chx 储存器地址
par 外储存器地址
mar 储存器地址
ndtr 数据量
*/
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{
DMA_InitTypeDef DMA_InitStruct;
if((u32)DMA_Streamx>(u32)DMA2) //按地址判断DMA几,后使能DMA几时钟
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
}
else
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
}
DMA_DeInit( DMA_Streamx); //数据流禁止
while(DMA_GetCmdStatus(DMA_Streamx)==1);//等待数据流被禁止成功
DMA_InitStruct.DMA_Channel=chx;//通道配置
DMA_InitStruct.DMA_PeripheralBaseAddr=par;//外设地址
DMA_InitStruct.DMA_Memory0BaseAddr=mar;//存储器地址
DMA_InitStruct.DMA_DIR=DMA_DIR_MemoryToPeripheral; //传输方向
DMA_InitStruct.DMA_BufferSize=ndtr; //数据量,依赖于配置的外设存储器的宽度
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址不增加
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//存储器地址增加
DMA_InitStruct.DMA_PeripheralDataSize= DMA_PeripheralDataSize_Byte;//外设数据宽度,字节
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//内存数据宽度。字节
DMA_InitStruct.DMA_Mode=DMA_Mode_Normal; //正常模式
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;//优先级为中
DMA_InitStruct.DMA_FIFOMode=DMA_FIFOMode_Disable;//不用FIFO
DMA_InitStruct.DMA_FIFOThreshold=DMA_FIFOThreshold_Full; //FIFo配置阈值为满,FIFO被禁止,满不满都不起作用
DMA_InitStruct.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;//突增模式
DMA_InitStruct.DMA_MemoryBurst=DMA_MemoryBurst_Single;//突增模式
DMA_Init(DMA_Streamx,&DMA_InitStruct);
}
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)//DMA使能
{
DMA_Cmd( DMA_Streamx,DISABLE); //禁止数据流
while(DMA_GetCmdStatus(DMA_Streamx)==1); //等待禁止成功
DMA_SetCurrDataCounter(DMA_Streamx, ndtr);//设置数据量
DMA_Cmd( DMA_Streamx,ENABLE); //使能数据流
}
主程序:触发DMA
#include "main.h"
#define SEND_BUF_SIZE 8200
unsigned char SendBuff[SEND_BUF_SIZE];
unsigned char buff1[]={"哥们你看到了吗,这是DMA传输1234 \r\n "};
int main(void)
{
u8 key; //保存键值
u32 i;
delay_init(168); //初始化延时函数
uart_init(115200);
LED_Init(); //初始化LED端口
BEEP_Init(); //初始化蜂鸣器端口
KEY_Init(); //初始化与按键连接的硬件接口
MYDMA_Config(DMA2_Stream7,DMA_Channel_4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);//初始化DMA
for( i=0;i<8200;i=i+sizeof(buff1)-1 ) //填充数据到数组,填满
{
memcpy( SendBuff+i,buff1 ,sizeof(buff1) );
}
while(1)
{
key=KEY_Scan(0); //得到键值
if(key)
{
switch(key)
{
case WKUP_PRES: //控制蜂鸣器
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
MYDMA_Enable(DMA2_Stream7,SEND_BUF_SIZE);//此时可以执行其他任务
while(1)//以下为测试用到的,等待DMA传输完成,正常这里用不到,仅仅测试用
{
if(DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7)==SET)
{
DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);
break;
}
}
break;
case KEY0_PRES: //控制LED0翻转
LED0=!LED0;
printf("%s ",SendBuff);
break;
case KEY1_PRES: //控制LED1翻转
LED1=!LED1;
break;
case KEY2_PRES: //同时控制LED0,LED1翻转
LED0=!LED0;
LED1=!LED1;
break;
}
}else delay_ms(10);
}
}