Qt串口基本设置与协议收发

news2025/1/20 3:56:16

前言

1.一直都想要做一个Qt上位机,趁着这个周末有时间,动手写一下

2.comboBox没有点击的信号,所以做了一个触发的功能

3.Qt的数据类型很奇怪,转来转去的我也搞得很迷糊

4.给自己挖个坑,下一期做一个查看波形的上位机

有纰漏请指出,转载请说明。

学习交流请发邮件 1280253714@qq.com

串口功能

波特率设置

串口开关

串口异常检测

字符串/HEX收发

定时发送

接收数据分隔

协议组包

协议拆包

源代码

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtSerialPort/QSerialPort>         // 提供访问串口的功能
#include <QtSerialPort/QSerialPortInfo>      // 提供系统中存在的串口信息
#include <QTime>
#include <QtCharts>
#include <QTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

    //用于表格设置
    enum FieldColNum {
        colTime,
        colCmd1,
        colCmd2,
        colData,
    };
    enum CellType {
        ctTime,
        ctCmd1,
        ctCmd2,
        ctData,
    };
public:
    int tableRowCnt = 0;
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void serialPortInit();  //串口初始化
    void windowInit();      //显示窗口初始化
    void refreshCom();      //刷新串口
    void tableInit();       //表格初始化
    void createItemsARow(int rowNum, QByteArray *protocalData); //表格新建一行
    QString ByteArrayToHexString(QByteArray &ba);
private:
    Ui::Widget *ui;
    QSerialPort* serialPort;
public slots:
    void comboBoxClicked(); //comboBox被点击
private slots:

    void sendData();            //发送串口数据
    void receiveData();         //接收串口数据
    void openSerialport();      //串口开启
    void closeSerialport();     //串口关闭
    void setBuad(int);          //设置波特率
    void clearRcv();            //清楚接收缓存
    void on_btnConvert_clicked();   //转换按钮被点击
    void on_btnClear_clicked();     //清楚转化的按钮被点击
    void sendProtocalHexData();     //以hex格式发送串口数据
    void handleSerialError(QSerialPort::SerialPortError serialPortErr); //串口异常捕获
};


#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "newcombobox.h"
#include <algorithm>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建一个定时器用来定时发送
    QTimer *timer1 = new QTimer();
    timer1->start(1000);
    connect(timer1,&QTimer::timeout,[=](){
        int timed = ui->comboBox_timedSend->currentText().toInt();
        timer1->start(timed);
        if(ui->checkBox_timedSend->isChecked() == true)
        {
            sendData();
        }
    });

    windowInit();
    tableInit();
    serialPort = new QSerialPort();
    serialPortInit();


    connect(serialPort,SIGNAL(readyRead()),this,SLOT(receiveData()));
    connect(serialPort,SIGNAL(errorOccurred(QSerialPort::SerialPortError)),this,SLOT(handleSerialError(QSerialPort::SerialPortError)));
    connect(ui->pushButton_sendData,SIGNAL(clicked()),this,SLOT(sendData()));
    connect(ui->pushButton_openSerialPort,SIGNAL(clicked()),this,SLOT(openSerialport()));
    connect(ui->pushButton_closeSerialPort,SIGNAL(clicked()),this,SLOT(closeSerialport()));
    connect(ui->comboBox_chooseCom,SIGNAL(clicked()),this,SLOT(comboBoxClicked()));
    connect(ui->pushButton_clearRcv,SIGNAL(clicked()),this,SLOT(clearRcv()));
    connect(ui->pushButton_convert,SIGNAL(clicked()),this,SLOT(on_btnConvert_clicked()));
    connect(ui->pushButton_clearConvertData,SIGNAL(clicked()),this,SLOT(on_btnClear_clicked()));
    connect(ui->pushButton_sendProtocalData,SIGNAL(clicked()),this,SLOT(sendProtocalHexData()));
    connect(serialPort,SIGNAL(errorOccurred(QSerialPort::SerialPortError)),this,SLOT(handleSerialError(QSerialPort::SerialPortError)));
    connect(ui->comboBox_setBuad,SIGNAL(activated(int)),this,SLOT(setBuad(int)));
}

Widget::~Widget()
{
    delete ui;
}

