a.基础的底层串口类:SimpleSerialPort
…
下面看的读写功能:异步读写
…
同步读写:
1.需要用到锁机制QMutexLocker
QMutexLocker创建时将传入的QMutex锁定,释放时将传入的QMutex解锁
2.用到时间循环类QEventLoop
exec()启动该事件的本地循环,一直循环
quit()终止该事件的循环
#include <QMetaEnum>
#include <QMutex>
#include <QThread>
#include <QEventLoop>
//同步写入数据,整个处理过程顺序执行,当各个过程都执行完毕,并返回结果,是所有的操作都做完,才返回给用户结果
QByteArray writeAndRead(QString data, bool* ok = nullptr, QByteArray endflag = "");
QByteArray writeAndRead(QByteArray data, bool* ok = nullptr, QByteArray endflag = "");
QByteArray SimpleSerialPort::writeAndRead(QString data, bool* ok, QByteArray endflag)
{
return writeAndRead(data.toLatin1(), ok, endflag);
}
QByteArray SimpleSerialPort::writeAndRead(QByteArray data, bool* ok, QByteArray endflag)
{
if (!isConnected()) {
if (ok) {
*ok = false;
}
return QByteArray();
}
//加锁
//QMutexLocker创建时将传入的QMutex锁定,释放时将传入的QMutex解锁
QMutexLocker locker(&m_lock);
disconnect(m_com, SIGNAL(readyRead()), this, SLOT(readyData()));
//这里还可加时钟,控制超时时间(文章下面有介绍)
QEventLoop loop; //Qt中的事件循环类
connect(m_com, SIGNAL(readyRead()), &loop, SLOT(quit())); // 有数据待读 quit()终止该事件的本地循环
int writeSize = m_com->write(data);
m_com->flush();
if (writeSize != data.size()) {
//记录错误
qDebug()<<QSC("写数据错误");
//return _changeStateAndRecoverConnect(ok);
}
loop.exec(); //exec()启动该事件的本地循环,一直循环
QByteArray recvData = m_com->readAll();
if (!recvData.endsWith(endflag)) {
int retry = 3;
while (retry--)
{
QThread::msleep(100);
recvData += m_com->readAll();
if (recvData.endsWith(endflag)) {
break;
}
}
if (!recvData.endsWith(endflag)) {
//记录错误
qDebug()<<QSC("读数据错误");
//return _changeStateAndRecoverConnect(ok);
}
}
return recvData;
//return _changeStateAndRecoverConnect(ok, true, recvData);
}
如果需要记录是否有错误的话,可以使用函数_changeStateAndRecoverConnect,同时返回数据和是否有错误的信息
(把上面代码注释掉的_changeStateAndRecoverConnect部分取消注释即可)
private:
QByteArray _changeStateAndRecoverConnect(bool* ok, bool state = false, QByteArray data = QByteArray());
QByteArray SimpleSerialPort::_changeStateAndRecoverConnect(bool* ok, bool state, QByteArray data)
{
if (ok) {
*ok = state;
}
connect(m_com, SIGNAL(readyRead()), this, SLOT(readyData()), Qt::QueuedConnection);
return data;
}
还可以加一些检测是否超时的错误在程序中,再把writeAndRead函数进行修改
//.h文件
#include <QTimer>
#include <QDateTime>
//新增接口设置超时时间
void setTimeout(int ms);
private slots:
// 同步读写超时
void timerTimeout();
...
int m_timeout = 3000;
bool m_timerTimeout = false;
//.cpp
QByteArray SimpleSerialPort::writeAndRead(QByteArray data, bool* ok, QByteArray endflag)
{
...
// 加锁
QMutexLocker locker(&m_lock);
disconnect(m_com, SIGNAL(readyRead()), this, SLOT(readyData()));
m_timerTimeout = false;
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
connect(m_com, SIGNAL(readyRead()), &loop, SLOT(quit())); // 有数据待读
connect(&timer, SIGNAL(timeout()), this, SLOT(timerTimeout()));
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); // 读数据超时
QString sendTime = QDateTime::currentDateTime().toString("yyyy MM dd hh:mm:ss.zzz");
int writeSize = m_com->write(data);
m_com->flush();
if (writeSize != data.size()) {
return _changeStateAndRecoverConnect(ok);
}
timer.start(m_timeout);
loop.exec();
QString recvTime = QDateTime::currentDateTime().toString("yyyy MM dd hh:mm:ss.zzz");
if (m_timerTimeout) {
return _changeStateAndRecoverConnect(ok);
}
QByteArray recvData = m_com->readAll();
...
}
void SimpleSerialPort::timerTimeout()
{
m_timerTimeout = true;
}
void SimpleSerialPort::setTimeout(int ms)
{
m_timeout = ms;
}
b.供调用的串口接口类:SerialTest
先导入头文件SimpleSerialPort,在构造函数中实例化来导入的类SimpleSerialPort。
//.h
public:
SerialTest(QObject* parent = nullptr);
~SerialTest();
private:
SimpleSerialPort* m_serial;
//.cpp
SerialTest::SerialTest(QObject* parent)
:QObject(parent)
{
m_serial = new SimpleSerialPort();
}
定义可直接使用的功能接口:
1.查找端口:使用m_serial->availableSerialPorts();
Q_INVOKABLE QString Haat_FindPorts();
QString HaatSerial::Haat_FindPorts()
{
QStringList portsList = m_serial->availableSerialPorts();
QString ports = "";
for (QString portTemp : portsList)
ports = ports + "," + portTemp;
return ports;
}
2.连接端口:使用m_serial->openSerialPort(port, m_serialSetting); 在程序中组装成SimpleSerialPort中存储端口的结构体SerialPortSettings
Q_INVOKABLE bool Haat_ConnectSerial(QString port, int baudRate = 9600, int dataBits = 8, QString parity = "None", int stopBits = 1, QString flowControl = "None");
bool SerialTest::Haat_ConnectSerial(QString port, int baudRate, int dataBits, QString parity, int stopBits, QString flowControl)
{
SerialPortSettings m_serialSetting;
switch (baudRate)
{
case 9600:
m_serialSetting.baudRate = QSerialPort::Baud9600;
break;
case 19200:
m_serialSetting.baudRate = QSerialPort::Baud19200;
break;
case 38400:
m_serialSetting.baudRate = QSerialPort::Baud38400;
break;
case 57600:
m_serialSetting.baudRate = QSerialPort::Baud57600;
break;
case 115200:
m_serialSetting.baudRate = QSerialPort::Baud115200;
break;
default:
m_serialSetting.baudRate = QSerialPort::Baud115200;
break;
}
m_serialSetting.dataBits = QSerialPort::Data8;
m_serialSetting.parity = QSerialPort::NoParity;
m_serialSetting.stopBits = QSerialPort::OneStop;
m_serialSetting.flowControl = QSerialPort::NoFlowControl;
bool isOpen = m_serial->openSerialPort(port, m_serialSetting);
return isOpen;
}
3.断开端口
Q_INVOKABLE void Haat_DisconnectSerial();
void SerialTest::Haat_DisconnectSerial()
{
m_serial->closeSerialPort();
}
4.收发数据
Q_INVOKABLE QString Haat_SendData(QString data, bool isHex = true);
QString SerialTest::Haat_SendData(QString data, bool isHex)
{
QByteArray receiveData;
QString result;
//暂时只用QString 类型的,isHex暂时不做转换
receiveData = m_serial->writeAndRead(data.toLatin1());
result = QString::fromLatin1(receiveData);
return result;
}
3.写ui文件测试接口
a.ui界面设置
用到的的控件有:按钮:PushButton 下拉框:ComboBox 单选按钮:RadioButton 输入框:LineEdit 文本显示框:TextEdit
可以用GroupBox先画出基本的位置布局,选好之后如果需要基于最底层进行填充,则点到最底层的空白部分右键,选择布局-》水平或者垂直布局则可以从图一变成图二:
图一:
图二:
直接在ui中进行修改,基本界面为:
b.修改UI设计师类的.h和.cpp,调用功能接口进行测试
(见下一篇)