单片机串口环形缓冲区进行数据帧接收和处理的问题

2020-01-19 19:38发布

设计目标:
1. 单片机时钟10MHz,串口波特率115200
2. 由于波特率较高,串口必须采用中断方式接收
3. 串口接受完数据必须对数据进行解帧,即判断出帧头和帧尾,这部分放while循环里面处理

搜索了下,发现下面的代码比较符合要求
但是根据信息有几个问题
1. UART_Receive_Size在中断函数和一般函数都出现,会产生其真实值被中断后覆盖掉的问题,导致值出错(最终结果是有可能丢帧)
2. 可不可以不用UART_Receive_Size,而是定义两个指针,分别指向接收位置和处理位置,则缓冲区的满(full)
    不用UART_Receive_Size进行判断,而是利用这两个指针的关系进行判断。这样子可以避免问题1?()
3. 求测试过的健壮性好的代码,谢谢


  1. //系统可修改参数宏定义
  2. #define BUFFER_SIEZ 64
  3. //控制命令定义
  4. #define COMMUNCIATE 0
  5. #define SET_SYSTEM_CAL_FULL 1
  6. #define SET_SYSTEM_CAL_MV_V 2
  7. #define SET_SYSTEM_OL 3
  8. #define SET_POWER_OFF_TIME 4 //设定系统关机时间
  9. #define READ_SYSTEM_DATA 5

  10. //变量定义
  11. //串口缓冲区 建立一个环形缓冲区,收发
  12. unsigned char xdata UART_Receive_Size=0;//串口缓冲区接收字节数
  13. unsigned char xdata UART_Receive_First=0;//串口缓冲区接收字节开始位置
  14. unsigned char xdata UART_Read_First=0;
  15. unsigned char xdata UART_Buffer[BUFFER_SIEZ];//串口缓冲区
  16. unsigned char xdata UART_Send_Byte_Ok=0;//发送一字节成功
  17. //中断处理
  18. //串口初始化根据自己的单片机写就行
  19. void UART0_Interrupt (void) interrupt 4  
  20. {
  21.    if(RI0)                         //如果是发中断,返回
  22.    {   
  23.      RI0=0;  //清除中断标志
  24.      if(UART_Receive_Size<=BUFFER_SIEZ)//缓冲区未满,装载数据
  25.      {
  26.        UART_Buffer[UART_Receive_First++]=SBUF0;
  27.        UART_Receive_Size++;//串口缓冲区接收字节数
  28.        if(UART_Receive_First>=BUFFER_SIEZ)//循环装入缓冲区
  29.          UART_Receive_First = 0;
  30.      }
  31.    }
  32.   if(TI0)
  33.   {         
  34.     TI0=0;
  35.    UART_Send_Byte_Ok=1;
  36.   }
  37. }

  38. //在缓冲区中读取一帧数据
  39. void Do_Commend()
  40. {
  41.   unsigned char Buf[8]={0,0,0,0,0,0,0,0};//帧数据缓冲区
  42.   unsigned char i=0;
  43.   unsigned char data_packge_flag=0;
  44.   
  45.   if(UART_Receive_Size>=8)//缓冲区字节数大于等于一个包字节数
  46.   {
  47.     while(UART_Receive_Size!=0)//寻找帧数据头
  48.     {
  49.          if(UART_Buffer[UART_Read_First]==0xaa)
  50.          {
  51.            if(UART_Read_First+7<BUFFER_SIEZ)
  52.            {
  53.               if(UART_Buffer[UART_Read_First+7]==0x55)
  54.               {
  55.                data_packge_flag=1;
  56.                break;
  57.               }
  58.            }
  59.            else
  60.            {
  61.               if(UART_Buffer[7-(BUFFER_SIEZ-UART_Read_First)]==0x55)
  62.               data_packge_flag=1;
  63.               break;
  64.            }
  65.          }
  66.          UART_Read_First++;
  67.          if(UART_Read_First>=BUFFER_SIEZ)//环形 缓冲区折行
  68.          UART_Read_First=0;
  69.          UART_Receive_Size--;
  70.     }
  71.     if(data_packge_flag==1)//寻找到枕头
  72.     {
  73.        for(i=0;i<8;i++)//读取帧数据
  74.        {
  75.           Buf=UART_Buffer[UART_Read_First];
  76.           UART_Receive_Size--;
  77.           UART_Read_First++;
  78.           if(UART_Read_First>=BUFFER_SIEZ)//环形 缓冲区折行
  79.             UART_Read_First=0;
  80.        }
  81.        data_packge_flag=0;
  82.     }
  83.     if(Buf[7]==0x55&&Buf[0]==0xaa)  //接收到一个正确的数据包
  84.     {
  85.           switch(Buf[1])
  86.           {
  87.             ...
  88.           }
  89.         }
  90.   }
  91. }
