STM32使用HAL库UART接收不定长数据-1

使用STM32的HAL库实现UART串口不定长数据的接收

使用STM32的UART接收数据的时候,经常会遇到接收长度不固定的数据,比如一帧数据可能是10个字节,也可能是12个字节。这种数据称为不定长数据。

现有的很多通信协议是不定长的,比如modbus-rtu,其不同指令的长度是不同的,还比如与ESP8266通信使用的AT指令,其数据长度也不是固定的。

对于上面所说的不定长数据,可以使用状态机的方式判断一帧数据的结束,即在UART接收中断中按照通信协议的格式进行判断,确定哪个字节为帧的结尾。但是这种方式编程比较复杂。

有一个更简单的方法,就是判断一帧数据的时间间隔。默认两帧数据之间是有一定时间间隔的,比如10ms,那么当收到一个字节的数据后,5ms内没有收到新的字节,就认为一帧数据的结束。这种方法能够大大简化UART接收中断的处理函数。只需要在主流程里判断这一帧数据是否合法即可,能够很好的减低编程难度,也提高了MCU的处理效率。

那么如何实现这种编程方式呢?这里介绍一种通过HAL库实现的方式。

不定长数据接收方式

在STM32中最通用的方式就是借助UART的IDLE中断来实现,即结合UART的接收DMA和IDLE中断来实现。这种方式适用于STM32的所有芯片。

UART的IDLE中断是:当一个空闲帧被检测到时产生的中断。空闲帧如下图所示,为持续一个字节数据的高电平,就是空闲帧。也就是持续1个字节总线上没有数据就是一个空闲帧。

通过设置UART使用DMA进行接收,使得UART能够自动接收数据,并放入缓冲区中。当STM32检测到IDLE中断后,通过回调函数对接收到的数据进行处理。这样不用每接收到一个字节就处理,提高了MCU的运行效率。

HAL库函数

在STM32的HAL库中提供了现成的函数
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

这个函数使用DMA接收数据,当接收到Size个数据或者遇到IDLE中断时完成接收,然后调用下面的回调函数。

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)

库函数使用注意实现

因此对一帧数据的处理函数要放到 HAL_UARTEx_RxEventCallback() 函数中。注意这个函数是在中断程序中,因此其处理函数要尽量的短。而且其参数Size 在DMA为循环模式时,指的是在接收缓冲区中下一个字节的写入索引位置,这个要注意。

在使用HAL_UARTEx_RxEventCallback() 回调函数的时候也要注意,这个函数在DMA的"半传输"中断和"传输完成"中断的时候也会进入此回调函数。比如说使用HAL_UARTEx_ReceiveToIdle_DMA() 函数接收20个字节,那么在接收到10个字节的时候会触发DMA"半传输中断",接收到20个字节的时候会触发DMA的"传输完成"中断。DMA的"传输完成"中断不会产生影响,因为此时接收也已经完成了。但是"半传输"中断会影响处理,比如接收到不定长数据为12个字节,那么在收到10个字节的时候会进入HAL_UARTEx_RxEventCallback() 回调函数,在接受到12个字节的时候还会进入HAL_UARTEx_RxEventCallback() 回调函数。

防止DMA中断影响的方法

最简单的处理方法是关闭DMA的这2个中断,可以在调用HAL_UARTEx_ReceiveToIdle_DMA() 函数后,关闭这2个中断,参考如下代码:

  HAL_UARTEx_ReceiveToIdle_DMA(&huart1,recvbuf,20);
  __HAL_DMA_DISABLE_IT(huart1.hdmarx,DMA_IT_HT); //关闭半传输中断
  __HAL_DMA_DISABLE_IT(huart1.hdmarx,DMA_IT_TC); //关闭传输完成中断

这种方法既适用于将UART的DMA接收设置为循环接收,也适用于将DMA设置为单次接收模式。

如果不想关闭中断,而且DMA接收模式时单次模式,那么也可以在回调函数中进行判断,判断方法如下:

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
  if(huart->RxState == HAL_UART_STATE_READY){
    // 接收完成
  }else{
    // 半传输中断
  }
}

在"半传输"中断进入时,接收没有完成,因此huart->RxState 的值是HAL_UART_STATE_BUSY_RX ,因此可以通过判断huart->RxState 的值是否等于HAL_UART_STATE_READY ,得知是因哪种事件进入的回调函数。

以上就是STM32使用HAL库实现UART不定长数据接收的一种方法,使用起来很简便。但是使用IDLE中断判断1帧数据结束还有有一定问题的,比如说在终端发送数据的时候,一帧数据中字节与字节之间的时间间隔比1个字节长了,就会造成误判。所以在使用的时候还是要注意区分的。在新出的STM32处理器中,针对这一点提供也新的解决方案,就是包含一个时间间隔可以设置的功能,这个将在下一次进行分享。

相关推荐
FreakStudio1 小时前
全网最适合入门的面向对象编程教程:56 Python字符串与序列化-正则表达式和re模块应用
python·单片机·嵌入式·面向对象·电子diy
EVERSPIN4 小时前
分享国产32位单片机的电机控制方案
单片机·嵌入式硬件
每天一杯冰美式oh4 小时前
51单片机的家用煤气报警系统【proteus仿真+程序+报告+原理图+演示视频】
嵌入式硬件·51单片机·proteus
芯橦7 小时前
【瑞昱RTL8763E】音频
单片机·嵌入式硬件·mcu·物联网·音视频·visual studio code·智能手表
夜间去看海11 小时前
基于单片机的智能浇花系统
单片机·嵌入式硬件·智能浇花
VirtuousLiu11 小时前
LM74912-Q1用作电源开关
单片机·嵌入式硬件·ti·电源设计·lm74912·电源开关
打地基的小白11 小时前
软件I2C-基于江科大源码进行的原理解析和改造升级
stm32·单片机·嵌入式硬件·通信模式·i2c
Echo_cy_12 小时前
STM32 DMA+AD多通道
stm32·单片机·嵌入式硬件
朴人12 小时前
【从零开始实现stm32无刷电机FOC】【实践】【7.2/7 完整代码编写】
stm32·单片机·嵌入式硬件·foc
追梦少年时12 小时前
STM32中断——外部中断
stm32·单片机·嵌入式硬件