我遇到个需求:

目标:开发一个串口上位机软件,用于和单片机等进行串口通信。

内部原理:调用串口类,接收到数据进行解析和显示

效果:提供了友好的简洁美观界面,下面就是软件的界面:(支持hex十六进制显示和发送)

代码简单展示

核心代码如下

头文件:

#ifndef MAINWINDOWUART_H
#define MAINWINDOWUART_H

#include <QMainWindow>

class QSerialPort;
class QLabel;


namespace Ui/*Uart*/ {
class MainWindowUart;
}

class MainWindowUart : public QMainWindow
{
    Q_OBJECT

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

    bool statusPort=false;
    QSerialPort *serial;
    QLabel *lableStatusBar;
    QList<QByteArray> listByte;

    int hex2Str(QByteArray buf, QString &hex);
    int Str2HexMe(QString str, QByteArray &data);
    void deletStrBlank(QString &s);

private slots:

    void read32ByteData();

    void on_pushButtonFresh_clicked();

    void on_pushButtonOpen_clicked();

    void on_pushButtonSend_clicked();

    void on_pushButtonClearRxd_clicked();

    void on_pushButtonClearTxd_clicked();

    void on_actionS_triggered();

    void on_action_T_triggered();

    void on_action_D_triggered();
    
private:
    Ui/*Uart*/::MainWindowUart *ui;
};

#endif // MAINWINDOWUART_H

源文件:

#include "mainwindowuart.h"
#include "ui_mainwindowuart.h"
#include "mainwindow.h"
#include "mainwindowdraw.h"

#include <QTime>
#include <QFileDialog>
#include <QFileInfo>
#include <QTextStream>
#include <QTextCodec>
#include <QMessageBox>
#include <QGraphicsOpacityEffect>
#include <QIcon>
#include <QInputDialog>
#include <QMessageBox>
#include <QDebug>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QLabel>
#include <QFile>

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

    // 设置波特率下拉菜单默认显示第三项
    ui->BaudBox->setCurrentIndex(3);
    on_pushButtonFresh_clicked();
    // 关闭发送按钮的使能
    qDebug() << tr("界面设定成功!")<<endl;
    lableStatusBar=new QLabel(this);
    // 初始化类中定义的这个状态栏标签,由于类里面无法指定其父对象,所以在这个构造函数这里指定
    QString port="Not Open";
    // 状态栏显示串口未打开信息
    lableStatusBar->setText("串口打开状态:"+QObject::tr("<font color=red>%1</font>").arg(port));
    lableStatusBar->setFont(QFont("nihao",8));
    ui->statusbar->addWidget(lableStatusBar);

}
/**********************************************************************************************
* 读取接收到的数据,注:为了和前面函数名兼容,虽然这个槽函数名字还是读取一字节,但是由于已经
* 改了接收缓冲区为默认值(32B),所以这里不会一字节就读一次
**********************************************************************************************/
void MainWindowUart::read32ByteData()
{
    QByteArray buf;
    buf = serial->readAll();
    if(!buf.isEmpty())
    {
        // 如果勾选了Hex显示
        if(ui->checkBoxHexRxd->isChecked())
        {
            QString strHex = ui->textEditRxd->toPlainText();
            QString str1Hex;
            hex2Str(buf,str1Hex);
            strHex=strHex+str1Hex;
            ui->textEditRxd->clear();
            ui->textEditRxd->setText(strHex);
        }
        else
        {
            //说明本次刚好接收到换行符的一半,这时先别显示,否则会多一个换行
            if(buf.at(buf.size()-1) == '\r')    
            {
                listByte.append(buf);
            }
            else
            {
                QString str = ui->textEditRxd->toPlainText();
                for (int i = 0; i < listByte.size(); ++i) {
                    str=str+QString(listByte.at(i));                    
                }
                str=str+QString(buf);
                listByte.clear();
                ui->textEditRxd->clear();
                ui->textEditRxd->setText(str);                
            }

            
//            ui->textEditRxd->append(QString(buf));  //append会自动换行
            qDebug()<<QString(buf);
        }
    }
    buf.clear();
}
MainWindowUart::~MainWindowUart()
{
    delete ui;
}

