Qt串口QSerialPort的多线程 及 QSerialPort的waitForReadyRead函数的问题

2023-05-16

在官方的文档中提到,QSerialPort是不支持跨线程调用。
在这里插入图片描述因此不能在主线程(UI线程)创建QSerialPort,然后传递个指针给子线程,然后在子线程中执行耗时的发送任务。
最好的方式是在子线程里面创建QSerialPort,然后通过信号槽来使用。

但是我想偷个懒,我已经在主线程创建了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”),但是串口助手那边就是收不到信息。

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

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

bool QSerialPort::waitForBytesWritten(int msecs = 30000)

或者在子线程中new一个QSerialPort成员对象,感觉会更好一点。


QSerialPort的waitForReadyRead函数的问题

另外,Qt的waitForReadyRead函数经过测试是有问题(Qt5.12.9),无论怎样都是超时返回。别人也遇到这个问题,而且是升级5.12.10就ok。估计是个bug
-----经过测试,Qt5.12.12也还是不行。
-----再经过测试,发现不是版本的问题,而是Qt事件机制的问题 。灵感来自这里
最后发现将串口的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,槽函数都是在主线程(串口所在的线程)执行的。所以究竟真正的原因是啥,我也不知道了。
找到他的源码,看到有个 readCompletionOverlapped,从名字来看作用应该是判断read是否完成。所以,猜想一下,这个waitForReadyRead函数,可能在接收ReadyRead信号后,会利用这个readCompletionOverlapped来判断是否需要返回,而这个东西与read有关,因此,是不是说只要我不读,他就正常工作了呢?
在这里插入图片描述结果一试,果然可以。
总结一下,解决QSerialPort的waitForReadyRead()总是超时返回的办法有两个:
1.在绑定了readyRead()的我们自己写的槽函数中,不要进行读取数据的操作。
2.将readyRead()与我们自己写的槽函数绑定时,使用Qt::QueuedConnection。

