赞
踩
增加了一个PID调参的模块
PID算法之前在验证电机时是可用的
由于我没有实物可演示PID,所以写了个虚拟的PID,故调参效果看起来怪怪的
不过重点不在PID调参,而是命令的下发与接收
其他方面跟之前更新的波形显示没什么区别
Qt上位机PID调参
支持开始显示、停止显示
波形数据写到TXT文件
Excel、Python或MATLAB对数据进行进一步地分析
- QVector<QString> waveArray;
-
- QDateTime curTime = QDateTime::currentDateTime();
- QString date = curTime.toString("yyyy-MM-dd"); //设置显示格式
- QString time = curTime.toString("hh:mm:ss:zzz"); //设置显示格式
- waveArray<<date<<"\t"<<time<<"\t"<<QString::number(receiveParsingData)<<"\n";
每接受一帧数据则对数据进行保存
- void MainWindow::waveDataOutputToFile()
- {
- QFileDialog dlg(this);
-
- //获取内容的保存路径
- QString fileName = dlg.getSaveFileName(this, tr("Save As"), "./", tr("Text File(*.txt)"));
-
- if( fileName == "" )
- {
- return;
- }
-
- //内容保存到路径文件
- QFile file(fileName);
-
- //以文本方式打开
- if( file.open(QIODevice::WriteOnly | QIODevice::Text) )
- {
- QTextStream out(&file); //IO设备对象的地址对其进行初始化
-
- QVector<QString>::iterator iter;
- for (iter=waveArray.begin();iter!=waveArray.end();iter++)
- out << *iter << "\0";
-
- QMessageBox::warning(this, tr("Finish"), tr("Successfully save the file!"));
-
- file.close();
- waveArray.clear();
- }
- else
- {
- QMessageBox::warning(this, tr("Error"), tr("File to open file!"));
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
文件输出
Qt上位机波形显示与数据保存
将单片机ADC采集到的电压进行组包
Qt收到后进行拆包
- void MainWindow::parsingData(QByteArray *protocalData)
- {
- uchar preFix = 0xA5;
- uchar cmd1Rcv = 0xAB;
- uchar cmd2Rcv = 0x11;
- uchar crc = 0;
- uchar temp = 0;
- temp = static_cast<uchar>(protocalData->at(0));
- if(static_cast<uchar>(protocalData->at(0)) == preFix)
- {
- for(int i=1; i<protocalData->length()-2; i++)
- {
- temp = static_cast<uchar>(protocalData->at(i));
- crc += static_cast<uchar>(protocalData->at(i));
- }
- temp = static_cast<uchar>(protocalData->at(protocalData->length()-2));
- if(crc != static_cast<uchar>(protocalData->at(protocalData->length()-2)))
- {
- return;
- }
- uchar cmd1 = protocalData->at(2);
- uchar cmd2 = protocalData->at(3);
- uchar data = protocalData->at(4);
-
- if((cmd1==cmd1Rcv) && (cmd2 ==cmd2Rcv))
- {
- uchar uData[4];
- uData[0] = static_cast<uchar>(protocalData->at(4));
- uData[1] = static_cast<uchar>(protocalData->at(5));
- uData[2] = static_cast<uchar>(protocalData->at(6));
- uData[3] = static_cast<uchar>(protocalData->at(7));
- *(int *)&receiveParsingData = *(int *)&uData;
- ui->lcdnumber_vlotage->display(receiveParsingData);
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
LCD显示当前电压
Qt上位机显示动态数据
从协议中进行数据截取
波形显示
graphicsView放大缩小、原始大小
中文显示
发送/接受 帧计数
文件输出
支持时间戳模式自动换行
HEX接收时自动用空格分隔
1.一直都想要做一个Qt上位机,趁着这个周末有时间,动手写一下
2.comboBox没有点击的信号,所以做了一个触发的功能
3.Qt的数据类型很奇怪,转来转去的我也搞得很迷糊
4.给自己挖个坑,下一期做一个查看波形的上位机
有纰漏请指出,转载请说明。
学习交流请发邮件 1280253714@qq.com
波特率设置
串口开关
串口异常检测
字符串/HEX收发
定时发送
接收数据分隔
协议组包
协议拆包
widget.h
- #ifndef WIDGET_H
- #define WIDGET_H
-
- #include <QWidget>
- #include <QtSerialPort/QSerialPort> // 提供访问串口的功能
- #include <QtSerialPort/QSerialPortInfo> // 提供系统中存在的串口信息
- #include <QTime>
- #include <QtCharts>
- #include <QTimer>
-
- 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
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
widget.cpp
- #include "widget.h"
- #include "ui_widget.h"
- #include "newcombobox.h"
- #include <algorithm>
-
-
- 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;i<ui->tableWidget->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<uchar>(protocalData->at(0));
- if(static_cast<uchar>(protocalData->at(0)) == preFix)
- {
- for(int i=1; i<protocalData->length()-2; i++)
- {
- temp = static_cast<uchar>(protocalData->at(i));
- crc += static_cast<uchar>(protocalData->at(i));
- }
- temp = static_cast<uchar>(protocalData->at(protocalData->length()-2));
- if(crc != static_cast<uchar>(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<uint8_t>(cmd1);
- QString hexStr1 = QString("%1").arg(str1, 2, 16, QLatin1Char('0')).toUpper();
- uint8_t str2 = static_cast<uint8_t>(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<QSerialPortInfo> 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<uint8_t>(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; i<str3.length(); i+=2)
- {
- tmp = ui->lineEditData->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()));
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
ui
代码框架概览
串口上位机(基本设置/协议收发)演示_哔哩哔哩_bilibili
串口上位机(基本设置/协议收发)演示
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。