扔掉xshell,基于 QT 實現一個串口命令行工具(帶源碼)

背景

xshell 帶有支持串口的命令行能力, 可以方便的和下位機用命令進行交互,如下圖所示:

msh >
msh >
msh >version\ | /
- RT -     Thread Operating System/ | \     3.1.3 build Nov  7 20232006 - 2019 Copyright by rt-thread team
msh >
msh >
msh >

假設有這樣一種使用場景,我們經常會使用串口調試助手連接串口進行16進制或者ascii的數據調試,但同時又想使用命令行工具下發指令,比如查看文件夾等等。因為串口是獨占式連接,所以我們就必須關閉串口調試助手的串口連接,再打開xshell連接,沒辦法做到同時使用。

假如有這種使用訴求,那作為程序員我們就有必要在一個軟件同時實現這兩個功能,則這兩個功能就可以同時使用了。所以本文重點是如何實現串口命令行,關于串口調試助手的功能比較簡單,就不再說明。

關鍵知識點

原理說明

不同于常見的比如windows的cmd命令行,linux的shell終端,或其他bash環境等等,他們是一個指令作為一個單元發送給下位機,比如:ls ,上位機會將"ls"整個單詞加上結束符"\r\n"發送給下位機處理。而串口命令行有一個特點是逐字符發送和顯示,比如"ls" 會先發送 “l” ,然后下位機回復"l",上位機收到"l"進行顯示。上位機再發送"s",下位機再回復"s",上位機收到"s" 進行顯示。最后當用戶敲下回車鍵時,上位機發送 “\r\n”(只是舉例說明),下位機此時會解析整條指令,并將處理好的數據返回給上位機,上位機簡單處理后進行顯示。所以基于串口的命令行工具有個特點是:如果串口連接不正常或者串口正常但是下位機程序運行不正常,通過上位機發送的命令下位機無法回復,則上位機不顯示任何東西(因為沒有收到下位機的回復)。

經過調研發現mcu的命令行解析工具都是基于逐字符方式實現的,比如 finsh、letter shell等,個人猜測這樣做的目的可能是因為下位機設備的資源限制或者uart的限制?或者說實時性? 有知道的同學可以評論區回答一下。

關鍵鍵值

詳見:ASCII碼一覽表,ASCII碼對照表

ASCII 編碼中第 0~31 個字符(開頭的 32 個字符)以及第 127 個字符(最后一個字符)都是不可見的(無法顯示),但是它們都具有一些特殊功能,所以稱為控制字符( Control Character)或者功能碼(Function Code)。這 33 個控制字符大都與通信、數據存儲以及老式設備有關。

不可見的意思就是無法在屏幕上顯示出來,但是代碼中可以用char表示。比如 tab 鍵對應的 \t,如果非要顯示的話,只能當作常規的字符串 一個反斜杠+一個字母 t 進行顯示,而無法代表其本身的意思。

剩下的95個字符就是我們常見的比如:0-9,a-z,A-Z等,這些字符可以被識別和顯示,也就是用戶可以輸入并顯示出來,可以被作為傳輸字符來使用。所以對于我們的程序來講,需要特殊處理的字符就是33個字符,當然并不是所有,我們只需要處理我們常見的支持的字符即可,比如回車符、制表符等。而其他的字符作為用戶輸入的指令進行下發和回顯即可。

常見的鍵對應的指令如:

/** handle control key* up key  : 0x1b 0x5b 0x41* down key: 0x1b 0x5b 0x42* right key:0x1b 0x5b 0x43* left key: 0x1b 0x5b 0x44*//* received null or error */ch == '\0' || ch == 0xFF/* handle tab key */ch == '\t'/* handle backspace key */(ch == 0x7f || ch == 0x08)/* handle end of line, break */ch == '\r' || ch == '\n'

關鍵代碼


