【STM32标准库】移植letter shell

一、使用letter shell可以通过STM32的串口实现命令行输入的功能

letter shell的源码可以到github下载:GitHub - NevermindZZT/letter-shell: letter shell

二、下载后的源码如下,将\letter-shell-master\src文件夹下的全部文件复制到自己的工程,并且新增shell_port.c、shell_port.h两个文件,用于实现自己的接口函数。

三、在keil工程里加入这几个c文件,记得把头文件路径也包含进来

四、shell_cfg.h文件是关于letter shell的各种配置的宏,作者已经非常贴心地全部写上注释,各位可以根据自己的需求做修改,以下是我目前使用的配置,仅供参考。

shell_cfg.h:

/**
 * @file shell_cfg.h
 * @author Letter ([email protected])
 * @brief shell config
 * @version 3.0.0
 * @date 2019-12-31
 * 
 * @copyright (c) 2019 Letter
 * 
 */

#ifndef __SHELL_CFG_H__
#define __SHELL_CFG_H__


/**
 * @brief 是否使用默认shell任务while循环,使能宏`SHELL_USING_TASK`后此宏有意义
 *        使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell
 *        任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()`
 */
#define     SHELL_TASK_WHILE           0

/**
 * @brief 是否使用命令导出方式
 *        使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令
 *        定义shell命令,关闭此宏的情况下,需要使用命令表的方式
 */
#define     SHELL_USING_CMD_EXPORT      1

/**
 * @brief 是否使用shell伴生对象
 *        一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象
 */
#define     SHELL_USING_COMPANION       0

/**
 * @brief 支持shell尾行模式
 */
#define     SHELL_SUPPORT_END_LINE      0

/**
 * @brief 是否在输出命令列表中列出用户
 */
#define     SHELL_HELP_LIST_USER        0

/**
 * @brief 是否在输出命令列表中列出变量
 */
#define     SHELL_HELP_LIST_VAR         0

/**
 * @brief 是否在输出命令列表中列出按键
 */
#define     SHELL_HELP_LIST_KEY         0

/**
 * @brief 是否在输出命令列表中展示命令权限
 */
#define     SHELL_HELP_SHOW_PERMISSION     0//1

/**
 * @brief 使用LF作为命令行回车触发
 *        可以和SHELL_ENTER_CR同时开启
 */
#define     SHELL_ENTER_LF              0//1

/**
 * @brief 使用CR作为命令行回车触发
 *        可以和SHELL_ENTER_LF同时开启
 */
#define     SHELL_ENTER_CR              1

/**
 * @brief 使用CRLF作为命令行回车触发
 *        不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启
 */
#define     SHELL_ENTER_CRLF            0

/**
 * @brief 使用执行未导出函数的功能
 *        启用后,可以通过`exec [addr] [args]`直接执行对应地址的函数
 * @attention 如果地址错误,可能会直接引起程序崩溃
 */
#define     SHELL_EXEC_UNDEF_FUNC       0

/**
 * @brief shell命令参数最大数量
 *        包含命令名在内,超过16个参数并且使用了参数自动转换的情况下,需要修改源码
 */
#define     SHELL_PARAMETER_MAX_NUMBER      8

/**
 * @brief 历史命令记录数量
 */
#define     SHELL_HISTORY_MAX_NUMBER    5

/**
 * @brief 双击间隔(ms)
 *        使能宏`SHELL_LONG_HELP`后此宏生效,定义双击tab补全help的时间间隔
 */
#define     SHELL_DOUBLE_CLICK_TIME     0//200

/**
 * @brief 快速帮助
 *        作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息
 */
#define     SHELL_QUICK_HELP            0//1

/**
 * @brief 保存命令返回值
 *        开启后会默认定义一个`RETVAL`变量,会保存上一次命令执行的返回值,可以在随后的命令中进行调用
 *        如果命令的`SHELL_CMD_DISABLE_RETURN`标志被设置,则该命令不会更新`RETVAL`
 */
#define     SHELL_KEEP_RETURN_VALUE     0

/**
 * @brief 管理的最大shell数量
 */
#define     SHELL_MAX_NUMBER            5

/**
 * @brief shell格式化输出的缓冲大小
 *        为0时不使用shell格式化输出
 */
#define     SHELL_PRINT_BUFFER          128

/**
 * @brief shell格式化输入的缓冲大小
 *        为0时不使用shell格式化输入
 * @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用
 */
#define     SHELL_SCAN_BUFFER          0

/**
 * @brief 获取系统时间(ms)
 *        定义此宏为获取系统Tick,如`HAL_GetTick()`
 * @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定
 */
#define     SHELL_GET_TICK()            0

/**
 * @brief 使用锁
 * @note 使用shell锁时,需要对加锁和解锁进行实现
 */
#define     SHELL_USING_LOCK            0

/**
 * @brief shell内存分配
 *        shell本身不需要此接口,若使用shell伴生对象,需要进行定义
 */
#define     SHELL_MALLOC(size)          0

/**
 * @brief shell内存释放
 *        shell本身不需要此接口,若使用shell伴生对象,需要进行定义
 */
#define     SHELL_FREE(obj)             0

/**
 * @brief 是否显示shell信息
 */
