使用DMA和空闲中断实现了串口接收不定长的数据功能,使用HAL库函数实现一下上述功能。
串口的空闲中断:顾名思义,就是当串口在一定的时间内没有接收到数据时,触发控制状态,从而产生空闲中断;一般来说,STM32在数据交互时,传输字节之间的间隔很短,然后再一个字节的通讯时间内,没有收到数据时,意味着程序进入了空闲中断,所以在程序的初始化后,我们只需要开启空闲中断后,然后在串口的回调函数中,进行数据迁移就可以,这里为了方便验证,将数据做回传处理。
一:cube MX配置和之前的帖子写的相同,这里就不做过多的介绍:
大家可以看下之前的帖子
NUCLEO-U083RC学习历程19-学习串口DMA发送和DMA的不定长接收-电子产品世界论坛
https://forum.eepw.com.cn/thread/389467/1
二:HAL库中有关串口接收的处理函数
在HAL库中使用串口中断或者时空闲中断,区别在于接收每个字节进入中断还是在未接收到一个字节时间内,进入中断,后者就是为了当全部数据接收时,使用HAL库自动处理数据是否完整的,从而进入库里面的回调函数。
2.1 启动接收DMA接收空闲中断
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
当在主函数里面调用上述函数,就会自动启用串口的以DMA接收数据时,产生空闲中断,当数据接收完成后,进入回调函数进行处理。我们在回调函数中处理串口接收到数据即可。
注意:这里进入空闲中断存在两种方式:
a:接收数据时,发生一个字节时间内没有接收到数据时
b:当串口接收到数据,与定义接收的长度一致时,进入空闲中断。所以这就要求我们设置需要将接收的数组设置大一些,以保证我们可以正常接收到完整的数据,防止数据的丢失。
2.2 手动关闭DMA的中断
#define __HAL_DMA_DISABLE_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->CCR &= ~(__INTERRUPT__))
HAL库中对DMA有传输错误、半传输完成和全部传输完成中断,三种不同的方式,如果说不把上述中断关闭的话,当串口接收一组数据时候,存在接收一半数据,进入接收一半的回调函数,接收完成后的回调函数,意味着串口会进入两次中断,从而多进入一次中断,往往半传输中断的回调函数意义不大,而且占用了中断资源。
2.3 接收完成回调函数
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);
这里的接收完成中断函数,主要是接收完成后,在回调中需要再次开启中断,否则不能再次进入中断处理。
2.4 传输错误中断处理
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
写这个错误中断处理函数,主要是为了HAL在接收数据时出错,可能时外部干扰源引起的,在接收错误的回调函数中,同样需要处理空闲中断的接收,否则程序也无法再次开启空闲中断。
三:程序如下所示:
3.1 接收完成处理,做数据回传处理
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if(huart == &huart2) { // __HAL_DMA_DISABLE(&hdma_usart2_rx); // HAL_UARTEx_ReceiveToIdle_DMA(&huart2,RecBuffer2,256); // __HAL_DMA_ENABLE(&hdma_usart2_rx); HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RecBuffer2, 256); // 接收完毕后重启 HAL_UART_Transmit(&huart2, RecBuffer2, Size, 100); // 将接收到的数据再发出 __HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT); // 手动关闭DMA_IT_HT中断 memset(RecBuffer2, 0, 32); // 清除接收缓存 // mcudRxLength = Size; // LocalSlaveProcess(mcudRxLength,GetLocalAddr()); } }
3.2 接收错误处理函数
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart) { if(huart->Instance == USART2) { HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RecBuffer2, 256); // 接收发生错误后重启 __HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT); // 手动关闭DMA_IT_HT中断 memset(RecBuffer2, 0, 32); // 清除接收缓存 } }
3.3 主程序下添加如下代码:
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RecBuffer2, 256); __HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);
四:实测图片如下:
可以看到以5ms的时间间隔进行测试,发送和接收的数据容错率很低。
实测较之前的处理方式,自己手动编写空闲中断处理函数,在串口中断调用的方式,本次使用HAL库的函数进行处理,增加了接收错误的处理部分;从而保证数据输出时候的稳定性。
测试代码如下所示: