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