1. 程式人生 > >【QT】編寫一個簡易的串列埠軟體

【QT】編寫一個簡易的串列埠軟體

簡述

QT學了一點發現學不進去,索性看看能不能直接擼個程式,於是就有了這個簡易的串列埠軟體…


分析需求

這是XCOM串列埠收發軟體,以此為例

在這裡插入圖片描述

目的很明確:

  1. 串列埠列表要能顯示所有已經接上PC上的COM
  2. 以我們的配置115200-8-1-N開啟串列埠
  3. 能收、發

新建一個Qt專案

既然用到串列埠,那麼我們就要在.pro裡增加串列埠的庫

QT       += serialport  # 串列埠庫

以及mainwindow.h裡新增標頭檔案

#include "qDebug.h"
// 除錯輸出用 #include <QtSerialPort/QSerialPort> // 提供訪問串列埠的功能 #include <QtSerialPort/QSerialPortInfo> // 提供系統中存在的串列埠的資訊

先拖控制元件把ui整出來

在這裡插入圖片描述

要注意的是,除了Label,其他的控制元件都記得命名呀,當然你用預設的也行…

開啟串列埠功能

1. 選擇串列埠功能

我們要開啟的串列埠,並不希望他是臨時變數,我們用到的地方多呢!所以就在標頭檔案先定義一個私有成員變數

QSerialPort *
serial;

我們希望他在開啟的時候就能載入所有的COM,所以我們在MainWindow裡做這一步,建立視窗的時候就初始化COM列表,順帶著我們為serial開闢空間

	// 我們在 ui->setupUi(this); 後面加入這些程式碼即可
	
    // 新增所有串列埠到comboBox裡(串列埠號的名字叫comboBox_COMx)
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        ui->comboBox_COMx->addItem
(info.portName()); //+':'+info.description() } serial = new QSerialPort(this);

我們用info去引用一個個的從串列埠的列表裡取出COM的資訊,如果有,我們就把他加入到【串列埠選擇】的下拉欄裡

這個時候如果我們執行,就會發現【串列埠選擇】裡已有了PC上已插入的COM裝置。

2. 開啟串列埠

首先為按鈕【開啟串列埠】新增槽,然後在槽函式裡編寫我們的程式碼:
打開了 總不能就一直開吧,所以我們做了一個開關,通過按鈕上的字來判斷是開還是關

 // 點選開啟串列埠
    if(ui->pushButton_OpenSerial->text()==tr("開啟串列埠"))
    {
        ui->pushButton_OpenSerial->setText(tr("關閉串列埠"));
    }
    else
    {
        ui->pushButton_OpenSerial->setText(tr("開啟串列埠"));
        
    }

這個時候我們執行,會發現開啟串列埠的按鈕已經可以正常工作了,點選開啟,會顯示關閉按鈕。點選關閉,會顯示開啟按鈕。

按鈕做完了,接著就要做功能了,如果要關閉,我們就可以呼叫serial->close();來關閉串列埠,如果要開啟,我們就去開啟,不過不知道能不能開啟(可能別的程式正在使用),所以加個if判斷一下。

    // 點選開啟串列埠
    if(ui->pushButton_OpenSerial->text()==tr("開啟串列埠"))
    {
        ui->pushButton_OpenSerial->setText(tr("關閉串列埠"));
        // 設定串列埠號(以當前顯示的COM號為要開啟的串列埠)
        serial->setPortName(ui->comboBox_COMx->currentText());
        // 以讀寫方式開啟串列埠
        if(serial->open(QIODevice::ReadWrite))
        {
            qDebug() << "Open OK" << endl;
        }
        else
        {
            qDebug() << "Open Failed" << endl;
        }
    }
    else
    {
        ui->pushButton_OpenSerial->setText(tr("開啟串列埠"));
        serial->close();
    }

現在我們執行一下,看除錯資訊知道目前都是正確的。能正確開關串列埠。

既然打開了串列埠,那麼我們就配置一下串列埠的引數,因為只是一個簡易的,所以就固定死串列埠為8-1-N,把一下程式碼放入成功開啟串列埠的if內。

//設定波特率
            serial->setBaudRate(115200);
            //設定資料位
            serial->setDataBits(QSerialPort::Data8);
            //設定校驗位
            serial->setParity(QSerialPort::NoParity);
            //設定流控制
            serial->setFlowControl(QSerialPort::NoFlowControl);
            //設定停止位
            serial->setStopBits(QSerialPort::OneStop);

這就完了嗎?還沒有,我們配置好了串列埠,如何進行接收呢?我們利用的是QT的訊號與槽機制,當串列埠收到資料,就會發出readyRead()訊號,所以我們利用這個訊號,建立一個槽用來接收字元。

先在標頭檔案定義private slots函式 void recv_data(void);

然後我們緊接著剛剛配置串列埠後面寫上connect()來連線訊號與槽

connect(serial, SIGNAL(readyRead()), this, SLOT(recv_data()));

這樣我們點選開啟訊號與槽後,就會配置串列埠,啟用訊號與槽。