void MainWindowUart::on_pushButtonFresh_clicked()
{
    ui->PortBox->clear();
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QSerialPort serial1;
        serial1.setPort(info);
        ui->PortBox->addItem(serial1.portName());
    }
    ui->PortBox->setCurrentIndex(ui->PortBox->count()-1);

}

void MainWindowUart::on_pushButtonOpen_clicked()
{
    if(statusPort==false)
    {
        if(ui->PortBox->currentText() == "")
            return;
        serial = new QSerialPort;
        // 设置串口名
        serial->setPortName(ui->PortBox->currentText());
        // 打开串口,成功才能往下走
        if(!serial->open(QIODevice::ReadWrite))
        {
            if(serial->error() == QSerialPort::PermissionError)
            {
                QMessageBox::critical(this,"erro","串口打开失败,该串口已被占用");
                serial->deleteLater();
                return;
            }
        }
        statusPort=true;
        QIcon icoButtonOpenPort(":/new/prefix1/openPort.PNG");
        ui->pushButtonOpen->setIcon(icoButtonOpenPort);
        ui->pushButtonOpen->setIconSize(QSize(71,37));
        // 设置波特率
        serial->setBaudRate(ui->BaudBox->currentText().toInt());
        // 设置数据位数
        switch(ui->BitNumBox->currentIndex())
        {
            case 0: serial->setDataBits(QSerialPort::Data8); break;
            default: break;
        }
        // 设置奇偶校验
        switch(ui->ParityBox->currentIndex())
        {
            case 0: serial->setParity(QSerialPort::NoParity); break;
            default: break;
        }
        // 设置停止位
        switch(ui->StopBox->currentIndex())
        {
            case 0: serial->setStopBits(QSerialPort::OneStop); break;
            case 1: serial->setStopBits(QSerialPort::TwoStop); break;
            default: break;
        }
        // 设置流控制
        serial->setFlowControl(QSerialPort::NoFlowControl);

        QString port=ui->PortBox->currentText()+" 波特率:"+ui->BaudBox->currentText()+" 数据位:"+ui->BitNumBox->currentText()+" 停止位:"+ui->StopBox->currentText();
        lableStatusBar->setText("串口打开状态:"+QObject::tr("<font color=green>%1</font>").arg(port));
        lableStatusBar->setFont(QFont("nihao",8));
        ui->statusbar->addWidget(lableStatusBar);
        // 关闭设置菜单使能
        ui->PortBox->setEnabled(false);
        ui->BaudBox->setEnabled(false);
        ui->BitNumBox->setEnabled(false);
        ui->ParityBox->setEnabled(false);
        ui->StopBox->setEnabled(false);
        ui->pushButtonFresh->setEnabled(false);
        ui->pushButtonSend->setEnabled(true);
        // 连接信号槽
        QObject::connect(serial, &QSerialPort::readyRead, this, &MainWindowUart::read32ByteData);

    }
    else
    {
        statusPort=false;
        QIcon icoButtonOpenPort(":/new/prefix1/closePort.PNG");
        ui->pushButtonOpen->setIcon(icoButtonOpenPort);
        ui->pushButtonOpen->setIconSize(QSize(71,37));

        // 关闭串口
        serial->clear();
        serial->close();
        serial->deleteLater();
        // 恢复设置使能
        ui->PortBox->setEnabled(true);
        ui->BaudBox->setEnabled(true);
        ui->BitNumBox->setEnabled(true);
        ui->ParityBox->setEnabled(true);
        ui->StopBox->setEnabled(true);
        ui->pushButtonFresh->setEnabled(true);
        ui->pushButtonSend->setEnabled(false);
        QString port="Not Open";
        lableStatusBar->setText("串口打开状态:"+QObject::tr("<font color=red>%1</font>").arg(port));
        lableStatusBar->setFont(QFont("nihao",8));
        ui->statusbar->addWidget(lableStatusBar);
    }

}

