最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

單片機(jī)串口控制臺(tái)方案

2023-04-23 22:18 作者:老閑漁  | 我要投稿

通過串口使用類似Linux命令的方式控制單片機(jī),大幅提高調(diào)試效率,可以在非debug模式下實(shí)現(xiàn)類似debug的效果

網(wǎng)上找了找類似的東西,好像資料不是很多的樣子,F(xiàn)reeRTOS提供了解決方案,RTthread也有FinSH控制臺(tái),但是感覺有點(diǎn)復(fù)雜,所以自己做了一個(gè),非常輕量,快速,沒有多余的功能

????????串口接收部分使用FreeRTOS操作系統(tǒng)+任務(wù)通知+DMA+空閑中斷實(shí)現(xiàn)(參考:CH32串口接收方案),當(dāng)然也可以自己使用其他的方式去實(shí)現(xiàn)

效果展示

在終端中輸入? list,用于顯示當(dāng)前已注冊(cè)命令

list命令

在終端中輸入 info,顯示當(dāng)前已有任務(wù)的剩余堆棧,這對(duì)于合理分配任務(wù)大小很有幫助

info命令

大致工作流程

????????首先初始化控制臺(tái),主要是啟動(dòng)串口接收,因?yàn)榭刂婆_(tái)使用了動(dòng)態(tài)單向鏈表,所以還需要為頭節(jié)點(diǎn)分配內(nèi)存,初始化完成后即可將自己需要的命令字和功能回調(diào)函數(shù)注冊(cè)進(jìn)去(命令字和功能函數(shù)是一一對(duì)應(yīng)的,每個(gè)命令都對(duì)應(yīng)了一個(gè)特定的功能函數(shù)),之后由串口接收任務(wù)等待上位機(jī)命令,接收到命令后根據(jù)命令去調(diào)用指定的功能函數(shù)即可

詳解

一、?相關(guān)定義(.h)

console.h

????????首先需要一個(gè)函數(shù)指針類型,這個(gè)類型就是注冊(cè)進(jìn)系統(tǒng)的功能回調(diào)函數(shù)的類型,無返回值,有一個(gè)char* 類型的參數(shù),可以用于接收上位機(jī)發(fā)送的指令,進(jìn)行參數(shù)解析,用于實(shí)現(xiàn)類似于“setv -1 -200 -3000”這樣的帶參效果

????????然后定義了一個(gè)自定義的錯(cuò)誤類型,這個(gè)可有可無

????????最后是鏈表的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)由 一個(gè)命令字+一個(gè)功能函數(shù)+下個(gè)節(jié)點(diǎn)的地址? 組成

二、?具體實(shí)現(xiàn)(.C)

console.c

1.全局變量

????????定義了一個(gè)任務(wù)句柄,可有可無

????????定義了一個(gè)鏈表頭指針,用于查找控制整個(gè)鏈表

????????定義了一個(gè)串口接收數(shù)組,用于接收命令,空間可以給大點(diǎn)也可以給小一點(diǎn),因?yàn)槊畈⒉粫?huì)很長,但是考慮到誤操作的問題,太小了容易導(dǎo)致溢出

2.串口相關(guān)

????????第一個(gè)函數(shù)用于啟動(dòng)串口接收,使用DMA+串口空閑中斷的方式

????????第二函數(shù)是接收回調(diào)函數(shù),這個(gè)函數(shù)被放置在STM32的串口空閑事件中(

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)

)其他單片機(jī)則放置在對(duì)應(yīng)的中斷觸發(fā)處,用于通知控制臺(tái)任務(wù)執(zhí)行以及使能下次接收

3.初始化控制臺(tái)

????????首先調(diào)用上面寫好的函數(shù)完成串口配置,然后為之前定義的頭指針分配一塊內(nèi)存,做一下簡單的空指針判斷,隨后完成初始化

4.命令注冊(cè)

