• 23706阅读
  • 14回复

[提问]小弟又来问QSerialPort的问题,关於接收 [复制链接]

上一主题 下一主题
离线琉璃螃蟹
 

只看楼主 倒序阅读 楼主  发表于: 2014-07-08
各位大大
我的写入部份代码如下

  1. bool ok;
  2.     QString valueStr="AA01000000AB07";
  3.     foo.integer=valueStr.toLongLong(&ok,16);
  4.     char data[] ={foo.byte[6],foo.byte[5],foo.byte[4],foo.byte[3],foo.byte[2],foo.byte[1],foo.byte[0]};
  5.     QByteArray mydata = QByteArray::fromRawData(reinterpret_cast<const char*>(data), sizeof(data));
  6.     serial->write(mydata);
读取部份代码如下
  1. if(serial->waitForReadyRead(200))
  2.         {
  3.             qDebug()<<"Count:"<<count++;
  4.             QByteArray dd = serial->readAll();  
  5.             qDebug()<<dd.toHex();
  6.             qDebug()<<"Bytes"<<dd.size()<<"  Time:"<<QDateTime::currentMSecsSinceEpoch()<<endl<<endl;
  7.             serial->clear();
  8.             msleep(500);
  9.             
  10.         }
两者包在一个while loop当中
装置完整收到符合协定的7个Bytes 就会回传7个Bytes

我如果把读写分别在两个按钮来触发的话,读取是对的
但是如果像这样写在回圈里,读取却不完整
有时收到1 Btye 有时收到2Byte 有时有收到7Byte....
一整个诡异

请问有解法吗?我卡很久罗

离线labhome

只看该作者 1楼 发表于: 2014-07-08
1.不建议你这么干
2.if(serial->waitForReadyRead(200)),用更大参数,自己试,直到每次都能取到完整字节为止,不推荐你这么干
3.这样也许好点,但请保证不会死循环
    QByteArray dd = serial->readAll();
   while (dd < 7)
   {
        dd = dd + serial->readAll();
    }
希望你不要天真的以为我上面的代码可以直接运行
4条评分好评度+1贡献值+1金钱+1威望+1
琉璃螃蟹 好评度 +1 - 2014-07-08
琉璃螃蟹 贡献值 +1 - 2014-07-08
琉璃螃蟹 威望 +1 - 2014-07-08
琉璃螃蟹 金钱 +1 - 2014-07-08
离线琉璃螃蟹

只看该作者 2楼 发表于: 2014-07-08
回 labhome 的帖子
labhome:1.不建议你这么干
2.if(serial->waitForReadyRead(200)),用更大参数,自己试,直到每次都能取到完整字节为止,不推荐你这么干
3.这样也许好点,但请保证不会死循环
    QByteArray dd = serial->readAll();
   while (dd < 7)
....... (2014-07-08 15:46) 

我放在QThread里的目的是想保有GUI的操控能力
并让它能在背景做该做的事,之後还要想办法从中取资料判断

2. 尝试过 wait更久,并不会就取到完整的值,它似乎是有判断到可读就会读进来
3. 会死回圈,因为读过以後就没东西读了

总之还是感谢您肯拨空回我问题
离线realfan

只看该作者 3楼 发表于: 2014-07-09
回 琉璃螃蟹 的帖子
琉璃螃蟹:我放在QThread里的目的是想保有GUI的操控能力
并让它能在背景做该做的事,之後还要想办法从中取资料判断
2. 尝试过 wait更久,并不会就取到完整的值,它似乎是有判断到可读就会读进来
....... (2014-07-08 16:27) 

要注意的一点,读数据可能还是在主线程中。
离线琉璃螃蟹

只看该作者 4楼 发表于: 2014-07-09
回 realfan 的帖子
realfan:要注意的一点,读数据可能还是在主线程中。 (2014-07-09 08:43) 

我写与读都是在 Qthread 的run里
应该跟主线程没关系

我本来想用Signal & SLOT的方式去跟非GUI的类沟通
可是看范例,做不出来

苦恼阿
离线labhome

只看该作者 5楼 发表于: 2014-07-09
用Signal & SLOT的方式
最好这样做,另外,非GUI类的信号/槽机制与GUI类没有什么不同,QSerialPort本身也是非GUI类

离线琉璃螃蟹

只看该作者 6楼 发表于: 2014-07-09
回 labhome 的帖子
labhome:最好这样做,另外,非GUI类的信号/槽机制与GUI类没有什么不同,QSerialPort本身也是非GUI类
 (2014-07-09 11:46) 

我也想这样做,但是翻出来的范例
我都没办法实做出来,error老是跟我说重新定义function