void QVTerminal::keyPressEvent(QKeyEvent* event)
{QByteArray data;switch (event->key()) {case Qt::Key_Up://char bytes[3] = {0x1b, 0x5b, 0x41};data.append("\033[A");break;case Qt::Key_Down:data.append("\033[B");break;case Qt::Key_Right:data.append("\033[C");break;case Qt::Key_Left:data.append("\033[D");break;case Qt::Key_Home:data.append('\x01');break;case Qt::Key_End:data.append('\x05');break;case Qt::Key_Tab:data.append('\t');break;case Qt::Key_Backspace:data.append('\b');break;case Qt::Key_Return:data.append('\n');break;default:data.append(event->text().toUtf8());QAbstractScrollArea::keyPressEvent(event);}emit transmitData(data);
}

這是按鍵發送的核心代碼,比如我們輸入"version",并按下回車,用串口抓包助手(推薦CommMonitor10.0.3版本,免費)可以看到下位機收到的數據和回復的數據:

COM5,Wirte(1): 76  | v
COM5, Read(1): 76  | v
COM5,Wirte(1): 65  | e
COM5, Read(1): 65  | e
COM5,Wirte(1): 72  | r
COM5, Read(1): 72  | r
COM5,Wirte(1): 73  | s
COM5, Read(1): 73  | s
COM5,Wirte(1): 69  | i
COM5, Read(1): 69  | i
COM5,Wirte(1): 6F  | o
COM5, Read(1): 6F  | o
COM5,Wirte(1): 6E  | n
COM5, Read(1): 6E  | n
COM5,Wirte(1): 0D  | \#13
COM5, Read(32): 0D 0A 0D 0A 20 5C 20 7C 20 2F 0D 0A 2D 20 52 54 20 2D 20 20 20 20 20 54 68 72 65 61 64 20 4F 70  | \#13\#10\#13\#10 \ | /\#13\#10- RT -     Thread Op
COM5, Read(64): 65 72 61 74 69 6E 67 20 53 79 73 74 65 6D 0D 0A 20 2F 20 7C 20 5C 20 20 20 20 20 33 2E 31 2E 33 20 62 75 69 6C 64 20 4E 6F 76 20 20 37 20 32 30 32 33 0D 0A 20 32 30 30 36 20 2D 20 32 30 31 39  | erating System\#13\#10 / | \     3.1.3 build Nov  7 2023\#13\#10 2006 - 2019
COM5, Read(32): 20 43 6F 70 79 72 69 67 68 74 20 62 79 20 72 74 2D 74 68 72 65 61 64 20 74 65 61 6D 0D 0A 6D 73  |  Copyright by rt-thread team\#13\#10ms
COM5, Read(3): 68 20 3E  | h >

可以看到我們write一個字符,下位機就回復一個字符,直到我們發送"0D",也就是Enter鍵"\r",下位機才會返回這個指令的最終響應數據。

下面的代碼是收到下位機數據后的處理:


void QVTerminal::appendData(const QByteArray& data)
{QByteArray text;setUpdatesEnabled(false);QByteArray::const_iterator it = data.cbegin();while (it != data.cend()) {QChar c = *it;switch (state) {case QVTerminal::Text:switch (c.unicode()) {case '\033':appendString(text);text.clear();state = QVTerminal::Escape;break;case '\r':appendString(text);text.clear();cursorPos.setX(0);break;case '\n':appendString(text);text.clear();moveCursor(0, 1);break;case '\b':appendString(text);text.clear();moveCursor(-1, 0);break;default:if (c.isPrint()) {text.append(c);}}break;case QVTerminal::Escape:formatValue = 0;if (c == '[') {state = QVTerminal::Format;} else if (c == '(') {state = QVTerminal::ResetFont;}break;case QVTerminal::Format:if (c >= '0' && c <= '9') {formatValue = formatValue * 10 + (c.cell() - '0');} else {formatChar(c);state = QVTerminal::Text;}break;case QVTerminal::ResetFont:curentFormat = format;state = QVTerminal::Text;break;}it++;}appendString(text);verticalScrollBar()->setRange(0, ch * (layout->lineCount() + 1) - viewport()->size().height());verticalScrollBar()->setValue(verticalScrollBar()->maximum());setUpdatesEnabled(true);update();
}

下載地址: https://download.csdn.net/download/u012534831/88619133

其他代碼我打包上傳到csdn資源中,關注公號后在后臺留言需要下載的資源,我看到后免費發給你,并可以得到我的免費解答。 原創不易,謝謝支持。

在這里插入圖片描述

關注公眾號 QTShared,帶你探索更多QT相關知識。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/214383.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214383.shtml
英文地址,請注明出處:http://en.pswp.cn/news/214383.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

this.$emit(‘update:isVisible‘, false)作用

這個寫是不是很新穎&#xff0c;傳父組件傳值&#xff01;這是什么鬼。。。 假設你有以下邏輯業務。在A頁面彈出一個組件B&#xff0c;A組件里面使用B組件&#xff0c;是否展示B組件你使用的是baselineShow變量控制&#xff01; <BaselineData :isVisible.sync"basel…

如何在Word中簡潔地插入代碼

如何在Word中簡潔地插入代碼 背景&#xff1a; ? 最近在一寫一些論文或者報告的時候&#xff0c;需要將源代碼放在論文的最后&#xff0c;有一個很頭疼的問題&#xff0c;如果直接把代碼從編輯器復制到word中&#xff0c;就變成了下面這個樣子&#xff1a; 這有點丑陋啊&…

Qt簡介、C++工程文件分離、創建Qt工程、Qt的幫助文檔

QT 簡介 core&#xff1a;核心模塊&#xff0c;非圖形的接口類&#xff0c;為其它模塊提供支持 gui&#xff1a;圖形用戶接口&#xff0c;qt5之前 widgets&#xff1a;圖形界面相關的類模塊 qt5之后的 database&#xff1a;數據庫模塊 network&#xff1a;網絡模塊 QT 特性 開…

Linux系統的各項命令

文章目錄 Linux系統的目錄結構Linux路徑的描述方式Linux命令入門**什么是命令、命令行**Linux命令基礎格式 ls命令入門HOME目錄和工作目錄ls命令的參數和選項ls命令的 -a選項ls命令的 -l選項ls命令選項的組合使用ls選項和參數的組合使用ls命令的 -h選項 目錄切換相關命令&#…

多線程案例-阻塞隊列

阻塞隊列是什么 阻塞隊列是一種特殊的隊列.也遵循"先進先出"的原則 阻塞隊列能是一種線程安全的數據結構,并且具有以下特性: 當隊列滿的時候,繼續入隊列就會阻塞,直到有其他線程從隊列中取走元素. 當隊列空的時候,繼續出隊列也會阻塞,直到有其他線程往隊列中插入元素…

這七款網工在線畫拓撲工具,絕了!

你們好&#xff0c;我的網工朋友。 畫拓撲圖&#xff0c;絕對是網絡工程師的基操。 上次給你來了篇手把手教你繪制拓撲圖的好文&#xff0c;還沒看過的先去看啊&#xff1a;《網絡拓撲圖怎么畫最好&#xff1f;》。 關于畫拓撲的工具&#xff0c;那就多了&#xff0c;直接用…

數據結構與算法-D8D9隊列實現及應用

隊列&#xff1a;限制在兩端進行插入和刪除的線性表 允許進行存入操作的一端為“隊尾” 允許進行刪除操作的一端為“隊頭” 順序隊列 注意&#xff1a;front指向隊頭元素的位置 rear指向隊尾元素的下一個位置 實現循環隊列&#xff1a;(rear1)%N取余&#xff0c;為了區分空…

Connection refused: no further information

解決目錄 一、報錯信息二、解決方法 一、報錯信息 二、解決方法 1、報錯原因是開啟了代理&#xff0c;像AS是絕對不能開代理的。 2、設置為No proxy&#xff0c;然后Apply再選擇OK&#xff0c;重新同步。 要遠離消耗你的人和事&#xff0c;不要花費任何情緒或者精力在他們身…

unity Pc獲取本機Mac地址

1.此方法只能獲取眾多Mac中的一個 private static string GetMacAddress(){string physicalAddress "";NetworkInterface[] nice NetworkInterface.GetAllNetworkInterfaces();foreach (NetworkInterface adaper in nice){Debug.Log(adaper.Description);if (adape…

Linux網絡——高級IO

目錄 一.五種IO模型 1.阻塞式IO 2.非阻塞式IO 3.信號驅動IO 4.多路轉接IO&#xff1a; 5.異步IO 二.同步通信 vs 異步通信 三.設置非阻塞IO 1.阻塞 vs 非阻塞 2.非阻塞IO 3.實現函數SetNoBlock 四.I/O多路轉接之select 1.初識select 2.select函數原型 3.socket就緒…

UEFI下Windows10和Ubuntu22.04雙系統安裝圖解

目錄 簡介制作U盤啟動盤并從U盤啟動電腦安裝系統安裝Windows系統安裝Ubuntu 附錄雙系統時間不一致 簡介 傳統 Legacy BIOS主板下的操作系統安裝可參考本人博客 U盤系統盤制作與系統安裝&#xff08;詳細圖解&#xff09; &#xff0c;本文介紹UEFI主板下的雙系統安裝&#xff…

手把手教你在GPU T4卡上安裝硬解環境+編譯硬解的ffmpeg

系列文章目錄 文章目錄 系列文章目錄前言一、NVDIA環境軟件安裝二、FFMPEG編譯過程總結前言 通常開發流媒體服務,經常需要ffmpeg支持硬解解碼功能,即常見的GPU解碼,如cuda解碼等。下面主要講解在全新的環境中怎么安裝nvidia的環境與編譯ffmpeg的過程。 運行環境Centos7.5 G…

解決 Element-ui中 表格(Table)使用 v-if 條件切換后,表格的列的篩選不顯示了

解決方法 在每個需要使用 v-if 或 v-else 的 el-table-column 上增加 key 作為唯一標識&#xff0c;這樣渲染的時候就不會因為復用原則導致列數據混亂了。關于key值&#xff0c;一般習慣使用字段名&#xff0c;也可隨機生成一個值&#xff0c;只要具有唯一性就可以。

如何快速上手不熟悉的庫

首先需要一個編輯器vscode或者pycharm 然后&#xff0c;不要傻乎乎的自己急著去看代碼。 先看有沒有文檔和使用手冊&#xff0c;一般都有一個quick_start.md文件或者其他的.md文件。 然后&#xff0c;還是不急著看代碼&#xff0c;先看代碼的注釋。 比如我現在要從這里找到…

Java王者榮耀火柴人

主要功能 鍵盤W,A,S,D鍵&#xff1a;控制玩家上下左右移動。按鈕一&#xff1a;控制英雄發射一個矩形攻擊紅方小兵。按鈕控制英雄發射魅惑技能&#xff0c;傷害小兵并讓小兵停止移動。技能三&#xff1a;攻擊多個敵人并讓小兵停止移動。普攻&#xff1a;對小兵造成基礎傷害。小…

LVGL——按鈕部件

目錄 一、組成部分 二、按鈕部件操作 1、創建 2、設置樣式 3、添加事件 4、代碼例程 三、按鈕部件案例 一、組成部分 主體&#xff08;LV_PART_MAIN&#xff09; 二、按鈕部件操作 1、創建 lv_obj_t *btn lv_btn_create( parent );2、設置樣式 lv_obj_set_siz…

nginx配置自動壓縮-gzip壓縮

1.nginx配置文件 server里添加gzip配置信息。 重啟nginx服務 對比效果&#xff1a;上圖是沒有開啟gzip自動壓縮&#xff0c;總共資源是1.3M&#xff0c;傳輸1.3MB&#xff0c;下圖是開啟gzip壓縮&#xff0c;總共資源是1.3M&#xff0c;傳輸了973KB。

Axure簡單安裝與入門

目錄 一.Axure簡介 二.應用場景 三.安裝與漢化 3.1.安裝 3.2.漢化 四. 入門 4.1.復制、剪切及粘貼區域 4.2.選擇模式 4.3. 插入形狀 4.4.預覽、共享 感謝大家觀看&#xff01;希望能幫到你哦&#xff01;&#xff01;&#xff01; 一.Axure簡介 Axure RP是一款專業的原型…

四種數據庫執行腳本文件導入數據的方式

執行腳本文件的方式 Mysql mysql執行sql腳本文件的方法&#xff1a; 1、在命令行輸入mysql -uroot -h10.235.5.55 -p’123456’ -P3306 < F:\hello\niuzi.sql 2、在命令行輸入【source F:\hello\niuzi.sql】 mysql -uroot -h10.235.5.55 -p’123456’ -P3306 -e "sou…