STM32F103ZET6串口使用USAR_TFLAG_IDLE空闲中断实现UART_DMA接收和发送不定长数据

本文是实现STM32F103ZET6串口通过使用STM32的IDLE空闲中断(USAR_TFLAG_IDLE)实现UART_DMA接收和发送(Rx和Tx均通过DMA通道)不定长数据。本文实现了UART1_DMA、UART2_DMA、UART3_DMA、UART4_DMA,文末均有代码下载

本文以UART1_DMA为例。

一、为什么要使用 UART DMA 传输

直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。通俗的讲就是CPU不需要负责具体数据的收发,只要告诉DMA将一块数据从某处搬运到某处,搬完后,CPU再去使用。

二、STM32F103ZET6 DMA控制器

STM32F103ZET6有2个DMA控制器,UART1/2/3在DMA1控制器,UART4在DMA2控制器,UART5不支持DMA功能。

外设的DMA请求,可以通过设置相应外设寄存器中的控制位,被独立地开启或关闭。查询手册,STM32F103ZET6的DMA通道如下表,由表可知,UART1的TX是通道4,RX是通道5。

如图:

STM32F103ZET6串口使用USAR_TFLAG_IDLE空闲中断实现UART_DMA接收和发送不定长数据_第1张图片

STM32F103ZET6串口使用USAR_TFLAG_IDLE空闲中断实现UART_DMA接收和发送不定长数据_第2张图片

 代码:

(1)串口初始化

/**********************************************************************
  * @brief  USART1初始化
  * @param  null
  * @retval null
  **********************************************************************/
void USART1_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	//打开串口GPIO时钟
	USART1_GPIO_APBxClkCmd(USART1_GPIO_CLK, ENABLE);
	//打开串口外设时钟
	USART1_APBxClkCmd(USART1_CLK, ENABLE);
	//将USART_Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = USART1_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(USART1_TX_GPIO_PORT, &GPIO_InitStructure);
	//将USART_Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = USART1_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(USART1_RX_GPIO_PORT, &GPIO_InitStructure);
	//配置串口工作参数
	//配置波特率
	USART_InitStructure.USART_BaudRate = USART1_BAUDRATE;
	//配置针数数据字长
	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);
	//配置串口中断优先级
	NVIC_Configuration(USART1_IRQ);
	//使能串口接收中断,接收一帧数据产生USART_IT_IDLE中断
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); 
    //使能串口接收中断,发送完一帧数据产生USART_IT_TC中断
	USART_ITConfig(USART1, USART_IT_TC,   ENABLE);
	//使能串口
	USART_Cmd(USART1, ENABLE);	    
}

 

(2)USARTx_TX DMA初始化

/**
  * @brief  USARTx TX DMA 配置,内存到外设(USART1->DR)
  * @param  null
  * @retval null
  */
void USART1_TX_DMA_Config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	DMA_DeInit(USART1_TX_DMA_CHANNEL);		//USART_TX_DMA

	//开启DMA时钟,DMA1
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	//设置DMA源地址,串口数据寄存器地址
	DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_ADDRESS;
	//内存地址(要传输的变量的指针)
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_TX_BUF;
	//方向:从内存到外设
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	//传输大小
	DMA_InitStructure.DMA_BufferSize = 0;
	//外设地址不增    
	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模式,一次或者循环模式
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
	//优先级:中
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
	//禁止内存到内存传输
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	//配置DMA UART TX通道   
	DMA_Init(USART1_TX_DMA_CHANNEL, &DMA_InitStructure);		
	USART_DMACmd(USART1, USART_DMAReq_Tx , ENABLE);
	//使能DMA TX通道
	DMA_Cmd(USART1_TX_DMA_CHANNEL,DISABLE);
}

(3)USARTx_RX DMA初始化

/**
  * @brief  USARTx RX DMA 配置,外设到内存(USART1->DR)
  * @param  null
  * @retval null
  */
void USART1_RX_DMA_Config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	DMA_DeInit(USART1_RX_DMA_CHANNEL);  // USART_TX_DMA

	//开启DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	//设置DMA源地址,串口数据寄存器地址
	DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_ADDRESS;
	//内存地址(要传输的变量的指针)
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_RX_BUF;
	//方向:外设到内存
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC ;
	//传输大小
	DMA_InitStructure.DMA_BufferSize = sizeof(USART1_RX_BUF);
	//外设地址不增   
	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模式,一次或者循环模式
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
	//优先级:中
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
	//禁止内存到内存的传输  
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	// 配置DMA通道
	DMA_Init(USART1_RX_DMA_CHANNEL , &DMA_InitStructure);	
	USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
	//使能DMA_Rx通道
	DMA_Cmd(USART1_RX_DMA_CHANNEL,ENABLE);
}

