直接存储器存取(DMA)简介及程序设计

描述

1.DMA简介

      直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。
      STM32F10x中有两个DMA控制器(DMA1有7个通道,DMA2有5个通道)每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

DMA1请求映像
  USART1_TX – DMA1_Chnanel4
  USART1_RC – DMA1_Channel5
  利用DMA功能完成串口数据处理,无需CPU干预,减少CPU占用。

dma

2.程序设计

 1.DMA1_CH4配置

/******DMA配合串口1发送数据************
**形参:u32 cpar -- 外设地址
**
**例:DMA_CH4_Init(&USART1->DR,buff)
**外设地址:USART1->DR的地址
**					buff的地址
**************************************/
void DMA_CH4_Init(u32 cpar)
{
	RCC->AHBENR|=1<<0;//dma1时钟使能
	DMA1_Channel4->CCR&=~(1<<14);//非存储器到存储器模式
	DMA1_Channel4->CCR|=0x3<<12;//设置CH4优先级为最高
	DMA1_Channel4->CCR&=~(0x3<<10);//存储器数据宽度8位
	DMA1_Channel4->CCR&=~(0x3<<8);//外设数据宽度8位	
	DMA1_Channel4->CCR|=1<<7;//存储器地址增量
	DMA1_Channel4->CCR&=~(1<<6);//外设地址不增量
	DMA1_Channel4->CCR&=~(1<<5);//不执行循环操作
	DMA1_Channel4->CCR|=1<<4;//从存储器读
	DMA1_Channel4->CPAR=cpar;//外设地址
}
/**********开启DMA1_CH4数据传输*************
***
***形参:u16 data_len -- DMA要传输数目
***********************************************/
void DMA_CH4_Start(u8 *buff,u16 data_len)
{
	DMA1_Channel4->CMAR=(u32)buff;//存储器地址
	DMA1_Channel4->CCR&=~(1<<0);//关闭通道传输
	DMA1_Channel4->CNDTR=data_len;//设置传输数量
	DMA1_Channel4->CCR|=1<<0;//开启通道传输
}

  2.DMA1_CH5配置

/******DMA配合串口1接收数据************
**形参:u32 cpar -- 外设地址
**			u32 cmar -- 存储器地址
**
**例:DMA_CH5_Init(&USART1->DR,buff)
**外设地址:USART1->DR的地址
**					buff的地址
**************************************/
void DMA_CH5_Init(u32 cpar,u32 cmar)
{
	RCC->AHBENR|=1<<0;//dma1时钟使能
	DMA1_Channel5->CCR&=~(1<<14);//非存储器到存储器模式
	DMA1_Channel5->CCR|=0x3<<12;//设置CH4优先级为最高
	DMA1_Channel5->CCR&=~(0x3<<10);//存储器数据宽度8位
	DMA1_Channel5->CCR&=~(0x3<<8);//外设数据宽度8位	
	DMA1_Channel5->CCR|=1<<7;//存储器地址增量
	DMA1_Channel5->CCR&=~(1<<6);//外设地址不增量
	DMA1_Channel5->CCR&=~(1<<5);//执行循环操作
	DMA1_Channel5->CCR&=~(1<<4);//从外设读
	DMA1_Channel5->CPAR=cpar;//外设地址
	DMA1_Channel5->CMAR=cmar;//存储器地址
	DMA1_Channel5->CCR&=~(1<<0);//关闭通道传输
	DMA1_Channel5->CNDTR=1024;//设置传输数量
	DMA1_Channel5->CCR|=1<<0;//开启通道传输
}

  3.USART1配置

/********************串口初始化函数封装*********************
****硬件接口:USART1_TX -- PA9(发送)
**						USART1-RX --PA10(接收)
**						USART2_TX -- PA2(发送)
**						USART2-RX --PA3(接收)
**						USART3_TX -- PB10(发送)
**						USART3_RX -- PB11(接收)
形参:USART_TypeDef *USARTx -- 要配置的哪个串口
**			u32 baud  --波特率
**			u32 sysclk --时钟频率(USART1 --72MHZ ,USAT2USART3 --36MHZ)
**
***********************************************************/
void Usartx_Init(USART_TypeDef *USARTx,u32 baud,u32 sysclk)
{
	if(USART1 == USARTx)
	{
			/*1.开时钟*/
		RCC->APB2ENR|=1<<2;//PA时钟
		RCC->APB2ENR|=1<<14;//串口时钟
		RCC->APB2RSTR|=1<<14;//串口复位
		RCC->APB2RSTR&=~(1<<14);//取消复位
		/*2.配置GPIO口*/
		GPIOA->CRH&=0xFFFFF00F;
		GPIOA->CRH|=0x000008B0;//上下拉输入,复用推挽输出	
		USART1->CR3|=1<<7;//DMA发送
		USART1->CR3|=1<<6;//DMA接收
		USART1->CR1|=1<<4;//IDLE(空闲帧中断)
		STM32_NVIC_SetPriority(USART1_IRQn,0,1);//设置优先级
	}
	else if(USART2 == USARTx)
	{
		/*1.开时钟*/
		RCC->APB2ENR|=1<<2;//PA时钟
		RCC->APB1ENR|=1<<17;//USART2时钟
		RCC->APB1RSTR|=1<<17;//开复位时钟
		RCC->APB1RSTR&=~(1<<17);//取消复位
		/*2.配置GPIO口*/
		GPIOA->CRL&=0xFFFF00FF;//清除原来寄存器中的值
		GPIOA->CRL|=0x00008B00;		
		#ifdef USART2_IRQ
			USART2->CR1|=1<<5;//串口2接收中断
			STM32_NVIC_SetPriority(USART2_IRQn,1,2);//设置优先级
		#endif
	}
	else if(USART3 == USARTx)
	{
		/*1.开时钟*/
		RCC->APB2ENR|=1<<3;//PB时钟
		RCC->APB1ENR|=1<<18;//USART3时钟
		RCC->APB1RSTR|=1<<18;//开复位时钟
		RCC->APB1RSTR&=~(1<<18);//取消复位
		/*2.配置GPIO口*/
		GPIOB->CRH&=0xFFFF00FF;
		GPIOB->CRH|=0x00008B00;	
		#ifdef USART3_IRQ
			USART3->CR1|=1<<5;//开启接收中断
			STM32_NVIC_SetPriority(USART3_IRQn,0,0);//设置优先级
		#endif
	}
	else return;
	/*3.配置串口核心寄存器*/
	USARTx->BRR=sysclk*1000000/baud;//设置波特率
	USARTx->CR1|=1<<2;//接收使能
	USARTx->CR1|=1<<3;//发送使能
	USARTx->CR1|=1<<13;//使能串口3
	#ifdef DMA_USART1_SEND //利用DMA完成数据收发
		DMA_CH5_Init((u32 )&USART1->DR,(u32 )usart1_rx_buff);//DMA配合串口1接收数据
		DMA_CH4_Init((u32 )&USART1->DR);
	#endif
}

4.串口1中断,DMA数据接收数据

u8 usart1_rx_buff[1024];//串口1接收数据缓冲区
u16 usart1_cnt=0;//保存数组下班
u8 usart1_flag;//接收完成标志符
void USART1_IRQHandler(void)
{
	U8 C;
	//清除标志:先读USART_SR,再读USART_DR
	if(USART1->SR&1<<4)//空闲帧
	{
		C=USART1->DR;
		C=c;
       // USART1->DR=c;
		Usart1_Receive_Data();//接收数据处理函数
	}
	USART1->SR=0;//清除标志位
}

/*************************串口接收数据函数********************/
void Usart1_Receive_Data(void)
{
	DMA1_Channel5->CCR&=~(1<<0);//关闭通道传输
	usart1_cnt=1024-DMA1_Channel5->CNDTR;//获取接收到是字符长度
	if(usart1_cnt>=1024)usart1_cnt=0;
	DMA1->IFCR|=1<<17;//清除标志位
	DMA1_Channel5->CNDTR=1024;//从新赋值
	DMA1_Channel5->CCR|=1<<0;//开启通道传输
	usart1_flag=1;
}

  5.串口数据发送

/****************串口发送字符串*****************************/
void Usart1_SendStr(u8 *str)
{
	#ifdef DMA_USART1_SEND
		DMA_CH4_Start(str,strlen((char *)str));
	#else
		while(*str!='�')
		{
			USART1->DR=*str++;
			while(!(USART1->SR&1<<7));
		}
	#endif
}

      6.硬件初始化,DMA配合串口调试

int main()
{
	u8 key;
	Beep_Init();//蜂鸣器初始化
	Led_Init();//LED初始化
	Key_Init();//按键初始化
	Usartx_Init(USART1,115200,72);
	while(1)
	{
		key=Key_Scan();
		if(key)
		{
			Usart1_SendStr((u8 *)"DMA1配合串口收发数据不定长测试234556789rn");
		}
		if(usart1_flag)
		{
			usart1_rx_buff[usart1_cnt]='�';
			printf("%srn",usart1_rx_buff);
			usart1_flag=0;
		}
	}
}

      7.运行效果

dma

 

 

  审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分