QT c++串口通讯-学习qt最符合的第一个小步伐

打印 上一主题 下一主题

主题 510|帖子 510|积分 1530

目录
提示:编程环境与辅助步伐
一、项目新建,步伐完善,代码辨认
1.1项目新建
1.2、完善步伐,步伐属性设置
1.3环境辨认代码,进入舒畅的编程中。
1.4添加串口界面(现实就是添加一个qt类)        
二、串口通讯实现
2.1界面控件添加
2.2点击按钮,唤出窗口
2.3 串口通讯界面,串口毗连实现
2.3.1串口添加
2.3.2 其他串口信息添加
2.4主界面信息接收,命令下发。
        c++,与Qt学习了一段时间,感觉编写串口通讯软件是比力查验自己是否初步入门qt的一个标志。c++与qt编程学习过程中,感觉知识点繁芜繁琐,很难抓住核心。最开始学习《C++Prier Puls》,重新看起,真的是痛苦。之后在工作中,在现实编程过程中,感觉照旧在现实编程中学习是提拔比力快的方法。在打仗的各种编程实践中,我感觉串口通讯步伐就是一个非常好的实践例子。
        首先串口通讯步伐的编写有助于初学者明白C++面临对象编程的一个核心:类,以及qt的一个核心信号与槽。
1、什么是C+的类,类的成员变量、成员函数。private 、public、protected函数之间区别。
2、qt的核心,信号与槽。信号与槽的毗连机制,什么时候毗连,什么时候断开。
        而后,在编程过程中需要把握两个十分重要类,用于数据存储,数据啊之间转换的两个重要类QString和QByteArray类的根本使用。学习qt前期,需要了解大量的专有类。qt为很多问题提供了专门的类,例如串口通讯的QSerialPort类,类有封装好的各种函数、信号等能够解决你遇到的9成问题。
提示:编程环境与辅助步伐

 qt与c++学习,编写串口通讯步伐,编程环境与辅助步伐
1、我的用的版本是VS2015,qt5.9.6。
2、编写过程中使用串口通讯助手,来收发串口消息,虚拟串口驱动来创建虚拟串口。
        环境配置,网上有很多教程,串口助手、虚拟串口驱动步伐网上也可以找到,后续也会出教程。
一、项目新建,步伐完善,代码辨认

        第一步就是新建项目,在使用VS2015+QT组合的时候,由于涉及到串口通讯,在新建项目的时候,需要包罗QSerialPort类,在项目新建的时候需要注意。
1.1项目新建

        在VS2015中新建QT窗口项目,这里我定名的是SerialPort,点击确认,后弹出一个qt提示串口,点击Next。

      模式选择DebugSerial Port模块包罗。
         这个界面上中,选择Release或者Debug都可以,如果考虑到之后步伐打包,Release模式步伐体积会小很多。但是我这里遇到一个bug,我选择Release模式,点击QtModules,勾选SerialPort之后,回来又酿成Debug模式了。很奇怪,不过没关系,这之后还能修改。

        点击确认之后,来到这个界面,这里可以选择步伐基于什么类:Base Class,我这里选择的是QWidget。这个步伐编辑完成之后,可很容易嵌入其他步伐中中,如果选择QMianWidow,就无法嵌入另一个基于QMainWindow创建的步伐,一个步伐只能有一个mainwindow。如果只是单独一个步伐话,两者都可以。

        点击Finish,就完成了一个一个项目的创建,如下图所示,双击ui文件,得到一个空缺的ui界面,这时候步伐运行,得到的也是一个空缺界面。
        接下来就是项目的属性设置,

1.2、完善步伐,步伐属性设置

        你可能注意到上述界面中,在没有运行步伐之前,步伐代码中又红色波浪线,这说明VS没有辨认出这代码。还有几个点需要注意。
1、管理器改为Release模式

2、项目属性设置,在左侧项目名称SerialPort右键单击,出现属性,点击进入属性界面。可以看到这里的配置是Debug模式。

        在属性页中切换配置为release,查察两个模式下 Qt Project Settings的Qt Modules,发现release模式下缺少serialport模块,这里需要手动添加,然后生存。到这里步伐的根本设置就完成了,下一步:解决代码红色波浪线问题。

