当前位置:   article > 正文

Qt串口基本设置与协议收发、波形显示_qt串口通信的接收与发送

qt串口通信的接收与发送

2023.10.23更新

2023.9.18更新

增加了一个PID调参的模块

PID算法之前在验证电机时是可用的

由于我没有实物可演示PID,所以写了个虚拟的PID,故调参效果看起来怪怪的

不过重点不在PID调参,而是命令的下发与接收

其他方面跟之前更新的波形显示没什么区别

Qt上位机PID调参_哔哩哔哩_bilibili

Qt上位机PID调参


2023.9.17更新

支持开始显示、停止显示

波形数据写到TXT文件

Excel、Python或MATLAB对数据进行进一步地分析

  1. QVector<QString> waveArray;
  2. QDateTime curTime = QDateTime::currentDateTime();
  3. QString date = curTime.toString("yyyy-MM-dd"); //设置显示格式
  4. QString time = curTime.toString("hh:mm:ss:zzz"); //设置显示格式
  5. waveArray<<date<<"\t"<<time<<"\t"<<QString::number(receiveParsingData)<<"\n";

每接受一帧数据则对数据进行保存 

  1. void MainWindow::waveDataOutputToFile()
  2. {
  3. QFileDialog dlg(this);
  4. //获取内容的保存路径
  5. QString fileName = dlg.getSaveFileName(this, tr("Save As"), "./", tr("Text File(*.txt)"));
  6. if( fileName == "" )
  7. {
  8. return;
  9. }
  10. //内容保存到路径文件
  11. QFile file(fileName);
  12. //以文本方式打开
  13. if( file.open(QIODevice::WriteOnly | QIODevice::Text) )
  14. {
  15. QTextStream out(&file); //IO设备对象的地址对其进行初始化
  16. QVector<QString>::iterator iter;
  17. for (iter=waveArray.begin();iter!=waveArray.end();iter++)
  18. out << *iter << "\0";
  19. QMessageBox::warning(this, tr("Finish"), tr("Successfully save the file!"));
  20. file.close();
  21. waveArray.clear();
  22. }
  23. else
  24. {
  25. QMessageBox::warning(this, tr("Error"), tr("File to open file!"));
  26. }
  27. }

文件输出

Qt上位机波形显示与数据保存_哔哩哔哩_bilibili

Qt上位机波形显示与数据保存


2023.9.15更新

将单片机ADC采集到的电压进行组包

Qt收到后进行拆包

  1. void MainWindow::parsingData(QByteArray *protocalData)
  2. {
  3. uchar preFix = 0xA5;
  4. uchar cmd1Rcv = 0xAB;
  5. uchar cmd2Rcv = 0x11;
  6. uchar crc = 0;
  7. uchar temp = 0;
  8. temp = static_cast<uchar>(protocalData->at(0));
  9. if(static_cast<uchar>(protocalData->at(0)) == preFix)
  10. {
  11. for(int i=1; i<protocalData->length()-2; i++)
  12. {
  13. temp = static_cast<uchar>(protocalData->at(i));
  14. crc += static_cast<uchar>(protocalData->at(i));
  15. }
  16. temp = static_cast<uchar>(protocalData->at(protocalData->length()-2));
  17. if(crc != static_cast<uchar>(protocalData->at(protocalData->length()-2)))
  18. {
  19. return;
  20. }
  21. uchar cmd1 = protocalData->at(2);
  22. uchar cmd2 = protocalData->at(3);
  23. uchar data = protocalData->at(4);
  24. if((cmd1==cmd1Rcv) && (cmd2 ==cmd2Rcv))
  25. {
  26. uchar uData[4];
  27. uData[0] = static_cast<uchar>(protocalData->at(4));
  28. uData[1] = static_cast<uchar>(protocalData->at(5));
  29. uData[2] = static_cast<uchar>(protocalData->at(6));
  30. uData[3] = static_cast<uchar>(protocalData->at(7));
  31. *(int *)&receiveParsingData = *(int *)&uData;
  32. ui->lcdnumber_vlotage->display(receiveParsingData);
  33. }
  34. }
  35. }

LCD显示当前电压

Qt上位机显示动态数据_哔哩哔哩_bilibili

