2019年3月13日-HAL库UART使用DMA收发(二)

1. 串口不定长的收发使用IDEL中断会比较好实现

之前已经完成了定长数据的接收、以及发送;
现在写不定长的接收,首先在函数中添加串口1的IDEL中断;

 __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);

然后在串口1的中断函数中添加空闲帧的中断处理

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	HAL_UART_IDLECallback(&huart1);
  /* USER CODE END USART1_IRQn 1 */
}

查看手册,注意到DMA stream x number of data register(DMA 数据流 x 数据项数寄存器)的描述,所以在执行

__HAL_DMA_SET_COUNTER(huart->hdmarx, USART_BUF_SIZE);
//((__HANDLE__)->Instance->NDTR = (uint16_t)(__COUNTER__))

之前、需要

__HAL_DMA_DISABLE(huart->hdmarx);

否则,是无法更改NDTR寄存器的;
2019年3月13日-HAL库UART使用DMA收发(二)
其中空闲帧处理程序如下:

/**
  * @brief  This function is HAL_UART_IDLECallback.
  * @retval None
  */
void HAL_UART_IDLECallback(UART_HandleTypeDef *huart)
{
	uint32_t clearStatus = 0;

	if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)
	{
		__HAL_UART_CLEAR_IDLEFLAG(huart);	
		
		clearStatus = huart->Instance->SR;
		clearStatus = huart->Instance->DR;
		clearStatus = clearStatus;
		
		__HAL_DMA_DISABLE(huart->hdmarx);
		
		aRX_Count = USART_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx);			
		memcpy(aRxBuffer_Save,aRxBuffer,aRX_Count);	
			
		__HAL_DMA_SET_COUNTER(huart->hdmarx, USART_BUF_SIZE);		
		__HAL_DMA_ENABLE(huart->hdmarx);
	}
}

在主函数中判断,当有数据进来的时候,原封不动发回去;

if(aRX_Count > 0)
	  {
		HAL_UART_Transmit_DMA(&huart1,aRxBuffer_Save,aRX_Count); // DMA发送出去
		  aRX_Count = 0;
	  } 

至此,使用DMA来进行UART的接收发送基本实现,但是还不能直接应用,需要进行一些问题的修复,比如DMA的中断处理等,关于FIFO的应用看了看,有些复杂,暂时先放一放。

注:设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxState为READY,并调用
UART接收完成中断的回调函数。 所以,不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数。

2.继续

在实际的调试过程中、发现当数据大于设置的DMA接收长度时,就不好用了;但是想想是环形接收,应该可以的。
于是,修改程序,当DMA中断时,即DMA2_Stream2_IRQn时,

memcpy(aRxBuffer_Save,aRxBuffer,USART_BUF_SIZE);
HAL_UART_Transmit_DMA(&huart1,aRxBuffer_Save,USART_BUF_SIZE); // DMA发送出去

将数据立刻发送出去,由于是环形队列,直接自动接收下一次的数据,不需要设置什么;
但是必须要注意到:

void DMA2_Stream2_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream2_IRQn 0 */

  /* USER CODE END DMA2_Stream2_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart1_rx);
  /* USER CODE BEGIN DMA2_Stream2_IRQn 1 */

  /* USER CODE END DMA2_Stream2_IRQn 1 */
}

这个中断会调用USART1_IRQHandler

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	HAL_UART_IDLECallback(&huart1);

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

所以修改空闲帧中断回调函数;

/**
  * @brief  This function is HAL_UART_IDLECallback.
  * @retval None
  */
void HAL_UART_IDLECallback(UART_HandleTypeDef *huart)
{
	uint32_t clearStatus = 0;

	if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)
	{
		__HAL_UART_CLEAR_IDLEFLAG(huart);
		
		clearStatus = huart->Instance->SR;
		clearStatus = huart->Instance->DR;
		clearStatus = clearStatus;
		
		HAL_NVIC_DisableIRQ(DMA2_Stream2_IRQn);		//如果不关中断、那么__HAL_DMA_DISABLE会触发DMA2_Stream2_IRQHandler
		
		__HAL_DMA_DISABLE(huart->hdmarx);
		
		aRX_Count = USART_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx);		
		memcpy(aRxBuffer_Save,aRxBuffer,aRX_Count);		
		__HAL_DMA_SET_COUNTER(huart->hdmarx, USART_BUF_SIZE);
		
		__HAL_DMA_CLEAR_FLAG(huart->hdmarx, DMA_FLAG_TCIF2_6);		//复位中断
		HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
		__HAL_DMA_ENABLE(huart->hdmarx);
		
	}
}

程序目前正常运行,未发生明显错误;

附录:
2019年3月13日-HAL库UART使用DMA收发(二)
2019年3月13日-HAL库UART使用DMA收发(二)
2019年3月13日-HAL库UART使用DMA收发(二)

2019年3月13日-HAL库UART使用DMA收发(二)