0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一个小而美的嵌入式shell - letter shell

嵌入式大杂烩 来源:嵌入式大杂烩 作者:嵌入式大杂烩 2023-06-10 15:11 次阅读

源码地址:https://github.com/NevermindZZT/letter-shell

1 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。

F:\\File\\嵌入式项目\\1 工具\\一个小而美的嵌入式shell - letter shell\\源码\\letter-shell\\doc\\img\\shell_info.png

Letter shell有如下功能:

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

2 Letter shell移植

Shell是一个命令行交互式形式存在,那最常规的就是使用MCU的串口资源了,当然也可使用USB模拟的虚拟串口。

Letter shell的移植比较简单,既然需要占用串口资源,那么首先要准备一个裸机工程,该工程需要事先串口的收发,关于串口的实现请参看逼着文章:

标准库:https://bruceou.blog.csdn.net/article/details/79341769

HAL库:https://bruceou.blog.csdn.net/article/details/109190370

笔者本文以标准库为例讲解。

1.复制源码

首先下载letter-shell,然后在工程中新建Letter_shell目录,将letter-shell目录下的src的文件复制到工程目录Middlewares/Letter_shell中。

C:\\Users\\BruceOu\\Desktop\\1.png

2.新建接口文件

在工程用户目录下新建shell_port.c和shell_port.h文件,当然也可以放在User目录。

3.配置工程

打开Keil,添加相应的文件。

1686380797023qlrrgj4bab

然后添加相应的头文件路径。

168638079732110406rx2s2

接下来就是实现Letter shell的收发。

发送代码如下:

/**
 * @brief 用户shell写
 * 
 * @param data 数据
 */
void userShellWrite(char data)
{
    USART_SendData(USART1, (uint8_t) data);
    /* 等待发送完毕 */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);		
}

接收采用中断的方式,代码如下:

/**
  * @brief  This function handles USART1 Handler.
  * @param  None
  * @retval None
  */
void USART1_IRQHandler(void)
{
    uint8_t ch;  //接收中断缓冲

    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
      ch = USART_ReceiveData(USART1);

      //ch = USART1- >DR;
      //调用shell处理数据的接口
      shellHandler(&shell, ch);
    } 
}

还需要实现Letter shell初始化接口。

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

最后在主函数中初始化即可。

/**
  * @brief  mian
  * @param  None
  * @retval int
  */
int main(void)
{
    /* 配置SysTick 为10us中断一次 */
    SysTick_Init();

    /* USART1 配置模式为 115200 8-N-1,中断接收 */
    USART1_Config();

    userShellInit();

    for(;;)
    {
        Delay_ms(50);
    }
}

好了,这就移植完成了,编译、下载,连接串口1,使用xshell等工具,打印信息如下:

1686380797656v36x5l4oy3

很简单吧。

3 Letter shell应用

3.1 Letter shell宏定义

在开发Letter shell应用前,需要知道Letter shell的宏定义,其宏定义在shell_cfg.h文件。

#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命令参数最大数量
 *        包含命令名在内,超过8个参数并且使用了参数自动转换的情况下,需要修改源码
 */
#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 管理的最大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 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

shell_cfg.h文件已经注释了,笔者就不再赘述了。

3.2 Letter shell内置命令

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

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

168638079794723wdssedte

  • 自定义 shell命令

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

SHELL_EXPORT_CMD(_attr, _name, _func, _desc)

参数 描述
_attr 命令属性
_name 命令名
_func 命令函数
_desc 命令描述

原型如下:

#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \\
				const char shellCmd##_name[] = #_name; \\
				const char shellDesc##_name[] = #_desc; \\
				const ShellCommand \\
				shellCommand##_name SECTION("shellCommand") =  \\
				{ \\
						.attr.value = _attr, \\
						.data.cmd.name = shellCmd##_name, \\
						.data.cmd.function = (int (*)())_func, \\
						.data.cmd.desc = shellDesc##_name \\
				}

导出无参数命令时,函数的入参为 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 命令,运行结果如下所示:

1686380798207dxg57s5879

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

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), parameter_test, parameter_test, parameter test);

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

1686380798488wepvp3mzc7

