玩過Linux的朋友, 是不是對Linux無所不能的串口Shell命令控制臺羨慕不已, 要是自己做的STM32F系列低檔次的MCU也有這種控制交互能力, 會給調試/維護和配置省下多少麻煩事呀, 比如啟動/關閉調試或自檢模式, 打印調試信息, 配置系統參數, 傳輸文件等等, 也有相當多的朋友憑借自己出色的編程能力可以實現這些功能, 這里提出我的這個解決方案, 以作交流.
本平臺(xc_shell)具備以下性能特點:
大量主要代碼, 和具體硬件無關, 移植性強,代碼文件少.
只有在處理用戶的輸入命令時, 才占用CPU資源, 且代碼可裁剪到1KB SRAM和4KB Flash;
用戶可以非常靈活的添加按模板編寫的命令腳本文件, 自定義擴張能力強.
支持操作系統和非操作系統兩種場景應用.
支持Ymodem文件傳輸協議
支持將Flash的扇區開辟為參數區, 可實現本地/遠程升級。
實用Led燈信號管理, 可將65535虛擬信號燈選擇輸出到1個實體LED燈上, 調試時序和狀態非常有用
擁有基礎的LED管理, 調試模式設置, 命令幫助指令, 復位指令等基礎功能
功能越多設計會越復雜, 為了解釋清楚代碼, 先向大家解釋一下以上功能的基礎實現原理, 并提供一個最小的的源碼工程。
類比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指令
*----------------------*/
typedef?struct
{
?const?char?*?const??pcCmdStr;?????//指令字符串(只能為小寫字母)
?const?char?*?const??pcHelpStr;?????//指令描述,必須以:"\r\n結束".?比如:"help:?Returns?a?list\r\n".
?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]\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",
?
?//處理函數
?.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\r\n”則可以了
在xc_shell使用“/r/n”作為命令的結束符, 而SecureCRT按下Enter不是輸入“/r/n”故而需要按下圖設置:此外在《終端》/仿真/高級中選取【本地回顯】?

□?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文件
1) 設置使用微庫:

2)配置包含和路徑,注意使用了“--c99”標準,如圖

3) 編譯工程,無錯誤警告后燒寫程序到開發板運行。
按圖輸入一下指令,SHELL平臺會回復相關信息。其中輸入“led set 0=1”會將信號1分配到物理LED0上;輸入“led set 0=2”會將信號2分配到物理LED0上。這樣用戶編寫程序代碼時相當于擁有了超級多的LED信號可用,調試時序非常有用。?

假設我要編寫一個自己的指令腳本, 來讀取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?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..以及串口數據收發操作
/******************************************************************************
*********************************?文件引用部分?********************************
******************************************************************************/
/*---------------------*?
*?????模塊驅動引用
*----------------------*/
//#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]\r\n"
?"?mcu?rd?\t\t-?Read?FLASH?information.\r\n"
?"\r\n",
?
?//處理函數
?&Shell_MCU_Service,
?
?//附帶數據
?0,
??#ifdef?SHELL_USE_YMODEM??
????//存儲介質
????NULL,
??#endif
};
?
/*---------------------*?
*?????CLI鏈表節(輸出)
*----------------------*/
Cmd_List_t??McuList??=?{&CLI_McuMsg??????,NULL};?//IAP指令鏈表
?
/******************************************************************************
*********************************?函?數?聲?明?*********************************
******************************************************************************/
/******************************************************************************
/?函數功能:STM32F103控制函數
/?修改日期:2015/7/14?20:22:02
/?輸入參數: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?19:04:15
/?輸入參數:輸入當前的程序版本
/?輸出參數: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)下載到開發板運行即可在終端下看到新支持的CLI指令:

來源:https://dablelv.blog.csdn.net/article
本文來源網絡,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯系我進行刪除。
關注公眾號,加星標,回復1024獲取學習資料,每天進步一點點。
聲明:
本號原創、轉載的文章、圖片等版權歸原作者所有,如有侵權,請聯系刪除。
關注、點贊、在看、轉發,支持優質內容!
?