
出品 | 網(wǎng)絡(luò)素材
玩過Linux的朋友, 是不是對Linux無所不能的串口Shell命令控制臺羨慕不已, 要是自己做的STM32F系列低檔次的MCU也有這種控制交互能力, 會給調(diào)試/維護(hù)和配置省下多少麻煩事呀, 比如啟動/關(guān)閉調(diào)試或自檢模式, 打印調(diào)試信息, 配置系統(tǒng)參數(shù), 傳輸文件等等, 也有相當(dāng)多的朋友憑借自己出色的編程能力可以實(shí)現(xiàn)這些功能, 這里提出我的這個解決方案, 以作交流.
本平臺(xc_shell)具備以下性能特點(diǎn):
大量主要代碼, 和具體硬件無關(guān), 移植性強(qiáng),代碼文件少.
只有在處理用戶的輸入命令時, 才占用CPU資源, 且代碼可裁剪到1KB SRAM和4KB Flash;
用戶可以非常靈活的添加按模板編寫的命令腳本文件, 自定義擴(kuò)張能力強(qiáng).
支持操作系統(tǒng)和非操作系統(tǒng)兩種場景應(yīng)用.
支持Ymodem文件傳輸協(xié)議
支持將Flash的扇區(qū)開辟為參數(shù)區(qū), 可實(shí)現(xiàn)本地/遠(yuǎn)程升級。
實(shí)用Led燈信號管理, 可將65535虛擬信號燈選擇輸出到1個實(shí)體LED燈上, 調(diào)試時序和狀態(tài)非常有用
擁有基礎(chǔ)的LED管理, 調(diào)試模式設(shè)置, 命令幫助指令, 復(fù)位指令等基礎(chǔ)功能
功能越多設(shè)計(jì)會越復(fù)雜, 為了解釋清楚代碼, 先向大家解釋一下以上功能的基礎(chǔ)實(shí)現(xiàn)原理, 并提供一個最小的的源碼工程。
類比Linux會發(fā)現(xiàn), 設(shè)備的硬件接口往往會被虛擬成一個文件(驅(qū)動), 而Linux內(nèi)核完全與硬件系統(tǒng)無任何字節(jié)關(guān)聯(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)絡(luò)的開發(fā)板按此結(jié)構(gòu)體,虛擬一個TTY對象, 豈不是就可以實(shí)現(xiàn)一個網(wǎng)絡(luò)遠(yuǎn)程控制臺了! ?這點(diǎn)確實(shí)是可以的!
2) 當(dāng)然諸如多TTY串口實(shí)現(xiàn)接口互換等, 都是一個指針和step2中的注入回調(diào)處理交換的問題。
3)用戶在使用api_TxdFrame或api_TxdByte時”bsp_ttyX.c“,會驅(qū)動具體MCU的串口將數(shù)據(jù)發(fā)送出去, 收到一幀數(shù)據(jù)后,若用戶設(shè)置了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也是一個結(jié)構(gòu)體對象:
/*---------------------*?
*???????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=
{
????//識別關(guān)鍵字
????.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??=?&Shell_WIZ_Service,????????//見實(shí)體函數(shù)
?
?//附帶數(shù)據(jù)
?.ucExpParam?=?0,
?
??#ifdef?SHELL_USE_YMODEM??
????//存儲介質(zhì)
????.phStorage??=?NULL,
??#endif
};
/*---------------------*?
*???????CLI?鏈表節(jié)
*----------------------*/
Cmd_List_t??WizList???=?{?&CLI_WizMsg,???NULL,};?//Shell指令的頭
如配置IP地址輸入“wiz wr ip 192.168.1.250\r\n”則可以了
在xc_shell使用“/r/n”作為命令的結(jié)束符, 而SecureCRT按下Enter不是輸入“/r/n”故而需要按下圖設(shè)置:此外在《終端》/仿真/高級中選取【本地回顯】

□?XC_SHELL
├──┬──?BSP_LIB????BSP庫,硬件相關(guān)驅(qū)動代買
│??├────?bsp_ledx.c???基礎(chǔ)LED驅(qū)動??????
│??└────?bsp_tty0.c???調(diào)試串口驅(qū)動
│
├──┬──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中啟動)
│??├────?xc_iap.c?????Flash的IAP操作,需要bsp_flash.c驅(qū)動支持
│??└────?shell_iap.c??shell的用戶腳本模板
│
├──┬──SHELL_INC???SHELL頭文件
│??├────?bsp_type.h???驅(qū)動結(jié)構(gòu)定義
│??├────?xc_shell.h???SHELL頭文件
│??└────?xconfig.h????硬件無關(guān)配置文件
│
├──┬──STM32F10x_StdPeriph_Lib_V3.5.0??STM32的標(biāo)準(zhǔn)外設(shè)庫
│??└────?......
│
└──┬──USER????????用戶文件????
???├─?.....???????
???└────?main.c????????main文件
1) 設(shè)置使用微庫:

2)配置包含和路徑,注意使用了“--c99”標(biāo)準(zhǔn),如圖

3) 編譯工程,無錯誤警告后燒寫程序到開發(fā)板運(yùn)行。
按圖輸入一下指令,SHELL平臺會回復(fù)相關(guān)信息。其中輸入“l(fā)ed set 0=1”會將信號1分配到物理LED0上;輸入“l(fā)ed set 0=2”會將信號2分配到物理LED0上。這樣用戶編寫程序代碼時相當(dāng)于擁有了超級多的LED信號可用,調(diào)試時序非常有用。
假設(shè)我要編寫一個自己的指令腳本, 來讀取MCU的關(guān)鍵信息,關(guān)鍵字為mcu, 文件命名為shell_mcu.c;當(dāng)輸入“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?15:13:57
**?Last?Version:????????????V1.0
**?Description:?????????????shell測試
**
**------------------------------------------------------------------------------
**?Created?By:??????????????wanxuncpx
**?Created?date:????????????2017/9/17?15:14:08
**?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..以及串口數(shù)據(jù)收發(fā)操作
/******************************************************************************
*********************************?文件引用部分?********************************
******************************************************************************/
/*---------------------*?
*?????模塊驅(qū)動引用
*----------------------*/
//#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=
{
????//識別關(guān)鍵字
????"mcu",
????
????//幫助內(nèi)容
?"[mcu?contorls]\r\n"
?"?mcu?rd?\t\t-?Read?FLASH?information.\r\n"
?"\r\n",
?
?//處理函數(shù)
?&Shell_MCU_Service,
?
?//附帶數(shù)據(jù)
?0,
??#ifdef?SHELL_USE_YMODEM??
????//存儲介質(zhì)
????NULL,
??#endif
};
?
/*---------------------*?
*?????CLI鏈表節(jié)(輸出)
*----------------------*/
Cmd_List_t??McuList??=?{&CLI_McuMsg??????,NULL};?//IAP指令鏈表
?
/******************************************************************************
*********************************?函?數(shù)?聲?明?*********************************
******************************************************************************/
/******************************************************************************
/?函數(shù)功能:STM32F103控制函數(shù)
/?修改日期:2015/7/14?20:22:02
/?輸入?yún)?shù):none
/?輸出參數(shù):none
/?使用說明:需要執(zhí)行約10s
******************************************************************************/
static?bool?FLASH_ioctl(uint8_t?cmd,void?*?param)
{
????#define?UID_ADDR????????????0x1FFFF7E0??//閃存容量寄存器,值對應(yīng)KB單位
????#define?MAC_ADDR????????????0x1FFFF7E8??//MCU的唯一ID號,共12個字節(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?19:04:15
/?輸入?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:\t%dKB\r\n",retval);
????????????return?true;
????????}
????????else?if(1==wval)?{
????????????FLASH_ioctl(1,buff);
????????????printf("->MAC:\t?");
????????????for(i=0;?i0]-1;?i++){printf("%02X-",buff[i+1]);}
????????????printf("%02X\r\n",buff[i+1]);
????????????return?true;
????????}
????????else?return?false;
????}
????else?if(StrComp(ptRxd,"help\r\n"))??????//指令幫助
????{
????????shell_SendStr((void?*)CLI_McuMsg.pcHelpStr);
????????return?true;
????}
????else?return?false;
}
?
/******************************************************************************
***********************************???END??************************************
******************************************************************************/
#endif
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)下載到開發(fā)板運(yùn)行即可在終端下看到新支持的CLI指令:

本文來源網(wǎng)絡(luò),免費(fèi)傳達(dá)知識,版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請聯(lián)系我進(jìn)行刪除。
???????????????? ?END ????????????????
關(guān)注我的微信公眾號,回復(fù)“加群”按規(guī)則加入技術(shù)交流群。
點(diǎn)擊“閱讀原文”查看更多分享,歡迎點(diǎn)分享、收藏、點(diǎn)贊、在看。