值得注意的是,命令参数的最大个数在中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的设计思想。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    4983

    文章

    18288

    浏览量

    288526
  • Linux
    +关注

    关注

    87

    文章

    10991

    浏览量

    206742
  • WINDOWS
    +关注

    关注

    3

    文章

    3440

    浏览量

    87144
  • Shell
    +关注

    关注

    1

    文章

    358

    浏览量

    22903
  • GitHub
    +关注

    关注

    3

    文章

    457

    浏览量

    15926
收藏 人收藏

    评论

    相关推荐

    嵌入式Linux入门(五、Shell脚本编程上:认识Shell脚本)

    大家好,是矜辰所致,嵌入式 Linux入 门第五课,本课开始简单学习一下 Shell 脚本编程。
    的头像 发表于 07-13 16:10 1014次阅读
    <b class='flag-5'>嵌入式</b>Linux入门(五、<b class='flag-5'>Shell</b>脚本编程上:认识<b class='flag-5'>Shell</b>脚本)

    基于APM32F4移植使用letter-shell命令行终端

    letter shell是一个C语言编写的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式
    的头像 发表于 11-03 17:23 768次阅读
    基于APM32F4移植使用<b class='flag-5'>letter-shell</b>命令行终端

    Your Second Shell Script# #嵌入式开发

    嵌入式Shell
    嵌入小黑
    发布于 :2022年08月26日 14:34:12

    #硬声创作季 嵌入式基础:Shell的使用视频

    嵌入式Shell
    Mr_haohao
    发布于 :2022年10月24日 08:07:00

    嵌入式Linux开发入门普及课程之Shell编程基础

    视频主题:Shell编程基础视频主讲:曾宏安,华清远见企业学院(www.farsight.com.cn)讲师。视频简介:嵌入式公益培训活动是华清远见嵌入式普及推广活动之,主要面向
    发表于 08-13 15:08

    嵌入式系统开发或者是驱动开发对shell编程要求很高吗?

    嵌入式系统开发或者是驱动开发对shell编程要求很高吗?还是只用了解下就可以了,我现在可以看懂shell程序了,这样有必要继续深入学习shell吗?求前辈指导~~~~谢谢~
    发表于 03-04 20:32

    嵌入式Linux开发工具-shell编程介绍

    嵌入式Linux系统开发(文件、进程、线程、网络)20嵌入式系统移植8h嵌入式驱动开发8h自动生成: CMake autotool嵌入式Linux开发工具-
    发表于 11-08 08:50

    Shell与变量的相关资料分享

    笔记系列嵌入式系统方向学习笔记1嵌入式系统方向学习笔记2嵌入式系统方向学习笔记3嵌入式系统方向学习笔记4文章目录笔记系列前言、开发工具的总
    发表于 12-22 08:11

    脚本shell语言的相关资料推荐

    嵌入式学习中,Shell就是命令行解释器,它的作用就是遵循定的语法将输入的命令加以解释并传给系统。它为用户提供了
    发表于 12-23 06:24

    shell的分类有哪些?如何使用?

    嵌入式入门学习笔记,遇到的问题以及心得体会!DAY13概述:1.shell脚本(编程)2.shellshell脚本3.shell的分类4.
    发表于 12-23 08:21

    shell脚本的相关资料分享

    脚本提供数组、循环、条件判断的等功能shell脚本般是Linux运维或者系统管理员要掌握的,作为嵌入式开发人员,只需要掌握shell脚本最基础的部分即可二、
    发表于 12-23 06:17

    手把手教你制作shell解释器

    Linux系统编程第06期:从零实现shell解释器 6年嵌入式开发经验...
    发表于 12-23 08:21

    【CW32饭盒派开发板试用体验】+ 串口letter-shell 移植

    本次主要移植letter-shell过程。 letter shellC语言编写的,可以嵌入
    发表于 06-11 12:20

    嵌入式和物联网的shell脚本学习指南之shell脚本入门免费下载

    本文档的主要内容详细技术的是嵌入式和物联网的shell脚本学习指南之shell脚本入门免费下载。
    发表于 09-11 17:15 20次下载
    <b class='flag-5'>嵌入式</b>和物联网的<b class='flag-5'>shell</b>脚本学习指南之<b class='flag-5'>shell</b>脚本入门免费下载

    AN023 如何在GD平台移植Letter shell

    AN023 如何在GD平台移植Letter shell
    发表于 02-27 18:25 1次下载
    AN023 如何在GD平台移植<b class='flag-5'>Letter</b> <b class='flag-5'>shell</b>