QT做的串口助手,子线程里waitforreadyread的问题

看不破说破 2019-01-16 05:25:00
1.问题:
QT做的串口助手,主线程主要管理 GUI界面及打开、关闭、初始化串口;子线程用阻塞的方式读取串口数据;问题是子线程读数据时阻塞里面用readALL和read读出来的数据有时正确,有时是双份的。
例如:用虚拟串口测试(COM3/COM11),COM11发数据(用网上的稳定完善的串口助手),COM3收数据(用自己写的串口助手);COM11发送 “1”,COM3有时收到“1",有时收到"11",发的次数多了更可能收到"111"。

2.代码:
直接贴核心问题所在的代码。

mythread.cpp

#include "mythread.h"
#include<QThread>
#include<qDebug>




myThread::myThread(QObject *parent) : QObject(parent)
{
isStop = false;
qDebug()<<"子"
"线程号:"<<QThread::currentThread();

}

void myThread::myTimeout(QSerialPort *sendSerialPort)//线程处理函数
{
qDebug()<<"哪个线程号:"<<QThread::currentThread();
int num = 0;
QByteArray readbuff;

while(isStop == false)
{

if((sendSerialPort)->waitForReadyRead(13) == true)
{


num = (sendSerialPort)->bytesAvailable();
//qDebug()<<"串口数据个数:"<<num<<endl;


readbuff = (sendSerialPort)->read(num);
(sendSerialPort)->clear();


if(num >= 1)
{
emit mySignal(readbuff);

// qDebug()<<"从串口读到的数据:"<<tr(readbuff)<<endl;

}




/*

num = (sendSerialPort)->bytesAvailable();

QByteArray readbuff;

readbuff.append(sendSerialPort->readAll());

// while(sendSerialPort->waitForReadyRead(20))

// readbuff.append(sendSerialPort->readAll());

if(num >= 1)
{
emit mySignal(readbuff);

qDebug()<<"从串口读到的数据:"<<tr(readbuff)<<endl;

}
*/

}
}
}


void myThread::setFlag(bool flag)
{
isStop = flag;
}




3.报错信息:无


4.尝试过的方式、方法:
(1) 一直感觉是readAll 和 waitforreadyread的问题。
为了排除是线程的问题,用改写run的方法创建了线程,还是一样的现象。
在主线程里无法用阻塞读写,不然无法执行软件界面。
所以不是线程问题。
(2) 在while循环里面,隔多少秒读一次串口数据(非阻塞),没有任何问题,都是完善的;但是我想要的是阻塞读能正确读取串口数据。
(3). read和readAll都试过,现象一样不行。

5.相关截图:
截图不用了,没什么用,是思想问题,不是表面的报错问题。

特别提示:个人感觉问题主要在 mythread.cpp 的mytimeout函数(线程处理函数)里面(mytimeout名字乱起的,不用理会其意思)。 用/* */ 注释的不用太理会。

希望有大佬能帮忙解决,谢谢。
...全文
4150 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
看不破说破 2020-02-06
  • 打赏
  • 举报
回复
QT版本用最新版本试试。
King_zkk 2020-01-13
  • 打赏
  • 举报
回复
你创建的mythread不能有父类,把串口操作写一个work类move到mythread
shgb2003 2020-01-09
  • 打赏
  • 举报
回复
这个问题是因为QSerialPort在主线程,waitfor函数在子线程里。timer是不能跨线程的。所以失败的
暖暖的纠结 2019-09-04
  • 打赏
  • 举报
回复
老哥,相同问题。。Qt版本问题是指什么意思?
boatraining 2019-07-31
  • 打赏
  • 举报
回复
请问使用的是什么版本 ? 我 qt5.7 mingw 也有这问题。。。
看不破说破 2019-01-25
  • 打赏
  • 举报
回复
QT版本问题,结束。。。。。
kerwin liu 2019-01-17
  • 打赏
  • 举报
回复
这样就行了 ,不用太复杂。
if(serial.waitForReadyRead(10))
{
receiveData = serial.readAll();
}

另外关注一下是不是你的虚拟串口出的问题,造成的重复
看不破说破 2019-01-17
  • 打赏
  • 举报
回复
谢谢老哥的回复
看不破说破 2019-01-16
  • 打赏
  • 举报
回复

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"
#include"qpushbutton.h"
#include<QThread>
#include<QDebug>
#include<QMessageBox>

MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
setFixedSize(this->width(), this->height());//将主窗口设置为初始大小,禁止将主窗口变大。
scancom();//扫描可用端口
ui->pushButton_comclose->setEnabled(false);//使“关闭串口”按钮失能

qDebug()<<"主"
"线程号:"<<QThread::currentThread();


//动态分配空间,不能指定父对象
myT = new myThread;
//创建子线程
mythread = new QThread(this);
//把自定义线程加入到子线程中
myT->moveToThread(mythread);

connect(myT,&myThread::mySignal,this,&MyWidget::dealSignal);//子线程将数据通过带参信号将数据传给主线程处理。