????????該函數(shù)擁有兩個(gè)參數(shù),分別是指令和對(duì)應(yīng)函數(shù),首先定義一個(gè)“now”臨時(shí)節(jié)點(diǎn),讓他等于頭節(jié)點(diǎn),然后判斷他的 指向下個(gè)節(jié)點(diǎn)的 指針變量 是否為空,如果不為空則說明不是尾節(jié)點(diǎn),需要判斷一下當(dāng)前節(jié)點(diǎn)的指令和參數(shù)傳入的指令是否一致,如果一致則說明指令重復(fù),退出函數(shù)即可,如果指令不一樣,則讓now節(jié)點(diǎn)等于該變量指向的節(jié)點(diǎn),隨后進(jìn)入下一個(gè)循環(huán)判斷,直到找到尾節(jié)點(diǎn),找到尾節(jié)點(diǎn)后即可分配一塊節(jié)點(diǎn)內(nèi)存,并讓尾節(jié)點(diǎn)指向這個(gè)節(jié)點(diǎn),同時(shí)使用傳入?yún)?shù)為該節(jié)點(diǎn)變量進(jìn)行賦值,至此完成命令注冊(cè)

5. 命令卸載

????????使用該函數(shù)可以實(shí)現(xiàn)注銷一個(gè)指令的操作,但是我覺得沒什么用,因?yàn)樽?cè)了命令之后基本上也不會(huì)有什么注銷的需求,所以就沒寫(笑),如果需要的話可以自己實(shí)現(xiàn),無非就是比較到相同的指令,然后釋放這個(gè)節(jié)點(diǎn)的內(nèi)存,并讓上個(gè)節(jié)點(diǎn)指向下個(gè)節(jié)點(diǎn)

6.?命令調(diào)度

????????函數(shù)傳入的是終端輸入的命令字符串,首先用自制的字符串比較方法進(jìn)行指令的比較判斷,一個(gè)一個(gè)比較,直到發(fā)現(xiàn)不一樣的字符,此時(shí)看看節(jié)點(diǎn)的命令 是否 每個(gè)字符都比較完了,通過字符串結(jié)尾的\0判斷,如果當(dāng)前index的位置不是\0說明還沒比較結(jié)束就出現(xiàn)了不一樣的字符,指令不匹配進(jìn)入下一次循環(huán),如果是\0則說明匹配到了一個(gè)節(jié)點(diǎn),則可以調(diào)用該節(jié)點(diǎn)內(nèi)部的功能函數(shù),并且將上位機(jī)輸入內(nèi)容以參數(shù)傳進(jìn),繼續(xù)做參數(shù)解析(這樣子的好處是只關(guān)心輸入內(nèi)容的前面一部分是否與指令相同,而不關(guān)心其他的部分,可以方便的做指令參數(shù),例如注冊(cè)的指令為“open”,輸入的內(nèi)容為“open -1 -2”,即可匹配到open命令,并將整個(gè)字符串傳入到功能函數(shù)中,在函數(shù)內(nèi)解析出剩下的參數(shù))

7. 命令展示

????????一個(gè)功能函數(shù),用于打印出當(dāng)前已注冊(cè)的命令有哪些


????

終端介紹

? ? ? ??在開始下一節(jié)之前先介紹一個(gè)概念,終端的輸入方式和串口調(diào)試助手的輸入方式是不一樣的,在串口調(diào)試助手中會(huì)先輸完完整的信息,然后點(diǎn)擊發(fā)送,單片機(jī)一次性收到一整條消息,并且也能看到自己到底寫了什么,如下圖

一次發(fā)送一整條

???????終端則截然不同,首先終端的輸入是一個(gè)字一個(gè)字發(fā)送的,假設(shè)在終端中鍵盤按下了一個(gè)h,此時(shí)這個(gè)h就已經(jīng)發(fā)送給單片機(jī)了,并且終端上并不會(huì)顯示h,因?yàn)榻K端只管發(fā)送,要想看到自己寫了什么內(nèi)容,需要單片機(jī)接收到字符后再發(fā)送回終端,以此才能實(shí)現(xiàn)類似于Linux終端那樣的效果

????????我使用終端是MobaXterm,不用付費(fèi)也能用,為什么要用終端不用串口助手呢,因?yàn)?/span>終端可以進(jìn)行顏色的解析,大部分串口助手只能顯示黑色的字,無法解析顏色


8. 控制臺(tái)任務(wù)

????????首先定義一個(gè)臨時(shí)數(shù)組,因?yàn)榻K端發(fā)送數(shù)據(jù)是一個(gè)一個(gè)發(fā)的,所以不能以一幀做為結(jié)束,在判斷回車結(jié)束之前需要存放在這個(gè)緩存區(qū)