//串口异常捕获
void Widget::handleSerialError(QSerialPort::SerialPortError serialPortErr)
{
    if(serialPortErr == QSerialPort::ResourceError)
    {
        QMessageBox::critical(NULL, "critical", "设备拔出", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
        closeSerialport();
    }
    if(serialPortErr == QSerialPort::DeviceNotFoundError)
    {
        QMessageBox::critical(NULL, "critical", "找不到串口", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
        closeSerialport();
    }
}

void Widget::comboBoxClicked()
{
    refreshCom();
}

void Widget::windowInit()
{
    ui->pushButton_closeSerialPort->setEnabled(false);
    ui->pushButton_openSerialPort->setEnabled(false);
    ui->pushButton_sendData->setEnabled(false);
    setWindowTitle(tr("串口收发"));

    ui->comboBox_timedSend->addItem("10");
    ui->comboBox_timedSend->addItem("100");
    ui->comboBox_timedSend->addItem("1000");
    ui->comboBox_timedSend->setCurrentIndex(2);
}

void Widget::refreshCom()
{
    //显示串口列表
    ui->comboBox_chooseCom->clear();
    foreach(QSerialPortInfo portInfo, QSerialPortInfo::availablePorts())
        ui->comboBox_chooseCom->addItem(portInfo.portName()+":"+portInfo.description());
    ui->pushButton_openSerialPort->setEnabled(ui->comboBox_chooseCom->count()>0);	//
}

void Widget::serialPortInit(){

    refreshCom();

    ui->comboBox_setBuad->addItem("9600");
    ui->comboBox_setBuad->addItem("115200");
    ui->comboBox_setBuad->addItem("921600");
    ui->comboBox_setBuad->setCurrentIndex(1);

}

void Widget::tableInit()
{
    QStringList headerText;
    headerText<<"时间"<<"命令1"<<"命令2"<<"数据";
    ui->tableWidget->setColumnCount(headerText.size());      //设置表格列数
    ui->tableWidget->resizeColumnsToContents();
    for (int i=0;i<ui->tableWidget->columnCount();i++)
    {
        QTableWidgetItem *headerItem=new QTableWidgetItem(headerText.at(i));
        QFont font=headerItem->font();   //获取原有字体设置
        font.setBold(true);              //设置为粗体
        font.setPointSize(10);           //字体大小
        headerItem->setForeground(QBrush(Qt::black));  //设置文字颜色
        headerItem->setFont(font);       //设置字体
        ui->tableWidget->setHorizontalHeaderItem(i,headerItem);    //设置表头单元格的item
    }
    ui->tableWidget->setColumnWidth(0, 60);
    ui->tableWidget->setColumnWidth(1, 50);
    ui->tableWidget->setColumnWidth(2, 50);
    ui->tableWidget->setColumnWidth(3, 250);
}

//为一行的单元格创建 Items
void Widget::createItemsARow(int rowNum, QByteArray *protocalData)
{
    uchar preFix = 0xA5;
    uchar crc = 0;
    uchar temp = 0;
    temp = static_cast<uchar>(protocalData->at(0));
    if(static_cast<uchar>(protocalData->at(0)) == preFix)
    {
        for(int i=1; i<protocalData->length()-2; i++)
        {
            temp = static_cast<uchar>(protocalData->at(i));
            crc += static_cast<uchar>(protocalData->at(i));
        }
        temp = static_cast<uchar>(protocalData->at(protocalData->length()-2));
        if(crc != static_cast<uchar>(protocalData->at(protocalData->length()-2)))
        {
            return;
        }
        uchar len = protocalData->at(1);
        uchar cmd1 = protocalData->at(2);
        uchar cmd2 = protocalData->at(3);
        QByteArray data = protocalData->mid(4,len-6);
        QDateTime curTime = QDateTime::currentDateTime();//获取系统现在的时间
        QString time = curTime.toString("hh:mm:ss"); //设置显示格式

        uint8_t str1 = static_cast<uint8_t>(cmd1);
        QString hexStr1 = QString("%1").arg(str1, 2, 16, QLatin1Char('0')).toUpper();
        uint8_t str2 = static_cast<uint8_t>(cmd2);
        QString hexStr2 = QString("%1").arg(str2, 2, 16, QLatin1Char('0')).toUpper();
        QString testdata = ByteArrayToHexString(data).toLatin1().toUpper();

        QTableWidgetItem *item = new QTableWidgetItem(time, ctTime);
        ui->tableWidget->setItem(rowNum, colTime, item);

        item = new QTableWidgetItem(hexStr1, ctCmd1);
        ui->tableWidget->setItem(rowNum, colCmd1, item);

        item = new QTableWidgetItem(hexStr2, ctCmd2);
        ui->tableWidget->setItem(rowNum, colCmd2, item);

        item = new QTableWidgetItem(testdata, ctData);
        ui->tableWidget->setItem(rowNum, colData, item);
    }


    auto lastRowIndex = ui->tableWidget->rowCount()-1; // 最后一行的索引
    auto lastModelIndex = ui->tableWidget->model()->index(lastRowIndex, 0);
    ui->tableWidget->scrollTo(lastModelIndex);         // 滚动到最后一行
}

QString Widget::ByteArrayToHexString(QByteArray &ba)
{
    QDataStream out(&ba,QIODevice::ReadWrite);   //将str的数据 读到out里面去
    QString buf;
    while(!out.atEnd())
    {
        qint8 outChar = 0;
        out >> outChar;   //每次一个字节的填充到 outchar
        QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0')).toUpper() + QString(" ");   //2 字符宽度
        buf += str;
    }
    return buf;
}

void Widget::sendData()
{
    QString message = ui->lineEdit_sendData->text();
    if(ui->checkBox_hexSend->isChecked() == true)
    {
        serialPort->write(QByteArray::fromHex(message.toLatin1()));
    }
    else
    {
        serialPort->write(message.toLatin1());
    }
}

void Widget::receiveData()
{
    QByteArray message;
    QString hexMsg;
    message.append(serialPort->readAll());
    QDateTime time = QDateTime::currentDateTime();  //获取系统现在的时间
    QString date = time.toString("hh:mm:ss");       //设置显示格式
    if(ui->checkBox_hexRcv->isChecked() == true)
    {
        tableRowCnt++;
        ui->tableWidget->setRowCount(tableRowCnt);
        createItemsARow(tableRowCnt-1,&message);

        hexMsg = ByteArrayToHexString(message).toLatin1();
        ui->textEdit_RecData->append(date+QString("->  ")+hexMsg.toUpper());
    }
    else
    {
        ui->textEdit_RecData->append(date+QString("->  ")+message);
    }
}
void Widget::openSerialport()
{
    ui->pushButton_closeSerialPort->setEnabled(true);
    ui->pushButton_openSerialPort->setEnabled(false);

    QList<QSerialPortInfo>  comList = QSerialPortInfo::availablePorts();
    QSerialPortInfo portInfo = comList.at(ui->comboBox_chooseCom->currentIndex());
    serialPort->setPort(portInfo);      //设置使用哪个串口

    if(serialPort->open(QIODevice::ReadWrite) == false)
    {
        QMessageBox::critical(NULL, "critical", "找不到串口/串口被占用", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
        closeSerialport();
    }
    else
    {
        serialPort->setBaudRate(QSerialPort::Baud115200);
        serialPort->setDataBits(QSerialPort::Data8);
        serialPort->setParity(QSerialPort::NoParity);
        serialPort->setStopBits(QSerialPort::OneStop);
        serialPort->setFlowControl(QSerialPort::NoFlowControl);

        ui->pushButton_sendData->setEnabled(true);
    }
}

void Widget::closeSerialport()
{
    if(serialPort->isOpen()){
        serialPort->clear();
        serialPort->close();
    }
    ui->pushButton_closeSerialPort->setEnabled(false);
    ui->pushButton_openSerialPort->setEnabled(true);
}

void Widget::setBuad(int buad)
{
    QString str = ui->comboBox_setBuad->currentText();
    serialPort->setBaudRate(str.toInt());
}

void Widget::clearRcv()
{
    ui->textEdit_RecData->clear();
}

void Widget::on_btnClear_clicked()
{
    ui->lineEdit_protocalData->clear();
}

void Widget::on_btnConvert_clicked()
{
    ui->lineEdit_protocalData->clear();
    bool ok;
    QString str = "A5";
    int val1= ui->lineEditCmd1->text().toInt(&ok,16);  //以十六进制数读入
    QString str1 = QString("%1").arg(val1, 2, 16, QLatin1Char('0'));

    int val2= ui->lineEditCmd2->text().toInt(&ok,16);  //以十六进制数读入
    QString str2 = QString("%1").arg(val2, 2, 16, QLatin1Char('0'));

    if ((str1.isEmpty())||(str2.isEmpty()))
        return;

    int val3= ui->lineEditData->text().toInt(&ok,16);  //以十六进制数读入
    QString str3 = QString("%1").arg(val3, 2, 16, QLatin1Char('0'));

    uint8_t len = 6 + static_cast<uint8_t>(str3.length()/2);
    QString hexStr = QString("%1").arg(len, 2, 16, QLatin1Char('0'));

    str.append(hexStr);
    str.append(str1);
    str.append(str2);
    str.append(str3);
    uint8_t crc;
    QString tmp;
    for(int i=0; i<str3.length(); i+=2)
    {
        tmp = ui->lineEditData->text()[i];
        tmp += ui->lineEditData->text()[i+1];
        crc+= tmp.toInt(&ok,16);
        tmp = "";
    }
    crc += len;
    crc += val1;
    crc += val2;
    QString hexCrc= QString("%1").arg(crc, 2, 16, QLatin1Char('0'));
    str.append(hexCrc);
    str.append("5A");
    str = str.toUpper();
    ui->lineEdit_protocalData->insert(str);
}

void Widget::sendProtocalHexData()
{
    QString message = ui->lineEdit_protocalData->text();
    serialPort->write(QByteArray::fromHex(message.toLatin1()));
}

ui

代码框架概览

演示视频

串口上位机(基本设置/协议收发)演示_哔哩哔哩_bilibili

串口上位机(基本设置/协议收发)演示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/997184.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java输入-a,-b,geek,-c,888,-d,[hello,world]字符之后,如何将[hello,world]这个不分开

Java输入-a,-b,geek,-c,888,-d,[hello,world]字符之后&#xff0c;如何将[hello,world]这个不分开&#xff1f; 你可以使用命令行参数解析库来处理Java输入中的各个参数。在这种情况下&#xff0c;你可以使用Apache Commons CLI库来解析命令行参数。以下是一个示例代码片段&am…

MATLAB遗传算法求解生鲜货损制冷时间窗碳排放多成本车辆路径规划问题

MATLAB遗传算法求解生鲜货损制冷时间窗碳排放多成本车辆路径规划问题实例 1、问题描述 已知配送中心和需求门店的地理位置,并且已经获得各个门店的需求量。关于送货时间的要求,门店都有规定的时间窗,对于超过规定时间窗外的配送时间会产生相应的惩罚成本。为保持生鲜农产品的…

2023.09.10 学习周报

文章目录 摘要文献阅读1-1 题目1-2 创新点1-3 本文工作2-1 题目2-2 什么是图2-3 图神经网络2-4 信息传递3-1 题目3-2 创新点3-3 本文工作 深度学习1.GNN的构建步骤2.构建图的方法3.GNN的简单样例 总结 摘要 本周阅读了三篇文章&#xff0c;第一篇是基于物理信息深度学习和激光…

【LeetCode题目详解】第九章 动态规划part11 ● 123.买卖股票的最佳时机III ● 188.买卖股票的最佳时机IV (day50补)

本文章代码以c为例&#xff01; 一、力扣第123题&#xff1a;买卖股票的最佳时机 III 题目&#xff1a; 给定一个数组&#xff0c;它的第 i 个元素是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 注意&#xff1…

基于springboot+vue的在线课程学习网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

unity实现Perlin噪声

Perlin噪声目的是为了生成连续变化的随机值。这里推荐一个使用unity实现的perlin噪声插件&#xff0c;包含源码&#xff0c;源码下载地址&#xff1a;https://download.csdn.net/download/hulinhulin/88323641https://download.csdn.net/download/hulinhulin/88323641 插件界面…

vscode 下载安装

vscode 下载安装常用插件 vscode 官网&#xff1a; https://code.visualstudio.com/ 点击右上角 Download 进入下载选择页面 选择自己使用操作对应 CPU 架构 下载 本文使用 x86 架构 64位 windows 系统为例 跳转下载页面 自动 开始下载 下载不开始&#xff1f;试试这个直…

关于黑马hive课程案例FineBI中文乱码的解决

文章目录 问题描述情况一的解决情况二的解决 ETL数据清洗知识社交案例参考代码结果展示 问题描述 情况1&#xff1a;FineBI导入表名中文乱码&#xff0c;字段内容正常情况2&#xff1a;FineBI导入表字段中文乱码&#xff0c;表名内容正常 情况一的解决 使用navcat等工具连接…

第九章 Linux实际操作——Linux磁盘分区、挂载

第九章 Linux实际操作——Linux磁盘分区、挂载 9.1 Linux分区9.1.1原理介绍9.1.2 硬盘说明9.1.3 查看所有设备搭载情况 9.2 挂载的经典案例9.2.1 说明9.2.2 如何增加一块硬盘9.2.3 虚拟机增加硬盘步骤 9.3 磁盘情况查询9.3.1 查询系统整体磁盘使用情况9.3.2 查询指定目录的磁盘…

TypeScript对象类型

废话不多说&#xff0c;还是挑点有营养的讲。 对象类型 1、匿名对象 匿名对象类型是在定义变量时直接使用花括号{}&#xff0c;来定义一个对象类型。 const person: { name: string, age: number } { name: John, age: 25 }; 2、接口类型 使用接口来定义对象类型&#x…

二叉树的顺序结构以及堆的实现——【数据结构】

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 上篇文章&#xff0c;我们认识了什么是树以及二叉树的基本内容、表示方法……接下来我们继续来深入二叉树&#xff0c;感受其中的魅力。 目录 二叉树的顺序结构 堆的概念及结构 堆的实现 堆的创建 堆的初始化与…

LeetCode(力扣)455. 分发饼干Python

LeetCode20. 有效的括号 题目链接代码 题目链接 https://leetcode.cn/problems/assign-cookies/ 代码 从大遍历 class Solution:def findContentChildren(self, g: List[int], s: List[int]) -> int:g.sort()s.sort()index len(s) - 1result 0for i in range(len(g) -…

老胡的周刊(第107期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 open-interpreter[2] 基于 LLM 为你提供一种…

车载软件架构——基础软件供应商开发工具链(一)

车载软件架构——基础软件供应商&开发工具链(一) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己…

创建 gstreamer 插件的几种方式

系列文章目录 创建 gstreamer 插件的几种方式 使用 gst-template 创建自己的gstreamer 插件 使用 gst-plugins-bad 里面的 gst-element-maker 工具创建gstreamer 插件 文章目录 系列文章目录前言一、gstreamer 官网提供创建插件的方法总结参考资料前言 本系列文章主要介绍如何…

EasyExcel实现指定行列的相同内容单元格合并

ExcelMergeUtil工具类 package com.sdy.resdir.biz.util;import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHold…

机器学习从0到1

机器学习&#xff0c;即machine learning 感谢easyai的精彩讲解&#xff1a; easyai网址 文章目录 机器学习的概念机器学习的原理监督学习&#xff0c;非监督学习&#xff0c;强化学习监督学习非监督学习强化学习 机器学习实操的7个步骤现在举一个具体的任务来说明这些步骤1.收…

Visual studio解决‘scanf: This function or variable may be unsafe. 问题

使用C语言的scanf函数在Visual Studio软件上运行会报如下错误&#xff1a; scanf: This function or variable may be unsafe. Consider using scanf s instead. To disable deprecation, use. CRT SECURE NO WARNINGS. See online help for details. 这个函数或变量可能是不安…

Red-Black Tree红黑树

红黑树特点&#xff1a; 1.根节点必须为黑色&#xff1b; 2.每个节点到子节点经过相同数目的黑色节点&#xff1b; 3.红色节点的子节点必须是黑色&#xff1b; 4.空指针也作为节点 红黑树如何插入新的节点&#xff1f; 1.如果是空树&#xff0c;插入新的黑色节点作为根节点&am…

Wine 8.14 开发版正式发布

Wine 8.14 最新开发版已正式发布。 Wine (Wine Is Not an Emulator) 是一个能够在多种兼容 POSIX 接口的操作系统&#xff08;诸如 Linux、macOS 与 BSD 等&#xff09;上运行 Windows 应用的兼容层。它不是像虚拟机或者模拟器一样模仿内部的 Windows 逻辑&#xff0c;而是将 …