QT上位机串口实时温湿度显示

STM32与上位机通信协议——UART协议:
串行通讯需要有通信协议
通信协议:规定发送与接收方,通信的方式与要求,数据的格式
由RXD和TXD两条线,由于没有时钟线,所以需要规定波特率
数据传输速率就是波特率
UART(串行异步全双工)
采用的是串行通信,也就是一条传输线,一位一位的顺序发送(可以远距离传输,传输较慢)
异步通信是以一个字符为传输单位,每个字符为10位(1个起始位,7个数据位,1个校验位,1个停止位)
通信中两个字符之间的时间间隔不固定,但是同一个字符相邻位之间时间间隔是固定的:
数据通信格式
1、起始位
2、数据位
3、校验位
4、停止位
5、空闲位
STM32需要通过串口向上位机完成数据上传,然后完成实时显示。
所以需要先完成上位机的一个开发,上位机开发工具选择QT
创建QT工程:
在这里插入图片描述

选择QT Widgets 修改名称为:serialdisplay,路径自己选择
在这里插入图片描述

直接点击下一步,到这个界面:
在这里插入图片描述
修改一下类名为:SerialDisplay
在这里插入图片描述

直接点击下一步,直到点击完成 创建工程就成功了,创建完成可以运行一下;
接下来就是添加空间,找到设计界面,点击ui
在这里插入图片描述
就转到这个界面,可以开始添加控件,左边就是控件栏:
在这里插入图片描述
添加完控件如下:
在这里插入图片描述
修改右边对应控件的名字,便于代码里使用;
在这里插入图片描述
然后就是代码部分:
右击你要实现功能的控件,比如说打开串口按钮如下:
在这里插入图片描述
点击转到槽函数:
在这里插入图片描述
选择点击触发,点击ok,就会跳转到以下界面:
在这里插入图片描述
对应实现该函数即可。其他我就不一一介绍了:
直接上源码:
serialdisplay.cpp文件

#include "serialdisplay.h"
#include "ui_serialdisplay.h"
#include <QDebug>
#include <QMessageBox>
#include <QFontDialog>

#include <QJsonObject>
#include <QJsonDocument>

SerialDisplay::SerialDisplay(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::SerialDisplay)
{
    ui->setupUi(this); 
    serialflag = 0;//表示串口关闭
    setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint);// 禁止最大化按钮
    serialport = new QSerialPort();//分配内存
    ui->dateTimeEdit->setDisabled(true);//
    timerid = startTimer(1000);//开启定时器,每隔1秒
}

SerialDisplay::~SerialDisplay()
{
    killTimer(timerid);//关闭
    delete ui;
    delete serialport;
}

