• 注册 / 登录
  • 切换到窄版
  • 查看: 1215|回复: 0

    STM32串口命令控制台的方法

    [复制链接]

    665

    主题

    679

    帖子

    6476

    积分

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    6476
    发表于 2023-12-22 11:30:02 | 显示全部楼层 |阅读模式

    路线栈欢迎您!

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    一、前言

    玩过Linux的朋友, 是不是对Linux无所不能的串口Shell命令控制台羡慕不已, 要是自己做的STM32F系列低档次的MCU也有这种控制交互能力, 会给调试/维护和配置省下多少麻烦事呀, 比如启动/关闭调试或自检模式, 打印调试信息, 配置系统参数, 传输文件等等, 也有相当多的朋友凭借自己出色的编程能力可以实现这些功能, 这里提出我的这个解决方案, 以作交流.

    本平台(xc_shell)具备以下性能特点:

    大量主要代码, 和具体硬件无关, 移植性强,代码文件少.

    只有在处理用户的输入命令时, 才占用CPU资源, 且代码可裁剪到1KB SRAM和4KB Flash;

    用户可以非常灵活的添加按模板编写的命令脚本文件, 自定义扩张能力强.

    支持操作系统和非操作系统两种场景应用.

    支持Ymodem文件传输协议

    支持将Flash的扇区开辟为参数区, 可实现本地/远程升级。

    实用Led灯信号管理, 可将65535虚拟信号灯选择输出到1个实体LED灯上, 调试时序和状态非常有用

    拥有基础的LED管理, 调试模式设置, 命令帮助指令, 复位指令等基础功能

    功能越多设计会越复杂, 为了解释清楚代码, 先向大家解释一下以上功能的基础实现原理, 并提供一个最小的的源码工程。

    二、xc_shell平台介绍

    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回调处理方法,则会在中断中执行用户的回调处理。

    1. /*---------------------*
    2. *     指正函数定义
    3. *----------------------*/
    4. typedef void    (*pvFunDummy)(void);

    5. //输入整行,输出逻辑
    6. typedef void    (*pvFunVoid) (void);
    7. typedef void    (*pvFunBool) (bool     bVal);
    8. typedef void    (*pvFunChar) (uint8_t  cVal);
    9. typedef void    (*pvFunShort)(uint16_t sVal);
    10. typedef void    (*pvFunWord) (uint32_t wVal);

    11. //输入整行,输出逻辑
    12. typedef bool    (*pbFunVoid) (void);
    13. typedef bool    (*pbFunBool) (bool     bVal);
    14. typedef bool    (*pbFunChar) (uint8_t  cVal);
    15. typedef bool    (*pbFunShort)(uint16_t sVal);
    16. typedef bool    (*pbFunWord) (uint32_t wVal);

    17. //输入整形指针,输出逻辑
    18. typedef bool    (*pbFun_pVoid) (void * pVoid);
    19. typedef bool    (*pbFun_pChar) (uint8_t  * pStr);
    20. typedef bool    (*pbFun_pShort)(uint16_t * pShor);
    21. typedef bool    (*pbFun_pWord) (uint32_t * pWord);

    22. //输入数据帧,输出逻辑
    23. typedef bool    (*pbFun_Buffx)(void * pcBuff, uint16_t len );
    24. typedef bool    (*pbFun_Bytex)(uint8_t * pcByte, uint16_t len );
    25. /*---------------------*
    26. *    TTYx 句柄结构
    27. *----------------------*/
    28. typedef struct TTYx_HANDLE_STRUCT
    29. {
    30.     const char  * const name;       //驱动器名
    31.     const uint16_t      rxSize;     //接收大小
    32.     const uint16_t      txSize;     //发送大小
    33.    
    34.     //------------------------------------------------------
    35.     //step1: 用户可用API
    36.     const pvFunWord     init;           //初始化.
    37.     const pbFun_Bytex   api_TxdFrame;   //发送数据帧. (发送帧)
    38.     const pbFunChar     api_TxdByte;    //发送数据字节
    39.    
    40.     //------------------------------------------------------
    41.     //step2: 注入回调函数
    42.     pbFun_Bytex         inj_RcvFrame;   //(ISR)接收数据帧. (接收帧)
    43.     pvFunDummy          inj_TxdReady;   //(ISR)发送完毕回调
    44.    
    45.     //------------------------------------------------------
    46.     //step3: 接收回调函数
    47.     struct TTYx_HANDLE_STRUCT * pvNext; //连接到下一个指令
    48. }TTYx_HANDLE;
    复制代码

    2. 可注入的命令脚本(CLI)实现

    命令CLI也是一个结构体对象:

    1. /*---------------------*
    2. *       CLI指令
    3. *----------------------*/
    4. typedef struct
    5. {
    6. const char * const  pcCmdStr;     //指令字符串(只能为小写字母)
    7. const char * const  pcHelpStr;     //指令描述,必须以:"\r\n结束". 比如:"help: Returns a list\r\n".
    8. const pFunHook      pxCmdHook;     //指向回调函数的指针,处理成功返回真否者返回0;
    9. uint8_t             ucExpParam;     //指令期望的参数个数
    10. const MEDIA_HANDLE *phStorage;      //指向存储介质,没有的话填充NULL  
    11. }Cmd_Typedef_t;
    复制代码

    各位朋友可能会使用到非常多的自定义CLI命令, 格式诸如这个网卡的命令:

    1. const Cmd_Typedef_t CLI_WizMsg=
    2. {
    3.     //识别关键字
    4.     .pcCmdStr   = "wiz",
    5.     //帮助内容
    6.     .pcHelpStr  =
    7.     "[WIZ contorls]\r\n"
    8. " wiz help\r\n"
    9. " wiz rd info\r\n"
    10. " wiz reset\r\n"
    11. " wiz wr ip <D0>.<D1>.<D2>.<D3>\r\n"
    12. " wiz wr mask <D0>.<D1>.<D2>.<D3>\r\n"
    13. " wiz wr way <D0>.<D1>.<D2>.<D3>\r\n"
    14. " wiz wr mac <H0>-<H1>-<H2>-<H3>-<H4>-<H5>\r\n"
    15. " wiz wr port <udp> <bak> <vol> <pic>\r\n"
    16. " wiz wr sip <D0>.<D1>.<D2>.<D3> <port>\r\n"
    17. " wiz wr cip <D0>.<D1>.<D2>.<D3> <port>\r\n"
    18. " wiz load default\r\n"
    19. "[WIZ Test mode]\r\n"
    20. " wiz loop open\r\n"
    21. " wiz loop close\r\n"
    22. "\r\n",

    23. //处理函数
    24. .pxCmdHook  = &Shell_WIZ_Service,        //见实体函数

    25. //附带数据
    26. .ucExpParam = 0,

    27.   #ifdef SHELL_USE_YMODEM  
    28.     //存储介质
    29.     .phStorage  = NULL,
    30.   #endif
    31. };
    32. /*---------------------*
    33. *       CLI 链表节
    34. *----------------------*/
    35. Cmd_List_t  WizList   = { &CLI_WizMsg,   NULL,}; //Shell指令的头
    复制代码

    如配置IP地址输入“wiz wr ip 192.168.1.250\r\n”则可以了

    三、环境准备

    1. 硬件开发环境:STM32F103系列开发板一块, 带USART1接口、USB转串口线一根。
    2. 软件开发环境:MDK4.72或以上、SecureCRT串口超级终端
    3. 软件配置:在xc_shell使用“/r/n”作为命令的结束符, 而SecureCRT按下Enter不是输入“/r/n”故而需要按下图设置:此外在《终端》/仿真/高级中选取【本地回显】

    1.png

    四、代码介绍

    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文件

    2. 工程设置要点

    1) 设置使用微库:

    2.png

    2)配置包含和路径,注意使用了“--c99”标准,如图

    3.png

    3) 编译工程,无错误警告后烧写程序到开发板运行。

    3. 最终效果

    按图输入一下指令,SHELL平台会回复相关信息。其中输入“led set 0=1”会将信号1分配到物理LED0上;输入“led set 0=2”会将信号2分配到物理LED0上。这样用户编写程序代码时相当于拥有了超级多的LED信号可用,调试时序非常有用。

    4.png

    五、添加自己的指令脚本

    1. 源代码示例

    假设我要编写一个自己的指令脚本, 来读取MCU的关键信息,关键字为mcu, 文件命名为shell_mcu.c;当输入“mcu rd 0”时显示MCU的FLASH大小,输入“mcu rd 1”时读取MCU的唯一ID信息。shell_mcu.c源代码:

    1. /*********************************Copyright (c)*********************************
    2. **
    3. **---------------------------------File Info------------------------------------
    4. ** File Name:               shell_mcu.c
    5. ** Last modified Date:      2017/9/17 15:13:57
    6. ** Last Version:            V1.0
    7. ** Description:             shell测试
    8. **
    9. **------------------------------------------------------------------------------
    10. ** Created By:              wanxuncpx
    11. ** Created date:            2017/9/17 15:14:08
    12. ** Version:                 V1.0
    13. ** Descriptions:            none
    14. **------------------------------------------------------------------------------
    15. ** HW_CMU:                  STM32F103
    16. ** Libraries:               STM32F10x_StdPeriph_Lib_V3.5.0
    17. ** version                  V3.5
    18. *******************************************************************************/

    19. /******************************************************************************
    20. 更新说明:
    21. ******************************************************************************/

    22. /******************************************************************************
    23. *********************************  编 译 控 制 ********************************
    24. ******************************************************************************/
    25. #define MCU_SHELL               //注释掉时屏蔽iap shell功能

    26. #include "xc_shell.h"       //Shell支持文件,含bool,uint8_t..以及串口数据收发操作
    27. /******************************************************************************
    28. ********************************* 文件引用部分 ********************************
    29. ******************************************************************************/
    30. /*---------------------*
    31. *     模块驱动引用
    32. *----------------------*/
    33. //#include "net_w5500.h"

    34. #ifdef MCU_SHELL
    35. /******************************************************************************
    36. ********************************** Shell实例 **********************************
    37. ******************************************************************************/
    38. /*---------------------*
    39. *      CLI指令服务
    40. *----------------------*/
    41. extern bool Shell_MCU_Service(void * pcBuff, uint16_t len );

    42. /*---------------------*
    43. *       CLI 结构
    44. *----------------------*/
    45. const Cmd_Typedef_t CLI_McuMsg=
    46. {
    47.     //识别关键字
    48.     "mcu",
    49.    
    50.     //帮助内容
    51. "[mcu contorls]\r\n"
    52. " mcu rd <d>\t\t- Read FLASH information.\r\n"
    53. "\r\n",

    54. //处理函数
    55. &Shell_MCU_Service,

    56. //附带数据
    57. 0,
    58.   #ifdef SHELL_USE_YMODEM  
    59.     //存储介质
    60.     NULL,
    61.   #endif
    62. };

    63. /*---------------------*
    64. *     CLI链表节(输出)
    65. *----------------------*/
    66. Cmd_List_t  McuList  = {&CLI_McuMsg      ,NULL}; //IAP指令链表

    67. /******************************************************************************
    68. ********************************* 函 数 声 明 *********************************
    69. ******************************************************************************/
    70. /******************************************************************************
    71. / 函数功能:STM32F103控制函数
    72. / 修改日期:2015/7/14 20:22:02
    73. / 输入参数:none
    74. / 输出参数:none
    75. / 使用说明:需要执行约10s
    76. ******************************************************************************/
    77. static bool FLASH_ioctl(uint8_t cmd,void * param)
    78. {
    79.     #define UID_ADDR            0x1FFFF7E0  //闪存容量寄存器,值对应KB单位
    80.     #define MAC_ADDR            0x1FFFF7E8  //MCU的唯一ID号,共12个字节
    81.     #define UID_SIZE            2           //UID的字节数
    82.     #define MAC_SIZE            12          //MAC的字节数

    83.     //step1: 检查参数
    84.     if(!param)return false;
    85.         
    86.     //step2: 处理数据
    87.     switch(cmd){
    88.       case 0 : {       //获取FLASH的的UID
    89.         uint16_t * ptDst = (uint16_t *)((uint32_t)param+1);
    90.         
    91.         *ptDst = *(uint16_t *)UID_ADDR;
    92.         *(uint8_t  *)param =  UID_SIZE;
    93.         return true;
    94.       }
    95.       case 1 : {       //获取芯片的MAC地址
    96.         uint32_t * ptDst = (uint32_t *)((uint32_t)param+1);
    97.         uint32_t * ptSrc = (uint32_t *)MAC_ADDR;
    98.    
    99.         *ptDst++ = *ptSrc++;
    100.         *ptDst++ = *ptSrc++;
    101.         *ptDst++ = *ptSrc++;
    102.         *(uint8_t  *)param = MAC_SIZE;
    103.         return true;
    104.       }
    105.       default:return false;
    106.     }
    107. }

    108. /******************************************************************************
    109. / 函数功能:文件系统Shel指令处理
    110. / 修改日期:2013/9/10 19:04:15
    111. / 输入参数:输入当前的程序版本
    112. / 输出参数:none
    113. / 使用说明:none
    114. ******************************************************************************/
    115. bool Shell_MCU_Service(void * pcBuff, uint16_t len )
    116. {
    117.     uint8_t    *ptRxd;          //用于接收指令处理
    118.     int         i;
    119.     uint16_t    retval;
    120.     uint8_t     buff[32];
    121.    
    122.     //处理指令
    123.     //--------------------------------------------------------------------------
    124.     ptRxd = (uint8_t *)pcBuff;
    125.    
    126.     if(StrComp(ptRxd,"rd ")) //读取FLASH信息
    127.     {
    128.         int wval;
    129.         
    130.         if(1 != sscanf((void *)ptRxd,"%*s%d",&wval) )return false;
    131.         if( wval>2 )return false;
    132.         if(0==wval) {
    133.             FLASH_ioctl(0,buff);
    134.             retval = *(uint16_t *)(buff+1) ;
    135.             printf("->Flash:\t%dKB\r\n",retval);
    136.             return true;
    137.         }
    138.         else if(1==wval) {
    139.             FLASH_ioctl(1,buff);
    140.             printf("->MAC:\t ");
    141.             for(i=0; i<buff[0]-1; i++){printf("%02X-",buff[i+1]);}
    142.             printf("%02X\r\n",buff[i+1]);
    143.             return true;
    144.         }
    145.         else return false;
    146.     }
    147.     else if(StrComp(ptRxd,"help\r\n"))      //指令帮助
    148.     {
    149.         shell_SendStr((void *)CLI_McuMsg.pcHelpStr);
    150.         return true;
    151.     }
    152.     else return false;
    153. }

    154. /******************************************************************************
    155. ***********************************   END  ************************************
    156. ******************************************************************************/
    157. #endif
    复制代码

    2. 实现步骤

    1) 将该文件添加到工程下。

    2) 在main.c中用extern 引用McuList,源代码为:

    1. /*---------------------*
    2. *     Shell指令链表
    3. *----------------------*/
    4. extern Cmd_List_t  McuList;
    复制代码

    3)在main.c初始化时添加:

    1. //----------------------------------------------------------
    2. //step1: shell初始化
    3. shell_Init(115200,ledx_cfg);        //初始化shell接口
    4. CLI_AddCmd(&McuList);     //添加模块指令到链表
    复制代码

    4)编译工程文件。

    5)下载到开发板运行即可在终端下看到新支持的CLI指令:

    5.png

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    小黑屋|路丝栈 ( 粤ICP备2021053448号 )

    GMT+8, 2024-9-17 03:42 , Processed in 0.048412 second(s), 21 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表