現在萬事俱備,只欠東風,我們繼續寫槽函式recv_data()來接收串列埠的資料

仔細思考一下,平時在用串列埠軟體的時候,有新的資料發來會自動出現在舊的資料後面。
所以我們的做法是,先暫時儲存顯示的資料,然後把讀到的資料加在後面,再清除顯示的資料,把合成的資料再顯示出來,就達到我們的效果了。

void MainWindow::recv_data(void)
{
    qDebug() << "Recv Data" << endl;
    QByteArray buf = serial->readAll();
    if(!buf.isEmpty())
    {
        QString str = ui->textEdit_ReceiveMsg->toPlainText();
        str+=tr(buf);
        ui->textEdit_ReceiveMsg->clear();
        ui->textEdit_ReceiveMsg->append(str);
    }
}

這個時候我們執行一下,開啟串列埠,用微控制器來發資料,實測已經可以正確顯示在螢幕上了

3. 傳送資料

對於傳送資料,有了剛才的經驗就比較簡單了。
為UI的【傳送】鍵新增一個槽函式,當我們點選【傳送】時,串列埠就傳送我們寫入的資訊。
所以我們在傳送的槽函式內寫入:

	// 先讀取我們寫入的文字
	QString str = ui->textEdit_SendMsg->toPlainText().toLatin1();
    qDebug() << str <<endl;
    // 寫入
    serial->write(str.toLatin1());

串列埠的write函式的引數有兩個

  1. 第一個是char *型別,所以我們要str轉char *,用的.toLatin1()來轉換。
  2. 第二個是傳入字元最大數量(我選擇不設定所以沒填)

此時我們再執行,發現收發都正常了。

附上我的完整程式碼(不包含UI,看控制元件的英文名字應該能對上圖片)

mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtSerialPort/QSerialPort>         // 提供訪問串列埠的功能
#include <QtSerialPort/QSerialPortInfo>     // 提供系統中存在的串列埠的資訊

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_OpenSerial_clicked();
    void recv_data(void);

    void on_pushButton_SendMsg_clicked();

    void on_pushButton_ClearRecv_clicked();

private:
    Ui::MainWindow *ui;

    QSerialPort *serial;
};

#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "qDebug.h"
#include "QObject"
#include "QTimer"

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

    // 初始化
    // 新增所有串列埠到comboBox裡(串列埠號的名字叫comboBox_COMx)
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        ui->comboBox_COMx->addItem(info.portName()); //+':'+info.description()
    }
    serial = new QSerialPort(this);
}

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

void MainWindow::recv_data(void)
{
    qDebug() << "Recv Data" << endl;
    QByteArray buf = serial->readAll();
    if(!buf.isEmpty())
    {
        QString str = ui->textEdit_ReceiveMsg->toPlainText();
        str+=tr(buf);
        ui->textEdit_ReceiveMsg->clear();
        ui->textEdit_ReceiveMsg->append(str);
    }
}

void MainWindow::on_pushButton_OpenSerial_clicked()
{
    // 點選開啟串列埠
    if(ui->pushButton_OpenSerial->text()==tr("開啟串列埠"))
    {
        ui->pushButton_OpenSerial->setText(tr("關閉串列埠"));
        // 開闢空間
        //serial = new QSerialPort(this);

        // 設定串列埠號
        serial->setPortName(ui->comboBox_COMx->currentText());

        // 以讀寫方式開啟串列埠
        if(serial->open(QIODevice::ReadWrite))
        {
            qDebug() << "Open OK" << endl;
            // 此處寫死為 8-1-N
            //設定波特率
            serial->setBaudRate(115200);
            //設定資料位
            serial->setDataBits(QSerialPort::Data8);
            //設定校驗位
            serial->setParity(QSerialPort::NoParity);
            //設定流控制
            serial->setFlowControl(QSerialPort::NoFlowControl);
            //設定停止位
            serial->setStopBits(QSerialPort::OneStop);

            connect(serial, SIGNAL(readyRead()), this, SLOT(recv_data()));
        }
        else
        {
            qDebug() << "Open Failed" << endl;
        }

    }
    else
    {
        ui->pushButton_OpenSerial->setText(tr("開啟串列埠"));
        serial->close();
    }
}

void MainWindow::on_pushButton_SendMsg_clicked()
{
    QString str = ui->textEdit_SendMsg->toPlainText().toLatin1();
    qDebug() << str <<endl;
    serial->write(str.toLatin1());
}

void MainWindow::on_pushButton_ClearRecv_clicked()
{
    ui->textEdit_ReceiveMsg->clear();
}

收尾

其實還有很多其他的功能可以加進去,我就多做了一個【清除接收】…
甚至還有BUG…就是隻有開啟軟體的時候才載入已有COM口,所以還要加上點選下拉框的時候就掃描更新一遍。
此時的波特率、停止位啥的都是寫死的,所以真正要做完的話,還需要把這些控制元件都做上
還有等等等等…
哈哈 主要功能做出來了,其他的往上新增就好了。
打包exe教程 https://blog.csdn.net/syzdev/article/details/80860619
over…