快捷搜索:  汽车  科技

qt读取串口数据(同一个类不同代码)

qt读取串口数据(同一个类不同代码)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; } QBy

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

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

qt读取串口数据(同一个类不同代码)(1)

串口参数处理

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 <QMutex> #include <QWaitCondition> #include <QQueue> template<class T> 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<T> mQueue; QWaitCondition mNotEmpty; QWaitCondition mNotFull; QMutex mMutex; //mutable QReadWriteLock RWlock; //优点可以多线程同时读,比QMutex更高效 }; #endif // CONCURRENTQUEUE_H

使用时如下定义即可

ConcurrentQueue<QByteArray> 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处理文章。

猜您喜欢: