QT 篇 QT上位机串口编程

最近因为项目需要,需要用到上位机,通过串口与上位机进行通讯,来上传和下发一些数据以及控制指令,所以用QT写了一个上位机,并记录下来,免得到时候要用又到处翻资料。

QT版本:QT Creater 4.80
硬件:stm32 + 串口转ttl模块

默认安装好QT了

1.新建工程

选择Qt Widgets

然后下一步

java开发上位机串口通信 上位机串口程序_qt


自己填一个项目名字和项目路径

java开发上位机串口通信 上位机串口程序_java开发上位机串口通信_02


我只安装了这一个,我就选了这个

java开发上位机串口通信 上位机串口程序_串口_03

这里主要是一些基类的选择
这里我选择QMainWindow

类名,文件名什么的,你们看看要不要改名

java开发上位机串口通信 上位机串口程序_串口_04


项目管理这里版本控制选择无

java开发上位机串口通信 上位机串口程序_c++_05


然后就完成了创建了

2.添加类和库文件

QT5自带有串口的封装库 QSerialPort ,我们要用的相关的函数,所以要在.pro文件里面添加一样代码,在QT += core gui的基础上添加serialport

QT       += core gui serialport

在mainwindo.h里添加串口的一些库

#include <QSerialPort>			//访问串口的库
#include <QSerialPortInfo>		//查询串口信息的库
#include <QDebug>				//用于调试打印输出的库
#include <QTimer>				//定时器的库
#include <QTime>				//时间的库
#include <QDate>				//日期的库
#include <QMessageBox>			//一个小的弹窗库

其实上面这些这需要添加最顶上的了两个就够了,其余的是我有其他的用处就加了进去

3.设计上位机页面

这是我自己设计好的上位机

java开发上位机串口通信 上位机串口程序_单片机_06


布局上参考了一下正点原子的XCOM串口软件

java开发上位机串口通信 上位机串口程序_单片机_07

上位机页面
先设置好窗口的大小固定位800x480
窗口最大和最小都是800x480 这样一来就没办法拉伸了,就不会影响美观

java开发上位机串口通信 上位机串口程序_c++_08


然后可以按照我的设计页面来摆放一些控件

当然你们也可以不用这么多控件,因为实际上来说,需要修改的串口参数其实就只有名称和波特率而已,其余的是可以固定不做改变的

看需求

对象的名称最好改一下,不然到时候编写代码的时候你会不记得哪个代表哪一个部件,我的部件的名称

java开发上位机串口通信 上位机串口程序_java开发上位机串口通信_09


对象名称

java开发上位机串口通信 上位机串口程序_c++_10


摆放好这些部件后

分别添加波特率

停止位

数据位

奇偶校验等等

java开发上位机串口通信 上位机串口程序_c++_11


然后设置Combox的初始值我把串口的波特率设置为115200是初始值

其余的你们看着来

CurrentIndex的索引和数组一样,从0开始

java开发上位机串口通信 上位机串口程序_qt_12

4.添加函数 扫描串口和初始化串口设置

在mainwindow.h里的
private:

//定时器
    QTimer *timer;
    // 串口对象
    QSerialPort *serialport;
    //扫描串口
    void scanSerialPort();
    //初始化
    void serialPortInit();

public

int btn_on_off = 0;

    int btn_state = 0;

mainwindow.cpp文件

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
	//对象实例化
    serialport = new QSerialPort(this);
	
    timer = new QTimer(this);
	
    scanSerialPort();

}

void MainWindow::scanSerialPort()
{
	//查询可用的串口信息
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        {
        	//添加串口到下拉菜单
             ui->cbx_setPortName->addItem(info.portName());
        }
        qDebug()<<"已发现串口:"<<ui->cbx_setPortName->currentText();
    }
}



void MainWindow::serialPortInit()
{
    //设置串口名称
    serialport->setPortName(ui->cbx_setPortName->currentText());
   qDebug()<<"串口名称:"<<serialport->portName();

   if (!serialport->open(QIODevice::ReadWrite))
   {
		qDebug()<<"错误,串口无法打开,可能被占用!";
		QMessageBox::about(this,"错误","串口无法打开,可能被占用!");
		serialport->close();
		return ;
   }

    //波特率
    serialport->setBaudRate(ui->cbx_setBaudRate->currentText().toInt());
    qDebug()<<"波特率:"<<ui->cbx_setBaudRate->currentText().toInt();
    //停止位
    switch (ui->cbx_setStopBits->currentText().toInt())
    {
    case 1 :  serialport->setStopBits(QSerialPort::OneStop); break;
    case 2 :  serialport->setStopBits(QSerialPort::TwoStop); break;
        default: break;
    }
    qDebug()<<"停止位:"<<serialport->stopBits();
    //数据位
    switch (ui->cbx_setDataBits->currentText().toInt())
    {
    case 5 :  serialport->setDataBits(QSerialPort::Data5); break;
    case 6 :  serialport->setDataBits(QSerialPort::Data6); break;
    case 7 :  serialport->setDataBits(QSerialPort::Data7); break;
    case 8 :  serialport->setDataBits(QSerialPort::Data8); break;
        default: break;
    }
    //奇偶位
     switch (ui->cbx_setParity->currentIndex())
    {
    case 0 :  serialport->setParity(QSerialPort::NoParity); break;
    case 1 :  serialport->setParity(QSerialPort::OddParity); break;
    case 2 :  serialport->setParity(QSerialPort::EvenParity); break;
        default: break;
    }
    qDebug()<<"奇偶位:"<<serialport->parity();

    serialport->setFlowControl(QSerialPort::NoFlowControl) ;


}

然后打开ui文件

找到打开串口这个PushButton

转到槽函数

就会自动帮你生成信号函数和槽函数

java开发上位机串口通信 上位机串口程序_串口_13


打开串口的槽函数

void MainWindow::on_btn_open_close_clicked()
{
         if (btn_on_off == 0)
         {

             ui->cbx_setPortName->setEnabled(false);
             ui->cbx_setBaudRate->setEnabled(false);
             ui->cbx_setDataBits->setEnabled(false);
             ui->cbx_setStopBits->setEnabled(false);
             ui->cbx_setParity->setEnabled(false);
             ui->btn_open_close->setText("关闭串口");
             qDebug()<<"打开串口:";
             serialPortInit();
         }

        if (btn_on_off == 1)
        {

            serialport->close();
            ui->cbx_setPortName->setEnabled(true);
            ui->cbx_setBaudRate->setEnabled(true);
            ui->cbx_setDataBits->setEnabled(true);
            ui->cbx_setStopBits->setEnabled(true);
            ui->cbx_setParity->setEnabled(true);
            ui->btn_open_close->setText("打开串口");
        }
        btn_on_off = !btn_on_off;
}

到这里基本上就可以成功打开串口了

java开发上位机串口通信 上位机串口程序_java开发上位机串口通信_14

5. 读串口函数 serialport->readAll();

在mainwindow.h 的private slots: 添加

void serialPortReadyRead();

    void serialPortWrite();

对应的槽函数

void MainWindow::serialPortReadyRead()
{
    QByteArray temp = serialport->readAll();  
     QString str = ui->textEdit_rx->toPlainText();
     str =  QString::fromLocal8Bit(temp);//显示中文
      ui->textEdit_rx->append(str);
}

此时要手动链接信号函数和槽函数
mainwindow.cpp的MainWindow::MainWindow(QWidget *parent) :添加

connect(serialport,SIGNAL(readyRead()),this,
                              SLOT(serialPortReadyRead()));
                              
     //SIGNAL 是信号函数,QT的串口自带了
     //SLOT是自己定义的槽函数

按着ctrl点击readyRead(),就可以跳转到这里:

java开发上位机串口通信 上位机串口程序_qt_15

6.写串口函数 serialport->write(buff);

void MainWindow::serialPortWrite()
{
    QByteArray buff;
	//判断是否非空
    if(!ui->textEdit_tx->toPlainText().isEmpty())
    {
        buff = ui->textEdit_tx->toPlainText().toLocal8Bit();//可以写中文
        serialport->write(buff);
    }
}

把serialPortWrite()关联到发送的槽函数下
顺便把清空接收和清空发送也关联起来

void MainWindow::on_btn_clear_rx_clicked()
{
    ui->textEdit_rx->clear();
}

void MainWindow::on_btn_clear_tx_clicked()
{
    ui->textEdit_tx->clear();
}

void MainWindow::on_btn_sent_clicked()
{
    serialPortWrite();
}

接下来就是测试结果

第一个是stm32打印的上来的串口信息

第二个是自发自收(usb转串口的tx和rx短接)

分割线:=======================================================================================
2022-11-04新增修订

评论区有小伙伴提出没有自刷新串口的功能,以及str += QString::fromLocal8Bit(temp);

现在去掉“+”号,变成str = QString::fromLocal8Bit(temp);

在原有的基础上

增加一个函数

mainwindows.cpp

void MainWindow::reflash_com()
{

    ui->cbx_setPortName->clear();//清除选择串口下拉栏添加的所有项目
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        {
            ui->cbx_setPortName->addItem(info.portName());
        }
    }
    ui->cbx_setPortName->setCurrentIndex(choose_index);
    qDebug()<<"choose_index:"<<choose_index;
}

此外增加一个变量choose_index 用于记录上次选择串口的项目index

函数和变量记得在对应的.h文件增加声明

同时把reflash_com()放到500ms定时器里面

void MainWindow::timer_500ms()
{
    QTime time = QTime::currentTime();
    QDate date = QDate::currentDate();
    ui->label_Date_Time->setText(date.toString("yyyy-MM-dd") +" "+ time.toString("hh:mm:ss"));
    ui->textEdit_rx->setFontPointSize(ui->cbx_setFontPointSize->currentText().toInt());
    
    if (btn_on_off == 0)//判断串口处于未打开状态
    {
      choose_index = ui->cbx_setPortName->currentIndex();
      reflash_com();
    }
}

效果:

java开发上位机串口通信 上位机串口程序_串口_16