//串口获取
void SerialGetData::Serial_read()
{
    int temp = 0;//
    int humi = 0;
    QByteArray recvData;
    try {
        recvData = SerialGetData::serial_recv_Data();//从串口接收数据
    } catch (MyExcption &err) {
        QMessageBox::warning(NULL , "提示", err.what());
        return;
    }
    QString receive = QString::fromLocal8Bit(recvData.constData());
    //json格式数据解析 比如:{"温度":"10℃","湿度":"20%"}
    QJsonParseError err;
    QByteArray arr ;
    arr.append(receive);
    QJsonDocument doc = QJsonDocument::fromJson(arr,&err);
    if(err.error != QJsonParseError::NoError){
        qDebug() << "转换失败";
        return;
    }
    QJsonObject obj = doc.object();
    temp = SerialGetData::getNumsFromStr(obj.value("温度").toString());
    humi = SerialGetData::getNumsFromStr(obj.value("湿度").toString());
   // qDebug() << receive;

    /*分割字符串的方法*/
    /*//自定义数据格式(温度:10-湿度:20或者10℃-20%)
    QStringList list = receive.split("-");//以:分割字符串
    if(list[0].contains("温度") || list[0].contains("℃")){
        temp = SerialGetData::getNumsFromStr(list[0]);
        humi = SerialGetData::getNumsFromStr(list[1]);
    }
    else if(list[0].contains("湿度") || list[0].contains("%")){
        temp = SerialGetData::getNumsFromStr(list[1]);
        humi = SerialGetData::getNumsFromStr(list[0]);
    }
    else{
        QMessageBox::information(NULL,"提示","数据格式错误",QMessageBox::Ok);
        return;
    }
    */
    ui->lcdTemp->display(temp);
    ui->lcdHuim->display(humi);
}
//串口接收
QByteArray SerialGetData::serial_recv_Data() throw(MyExcption)
{
    //读取串口收到的数据
    QByteArray buffer = serialport->readAll();
    //为空的话
    if(buffer.isEmpty()){
        throw("收到数据为空");
        return NULL;
    }
    return buffer;
}
//获取字符串中数字部分
int SerialGetData::getNumsFromStr(QString data)
{
    QString num;
    int j = 0;
    for(int i = 0 ; i < data.length();i++){
        if(data[i] >= '0' && data[i] <= '9'){
            num[j] = data[i];
            j++;
        }
    }
    return num.toInt();
}
//扫描串口
void SerialGetData::on_scanSerialBtn_clicked()
{
    // 清除当前显示的端口号
     ui->serialPortBox->clear();
     //检索端口号
     foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
     {
         ui->serialPortBox->addItem(info.portName());
     }
}
//打开串口
void SerialGetData::on_openSerialBtn_clicked()
{
    if(ui->openSerialBtn->text() == QString("打开串口"))  //串口未打开
    {
        //设置端口号
        //qDebug() << ui->serialPortBox->currentText();
        serialport->setPortName(ui->serialPortBox->currentText());
        //设置波特率
        serialport->setBaudRate(ui->baudRateBox->currentText().toInt());
        //设置数据位
        switch (ui->dataBitBox->currentText().toInt())
        {
            case 8: serialport->setDataBits(QSerialPort::Data8); break;
            case 7: serialport->setDataBits(QSerialPort::Data7); break;
            case 6: serialport->setDataBits(QSerialPort::Data6); break;
            case 5: serialport->setDataBits(QSerialPort::Data5); break;
            default: break;
        }
        //设置停止位
        switch (ui->stopBitBox->currentText().toInt())
        {
            case 1: serialport->setStopBits(QSerialPort::OneStop);break;
            case 2: serialport->setStopBits(QSerialPort::TwoStop);break;
            default:break;
        }
        //设置校验方式
        switch (ui->chekBitBox->currentIndex())
        {
            case 0: serialport->setParity(QSerialPort::NoParity);break;
            default:break;
        }
        //设置流控制模式
        serialport->setFlowControl(QSerialPort::NoFlowControl);
        //打开串口
        if(serialport->open(QIODevice::ReadWrite)==false)
        {
            QMessageBox::warning(NULL , "提示", "串口打开失败!");
            return;
        }
        // 失能串口设置控件
        ui->serialPortBox->setEnabled(false);
        ui->chekBitBox->setEnabled(false);
        ui->baudRateBox->setEnabled(false);
        ui->dataBitBox->setEnabled(false);
        ui->stopBitBox->setEnabled(false);
        ui->scanSerialBtn->setEnabled(false);
        //调整串口控制按钮的文字提示
        ui->openSerialBtn->setText(QString("关闭串口"));
        ui->openSerialBtn->setStyleSheet("background-color: rgb(255, 255, 255);\
                                       color: rgb(255,0,0);\
                                       border-color: rgb(255, 0, 0);");
        serialflag = 1;//串口打开
    }
    else       //串口已经打开
    {
        serialport->close();
        // 使能串口设置控件
        ui->serialPortBox->setEnabled(true);
        ui->chekBitBox->setEnabled(true);
        ui->baudRateBox->setEnabled(true);
        ui->dataBitBox->setEnabled(true);
        ui->stopBitBox->setEnabled(true);
        ui->scanSerialBtn->setEnabled(true);
        //调整串口控制按钮的文字提示
        ui->openSerialBtn->setText(QString("打开串口"));
        ui->openSerialBtn->setStyleSheet("background-color: rgb(0, 255, 255);\
                                       color: rgb(0,0,255);\
                                       border-color: rgb(255, 0, 0);");
        serialflag = 0;//串口关闭
    }
}
//建立连接
void SerialGetData::on_eConnectBtn_clicked()
{
   if(serialflag == 0){
       QMessageBox::information(NULL,"提示","串口未打开",QMessageBox::Ok);
       return;
   }
   ui->eConnectBtn->setEnabled(false);
   ui->eDisconnectBtn->setEnabled(true);
   ui->eConnectBtn->setStyleSheet("background-color: rgb(255, 255, 255);\
                                  color: rgb(255, 0, 0);\
                                  border-color: rgb(255, 0, 0);");
   ui->eDisconnectBtn->setStyleSheet("background-color: rgb(0, 255, 255);\
                                  color: rgb(0, 0, 255);\
                                  border-color: rgb(255, 0, 0);");
   /*
   if(connectflag == 1){
       QMessageBox::information(NULL,"提示","已建立连接",QMessageBox::Ok);
       return;
   }*/
   connect(serialport,&QSerialPort::readyRead,this,&SerialGetData::Serial_read);
   //connectflag = 1;//已为信号绑定槽函数
}
//关闭连接
void SerialGetData::on_eDisconnectBtn_clicked()
{
    if(serialflag == 0){
        QMessageBox::information(NULL,"提示","串口未打开",QMessageBox::Ok);
        return;
    }
    ui->eDisconnectBtn->setEnabled(false);
    ui->eConnectBtn->setEnabled(true);
    ui->eConnectBtn->setStyleSheet("background-color: rgb(0, 255, 255);\
                                   color: rgb(0, 0, 255);\
                                   border-color: rgb(255, 0, 0);");
    ui->eDisconnectBtn->setStyleSheet("background-color: rgb(255, 255, 255);\
                                   color: rgb(255, 0, 0);\
                                   border-color: rgb(255, 0, 0);");
    /*
    if(connectflag == 0){
        QMessageBox::information(NULL,"提示","未建立连接",QMessageBox::Ok);
        return;
    }*/
    disconnect(serialport,&QSerialPort::readyRead,this,&SerialGetData::Serial_read);
    //connectflag = 0;//已解除绑定
    ui->lcdTemp->display(0);
    ui->lcdHuim->display(0);
}
//定时器,定时获取时间展示
void SerialGetData::timerEvent(QTimerEvent *event)
{
    //获取当前时间
    QDate currentdate = dateTime.currentDateTime().date();
    QTime currenttime = dateTime.currentDateTime().time();
    if(event->timerId() == timerid){
        ui->dateTimeEdit->setDate(currentdate);
        ui->dateTimeEdit->setTime(currenttime);
    }
}