void MainWindowUart::on_pushButtonSend_clicked()
{
    if(statusPort==true)
    {
        if(ui->checkBoxHexSend->isChecked())
        {
            QString str=ui->textEditTxd->toPlainText();
            QByteArray data;            //由于char *类型是有符号型的,只能表示0-127,所以得
                                        //用QByteArray类型,刚好串口write刚好有这个重载函数
            int len=Str2HexMe(str,data);
            serial->write(data,len);    //因为发送的char里面可能含有0x00,所以
        }
        else
        {
            QTextCodec *codec=QTextCodec::codecForName("UTF-8");
            serial->write(codec->fromUnicode(ui->textEditTxd->toPlainText()));
        }
    }
}


/**********************************************************************************************
* @brief            把十六进制数转为字符串格式
*
* @param_i  buf     输入十六进制数组
* @param_o  hex     输出字符串
* @return           输入的十六进制个数
* @note:            (1).此函数用于窗口的十六进制显示模式显示接收到的数据
**********************************************************************************************/
int MainWindowUart::hex2Str(QByteArray buf,QString &hex)
{

    for (int i = 0; i < buf.size(); ++i)
    {
        if(buf[i]==0x00)
        {
            hex=hex+"00";
        }
        else
        {
            hex=hex+QString("%1").arg((unsigned char)buf[i],2,16,QChar('0')).toUpper();
        }
        hex=hex+" ";
    }
    return buf.size();
}

/**********************************************************************************************
* @brief            把字符串转为十六进制数格式
*
* @param_i  str     输入十六进制字符串
* @param_o  data    输出十六进制数
* @return           输出的十六进制个数
* @note:            (1).此函数用于窗口的十六进制发送模式发送数据
**********************************************************************************************/
int MainWindowUart::Str2HexMe(QString str,QByteArray &data)
{
    deletStrBlank(str);
    int len=str.length();
    bool ok;
    for (int i = 0; i < len/2; ++i) {
        data[i]=str.mid(2*i,2).toInt(&ok,16);
    }
    return len/2;
}

/**********************************************************************************************
* @brief            删除字符串中空格
*
* @param_i  s       输入字符串
* @note:            (1).此函数用于把一个字符串中的空格都删除掉,使得我们更容易处理数据
**********************************************************************************************/
void MainWindowUart::deletStrBlank(QString &s)
{
    int len=s.length();
    int j=0;
    for (int i = 0; i < len; ++i) {
        if(!s.at(i).isSpace())
        {
            s[j]=s[i];
            ++j;
        }
    }
    s.remove(j,s.length()-j);   //经过计算和验证这是完全正确的
}

void MainWindowUart::on_pushButtonClearRxd_clicked()
{
    ui->textEditRxd->clear();
}

void MainWindowUart::on_pushButtonClearTxd_clicked()
{
    ui->textEditTxd->clear();
    
}

void MainWindowUart::on_actionS_triggered()
{
    //  获取系统时间
    QDateTime time=QDateTime::currentDateTime();
    QString strTime=time.toString("yy-MM-dd&&hh-mm-ss");
    //  打开文件选择对话框
    QString fileName = QFileDialog::getSaveFileName(this,
          tr("保存串口接收数据"), "串口接收数据"+strTime, tr("文本文档 (*.txt)"));
    if(!fileName.isNull())
        //  如果确实选取了目标保存文件路径,就保存,否则就是不保存
    {
        //  以只写方式打开,如果文件不存在,那么会创建该文件
        QFile file(fileName);
        if (!file.open(QIODevice::WriteOnly  | QIODevice::Text))
            qDebug() << file.errorString();
        QTextStream out(&file);
        out<<ui->textEditRxd->toPlainText();
        file.close();
    }
}

void MainWindowUart::on_action_T_triggered()
{
    (new MainWindow())->show();
    close();
}

void MainWindowUart::on_action_D_triggered()
{
    (new MainWindowDraw())->show();
    close();
}

如果有需要学习和使用这个软件源码的,通过我的QQ邮箱:2488890051@qq.com(马上就能回复)联系我,算是对我的劳动成果的鼓励,开发花了大量的时间和精力,作为学生,着实不容易。 (大佬们,记得关注我的博客,且下面评论留言喔,谢谢哈~)

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