(4)串口中断&DMA接收数据

/***********************************************************************
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  null
  * @retval null
  **********************************************************************/
static void NVIC_Configuration(uint8_t NVIC_IRQChannel)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	/* 嵌套向量中断控制器组选择 */
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
	/* 配置USART为中断源 */
	NVIC_InitStructure.NVIC_IRQChannel = NVIC_IRQChannel;
	/* 抢断优先级 */
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	/* 子优先级 */
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	/*使能中断 */
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	/* 初始化配置NVIC */
	NVIC_Init(&NVIC_InitStructure);
}





 /***********************************************************************
  * @brief  USART1中断
  * @param  null
  * @retval null
  **********************************************************************/
void USART1_IRQHandler(void)
{
	uint8_t clear = clear;
	//串口接收一次数据结束
	if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE) !=RESET )
	{
		clear = USART1->SR;
		clear = USART1->DR;
		//清除USART1空闲中断标志
		USART_ClearITPendingBit(USART1, USART_FLAG_IDLE);  
		USART1_Rx_Len = UART_BUF_SIZE - DMA_GetCurrDataCounter(USART1_RX_DMA_CHANNEL);
		//清除RX_DMA的传输寄存器
		USART1_DMA_RX_CLC(USART1_RX_DMA_CHANNEL);//RX
		
		//Todo
		//...
		//对收到的数据再此进行解析
		//本Demo将接收到的数据复制到发送数据,发送出去。即收发的数据原封不动发送出去
		//...
		
		USART1_Tx_Len = USART1_Rx_Len;
		memcpy(USART1_TX_BUF,USART1_RX_BUF,USART1_Rx_Len); 
		//发送数据
		USART1_DMA_SendBuf(USART1_TX_BUF, USART1_Tx_Len);
	}
	//数据发送完成中断
    if(USART_GetITStatus(USART1, USART_IT_TC) != RESET)   
    {
		//清除数据发送完成中断
        USART_ClearITPendingBit(USART1, USART_IT_TC);  
		//关闭SART1 DMA Tx通道
        DMA_Cmd(USART1_TX_DMA_CHANNEL, DISABLE);    
		//初始化接收发送数组等
		USART1_Rx_Len = 0;
		USART1_Tx_Len = 0;
		memset(USART1_RX_BUF,0,UART_BUF_SIZE);
		memset(USART1_TX_BUF,0,UART_BUF_SIZE);
	}
}

(5)DMA发送数据

//开启一次DMA数据传输
void USART1_DMA_RX_CLC(DMA_Channel_TypeDef *DMA_CHx)
{ 
	DMA_Cmd(DMA_CHx, DISABLE );  //关闭SART1 TX DMA1所指示的通道      
 	DMA_SetCurrDataCounter(DMA_CHx,UART_BUF_SIZE);//设置DMA通道的DMA缓存大小
 	DMA_Cmd(DMA_CHx, ENABLE);  //使能USART1 TX DMA1所指示的通道
}

//USART1 DMA发送数据
void USART1_DMA_SendBuf(uint8_t *arr, uint8_t len)
{
	uint8_t sendLen= 0;
	if(len == 0)
	{		
		return;
	}
	
	sendLen = len > UART_BUF_SIZE ? UART_BUF_SIZE : len;	
    //判断DMA通道里是否还有数据,即上一帧数据是否发送完毕
    while(DMA_GetCurrDataCounter(USART1_TX_DMA_CHANNEL)); 
   
	if(arr) 
	{
		memcpy(USART1_TX_BUF, arr, sendLen);
	}
	//关闭USART1_DMA_Tx通道
    DMA_Cmd(USART1_TX_DMA_CHANNEL, DISABLE);
	//设置DMA_TX要传输的数据长度
    DMA_SetCurrDataCounter(USART1_TX_DMA_CHANNEL, sendLen); 
	//使能USART1 DMA Tx通道,发送数据
    DMA_Cmd(USART1_TX_DMA_CHANNEL, ENABLE);
}

-------------------------------------------------

代码下载:

(1)STM32F103ZET6 UART1_DMA收发不定长数据:

https://download.csdn.net/download/wangzhichunnihao/69328698

(2)STM32F103ZET6 UART2_DMA收发不定长数据:

https://download.csdn.net/download/wangzhichunnihao/69329351

(3)STM32F103ZET6 UART3_DMA收发不定长数据:

https://download.csdn.net/download/wangzhichunnihao/69330092

(4)STM32F103ZET6 UART4_DMA收发不定长数据:

STM32F103ZET6UART4_DMA收发不定长数据-嵌入式文档类资源-CSDN下载

你可能感兴趣的:(ARM开发,stm32,uart,串口通信,dma,串口dma)