#define     SHELL_SHOW_INFO             1

/**
 * @brief 是否在登录后清除命令行
 */
#define     SHELL_CLS_WHEN_LOGIN        1

/**
 * @brief shell默认用户
 */
#define     SHELL_DEFAULT_USER          "letter"

/**
 * @brief shell默认用户密码
 *        若默认用户不需要密码,设为""
 */
#define     SHELL_DEFAULT_USER_PASSWORD ""

/**
 * @brief shell自动锁定超时
 *        shell当前用户密码有效的时候生效,超时后会自动重新锁定shell
 *        设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位
 * @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效
 */
#define     SHELL_LOCK_TIMEOUT          0//0 * 60 * 1000

#endif

五、接下来实现自己的接口函数

在shell_port.c中,User_Shell_Write函数调用串口发送函数即可,User_Shell_Read为串口接收函数,这里我已经利用串口中断将串口的数据接收到一个buff中,因此将数据从buff中读出即可。User_Shell_Init函数就是注册以上两个读写函数,并且调用初始化函数。

shell_port.c:

#include "stdio.h"
#include "stdint.h"
#include "shell_port.h"
#include "WarGod_Uart.h"
#include "WarGod_led.h"

/* 1. 创建shell对象,开辟shell缓冲区 */
Shell shell;
char shell_buffer[512];

/* 2. 自己实现shell写函数 */
signed short User_Shell_Write(char *data, unsigned short len)
{
  Uart1_Send_Bytes((uint8_t *)data,(uint16_t)len);
  return len;
}

/* 3. 自己实现shell读函数 */
signed short User_Shell_Read(char *data, unsigned short len)
{
  stRingBuff *pRxDataBuffer = &gUsart1RxDataBuffer;
  
  if(pRxDataBuffer->head != pRxDataBuffer->tail)
  {
    ReadRingBuffer(pRxDataBuffer,(unsigned char *)data,len);
    return len;
  }
  return 0;
}

/* 4. 编写初始化函数 */
void User_Shell_Init(void)
{
    //注册自己实现的读写函数
  shell.write = User_Shell_Write;
  shell.read = User_Shell_Read;
    
    //调用shell初始化函数
  shellInit(&shell, shell_buffer, 512);
}

shell_port.h:

#ifndef _SHELL_PORT_H_
#define    _SHELL_PORT_H_

#include "shell.h"
#include "main.h"

extern Shell shell;

void User_Shell_Init(void);

#endif

六、在main.c文件中包含头文件 #include "shell_port.h",在main函数中调用User_Shell_Init();函数 在while(1)循环中调用shellTask(&shell);函数。

扫描二维码关注公众号,回复: 15065589 查看本文章
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>
#include "main.h"

#include "WarGod_Uart.h"
#include "shell_port.h"

/*启动系统滴答定时器 SysTick*/
void SysTick_Init(void)
{
  /*SystemCoreClock/1000 = 72000*/
  /*SysTick_Config(72000)代表:72000*(1/72MHz)=1/1000=1(ms)。即定时为1ms*/
  if(SysTick_Config(SystemCoreClock/1000))//1ms定时器
  {
    while(1);
  }
  //SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;//若无法启动则关闭
}

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(void)
{
  /* Add your application code here
     */       
  SysTick_Init();/*初始化系统滴答定时器 SysTick*/  
  USART1_Init(115200);/*初始化串口1*/  
  User_Shell_Init();  
  /* Infinite loop */
  while (1)
  {
    shellTask(&shell);
  }
}

到这里为止,理论上就可以使用命令行shell的功能了,将工程编译下载,通过MobaXterm连接串口就可以看到以下效果:

七、SHELL_EXPORT_CMD函数的应用,继续在shell_port.c:文件中加入以下代码

int  Shell_LedOn(int i, char ch, char *str)
{
  if(i == 0)
  {
    Led0_On();
    printf("Shell Set LED0 On\r\n", i);
  }
  else if(i == 1)
  {
    Led1_On();
    printf("Shell Set LED1 On\r\n", i);
  }
  else printf("Parameter out of range!\r\n");
    
    return 0;
}

int  Shell_LedOff(int i, char ch, char *str)
{
  if(i == 0)
  {
    Led0_Off();
    printf("Shell Set LED0 Off\r\n", i);
  }
  else if(i == 1)
  {
    Led1_Off();
    printf("Shell Set LED1 Off\r\n", i);
  }
  else printf("Parameter out of range!\r\n");
    
    return 0;
}

/***********************************************************************
* 函数名称: #define SHELL_EXPORT_CMD(_attr, _name, _func, _desc)
* 输入参数:
          _attr :命令属性
          _name :命令名
          _func :命令函数
          _desc :命令描述
* 输出参数:
* 返 回 值:
*  其   它:
***********************************************************************/
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), sledon, Shell_LedOn, NULL);
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), sledoff, Shell_LedOff, NULL);

这里具体实现了打开和关闭led的函数,并通过SHELL_EXPORT_CMD函数注册这两个命令,再次编译下载好就可以看到以下效果:

猜你喜欢

转载自blog.csdn.net/weixin_46183891/article/details/129454498
今日推荐