网站首页 > 文章精选 正文
串口通讯在各种外设通讯中是常见接口,因为各种嵌入式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桌面客户端在Linux下的开发流程
- 下一篇: 万能药:胺碘酮使用注意事项(胺碘酮用法及用量)
猜你喜欢
- 2025-06-08 Qt MQTT之服务器介绍(mqtt服务器功能)
- 2025-06-08 在 STM32 开发里,RTOS 真比裸机编程更有优势?别盲目迷信!
- 2025-06-08 C++大型流媒体项目-从底层到应用层千万级直播系统实战
- 2025-06-08 万能药:胺碘酮使用注意事项(胺碘酮用法及用量)
- 2025-06-08 室性心律失常首选药物大汇总(室性心律失常首选下列哪种药物)
- 2025-06-08 QT桌面客户端在Linux下的开发流程
- 2025-06-08 座舱SOC的Linux内核性能调优(座舱soc起不来)
- 2025-06-08 为什么要有协程?(为什么要有协议)
- 2025-06-08 干货收藏!10分钟帮你搞定3种心电图!
- 2025-06-08 Qt推流(视频文件/视频流/摄像头/桌面转流媒体rtmp+hls+webrtc)
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)
- mysql数据库面试题 (57)