草庐IT

Qt实现串口通讯实例

乔伊波伊 o(*^@^*)o 2023-04-18 原文

欢迎小伙伴的点评✨✨,相互学习🚀🚀🚀
博主🧑🧑 本着开源的精神交流Qt开发的经验、将持续更新续章,为社区贡献博主自身的开源精神👩‍🚀

文章目录


前言

本章节将会给大家带来Qt串口通讯软件的简单实例

一、串口是什么?

串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。典型地,串口用于ASCII码字符的传输。通信使用3根线完成,分别是地线、发送、接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。
串口: 串口是一个泛称,UART,TTL,RS232,RS485都遵循类似的通信时序协议,因此都被通称为串口。

UART接口: 通用异步收发器(Universal Asynchronous Receiver/Transmitter),UART是串口收发的逻辑电路,这部分可以独立成芯片,也可以作为模块嵌入到其他芯片里,单片机、SOC、PC里都会有UART模块。
COM口: 特指台式计算机或一些电子设备上的D-SUB外形(一种连接器结构,VGA接口的连接器也是D-SUB)的串行通信口,应用了串口通信时序和RS232的逻辑电平。
USB口: 通用串行总线,和串口完全是两个概念。虽然也是串行方式通信,但由于USB的通信时序和信号电平都和串口完全不同,因此和串口没有任何关系。USB是高速的通信接口,用于PC连接各种外设,U盘、键鼠、移动硬盘、当然也包括“USB转串口”的模块。(USB转串口模块,就是USB接口的UART模块)

波特率: 这是一个衡量符号传输速率的参数。指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。一般调制速率大于波特率,比如曼彻斯特编码)。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
数据位: 这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
停止位: 用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
奇偶校验位: 在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
流控制: 流控可以使数据接收设备在不能接收数据时通知数据发送设备,使其停止发送。串口的流控经常采用硬件流控和软件流控两种方式。

二、图示实例

使用Configure Virtual Serial Port Driver工具模拟串口
GitHub 使用git 命令下载

git clone  https://github.com/dhn111/Qt.git 

Qt串口通信调试工具包

三、实例实例解析

先在 ” .pro “ 文件中添加 如下图所示:

QT       +=serialport

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QComboBox>
#include <QWidget>
#include <QGridLayout>
#include <QMessageBox>
#include <QLabel>
#include <QTextEdit>
#include <QPushButton>
#include <QSerialPort>      //提供访问串口的功能
#include <QSerialPortInfo>  //提供系统中存在的串口的信息
#include <QDebug>
#include <QMessageBox>
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    QComboBox *ComboBox_1;   //串口端口
    QComboBox *ComboBox_2;   //波特率
    QComboBox *ComboBox_3;   //校验位
    QComboBox *ComboBox_4;   //数据位
    QComboBox *ComboBox_5;   //停止位
    QComboBox *ComboBox_6;   //流控制

    QLabel    *Label_1;   //串口端口
    QLabel    *Label_2;   //波特率
    QLabel    *Label_3;   //校验位
    QLabel    *Label_4;   //数据位
    QLabel    *Label_5;   //停止位
    QLabel    *Label_6;   //流控制
    QLabel    *Label_7;   //发送数据窗口
    QLabel    *Label_8;   //接受数据窗口

    QTextEdit *TextEdit_1; //数据发送窗口
    QTextEdit *TextEdit_2; //数据接受窗口

    QPushButton *PushBtton_1; //打开串口按钮
    QPushButton *PushBtton_2; //数据发送按钮


    QSerialPort *myport; //串口

    QGridLayout *layout;     //布局

    bool OpenPort;         //是否已打开串口

    void botelv(const QString &arg);  //设置波特率

    void jiaoyanwei(int arg);         //设置校验位

    void shujuwei(const QString &arg);  //设置数据位

    void tingzhiwei(const QString &arg);  //设置停止位

    void liukongzhi(int arg1);      //设置流控制