????????將 查看已存在命令 的函數(shù)注冊(cè)到控制臺(tái)中,這樣就可以在控制臺(tái)查看已有命令了,將該函數(shù)與“l(fā)ist”命令綁定

????????使用任務(wù)等待,阻塞在這里,直到收到數(shù)據(jù)為止

????????收到消息后就將數(shù)據(jù)存入緩沖區(qū),同時(shí)記錄當(dāng)前數(shù)據(jù)的累計(jì)大小

????????判斷一下當(dāng)前數(shù)據(jù)的結(jié)尾是不是\r或者\(yùn)n,如果是的話則說明一幀結(jié)束,同時(shí)判斷一下數(shù)據(jù)大小,如果發(fā)現(xiàn)沒有數(shù)據(jù),只是在終端單純敲了個(gè)回車的話,則打印一個(gè)>>,看起來更有感覺,如果有數(shù)據(jù)則使用cs_fun_callback()函數(shù)進(jìn)行命令分發(fā)

>>

????????終端里輸入命令的時(shí)候可能會(huì)輸錯(cuò),所以需要實(shí)現(xiàn)退格刪除功能,單片機(jī)檢測(cè)退格,如果發(fā)現(xiàn)輸入了退格,先把緩沖區(qū)的下標(biāo)記錄變量-2,為什么是-2不是-1,因?yàn)槭紫纫獪p掉當(dāng)前輸入的退格鍵,然后再減掉真正輸錯(cuò)的那個(gè)字,同時(shí)在控制臺(tái)先打印一個(gè)退格,讓光標(biāo)往前移動(dòng)一格,再打印一個(gè)空格,遮蓋掉打錯(cuò)的字,然后再發(fā)送一個(gè)退格,退回到即將輸入的位置

????????如果都不是上述的情況,則說明只是普通的輸入了一個(gè)字符,將這個(gè)字符通過終端打印出來,實(shí)現(xiàn)“回顯”的效果

????????當(dāng)然最重要的,要?jiǎng)?chuàng)建一個(gè)線程,讓他跑起來


9. 功能函數(shù)示例

????????該函數(shù)實(shí)現(xiàn)了控制 某個(gè)電機(jī) 以 指定速度 轉(zhuǎn)動(dòng) 多少圈,傳入?yún)?shù)由命令分發(fā)函數(shù)傳入,內(nèi)容就是輸入的串口內(nèi)容

設(shè)置電機(jī)位置

用字符串切割函數(shù)即可取出所需的參數(shù),通過函數(shù)轉(zhuǎn)換為自己想要的類型后即可使用

結(jié)束

????????至此內(nèi)容結(jié)束,完成了完整的控制臺(tái)功能,可以自定義任意多的命令,并在單片機(jī)運(yùn)行的時(shí)候隨時(shí)使用,比如需要檢測(cè)任務(wù)剩余堆棧,以前需要一直打印信息,導(dǎo)致串口上有一大堆無用數(shù)據(jù),淹沒了真正重要的有價(jià)值的數(shù)據(jù),現(xiàn)在只需要將打印內(nèi)存信息的功能封裝成一個(gè)函數(shù),注冊(cè)一個(gè)命令,即可在適當(dāng)?shù)臅r(shí)候人為控制打印一次,方便簡潔

????????還可以注冊(cè)一些可能需要靈活更改、變化的功能,比如以前想要改某個(gè)參數(shù)或功能需要在代碼當(dāng)中更改,然后重新編譯下載,但是現(xiàn)在只需要提前做好功能函數(shù),即可通過控制臺(tái)隨時(shí)更改,極大的方便了測(cè)試

????????總之好處多多,該方案為本人原創(chuàng),歡迎提出意見?(?? ? ??)?

????????感謝閱讀(●'?'●)


單片機(jī)串口控制臺(tái)方案的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
清流县| 冀州市| 孟州市| 临澧县| 弋阳县| 卢龙县| 彰化市| 临漳县| 高阳县| 阿合奇县| 古浪县| 大港区| 城步| 扎兰屯市| 游戏| 天长市| 凤翔县| 靖宇县| 剑阁县| 远安县| 茌平县| 揭阳市| 大田县| 三江| 开阳县| 崇文区| 上饶县| 郎溪县| 崇仁县| 西畴县| 荣昌县| 治多县| 云安县| 晋江市| 久治县| 安岳县| 和静县| 丹凤县| 麻阳| 武宣县| 新河县|