1. 前言
玩過Linux的朋友, 是不是對(duì)Linux無所不能的串口Shell命令控制臺(tái)羨慕不已, 要是自己做的STM32F系列低檔次的MCU也有這種控制交互能力, 會(huì)給調(diào)試/維護(hù)和配置省下多少麻煩事呀, 比如啟動(dòng)/關(guān)閉調(diào)試或自檢模式, 打印調(diào)試信息, 配置系統(tǒng)參數(shù), 傳輸文件等等, 也有相當(dāng)多的朋友憑借自己出色的編程能力可以實(shí)現(xiàn)這些功能, 這里提出我的這個(gè)解決方案, 以作交流.
本平臺(tái)(xc_shell)具備以下性能特點(diǎn):
大量主要代碼, 和具體硬件無關(guān), 移植性強(qiáng),代碼文件少.
只有在處理用戶的輸入命令時(shí), 才占用CPU資源, 且代碼可裁剪到1KB SRAM和4KB Flash;
用戶可以非常靈活的添加按模板編寫的命令腳本文件, 自定義擴(kuò)張能力強(qiáng).
支持操作系統(tǒng)和非操作系統(tǒng)兩種場(chǎng)景應(yīng)用.
支持Ymodem文件傳輸協(xié)議
支持將Flash的扇區(qū)開辟為參數(shù)區(qū), 可實(shí)現(xiàn)本地/遠(yuǎn)程升級(jí)。
實(shí)用Led燈信號(hào)管理, 可將65535虛擬信號(hào)燈選擇輸出到1個(gè)實(shí)體LED燈上, 調(diào)試時(shí)序和狀態(tài)非常有用
擁有基礎(chǔ)的LED管理, 調(diào)試模式設(shè)置, 命令幫助指令, 復(fù)位指令等基礎(chǔ)功能
功能越多設(shè)計(jì)會(huì)越復(fù)雜, 為了解釋清楚代碼, 先向大家解釋一下以上功能的基礎(chǔ)實(shí)現(xiàn)原理, 并提供一個(gè)最小的的源碼工程。
2. xc_shell平臺(tái)介紹
2.1 如何實(shí)現(xiàn)硬件無關(guān)
類比Linux會(huì)發(fā)現(xiàn), 設(shè)備的硬件接口往往會(huì)被虛擬成一個(gè)文件(驅(qū)動(dòng)), 而Linux內(nèi)核完全與硬件系統(tǒng)無任何字節(jié)關(guān)聯(lián), 不同平臺(tái)驅(qū)動(dòng)不同而已, 故而本xc_shell的串口驅(qū)動(dòng)也采用了相似的思路:
1) 串口驅(qū)動(dòng)用一個(gè)結(jié)構(gòu)體描述, 這樣只需在xc_shell.c中用指針指向這個(gè)TTYx_HANDLE結(jié)構(gòu)體對(duì)象就可以將串口(tty)硬件與內(nèi)核聯(lián)系在一起, 聰明的朋友可能會(huì)想到, 假如我將帶網(wǎng)絡(luò)的開發(fā)板按此結(jié)構(gòu)體,虛擬一個(gè)TTY對(duì)象, 豈不是就可以實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)遠(yuǎn)程控制臺(tái)了! ?這點(diǎn)確實(shí)是可以的!
2) 當(dāng)然諸如多TTY串口實(shí)現(xiàn)接口互換等, 都是一個(gè)指針和step2中的注入回調(diào)處理交換的問題。
3)用戶在使用api_TxdFrame或api_TxdByte時(shí)”bsp_ttyX.c“,會(huì)驅(qū)動(dòng)具體MCU的串口將數(shù)據(jù)發(fā)送出去, 收到一幀數(shù)據(jù)后,若用戶設(shè)置了inj_RcvFrame回調(diào)處理方法,則會(huì)在中斷中執(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ū)動(dòng)器名 ????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;?//連接到下一個(gè)指令? }TTYx_HANDLE;
?
?
可注入的命令腳本(CLI)實(shí)現(xiàn)
命令CLI也是一個(gè)結(jié)構(gòu)體對(duì)象:
?
?
/*---------------------*? *???????CLI指令 *----------------------*/ typedef?struct { ?const?char?*?const??pcCmdStr;?????//指令字符串(只能為小寫字母) ?const?char?*?const??pcHelpStr;?????//指令描述,必須以:" 結(jié)束".?比如:"help:?Returns?a?list ". ?const?pFunHook??????pxCmdHook;?????//指向回調(diào)函數(shù)的指針,處理成功返回真否者返回0; ?uint8_t?????????????ucExpParam;?????//指令期望的參數(shù)個(gè)數(shù) ?const?MEDIA_HANDLE?*phStorage;??????//指向存儲(chǔ)介質(zhì),沒有的話填充NULL?? }Cmd_Typedef_t;?
?
?
各位朋友可能會(huì)使用到非常多的自定義CLI命令, 格式諸如這個(gè)網(wǎng)卡的命令:
?
?
const?Cmd_Typedef_t?CLI_WizMsg= { ????//識(shí)別關(guān)鍵字 ????.pcCmdStr???=?"wiz", ????//幫助內(nèi)容 ????.pcHelpStr??= ????"[WIZ?contorls] " ?"?wiz?help " ?"?wiz?rd?info " ?"?wiz?reset " ?"?wiz?wr?ip?. . . " ?"?wiz?wr?mask? . . . " ?"?wiz?wr?way? . . . " ?"?wiz?wr?mac? - -
-
-
-
" ?"?wiz?wr?port?
? ? ? " ?"?wiz?wr?sip? . . . ? " ?"?wiz?wr?cip? . . . ? " ?"?wiz?load?default " ?"[WIZ?Test?mode] " ?"?wiz?loop?open " ?"?wiz?loop?close " ?" ", ? ?//處理函數(shù) ?.pxCmdHook??=?&Shell_WIZ_Service,????????//見實(shí)體函數(shù) ? ?//附帶數(shù)據(jù) ?.ucExpParam?=?0, ? ??#ifdef?SHELL_USE_YMODEM?? ????//存儲(chǔ)介質(zhì) ????.phStorage??=?NULL, ??#endif }; /*---------------------*? *???????CLI?鏈表節(jié) *----------------------*/ Cmd_List_t??WizList???=?{?&CLI_WizMsg,???NULL,};?//Shell指令的頭
?
?
如配置IP地址輸入“wiz wr ip 192.168.1.250 ”則可以了
3. 環(huán)境準(zhǔn)備
3.1 硬件開發(fā)環(huán)境
STM32F103系列開發(fā)板一塊, 帶USART1接口。
USB轉(zhuǎn)串口線一根。
3.2 軟件開發(fā)環(huán)境
MDK4.72或以上
SecureCRT串口超級(jí)終端
3.3 軟件配置
在xc_shell使用“/r/n”作為命令的結(jié)束符, 而SecureCRT按下Enter不是輸入“/r/n”故而需要按下圖設(shè)置:此外在《終端》/仿真/高級(jí)中選取【本地回顯】?
4. 代碼介紹
4.1 目錄結(jié)構(gòu)
?
?
□?XC_SHELL ├──┬──?BSP_LIB????BSP庫(kù),硬件相關(guān)驅(qū)動(dòng)代買 │??├────?bsp_ledx.c???基礎(chǔ)LED驅(qū)動(dòng)?????? │??└────?bsp_tty0.c???調(diào)試串口驅(qū)動(dòng) │ ├──┬──MDK-ARM?????工程文件 │??└────?Project.uvproj │ ├──┬──SHELL_CFG???SHELL配置頭文件 │??└────?user_eval.h │ ├──┬──SHELL_CORE??SHELL內(nèi)核文件 │??├────?xc_shell.c???SHELL內(nèi)核文件 │??├────?xc_ymodem.c??Ymodem傳輸協(xié)議(默認(rèn)關(guān)閉,在xc_shell.h中啟動(dòng)) │??├────?xc_iap.c?????Flash的IAP操作,需要bsp_flash.c驅(qū)動(dòng)支持 │??└────?shell_iap.c??shell的用戶腳本模板 │ ├──┬──SHELL_INC???SHELL頭文件 │??├────?bsp_type.h???驅(qū)動(dòng)結(jié)構(gòu)定義 │??├────?xc_shell.h???SHELL頭文件 │??└────?xconfig.h????硬件無關(guān)配置文件 │ ├──┬──STM32F10x_StdPeriph_Lib_V3.5.0??STM32的標(biāo)準(zhǔn)外設(shè)庫(kù) │??└────?...... │ └──┬──USER????????用戶文件???? ???├─?.....??????? ???└────?main.c????????main文件
?
?
4.2 工程設(shè)置要點(diǎn)
1) 設(shè)置使用微庫(kù):
2)配置包含和路徑,注意使用了“--c99”標(biāo)準(zhǔn),如圖
3) 編譯工程,無錯(cuò)誤警告后燒寫程序到開發(fā)板運(yùn)行。
4.3 最終效果
按圖輸入一下指令,SHELL平臺(tái)會(huì)回復(fù)相關(guān)信息。其中輸入“l(fā)ed set 0=1”會(huì)將信號(hào)1分配到物理LED0上;輸入“l(fā)ed set 0=2”會(huì)將信號(hào)2分配到物理LED0上。這樣用戶編寫程序代碼時(shí)相當(dāng)于擁有了超級(jí)多的LED信號(hào)可用,調(diào)試時(shí)序非常有用。?
5. 添加自己的指令腳本
5.1 源代碼示例
假設(shè)我要編寫一個(gè)自己的指令腳本, 來讀取MCU的關(guān)鍵信息,關(guān)鍵字為mcu, 文件命名為shell_mcu.c;當(dāng)輸入“mcu rd 0”時(shí)顯示MCU的FLASH大小,輸入“mcu rd 1”時(shí)讀取MCU的唯一ID信息。shell_mcu.c源代碼:
?
?
/*********************************Copyright?(c)********************************* **??????????????????????????????? **?????????????????????????????????FIVE工作組 ** **---------------------------------File?Info------------------------------------ **?File?Name:???????????????shell_mcu.c **?Last?modified?Date:??????2017/9/17?1557 **?Last?Version:????????????V1.0 **?Description:?????????????shell測(cè)試 ** **------------------------------------------------------------------------------ **?Created?By:??????????????wanxuncpx **?Created?date:????????????2017/9/17?1508 **?Version:?????????????????V1.0 **?Descriptions:????????????none **------------------------------------------------------------------------------ **?HW_CMU:??????????????????STM32F103 **?Libraries:???????????????STM32F10x_StdPeriph_Lib_V3.5.0 **?version??????????????????V3.5 *******************************************************************************/ ? /****************************************************************************** 更新說明: ******************************************************************************/ ? /****************************************************************************** *********************************??編?譯?控?制?******************************** ******************************************************************************/ #define?MCU_SHELL???????????????//注釋掉時(shí)屏蔽iap?shell功能 ? #include?"xc_shell.h"???????//Shell支持文件,含bool,uint8_t..以及串口數(shù)據(jù)收發(fā)操作 /****************************************************************************** *********************************?文件引用部分?******************************** ******************************************************************************/ /*---------------------*? *?????模塊驅(qū)動(dòng)引用 *----------------------*/ //#include?"net_w5500.h" ? #ifdef?MCU_SHELL /****************************************************************************** **********************************?Shell實(shí)例?********************************** ******************************************************************************/ /*---------------------*? *??????CLI指令服務(wù) *----------------------*/ extern?bool?Shell_MCU_Service(void?*?pcBuff,?uint16_t?len?); ? /*---------------------*? *???????CLI?結(jié)構(gòu) *----------------------*/ const?Cmd_Typedef_t?CLI_McuMsg= { ????//識(shí)別關(guān)鍵字 ????"mcu", ???? ????//幫助內(nèi)容 ?"[mcu?contorls] " ?"?mcu?rd?-?Read?FLASH?information. " ?" ", ? ?//處理函數(shù) ?&Shell_MCU_Service, ? ?//附帶數(shù)據(jù) ?0, ??#ifdef?SHELL_USE_YMODEM?? ????//存儲(chǔ)介質(zhì) ????NULL, ??#endif }; ? /*---------------------*? *?????CLI鏈表節(jié)(輸出) *----------------------*/ Cmd_List_t??McuList??=?{&CLI_McuMsg??????,NULL};?//IAP指令鏈表 ? /****************************************************************************** *********************************?函?數(shù)?聲?明?********************************* ******************************************************************************/ /****************************************************************************** /?函數(shù)功能:STM32F103控制函數(shù) /?修改日期:2015/7/14?2002 /?輸入?yún)?shù):none /?輸出參數(shù):none /?使用說明:需要執(zhí)行約10s ******************************************************************************/ static?bool?FLASH_ioctl(uint8_t?cmd,void?*?param) { ????#define?UID_ADDR????????????0x1FFFF7E0??//閃存容量寄存器,值對(duì)應(yīng)KB單位 ????#define?MAC_ADDR????????????0x1FFFF7E8??//MCU的唯一ID號(hào),共12個(gè)字節(jié) ????#define?UID_SIZE????????????2???????????//UID的字節(jié)數(shù) ????#define?MAC_SIZE????????????12??????????//MAC的字節(jié)數(shù) ? ????//step1:?檢查參數(shù) ????if(!param)return?false; ???????? ????//step2:?處理數(shù)據(jù) ????switch(cmd){ ??????case?0?:?{???????//獲取FLASH的的UID ????????uint16_t?*?ptDst?=?(uint16_t?*)((uint32_t)param+1); ???????? ????????*ptDst?=?*(uint16_t?*)UID_ADDR; ????????*(uint8_t??*)param?=??UID_SIZE; ????????return?true; ??????} ??????case?1?:?{???????//獲取芯片的MAC地址 ????????uint32_t?*?ptDst?=?(uint32_t?*)((uint32_t)param+1); ????????uint32_t?*?ptSrc?=?(uint32_t?*)MAC_ADDR; ???? ????????*ptDst++?=?*ptSrc++; ????????*ptDst++?=?*ptSrc++; ????????*ptDst++?=?*ptSrc++; ????????*(uint8_t??*)param?=?MAC_SIZE; ????????return?true; ??????} ??????default:return?false; ????} } ? /****************************************************************************** /?函數(shù)功能:文件系統(tǒng)Shel指令處理 /?修改日期:2013/9/10?1915 /?輸入?yún)?shù):輸入當(dāng)前的程序版本 /?輸出參數(shù):none /?使用說明:none ******************************************************************************/ bool?Shell_MCU_Service(void?*?pcBuff,?uint16_t?len?) { ????uint8_t????*ptRxd;??????????//用于接收指令處理 ????int?????????i; ????uint16_t????retval; ????uint8_t?????buff[32]; ???? ????//處理指令 ????//-------------------------------------------------------------------------- ????ptRxd?=?(uint8_t?*)pcBuff; ???? ????if(StrComp(ptRxd,"rd?"))?//讀取FLASH信息 ????{ ????????int?wval; ???????? ????????if(1?!=?sscanf((void?*)ptRxd,"%*s%d",&wval)?)return?false; ????????if(?wval>2?)return?false; ????????if(0==wval)?{ ????????????FLASH_ioctl(0,buff); ????????????retval?=?*(uint16_t?*)(buff+1)?; ????????????printf("->Flash: %dKB ",retval); ????????????return?true; ????????} ????????else?if(1==wval)?{ ????????????FLASH_ioctl(1,buff); ????????????printf("->MAC: ?"); ????????????for(i=0;?i ?
?
5.2 實(shí)現(xiàn)步驟
1) 將該文件添加到工程下。
2) 在main.c中用extern 引用McuList,源代碼為:
?
?
/*---------------------*? *?????Shell指令鏈表 *----------------------*/ extern?Cmd_List_t??McuList;?
?
3)在main.c初始化時(shí)添加:
?
?
//---------------------------------------------------------- //step1:?shell初始化 shell_Init(115200,ledx_cfg);????????//初始化shell接口 CLI_AddCmd(&McuList);?????//添加模塊指令到鏈表?
?
4)編譯工程文件。
5)下載到開發(fā)板運(yùn)行即可在終端下看到新支持的CLI指令:
審核編輯:黃飛
評(píng)論
查看更多