文章目录

  • 1.QSerialPort的多线程限制
    • 1.1.尝试通过复制参数在子线程运行
      • 1.1.1.操作原理
      • 1.1.2.QSerialPort::write的异步特性
  • 2.使用QSerialPort的waitForReadyRead
    • 2.1.遇到的问题
    • 2.2.资料查找
    • 2.3.无意发现
    • 2.4.尝试Qt源码分析
    • 2.5.解决方法总结
    • 2.6.原因分析

1.QSerialPort的多线程限制

在官方的文档中提到,QSerialPort是不支持跨线程调用。
因此不能在主线程(UI线程)创建QSerialPort,然后传递个指针给子线程,然后在子线程中执行耗时的发送任务。
最好的方式是在子线程里面创建QSerialPort,然后主线程通过信号槽来间接使用(参数还是可以通过QSerialPort的指针来读写)。

1.1.尝试通过复制参数在子线程运行

这里我想偷个懒,我已经在主线程创建了QSerialPort,并且界面相关的东西都做好了,不想改动太大,因此我想了个办法:
在新线程里面建立一个临时对象,复制传递进去的串口参数,执行完就干掉他。

1.1.1.操作原理

把传递进来的、主线程的QSerialPort给close掉,相当于取消占用。然后在子线程中根据这个串口的配置,重新打开一个QSerialPort对象,并执行写操作。

void MainWindow::on_pushButton_writeFlash_clicked()
{//    mHexfileReader->writeToDevice(mPort);//让writeToDevice函数在子线程中执行QMetaObject::invokeMethod(mHexfileReader, "writeToDevice", Qt::QueuedConnection, Q_ARG(QSerialPort*, mPort));
}------------------------------------
//move to子线程
HexFileReader::HexFileReader(QObject *parent) : QObject(parent)
{QThread *thread = new QThread();this->moveToThread(thread);thread->start();
}void HexFileReader::writeToDevice(QSerialPort *port)
{port->close();QSerialPort tmpPort;tmpPort.setPortName( port->portName()   );tmpPort.setBaudRate( port->baudRate()   );tmpPort.setParity(   port->parity()     );tmpPort.setDataBits( port->dataBits()   );tmpPort.setStopBits( port->stopBits()   );// connect(&tmpPort, &QSerialPort::readyRead, [&tmpPort](){//    qDebug() << "read all:" << tmpPort.readAll();// });auto ret = tmpPort.open(QSerialPort::ReadWrite);qDebug() << "open port:" << ret;if(tmpPort.isOpen()){uchar header[] = {0x31, 0xce};qDebug() << "write header:" << tmpPort.write((char*)header, 2);}tmpPort.close();
}

但是发现,串口成功打开,发送也成功( “write header: 2”),但是串口助手那边就是收不到信息。

1.1.2.QSerialPort::write的异步特性

最后通过漫长的调试(取消多线程、把临时变量换成类成员变量、逐步调试等),终于发现了原因:QSerialPort的write函数,其实并不是同步的
也就是说当调用该函数,并取得返回值后,并不是表示数据已经完全发送出去了。
假如在调用完这个函数之后,立马析构掉QSerialPort对象,那么对方很可能会什么也收不到。

知道了原因,那就好解决了:
在执行写操作之后,执行这个,等待写入(发送)完成。

bool QSerialPort::waitForBytesWritten(int msecs = 30000)

2.使用QSerialPort的waitForReadyRead

前面使用了waitForBytesWritten,我们再看看类似的一个函数:waitForReadyRead。

2.1.遇到的问题

Qt的waitForReadyRead函数经过测试,假如使用不当是有问题(Qt5.12.9),无论怎样都是超时返回。就算别人明确发送了数据过来,而且QSerialPort::readyRead信号也发出了,他还是等到超时,再返回超时错误。

2.2.资料查找

别人也遇到这个问题,而且是升级5.12.10就ok。估计是个bug 。
-----经过测试,Qt5.12.12也还是不行。
-----再经过测试,发现不是版本的问题,而是Qt事件机制的问题 。灵感来自这里

2.3.无意发现

最后发现将串口的readyRead的信号槽连接方式由

    connect(mPort, &QSerialPort::readyRead, [=](){qDebug() << mPort->readAll().toHex(' ') << QTime::currentTime();});

变成这样就OK了(用Qt5.12.9也可以)。

    connect(mPort, &QSerialPort::readyRead, this, [=](){qDebug() << mPort->readAll().toHex(' ') << QTime::currentTime();}, Qt::QueuedConnection);

假如说硬要分析的话,可能是因为这个waitForReadyRead会导致当前进程阻塞,然后就算readyRead信号发出了也无法响应。所以我们在连接串口的readyRead时,要用Qt::QueuedConnection,来使readyRead的所有 信号槽组合 能够执行,而不是卡住。
换言之, 假如你完全不连接这个ReadyRead,那么这个waitForReadyRead函数肯定是可以正常工作的。
但是经过测试,无论使用 Qt::DirectConnection 还是 Qt::QueuedConnection,槽函数都是在主线程(串口所在的线程)执行的,所以应该不是什么阻塞线程的问题。所以究竟真正的原因是啥,我也不知道了。

2.4.尝试Qt源码分析

找到他的源码,看到有个 readCompletionOverlapped,从名字来看作用应该是判断read是否完成。所以,猜想一下,这个waitForReadyRead函数,可能在接收ReadyRead信号后,会利用这个readCompletionOverlapped来判断是否需要返回,而这个东西与read有关,因此,是不是说只要我不读,他就正常工作了呢?
结果一试,果然可以。即使你connect了QSerialPort::readyRead信号,只要你不执行 QSerialPort::read()、QSerialPort::readAll()等函数,waitForReadyRead还是可以正常按照预期执行的。