1.3环境辨认代码,进入舒畅的编程中。

        照旧项目右键-天生步伐,天生乐成之后——重新扫描步伐,这时候就发当代码中错误提示的红线消散,这时候输入代码,也有了提示。

1.4添加串口界面(现实就是添加一个qt类)        

        这里有一个问题,现实的步伐中,我们希望界面轻便、有序。特别是一些控制硬件的上位机,需要控制的参数多且杂。所以我们将串口毗连放到另一个界面中,串口毗连与步伐数据表现分开。现实就是将串口毗连类与主界面类分开,两者之间用信号与槽毗连,实现相互通讯。这就需要在项目再添加一个qt类,开始有点儿难了,但是相信我,并不比之前有多少难度,开始!

        项目-右键,1添加-Add Qt Class-选择Qt Widgets Class,然后给类定名,点击Add
                接下来就是qt的类创建指引,这里基类照旧选择QWidget。创建完成之后就会得到如许一个类。到这里我们还没有写一行代码,都是根本的操纵。

二、串口通讯实现

2.1界面控件添加

        现在固然两个界面都添加了,但是他们之间并没有相互训练,步伐运行之后只能看到第一个界面,无法看到第二个界面。这就需要在一个界面叫醒机制。
目标:主界面增加一个按钮,点击按钮,第二个界面表现。
        在ui界面左侧通过拖动相应控件到界面上就可以,下图我添加了一些控件用于表现数据。
         串口通讯过程中需要知道串口名称、波特率、数据位、停止位、校验位,向ui界面中拖入PushButton,ComBox,和Label控件,并为其定名。如下图所示。
我们的逻辑是,软件运行,打开界面,点击革新后,下拉框出现串口,选择串口后,选择波特率、数据位、停止位、校验位,确定后,点击毗连,实现串口毗连。接下来就是在代码中实现我们预设的逻辑。

2.2点击按钮,唤出窗口

        QT中不同类之间的数据通报通过信号与槽毗连。一个类发送信号,另一个类的槽函数接收信号。qt的类中很多自带有信号Signal,这个可以在qt的帮助文件中搜索相应的类来查察。这里QPushButton其中的一个信号就是clicked()。
        在qt中通常的做法是使用connnect来毗连信号与槽,但是也有一个方便的机制,就是按名称毗连。比如我们希望点击界面的革新按钮后,能够转到一个槽函数来执行串口的革新操纵。
就可以如许做:在SerialPort类中,h头文件中:
1、首先包罗PortWidgets.h;
2、私有成员中增加一个PortWidgets私有成员。
3、添加private slots:然后添加私有槽函数: void on_pushButton_connect_clicked();这个函数是如许定名的:on_控件名_信号名();如许qt就能自动将QPushButton的clicked()信号与该函数毗连。

接下来在cpp文件中实现该函数。点击按钮,串口界面出现。
首先在构造函数中,将串口类指针初始化。接下俩在槽函数中,第一步是判断串口指针是否为空,为空就新建类,然后表现。这里就涉及到类构造,及公共函数public的使用。portwidgets是类的一个实例,它在类外,只能够使用公共函数,public函数,比如这个showNormal();
  1. SerialPort::SerialPort(QWidget *parent)
  2.     : QWidget(parent)
  3. {
  4.     ui.setupUi(this);
  5.         portwidgets = NULL;
  6. }
  7. //按钮点击
  8. void SerialPort::on_pushButton_connect_clicked()
  9. {
  10.         if (portwidgets == NULL)
  11.         {
  12.                 portwidgets = new PortWidgets();
  13.         }
  14.         portwidgets->showNormal();//界面显示
  15. }
复制代码
统统正常后,运行步伐,点击按钮,第二个界面就会出现。

2.3 串口通讯界面,串口毗连实现

        现在实现界面串口界面打开,接下来就是实现串口刷星,波特率、数据位、停止位、校验位,及串口毗连。
2.3.1串口添加