可以推荐一个简单的范例吗?
离线琉璃螃蟹

只看该作者 7楼 发表于: 2014-07-16
贴代码求助,大德们有空的时候帮忙看看

第一种
  1. #include "test.h"
  2. union
  3. {
  4.     unsigned long long int integer ;
  5.     unsigned  char byte[7];
  6. }foo;
  7. extern QSerialPort *serial; //引用Mainwindow 宣告的全域变数
  8. int count;
  9. Test::Test(QObject *parent) :
  10.     QThread(parent)
  11. {
  12. //重新设定 SerialPort
  13.     serial->close();
  14.     QSerialPortInfo info("ttyUSB0");
  15.     serial = new QSerialPort(info);
  16.     serial->open(QIODevice::ReadWrite);
  17.     serial->setPortName("/dev/ttyUSB0");
  18.     serial->setBaudRate(QSerialPort::Baud19200);
  19.     serial->setDataBits(QSerialPort::Data8);
  20.     serial->setParity(QSerialPort::NoParity);
  21.     serial->setFlowControl(QSerialPort::NoFlowControl);
  22.     serial->setReadBufferSize(4096);
  23. }
  24. void Test::run() // QThread 的执行程式
  25. {
  26.     count =1;
  27.     while(1)
  28.     {
  29.     //准备资料写入
  30.     bool ok;
  31.     QString valueStr="AA01000000AB07";
  32.     foo.integer=valueStr.toLongLong(&ok,16);
  33.     char data[] ={foo.byte[6],foo.byte[5],foo.byte[4],foo.byte[3],foo.byte[2],foo.byte[1],foo.byte[0]};
  34.     QByteArray mydata = QByteArray::fromRawData(reinterpret_cast<const char*>(data), sizeof(data));
  35.     
  36.     serial->write(mydata);//写入
  37.      qDebug()<<"Write:"<<mydata.toHex(); //印出写入的资料
  38.         if(serial->waitForReadyRead(500)) // 500ms 内有收到可以读取的资讯
  39.         {
  40.             qDebug()<<"Count:"<<count++; //印出进入回圈次数
  41.             msleep(100); //线程暂停 100ms
  42.             
  43.             QByteArray dd = serial->readAll(); //读取SerialPort的资讯
  44.             qDebug()<<dd.toHex(); //输出读取的资讯
  45.             qDebug()<<"Bytes"<<dd.size()<<"  Time:"<<QDateTime::currentMSecsSinceEpoch()<<endl<<endl;//计算读取到的Bytes数 与系统msec时间
  46.         
  47.             serial->clear(); //清空Serial交换资料
  48.         }
  49.     }    
  50.     this->exec(); //执行此程序
  51. }
  52. /*
  53. 执行结果:
  54. QObject: Cannot create children for a parent that is in a different thread.
  55. (Parent is QSerialPort(0xa1e0c30), parent's thread is QThread(0x9fb2728), current thread is Test(0xa18f6e8)
  56. Write: "aa01000000ab07"
  57. Count: 1
  58. "aa01000000ab07"
  59. Bytes 7   Time: 1405484429394
  60. Write: "aa01000000ab07"
  61. Count: 2
  62. "aa01000000ab07"
  63. Bytes 7   Time: 1405484429506
  64. 一直到20几次以后...
  65. Write: "aa01000000ab07"
  66. Count: 21
  67. "aa01000000ab07"
  68. Bytes 7   Time: 1405484432714
  69. Write: "aa01000000ab07"
  70. T1: malloc.c:2869: __libc_malloc: Assertion `!victim || ((((mchunkptr)((char*)(victim) - 2*(sizeof(size_t)))))->size & 0x2) || ar_ptr == (((((mchunkptr)((char*)(victim) - 2*(sizeof(size_t)))))->size & 0x4) ? ((heap_info *)((unsigned long)(((mchunkptr)((char*)(victim) - 2*(sizeof(size_t))))) & ~((2 * (512 * 1024))-1)))->ar_ptr : &main_arena)' failed.
  71. 程式死掉
  72. */
