wap網(wǎng)站適配競價托管外包
最近在學習qt,于是打算搞一個串口調(diào)試助手,初學C++和QT,有理解錯誤希望能給出意見。
總結(jié)歸納
2023.09.22:能夠顯示串口數(shù)據(jù)
2023.09.25:增加按鈕打開以及之前沒注意到的異常處理
2023.09.27:知識補充串口編程Qt Serial Port
2023.09.27:通過所學修改波特率和com口
2023.09.28:增加發(fā)送串口數(shù)據(jù)
1、顯示串口數(shù)據(jù)
創(chuàng)建qt widget工程,ui界面就一個控件QTextEdit,名稱為Serial_Display
因為功能比較簡單,只需要顯示串口數(shù)據(jù)即可。首先肯定是整體的初始化,需要在.pro文件中添加QT ? ? ? += serialport。包含兩個頭文件<QtSerialPort/QSerialPort>和<QtSerialPort/QSerialPortInfo>。接下來就是串口初始化配置
QSerialPort *serialPort = new QSerialPort(this);serialPort->setPortName("COM6"); // 替換為你的串口名稱
serialPort->setBaudRate(QSerialPort::Baud115200);
serialPort->setParity(QSerialPort::NoParity);
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setFlowControl(QSerialPort::NoFlowControl);
serialPort->open(QIODevice::ReadOnly); // 僅讀
這一步有點像單片機的配置模塊,嫌麻煩所以直接在widget中的共有變量中聲明了QSerialPort。然后是讀取數(shù)據(jù)函數(shù)
void Widget::readSerialData()
{QByteArray data = serialPort->readAll(); // 讀取串口數(shù)據(jù)ui->Serial_Display->append(QString::fromUtf8(data)); // 在QTextEdit控件中顯示數(shù)據(jù)
}
這里有一個問題就是readSerialData定義成了槽函數(shù),而串口讀取就相當于一個信號,信號與槽好像是qt的核心。我的理解就是信號是觸發(fā),槽函數(shù)是一個動作,所以讀串口觸發(fā)了文本寫入,也就是readSerialData函數(shù)。
connect(serialPort, &QSerialPort::readyRead, this, &Widget::readSerialData);
所以整體的代碼為:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);///連接信號與槽connect(serialPort, &QSerialPort::readyRead, this, &Widget::readSerialData);///端口處理serialPort->setPortName("COM6"); // 替換為你的串口名稱serialPort->setBaudRate(QSerialPort::Baud115200);serialPort->setParity(QSerialPort::NoParity);serialPort->setDataBits(QSerialPort::Data8);serialPort->setStopBits(QSerialPort::OneStop);serialPort->setFlowControl(QSerialPort::NoFlowControl);serialPort->open(QIODevice::ReadOnly); // 僅讀
}void Widget::readSerialData()
{QByteArray data = serialPort->readAll(); // 讀取串口數(shù)據(jù)ui->Serial_Display->append(QString::fromUtf8(data)); // 在QTextEdit控件中顯示數(shù)據(jù)
}
這里有個問題,串口確實接收到了數(shù)據(jù),但是都是亂碼,QByteArray讀取的是串口字節(jié),而ui->Serial_Display->append是QString類型,所以可以將data轉(zhuǎn)換成十六進制。
void Widget::readSerialData()
{QByteArray data = 0;QString hexString = 0;data = serialPort->readAll(); // 讀取串口字節(jié)hexString = data.toHex(); // 將字節(jié)數(shù)組轉(zhuǎn)換為十六進制字符串ui->Serial_Display->append(hexString); // 在QTextEdit控件中顯示數(shù)據(jù)
}
2、增加按鈕打開以及之前沒注意到的異常處理
在ui界面增加一個QComboBox界面,也就是下拉框,用來掃描所有端口然后增加兩個QPushButton,一個用來打開串口,另一個用來關(guān)閉串口。
這里有一點就是波特率需要能夠自主選擇,這里可以雙擊波特率的下拉框,然后在編輯組合框中添加需要的串口波特率。
接下來就是端口掃描
// 端口掃描
#if Serial_Scanforeach (const QSerialPortInfo &inf0, QSerialPortInfo::availablePorts()){serialNamePort << inf0.portName(); // 端口名稱}ui->serialBox->addItems(serialNamePort); // 把掃描到的端口放入BOX中
#endif
這里的ui->serialBox,表示選中了畫的ui界面中的serialBox,之前的串口下拉框的名稱就是這個,這個可以自己定義。
這段代碼的解釋:這段代碼是用C++編寫的。其中,'foreach'是一個循環(huán)結(jié)構(gòu),用于遍歷集合中的每個元素。'QSerialPortInfo::availablePorts()'是Qt框架中QSerialPortInfo類的一個靜態(tài)方法,它返回一個包含所有可用串口信息的QList對象。'inf0'是遍歷過程中每個QSerialPortInfo對象的引用。'portName()'方法返回串口的名稱。'serialNamePort'可能是一個QStringList對象,用于存儲所有可用串口的名稱。最后,'ui->serialBox->addItems(serialNamePort)'將所有可用的串口名稱添加到名為'serialBox'的QComboBox控件中。沒錯,是搜的文心一言的解釋!!!!!
那么串口的設(shè)置,尤其是串口號和串口波特率就需要進行相關(guān)的更改
/* 串口設(shè)置 */
#if Serial_Setserialport->setPortName(ui->serialBox->currentText()); // 串口下拉框當前數(shù)據(jù)serialport->setBaudRate(ui->baudrateBox->currentText().toInt()); // 設(shè)置為波特率下拉框中的當前數(shù)據(jù)serialport->setDataBits(QSerialPort::Data8); // 默認8位數(shù)據(jù)serialport->setStopBits(QSerialPort::OneStop); // 默認一位停止位serialport->setParity(QSerialPort::NoParity); // 默認沒有奇偶校驗位serialport->setFlowControl(QSerialPort::NoFlowControl); // 沒有流控制
#endif
接著就是串口打開操作,在打開串口的槽函數(shù)中(右擊打開串口轉(zhuǎn)到槽,選中clicked())
打開串口有兩個注意點,一個是就是普通的打開串口,第二是如果已經(jīng)打開串口就需要提示串口已打開。
void Widget::on_openButton_clicked(void)
{// 串口打開提示
#if Serial_Warningif (serialport->open(QIODevice::ReadWrite)) // 打開串口{// information函數(shù)表示普通的提示功能QMessageBox::information(this, "提示", "串口打開成功");}else{#if ERROR// critical函數(shù)表示警告QMessageBox::critical(this, "提示", "串口已打開");#endif}
#endif
}
而關(guān)閉串口只要直接關(guān)閉就行了
void Widget::on_closeButton_clicked()
{
#if Serial_Closed_Button// 關(guān)閉槽函數(shù)serialport->close();// 關(guān)閉串口提示QMessageBox::information(this, "提示", "關(guān)閉串口");
#endif
}
有一處改動,將串口設(shè)置放到打開串口的槽函數(shù)中。發(fā)現(xiàn)如果在Widget::Widget函數(shù)中先掃描然后初始化串口會導致程序異常,我也不知道為什么????
3、知識補充串口編程Qt Serial Port
Qt Serial Port模塊:這個模塊用于串口通信編程,Qt Serial Port有兩個類QSerialPortInfo和QSerialPort。
QSerialPortInfo類里面有兩個函數(shù),我的理解是就是用來獲取數(shù)據(jù)的
????????QSerialPortInfo::availablePorts()? ? ? ? ? ? ? ? //獲取串口列表
????????QSerialPortInfo::standardBaudRates()? ? ? ? // 獲取串口支持波特率列表
返回值:獲取串口列表QList<QSerialPortInfo>,獲取波特率的返回值是QList<qint32>。那接下來就有個問題,怎么使用這兩個函數(shù)?
1)QSerialPortInfo::availablePorts()
?- 這個函數(shù)返回的類型是QStringList,表示一個列表,看名字也看得出來??梢酝ㄟ^遍歷這個列表獲取系統(tǒng)所有的可用串口。
#include <QSerialPortInfo>
#include <QDebug> int main() { // 獲取所有可用的串口 QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts(); // 遍歷并打印串口名稱 for(const QSerialPortInfo &port : ports) { qDebug() << "Port Name:" << port.portName(); } return 0;
}
2、QSerialPortInfo::standardBaudRates()
?- 這個函數(shù)返回一個包含所有標準波特率的QList<qint32>。這些波特率是串口通信中常用的標準波特率。
// 獲取標準波特率列表 QList<qint32> baudRates = QSerialPortInfo::standardBaudRates(); // 遍歷并打印波特率 for(qint32 baudRate : baudRates) { qDebug() << "Baud Rate:" << baudRate; }
4、通過所學修改波特率和com口的獲取方法
根據(jù)之前學的qseriportinfo中的操作,就可以修改波特率為所有波特率
#if Read_Serial// 獲取波特率QString Baud_String;QStringList Baud_StringList;QList<qint32> baudRates = QSerialPortInfo::standardBaudRates(); // 獲取標準波特率列表for(const qint32 &baudRate : baudRates) // 遍歷全部波特率列表{Baud_String = QString::number(baudRate); // 將qint32轉(zhuǎn)換成qStringBaud_StringList.append(Baud_String); // 添加到QString_List}// 把標準波特率添加到ui界面ui->baudrateBox->addItems(Baud_StringList);
#endif
同樣的獲取端口列表修改
#if Serial_Scan// 獲取COM口QString COM_String;QStringList COM_StringList;QList<QSerialPortInfo> COM_Ports = QSerialPortInfo::availablePorts();for(const QSerialPortInfo &port : COM_Ports) // 遍歷全部端口列表{COM_String = port.portName(); // qstring保存端口名稱COM_StringList.append(COM_String); // 添加到COM_string列表}ui->serialBox->addItems(COM_StringList); // 把掃描到的端口放入BOX中
#endif
接下來是QSerialPort類,學習這個的時候有一個概念叫繼承與重載,之前學習C++只學了個皮毛,所以打算記錄一下。
繼承和重載面向?qū)ο蟮囊粋€概念,類和對象之間的一種關(guān)系?;仡櫪^承和重載之前,有一個概念是類和對象。
什么是類和對象?
雖然之前已經(jīng)接觸過類和對象,但還是只停留在似懂非懂的層面。類,英文名叫class,對象的英文名叫object。類就是有相同的屬性和方法的東西,而對象就是具體的東西。舉個例子,比如說人就是一類,這個類里面有很多屬性,比如說身高體重。而具體的人就是對象,比如說張三,他的身高160,體重50kg。轉(zhuǎn)換成計算機語言就是先聲明一個類,比如class person{身高,體重}。接著用這個類再聲明一個叫張三的人person 張三。然后再賦值屬性,比如張三->身高=150,張三->體重->50kg。不知道對不對,先暫時這么理解。
什么是繼承和重載?
接下來就可以學習繼承和重載了。繼承:一種類與類之間的關(guān)系,它表示一個類(稱為子類)可以繼承另一個類(稱為父類)的屬性和方法,繼承允許我們在不重復編寫代碼的情況下,擴展和定制現(xiàn)有代碼的基礎(chǔ)。
重載:重載是指在同一類中,可以定義多個具有相同名稱但參數(shù)列表不同的方法。這些方法可以有不同的返回類型,但關(guān)鍵是參數(shù)列表必須不同。
QSerialPort類中有以下一些重要的函數(shù):
1、open(OpenMode mode):這是QIODevice::open()的繼承和重載。這個函數(shù)的功能是用“OpenMode”模式打開串口,如果打開成功就返回true,否則就返回false并且會報錯,錯誤碼可以通過調(diào)用“erro()”函數(shù)來查看。
2、readData(char * data, qint64 maxSize):這是QIODevice::readData()的繼承和重載,用于從串口讀取數(shù)據(jù)。
3、readLineData(char * data, qint64 maxSize):這是QIODevice::readLineData()的繼承和重載,用于從串口讀取數(shù)據(jù)直到遇到換行符。
可以看出QSerialPort本質(zhì)是對QIQDevice進行了一些處理和擴展,使得滿足串口的應用場景。
5、增加發(fā)送串口數(shù)據(jù)
在ui界面添加一個發(fā)送文本SendData和發(fā)送按鈕。在發(fā)送的clicked()槽函數(shù)中做這么幾件事,首先是獲取發(fā)送文本的數(shù)據(jù),因為是第一次添加發(fā)送數(shù)據(jù),所以按照一個byte去發(fā),后面要多組數(shù)據(jù)的話,再想辦法改進。
先要獲取發(fā)送的數(shù)據(jù):DataToSend = ui->SendData->toPlainText(),接下來就是轉(zhuǎn)換成字節(jié),然后使用serialport的成員函數(shù)write把數(shù)據(jù)發(fā)送出去
#if Serial_Send
// 串口發(fā)送
QByteArray Data;
QString DataToSend;
QByteArray DataByte;DataToSend = ui->SendData->toPlainText(); // 獲取發(fā)送中的數(shù)據(jù)
DataByte = DataToSend.toUtf8(); // 轉(zhuǎn)換成字節(jié)數(shù)組,適應串口發(fā)送
serialport->write(DataByte); // 發(fā)送數(shù)據(jù)
#endif
實際的效果:
0、發(fā)布程序
使用windeployqt,首先是qt creator界面選擇release運行程序,在輸出目錄找到.exe文件
是以管理員身份運行,qt里面的命令行程序(安裝完qt自帶的),然后跳轉(zhuǎn)到.exe所在的目錄,命令
cd /d 后面再跟著目錄:cd /d C:\Users\dell\Desktop\qt project\exe
然后輸入windeployqt+exe的名稱:windeployqt?untitled.exe。