使用了Qt::QueuedConnection就可以的原因,我觉得应该是:
通过使用Qt::QueuedConnection,将槽函数放到了连接到该信号的 槽函数队列 的比较后的位置,(调用waitForReadyRead时,其实里面也执行了类似信号槽的绑定,而且这个绑定应该是 Qt::DirectConnection),因此在信号发出之后,先执行waitForReadyRead里面的槽函数,然后再执行我们自己的函数。这样我们的函数就不会影响到 readCompletionOverlapped 这个变量了。
在这里插入图片描述在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • linux 进程线程思维导图

  • 修改结构体中成员的值

    include lt iostream gt using namespace std struct student char name 10 float grade 更改student数据的grade成员 xff0c 参数形式为引用 voi
  • C++中冒号的用法

    1 冒号 xff08 xff09 用法 xff08 1 xff09 表示机构内位域的定义 xff08 即该变量占几个bit空间 xff09 typedef struct XXX unsigned char a 4 unsigned char
  • C/C++预处理指令

    本文主要记录了C C 43 43 预处理指令 xff0c 常见的预处理指令如下 xff1a 空指令 xff0c 无任何效果 include包含一个源代码文件 define定义宏 undef取消已定义的宏 if如果给定条件为真 xff0c 则
  • STM32F1常用外设介绍(超详细35000字介绍)

    STM32学习笔记 GPIO配置步骤 步骤 xff1a 第一步 xff0c 使用RCC开启GPIO的时钟 第二步 xff0c 使用GPIO Init 函数初始化GPIO 第三步 xff0c 使用输出或者输入的函数控制GPIO口 常用的RCC
  • MFC拷贝文件及进度条显示

    参考 xff1a 封装CopyFileEx函数 xff0c 实现文件复制中的暂停 xff0c 控速 xff0c 获取进度 http blog csdn net career2011 article details 6844513 实例讲解C
  • 什么是寄存器?(STM32)

    什么是寄存器 xff1f 我们现在在开发STM32时 xff0c 已经很少用到寄存器编程 xff0c 更多的使用ST公司所提供的标准库和最新的HAL库进行编程实现 xff0c 但是不管是标准库还是HAL库都是在原来的寄存器层面上进行了封装
  • 计算机网络——物理层(一)

    物理层 xff08 部分 xff09 机械特性 xff1a 指明接口所用接线器的形状和尺寸 引脚数目和排列 固定和锁定装置等 电气特性 xff1a 指明在接口电缆的各条线上出现的电压的范围 功能特性 xff1a 指明某条线上出现的某一电平的
  • 洛谷P5717-三角形分类

    洛谷P5717 三角形分类 题目 这道题更像是初中题 xff0c 但是怎么能完整的按照题目的意思来解决呢 xff0c 说实话这个题卡了我有一会儿 xff0c 要做一次性做出这个题 xff0c 我觉得需要搞清楚if if 和if else i
  • 洛谷P1424-小鱼的航程(改进版)

    洛谷P1424 小鱼的航程 xff08 改进版 xff09 这个题我第一次做的时候 xff0c 有两个没过 xff0c 后来检查的时候发现原来是没有考虑开始的时间是不是周六周日 xff0c 如果是周六要在原来的天数上 2 xff0c 如果是
  • freertos-简介(一)

    FreeRTOS 裸机 不带任何操作系统 只能先打完游戏回复信息 实时性差 xff0c 程序轮流执行delay空等待 xff0c CPU不执行其他代码结构臃肿 xff0c 实现功能都在while循环 RTOS 实时操作系统 会执行打游戏一个
  • PCB设计过程中AD使用流程详解(超详细)

    PCB设计过程中AD使用流程详解 xff08 超详细 xff09 1 设计前期部分 规则设定 xff1a Preference system file type关联文件 xff08 所有关联 xff09 PCB editor General
  • python面向对象编程

    符合python风格的对象 先来看一个向量类的例子 span class token keyword class span span class token class name Vector2d span span class token
  • DIY 一个树莓派无人机

    学习目标 xff1a DIY 一个树莓派无人机 这篇文章来源于DevicePlus com英语网站的翻译稿 提示 xff1a 这里可以添加学习目标 学习内容 xff1a 提示 xff1a 这里可以添加要学的内容 今天 xff0c 我们将利用
  • Linux开源杀毒软件CLamAV介绍

    Linux开源杀毒软件CLamAV介绍 很多用户可能不知道在Linux上会有计算机病毒 xff0c 虽然Linux上的病毒不像在Windows上那么常见 xff0c 但实际上 xff0c 很多重要系统均采用Linux系统作为服务器的操作系统
  • vrpn_cient_ros发送频率无法调整提供解决思路

    最近写了节点来订阅ros client ros 但是发现在launch文件修改update frequency不起作用 xff0c 然而我又需要通过串口给下位机发送数据 xff0c 频率一快 xff0c 串口直接堵死了 怎么调都freque
  • 封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度

    封装CopyFileEx函数 xff0c 实现文件复制中的暂停 xff0c 控速 xff0c 获取进度等 前段时间无意间想到如何控制文件复制过程的复制速度 xff0c 并且能实时获得复制进度 对于一个几兆甚至更小的文件 xff0c 调用AP
  • 字符串结束符'\0' -何时自动加- 字符串定义方法

    转载 字符串定义方法 有两种方法 1 用字符数组 xff1b 2 用字符指针 xff1b 对应两种定义方法 xff0c 有不同的初始化以及赋值方法 对字符数组 xff0c 有以下几种定义方法 xff1a 1 char str 61 34 1
  • 基于Airsim的sitl模拟环境配置(ubuntu 16.04)

    基于Airsim的sitl模拟环境配置 xff08 ubuntu 16 04 xff09 sitl仿真 xff0c 软件在环仿真可以不使用任何硬件就可以进行模拟飞行或驾驶 xff0c 实验室获取数据非常有用 基于Airsim的sitl模拟需
  • F450机架组装及飞控安装细节

    http tieba baidu com p 5342947735

