2019年3月13日-HAL库UART使用DMA收发(二)
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寄存器的;
其中空闲帧处理程序如下:
/**
* @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);
}
}
程序目前正常运行,未发生明显错误;
附录: