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

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. }
复制代码
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
19条回答
ziruo2002ab
2020-01-20 07:51
Ray______ 发表于 2015-9-26 16:08
然后定义一个指针,遍历寻找帧头,找到头后直接判断尾是否相等,帧尾正确则找到了数据入口,就可以用数组处 ...

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

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

一周热门 更多>