Qt上位机显示动态数据


2023.9.14更新

从协议中进行数据截取

波形显示

graphicsView放大缩小、原始大小


2023.9.12更新

中文显示

发送/接受 帧计数

文件输出

支持时间戳模式自动换行

HEX接收时自动用空格分隔


前言

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

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

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

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

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

学习交流请发邮件 1280253714@qq.com

串口功能

波特率设置

串口开关

串口异常检测

字符串/HEX收发

定时发送

接收数据分隔

协议组包

协议拆包

源代码

widget.h

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include <QtSerialPort/QSerialPort> // 提供访问串口的功能
  5. #include <QtSerialPort/QSerialPortInfo> // 提供系统中存在的串口信息
  6. #include <QTime>
  7. #include <QtCharts>
  8. #include <QTimer>
  9. QT_BEGIN_NAMESPACE
  10. namespace Ui { class Widget; }
  11. QT_END_NAMESPACE
  12. class Widget : public QWidget
  13. {
  14. Q_OBJECT
  15. //用于表格设置
  16. enum FieldColNum {
  17. colTime,
  18. colCmd1,
  19. colCmd2,
  20. colData,
  21. };
  22. enum CellType {
  23. ctTime,
  24. ctCmd1,
  25. ctCmd2,
  26. ctData,
  27. };
  28. public:
  29. int tableRowCnt = 0;
  30. public:
  31. Widget(QWidget *parent = nullptr);
  32. ~Widget();
  33. void serialPortInit(); //串口初始化
  34. void windowInit(); //显示窗口初始化
  35. void refreshCom(); //刷新串口
  36. void tableInit(); //表格初始化
  37. void createItemsARow(int rowNum, QByteArray *protocalData); //表格新建一行
  38. QString ByteArrayToHexString(QByteArray &ba);
  39. private:
  40. Ui::Widget *ui;
  41. QSerialPort* serialPort;
  42. public slots:
  43. void comboBoxClicked(); //comboBox被点击
  44. private slots:
  45. void sendData(); //发送串口数据
  46. void receiveData(); //接收串口数据
  47. void openSerialport(); //串口开启
  48. void closeSerialport(); //串口关闭
  49. void setBuad(int); //设置波特率
  50. void clearRcv(); //清楚接收缓存
  51. void on_btnConvert_clicked(); //转换按钮被点击
  52. void on_btnClear_clicked(); //清楚转化的按钮被点击
  53. void sendProtocalHexData(); //以hex格式发送串口数据
  54. void handleSerialError(QSerialPort::SerialPortError serialPortErr); //串口异常捕获
  55. };
  56. #endif // WIDGET_H

