上一篇:QT5串口上位机——从零开始教你写01
简单串口编写
2.QSerialPort模块配置
QT的QtSerialPort模块
Qt中提供了两个C++类,分别是QSerialPort 和QSerialPortInfo。
它们功能如下:
QSerialPort :提供了操作串口的各种接口。
QSerialPortInfo :可以提供计算机中可用串口的各种信息。
QtSerialPort模块使用方法
首先,需要在pro文件中增加如下内容:
QT += serialport
然后执行qmake,如果未执行 后面添加头文件时会报错。
给项目添加新的**C++**类,
选择C++ Class
取名Serial,点击下一步即可生成对应的文件
在生成的serial.h中进行如下操作
#ifndef SERIAL_H
#define SERIAL_H
#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
class Serial : public QObject
{
Q_OBJECT
public:
explicit Serial(QObject *parent = nullptr);
~Serial(void);
void SerialOpen();
void SerialClose();
private:
QSerialPort* MySerial;
signals:
void SetInfo(QString info);
void isnoSerialOpen();
};
#endif
分别将光标置于函数后面 按下快捷键 alt + enter
~Serial(void); //添加析构函数
void SerialOpen(); //添加打开串口函数
void SerialClose(); //添加关闭串口函数
出现以下图片时 回车 即可在 cpp 文件中定义函数。
动图展示。
~Serial(void); 用来delete 之后程序中 new出来的变量
void SerialOpen();和 void SerialClose();则是用来进行打开串口的操作。
首先在 cpp 文件中 对MySerial进行实例化。
然后,鼠标放在MySerial上按下快捷键F1,打开QSerialPort的帮助文档。找到Public Functions
打开Detailed Description
从帮助文档中可以看出来,我们需要对串口进行的一些配置。
配置串口参数
操作步骤如下:
1.首先需要设置要打开的串口名,这里可以通过**setPortName()或者setPort()**进行配置
2.然后通过使用open()函数以 read-only (r/o), write-only (w/o), or read-write (r/w) 模式之一打开串口
3.然后,检测串口是否被打开 (且没有其他的进程或者线程打开串口,如果有就关闭串口在重新打开)
4.最后,配置串口参数如配置串口名,波特率,数据位,校验位,停止位和流控位
配置函数如下:
void setPortName(const QString &name)
bool setBaudRate(qint32 baudRate, QSerialPort::Directions directions = AllDirections)
bool setDataBits(QSerialPort::DataBits dataBits)
bool setParity(QSerialPort::Parity parity)
bool setStopBits(QSerialPort::StopBits stopBits)
bool setFlowControl(QSerialPort::FlowControl flowControl)
首先,我们需要QString和qint32这两个类型的name参数和baudRate参数,这是通过ui界面的Qcombobox选项得到的。因为使用多线程的原因,不能直接调用,所以,这里通过构建结构体,通过传递结构体来传递参数。
鼠标右键点击工程,添加一个新的cpp头文件
设置头文件名称为 SerialInfo.h ,点击下一步,完成。
添加 SerialInfo.h的内容如下。
#ifndef SERIALINFO_H
#define SERIALINFO_H
#include <QVector>
#include <QMetaType>
typedef struct SerialInfos
{
QString comName;
qint32 baudRate;
qint32 dataBits;
qint32 parity;
qint32 stopBits;
qint32 flowControl;
qint32 Encode;
}Sinfo;
Q_DECLARE_METATYPE(Sinfo);
#endif
在serial.h中添加头文件
#include "SerialInfo.h"
并添加私有成员
private:
QSerialPort* MySerial;
Sinfo *info=nullptr;
QString InfoSet;
修改后的serial.h内容如下
#ifndef SERIAL_H
#define SERIAL_H
#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
#include "SerialInfo.h"
class Serial : public QObject
{
Q_OBJECT
public:
explicit Serial(QObject *parent = nullptr);
~Serial(void);
void SerialOpen();
void SerialClose();
private:
QSerialPort* MySerial;
Sinfo *info=nullptr;
QString InfoSet;
signals:
void SetInfo(QString info);
void isnoSerialOpen();
};
#endif
接下来在SerialOpen中操作。
首先判断串口是否打开,如果已经打开就关闭。这里调用SerialClose()(具体内容见下面SerialClose部分)
this->SerialClose();
然后设置串口名
MySerial->setPortName(QString(info->comName));
然后设置串口打开模式,R/W模式,如果设置失败发送错误信息,然后返回。
if(!MySerial->open(QIODevice::ReadWrite))
{
emit isnoSerialOpen();
return;
}
其中isnoSerialOpen()为设置的发送打开失败信号。
然后设置波特率,波特率通过info->baudRate设置
MySerial->setBaudRate(qint32(info->baudRate));
设置数据位,这里通过switch函数设置,其中setDataBits();中的参数通过使用F1查看,具体操作步骤如下。
使用同样的方法设置检验位、停止位和流控位。
在**SerialOpen()**中实现上述操作步骤,具体代码如下:
void Serial::SerialOpen()
{
this->SerialClose();
MySerial->setPortName(QString(info->comName));
InfoSet=QString::fromLocal8Bit("串口:");
InfoSet.append(QString(info->comName));
if(!MySerial->open(QIODevice::ReadWrite))
{
emit isnoSerialOpen();
return;
}
bool Bflag = MySerial->setBaudRate(qint32(info->baudRate));
if(Bflag){
InfoSet.append(QString::fromLocal8Bit(" 波特率:"));
QString baudRateinfo = QString::number(int(info->baudRate),10);
InfoSet.append(baudRateinfo);
}
else{
InfoSet.QString::fromLocal8Bit("波特率:Unknown");
};
switch (info->dataBits) {
case 0:
MySerial->setDataBits(QSerialPort::Data5);
InfoSet.append(QString::fromLocal8Bit(" 数据位:5"));
break;
case 1:
MySerial->setDataBits(QSerialPort::Data6);
InfoSet.append(QString::fromLocal8Bit(" 数据位:6"));
break;
case 2:
MySerial->setDataBits(QSerialPort::Data7);
InfoSet.append(QString::fromLocal8Bit(" 数据位:7"));
break;
case 3:
MySerial->setDataBits(QSerialPort::Data8);
InfoSet.append(QString::fromLocal8Bit(" 数据位:8"));
break;
default:
MySerial->setDataBits(QSerialPort::UnknownDataBits);
InfoSet.append(QString::fromLocal8Bit(" 数据位:Unknown"));
break;
}
switch (info->parity) {
case 0:
MySerial->setParity(QSerialPort::EvenParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Even"));
break;
case 1:
MySerial->setParity(QSerialPort::MarkParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Mark"));
break;
case 2:
MySerial->setParity(QSerialPort::NoParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:None"));
break;
case 3:
MySerial->setParity(QSerialPort::OddParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Odd"));
break;
case 4:
MySerial->setParity(QSerialPort::SpaceParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Space"));
break;
default:
MySerial->setParity(QSerialPort::UnknownParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Unknown"));
break;
}
switch (info->stopBits) {
case 0:
MySerial->setStopBits(QSerialPort::OneStop);
InfoSet.append(QString::fromLocal8Bit(" 停止位:1"));
break;
case 1:
MySerial->setStopBits(QSerialPort::OneAndHalfStop);
InfoSet.append(QString::fromLocal8Bit(" 停止位:1.5"));
break;
case 2:
MySerial->setStopBits(QSerialPort::TwoStop);
InfoSet.append(QString::fromLocal8Bit(" 停止位:2"));
break;
default:
MySerial->setStopBits(QSerialPort::UnknownStopBits);
InfoSet.append(QString::fromLocal8Bit(" 停止位:Unknown"));
break;
}
switch (info->flowControl) {
case 0:
MySerial->setFlowControl(QSerialPort::NoFlowControl);
InfoSet.append(QString::fromLocal8Bit(" 流控位:None"));
break;
case 1:
MySerial->setFlowControl(QSerialPort::HardwareControl);
InfoSet.append(QString::fromLocal8Bit(" 流控位:Hardware"));
break;
case 2:
MySerial->setFlowControl(QSerialPort::SoftwareControl);
InfoSet.append(QString::fromLocal8Bit(" 流控位:Software"));
break;
default:
MySerial->setFlowControl(QSerialPort::UnknownFlowControl);
InfoSet.append(QString::fromLocal8Bit(" 流控位:Unknown"));
break;
}
emit SetInfo(InfoSet);
}
添加**SerialClose()**函数。具体内容如下:
void Serial::SerialClose()
{
if(MySerial->isOpen())
{
MySerial->clear();
MySerial->close();
}
}
添加接收串口配置参数函数。这个函数主要用从接收从ui界面处选择的串口参数,并保存在info中,前面提到了是通过结构体来传递参数的,因此构造函数时,要添加结构体的形参。
在serial.h中添加公共函数*void RecvSerialConfig(Sinfo data); 。
public:
void RecvSerialConfig(Sinfo *data);
按下alt+enter,在serial.cpp中添加定义。
void Serial::RecvSerialConfig(Sinfo *data)
{
if(info!=nullptr)
{
delete info;
}
this->info = new Sinfo;
this->info->Encode=data->Encode;
this->info->baudRate=data->baudRate;
this->info->comName=data->comName;
this->info->dataBits=data->dataBits;
this->info->flowControl=data->flowControl;
this->info->parity=data->parity;
this->info->stopBits=data->stopBits;
}
这里要注意,申请内存空间时,结束后必须释放,不然容易导致内存泄漏。因此,需要在**~Serial(void);中添加delete info;**
**~Serial(void);**函数如下:
Serial::~Serial()
{
delete info;
}
配置发送和接收函数
配置完打开和关闭函数后,这里要配置发送和接收函数
在serial.h中添加公共函数void SendData(QByteArray data, bool hexflag); 和void RecvData();。添加信号void isRecvData(QByteArray);
public:
void SendData(QByteArray data, bool hexflag);
void RecvData();
signals:
void isRecvData(QByteArray);
按下alt+enter,在serial.cpp中添加定义。
其中void SendData(QByteArray data, bool hexflag); 函数中,data为ui界面传递的数据,hexflag则为是否通过hex模式发送。
isRecvData(QByteArray);则是向ui传递串口接收的数据。
具体函数内容为:
void Serial::SendData(QByteArray data, bool hexflag)
{
if(data.isEmpty())
{
return;
}
if(hexflag==true)
{
MySerial->write(data);
}
else{
data=SetCodeType(data,info->Encode);
MySerial->write(data);
}
}
void Serial::RecvData()
{
QByteArray info = MySerial->readAll();
if(info.isEmpty())
{
return ;
}
emit isRecvData(info);
}
这里**SetCodeType();**函数为自己定义的设置数据编码格式函数。具体实现方式见 配置编码格式函数 。
配置编码格式函数
给项目添加新的C++类,右击工程,选择ADD NEW …。
选择C++ Class
按照如下图选择,点击下一步,完成。
修改codetype.h内容如下:
#ifndef CODETYPE_H
#define CODETYPE_H
#include <QString>
#include <QTextCodec>
typedef enum
{
ASCII = 0,
Utf8,
Utf16,
GBK,
Big5,
ShiftJIS
}CodeType;
QByteArray SetCodeType(QByteArray const &data,qint32 control);
QByteArray GetCodeType(QByteArray const &data, qint32 control);
#endif
修改codetype.cpp内容如下:
#include "codetype.h"
QByteArray SetCodeType(const QByteArray &data, qint32 control)
{
QByteArray tmpData;
switch (control) {
case ASCII: tmpData=QTextCodec::codecForName("latin1")->fromUnicode(data);break;
case Utf8: tmpData= QTextCodec::codecForName("UTF-8")->fromUnicode(data);break;
case Utf16: tmpData= QTextCodec::codecForName("UTF-16")->fromUnicode(data);break;
case GBK: tmpData= QTextCodec::codecForName("GBK")->fromUnicode(data);break;
case Big5: tmpData= QTextCodec::codecForName("Big5")->fromUnicode(data);break;
case ShiftJIS: tmpData= QTextCodec::codecForName("Shift-JIS")->fromUnicode(data);break;
default:;break;
}
return tmpData;
}
QByteArray GetCodeType(const QByteArray &data, qint32 control)
{
QString tmpData;
switch (control) {
case ASCII: tmpData= QTextCodec::codecForName("latin1")->toUnicode(data);break;
case Utf8: tmpData= QTextCodec::codecForName("UTF-8")->toUnicode(data);break;
case Utf16: tmpData= QTextCodec::codecForName("UTF-16")->toUnicode(data);break;
case GBK: tmpData= QTextCodec::codecForName("GBK")->toUnicode(data);break;
case Big5: tmpData= QTextCodec::codecForName("Big5")->toUnicode(data);break;
case ShiftJIS: tmpData= QTextCodec::codecForName("Shift-JIS")->toUnicode(data);break;
default:;break;
}
return tmpData.toUtf8();
}
这里主要用到了QTextCodec这个类,具体内容可以看Qt的帮助文档,这里只简要概括。
1.需要包含**#include **这个头文件。
2.QTextCodec 类主要是将数据用来在非 Unicode 格式和 Unicode 之间进行转换。
至此,QtSerialPort配置完成。
公众号
|