第一步:扫描电脑所有串口。需要用到虚拟串口模拟器:搜索Configure Virtual Serial Port Driver,安装就可以。然后添加一对相互毗连的串口。com1--com2.接下来就是:实现点击按钮实现扫描所有串口功能。槽函数添加方式与之前一样,不再赘述。串口革新需要包罗两个头文件。
#include "qserialport.h"
#include "qserialportinfo.h"
        然后在现实编程过程中可以在槽函数中添加一个输出比如cout<<0<<endl,来查验信号与槽函数是否正常毗连。这段代码重要就是扫描电脑当前所有可用串口,获得名称,添加到响应的下拉框中,然后设置下拉框当前表现第一个串口。
  1. //刷新串口
  2. void PortWidgets::on_pushButton_refresh_clicked()
  3. {
  4.         QStringList portNameList;
  5.         QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts();
  6.         ui.comboBox_portlist->clear();
  7.         // 遍历所有可用的串行端口信息  
  8.         for (const QSerialPortInfo &info : serialPortInfos)
  9.         {
  10.                 portNameList.append(info.portName()); // 添加端口名到列表中  
  11.         }
  12.         // 将端口名列表添加到下拉框中  
  13.         ui.comboBox_portlist->addItems(portNameList);
  14.         // 默认情况下选择第一个端口
  15.         if (!portNameList.isEmpty())
  16.         {
  17.                 ui.comboBox_portlist->setCurrentIndex(0);
  18.         }
  19. }
复制代码
2.3.2 其他串口信息添加

        波特率、数据位、停止位、校验位,这里用了unordered_map文件中的键-值对应方法,如许在类实现中,就能够简化代码。头文件中类代码如下。
1、类中添加了void on_pushButton_portconnect_clicked();点击按钮,转到此函数中,实现串口毗连。
2、代码中,增加了void receive_data();槽函数,用于接收串口传输数据。串口类中有信号,readyread。串口收到信号就转到该槽函数处理。这个就需要手动毗连:
connect(myPort, &QSerialPort::readyRead, this, &ortWidgets::receive_data);
格式:connect(发送指针,&发送类::信号,接收指针,&接收类::接收槽函数)。槽函数实现中,
    QByteArray data = myPort->readAll();
    emit receivedata(data);
通过myPort读取到所有串口数据,然后有一个emit,是发送信号,这个信号由主界面的槽函数来接收。
  1. #pragma once
  2. #include <QWidget>
  3. #include "ui_PortWidgets.h"
  4. #include "qserialport.h"
  5. #include "qserialportinfo.h"
  6. #include <unordered_map> //非排序图
  7. #include <iostream>
  8. #include <qbytearray.h>
  9. #include <qthread.h>
  10. using namespace std;
  11. class PortWidgets : public QWidget
  12. {
  13.         Q_OBJECT
  14. public:
  15.         PortWidgets(QWidget *parent = Q_NULLPTR);
  16.         ~PortWidgets();
  17. signals:
  18.         void receivedata(QByteArray data);
  19.         private slots:
  20.         void on_pushButton_refresh_clicked();
  21.         void on_pushButton_portconnect_clicked();
  22.         void receive_data();
  23. public:
  24.         void write_data(QByteArray writedata);
  25. private:
  26.         Ui::PortWidgets ui;
  27.         QSerialPort *myPort;//串口指针
  28.         std::unordered_map<int, QSerialPort::BaudRate> baudRateMap =//波特率
  29.         {
  30.                 { 0, QSerialPort::Baud1200 },{ 1, QSerialPort::Baud2400 },{ 2, QSerialPort::Baud4800 },
  31.                 { 3, QSerialPort::Baud9600 },{ 4, QSerialPort::Baud19200 },{ 5, QSerialPort::Baud38400 },
  32.                 { 6, QSerialPort::Baud57600 },{ 7, QSerialPort::Baud115200 }
  33.         };
  34.         std::unordered_map<int, QSerialPort::DataBits>dataMap =//数据位
  35.         {
  36.                 { 0,QSerialPort::Data5 },{ 1,QSerialPort::Data6 },{ 2,QSerialPort::Data7 },{ 3,QSerialPort::Data8 }
  37.         };
  38.         std::unordered_map<int, QSerialPort::Parity>parityMap =//校验位
  39.         {
  40.                 { 0,QSerialPort::NoParity },{ 1,QSerialPort::OddParity },{ 2,QSerialPort::EvenParity }
  41.         };
  42.         std::unordered_map<int, QSerialPort::StopBits>stopMap =//停止位
  43.         {
  44.                 { 0,QSerialPort::OneStop },{ 1,QSerialPort::OneAndHalfStop },{ 2,QSerialPort::TwoStop }
  45.         };
  46. };
