[STM32 Standard Library] Transplant letter shell

1. Use the letter shell to realize the function of command line input through the serial port of STM32

The source code of letter shell can be downloaded from github: GitHub - NevermindZZT/letter-shell: letter shell

2. The downloaded source code is as follows, copy all the files in the \letter-shell-master\src folder to your own project, and add two files, shell_port.c and shell_port.h, to implement your own interface functions .

3. Add these c files to the keil project, remember to include the header file path

4. The shell_cfg.h file is a macro about the various configurations of the letter shell. The author has written all the notes very intimately. You can modify it according to your own needs. The following is the configuration I am currently using, for reference only.

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

5. Next, implement your own interface function

In shell_port.c, the User_Shell_Write function can call the serial port sending function, and User_Shell_Read is the serial port receiving function. Here I have used the serial port interrupt to receive the serial port data into a buff, so just read the data from the buff. The User_Shell_Init function is to register the above two read and write functions, and call the initialization function.

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

6. Include the header file #include "shell_port.h" in the main.c file, call the User_Shell_Init(); function in the main function and call the shellTask(&shell); function in the while(1) loop.

/* 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);
  }
}

So far, theoretically, you can use the command line shell function, compile and download the project, and connect the serial port through MobaXterm to see the following effects:

Seven, the application of the SHELL_EXPORT_CMD function, continue to add the following code in the shell_port.c: file

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函数注册这两个命令,再次编译下载好就可以看到以下效果:

Guess you like

Origin blog.csdn.net/weixin_46183891/article/details/129454498