public slots :
    void ON_PushBtton_1();   //打开串口槽函数
    void ON_PushBtton_2();   //发送数据槽函数
    void readPort();         //接受串口数据
};
#endif // MAINWINDOW_H

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{

    ComboBox_1 = new QComboBox();   //创建串口组合窗
    ComboBox_2 = new QComboBox();   //创建波特率组合窗
    ComboBox_3 = new QComboBox();   //创建校验位组合窗
    ComboBox_4 = new QComboBox();   //创建数据位组合窗
    ComboBox_5 = new QComboBox();   //创建停止位组合窗
    ComboBox_6 = new QComboBox();   //创建流控制组合窗


    Label_1   = new QLabel("串口端口");
    Label_2   = new QLabel("波特率");
    Label_3   = new QLabel("校验位");
    Label_4   = new QLabel("数据位");
    Label_5   = new QLabel("停止位");
    Label_6   = new QLabel("流控制");
    Label_7   = new QLabel("发送数据窗口");
    Label_8   = new QLabel("接受数据窗口");

    TextEdit_1  =  new QTextEdit(); //发送数据窗口
    TextEdit_2  =  new QTextEdit(); //接受数据窗口

    PushBtton_1 = new QPushButton("打开");//打开串口按钮
    PushBtton_2 = new QPushButton("发送");//数据发送按钮
    QWidget *centerWindow = new QWidget; //新建widget 类
    this->setCentralWidget(centerWindow);  //中间的空间会铺满整个widget

    layout = new QGridLayout(this);

    layout->addWidget(ComboBox_1,1,2); //创建组合窗 0行,1列
    layout->addWidget(ComboBox_2,2,2);
    layout->addWidget(ComboBox_3,3,2);
    layout->addWidget(ComboBox_4,4,2);
    layout->addWidget(ComboBox_5,5,2);
    layout->addWidget(ComboBox_6,6,2);


    layout->addWidget(Label_1,1,1); //创建组合窗 0行,1列
    layout->addWidget(Label_2,2,1);
    layout->addWidget(Label_3,3,1);
    layout->addWidget(Label_4,4,1);
    layout->addWidget(Label_5,5,1);
    layout->addWidget(Label_6,6,1);
    layout->addWidget(Label_7,7,1); //7行,1列
    layout->addWidget(PushBtton_1,7,2); //7行,2列

    layout->addWidget(TextEdit_1,8,1,1,2); //8行,1列,长1,宽1

    layout->addWidget(Label_8,9,1); //9行,1列,长1,宽1
    layout->addWidget(PushBtton_2,9,2); //9行,2列

    layout->addWidget(TextEdit_2,10,1,1,2); //10行,1列,长1,宽1



    centerWindow->setLayout(layout); //获取layout并设置控件位置

    myport = new QSerialPort(); //创建串口对象

    connect(myport, &QSerialPort::readyRead, this, &MainWindow::readPort);//绑定读取串口信息的信号和槽
    connect(PushBtton_1,SIGNAL(clicked(bool)),this,SLOT(ON_PushBtton_1()));//连接两个信号和槽
    connect(PushBtton_2,SIGNAL(clicked(bool)),this,SLOT(ON_PushBtton_2()));//连接两个信号和槽
    //获取允许使用的串口信息
    QList<QSerialPortInfo> port = QSerialPortInfo::availablePorts();
    if(port.isEmpty())
    {
        ComboBox_1->addItem("无效");
    }
    else
    {
        foreach(const QSerialPortInfo &port_info, port)
        {
            ComboBox_1->addItem(port_info.portName());
        }
    }

    ComboBox_2->addItem("1200");
    ComboBox_2->addItem("2400");
    ComboBox_2->addItem("4800");
    ComboBox_2->addItem("38400");
    ComboBox_2->addItem("115200");


    ComboBox_3->addItem("OddParity");
    ComboBox_3->addItem("EvenParity");
    ComboBox_3->addItem("NoParity");

    ComboBox_4->addItem("5");
    ComboBox_4->addItem("6");
    ComboBox_4->addItem("7");
    ComboBox_4->addItem("8");

    ComboBox_5->addItem("1");
    ComboBox_5->addItem("1.5");
    ComboBox_5->addItem("2");

    ComboBox_6->addItem("Hardware");
    ComboBox_6->addItem("Software");
    ComboBox_6->addItem("NoFlow");

    OpenPort = false;
}


//ui函数未使用
MainWindow::~MainWindow()
{

}

void MainWindow::ON_PushBtton_1()       //打开串口槽函数实现
{
    if(OpenPort == false)      //打开串口
    {
        //指定串口信息(串口名称)
        QSerialPortInfo *port = new QSerialPortInfo(ComboBox_1->currentText());
        //绑定串口
        myport->setPort(*port);

        /******
         * currentText 获取字符串
         * currentIndex 获取枚举整数
        ********/
        botelv(ComboBox_2->currentText()); //设置波特率
        jiaoyanwei(ComboBox_3->currentIndex()); //设置校验位
        shujuwei(ComboBox_4->currentText()); //设置数据位
        tingzhiwei(ComboBox_5->currentText()); //设置停止位
        liukongzhi(ComboBox_6->currentIndex()); //设置流控制

        //打开串口
        if(!myport->open(QIODevice::ReadWrite))
        {
            QMessageBox::warning(this,"警告","打开串口失败!");
            return;
        }

        PushBtton_1->setText(tr("关闭串口"));
        OpenPort = true;
    }
    else                    //关闭串口
    {
        myport->close();
        PushBtton_1->setText(tr("打开串口"));
        OpenPort = false;
    }
}