widget.cpp

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include "newcombobox.h"
  4. #include <algorithm>
  5. Widget::Widget(QWidget *parent)
  6. : QWidget(parent)
  7. , ui(new Ui::Widget)
  8. {
  9. ui->setupUi(this);
  10. //创建一个定时器用来定时发送
  11. QTimer *timer1 = new QTimer();
  12. timer1->start(1000);
  13. connect(timer1,&QTimer::timeout,[=](){
  14. int timed = ui->comboBox_timedSend->currentText().toInt();
  15. timer1->start(timed);
  16. if(ui->checkBox_timedSend->isChecked() == true)
  17. {
  18. sendData();
  19. }
  20. });
  21. windowInit();
  22. tableInit();
  23. serialPort = new QSerialPort();
  24. serialPortInit();
  25. connect(serialPort,SIGNAL(readyRead()),this,SLOT(receiveData()));
  26. connect(serialPort,SIGNAL(errorOccurred(QSerialPort::SerialPortError)),this,SLOT(handleSerialError(QSerialPort::SerialPortError)));
  27. connect(ui->pushButton_sendData,SIGNAL(clicked()),this,SLOT(sendData()));
  28. connect(ui->pushButton_openSerialPort,SIGNAL(clicked()),this,SLOT(openSerialport()));
  29. connect(ui->pushButton_closeSerialPort,SIGNAL(clicked()),this,SLOT(closeSerialport()));
  30. connect(ui->comboBox_chooseCom,SIGNAL(clicked()),this,SLOT(comboBoxClicked()));
  31. connect(ui->pushButton_clearRcv,SIGNAL(clicked()),this,SLOT(clearRcv()));
  32. connect(ui->pushButton_convert,SIGNAL(clicked()),this,SLOT(on_btnConvert_clicked()));
  33. connect(ui->pushButton_clearConvertData,SIGNAL(clicked()),this,SLOT(on_btnClear_clicked()));
  34. connect(ui->pushButton_sendProtocalData,SIGNAL(clicked()),this,SLOT(sendProtocalHexData()));
  35. connect(serialPort,SIGNAL(errorOccurred(QSerialPort::SerialPortError)),this,SLOT(handleSerialError(QSerialPort::SerialPortError)));
  36. connect(ui->comboBox_setBuad,SIGNAL(activated(int)),this,SLOT(setBuad(int)));
  37. }
  38. Widget::~Widget()
  39. {
  40. delete ui;
  41. }
  42. //串口异常捕获
  43. void Widget::handleSerialError(QSerialPort::SerialPortError serialPortErr)
  44. {
  45. if(serialPortErr == QSerialPort::ResourceError)
  46. {
  47. QMessageBox::critical(NULL, "critical", "设备拔出", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  48. closeSerialport();
  49. }
  50. if(serialPortErr == QSerialPort::DeviceNotFoundError)
  51. {
  52. QMessageBox::critical(NULL, "critical", "找不到串口", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  53. closeSerialport();
  54. }
  55. }
  56. void Widget::comboBoxClicked()
  57. {
  58. refreshCom();
  59. }
  60. void Widget::windowInit()
  61. {
  62. ui->pushButton_closeSerialPort->setEnabled(false);
  63. ui->pushButton_openSerialPort->setEnabled(false);
  64. ui->pushButton_sendData->setEnabled(false);
  65. setWindowTitle(tr("串口收发"));
  66. ui->comboBox_timedSend->addItem("10");
  67. ui->comboBox_timedSend->addItem("100");
  68. ui->comboBox_timedSend->addItem("1000");
  69. ui->comboBox_timedSend->setCurrentIndex(2);
  70. }
  71. void Widget::refreshCom()
  72. {
  73. //显示串口列表
  74. ui->comboBox_chooseCom->clear();
  75. foreach(QSerialPortInfo portInfo, QSerialPortInfo::availablePorts())
  76. ui->comboBox_chooseCom->addItem(portInfo.portName()+":"+portInfo.description());
  77. ui->pushButton_openSerialPort->setEnabled(ui->comboBox_chooseCom->count()>0); //
  78. }
  79. void Widget::serialPortInit(){
  80. refreshCom();
  81. ui->comboBox_setBuad->addItem("9600");
  82. ui->comboBox_setBuad->addItem("115200");
  83. ui->comboBox_setBuad->addItem("921600");
  84. ui->comboBox_setBuad->setCurrentIndex(1);
  85. }
  86. void Widget::tableInit()
  87. {
  88. QStringList headerText;
  89. headerText<<"时间"<<"命令1"<<"命令2"<<"数据";
  90. ui->tableWidget->setColumnCount(headerText.size()); //设置表格列数
  91. ui->tableWidget->resizeColumnsToContents();
  92. for (int i=0;i<ui->tableWidget->columnCount();i++)
  93. {
  94. QTableWidgetItem *headerItem=new QTableWidgetItem(headerText.at(i));
  95. QFont font=headerItem->font(); //获取原有字体设置
  96. font.setBold(true); //设置为粗体
  97. font.setPointSize(10); //字体大小
  98. headerItem->setForeground(QBrush(Qt::black)); //设置文字颜色
  99. headerItem->setFont(font); //设置字体
  100. ui->tableWidget->setHorizontalHeaderItem(i,headerItem); //设置表头单元格的item
  101. }
  102. ui->tableWidget->setColumnWidth(0, 60);
  103. ui->tableWidget->setColumnWidth(1, 50);
  104. ui->tableWidget->setColumnWidth(2, 50);
  105. ui->tableWidget->setColumnWidth(3, 250);
  106. }
  107. //为一行的单元格创建 Items
  108. void Widget::createItemsARow(int rowNum, QByteArray *protocalData)
  109. {
  110. uchar preFix = 0xA5;
  111. uchar crc = 0;
  112. uchar temp = 0;
  113. temp = static_cast<uchar>(protocalData->at(0));
  114. if(static_cast<uchar>(protocalData->at(0)) == preFix)
  115. {
  116. for(int i=1; i<protocalData->length()-2; i++)
  117. {
  118. temp = static_cast<uchar>(protocalData->at(i));
  119. crc += static_cast<uchar>(protocalData->at(i));
  120. }
  121. temp = static_cast<uchar>(protocalData->at(protocalData->length()-2));
  122. if(crc != static_cast<uchar>(protocalData->at(protocalData->length()-2)))
  123. {
  124. return;
  125. }
  126. uchar len = protocalData->at(1);
  127. uchar cmd1 = protocalData->at(2);
  128. uchar cmd2 = protocalData->at(3);
  129. QByteArray data = protocalData->mid(4,len-6);
  130. QDateTime curTime = QDateTime::currentDateTime();//获取系统现在的时间
  131. QString time = curTime.toString("hh:mm:ss"); //设置显示格式
  132. uint8_t str1 = static_cast<uint8_t>(cmd1);
  133. QString hexStr1 = QString("%1").arg(str1, 2, 16, QLatin1Char('0')).toUpper();
  134. uint8_t str2 = static_cast<uint8_t>(cmd2);
  135. QString hexStr2 = QString("%1").arg(str2, 2, 16, QLatin1Char('0')).toUpper();
  136. QString testdata = ByteArrayToHexString(data).toLatin1().toUpper();
  137. QTableWidgetItem *item = new QTableWidgetItem(time, ctTime);
  138. ui->tableWidget->setItem(rowNum, colTime, item);
  139. item = new QTableWidgetItem(hexStr1, ctCmd1);
  140. ui->tableWidget->setItem(rowNum, colCmd1, item);
  141. item = new QTableWidgetItem(hexStr2, ctCmd2);
  142. ui->tableWidget->setItem(rowNum, colCmd2, item);
  143. item = new QTableWidgetItem(testdata, ctData);
  144. ui->tableWidget->setItem(rowNum, colData, item);
  145. }
  146. auto lastRowIndex = ui->tableWidget->rowCount()-1; // 最后一行的索引
  147. auto lastModelIndex = ui->tableWidget->model()->index(lastRowIndex, 0);
  148. ui->tableWidget->scrollTo(lastModelIndex); // 滚动到最后一行
  149. }
  150. QString Widget::ByteArrayToHexString(QByteArray &ba)
  151. {
  152. QDataStream out(&ba,QIODevice::ReadWrite); //将str的数据 读到out里面去
  153. QString buf;
  154. while(!out.atEnd())
  155. {
  156. qint8 outChar = 0;
  157. out >> outChar; //每次一个字节的填充到 outchar
  158. QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0')).toUpper() + QString(" "); //2 字符宽度
  159. buf += str;
  160. }
  161. return buf;
  162. }
  163. void Widget::sendData()
  164. {
  165. QString message = ui->lineEdit_sendData->text();
  166. if(ui->checkBox_hexSend->isChecked() == true)
  167. {
  168. serialPort->write(QByteArray::fromHex(message.toLatin1()));
  169. }
  170. else
  171. {
  172. serialPort->write(message.toLatin1());
  173. }
  174. }
  175. void Widget::receiveData()
  176. {
  177. QByteArray message;
  178. QString hexMsg;
  179. message.append(serialPort->readAll());
  180. QDateTime time = QDateTime::currentDateTime(); //获取系统现在的时间
  181. QString date = time.toString("hh:mm:ss"); //设置显示格式
  182. if(ui->checkBox_hexRcv->isChecked() == true)
  183. {
  184. tableRowCnt++;
  185. ui->tableWidget->setRowCount(tableRowCnt);
  186. createItemsARow(tableRowCnt-1,&message);
  187. hexMsg = ByteArrayToHexString(message).toLatin1();
  188. ui->textEdit_RecData->append(date+QString("-> ")+hexMsg.toUpper());
  189. }
  190. else
  191. {
  192. ui->textEdit_RecData->append(date+QString("-> ")+message);
  193. }
  194. }
  195. void Widget::openSerialport()
  196. {
  197. ui->pushButton_closeSerialPort->setEnabled(true);
  198. ui->pushButton_openSerialPort->setEnabled(false);
  199. QList<QSerialPortInfo> comList = QSerialPortInfo::availablePorts();
  200. QSerialPortInfo portInfo = comList.at(ui->comboBox_chooseCom->currentIndex());
  201. serialPort->setPort(portInfo); //设置使用哪个串口
  202. if(serialPort->open(QIODevice::ReadWrite) == false)
  203. {
  204. QMessageBox::critical(NULL, "critical", "找不到串口/串口被占用", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  205. closeSerialport();
  206. }
  207. else
  208. {
  209. serialPort->setBaudRate(QSerialPort::Baud115200);
  210. serialPort->setDataBits(QSerialPort::Data8);
  211. serialPort->setParity(QSerialPort::NoParity);
  212. serialPort->setStopBits(QSerialPort::OneStop);
  213. serialPort->setFlowControl(QSerialPort::NoFlowControl);
  214. ui->pushButton_sendData->setEnabled(true);
  215. }
  216. }
  217. void Widget::closeSerialport()
  218. {
  219. if(serialPort->isOpen()){
  220. serialPort->clear();
  221. serialPort->close();
  222. }
  223. ui->pushButton_closeSerialPort->setEnabled(false);
  224. ui->pushButton_openSerialPort->setEnabled(true);
  225. }
  226. void Widget::setBuad(int buad)
  227. {
  228. QString str = ui->comboBox_setBuad->currentText();
  229. serialPort->setBaudRate(str.toInt());
  230. }
  231. void Widget::clearRcv()
  232. {
  233. ui->textEdit_RecData->clear();
  234. }
  235. void Widget::on_btnClear_clicked()
  236. {
  237. ui->lineEdit_protocalData->clear();
  238. }
  239. void Widget::on_btnConvert_clicked()
  240. {
  241. ui->lineEdit_protocalData->clear();
  242. bool ok;
  243. QString str = "A5";
  244. int val1= ui->lineEditCmd1->text().toInt(&ok,16); //以十六进制数读入
  245. QString str1 = QString("%1").arg(val1, 2, 16, QLatin1Char('0'));
  246. int val2= ui->lineEditCmd2->text().toInt(&ok,16); //以十六进制数读入
  247. QString str2 = QString("%1").arg(val2, 2, 16, QLatin1Char('0'));
  248. if ((str1.isEmpty())||(str2.isEmpty()))
  249. return;
  250. int val3= ui->lineEditData->text().toInt(&ok,16); //以十六进制数读入
  251. QString str3 = QString("%1").arg(val3, 2, 16, QLatin1Char('0'));
  252. uint8_t len = 6 + static_cast<uint8_t>(str3.length()/2);
  253. QString hexStr = QString("%1").arg(len, 2, 16, QLatin1Char('0'));
  254. str.append(hexStr);
  255. str.append(str1);
  256. str.append(str2);
  257. str.append(str3);
  258. uint8_t crc;
  259. QString tmp;
  260. for(int i=0; i<str3.length(); i+=2)
  261. {
  262. tmp = ui->lineEditData->text()[i];
  263. tmp += ui->lineEditData->text()[i+1];
  264. crc+= tmp.toInt(&ok,16);
  265. tmp = "";
  266. }
  267. crc += len;
  268. crc += val1;
  269. crc += val2;
  270. QString hexCrc= QString("%1").arg(crc, 2, 16, QLatin1Char('0'));
  271. str.append(hexCrc);
  272. str.append("5A");
  273. str = str.toUpper();
  274. ui->lineEdit_protocalData->insert(str);
  275. }
  276. void Widget::sendProtocalHexData()
  277. {
  278. QString message = ui->lineEdit_protocalData->text();
  279. serialPort->write(QByteArray::fromHex(message.toLatin1()));
  280. }

ui

代码框架概览

演示视频

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

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

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/545696
推荐阅读
相关标签
  

闽ICP备14008679号