DMA实验

本实验要用到串口 1 的发送,属于 DMA2 的数据流 7,通道 4,接 下来我们就介绍下使用库函数的配置步骤和方法。

1)使能 DMA2 时钟,并等待数据流可配置。

DMA 的时钟使能是通过 AHB1ENR 寄存器来控制的,这里我们要先使能时钟,才可以配置 DMA 相关寄存器。所以先要使能 DMA2 的时钟。另外,要对配置寄存器(DMA_SxCR)进行设置,必须 先等待其最低位为 0(也就是 DMA 传输禁止了),才可以进行配置。

库函数使能 DMA2 时钟的方法为:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能

等待 DMA 可配置,也就是等待 DMA_SxCR 寄存器最低位为 0 的方法为:

while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待 DMA 可配置

2) 初始化 DMA2 数据流 7,包括配置通道,外设地址,存储器地址,传输数据量等。

DMA 的某个数据流各种配置参数初始化是通过 DMA_Init 函数实现的:

void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);

typedef struct

{

uint32_t DMA_Channel;

uint32_t DMA_PeripheralBaseAddr;

uint32_t DMA_Memory0BaseAddr;

uint32_t DMA_DIR; uint32_t DMA_BufferSize;

uint32_t DMA_PeripheralInc;

uint32_t DMA_MemoryInc;

uint32_t DMA_PeripheralDataSize;

uint32_t DMA_MemoryDataSize;

uint32_t DMA_Mode;

uint32_t DMA_Priority;

uint32_t DMA_FIFOMode;

uint32_t DMA_FIFOThreshold;

uint32_t DMA_MemoryBurst;

uint32_t DMA_PeripheralBurst;

}DMA_InitTypeDef;

第一个参数 DMA_Channel 用来设置 DMA 数据流对应的通道。可供每 个数据流选择的通道请求多达 8 个,取值范围为:DMA_Channel_0~ DMA_Channel_7。

第二个参数 DMA_PeripheralBaseAddr 用来设置 DMA 传输的外设基地址,比如要进行串口 DMA 传输,那么外设基地址为串口接受发送数据存储器 USART1->DR 的地址,表示方法为 &USART1->DR。

第三个参数 DMA_Memory0BaseAddr 为内存基地址,也就是存放 DMA 传输数据的内存地 址。

第四个参数 DMA_DIR 设置数据传输方向,决定是从外设读取数据到内存还送从内存读取数据发送到外设,也就是外设是源地还是目的地,这里设置为从内存读取数据发送到串口, 所以外设自然就是目的地了,所以选择值为 DMA_DIR_PeripheralDST。

第五个参数 DMA_BufferSize 设置一次传输数据量的大小。

第六个参数 DMA_PeripheralInc 设置传输数据的时候外设地址是不变还是递增。如果设置 为递增,那么下一次传输的时候地址加 1,这里因为我们是一直往固定外设地址&USART1->DR 发送数据,所以地址不递增,值为 DMA_PeripheralInc_Disable;

第七个参 数 DMA_MemoryInc 设置传输数据时候内存地址是否递增。这个参数和 DMA_PeripheralInc 意思接近,只不过针对的是内存。这里我们的场景是将内存中连续存储单 元的数据发送到串口,毫无疑问内存地址是需要递增的,所以值为 DMA_MemoryInc_Enable。

第八个参数 DMA_PeripheralDataSize 用来设置外设的数据长度是为字节传输(8bits),半字传输 (16bits)还是字传输(32bits),这里我们是8位字节传输,所以值设置为DMA_PeripheralDataSize_Byte。

第九个参数 DMA_MemoryDataSize 是用来设置内存的数据长度,和第八个参数意思接近,这 里我们同样设置为字节传输 DMA_MemoryDataSize_Byte。

第十个参数 DMA_Mode 用来设置 DMA 模式是否循环采集,比如我们要从内存中采集 64 个字节发送到串口,如果设置为重复采集,那么它会在 64 个字节采集完成之后继续从内 存的第一个地址采集,如此循环。这里我们设置为一次连续采集完成之后不循环。所以设置值 为 DMA_Mode_Normal。

第十一个参数 DMA_Priority 是用来设置 DMA 通道的优先级,有低,中,高,超高三种模式,这里我们设置优先级别为中级,所以值为 DMA_Priority_Medium。优先级可以随便设置,因为我们只有一个数据流被开启了。假设有多个数据流开启(最多 8 个),那么就要设置优先级了,DMA 仲裁器将根据这些优先级的设置来决定先执行那个数据流的 DMA。优先级越高的,越早执行,当优先级相同的时候,根据硬件上的编号来决定哪个先执行(编号越小越优先)。

第十二个参数 DMA_FIFOMode 用来设置是否开启 FIFO 模式。不开启所以选择 DMA_FIFOMode_Disable。

第十三个参数 DMA_FIFOThreshold 用来选择 FIFO 阈值。根据前面讲解可以为 FIFO 容量的 1/4,1/2,3/4 以及 1 倍。

第十四个参数 DMA_MemoryBurst 用来配置存储器突发传输配置。可以选择为 4 个节拍的增 量突发传输 DMA_MemoryBurst_INC4,8 个节拍的增量突发传输 DMA_MemoryBurst_INC8,16 个街 拍的增量突发传输 DMA_MemoryBurst_INC16 以及单次传输 DMA_MemoryBurst_Single。