复制代码
cpp,类实现。
  1. #include "PortWidgets.h"PortWidgets::PortWidgets(QWidget *parent)        : QWidget(parent){        ui.setupUi(this);        QStringList baudratelist = { "1200","2400","4800","9600","19200","38400","57600","115200" };        ui.comboBox_BaudRate->addItems(baudratelist);        ui.comboBox_BaudRate->setCurrentIndex(3);        QStringList datalist = { QString::fromLocal8Bit("5位"),QString::fromLocal8Bit("6位"),                QString::fromLocal8Bit("7位"),QString::fromLocal8Bit("8位") };        ui.comboBox_Data->addItems(datalist);        ui.comboBox_Data->setCurrentIndex(3);        QStringList checklist = { QString::fromLocal8Bit("无校验"),QString::fromLocal8Bit("偶校验"),                QString::fromLocal8Bit("奇校验") };        ui.comboBox_Check->addItems(checklist);        ui.comboBox_Check->setCurrentIndex(0);        QStringList stoplist = { QString::fromLocal8Bit("1位"),QString::fromLocal8Bit("1.5位"),                QString::fromLocal8Bit("2位") };        ui.comboBox_Stop->addItems(stoplist);        ui.comboBox_Stop->setCurrentIndex(0);        on_pushButton_refresh_clicked();        myPort = NULL;        myPort = new QSerialPort(this);        connect(myPort, &QSerialPort::readyRead, this, &PortWidgets::receive_data);}PortWidgets::~PortWidgets(){}//刷新串口
  2. void PortWidgets::on_pushButton_refresh_clicked()
  3. {
  4.         QStringList portNameList;
  5.         QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts();
  6.         ui.comboBox_portlist->clear();
  7.         // 遍历所有可用的串行端口信息  
  8.         for (const QSerialPortInfo &info : serialPortInfos)
  9.         {
  10.                 portNameList.append(info.portName()); // 添加端口名到列表中  
  11.         }
  12.         // 将端口名列表添加到下拉框中  
  13.         ui.comboBox_portlist->addItems(portNameList);
  14.         // 默认情况下选择第一个端口
  15.         if (!portNameList.isEmpty())
  16.         {
  17.                 ui.comboBox_portlist->setCurrentIndex(0);
  18.         }
  19. }//串口毗连void PortWidgets::on_pushButton_portconnect_clicked(){        myPort->setPortName(ui.comboBox_portlist->currentText());        int baudindex = ui.comboBox_BaudRate->currentIndex();        myPort->setBaudRate(baudRateMap[baudindex]);        int dataindex = ui.comboBox_Data->currentIndex();        myPort->setDataBits(dataMap[dataindex]);        int stopindex = ui.comboBox_Stop->currentIndex();        myPort->setStopBits(stopMap[stopindex]);        int checkindex = ui.comboBox_Check->currentIndex();        myPort->setParity(parityMap[checkindex]);        myPort->setFlowControl(QSerialPort::NoFlowControl);                if (!myPort->isOpen())        {                myPort->open(QIODevice::ReadWrite);                if (myPort->isOpen())                        cout << "Connection success!" << endl;                else                        cout << "Connection failed!" << endl;        }}//下发命令void PortWidgets::write_data(QByteArray writedata){        myPort->write(writedata);}//接收来自串口的数据,发送到主串口void PortWidgets::receive_data(){        QByteArray data = myPort->readAll();        emit receivedata(data);}
复制代码
2.4主界面信息接收,命令下发。

        到这里,串口界面的根本功能已经具备了,也能够接收串口信息。下面就到了两个界面交互的部分。首先给出主界面类的定义及实现。
