STM32CubeMX 串口空闲中断加DMA 实现多串口不定长度收发数据,且不影响收发

[亡魂溺海] 提交于 2020-02-29 13:30:39

主题思想:

接收:配置串口DMA接收,打开串口的空闲中断,但是配置DMA接收的长度一定要合适,小了的话容易溢出。然后在串口的空闲中断里,关闭DMA的接收,且取出DMA数据,转存也好,直接处理也好。完了之后,再次配置DMA接收,把地址指向DMA接受数组的开始。

发送:DMA发送时,置位标记,需等发送完成中断里清零标记,防止连续调用DMA发送,造成上次没发完,这次直接更改了DMA发送的指针。导致发送不完整。

一、CubeMX配置(图不是一个工程的,仅作参考)

  1. 配置串口,可配置多个串口,方式相同 串口配置DMA配置中断配置
  2. 配置完成后,根据使用习惯生成代码 生成配置

二、改写代码(代码是F103,用了Uart1和Uart3)

  1. 打开MDK,修改DMA处void MX_DMA_Init(void) 里,无需DMA接收中断,注释掉DMA接收中断,具体注释掉通道几,根据芯片来
    void MX_DMA_Init(void) 
    {
      /* DMA controller clock enable */
      __HAL_RCC_DMA1_CLK_ENABLE();
    
      /* DMA interrupt init */
      /* DMA1_Channel2_IRQn interrupt configuration */
      HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
      HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
      /* DMA1_Channel3_IRQn interrupt configuration */
    //  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
    //  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
      /* DMA1_Channel4_IRQn interrupt configuration */
      HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
      HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
      /* DMA1_Channel5_IRQn interrupt configuration */
    //  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
    //  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
    
    }

     

  2. 在usart.h文件里定义串口接收数据类型我是如下定义的(usart.h)

/* USER CODE BEGIN Private defines */
#define RECEIVELEN 64
#define USART_DMA_SENDING 1//发送未完成
#define USART_DMA_SENDOVER 0//发送完成
	 
#define MODBUD_SENDMODE 1	 
	 
typedef struct
{
	uint8_t Receive:1;//空闲接收标记
	uint8_t Send:1;//发送完成标记
	uint16_t RxLen;//接收长度
	uint8_t RxBuf[RECEIVELEN];//DMA接收缓存
}UART_T;

extern UART_T xtUart1,xtUart3;
/* USER CODE END Private defines */


/* USER CODE BEGIN Prototypes */
void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length);
void Usart3SendData_DMA(uint8_t *pdata, uint16_t Length);
void UsartReceive_IDLE(UART_HandleTypeDef *huart);

/* USER CODE END Prototypes */

3. 然后在usart.c里需要添加空闲中断处理函数,以及DMA发送函数等。(usart.c)

    1)首先定义数据类型:

/* USER CODE BEGIN 0 */
 
UART_T xtUart1,xtUart3;
 
/* USER CODE END 0 */

    2) 然后定义空闲中断处理函数和DMA发送函数 ,串口的Idle中断函数名需要申明到uart.h文件里,方便中断文件函数里调用

/* USER CODE BEGIN 1 */
#ifdef __GNUC__
 
  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
 set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
 
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
	
PUTCHAR_PROTOTYPE
{
	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}
 
//DMA发送函数
void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)
{
	if(Length == 0) return;
	while(xtUart1.Send == USART_DMA_SENDING){}
	xtUart1.Send = USART_DMA_SENDING;
	HAL_UART_Transmit_DMA(&huart1, pdata, Length);
}

void Usart3SendData_DMA(uint8_t *pdata, uint16_t Length)
{
	if(Length == 0 || xtUart3.Send == USART_DMA_SENDING) 
		return;
	while(xtUart3.Send == USART_DMA_SENDING){}
	TXMODE_485();
	xtUart3.Send = USART_DMA_SENDING;
	HAL_UART_Transmit_DMA(&huart3, pdata, Length);
	
	

}
 
//DMA发送完成中断回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	 __HAL_DMA_DISABLE(huart->hdmatx);
	
	if(huart->Instance == huart1.Instance)	
		xtUart1.Send = USART_DMA_SENDOVER;	
	else if(huart->Instance == huart3.Instance)
	{
		xtUart3.Send = USART_DMA_SENDOVER;
		RXMODE_485();
		LED1ON();
		
	}
}
 
static void HAL_UART_DMAStopRX(UART_HandleTypeDef *huart)
{
	CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);	
	HAL_DMA_Abort(huart->hdmarx);	
	CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
  CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
	/* At end of Rx process, restore huart->RxState to Ready */
  huart->RxState = HAL_UART_STATE_READY;
}

//串口接收空闲中断
void UsartReceive_IDLE(UART_HandleTypeDef *huart)
{
	if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
	{ 		
//		HAL_UART_DMAStop(huart);

		HAL_UART_DMAStopRX(huart);
		__HAL_UART_CLEAR_IDLEFLAG(huart);
		if(huart->Instance == huart1.Instance)
		{			
			xtUart1.RxLen =  RECEIVELEN - __HAL_DMA_GET_COUNTER(huart->hdmarx);
			xtUart1.Receive=1;
			HAL_UART_Receive_DMA(&huart1,xtUart1.RxBuf,RECEIVELEN);

		}
		else if(huart->Instance == huart3.Instance)
		{
			xtUart3.RxLen =  RECEIVELEN - __HAL_DMA_GET_COUNTER(huart->hdmarx);
			xtUart3.Receive=1;
			HAL_UART_Receive_DMA(&huart3,xtUart3.RxBuf,RECEIVELEN);
		}
	}
	LED1OFF();
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
//	__HAL_UART_GET_IT_SOURCE(huart,UART_IT_ERR);
	
	__HAL_UART_CLEAR_PEFLAG(huart);
	
	//UART_FLAG_ORE/UART_FLAG_NE/UART_FLAG_FE/UART_FLAG_PE
//	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_ORE))
//		__HAL_UART_CLEAR_OREFLAG(huart);
//	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_NE))
//		__HAL_UART_CLEAR_NEFLAG(huart);
//	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_FE))
//		__HAL_UART_CLEAR_FEFLAG(huart);
//	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_PE))
//		__HAL_UART_CLEAR_PEFLAG(huart);

}

/* USER CODE END 1 */

4. 在中断文件里添加(当然,上面的空闲中断处理函数需要声明)(stm32fxxxit.c)

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	UsartReceive_IDLE(&huart1);
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
 
  /* USER CODE END USART1_IRQn 1 */
}

5. 主函数里,打开空闲中断,初始化DMA接收

/* USER CODE BEGIN 2 */
__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_TC);
	__HAL_UART_CLEAR_FLAG(&huart3,UART_FLAG_TC);
	
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
	__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
	
	HAL_UART_Receive_DMA(&huart1,xtUart1.RxBuf,RECEIVELEN);
	HAL_UART_Receive_DMA(&huart3,xtUart3.RxBuf,RECEIVELEN);
  /* USER CODE END 2 */

6. 然后就可以在while(1)里处理数据了

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
	
  while (1)
  {
		if(xtUart1.Receive)//如果产生了空闲中断
		{
			xtUart1.Receive=0;//清零标记
			Usart1SendData_DMA(xtUart1.RxBuf,xtUart1.RxLen);//串口打印收到的数据。
		}
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
		
  }
  /* USER CODE END 3 */

修改的代码就到这里了。但是HAL库版本更迭,可能函数名会不同。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!