网站首页 > 技术文章 正文
串口通讯在各种外设通讯中是常见接口,因为各种嵌入式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 <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处理文章。
- 上一篇: QT串口 QSerialPort类的使用
- 下一篇: Qt编写控件属性设计器8-网络采集
猜你喜欢
- 2024-09-22 Qt 串口通信 QSerialPort
- 2024-09-22 Qt编写控件属性设计器8-网络采集
- 2024-09-22 QT串口 QSerialPort类的使用
- 2024-09-22 Qt 端口的使用
- 2024-09-22 双手撸码20+天,串口软件(visual-serial)终于有了基本雏形
- 2024-09-22 Qt5.8中串口类QSerialPort
- 2024-09-22 Qt中如何使用QSerialPort进行串口通信
- 2024-09-22 Qt 串口通信接收数据不完整,怎么解决?
- 2024-09-22 Qt编写控件属性设计器7-串口采集
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)