STM32 USART 使用DMA 详解

前言(绕开吧):
       这段时间由于我们的项目Manibus板卡需要融入 WIFI, BLT, 网口,CAN,串口的多位一体通讯,互不干扰,而且可以相互调用彼此进行数据通讯,这里为了节省MCU资源,所以就使用DMA的方式来进行串口 和 ESP8266的通讯,接下来就介绍一下具体的操作内容!

        DMA具体的不介绍,总的来说,他就是一个中转站,数据给DMA,他帮你传递或接受,你只要读就行了!!
        接下来看代码!

        

void localUsartDMAConfig(void){

    DMA_InitTypeDef DMA_InitStructure;
	
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	  DMA_DeInit(DMA1_Channel4);
	   
	  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_Send_Branch_Buffer;
	  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	  DMA_InitStructure.DMA_BufferSize = USART1_BUR_MAX;
	  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	  DMA_InitStructure.DMA_Priority =DMA_Priority_High;
	  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	  DMA_Init(DMA1_Channel4,&DMA_InitStructure);
	  
	  DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
	  DMA_ITConfig(DMA1_Channel4,DMA_IT_TE,ENABLE); 		
		USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
		
		DMA_Cmd(DMA1_Channel4,DISABLE);
	
	  DMA_DeInit(DMA1_Channel5); 
		DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);
		DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_Rev_Branch_Buffer;
	  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
		DMA_InitStructure.DMA_BufferSize = USART1_BUR_MAX;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
		DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
		DMA_InitStructure.DMA_Priority =DMA_Priority_High;
		DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
		DMA_Init(DMA1_Channel5,&DMA_InitStructure);
		
		DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
	  DMA_ITConfig(DMA1_Channel5,DMA_IT_TE,ENABLE);
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
		
		DMA_Cmd(DMA1_Channel5,ENABLE);
		
}  

这里我们使用的是 USART1, 其对应的DMA 是 TX ->DMA1_Channel4,RX0->DMA1_Channel5, 这里我们还是使用DMA中断,就是DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
  DMA_ITConfig(DMA1_Channel4,DMA_IT_TE,ENABLE);
这样的好处就是,当数据接收满 或者发送完成后,他能自动重新的让其不使能,或者重新更新!


static void NVIC_Configuration(void)
{
		NVIC_InitTypeDef NVIC_InitStructure;

		//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
		NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);

		NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);

		NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);
	
}

这里我们配置NVIC的中断,这里有让DMA有更高的权限,这是为了让他能及时更新!

void localUsartConfig(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
		USART_InitTypeDef USART_InitStructure;

		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStructure);

		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_Init(GPIOA, &GPIO_InitStructure);


		USART_InitStructure.USART_BaudRate = 115200;
		USART_InitStructure.USART_WordLength = USART_WordLength_8b;
		USART_InitStructure.USART_StopBits = USART_StopBits_1;
		USART_InitStructure.USART_Parity = USART_Parity_No ;
		USART_InitStructure.USART_HardwareFlowControl = 
		USART_HardwareFlowControl_None;
		USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

		USART_Init(USART1, &USART_InitStructure);

		USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
		
		USART_Cmd(USART1, ENABLE);
		USART_ClearFlag(USART1,USART_FLAG_TC);

		localUsartDMAConfig();    //Set DMA 
    NVIC_Configuration();     //Set NVIC

		USART1_BASE_HAS_BEEN_INIT_FLAG =1;
	
}

这里配置 USART1,都是常规操作,这里我们使用的是IDLE空闲中断,就是发送一段字节后,串口空闲 没接受下一个数据了就会中断,也就是一个数据包,(以后都为废话)这样也适合我们的项目要求有关,分总线的通讯方式(分线向执行总线传递数据包)这里就要求不能分线数据交错!

void Usart_ReadArray_(uint8_t *array,u16 length)
{
	
	   if(length){			 
		 length =  length > USART1_BUR_MAX ? 1024: length; 
		 }else{		 
		 return;
		 }	 
     while(USART1_SendFlag_End ==0);
		  
	   if(array)memcpy(USART1_Send_Branch_Buffer,array,length);
		 
		 DMA_ClearITPendingBit(DMA1_IT_TC4);
	   DMA_Cmd(DMA1_Channel4, DISABLE);
	   DMA1_Channel4->CNDTR= length;
		 DMA_Cmd(DMA1_Channel4, ENABLE);
		 USART1_SendFlag_End = 0;	 
		 
}

这里不要被函数名误解,功能就是一个串口通过DMA发送字符串的函数! 接下来就到重点了,在配置DMA发送函数的时候,我们设置DMA1_Channel4 的使能为DISABLE,是为了不让他一直发送,配置了ENABLE,你会发现它一直发送 00 00.。。。。这也是很好理解的,所以我们在需要的时候让他使能发送! 函数中有个while是为了让他把已有的数据发送完成,再进行下一个数据包的发送!

void USART1_IRQHandler(void){

	u16 data_len;

 if(USART_GetITStatus(USART1,USART_IT_IDLE)!= RESET){
		
		data_len  =USART1->SR;
		data_len  =USART1->DR;	
	 
		DMA_Cmd(DMA1_Channel5,DISABLE);
		data_len = USART1_BUR_MAX - DMA_GetCurrDataCounter(DMA1_Channel5);
	 
		Usart_ReadArray_(USART1_Rev_Branch_Buffer,data_len);
	
	 
	 	DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_TE5 | DMA1_FLAG_HT5); 
		DMA1_Channel5->CNDTR = USART1_BUR_MAX;
	  
    DMA_Cmd(DMA1_Channel5,ENABLE);
 
	  USART_ClearITPendingBit(USART1, USART_IT_IDLE);
	}
}

这里判断时候产生空闲中断!  然后我们需要USART_ReceiveData 这里和先USART1->SR;USART1->DR; 一样;然后读取数据段长度;这里我进行读取在发送进行验证! 后面清除各种标志位;然后DMA重装Size, 使能接收DMA,让其继续接收;最后清除空闲中断的标志位!


void DMA1_Channel4_IRQHandler(void){

    DMA_ClearITPendingBit(DMA1_IT_TC4);
	  DMA_ClearITPendingBit(DMA1_IT_TE4);
    DMA_Cmd(DMA1_Channel4, DISABLE);
	  USART1_SendFlag_End = 1; 

}

void DMA1_Channel5_IRQHandler(void){
    
	  DMA_ClearITPendingBit(DMA1_IT_TC5);
	  DMA_ClearITPendingBit(DMA1_IT_TE5);
    DMA_Cmd(DMA1_Channel5, DISABLE); 
    DMA1_Channel5->CNDTR = USART1_BUR_MAX;
	  DMA_Cmd(DMA1_Channel5, ENABLE);
	
}

这里为DMA中断,发送中断是在发送完成后产生的,重新跟新标志位证明发送完成!  接收中断是在接收字节满了后产生的,重装Size!
以上基本结构就配置完成了,有误希望指正!

猜你喜欢

转载自blog.csdn.net/qq_37389133/article/details/79597774