第十五个参数 DMA_PeripheralBurst 用来配置外设突发传输配置。跟前面一个参数 DMA_MemoryBurst 作用类似,只不过一个针对的是存储器,一个是外设。这里选择单次传 输 DMA_PeripheralBurst_Single。

3)使能串口 1 的 DMA 发送

进行 DMA 配置之后,开启串口的 DMA 发送功能,使用的函数是: USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1 的 DMA 发送

如果是要使能串口 DMA 接受,那么第二个参数修改为 USART_DMAReq_Rx 即可。

4)使能 DMA2 数据流 7,启动传输。

使能 DMA 数据流的函数为: void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState)

使能 DMA2_Stream7,启动传输的方法为: DMA_Cmd (DMA2_Stream7,ENABLE);

5)查询 DMA 传输状态

在 DMA 传输过程中,我们要查询 DMA 传输通道的状态,使用的函数是:

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)

比如我们要查询 DMA 数据流 7 传输是否完成,方法是:

DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7);

获取当前剩余数据量大小的函数:

uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx);

比如我们要获取 DMA 数据流 7 还有多少个数据没有传输,方法是:

DMA_GetCurrDataCounter(DMA1_Channel4);

设置对应的 DMA 数据流传输的数据量大小,函数为:

void DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter);

程序

//DMAx 的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8 位数据宽度/存储器增量模式
//DMA_Streamx:DMA 数据流,DMA1_Stream0~7/DMA2_Stream0~7
//chx:DMA 通道选择, @ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址 mar:存储器地址 ndtr:数据传输量 
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{ 
DMA_InitTypeDef DMA_InitStructure;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前 stream 是属于 DMA2 还是 DMA1
{
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能
}else 
{
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1 时钟使能
}
 DMA_DeInit(DMA_Streamx);
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待 DMA 可配置
 /* 配置 DMA Stream */
 DMA_InitStructure.DMA_Channel = chx; //通道选择
 DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA 外设地址
 DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器 0 地址
 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式
 DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//外设非增量模式
 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
//外设数据长度:8 位
 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
//存储器数据长度:8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式禁止 
 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//FIFO 阈值
 DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
//存储器突发单次传输
 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
//外设突发单次传输
 DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化 DMA Stream
} 
//开启一次 DMA 传输
//DMA_Streamx:DMA 数据流,DMA1_Stream0~7/DMA2_Stream0~7 
//ndtr:数据传输量 
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
DMA_Cmd(DMA_Streamx, DISABLE); //关闭 DMA 传输
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //确保 DMA 可以被设置 
DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量 
DMA_Cmd(DMA_Streamx, ENABLE); //开启 DMA 传输
}

该部分代码仅仅 2 个函数,MYDMA_Config 函数,该函数是一个通用的 DMA 配置函数,DMA1/DMA2 的所有通道,都可以利用该函数配置,不过有些固定参数可能要适当修改(比如位宽,传输方向等)。该函数在外部只能修改 DMA 及数据流编号、通道号、外设地址、存储器地址(SxM0AR)传输数据量等几个参数,更多的 其他设置只能在该函数内部修改。MYDMA_Enable函数就是设置DMA缓存大小并且使能DMA 数据流。

mian 函数

#define SEND_BUF_SIZE 8200	//发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.

u8 SendBuff[SEND_BUF_SIZE];	//发送数据缓冲区
const u8 TEXT_TO_SEND[]={"ALIENTEK Explorer STM32F4 DMA 串口实验"};	 

  
int main(void)
{ 
	u16 i;
	u8 t=0;
	u8 j,mask=0;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);     //初始化延时函数
	uart_init(115200);	//初始化串口波特率为115200
	LED_Init();					//初始化LED 
 	LCD_Init();					//LCD初始化 
	KEY_Init(); 				//按键初始化 
 	MYDMA_Config(DMA2_Stream7,DMA_Channel_4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);//DMA2,STEAM7,CH4,外设为串口1,存储器为SendBuff,长度为:SEND_BUF_SIZE.
 //显示提示信息	
	j=sizeof(TEXT_TO_SEND);	   
	for(i=0;i<SEND_BUF_SIZE;i++)//填充ASCII字符集数据
    {
		if(t>=j)//加入换行符
		{
			if(mask)
			{
				SendBuff[i]=0x0a;
				t=0;
			}else 
			{
				SendBuff[i]=0x0d;
				mask++;
			}	
		}else//复制TEXT_TO_SEND语句
		{
			mask=0;
			SendBuff[i]=TEXT_TO_SEND[t];
			t++;
		}   	   
    }		 
	
	i=0;
	while(1)
	{
		t=KEY_Scan(0);
		if(t==KEY0_PRES)  //KEY0按下
		{
			printf("\r\nDMA DATA:\r\n"); 	        
      USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);  //使能串口1的DMA发送     
			MYDMA_Enable(DMA2_Stream7,SEND_BUF_SIZE);     //开始一次DMA传输!	  
		    //等待DMA传输完成,此时我们来做另外一些事,点灯
		    //实际应用中,传输数据期间,可以执行另外的任务
		    while(1)
		    {
				if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)//等待DMA2_Steam7传输完成
				{ 
					DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清除DMA2_Steam7传输完成标志
					break; 
		        }  
		    }			    
			printf("Transimit Finished!");//提示传送完成
		}
		i++;
		delay_ms(10);
		if(i==20)
		{
			LED0=!LED0;//提示系统正在运行	
			i=0;
		}		   
	}		    
}

运行视频

DMA实验

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值