上一期我们介绍了STM32中串口通信的接收三种方式:轮询接收、中断接收以及DMA接收三种方式。
我们简单的来回顾一下三种接收方式的优劣。轮询接收最为简单,不需要配置中断也不需要配置DMA,调用轮询接收函数后,MCU将会等待串口接收到指定长度的数据或者超出等待时间才会进行下一步。这种方法使用简单,但是每次只能接收固定的长度并且占用CPU资源。
第二种方式是中断接收:相比于轮询接收,中断接收并不会占用MCU的主要进程,而是在收到指定长度的字符后进入中断让用户进行处理。中断接收的方式解决了接收数据的时候占用CPU的情况。
第三种方式是DMA接收:在大量数据传输的情况下中断接收会频繁的进入中断服务函数。为了解决这个情况,使用DMA接收的方式可以让CPU无需参与,只需要定期处理DMA缓存区中的数据即可。
然而在串口通信的应用中,如何接收数据只是最基础的一个步骤。如何处理接收数据也是其中的一个难题,如果大家仔细观察不难发现在这三种串口接收方式中都有着各自的缺点,其中轮询接收和中断接收只能接收固定长度的数据才能进行下一步,但是在日常的使用中经常会碰到不定长数据的处理。
因此本期我们的内容主要聚焦在接收数据的后处理上,介绍在STM32中如何实现对不定长数据的处理。
1、结束标志位
最常见的实现串口不定长数据接收的方法是我们认为标记每段数据的结束位,对每一个接收到的数据进行数据判断,具体实现方法如下:
首先我们在初始化中开启串口中断接收,每接受到一个字符就进入中断去处理这个字符。
接着我们来到中断回调函数来编写处理逻辑:
uint8_t len=0;uint8_t recive_Buf[256];externuint8_t buf[1];
首先定义recive_Buf用以存放整段数据,定义len变量用以指示每次接收的长度。
if(buf[0] != 0x0A || len == 0)//非结束位{recive_Buf[len++] = buf[0]; //当前字符送入缓存区}else{recive_Buf[len++] = buf[0];if(recive_Buf[len-2] == 0x0D)//判断上一位是不是结束符号{recive_Buf[len++]='';HAL_UART_Transmit(&huart1,recive_Buf,len,0x100);memset(recive_Buf,0,len);len = 0;}}//重启中断接收HAL_UART_Receive_IT(&huart1,buf,1);
这里给出一段结束位接收代码,首先我们先规定我们的结束位:0x0D 0x0A,只有这两个同时出现的时候才判断这段内容结束。
首先进入中断之后,我们判断当前字符是不是结束字符,如果不是就把这个字符存入缓存区。
如果收到了结束字符就去判断上一个字符是不是另一个结束字符。
如果发现最后两个字符组合是0x0D 0x0A,就进入数据处理流程(文中为回传收到的内容)
用标志位可以方便的处理收到的内容,但是受限于标志字符的局限性,如果中间数据本身就带着结束字符,就会导致整段数据在中间异常截断。
因此这里我们介绍第二种实现串口不定长接收的方法:串口空闲中断。
2、串口空闲中断
观察手册,我们可以看到UART有着IDLE中断,全称IDLE line detected:空闲线路检测。
IDLE中断用于自动判断一帧数据传输结束。串口接收线(RX)从忙态(有数据传输) 进入空闲态(持续高电平)
代码中如此使用:
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能串口空闲中断HAL_UART_Receive_IT(&huart1,buf,1); //开启接收中断,每次只接收一个字符
首先我们在初始化区域使能串口空闲中断和串口接收中断。
voidUSART1_IRQHandler(void){/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 */if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)){__HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF);HAL_UART_Transmit(&huart1,recive_Buf,sizeof(recive_Buf),0x100);memset(recive_Buf,0,sizeof(recive_Buf));len = 0;}else{recive_Buf[len++]=buf[0];HAL_UART_Receive_IT(&huart1,buf,1);}/* USER CODE END USART1_IRQn 1 */}
接着在串口中断中判断引发该中断的是否是串口空闲引发的中断,如果是的话就进行字符串处理逻辑。
这样子我们就完成了利用串口中断进行不定长数据接收的方案。
但是串口空闲中断也是有弊端的,假如串口压根就是不空闲的,在不间断的传输信息,就会导致一直不进入空闲中断......
527