复制代码
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
18条回答
rain73
1楼-- · 2020-01-19 21:31
以前有段时间研究过这个,心得就是要好的效果,必须加大接收/发送的buffers,以及加快数据的处理时间。
环形缓冲区的样子有点像贪吃蛇,蛇头和蛇尾撞上了必然出错。解决办法就是要判断蛇头和蛇尾的距离,一
旦距离过小,蛇头要暂停接收数据,等待蛇尾取走数据,等距离拉开了就继续。

以下是以前记录的数据:
* 把接收的字节立即回显,接收无缓冲,发送经缓冲区:
   8字节缓冲区,能正确接收并回显大约4KB;
  16字节缓冲区,能正确接收并回显大约9KB;
  32字节缓冲区,能正确接收并回显大约18KB;
Ray______
2楼-- · 2020-01-19 22:25
 精彩回答 2  元偷偷看……
Ray______
3楼-- · 2020-01-20 03:44
然后定义一个指针,遍历寻找帧头,找到头后直接判断尾是否相等,帧尾正确则找到了数据入口,就可以用数组处理数据了
ziruo2002ab
4楼-- · 2020-01-20 07:51
Ray______ 发表于 2015-9-26 16:08
然后定义一个指针,遍历寻找帧头,找到头后直接判断尾是否相等,帧尾正确则找到了数据入口,就可以用数组处 ...

原理当然不复杂
复杂的是 当找到了帧头继续找帧尾时,到了数据缓冲区最后一个接收字节还没找到时,该怎么处理?
针对定义的帧协议,优化处理方式就很多了
如何在复杂度以及尽量少丢帧之间折衷,并不是那么容易的事

这个程序编不好很容易死掉,
如何提高程序健壮性是个很大的问题
ziruo2002ab
5楼-- · 2020-01-20 12:31
Ray______ 发表于 2015-9-26 16:08
然后定义一个指针,遍历寻找帧头,找到头后直接判断尾是否相等,帧尾正确则找到了数据入口,就可以用数组处 ...

原理当然不复杂
复杂的是 当找到了帧头继续找帧尾时,到了数据缓冲区最后一个接收字节还没找到时,该怎么处理?
针对定义的帧协议,优化处理方式就很多了
如何在复杂度以及尽量少丢帧之间折衷,并不是那么容易的事

这个程序编不好很容易死掉,
如何提高程序健壮性是个很大的问题
ziruo2002ab
6楼-- · 2020-01-20 13:23
Ray______ 发表于 2015-9-26 16:08
然后定义一个指针,遍历寻找帧头,找到头后直接判断尾是否相等,帧尾正确则找到了数据入口,就可以用数组处 ...

原理当然不复杂
复杂的是 当找到了帧头继续找帧尾时,到了数据缓冲区最后一个接收字节还没找到时,该怎么处理?
针对定义的帧协议,优化处理方式就很多了
如何在复杂度以及尽量少丢帧之间折衷,并不是那么容易的事

这个程序编不好很容易死掉,
如何提高程序健壮性是个很大的问题

一周热门 更多>