void MainWindow::ON_PushBtton_2()       //数据发送槽函数实现
{
    QString data = TextEdit_1->toPlainText();  //获取数据
    QByteArray arg = data.toLatin1();
    char *temp = arg.data();
    myport->write(temp); //发送数据
}

//接受串口数据
void MainWindow::readPort()
{
    QByteArray buf;
    buf = myport->readAll(); //接受数据
    QString data = buf;
    //qDebug() << data << endl;
    TextEdit_2->append(data); //写入到文本框中
}


//设置波特率
void MainWindow::botelv(const QString &arg)
{
    switch(arg.toInt())
    {
    case 1200:
        myport->setBaudRate(QSerialPort::Baud1200);
        break;
    case 2400:
        myport->setBaudRate(QSerialPort::Baud2400);
        break;
    case 9600:
        myport->setBaudRate(QSerialPort::Baud9600);
        break;
    case 38400:
        myport->setBaudRate(QSerialPort::Baud38400);
        break;
    case 115200:
        myport->setBaudRate(QSerialPort::Baud115200);
        //qDebug()<<"Baud115200";
        break;
    }
}

//设置校验位
void MainWindow::jiaoyanwei(int arg)
{
    switch(arg)
    {
    case 0:
        myport->setParity(QSerialPort::OddParity);
        break;
    case 1:
        myport->setParity(QSerialPort::EvenParity);
        break;
    case 2:
        myport->setParity(QSerialPort::NoParity);
        //qDebug()<<"NoParity";
        break;
    }
}

//设置数据位
void MainWindow::shujuwei(const QString &arg1)
{
    switch(arg1.toInt())
    {
    case 5:
        myport->setDataBits(QSerialPort::Data5);
        break;
    case 6:
        myport->setDataBits(QSerialPort::Data6);
        break;
    case 7:
        myport->setDataBits(QSerialPort::Data7);
        break;
    case 8:
        myport->setDataBits(QSerialPort::Data8);
        //qDebug()<<"Data8";
        break;
    }
}

//设置停止位
void MainWindow::tingzhiwei(const QString &arg)
{
    switch(arg.toInt())
    {
    case 1:
        myport->setStopBits(QSerialPort::OneStop);
        //qDebug()<<"OneStop";
        break;
    case 2:
        myport->setStopBits(QSerialPort::TwoStop);
        break;
    }
}

//设置流控制
void MainWindow::liukongzhi(int arg)
{
    switch(arg)
    {
    case 0:
        myport->setFlowControl(QSerialPort::HardwareControl);
        break;
    case 1:
        myport->setFlowControl(QSerialPort::SoftwareControl);
        break;
    case 2:
        myport->setFlowControl(QSerialPort::NoFlowControl);
        //qDebug()<<"NoFlowControl";
        break;
    }
}

四、总结

QT上位机软件实现串口通讯在嵌入式领域有着广泛的应用。

有关Qt实现串口通讯实例的更多相关文章

  1. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  2. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  5. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  6. ruby-on-rails - 使用 ruby​​ 将多个实例变量转换为散列的更好方法? - 2

    我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. Qt Designer的简单使用 - 2

    在前面两节的例子中,主界面窗口的尺寸和标签控件显示的矩形区域等,都是用C++代码编写的。窗口和控件的尺寸都是预估的,控件如果多起来,那就不好估计每个控件合适的位置和大小了。用C++代码编写图形界面的问题就是不直观,因此Qt项目开发了专门的可视化图形界面编辑器——QtDesigner(Qt设计师)。通过QtDesigner就可以很方便地创建图形界面文件*.ui,然后将ui文件应用到源代码里面,做到“所见即所得”,大大方便了图形界面的设计。本节就演示一下QtDesigner的简单使用,学习拖拽控件和设置控件属性,并将ui文件应用到Qt程序代码里。使用QtDesigner设计界面在开始菜单中找到「Q

  10. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

随机推荐