这是一篇简单的学习笔记,记录使用STM32串口接收不定长数据,之前都是使用接收中断+缓冲区的方式,这篇笔记使用DMA+空闲中断的方式。
这是简单的速记:
使用串口空闲中断的基本思路是
- 串口接收初始化:在main函数初始化部分调用
①开启空闲中断__HAL_UART_ENABLE_IT()
②发起一个串口DMA接收HAL_UART_Receive_DMA(&huart2, uartRecv_buffer, uartRecv_buffer_len)。
注:接收的缓冲区uartRecv_buffer、长度我们通过宏以及全区数组来定义,这样串口会自动接收数据放入uartRecv_buffer。 - 空闲中断回调函数:这个函数会在串口中断处理函数USART2_IRQHandler(void)中调用。一开始需要判断空闲中断标志位是否被置1,如果被置1则执行下面操作。
①清除idle标志位,停止DMA传输。
②计算DMA当前帧实际接收的数据长度 = 我们在1中设置要接收的长度 - DMA中剩余的空间长度;
③设置串口接收完成标志(这个标志是我们定义的全局变量,用于通知后台串口数据处理函数,接收完成,如果使用操作系统就可以通过“任务通知”等方式)。
注:空闲中断在HAL库中并没有提供预先定义的回调函数,这里我们自己定义一个就可以。 - 串口接收数据的后台处理函数:因为数据已经被DMA放到缓冲区uartRecv_buffer中,所以我们可以直接处理其中的数据,下面的示例代码只是在串口中回显出来。
后台数据处理函数包含两个部分:
①处理数据的业务逻辑,示例代码实现串口数据回显。
②清除DMA当前帧实际接收的数据长度uartRecv_buffer_curframe_len,接收完成标志uartRecv_end_flag,再发起下一次的串口DMA接收HAL_UART_Receive_DMA。
在CubeMX中配置:
配置串口接收的DMA:
使能串口和DMA中断
编写上面1,2,3步骤的函数:
static uint8_t DebugUartRecvBuf[DEBUG_UART_RECVBUFLEN];
static int DebugUartRecvCnt = 0;
static int DebugUartRecvCompleteFlge = 0;
extern DMA_HandleTypeDef hdma_usart2_rx;
/* Debug_uart rx DMA idle*/
/* 初始化函数 */
void DebugUartRecvInit(void)
{
/* 使能空闲中断 */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
/* 发起串口DMA接收 */
HAL_UART_Receive_DMA(&huart2, DebugUartRecvBuf, DEBUG_UART_RECVBUFLEN);
}
/* 空闲中断回调函数 */
void DebugUartRecv_IdleCallback(void)
{
/* 判断空闲中断发生 */
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) == SET)
{
/* 清除空闲中断标志位,暂停串口DMA传输 */
__HAL_UART_CLEAR_FLAG(&huart2, UART_CLEAR_IDLEF);
HAL_UART_DMAStop(&huart2);
/* 计算当前空闲帧接收到的数据长度 */
DebugUartRecvCnt = DEBUG_UART_RECVBUFLEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
/* 通知后台处理程序,接收完成 */
DebugUartRecvCompleteFlge = 1;
}
}
/*
** 串口数据接收的数据处理后台函数
** ret: 0 - ok, -1 - 无数据
*/
int DebugUartRecv_DealData(char *recvData, int *len)
{
if(DebugUartRecvCompleteFlge)
{
/* 数据处理 */
strncpy(recvData, (char *)DebugUartRecvBuf, DebugUartRecvCnt);
*len = DebugUartRecvCnt;
/* 恢复标志位、当前空闲帧接收到的数据长度 */
DebugUartRecvCompleteFlge = 0;
DebugUartRecvCnt = 0;
/* 再次发起下一次的串口DMA接收 */
HAL_UART_Receive_DMA(&huart2, DebugUartRecvBuf, DEBUG_UART_RECVBUFLEN);
return 0;
}
else
{
return -1;
}
}
函数被调用的地方如下所示: