你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】实用STM32的串口控制平台的实现!

[复制链接]
STMCU小助手 发布时间:2022-1-5 21:00
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回调处理方法,则会在中断中执行用户的回调处理。

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

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



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

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

  1. const Cmd_Typedef_t CLI_WizMsg=
  2. {
  3.     <i>//识别关键字</i>
  4.     .pcCmdStr   = "wiz",
  5.     <i>//帮助内容</i>
  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. <i>//处理函数</i>
  24. .pxCmdHook  = &Shell_WIZ_Service,        <i>//见实体函数</i>

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

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

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

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
  1. ├──┬── BSP_LIB    BSP库,硬件相关驱动代买
  2. │  ├──── bsp_ledx.c   基础LED驱动      
  3. │  └──── bsp_tty0.c   调试串口驱动

  4. ├──┬──MDK-ARM     工程文件
  5. │  └──── Project.uvproj

  6. ├──┬──SHELL_CFG   SHELL配置头文件
  7. │  └──── user_eval.h

  8. ├──┬──SHELL_CORE  SHELL内核文件
  9. │  ├──── xc_shell.c   SHELL内核文件
  10. │  ├──── xc_ymodem.c  Ymodem传输协议(默认关闭,在xc_shell.h中启动)
  11. │  ├──── xc_iap.c     Flash的IAP操作,需要bsp_flash.c驱动支持
  12. │  └──── shell_iap.c  shell的用户脚本模板

  13. ├──┬──SHELL_INC   SHELL头文件
  14. │  ├──── bsp_type.h   驱动结构定义
  15. │  ├──── xc_shell.h   SHELL头文件
  16. │  └──── xconfig.h    硬件无关配置文件

  17. ├──┬──STM32F10x_StdPeriph_Lib_V3.5.0  STM32的标准外设库
  18. │  └──── ......

  19. └──┬──USER        用户文件   
  20.    ├─ .....      
  21.    └──── main.c        main文件
复制代码

4.2 工程设置要点

1) 设置使用微库:

J{@]PI_A)431TG@)75W{_CX.png

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

Z2X3(U{9WKNBTW`PJ@9{P3K.png

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

4.3 最终效果

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

}S7`(NS`D2R$]Q]@3I_{O9U.png
5. 添加自己的指令脚本5.1 源代码示例

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

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

  21. <i>/******************************************************************************
  22. 更新说明:
  23. ******************************************************************************/</i>

  24. <i>/******************************************************************************
  25. *********************************  编 译 控 制 ********************************
  26. ******************************************************************************/</i>
  27. #define MCU_SHELL               //注释掉时屏蔽iap shell功能

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

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

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

  56. <i>//处理函数</i>
  57. &Shell_MCU_Service,

  58. <i>//附带数据</i>
  59. 0,
  60.   #ifdef SHELL_USE_YMODEM  
  61.     <i>//存储介质</i>
  62.     NULL,
  63.   #endif
  64. };

  65. <i>/*---------------------*
  66. *     CLI链表节(输出)
  67. *----------------------*/</i>
  68. Cmd_List_t  McuList  = {&CLI_McuMsg      ,NULL}; <i>//IAP指令链表</i>

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

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

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

  156. <i>/******************************************************************************
  157. ***********************************   END  ************************************
  158. ******************************************************************************/</i>
  159. #endif
复制代码

5.2 实现步骤

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

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

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

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

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

4)编译工程文件。

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

]8CYLJBFL{SSIG7D7`T.png



收藏 评论0 发布时间:2022-1-5 21:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版