第二种
  1. #include "test.h"
  2. union
  3. {
  4.     unsigned long long int integer ;
  5.     unsigned  char byte[7];
  6. }foo;
  7. extern QSerialPort *serial; //引用Mainwindow 宣告的全域变数
  8. int count;
  9. Test::Test(QObject *parent) :
  10.     QThread(parent)
  11. {
  12. }
  13. void Test::run() // QThread 的执行程式
  14. {
  15.     
  16.     //重新设定 SerialPort
  17.     serial->close();
  18.     QSerialPortInfo info("ttyUSB0");
  19.     serial = new QSerialPort(info);
  20.     serial->open(QIODevice::ReadWrite);
  21.     serial->setPortName("/dev/ttyUSB0");
  22.     serial->setBaudRate(QSerialPort::Baud19200);
  23.     serial->setDataBits(QSerialPort::Data8);
  24.     serial->setParity(QSerialPort::NoParity);
  25.     serial->setFlowControl(QSerialPort::NoFlowControl);
  26.     serial->setReadBufferSize(4096);
  27.     
  28.     count =1;
  29.     while(1)
  30.     {
  31.     //准备资料写入
  32.     bool ok;
  33.     QString valueStr="AA01000000AB07";
  34.     foo.integer=valueStr.toLongLong(&ok,16);
  35.     char data[] ={foo.byte[6],foo.byte[5],foo.byte[4],foo.byte[3],foo.byte[2],foo.byte[1],foo.byte[0]};
  36.     QByteArray mydata = QByteArray::fromRawData(reinterpret_cast<const char*>(data), sizeof(data));
  37.     
  38.     serial->write(mydata);//写入
  39.      qDebug()<<"Write:"<<mydata.toHex(); //印出写入的资料
  40.         if(serial->waitForReadyRead(500)) // 500ms 内有收到可以读取的资讯
  41.         {
  42.             qDebug()<<"Count:"<<count++; //印出进入回圈次数
  43.             msleep(100); //线程暂停 100ms
  44.             
  45.             QByteArray dd = serial->readAll(); //读取SerialPort的资讯
  46.             qDebug()<<dd.toHex(); //输出读取的资讯
  47.             qDebug()<<"Bytes"<<dd.size()<<"  Time:"<<QDateTime::currentMSecsSinceEpoch()<<endl<<endl; //计算读取到的Bytes数 与系统msec时间
  48.             
  49.             serial->clear(); //清空Serial交换资料
  50.         }
  51.     }    
  52.     this->exec(); //执行此程序
  53. }
  54. /*
  55. 执行结果:
  56. Write: "aa01000000ab07"
  57. Count: 1
  58. "aa01000000ab"
  59. Bytes 6   Time: 1405487774816
  60. Write: "aa01000000ab07"
  61. Count: 2
  62. "aa01"
  63. Bytes 2   Time: 1405487774988
  64. Write: "aa01000000ab07"
  65. Count: 3
  66. "aa"
  67. Bytes 1   Time: 1405487775124
  68. Write: "aa01000000ab07"
  69. Count: 4
  70. "aa0100"
  71. Bytes 3   Time: 1405487775271
  72. Write: "aa01000000ab07"
  73. Count: 5
  74. "aa"
  75. Bytes 1   Time: 1405487775431
  76. Write: "aa01000000ab07"
  77. Count: 6
  78. "aa"
  79. Bytes 1   Time: 1405487775585
  80. Write: "aa01000000ab07"
  81. Count: 7
  82. "aa"
  83. Bytes 1   Time: 1405487775756
  84. Write: "aa01000000ab07"
  85. Count: 8
  86. "aa"
  87. Bytes 1   Time: 1405487775910
  88. 持续一直跑
  89. 这方式应该是正确的,即使跑上一个小时,程式也不会死
  90. 但是几乎读不到完整的资料
  91. */



离线dbzhang800

只看该作者 8楼 发表于: 2014-07-16
首先,你的串口通讯应该有自己的协议。
其次,你要知道,一条指令可能分成几个片段被收到,也可能一次收到几条指令。所以,指令的识别需要依赖你的协议,而不是,将每次收到的数据作为一条指令。
再次,不要用QThread,直接用QSerialPort提供的信号槽。(不仅代码精炼,而且不易出错)
离线琉璃螃蟹