connect(this,&MyWidget::startThread,myT,&myThread::myTimeout);//startThread带参信号将主线程串口指针传给子线程,槽函数是线程处理函数

connect(this,&MyWidget::destroyed,this,&MyWidget::dealClose);//关窗口会产生destroy信号,槽函数保证线程正常退出

//线程处理函数内部不允许操作图形界面,否则程序执行错误,根本执行不了

//重要问题:解释一下connect()的第五个参数
//connect()的第五个参数表示连接方式,主要有默认、直接、队列三种方式;
//多线程时才有意义
//如果是多线程,默认使用队列(队列:槽函数所在线程和接收者一样)
//如果是单线程,默认使用直接(槽函数所在线程和发送者一样)
//最好、一般用默认,就是不填。


}

MyWidget::~MyWidget()
{
delete ui;
}

void MyWidget::dealClose()//主窗口关闭时处理的槽函数
{
on_pushButton_comclose_clicked();//关闭串口
delete myT;//养成好习惯,及时回收动态分配的空间
}

void MyWidget::dealSignal(QByteArray data)//真正的实现现象的函数
{
//将接收到的数据显示在接收区
QString string= ui->textEdit_recv->toPlainText();

qDebug()<<"收到的数据:"<<tr(data)<<endl;

string += tr(data);
ui->textEdit_recv->setText(string);
qDebug()<<"显示的数据:"<<string<<endl;

qDebug()<<"主线程号:"<<QThread::currentThread();

}

void MyWidget::scancom(void)//扫描可用端口并加入端口下拉框
{
mySerialPort = new QSerialPort;//新建串口类对象
if(NULL != mySerialPort)
{
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())//查找可用串口。找不到存在串口是不会进入到foreach内部的,存在不一定可用.
{
QSerialPort availableport;
int i;
availableport.setPortName(info.portName());

if(availableport.open(QIODevice::ReadWrite))//看串口是否可用(即可读写)。
{
int IsHaveInItemList =0;

for(i=0; i<ui->comboBox_port->count(); i++)//看串口是否在列表中
{
if(ui->comboBox_port->itemData(i) == availableport.portName())
{
IsHaveInItemList = 1;
}
}

if(IsHaveInItemList == 0)
{
ui->comboBox_port->addItem(availableport.portName());//加UI的下拉框里面
qDebug()<<info.portName()<<endl;
}
availableport.close();
}

}
}
else
{
QMessageBox::warning(this,tr("警告"),tr("串口没有初始化成功"),QMessageBox::Ok);
}
}


void MyWidget::opencom(void)//打开串口及串口初始化
{
if(NULL == mySerialPort)
{
mySerialPort = new QSerialPort();
}

if(ui->comboBox_port->count() != 0)//存在可用端口就进入
{
mySerialPort->setPortName(ui->comboBox_port->currentText());//设置端口名
mySerialPort->open(QIODevice::ReadWrite);//打开串口
mySerialPort->setBaudRate(ui->comboBox_baudrate->currentText().toInt());//设置波特率

switch(ui->comboBox_databit->currentText().toInt())//设置数据位
{
case 5:
mySerialPort->setDataBits(QSerialPort::Data5);
break;
case 6:
mySerialPort->setDataBits(QSerialPort::Data6);
break;
case 7:
mySerialPort->setDataBits(QSerialPort::Data7);
break;
case 8:
mySerialPort->setDataBits(QSerialPort::Data8);
break;
default:
mySerialPort->setDataBits(QSerialPort::Data8);
break;
}

switch(ui->comboBox_checkbit->currentIndex())//设置校验位
{
case 0:
mySerialPort->setParity(QSerialPort::NoParity);
break;
case 1:
mySerialPort->setParity(QSerialPort::OddParity);
break;
case 2:
mySerialPort->setParity(QSerialPort::EvenParity);
break;
}

switch(ui->comboBox_stopbit->currentText().toInt())//设置停止位
{
case 1:
mySerialPort->setStopBits(QSerialPort::OneStop);
break;
case 2:
mySerialPort->setStopBits(QSerialPort::TwoStop);
break;
}

mySerialPort->setFlowControl(QSerialPort::NoFlowControl);
mySerialPort->setReadBufferSize(68);//设置缓冲区的大小。如果读串口数据不能一次性读完,则数据会存入内部缓冲区。

//串口设置的下拉控件失能
ui->comboBox_baudrate->setEnabled(false);
ui->comboBox_checkbit->setEnabled(false);
ui->comboBox_databit->setEnabled(false);
ui->comboBox_port->setEnabled(false);
ui->comboBox_stopbit->setEnabled(false);

ui->pushButton_comopen->setEnabled(false);//使“打开串口”按钮失能
ui->pushButton_comclose->setEnabled(true);//使能“关闭串口”按钮

}
else
{
QMessageBox::warning(this,tr("警告"),tr("无可用串口!"),QMessageBox::Ok);
}

}


