一种串口完整帧数据接收的实现方式

来源:漫长当下 嵌入式 6 次阅读
摘要:今天讲一下串口通信,这个也是嵌入式中非常常用的,讲到通信那么就一定有通信协议,这样在通信的双方才能够互相识别对方发送的数据,通信协议呢就是双方或多方约定好数据传送的格式,然后按照指定的格式打包和解析,再接收或者传送处理后的数据就行了。 既然是约定的,那么格式就没限制了,只要双方能够按照这种协议进行通信就行了,很多时候我们都是使用自定义协议来完成通信,这里以一种简单的AT指令的形式来讲一下串口如何接

今天讲一下串口通信,这个也是嵌入式中非常常用的,讲到通信那么就一定有通信协议,这样在通信的双方才能够互相识别对方发送的数据,通信协议呢就是双方或多方约定好数据传送的格式,然后按照指定的格式打包和解析,再接收或者传送处理后的数据就行了。

既然是约定的,那么格式就没限制了,只要双方能够按照这种协议进行通信就行了,很多时候我们都是使用自定义协议来完成通信,这里以一种简单的AT指令的形式来讲一下串口如何接收一帧完整的数据。

指令格式:”AT+CMD=XXXX\r\n”

解析方式(仅供参考,使用的正则表达式):

//传送内容为无符号整型sscanf(aRxBuf,"%*[^=]=%u\r\n", &tmpVal);
//传送内容为字符串(若正文含有’\r’或’\n’时存在缺陷,会导致解析内容丢失,可使用strstr();函数方案来完成解析)sscanf(aRxBuf, "%*[^=]=%[^\r\n]",tmpBuf);

这种协议有固定的头和尾,其数据的长度是不定的,我们只能通过头和尾来进行解析,还是以STM32单片机为例,但是解析的方法是通用的,STM32单片机串口每产生一次接收完成中断,便收到了一个字节的数据(仅限单字节接收的方式),我们就可以通过匹配的方式来完成帧数据的接收,其代码如下:

/*接收缓冲区长度*/#define RX_BUF_LENGTH         250U
/*接收协议公共变量*/typedef struct {  volatile uint8_t step;        /*switch 语句跳转条件*/  volatile uint32_t aRxBufIndex; /*接收数据缓冲区索引*/  uint8_t aRxBuf[RX_BUF_LENGTH];}_ptcCom_t;
_ptcCom_t ch_usart1;
/** @brief  串口数据帧解析* @param  p:帧接收控制*         c:串口产生接收中断后收到的字节数据* @retval none*/static void HAL_USART1_RxCpltCallback(_ptcCom_t *p,const uint8_t c){  switch(p->step)  {    case 0:      p->aRxBufIndex = 0;      if(c == 'A')      {        p->step++;        p->aRxBuf[p->aRxBufIndex++] = c;      }      break;    case 1:      if(c == 'T')      {        p->step++;        p->aRxBuf[p->aRxBufIndex++] = c;      }      else        p->step = 0;      break;    case 2:      p->aRxBuf[p->aRxBufIndex++] = c;      //if index already in tail      if(p->aRxBufIndex >= (sizeof(p->aRxBuf) / sizeof(*(p->aRxBuf))))      {        /*如果接收缓冲区马上溢出了,但是还未收到数据的尾,可能数据传输过程中出错或丢失,则认为此数据无效,将其丢弃*/        if(!((p->aRxBuf[p->aRxBufIndex - 1] == '\n') &&             (p->aRxBuf[p->aRxBufIndex - 2] == '\r')))        {          //overflow          p->step = 0;          break;        }      }      /*如果收到了"\r\n",则认为收到一帧完整数据*/      else if(!((p->aRxBuf[p->aRxBufIndex - 1] == '\n') &&                (p->aRxBuf[p->aRxBufIndex - 2] == '\r')))      {        break;      }
      #if(OPT_DBG_INF > 0U)      p->aRxBuf[p->aRxBufIndex] = '\0';      PRINTF("usart1:%d,%s\n",p->aRxBufIndex,p->aRxBuf);      #endif      /*      这里可将数据存入FIFO,或者直接使用;      do something      */      //NOTICE:no 'break' here;    default:      p->step = 0;      break;  }}

这种方式最终得到的一帧完整的数据存放在相应的aRxBuf中,我们可以创建一个FIFO,将这帧数据存放到FIFO中,或者直接使用这帧数据都是可以的,至此一帧完整的数据就收到了,如果你的通信协议是16进制传输的,此方法同样适用。

    扩展:对于STM32单片机来说,可以利用DMA的空闲中断来接收一帧完整的数据,在以后的文章中,可能会讲解此种方法。

    后台回复“串口数据帧”五个字可获取本文源码。

评论区

登录后即可参与讨论

立即登录