串口通信是我們經常會遇到的問題。很多時候當我們設計一個串口應用時,我們希望有一個簡便的、可視的方式來驗證它。這一篇中我們就來基于QT設計一個串口調試工具。
1、概述
??在開始軟件設計之前,我們來簡略地分析一下這樣一個小軟件其要包含的主要內容有哪些。我們認為軟件需要如下幾個方面的內容:
-
串口參數的配置,我們希望串口號能夠自動搜索,而相應的配置參數我們可以選擇。
-
發(fā)送數據的輸入,對于本軟件我們需要輸入相應的數據以實現命令及消息的發(fā)送,所以我們需要設計數據的輸入區(qū)域以及發(fā)送交互按鈕等。
-
接收信息的顯示,作為調試工具,我們肯定希望能夠一目了然地看到接收到目標設備發(fā)送過來的消息,所以我們需要一個顯示區(qū)域來對接收的區(qū)域進行顯示。
-
運行狀態(tài)的顯示, 我們希望對操作的狀態(tài)進行反饋以指示操作的動作是否執(zhí)行,所以我們需要狀態(tài)欄來實現這一需求。
-
其它輔助功能, 還有如發(fā)送計數、接收計數、數據存儲等功能有時候也是需要的,所以我們一并考慮。
??對于串口工具其實網上就有不少,我們之所以要自己實現這么一個串口調試工具,主要的原因有兩點。一是,網上找到的相應工具某一個單獨的工具有時候不能完全滿足我們的需求,所以我們根據自己的需求設計這個工具能更好的滿足我們串口調試的需要。二是,通過這樣一個工具的實現,我們能夠加深對串口通訊相關知識的理解。
2、界面設計
??根據上一節(jié)中分析的需求,我們先來設計軟件的界面。我們在QT中基于QMainWindow類生成一個操作界面,包括菜單欄、工具欄和狀態(tài)欄以滿足需求中對狀態(tài)顯示及操作命令的要求。
??而在中間顯示區(qū)域,我們將其劃分為3行2列。在左邊的一列從上到下設置:串口配置操作區(qū)域、接收配置區(qū)域以及發(fā)送配置區(qū)域。在右側的一列從上到下設置:動態(tài)曲線顯示區(qū)域、信息接收顯示區(qū)域以及信息發(fā)送輸入區(qū)域。具體的界面設置如下圖所示:
??完成如上圖的布局后,我們可以選擇在屬性中配置空間的參數,也可以在代碼中添加相關的參數,本人習慣于在代碼中完成。完成整個布局后我們先試著運行程序,正常運行則出現如下的界面:
??上圖就是完成布局后的運行界面,不過我們還沒有實現相應的編碼,所以目前還不能實現我們第一節(jié)中所提出來的功能。
3、編碼實現
??接下來這一小節(jié),我們將來編碼實現相應的功能。我們主要將功能分為參數設置與操作功能、數據的輸入與發(fā)送功能以及數據的接收與顯示功能三個部分來實現。
3.1、參數設置與操作功能
??對于參數的配置除了串口號以外都可以直接使用ComboBox控件的相應函數添加。串口號這塊,我們希望搜索電腦安裝的串口并添加到控件中。具體的實現方式如下:
//搜索可用的串口,并添加到串口組合框
void MainWindow::SearchSerialPorts()
{
ui->comboBoxPort->clear();
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{
ui->comboBoxPort->addItem(info.portName());
}
}
??配置好串口參數后,我們可以打開串口以建立連接。需要說明的是我們打開串口間離連接時,我們需要將該串口的數據接收與我們的數據接收和處理函數建立信號槽連接。具體實現如下:
//打開串口
void MainWindow::on_actionConnect_triggered()
{
serialPort->setPortName(ui->comboBoxPort->currentText());
if(serialPort->open(QIODevice::ReadWrite)) //打開串口成功
{
serialPort->setBaudRate(ui->comboBoxBaud->currentText().toInt()); //設置波特率
switch(ui->comboBoxData->currentIndex()) //設置數據位數
{
case 1:serialPort->setDataBits(QSerialPort::Data8);break;
default: break;
}
switch(ui->comboBoxParity->currentIndex()) //設置奇偶校驗
{
case 0: serialPort->setParity(QSerialPort::NoParity);break;
default: break;
}
switch(ui->comboBoxStop->currentIndex()) //設置停止位
{
case 1: serialPort->setStopBits(QSerialPort::OneStop);break;
case 2: serialPort->setStopBits(QSerialPort::TwoStop);break;
default: break;
}
serialPort->setFlowControl(QSerialPort::NoFlowControl); //設置流控制
//連接槽函數
QObject::connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::ReadSerialData);
// 設置控件可否使用
ui->actionConnect->setEnabled(false);
ui->actionClose->setEnabled(true);
ui->actionRefresh->setEnabled(false);
}
else //打開失敗提示
{
QMessageBox::information(this,tr("錯誤"),tr("打開串口失敗!"),QMessageBox::Ok);
}
}
??同樣的,我們除了要打開串口建立連接外,還需要關閉串口斷開連接,具體的代碼如下:
//關閉串口
void MainWindow::on_actionClose_triggered()
{
serialPort->clear();
serialPort->close();
// 設置控件可否使用
ui->actionConnect->setEnabled(true);
ui->actionClose->setEnabled(false);
ui->actionRefresh->setEnabled(true);
}
3.2、數據的輸入與發(fā)送功能
??數據的輸入與發(fā)送,我們設計了5條命令,每條命令可以通過后面的按鈕手動發(fā)送,也可以自動循環(huán)發(fā)送。自動循環(huán)發(fā)送時,將對每條選中的命令以設定的時間間隔輪詢發(fā)送。
??首先我們來看看定時周期發(fā)送的過程。我們定義了一個計時器,以我們設定的時間周期觸發(fā)發(fā)送命令,每次發(fā)送復選框被選中的命令一條,依次循環(huán)直到人為停止循環(huán)發(fā)送為止。具體的代碼如下:
//定時周期發(fā)送
void MainWindow::CycleSendData()
{
QCheckBox* cbSend;
while(true)
{
snIndex=snIndex>=6?1:snIndex;
cbSend=ui->groupBoxMessage->findChild(QString("checkBoxSendEnable%1").arg(QString::number(snIndex)));
if(cbSend->isChecked())
{
WriteSerialData(snIndex);
snIndex++;
break;
}
snIndex++;
}
}
??手動單次發(fā)送則判斷是哪一個按鈕觸發(fā)的動作則操作對應的數據輸入框,將其中的內容以指定的格式發(fā)送出去。具體的操作代碼如下:
//按鈕觸發(fā)發(fā)送
void MainWindow::SingleSendData()
{
// 判斷如果Sender是QPushButton就執(zhí)行
if (QPushButton* btn = dynamic_cast(sender()))
{
QString senderName;
int sn=0;
senderName = btn->objectName();
sn = senderName.replace("pushButtonSend", "").toInt();
if((06))
{
WriteSerialData(sn);
}
}
}
3.3、數據的接收與現實功能
??在我們的設計中,數據的接收相對要簡單一些。當串口接收到數據后就會觸發(fā)我們的接收數據處理函數,并將以我們設定的格式顯示出來,具體的實現代碼如下:
//從串口接收數據
void MainWindow::ReadSerialData()
{
QByteArray rxDatas;
QString context;
rxDatas=serialPort->readAll();
if(!rxDatas.isNull())
{
if(ui->checkBoxRecieve->isChecked()) //十六進制顯示
{
context = rxDatas.toHex(' ');
context=context.toUpper();
}
else //ASCII顯示
{
context = rxDatas;
}
QString timeStrLine="["+QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+"][接收]: ";
context = timeStrLine+context+"\\n\\r";
QString content = ""+context+"";
ui->textBrowser->append(content);
receivedBytes=receivedBytes+rxDatas.size();
ui->lcdNumberRecieve->display(receivedBytes);
ui->statusbar->showMessage(tr("成功讀取%1字節(jié)數據").arg(rxDatas.size()));
}
rxDatas.clear();
}
4、小結
??完成了編碼調試后,我們來對開發(fā)的這一工具進行一些測試。首先我們安裝一個虛擬串口軟件用以虛擬我們用于測試的串口。如果有硬件接口最好,但是在我的電腦上沒有串口,所以我們使用虛擬串口來模擬一對串口。具體的配置如下圖所示:
??我們使用另一個串口工具來實現與我們開發(fā)的這一工具實現通訊驗證。我們使用access port來實現與這一工具的通訊。access port使用COM12,SerialMaster使用COM12,相應的串口參數配置為一致。具體的配置如下圖所示:
??我們使用access port發(fā)送數據,SerialMaster接收到并在顯示區(qū)顯示為藍色字符。同樣我們通過SerialMaster手動發(fā)送一條信息,可以在access port看到相應的數據并且在SerialMaster的顯示區(qū)域顯示為紅色字符。如下圖所示:
??接著我們試驗自動循環(huán)發(fā)送。我們將發(fā)送取得三條命令輸入,并將對應的復選框選擇為選中。并將自動發(fā)送復選框選中,就會按設定的時間間隔發(fā)送相應的命令。如下圖所示:
??到這里我們想要的串口調試工具就基本實現了,當然,后續(xù)我們還可以更具需要修改或添加一些功能以適應不同的應用尋求,甚至可以嵌入到一些應用中以實現必要的測試功能。我們將代碼發(fā)布到Gitee,歡迎下載和交流。
完整代碼下載地址:https://gitee.com/ErichMoonan/serial-master
-
Qt
+關注
關注
1文章
302瀏覽量
37899 -
界面設計
+關注
關注
0文章
22瀏覽量
10478 -
串口調試工具
+關注
關注
0文章
14瀏覽量
14376
發(fā)布評論請先 登錄
相關推薦
評論