void MyWidget::closecom(void)//关闭串口
{
qDebug()<<"端口"<<ui->comboBox_port->currentText()<<"已关闭"<<endl;
mySerialPort->close();

//串口设置的下拉控件使能
ui->comboBox_baudrate->setEnabled(true);
ui->comboBox_checkbit->setEnabled(true);
ui->comboBox_databit->setEnabled(true);
ui->comboBox_port->setEnabled(true);
ui->comboBox_stopbit->setEnabled(true);

ui->pushButton_comopen->setEnabled(true);//使能“打开串口”按钮
ui->pushButton_comclose->setEnabled(false);//使“关闭串口”按钮失能

}

void MyWidget::clearsend(void)//清发送区
{
ui->textEdit_send->clear();
}

void MyWidget::clearrecv(void)//清接收区
{
ui->textEdit_recv->clear();
}

void MyWidget::writechr(void)//写char数据进串口
{
//获取界面上的数据并转换成utf8格式的字节流
QByteArray sendData = ui->textEdit_send->toPlainText().toUtf8();
if ((sendData.isEmpty() == 0) && (sendData.isNull() == 0)) {
mySerialPort->write(sendData);
}
}

/*
void MyWidget::readfromcom(void)//读串口的数据
{
QByteArray buf;
buf = mySerialPort->readAll();//readAll()函数,读取串口的数据

if(buf.isEmpty() == 0)
{
qDebug()<<"哪个线程号?111:"<<QThread::currentThread();

QString str = ui->textEdit_recv->toPlainText();

str += tr(buf);//tr()函数

//ui->textEdit_recv->clear();
ui->textEdit_recv->setText(str);
//ui->textEdit_recv->append(str);

}
qDebug()<<"哪个线程号?:"<<QThread::currentThread();
qDebug()<<"2"<<endl;
buf.clear();
}
*/


void MyWidget::on_pushButton_comopen_clicked()//按下“打开串口”按钮对应的槽函数
{
opencom();//调用打开串口的函数

if(mythread->isRunning() == true)
{
return ;
}
//启动线程,但没有启动线程处理函数
mythread->start();
myT->setFlag(false);

//不能直接调用线程处理函数
//只能通过signal - slot方法调用
emit startThread(mySerialPort);//将主线程的QSerialPort 对象通过带参信号传给子线程

}

void MyWidget::on_pushButton_comclose_clicked()//按下“关闭串口”按钮对应的槽函数
{
if(mythread->isRunning() == false)
{
return ;
}

//
myT->setFlag(true);
mythread->quit();//退出线程,不建议使用terminate(),因为会导致严重的内存问题.
mythread->wait();//等待线程结束

closecom();//调用关闭串口的函数
}

void MyWidget::on_pushButton_clearsd_clicked()//按下“清发送区”按钮对应的槽函数
{
clearsend();
}

void MyWidget::on_pushButton_clearrecv_clicked()//按下“清接收区”按钮对应的槽函数
{
clearrecv();
}

void MyWidget::on_pushButton_send_clicked()//按下“发送”按钮对应的槽函数
{
writechr();
}
看不破说破 2019-01-16
  • 打赏
  • 举报
回复 1
头文件

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include<QSerialPort>
#include<QSerialPortInfo>

class myThread : public QObject
{
Q_OBJECT
public:
explicit myThread(QObject *parent = 0);

// void readfromcom(void);

//线程处理函数
void myTimeout(QSerialPort *sendSerialPort);
void setFlag(bool flag = true);

signals:
void mySignal(QByteArray data);

public slots:

private:
bool isStop;
// QSerialPort *mySerialPort;//necessary or will be wrong
};

#endif // MYTHREAD_H




mywidget.h


#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include<QMessageBox>
#include <QtSerialPort/QSerialPort> //提供访问串口的功能
#include <QtSerialPort/QSerialPortInfo> //提供系统中存在的串口的信息
#include<QDebug>
#include"mythread.h"

namespace Ui {
class MyWidget;
}

class MyWidget : public QWidget
{
Q_OBJECT

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

//一般函数
void scancom(void);//扫描可用端口,并添加到端口选项中。

void opencom(void);//打开串口

void closecom(void);//关闭串口
void clearsend(void);//清发送区
void clearrecv(void);//清接收区
void writechr(void);//写char数据
void readfromcom(void);//从端口读数据


//槽函数


private slots:
void on_pushButton_comopen_clicked();

void on_pushButton_comclose_clicked();

void on_pushButton_clearsd_clicked();

void on_pushButton_clearrecv_clicked();

void on_pushButton_send_clicked();

void dealSignal(QByteArray data);

void dealClose();



signals:
void startThread(QSerialPort *sendSerialPort);//启动子线程,将当前的端口名传到


private:
Ui::MyWidget *ui;
QSerialPort *mySerialPort;//necessary or will be wrong
myThread *myT;
QThread *mythread;
};

#endif // MYWIDGET_H

16,203

社区成员

发帖
与我相关
我的任务
社区描述
Qt 是一个跨平台应用程序框架。通过使用 Qt,您可以一次性开发应用程序和用户界面,然后将其部署到多个桌面和嵌入式操作系统,而无需重复编写源代码。
社区管理员
  • Qt
  • 亭台六七座
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