博客專欄

        EEPW首頁 > 博客 > 萬字干貨:單片機實現Shell控制臺保姆級教程

        萬字干貨:單片機實現Shell控制臺保姆級教程

        發布人:xiaomaidashu 時間:2023-11-10 來源:工程師 發布文章
        萬字干貨:單片機實現Shell控制臺保姆級教程
        小麥大叔 2023-11-08 12:34 發表于浙江


        1前言

        玩過Linux的朋友, 是不是對Linux無所不能的串口Shell命令控制臺羨慕不已, 要是自己做的STM32F系列低檔次的MCU也有這種控制交互能力。

        會給調試/維護和配置省下多少麻煩事呀, 比如啟動/關閉調試或自檢模式, 打印調試信息, 配置系統參數, 傳輸文件等等, 也有相當多的朋友憑借自己出色的編程能力可以實現這些功能, 這里提出我的這個解決方案, 以作交流.

        本平臺(xc_shell)具備以下性能特點:

        1. 大量主要代碼, 和具體硬件無關, 移植性強,代碼文件少.

        2. 只有在處理用戶的輸入命令時, 才占用CPU資源, 且代碼可裁剪到1KB SRAM和4KB Flash;

        3. 用戶可以非常靈活的添加按模板編寫的命令腳本文件, 自定義擴張能力強.

        4. 支持操作系統和非操作系統兩種場景應用.

        5. 支持Ymodem文件傳輸協議

        6. 支持將Flash的扇區開辟為參數區, 可實現本地/遠程升級。

        7. 實用Led燈信號管理, 可將65535虛擬信號燈選擇輸出到1個實體LED燈上, 調試時序和狀態非常有用

        8. 擁有基礎的LED管理, 調試模式設置, 命令幫助指令, 復位指令等基礎功能

        功能越多設計會越復雜, 為了解釋清楚代碼, 先向大家解釋一下以上功能的基礎實現原理, 并提供一個最小的的源碼工程。


        2xc_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;
        1. 可注入的命令腳本(CLI)實現

        命令CLI也是一個結構體對象:

        /*---------------------* 
        *       CLI指令
        *----------------------*/
        typedef struct
        {
         const char * const  pcCmdStr;     //指令字符串(只能為小寫字母)
         const char * const  pcHelpStr;     //指令描述,必須以:"rn結束". 比如:"help: Returns a listrn".
         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]rn"
         " wiz helprn"
         " wiz rd inforn"
         " wiz resetrn"
         " wiz wr ip <D0>.<D1>.<D2>.<D3>rn"
         " wiz wr mask <D0>.<D1>.<D2>.<D3>rn"
         " wiz wr way <D0>.<D1>.<D2>.<D3>rn"
         " wiz wr mac <H0>-<H1>-<H2>-<H3>-<H4>-<H5>rn"
         " wiz wr port <udp> <bak> <vol> <pic>rn"
         " wiz wr sip <D0>.<D1>.<D2>.<D3> <port>rn"
         " wiz wr cip <D0>.<D1>.<D2>.<D3> <port>rn"
         " wiz load defaultrn"
         "[WIZ Test mode]rn"
         " wiz loop openrn"
         " wiz loop closern"
         "rn",
         
         //處理函數
         .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.25


        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 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]rn"
         " mcu rd <d>tt- Read FLASH information.rn"
         "rn",
         
         //處理函數
         &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%dKBrn",retval);
                    return true;
                }
                else if(1==wval) {
                    FLASH_ioctl(1,buff);
                    printf("->MAC:t ");
                    for(i=0; i<buff[0]-1; i++){printf("%02X-",buff[i+1]);}
                    printf("%02Xrn",buff[i+1]);
                    return true;
                }
                else return false;
            }
            else if(StrComp(ptRxd,"helprn"))      //指令幫助
            {
                shell_SendStr((void *)CLI_McuMsg.pcHelpStr);
                return true;
            }
            else return false;
        }
         
        /******************************************************************************
        ***********************************   END  ************************************
        ******************************************************************************/
        #endif
        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指令:

        圖片

        來源:https://dablelv.blog.csdn.net/article

        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞: 單片機 Shell

        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 郑州市| 政和县| 金昌市| 宕昌县| 宁远县| 昌江| 柳河县| 稻城县| 错那县| 宁强县| 健康| 和静县| 延安市| 安岳县| 金昌市| 德格县| 钦州市| 大余县| 沙洋县| 鄂托克旗| 当阳市| 鸡西市| 巴林左旗| 类乌齐县| 台山市| 长岛县| 新乐市| 石门县| 广灵县| 古蔺县| 松江区| 鹤峰县| 涞水县| 永新县| 什邡市| 靖边县| 镇巴县| 乳源| 曲麻莱县| 根河市| 汉沽区|