IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> QT5串口上位机——从零开始教你写02 -> 正文阅读

[嵌入式]QT5串口上位机——从零开始教你写02

上一篇:QT5串口上位机——从零开始教你写01

简单串口编写

2.QSerialPort模块配置

QT的QtSerialPort模块

Qt中提供了两个C++类,分别是QSerialPortQSerialPortInfo

它们功能如下:

QSerialPort :提供了操作串口的各种接口。

QSerialPortInfo :可以提供计算机中可用串口的各种信息。

QtSerialPort模块使用方法

首先,需要在pro文件中增加如下内容:

QT += serialport    

然后执行qmake,如果未执行 后面添加头文件时会报错。

image-20211005013634387

给项目添加新的**C++**类,

image-20211027145302605

选择C++ Class

取名Serial,点击下一步即可生成对应的文件image-20211005014205307

在生成的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 // SERIAL_H


分别将光标置于函数后面 按下快捷键 alt + enter

    ~Serial(void);			//添加析构函数
    void SerialOpen();		//添加打开串口函数
    void SerialClose();		//添加关闭串口函数

出现以下图片时 回车 即可在 cpp 文件中定义函数。
image-20211024112606270

动图展示。

~Serial(void); 用来delete 之后程序中 new出来的变量

void SerialOpen();和 void SerialClose();则是用来进行打开串口的操作。

首先在 cpp 文件中 对MySerial进行实例化。

image-20211027151715679

然后,鼠标放在MySerial上按下快捷键F1,打开QSerialPort的帮助文档。找到Public Functions

image-20211027152623327

打开Detailed Description
image-20211027153753566

从帮助文档中可以看出来,我们需要对串口进行的一些配置。

配置串口参数

操作步骤如下:

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)

首先,我们需要QStringqint32这两个类型的name参数和baudRate参数,这是通过ui界面的Qcombobox选项得到的。因为使用多线程的原因,不能直接调用,所以,这里通过构建结构体,通过传递结构体来传递参数。

鼠标右键点击工程,添加一个新的cpp头文件

image-20211027193012335

设置头文件名称为 SerialInfo.h ,点击下一步,完成。

image-20211027193154402

添加 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声明后,就可以让自定义的类型设置到QVariant。
Q_DECLARE_METATYPE(Sinfo);

#endif // SERIALINFO_H

serial.h中添加头文件

#include "SerialInfo.h"

image-20211027194025777

并添加私有成员

private:
    QSerialPort* MySerial;
    Sinfo *info=nullptr;    //串口配置
	QString InfoSet;        //存储串口配置

image-20211027194229817

修改后的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 // SERIAL_H

接下来在SerialOpen中操作。

首先判断串口是否打开,如果已经打开就关闭。这里调用SerialClose()(具体内容见下面SerialClose部分)

this->SerialClose();

然后设置串口名

MySerial->setPortName(QString(info->comName));

然后设置串口打开模式,R/W模式,如果设置失败发送错误信息,然后返回。

if(!MySerial->open(QIODevice::ReadWrite))//用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存储串口设置信息,发送给mainWidget
    InfoSet.append(QString(info->comName));
    if(!MySerial->open(QIODevice::ReadWrite))//用ReadWrite 的模式尝试打开串口
    {
        emit isnoSerialOpen();  //发送打开失败的标志
        return;
    }
    //设置波特率
    bool Bflag = MySerial->setBaudRate(qint32(info->baudRate));
        if(Bflag){
            InfoSet.append(QString::fromLocal8Bit(" 波特率:"));
            //第一个参数为int变量,第二个参数10表示转换为10进制数
            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;//防止内存泄漏,关闭时 delete info;
    //接收参数设置
    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); 函数中,dataui界面传递的数据,hexflag则为是否通过hex模式发送。

isRecvData(QByteArray);则是向ui传递串口接收的数据。

具体函数内容为:

void Serial::SendData(QByteArray data, bool hexflag)
{
    if(data.isEmpty())
    {
        return;//没有读取到数据就退出
    }
    if(hexflag==true)
    {   //hex模式直接发送
        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,     //Utf8编码格式
    Utf16,    //Utf16编码格式
    GBK,  //GBK编码格式、兼容GBK18030、GB2312
    Big5,     //Big5
    ShiftJIS
}CodeType;
//设置编码格式
QByteArray SetCodeType(QByteArray const &data,qint32 control);
//解析编码格式
QByteArray GetCodeType(QByteArray const &data, qint32 control);
#endif // CODETYPE_H

修改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(); //设置成Unicode格式
}

这里主要用到了QTextCodec这个类,具体内容可以看Qt的帮助文档,这里只简要概括。

1.需要包含**#include **这个头文件。

2.QTextCodec 类主要是将数据用来在非 Unicode 格式和 Unicode 之间进行转换。

至此,QtSerialPort配置完成。

公众号
在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-29 13:13:03  更:2021-10-29 13:15:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:22:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码