百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

同一个类,不同代码,Qt 串口类QSerialPort 与各种外设通讯处理

liebian365 2025-03-28 18:18 28 浏览 0 评论

串口通讯在各种外设通讯中是常见接口,因为各种嵌入式CPU中串口标配,工业控制中如果不够还通过各种串口芯片进行扩展。比如spi接口的W25Q128FV.

对于软件而言,因为驱动接口固定,软件也相对好写,因此串口通讯也是嵌入式常见开发模式,但是对于受控设备类型五花八门,往往编程代码也是不尽相同。

串口参数处理

QSerialPort 是Qt用于串口处理类,在Linux/Windows能稳定工作,在Android实测也能按Linux操作。

按其文档,在HPUX等各种Unix平台也能使用,甚至MacOSX下,只要驱动正常是也是可以用。

Windows平台的串口端口名一般是 COM1 ~COMXX

而Linux 的命令比较自由,完全看驱动自己命名。比如/dev/ttyS0 ,如果是usb转串口往往,设备名称往往是 /dev/ttyUSB0 之类,使用时注意当usb串口进行插拔,其设备名会发现变化比如变成 /dev/ttyUSB1


半双工设备处理

很多单片机设备在进行设置时,局限于设备性能,在上一条指令未处理完之前,再发送下一条指令会不作响应,除非等到设备发送返回结果。比如我手头某家信号发生器(DDS)采用文本指令,生成信号往往需要调用多个指令设置不同参数,(频率,振幅等),它是一个STM32 单片机响应,因此在处理上一条指令,必须等到其响一个回车符,才能发送下一条指令。因此这类设备的串口是半双工的模式。

QSerialPort 处理这类设备,在发送命令后,必须要使用waitForReadyRead();一直等待设备的响应,为了保险往往还要多次等待。成功处理代码如下。

QByteArray W4ComDev::sendWaitRecv(const QByteArray&data,int recvTimeout)
{

    int ret =  mSerialPort.write(data);
     qDebug()<< __func__ << "ret "<<ret;
    if(ret<=0)
        return QByteArray();


     mSerialPort.waitForBytesWritten(recvTimeout);

    for(int i=0;i<3;i++){
        if(mSerialPort.waitForReadyRead(recvTimeout))
            break;
    }

    QByteArray result =  mSerialPort.readAll();

    if(textMode())
        qDebug() << "recv text \""<<result<<"\"";
    else
      qDebug() << "recv result  "<<result.toHex(' ');

    return result;
}

在处理时,要注意两点

在等待的过程中,是阻塞系统的执行的,因此为防止设备没有响应把整个软件卡死,必须要放在线程当中执行。

因为一次要发送多个队列,为了简化上层软件处理,可以设计一个命令队列,应用只需要一次把所有命令发送到队列,串口线程在逐条进行处理。

为此我队列可以直接用Qt自带队列类做了一个加锁队列

ifndef CONCURRENTQUEUE_H
#define CONCURRENTQUEUE_H

#include 
#include 
#include 

template
class ConcurrentQueue
{
public:
    ConcurrentQueue(int size=100){
       mQueueSize = size;
    }
    bool isFull(){
        if (-1 == mQueueSize)
            return false;

        mMutex.lock();
        int count = mQueue.count();
        mMutex.unlock();
        return count >= mQueueSize;
    }
    bool isEmpty(){
        mMutex.lock();
        bool empty = mQueue.isEmpty();
        mMutex.unlock();
        return empty;
    }
    void clean(){
        mMutex.lock();
        mQueue.clear();
        mNotFull.wakeAll();
        mMutex.unlock();
    }

    //取数据,如果没有,则直接退出
    T tryPull(){
        QMutexLocker locker(&mMutex);
        if (mQueue.count() == 0 )
            return T();

        return  mQueue.dequeue();

    }
   //加入数据,如果满则进入等待
    void pendPush(const T&t){
        QMutexLocker locker(&mMutex);

     //   if (mQueue.count() == mQueueSize)
      //        mNotFull.wait(&mMutex);
        mQueue.enqueue(t);

        mNotEmpty.wakeAll();


    }

    //取数据,如果没有则进入等待
    T pendPull(int timeout = 0){
        QMutexLocker locker(&mMutex);
        if (mQueue.count() == 0){
              if(timeout > 0){
                 if(!mNotEmpty.wait(&mMutex,timeout))
                      return T();
              }

              return T();
        }
//        else
//             mNotEmpty.wait(&mMutex);
        T i = mQueue.dequeue();


         mNotFull.wakeAll();
        return i;
    }

    void push(const T& t){
        mMutex.lock();
        mQueue.enqueue(t);
        mMutex.unlock();
    }
    T pull(){
        mMutex.lock();
        T i = mQueue.dequeue();
        mMutex.unlock();
        return i;
    }

    int count(){

        QMutexLocker locker(&mMutex);
        int count = mQueue.count();

        return count;
    }

    int queueSize() { return mQueueSize;}


protected:
    int mQueueSize = 100;
    QQueue mQueue;
    QWaitCondition mNotEmpty;
    QWaitCondition mNotFull;
    QMutex mMutex;
    //mutable QReadWriteLock RWlock; //优点可以多线程同时读,比QMutex更高效
};

#endif // CONCURRENTQUEUE_H

使用时如下定义即可

ConcurrentQueue txCmdQueue;


少量数据传输

比如BLE透传模块,这类设备往往每次传输不到20byte,而且数据往往是二进制。这样用

QSerialPort的信号readReady来异步接收数据往往不能及时收到。我的理解是QSerialPort需要接收到一定长度,或者收到回车符才会触发事件。这样会造成上位机软件处理不及时。

这种情况处理,需要采用同步接收方式,使用一个线程不断使用readAll()方法来处理,再结合