serialdisplay.h文件:

#ifndef SERIALDISPLAY_H
#define SERIALDISPLAY_H

#include <QMainWindow>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QDateTime>
#include "TOOL/myexcption.h"

QT_BEGIN_NAMESPACE
namespace Ui { class SerialDisplay; }
QT_END_NAMESPACE

class SerialDisplay : public QMainWindow
{
    Q_OBJECT

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

    QByteArray serial_recv_Data() throw(MyExcption);

    bool serial_sendData(QString data);

    int getNumsFromStr(QString data);//获取字符串中数字部分

private slots:
    void on_scanSerialBtn_clicked();

    void Serial_read();

    void on_openSerialBtn_clicked();

    void on_eConnectBtn_clicked();

    void on_eDisconnectBtn_clicked();

    void timerEvent(QTimerEvent *event)override;

private:
    Ui::SerialGetData *ui;

    QSerialPort *serialport;

    int timerid;

    int serialflag;

    QDateTime dateTime;
};
#endif // SERIALDISPLAY_H

上面两个文件用到了,自定义异常处理:
myexcption.cpp文件:

#include "myexcption.h"

MyExcption::MyExcption(const char *err)
{
    this->error = err;//
}

myexcption.h文件:

#ifndef MYEXCPTION_H
#define MYEXCPTION_H


class MyExcption
{
public:
    MyExcption(const char *err);
    const char* what() throw(){
        return this->error;
    }
private:
    const char *error;
};

#endif // MYEXCPTION_H

代码中完成了实时时间的显示,和温湿度显示,温湿度部分用到了json格式数据的处理,要求下位机传上来的数据必须是json格式的数据,接下来说说json格式数据:
json格式大概分为三种:
第一种,也是最简单的(JSON对象):

{
	"name":"smith",
	"age":30,
	"sex":}

第二种 (对象的属性也可以是JSON对象):

{
	"name":"smith",
	"age":18
	"sex":"school":{
		"sname":"南京大学",
		"address":"南京市鼓楼区汉口路22号"
	}
}

第三种(对象数组):

[
	{
		"title":"Java实战经典开发",
		"edition":3,
		"author":["smith","尼古拉斯","斯巴达"]
	},
	{
		"title":"Oracle实战经典开发",
		"edition":3,
		"author":["smith","尼古拉斯","斯巴达"]
	},
	{
		"title":"Vue实战经典开发",
		"edition":5,
		"author":["smith","尼古拉斯","斯巴达"]
	}
]

其实JSON数据就是一段字符串而已,只不过有不同意义的分隔符将其分割开来而已,我们看上面的符号,里面有[] ,{}等符号,其中

1 []中括号代表的是一个数组;
2 {}大括号代表的是一个对象
3 双引号“”表示的是属性值
4 冒号:代表的是前后之间的关系,冒号前面是属性的名称,后面是属性的值,这个值可以是基本数据类型,也可以是引用数据类型。
而我们只是简单的上传温湿度,所以只需要用到第一种:
可以规定数据格式为:

{
	"温度":"18℃",
	"湿度":"70%"
}

而下位机只需要按照这种格式发数据到串口即可。

实现效果图:

在这里插入图片描述
对之前的控件进行了颜色修改和美化。
完整工程链接:
https://pan.baidu.com/s/1wAj_yo7y_HXPXzXAevLImw 提取码: wpvp


版权声明:本文为weixin_56565733原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。