类定义:类中增加了几个私有槽函数;一个信号
1、void receive_data(QByteArray data);这是接收数据槽函数,用于响应串口类发送信号。
2、void on_lineEdit_data_returnPressed();这是响应界面中lineEdit按下enter后,进入该槽函数,在这里可以进行一些操纵。
3、void writedata(QByteArray data);该信号就是与串口类的下发命令毗连。
  1. #pragma once
  2. #include <QtWidgets/QWidget>
  3. #include "ui_SerialPort.h"
  4. #include "PortWidgets.h"
  5. class SerialPort : public QWidget
  6. {
  7.     Q_OBJECT
  8. public:
  9.     SerialPort(QWidget *parent = Q_NULLPTR);
  10.         private slots:
  11.         void on_pushButton_connect_clicked();
  12.         void on_lineEdit_data_returnPressed();
  13.         void receive_data(QByteArray data);//接收串口数据的槽
  14.         void on_pushButton_clear_clicked();//清除接收框的数据
  15. signals:
  16.         void writedata(QByteArray data);
  17. private:
  18.     Ui::SerialPortClass ui;
  19.         PortWidgets *portwidgets;
  20. };
复制代码
类实现:
1、可以看到,我们在串口毗连的时候,对两个类之间的信号与槽进行了毗连,这里需要注意的是,如果我们在中途想要断开毗连一段时间后,再次毗连串口。需要将信号与槽的毗连也断开,可以自己尝试一下,实现这个功能。
2、数据发送中,我们在void SerialPort:n_lineEdit_data_returnPressed()槽函数中,获取了当前编辑框的信息转为int,接着定义了一个QByteArray,通过如下的方式将该int转为16进制,
data.resize(1);
        data[0] = value & 0xff;
        emit writedata(data);
并发送信号,然后就转到串口类的槽函数中,完成命令下发。
void PotWidgets::write_data(QByteArray writedata)
{
    myPort->write(writedata);
}
  1. #include "SerialPort.h"
  2. SerialPort::SerialPort(QWidget *parent)
  3.     : QWidget(parent)
  4. {
  5.     ui.setupUi(this);
  6.         portwidgets = NULL;
  7. }
  8. //按钮点击
  9. void SerialPort::on_pushButton_connect_clicked()
  10. {
  11.         if (portwidgets == NULL)
  12.         {
  13.                 portwidgets = new PortWidgets();
  14.                 connect(portwidgets, &PortWidgets::receivedata, this, &SerialPort::receive_data);//接收来自串口界面的消息
  15.                 connect(this, &SerialPort::writedata, portwidgets, &PortWidgets::write_data);    //发送数据到串口界面
  16.         }
  17.         portwidgets->showNormal();//界面显示
  18. }
  19. //数据发送,编辑框回车确定
  20. void SerialPort::on_lineEdit_data_returnPressed()
  21. {
  22.         if (ui.lineEdit_data->text() != NULL)
  23.         {
  24.                 int value = ui.lineEdit_data->text().toInt();
  25.                 QByteArray data;
  26.                 data.resize(1);
  27.                 data[0] = value & 0xff;
  28.                 emit writedata(data);
  29.         }
  30. }
  31. //接收数据,显示
  32. void SerialPort::receive_data(QByteArray data)
  33. {
  34.         bool ok;
  35.         QString str = data.toHex(' ').toUpper();
  36.         ui.textEdit_receive->append(str);
  37. }
  38. //清除数据显示框
  39. void SerialPort::on_pushButton_clear_clicked()
  40. {
  41.         ui.textEdit_receive->clear();
  42. }
复制代码
背面的内容我没有写的很详细,但是源代码都给出了,初学者颠末前面的操纵后,然后看代码,不断明白,应该可以把握该步伐的精髓。同时这个代码照旧不完善的。
这里指出:
1、串口毗连之后,并没没有提示是否毗连
2、串口毗连之后,没有断开操纵。
3、这里串口接收消息,只能处理简单数据,现实工作汇总,串口接收数据快,且多,需要单独线程处理。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立聪堂德州十三局店

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表