只看该作者 9楼 发表于: 2014-07-16
回 dbzhang800 的帖子
dbzhang800:首先,你的串口通讯应该有自己的协议。
其次,你要知道,一条指令可能分成几个片段被收到,也可能一次收到几条指令。所以,指令的识别需要依赖你的协议,而不是,将每次收到的数据作为一条指令。
再次,不要用QThread,直接用QSerialPort提供的信号槽。(不仅代码精炼,而且不易出 .. (2014-07-16 14:11) 

感谢板主的鼎力相助
我的串口通讯确实有自己的协议,定义上是七个bytes为一个完整的资讯
这部份我了解要自己处理

因为主程式是GUI程式,如果直接在画面的那个程式用QSerialPort的讯号槽
并且跑一个无限回圈,GUI不是应该会停滞住吗?

还是您的意思是另外一个非QThread的Class用Signal&SLOT来处理
可是我的问题就是我不知道该怎麽去自定义这个东西
我想是自己的基础太差....

论坛中有类似的范例吗?不知道该用什麽关键字去搜寻
所以才斗胆贴文发问

离线dosmlp

只看该作者 10楼 发表于: 2014-07-16
用一个全局变量来接收串口数据,用信号和槽,每次接收到串口数据都在这个变量上追加数据,够7位后进行处理,然后继续下次的接收 反正我就是这么干的
头文件中定义串口:
QSerialPort *com0,*com1,*com2;//串口
初始化:
    com0 = new QSerialPort(this);
    com1 = new QSerialPort(this);
    com2 = new QSerialPort(this);
信号和槽的连接(一旦串口可读,则执行槽函数read_com(),槽函数也要在头文件中定义)
connect(com0,SIGNAL(readyRead()),this,SLOT(read_com()));
槽函数
void Widget::read_com()
{
    static QString data;
    data.append(QString(com0->readAll()));
    if(data.length()>=7){
        QString tmp;
        tmp=data.mid(0,7);
        data=data.mid(8);
    }
}
tmp就是取到的前七位数据
代码可能不对,但思路就是这样子
离线迷迷糊糊

只看该作者 11楼 发表于: 2014-07-16
我之前也遇到过类似的问题。
串口读取数据有两种方式,一种是有数据了会触发,一种是定时去读取数据。
下面这个文章介绍的不错。
http://blog.csdn.net/yafeilinux/article/details/4717706
我一开始也是用线程去读数据,后面发现用自己定义的协议去读数据也可以。省事。
注意:下面我的方法是为了读取一个完整的7个字节的方法。希望能帮到你!
关于数据读取的方法,我的做法是这样的,我用的是上面网址上的串口协议。
  1. void form::readMyCom()
  2. {
  3.     //bool ok;
  4.     QByteArray temp = myCom->readAll();   //读取串口数据
  5.     if(!temp.isEmpty())   //判断是否是空数据.按之前我的测试结果,有可能触发过来是空数据,所以加上这个比较保险.
  6.     {
  7. datbuf=datbuf+temp;     //在方法前面定义一个QByteArray 的datbuf用于存放串口数据,无论你来多少,数据全部都按先后顺序存进去。
  8. if(datbuf.length>7)   //判断datbuf里面的数据是否大于7位,如果是,说明有7个完整字节的数据,可以通过槽发送出去
  9. {
  10. emit this->sendbuf(datbuf.mid(0,7));    //发出7个完整字节的信号
  11. datbuf = datbuf.mid(7,datbuf.length-7);   //发出完毕后,把datbuf 的前7个字节清掉等待下次接收数据。
  12. }
  13.         }
  14.     }




离线琉璃螃蟹

只看该作者 12楼 发表于: 2014-07-16
感谢回复
我尝试看看

谢谢大家热心帮助
离线labhome

只看该作者 13楼 发表于: 2014-07-17
参照@dosmlp的方法,不能把美好的愿望放在下位不会出错的愿景上。
离线琉璃螃蟹

只看该作者 14楼 发表于: 2014-08-08
回报一下我後来处理的方式,与理解到的状况。

1. 不用回圈的方式控制连续行为,改用QTimer的方式去定时触发。
2. QTimer 在Ubuntu上的最小可控大概在20ms。
3. QT的Serial控制在高速下不稳定(每40ms一个传接回圈),数千次就会脱节出错。
4. 即使提升到100ms传接一次,大概跑9小时左右也会失控。
5. 原本以为是Buffer的问题,但现在每次读取完成後我会下 serial->clear(); ,应该就不是Buffer造成。

以上,提供有遇到相同问题的人参考。

==============以下发问时间================

另外再请教,我现在的作法是
传值用TimerA 设 100ms
传了之後 开TimerB 等待50ms,之後才 connect(TimerB ,SIGNAL(timeout()),this,SLOT(serialRead()));
进到serialRead()後 TimerB  关闭,读资料
之後有做前面前辈们给的意见,读不全补完的回圈,尝试次数30次,超过则放弃此次
这里用的读法是if(serial->waitForReadyRead(10))
後面再判断资料正确与否,错误则停止TimerA
这种跑法可以跑9小时左右资料传接都正常

我想问的是,除了QTimer以外,还有没有什麽其他不影响其他运作的等待指令呢?
waitForReadyRead() 只能确认ReadyRead()但是不保证内容是不是完整,7个bytes可能要读好多次才会读全
用qSleep 与 qWait 也都不可行

如果没有,我只能用QTimer顶着用了


快速回复
限100 字节
 
上一个 下一个