實用STM32的串口控制平臺的實現(xiàn)(建議收藏)
來源:CSDN
1. 前言
玩過Linux的朋友, 是不是對Linux無所不能的串口Shell命令控制臺羨慕不已, 要是自己做的STM32F系列低檔次的MCU也有這種控制交互能力, 會給調(diào)試/維護和配置省下多少麻煩事呀, 比如啟動/關閉調(diào)試或自檢模式, 打印調(diào)試信息, 配置系統(tǒng)參數(shù), 傳輸文件等等, 也有相當多的朋友憑借自己出色的編程能力可以實現(xiàn)這些功能, 這里提出我的這個解決方案, 以作交流.本平臺(xc_shell)具備以下性能特點:- 大量主要代碼, 和具體硬件無關, 移植性強,代碼文件少.
- 只有在處理用戶的輸入命令時, 才占用CPU資源, 且代碼可裁剪到1KB SRAM和4KB Flash;
- 用戶可以非常靈活的添加按模板編寫的命令腳本文件, 自定義擴張能力強.
- 支持操作系統(tǒng)和非操作系統(tǒng)兩種場景應用.
- 支持Ymodem文件傳輸協(xié)議
- 支持將Flash的扇區(qū)開辟為參數(shù)區(qū), 可實現(xiàn)本地/遠程升級。
- 實用Led燈信號管理, 可將65535虛擬信號燈選擇輸出到1個實體LED燈上, 調(diào)試時序和狀態(tài)非常有用
- 擁有基礎的LED管理, 調(diào)試模式設置, 命令幫助指令, 復位指令等基礎功能
2. xc_shell平臺介紹
2.1 如何實現(xiàn)硬件無關
類比Linux會發(fā)現(xiàn), 設備的硬件接口往往會被虛擬成一個文件(驅(qū)動), 而Linux內(nèi)核完全與硬件系統(tǒng)無任何字節(jié)關聯(lián), 不同平臺驅(qū)動不同而已, 故而本xc_shell的串口驅(qū)動也采用了相似的思路:1) 串口驅(qū)動用一個結(jié)構(gòu)體描述, 這樣只需在xc_shell.c中用指針指向這個TTYx_HANDLE結(jié)構(gòu)體對象就可以將串口(tty)硬件與內(nèi)核聯(lián)系在一起, 聰明的朋友可能會想到, 假如我將帶網(wǎng)絡的開發(fā)板按此結(jié)構(gòu)體,虛擬一個TTY對象, 豈不是就可以實現(xiàn)一個網(wǎng)絡遠程控制臺了! ?這點確實是可以的!2) 當然諸如多TTY串口實現(xiàn)接口互換等, 都是一個指針和step2中的注入回調(diào)處理交換的問題。3)用戶在使用api_TxdFrame或api_TxdByte時”bsp_ttyX.c“,會驅(qū)動具體MCU的串口將數(shù)據(jù)發(fā)送出去, 收到一幀數(shù)據(jù)后,若用戶設置了inj_RcvFrame回調(diào)處理方法,則會在中斷中執(zhí)行用戶的回調(diào)處理。/*---------------------*?
*?????指正函數(shù)定義
*----------------------*/
typedef?void????(*pvFunDummy)(void);
?
//輸入整行,輸出邏輯
typedef?void????(*pvFunVoid)?(void);
typedef?void????(*pvFunBool)?(bool?????bVal);
typedef?void????(*pvFunChar)?(uint8_t??cVal);
typedef?void????(*pvFunShort)(uint16_t?sVal);
typedef?void????(*pvFunWord)?(uint32_t?wVal);
?
//輸入整行,輸出邏輯
typedef?bool????(*pbFunVoid)?(void);
typedef?bool????(*pbFunBool)?(bool?????bVal);
typedef?bool????(*pbFunChar)?(uint8_t??cVal);
typedef?bool????(*pbFunShort)(uint16_t?sVal);
typedef?bool????(*pbFunWord)?(uint32_t?wVal);
?
//輸入整形指針,輸出邏輯
typedef?bool????(*pbFun_pVoid)?(void?*?pVoid);
typedef?bool????(*pbFun_pChar)?(uint8_t??*?pStr);
typedef?bool????(*pbFun_pShort)(uint16_t?*?pShor);
typedef?bool????(*pbFun_pWord)?(uint32_t?*?pWord);
?
//輸入數(shù)據(jù)幀,輸出邏輯
typedef?bool????(*pbFun_Buffx)(void?*?pcBuff,?uint16_t?len?);
typedef?bool????(*pbFun_Bytex)(uint8_t?*?pcByte,?uint16_t?len?);
/*---------------------*?
*????TTYx?句柄結(jié)構(gòu)
*----------------------*/
typedef?struct?TTYx_HANDLE_STRUCT?
{
????const?char??*?const?name;???????//驅(qū)動器名
????const?uint16_t??????rxSize;?????//接收大小
????const?uint16_t??????txSize;?????//發(fā)送大小
????
????//------------------------------------------------------
????//step1:?用戶可用API
????const?pvFunWord?????init;???????????//初始化.
????const?pbFun_Bytex???api_TxdFrame;???//發(fā)送數(shù)據(jù)幀.?(發(fā)送幀)
????const?pbFunChar?????api_TxdByte;????//發(fā)送數(shù)據(jù)字節(jié)
????
????//------------------------------------------------------
????//step2:?注入回調(diào)函數(shù)
????pbFun_Bytex?????????inj_RcvFrame;???//(ISR)接收數(shù)據(jù)幀.?(接收幀)
????pvFunDummy??????????inj_TxdReady;???//(ISR)發(fā)送完畢回調(diào)
????
????//------------------------------------------------------
????//step3:?接收回調(diào)函數(shù)
????struct?TTYx_HANDLE_STRUCT?*?pvNext;?//連接到下一個指令?
}TTYx_HANDLE;
- 可注入的命令腳本(CLI)實現(xiàn)
/*---------------------*?
*???????CLI指令
*----------------------*/
typedef?struct
{
?const?char?*?const??pcCmdStr;?????//指令字符串(只能為小寫字母)
?const?char?*?const??pcHelpStr;?????//指令描述,必須以:"\r\n結(jié)束".?比如:"help:?Returns?a?list\r\n".
?const?pFunHook??????pxCmdHook;?????//指向回調(diào)函數(shù)的指針,處理成功返回真否者返回0;
?uint8_t?????????????ucExpParam;?????//指令期望的參數(shù)個數(shù)
?const?MEDIA_HANDLE?*phStorage;??????//指向存儲介質(zhì),沒有的話填充NULL??
}Cmd_Typedef_t;
各位朋友可能會使用到非常多的自定義CLI命令, 格式諸如這個網(wǎng)卡的命令:
const?Cmd_Typedef_t?CLI_WizMsg=
{
????//識別關鍵字
????.pcCmdStr???=?"wiz",
????//幫助內(nèi)容
????.pcHelpStr??=
????"[WIZ?contorls]\r\n"
?"?wiz?help\r\n"
?"?wiz?rd?info\r\n"
?"?wiz?reset\r\n"
?"?wiz?wr?ip?...\r\n"
?"?wiz?wr?mask?...\r\n"
?"?wiz?wr?way?...\r\n"
?"?wiz?wr?mac?-----\r\n"
?"?wiz?wr?port????\r\n"
?"?wiz?wr?sip?...?\r\n"
?"?wiz?wr?cip?...?\r\n"
?"?wiz?load?default\r\n"
?"[WIZ?Test?mode]\r\n"
?"?wiz?loop?open\r\n"
?"?wiz?loop?close\r\n"
?"\r\n",
?
?//處理函數(shù)
?.pxCmdHook??=?