串口接收数据分包问题处理(QT上位机/单片机等)

串口数据出现分包如何正确完整接收

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

串口通信在QT上位机以及单片机或者安卓串口等使用情况下,经常容易出现一包数据分成几包的情况下,如何快速将这些分散的数据拼成完整一包相信很多单片机以及串口相关的开发人员都会遇到,可能很多简单的方式都能实现,但是一个有效耐得住考验少丢数据的方法也是很重要的。`

一、设计思路

1.数据的格式是头0XA4 0X4A 尾部是0X3C 0X3C,里面包含数据数据长度和帧校验等相关定义,本文只从头尾如何顺利提取到每一包。
2.思路1是每一次数据到来将数据保存起来,然后识别里面是否有尾部,如果没有,等待下一次,这样数据缓冲会越来越多,索引尾部多少会有点慢.
3.现在改为每来一次数据,识别当前数据是否有尾部,如果有,则认为当前数据是一包,则再将缓冲数据进行头部识别,识别不到头,证明是错误数据,不进行处理。识别成功,则调用相应处理函数。若数据中没有尾部,则将数据放入缓存。若数据最后一位是0X3C,则做好标记,下一包数据来临时优先判断第一个字节是不是0X3C。
若单次接收包含了多包数据也应能处理。

二、代码参考

QT代码参考
代码如下(示例):

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void setupPlot();
    void setCVMode();
    void setCCMode();
    void initActionsConnections();

private slots:

    void updateView();
    void updateLCDNumber();
    void handleTimeout();

    void showStatusMessage(const QString &message);
    void about();
    void openSerialPort();
    void closeSerialPort();
    void writeData(const QByteArray &data);
    void readData();
    void SerialRecv_solt();
    void process_serial_recv(char *pBuf,int len);
    void ComSend( unsigned char cmd1, int var1);
    int get_tail(unsigned char *pbuf,int len);
    int get_head(unsigned char *pbuf,int len);
    int send_copy_from_read(unsigned char *pbuf,int len);
    int process_pack(unsigned char* pbuf,int len);

    void on_doubleSpinBox_vol_valueChanged(double arg1);
    void on_doubleSpinBox_cur_valueChanged(double arg1);
    void on_radioButton_cv_clicked();
    void on_radioButton_cc_clicked();
    void on_pushButton_verify_clicked();
    void on_verticalSlider_vol_valueChanged(int value);
    void on_verticalSlider_cur_valueChanged(int value);
    void on_actionCtrl_triggered(bool checked);
    void on_actionUpdate_triggered();

private:
    Ui::MainWindow *ui;
    QSerialPort *serial;
    SettingsDialog *settings;
    QVector<double> vol, cur;
    QTimer *m_pTimer;

    unsigned char data_recv[1024];
    int len_pack=0;
    int maybe_tail;//疑似拿到了尾部一个字节
};
int MainWindow::get_tail(unsigned char *pbuf,int len)
{
    int i=0;
    int pos;
    for(i=0;i<len;i++)
    {
        if(pbuf[i]==0xc3)
        {
            if(i==(len-1))
            { 
              return 0;//疑似拿到了尾部一个字节
            }
            else
            {
               if(pbuf[i+1]==0xc3)
               pos=i+2;//第几个字节出现了尾部
               return pos;//完整的拿到了尾部
            }
        }
    }
    return -1;//没有拿到尾部任何信息
}
/**
 * @brief 获取头的位置
 * @param  pbuf              [in/out]
 * @param  len               [in/out]
 * @return int 
 */
int MainWindow::get_head(unsigned char *pbuf,int len)
{
    int i=0;
    for(i=0;i<len;i++)
    {
        if((pbuf[i]==0xA5)&&(i<(len-1)))
        {
          if(pbuf[i+1]==0x5A)
          return i; 
        }
    }
    return -1;//没有头部信息
      
}

int MainWindow:: process_pack(unsigned char* pbuf,int len)
{
    int pos_head=0;
    memcpy(data_recv+len_pack,pbuf,len); 
    len_pack+=len;

   pos_head=get_head(data_recv,len_pack);
   if(pos_head>=0)//找到了头
   {
       send_copy_from_read(data_recv+pos_head,len_pack-pos_head);
       qDebug() << "hello";
   }
   
  //相关标志位清零
   maybe_tail=0;
   len_pack=0;  
}
/*------------------------ slot ----------------------------------------------*/
/**
  ******************************************************************************
  * @brief 串口数据接收处理
  ******************************************************************************
  **/
void  MainWindow::SerialRecv_solt()
{
    QByteArray    data;
    data.resize (serial->bytesAvailable());
    serial->read (data.data(),data.size());
    //int pos_tail=0;
    //int pos_head=0;
    int size_now=0;
    int pos_data=0;
    int data_num=0;
    size_now=data.size();
    if((maybe_tail==1)&&((unsigned char )data.data()[0]==0xc3))//识别到结束位
    {
        process_pack((unsigned char *)data.data(),1);
        //将当前包剩余的数据赋值给下一包 
        if(data.size()>1)
        {
          memcpy(data_recv+len_pack,&data.data()[1],(data.size()-1)); 
          len_pack+=(data.size()-1);
        }     
    }
    else
    {
        maybe_tail=0;        
          //size_now--剩余字节数
           while(size_now>0)
            { 
              pos_data=data.size()-size_now;
              data_num=get_tail((unsigned char *)data.data()+pos_data,size_now);
               if(data_num>0)//识别到结束位
               {
                  process_pack((unsigned char *)data.data()+pos_data,data_num);
                  size_now=data.size()-data_num; 
               }
               else//没有识别到结束位
               {
                   if(data_num==0)
                    maybe_tail=1;
                    if((len_pack+data.size())<1024)//限制缓冲大小为1024个字节
                    {
                        memcpy(data_recv+len_pack,data.data()+pos_data,size_now);
                        len_pack+=size_now;
                    }
                    else
                    {
                        len_pack=0;//从头开始
                    }
                   break;
               }  
            }       
        
    }
}

总结

该代码测试之后可以经过任意几包数据混合,断开发送,使用串口测试过。

  • 5
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值