随机推荐

  • 富斯i6接收机及PPM编码器​​​​​​​接线

    没有完成发射机和接收机对码 xff0c 则需要按照如下过程对码 xff1a 1 将对码线连接到接收机上的 B VCC 接口 2 将电源线连接到接收机上任意其他接口 3 打开发射机电源 xff0c 同时常按发射机 BINDKEY 键 xff0
  • Pixhawk指示灯和安全开关含义

    Pixhawk指示灯的含义 红灯和蓝灯闪 xff1a 初始化中 请稍等 黄灯双闪 xff1a 错误 系统拒绝解锁 蓝灯闪 xff1a 已加锁 xff0c GPS搜星中 自动导航 xff0c 悬停 xff0c 还有返回出发点模式需要GPS锁定
  • MP地面站提示

    PIX飞控或者APM飞控在装机后 xff0c 经常遇到不能解锁的情况 xff0c 地面站会有提示 xff0c 下面列出了可能出现的情况 xff0c 可以一一对应的排除故障 当然 xff0c 你也可以在地面站设置解锁不自检 xff0c 不过安
  • win7下 pixhawk (ardupilot) 的编译

    前几天都在搞pixhawk源码编译问题 xff0c 什么在window下用Console或者eclipse xff0c 还是在Ubuntu下 xff0c 都做了 xff0c 而且把 mk文件都看了 xff0c 结果还是有bug 总结一下三种
  • pixhawk 基于UART5 的NSH环境搭建

    元器件 xff1a pixhawk 六针杜邦线 USB转uart模块 PC机上的串口调试软件 xff08 比如 Putty xff09 STEP 1 xff1a 制作调试通信线 xff08 六针杜邦线和USB转uart模块 xff09 xf
  • 操作系统 | 用户态和内核态的切换(中断、系统调用与过程(库函数)调用)

    文章目录 中断过程调用系统调用过程调用和系统调用的区别 中断 用户态 内核态之间的切换是怎么实现的 用户态 内核态 是通过中断实现的 并且 中断是唯一途径 核心态 用户态 的切换是通过执行一个特权指令 xff0c 将程序状态字 PSW 的标
  • MFC鼠标响应、鼠标画线

    鼠标响应关键就是对两个函数进行操作 xff1a OnLButtonDown和OnLButtonUp xff1b 1 使用MFC AppWizard exe xff09 建立一个单文档MFC工程 2 首先要在CxxxView类的定义里加上后续
  • stm32cubeide的freertos-消息队列发送结构体或者长消息,接收不完整的问题

    今天在项目创建消息队列 xff0c 消息队列中的数据类型的是结构体 xff0c 调用接口使用的cubeide自带的创建消息队列的接口 span class token keyword typedef span span class toke
  • cmake添加动态链接库

    如果使用gcc进行单个c文件编译的时候 xff0c 有时候后面会需要添加如 L l之类的参数 xff0c 如下 gcc o test test c L usr local lib lopencv 这里 L是制定动态库的位置 xff0c l则
  • Error installing 'file:///xxxx': editable mode is not supported for pyproject.toml-style project

    Please install using pip install no use pep517 e or upgrade pip to the latest version https stackoverflow com questions
  • MSCKF演示实验

    在工作空间目录进行编译 catkin make span class token operator span pkg msckf vio span class token operator span cmake span class tok
  • Linux下抓取串口log

    1 准备 串口线一条 手机一个 2 步骤 1将串口线插入电脑USB口 xff0c 开启另一个终端查看串口设备名 xff0c 输入下面命令 xff1a dmesg 2找到最下面一行 xff0c 尾部即是串口设备名称 xff0c 我这里叫做 t
  • git拉取远程代码,但不要覆盖本地新修改的代码

    git拉取远程代码 xff0c 但不要覆盖本地新修改的代码 先切换到自己的分支 git checkout 自己的分支名 在自己分支上 xff0c 先提交 git add 然后 xff0c 将提交的代码放到暂存区 git stash 切换到主
  • 树莓派(raspberry pi)启用root账户,设置树莓派SSH连接因超时闲置断开

    安装diskgenius恢复安装过树莓派相关操作系统的TF卡容量 http www embed net com thread 145 1 1 html 树莓派使用SD卡剩余空间 更新 xff0c 直接在命令行下输入raspi config命
  • VNC连接Ubuntu出现灰屏与打不开terminal的情况

    VNC出现灰屏 使用dpkg l grep gnome xfce查看安装的图形化桌面是哪一种 xff0c 在 vnc xstarup中修改启动方式 如若使用xfce桌面 xff0c 使用startxfce4启动桌面 VNC打不开termin
  • de1-soc培训教材记录

    第一章 准备 1 1 安装软件 Altera De1 SoC培训教材 中的环境 quartus 13 1embedded command shell 13 1我所用的linux是de1soc lxde 1604 1 1 1下载链接 1 培训
  • UOS(ARM64)安装Qt5

    本教程主要参考了https blog csdn net no2101 article details 106024632 主机信息 xff1a 厂家 xff1a 宝德 xff08 PowerLeader xff09 型号 xff1a PT6
  • ONVIF与RTSP的关系。

    先说结论 xff0c 个人觉得RTSP这个传输协议是ONVIF这个架构的部分功能 xff08 音视频流 xff09 的实现 也就是支持ONVIF的摄像头 xff0c 必定提供了RTSP视频流的接口 附 xff1a 海康威视RTSP的URL规
  • python创建httpserver,并处理get、post请求

    参考 xff1a https blog csdn net qq 44159028 article details 118099689 https blog csdn net gujing001 article details 5315279
  • Qt串口QSerialPort的多线程 及 QSerialPort的waitForReadyRead函数的问题

    在官方的文档中提到 xff0c QSerialPort是不支持跨线程调用 因此不能在主线程 xff08 UI线程 xff09 创建QSerialPort xff0c 然后传递个指针给子线程 xff0c 然后在子线程中执行耗时的发送任务 最好