2.5.解决方法总结

总结一下,解决QSerialPort的waitForReadyRead()总是超时返回的办法有两个:
1.在绑定了readyRead()的我们自己写的槽函数中,不要进行读取数据的操作。
2.将readyRead()与我们自己写的槽函数绑定时,使用Qt::QueuedConnection。

2.6.原因分析

使用了Qt::QueuedConnection就可以的原因,我觉得应该是:
通过使用Qt::QueuedConnection,将槽函数放到了连接到该信号的 槽函数队列 的比较后的位置,(调用waitForReadyRead时,可能里面也执行了类似信号槽的绑定,而且这个绑定应该是 Qt::DirectConnection),因此在信号发出之后,先执行waitForReadyRead里面的槽函数,然后再执行我们自己的函数。这样我们的函数就不会影响到 readCompletionOverlapped 这个变量了。
从下面这段测试代码也可以看到,使用了Qt::QueuedConnection的槽函数,即使你是先于其他槽函数与sender创建连接,他也还是在信号发出后在会在较后位置才执行。

Qt串口QSerialPort的多线程 及 QSerialPort的waitForReadyRead函数的问题相关推荐

  1. 【转】QT 串口QSerialPort + 解决接收数据不完整问题

    [转]QT 串口QSerialPort + 解决接收数据不完整问题 参考文章: (1)[转]QT 串口QSerialPort + 解决接收数据不完整问题 (2)https://www.cnblogs. ...

  2. Qt 串口类QSerialPort 使用笔记

    Qt 串口类QSerialPort 使用笔记 虽然现在大多数的家用PC机上已经不提供RS232接口了.但是由于RS232串口操作简单.通讯可靠,在工业领域中仍然有大量的应用.Qt以前的版本中,没有提供 ...

  3. android qt 串口通信,Qt串口通信开发之QSerialPort模块详细使用方法与实例

    Qt串口通信开发之QSerialPort模块详细使用方法与实例 发布时间:2020-10-23 12:19:05 来源:脚本之家 阅读:111 作者:沧海一笑-dj Qt串口通信基础及名词说明 串口通 ...

  4. QT串口QSerialPort解决接收数据不完整问题

    QT串口QSerialPort解决接收数据不完整问题 QSerialPort类例程:Examples\Qt-5.9.1\serialport\terminal,该例子完美展示了qt串口收发过程,直接在 ...

  5. Qt 串口类QSerialPort 学习笔记

    一.串口类简介 当前的QtSerialPort模块中提供了两个C++类,分别是QSerialPort 和QSerialPortInfo. QSerialPort 类提供了操作串口的各种接口. QSer ...

  6. 关于qt串口接收不完整,qstring转16进制

    关于Qt串口接收不完整 定时器接收 进制转换 定时器接收 之前,看了很多关于Qt接收的文章,貌似很少提到接收不完整,提到的做法就是让你一个一个判断,做一个状态机,有点臃肿:在实际工程里面,一般是发一个 ...

  7. QT开发(五十)——QT串口编程基础

    QT开发(五十)--QT串口编程基础 一.QtSerialPort简介 1.串口通信基础 目前使用最广泛的串口为DB9接口,适用于较近距离的通信.一般小于10米.DB9接口有9个针脚. 串口通信的主要 ...

  8. QT串口2--控制GSM模块的使用

    1.首先介绍下所用的gsm模块,YYROBOT_SIM800C 模块是一款高性能高性价比工业级的 GSM/GPRS 模块(开发板).本模块采用 SIMCOM 公司的工业级四频 850/900/1800 ...

  9. QT综合示例:QT串口通信

    QT综合示例:QT串口通信 0.界面: 1.代码: 如果用qt写程序作为上位机,然后通过和usb和下位机通信的时候,就需要用到qt中的串口通信了. 0.界面: 1.代码: 1).pro 添加: QT ...

最新文章

  1. 基于SSH实现教务管理系统
  2. JAVA中常用的异常处理情况
  3. Net设计模式实例之解释器模式(Interpreter Pattern)
  4. Spark RDD在Spark中的地位和作用如何?
  5. 安卓手机获取基站信息
  6. 吾解——HTTP(超文本传输协议)
  7. ssdp协议_Cotopaxi:使用指定IoT网络协议对IoT设备进行安全测试
  8. 代码意识流——花朵数问题(七)
  9. 外媒:新iPhone系列配备更大无线充电线圈 反向无线充即将到来
  10. 「消息秒回」是一种美德
  11. Java-Mail Java程序发送Email
  12. oracle 近一年,华为Mate 20 Pro发布近一年,仍受追捧,四个方面告诉你原因
  13. 骁龙8gen1性能怎么样 骁龙8gen1和苹果A15 哪个强
  14. js符号转码_js 字符串编码转换函数
  15. ping命令简单总结
  16. 手持两把锟斤拷,口中疾呼烫烫烫。脚踏千朵屯屯屯,笑看万物锘锘锘。
  17. GPS地图升级价格一览表
  18. Unity实战之见缝插针
  19. 计算机网络学习07(DNS域名系统详解)
  20. 什么是pisa测试_什么是pisa考试?

热门文章

  1. 拉卡拉收款宝拆解图示分析
  2. 蚂蚁最后一波实习生招聘了!HC充足,各岗均有!
  3. win10搜索框有图案怎么去掉?win10任务栏搜索框有图案
  4. python方法调用名字不一样_python属性访问和方法调用是不是不一样
  5. Linux在线安装harbor镜像仓库
  6. php一句话利用,常见php一句话webshell解析
  7. Chrome中的JS调试
  8. linux下的npm安装
  9. 2015专转本计算机答案解析,2015专转本计算机真题详解.pdf
  10. [chia] 用于P盘的SSD硬盘寿命是多久