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

前言

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

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

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

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

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

学习交流请发邮件 [email protected]

串口功能

波特率设置

串口开关

串口异常检测

字符串/HEX收发

定时发送

接收数据分隔

协议组包

协议拆包

源代码

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include          // 提供访问串口的功能
#include       // 提供系统中存在的串口信息
#include 
#include 
#include 

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 


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;itableWidget->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(protocalData->at(0));
    if(static_cast(protocalData->at(0)) == preFix)
    {
        for(int i=1; ilength()-2; i++)
        {
            temp = static_cast(protocalData->at(i));
            crc += static_cast(protocalData->at(i));
        }
        temp = static_cast(protocalData->at(protocalData->length()-2));
        if(crc != static_cast(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(cmd1);
        QString hexStr1 = QString("%1").arg(str1, 2, 16, QLatin1Char('0')).toUpper();
        uint8_t str2 = static_cast(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  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(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; ilineEditData->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

Qt串口基本设置与协议收发_第1张图片

Qt串口基本设置与协议收发_第2张图片

代码框架概览

Qt串口基本设置与协议收发_第3张图片

演示视频

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

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

你可能感兴趣的:(Qt,qt,开发语言)