《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell

目录

一、常用的串口/终端工具

二、常见嵌入式 shell

三、Letter shell简介

四、Letter Shell移植

1、复制源码

2.新建接口文件

3.配置工程

4、实现Letter shell的收发

5、主函数中初始化

五、Letter shell应用

1、Letter Shell 宏定义

2、Letter Shell 内置命令

3、Letter Shell 自定义 shell命令 

普通C函数形式

Main函数形式:

变量使用

注意


一、常用的串口/终端工具

Tabby
SecureCRT
MobaXterm
Putty
XShell
SSCOM
XCOM
Hypertrm
野火/山外多功能调试助手
友善串口调试助手
comNG
Commix
UartX
Termius
NxShell
FinalShell
Fish Shell
electerm
SEGGER Jlink-RTT Viewer
vscode EIDE 插件
……

注:本文章使用串口工具为:Putty

二、常见嵌入式 shell

对于嵌入式系统特别是 MCU 软件工程而言,一般用户开发不复杂的应用时会习惯直接跑裸机运行程序,针对这种没有操作系统的程序,如何高效便捷的进行系统调试往往是一个比较令人头疼的问题,而一些常见的嵌入式 shell 程序就是作为一个用户与设备端的连接桥梁的存在,极大的方便了系统的调试。往深了讲,shell 的运行原理是通过在命令行输入命令,shell 通过串口接收数据并且对命令进行解析,然后执行相应的操作。更通俗地来说,就是使用输入的字符串,匹配到对应的函数,然后执行下去。那么,就需要建立一个命令与函数的一一对应的关系,将其定义为一张执行表。
在使用嵌入式 shell 过程中,我整理了一些常用且好用的 shell 程序可以方便大家拿去使用,大致可以分为裸机适用的和 RTOS 自带的组件,它们分别是:

nr micro shell
Letter shell
RTT shell
USMART
cmd parser
FinSH msh
FreeRTOS CLI

上面每个 shell 原理都一样,移植的核心也就是分为接口适配以及增加自定义命令。

注:本文主要讲解Letter Shell

三、Letter shell简介

下载地址:https://gitee.com/zhang-ge/letter-shell

熟悉Linux的朋友的都知道,shell包裹在内核之外的人机交互界面,用于用户和内核之间打交道的功能,类似于windows CMD。 通过Shell将输入的命令与内核通讯,好让内核可以控制硬件开正确无误的操作工作。Shell有着不同的分类,比如Bourne shell(sh),Korn shell(ksh)、C shell (csh)、Bourne-again shell(bash)、tcsh。其中最常用的有csh和bash。Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的。

然而在嵌入式中,由于资源有限,自然很少使用shell,但随着MCU的资源越来越丰富,一些适用于嵌入式的shell工具也就问世了,本问将要介绍的是Letter shell,Letter shell是一个体积极小的嵌入式shell,当前最新版本是3.X。

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第1张图片

Letter shell有如下功能:

 命令自动补全,使用TAB键补全命令
 命令帮助,使用help [command]显示命令帮助
 帮助补全,输入命令后双击TAB键补全命令帮助指令
 快捷键,支持使用CTRL+A~Z组合按键直接调用函数
 shell变量,支持在shell中查看和修改变量值,支持变量作为命令参数
 登录密码,支持在shell中使用登录密码,支持超时自动锁定

四、Letter Shell移植

首先下载letter-shell,下载完成后目录如下图所示:

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第2张图片

1、复制源码

在工程中新建shell目录,将letter-shell目录下的src里面的所有文件复制到工程目录shell中。

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第3张图片

2.新建接口文件

在工程用户目录下新建shell_port.c和shell_port.h文件,我这里直接放在了工程目录下的shell文件中。

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第4张图片

3.配置工程

打开Keil,添加相应的文件和头文件路径。

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第5张图片

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第6张图片

4、实现Letter shell的收发

以下代码在上面步骤中自己建立的shell_port.c

#include "shell_port.h"
#include "shell.h"
#include "stm32f10x_usart.h"

Shell shell;

char shellBuffer[512];
/**
 * @brief 用户shell写
 * 
 * @param data 数据
 */
signed short userShellWrite(char* data,unsigned short len)
{
	int i;
	
	for(i=0;iDR;
      //调用shell处理数据的接口
      shellHandler(&shell, ch);
  } 
}

/**
 * @brief 用户shell初始化
 * 
 */
void userShellInit(void)
{	
    shell.write = userShellWrite;
    shellInit(&shell, shellBuffer, 512);
}

5、主函数中初始化

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "shell_port.h"
#include "shell.h"

int main(void)
{
	 
	delay_init();	    	 //延时函数初始化	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	uart_init(115200);	 //串口初始化为9600
	LED_Init();		  	 //初始化与LED连接的硬件接口 

	userShellInit();
	 
	 
	while(1)
	{
	}	 
}

到这里就能直接打开串口查看效果了,运行效果如下图所示

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第7张图片

五、Letter shell应用

1、Letter Shell 宏定义

在开发Letter shell应用前,需要知道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            1

/**
 * @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  1

/**
 * @brief 使用LF作为命令行回车触发
 *        可以和SHELL_ENTER_CR同时开启
 */
#define     SHELL_ENTER_LF              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     200

/**
 * @brief 快速帮助
 *        作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息
 */
#define     SHELL_QUICK_HELP            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 * 60 * 1000

#endif

2、Letter Shell 内置命令

在 Letter shell中默认内置了一些 shell命令,在 shell中输入 help 后回车或者直接按下 Tab 键,就可以打印当前系统支持的所有命令。

按下 Tab 键后可以列出当前支持的所有命令。以下为按下 Tab 键后打印出来的当前支持的所有显示Letter shell中的命令,左边是命令名称,右边是关于命令的描述:

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第8张图片

3、Letter Shell 自定义 shell命令 

自定义的 shell命令,可以在 shell模式下被运行,将一个命令导出到 shell模式可以使用如下宏接口:

SHELL_EXPORT_CMD(_attr, _name, _func, _desc)

参数

描述

_attr

命令属性

_name

命令名

_func

命令函数

_desc

命令描述

原型如下(shell.h):

    /**
     * @brief shell 命令定义
     * 
     * @param _attr 命令属性
     * @param _name 命令名
     * @param _func 命令函数
     * @param _desc 命令描述
     */
    #define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \
            const char shellCmd##_name[] = #_name; \
            const char shellDesc##_name[] = #_desc; \
            SHELL_USED const ShellCommand \
            shellCommand##_name SHELL_SECTION("shellCommand") =  \
            { \
                .attr.value = _attr, \
                .data.cmd.name = shellCmd##_name, \
                .data.cmd.function = (int (*)())_func, \
                .data.cmd.desc = shellDesc##_name \
            }

普通C函数形式

 导出无参数命令时,函数的入参为 void,示例如下:

void hello(void)
{
    printf("hello letter shell!\n");
}

//导出到命令列表里
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), hello, hello, hello);

系统运行起来后,在 shell控制台按 tab 键可以看到导出的命令,运行 hello 命令,运行结果如下所示:

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第9张图片

导出有参数的命令时,还可传入参数。导出有参数命令示例如下:

void parameter_test (int num, char *str)
{
  printf("parameter test: num = %d, str = %s !\r\n", num, str);

}
//导出到命令列表里
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, parameter_test, parameter_test, parameter test);

系统运行起来后,在 shell控制台按 tab 键可以看到导出的命令,运行parameter_test命令(可以直接tab补全),运行结果如下所示:

《嵌入式-开源项目Letter Shell》一个小而美的嵌入式shell_第10张图片

Main函数形式:

使用此方式,一个函数定义的例子如下:

int func(int argc, char *agrv[])
{
    printf("%dparameter(s)\r\n", argc);
    for (char i = 1; i < argc; i++)
    {
        printf("%s\r\n", argv[i]);
    }
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), func, func, test);

终端调用

letter:/$ func "hello world" 
2 parameter(s) 
hello world

变量使用

letter shell 3.x支持导出变量,通过命令行查看,设置以及使用变量的值

  • 导出变量

    变量导出使用SHELL_EXPORT_VAR宏,支持整形(char, short, int),字符串,指针以及节点变量,变量导出需要使用引用的方式,如果不允许对变量进行修改,在属性中添加SHELL_CMD_READ_ONLY

    int varInt = 0;
    SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_INT), varInt, &varInt, test);
    
    char str[] = "test string";
    SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_STRING), varStr, str, test);
    
    Log log;
    SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_POINT), log, &log, test);
  • 查看变量

    在命令行直接输入导出的变量名即可查看变量当前的值

    letter:/$ varInt
    varInt = 0, 0x00000000
    
    letter:/$ varStr
    varStr = "test string"
  • 修改变量

    使用setVar命令修改变量的值,对于字符串型变量,请确认字符串有分配足够的空间,指针类型的变量不可修改

    letter:/$ setVar varInt 45678
    varInt = 45678, 0x0000b26e
    
    letter:/$ setVar varStr "hello"
    varStr = "hello"
  • 使用变量

    letter shell 3.x的变量可以在命令中作为参数传递,对于需要传递结构体引用到命令中的场景特别适用,使用$+变量名的方式传递

    letter:/$ shellPrint $shell "hello world\r\n"
    hello world

注意

命令参数的最大个数在中shell_cfg.h配置,默认最大是8,但是命令占了一个参数,因此用户可用的应该是7个。 

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

Letter shell不仅可以使用命令的方式运行程序,还可像Linux的终端一样,还能通过上下键能选择历史命令,历史命令的个数默认最大为5个。
/**
 * @brief 历史命令记录数量
 */
#define     SHELL_HISTORY_MAX_NUMBER    5

Letter shell很强大,还有很多功能可玩,本文先讲到这里,更过使用说明可参考:letter-shell: letter shell一个嵌入式小型shell,特别适用于单片机系统。 (gitee.com)。

你可能感兴趣的:(开源,stm32)