void ComRecvThread::recvTest()
{
    QByteArray data;
    while(!needClose())
    {
        if(mDevice->waitForReadyRead(50)){
            // qApp->processEvents();
             data = mDevice->readAll(); //读取串口数据
        if(!data.isEmpty())
        {
        
            qDebug() << __func__ << "recv "<< data.toHex(' ');
            emit readReady(data);
        }
        else {
            qDebug() << "not recv";
        }

        QThread::msleep(30);
    }
    }

}

实测这样能及时收到反应。

大量数据连续数据

有一些工业控制设备,如信号基站等,在运行中会不断发送设备状态数据。数据量大而且往往连续不断发送。

这种情况用同步方法或用异步事件均可以。但这种情况会出现一些问题,一些协议包往往较长,一次readAll()无法读取,或者一个协议包正好在两次接收分别收上来。这就牵涉到分包和拼包的问题。

这个一般的做法是根据协议格式进行单独处理,但是这种处理需要另开一个buffer来按字节逐次检测,效率偏低也不通用。

这种情况QDataStream 就派上用场了,这个类非常好用。请参考我关于QDataStream处理文章。

相关推荐

精品博文嵌入式6410中蓝牙的使用

BluetoothUSB适配器拥有一个BluetoothCSR芯片组,并使用USB传输器来传输HCI数据分组。因此,LinuxUSB层、BlueZUSB传输器驱动程序以及B...

win10跟这台计算机连接的前一个usb设备工作不正常怎么办?

前几天小编闲来无事就跑到网站底下查看粉丝朋友给小编我留言询问的问题,还真的就给小编看到一个问题,那就是win10跟这台计算机连接的一个usb设备运行不正常怎么办,其实这个问题的解决方法时十分简单的,接...

制作成本上千元的键盘,厉害在哪?

这是稚晖君亲自写的开源资料!下方超长超详细教程预警!!全文导航:项目简介、项目原理说明、硬件说明、软件说明项目简介瀚文智能键盘是一把我为自己设计的——多功能、模块化机械键盘。键盘使用模块化设计。左侧的...

E-Marker芯片,USB数据线的“性能中枢”?

根据线缆行业的研究数据,在2019年搭载Type-C接口的设备出货量已达到20亿台,其中80%的笔记本电脑和台式电脑采用Type-C接口,50%的智能手机和平板电脑也使用Type-C接口。我们都知道,...

ZQWL-USBCANFD二次开发通讯协议V1.04

修订历史:1.功能介绍1.1型号说明本文档适用以下型号:  ZQWL-CAN(FD)系列产品,USB通讯采用CDC类实现,可以在PC机上虚拟出一个串口,串口参数N,8,1格式,波特率可以根据需要设置(...

win10系统无法识别usb设备怎么办(win10不能识别usb)

从驱动入手,那么win10系统无法识别usb设备怎么办呢?今天就为大家分享win10系统无法识别usb设备的解决方法。1、右键选择设备管理器,如图:  2、点击更新驱动程序,如图:  3、选择浏览...

微软七月Win8.1可选补丁有内涵,含大量修复

IT之家(www.ithome.com):微软七月Win8.1可选补丁有内涵,含大量修复昨日,微软如期为Win7、Win8.1发布7月份安全更新,累计为6枚安全补丁,分别修复总计29枚安全漏洞,其中2...

如何从零开始做一个 USB 键盘?(怎么制作usb)

分两种情况:1、做一个真正的USB键盘,这种设计基本上不涉及大量的软件编码。2、做一个模拟的USB键盘,实际上可以没有按键功能,这种的需要考虑大量的软件编码,实际上是一个单片机。第一种设计:买现成的U...

电脑识别U盘失败?5个实用小技巧,让你轻松搞定USB识别难题

电脑识别U盘失败?5个实用小技巧,让你轻松搞定USB识别难题注意:有些方法会清除USB设备里的数据,请谨慎操作,如果不想丢失数据,可以先连接到其他电脑,看能否将数据复制出来,或者用一些数据恢复软件去扫...

未知usb设备设备描述符请求失败怎么解决

出现未知daousb设备设备描述符请求失du败解决办zhi法如下:1、按下Windows+R打开【运行】;2、在版本运行的权限输入框中输入:services.msc按下回车键打开【服务】;2、在服务...

读《飘》47章20(飘每章概括)

AndAhwouldn'tleaveMissEllen'sgrandchildrenfornotrashystep-patobringup,never.Here,Ah...

英翻中 消失的过去 37(消失的英文怎么说?)

翻译(三十七):消失的过去/茱迪o皮考特VanishingActs/JodiPicoult”我能做什么?“直到听到了狄利亚轻柔的声音,我才意识到她已经在厨房里站了好一会儿了。当她说话的时候,...

RabbitMQ 延迟消息实战(rabbitmq如何保证消息不被重复消费)

现实生活中有一些场景需要延迟或在特定时间发送消息,例如智能热水器需要30分钟后打开,未支付的订单或发送短信、电子邮件和推送通知下午2:00开始的促销活动。RabbitMQ本身没有直接支持延迟...

Java对象拷贝原理剖析及最佳实践(java对象拷贝方法)

作者:宁海翔1前言对象拷贝,是我们在开发过程中,绕不开的过程,既存在于Po、Dto、Do、Vo各个表现层数据的转换,也存在于系统交互如序列化、反序列化。Java对象拷贝分为深拷贝和浅拷贝,目前常用的...

如何将 Qt 3D 渲染与 Qt Quick 2D 元素结合创建太阳系行星元素?

Qt组件推荐:QtitanRibbon:遵循MicrosoftRibbonUIParadigmforQt技术的RibbonUI组件,致力于为Windows、Linux和MacOSX提...

取消回复欢迎 发表评论: