DMA 局限性
DMA 传输完成会产生中断告知 CPU,这对于固定长度的数据是没什么问题的。但是对于不定长的数据就不行了,DMA 一定要接收到足够多(设定的长度)的数据时才产生完成中断,如果接收到的数据量小于设定的长度,这个时候 CPU 就无法通过中断方式取处理这点数据了。那 CPU 怎样优化这个缺陷呢?那就是使用轮询的方式,主动获取 DMA 当前收到了多少字节数据,然后决定要不要处理这些数据。但是,一旦使用轮询方式就背离了 DMA 原有的设计意图(为 CPU 减负)。
那还有什么办法可以优雅地解决这个问题吗?那就是使用串口空闲中断。
串口空闲中断
这里是转变了一个思路,单纯从 DMA 角度解决问题貌似找不到很好的答案,那就转换到串口上来。因为本身的工作是串口 DMA 接收任意长度数据。
串口有个空闲中断,大概是串口总线在一个字节的时间内没有再接收到数据,认为一帧数据传输完毕了,就会产生串口空闲中断。
这样我们就不使用 DMA 中断了,只使用串口空闲中断,即在串口空闲中断中获取 DMA 接收的数据并处理,然后再开启下次 DMA 接收。
关键代码
使能串口空闲中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
添加串口空闲中断处理函数
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 */
UART_IRQHandler_IDLE(&huart1); // 串口空闲中断处理函数
/* USER CODE END USART1_IRQn 1 */
}
/* USER CODE BEGIN 1 */
void UART_IRQHandler_IDLE(UART_HandleTypeDef *huart)
{
if(huart == &huart1)
{
if(__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)
{
HAL_UART_DMAStop(&huart1);
uint8_t data_length = sizeof(recv_buf) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
HAL_UART_Transmit(&huart1, recv_buf, data_length, 100);
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
__HAL_UART_CLEAR_OREFLAG(&huart1);
HAL_UART_Receive_DMA(&huart1, recv_buf, sizeof(recv_buf));
}
}
}
/* USER CODE END 1 */
避坑
在使用串口中断时,一定记得要清除 ORE flag,不然中断会出各种意想不到的异常,比如串口发送数据会触发串口接收中断。
__HAL_UART_CLEAR_OREFLAG(&huart1);
测试
完整代码
https://download.csdn.net/download/lyndon_li/88054148
对应硬件:STM32F107VCT6