《嵌入式 - 开源项目》一个小而美的嵌入式shell - 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。
Letter shell有如下功能:
命令自动补全,使用TAB键补全命令
命令帮助,使用help [command]显示命令帮助
帮助补全,输入命令后双击TAB键补全命令帮助指令
快捷键,支持使用CTRL+A~Z组合按键直接调用函数
shell变量,支持在shell中查看和修改变量值,支持变量作为命令参数
登录密码,支持在shell中使用登录密码,支持超时自动锁定
2 Letter shell移植
Shell是一个命令行交互式形式存在,那最常规的就是使用MCU的串口资源了,当然也可使用USB模拟的虚拟串口。
Letter shell的移植比较简单,既然需要占用串口资源,那么首先要准备一个裸机工程,该工程需要事先串口的收发,关于串口的实现请参看逼着文章:
标准库
HAL库
笔者本文以标准库为例讲解。
1.复制源码
首先下载letter-shell,然后在工程中新建Letter_shell目录,将letter-shell目录下的src的文件复制到工程目录Middlewares/Letter_shell中。
2.新建接口文件
在工程用户目录下新建shell_port.c和shell_port.h文件,当然也可以放在工程目录Letter_shell。
3.配置工程
打开Keil,添加相应的文件。
然后添加相应的头文件路径。
接下来就是实现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等工具,打印信息如下:
很简单吧。
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中的命令,左边是命令名称,右边是关于命令的描述:
自定义 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 命令,运行结果如下所示:
导出有参数的命令时,还可传入参数。导出有参数命令示例如下:
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命令,运行结果如下所示:
值得注意的是,命令参数的最大个数在中shell_cfg.h配置,默认最大是8,但是命令占了一个参数,因此用户可用的应该是7个。
/*** @brief shell命令参数最大数量* 包含命令名在内,超过8个参数并且使用了参数自动转换的情况下,需要修改源码*/
#define SHELL_PARAMETER_MAX_NUMBER 8Letter shell不仅可以使用命令的方式运行程序,还可像Linux的终端一样,还能通过上下键能选择历史命令,历史命令的个数默认最大为5个。
/*** @brief 历史命令记录数量*/
#define SHELL_HISTORY_MAX_NUMBER 5
非常的方便,Letter shell很强大,还有很多功能可玩,本文先讲到这里,后面再深入讲解Letter shell的设计思想。
开源项目合集
欢迎访问我的网站
BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎
欢迎订阅我的微信公众号
关注公众号[嵌入式实验楼]获取更多资讯
《嵌入式 - 开源项目》一个小而美的嵌入式shell - letter shell相关推荐
- 推荐一个必学的嵌入式开源项目
给大家推荐一个非常适合学习的嵌入式开源项目:mjpg-streamer. 如果你有C语言.Linux基础,那研究这个项目将会对你的技术有很大的提升. mjpg-streamer是谷歌开源的视频采集服务 ...
- 嵌入式开源项目哪里找
经常看微信公众号文章,虽然对嵌入式开源项目用得不多,但耳濡目染知道这是好东西,可以借势提升自己的代码水平,从代码逻辑到项目程序架构,再到项目需求刷新自己的认知. 之前认为尽快提升工作能力的一个选择是加 ...
- GitHub的嵌入式开源项目
来源:嵌入式大杂烩 关于GitHub,可能很多人误以为这是互联网人的专属,其实并不是,那上面嵌入式相关的开源项目是有很多的. 现分享一些高星开源项目(像RT-Thread.AWTK等大家都熟知的就不介 ...
- 分享一些最新的嵌入式开源项目
关注.星标公众号,直达精彩内容 来源:网路素材 大家平时学习的资源可能来自不同地方,对于程序员来说,Github上高星的开源项目值得了解并学习. 今天就给大家分享几个在GitHub上高星的嵌入式相关的 ...
- 01 将狼才鲸的嵌入式开源项目仓库链接
将狼才鲸空闲时间的嵌入式开源项目工作计划 作者 将狼才鲸 创建时间 2022-11-26 更新时间 -- 文章Markdown源文件Gitee仓库地址:才鲸嵌入式 / 将狼才鲸空闲时间的嵌入式开源项目 ...
- 助你成为嵌入式高手的100多个软硬件开源项目
大家好,转发一个朋友总结的资料文章,内容如下 大家好,我是老温,今天给大家推荐一个嵌入式开源项目汇总的仓库. 学习初期最难找的就是找学习资料了,本贴精心汇总了一些嵌入式相关资源,包括但不限于编程语言. ...
- 提高80%串口调试效率,这款Linux开源项目爱了
[开坑国产单片机GD32系列,带你零死角玩转GD32] GD32F103C8T6下的Letter Shell移植(基于KEIL) 目录 [开坑国产单片机GD32系列,带你零死角玩转GD32] GD32 ...
- (4.2.0)GitHub开源项目收集
(4.2.0)GitHub开源项目收集 2016年09月24日 16:45:051543人阅读 评论(0) 收藏 举报 分类: 4.2-android开源组件(98) 版权声明:本文为博主原创文章 ...
- Arduino ESP32录音+ SD卡储存开源项目
Arduino ESP32录音+ SDHC卡储存开源项目 一个好的开源项目可以帮助我们学习和提升自己的知识. ESP32录音并记录在SD卡中 开源项目地址 因为是国外的项目,基本都是github,,为 ...
最新文章
- PostgreSQL:创建自增序列id,分区表,分区表子表
- Innodb存储引擎的缓存命中率计算
- Java黑皮书课后题第4章:*4.6(图上的随机点)编写一个程序,产生一个圆心位于(0,0)原点半径为40的圆上面的三个随机点,显示由这三个随机点组成的三角形的三个角的度数
- zabbix磁盘的自动发现与磁盘指标监控
- com.android.tools.build:gradle:2.3.3,关于com.android.tools.build:gradle:3.4.2的构建问题
- 2012年7月份第4周51Aspx源码发布详情
- hdu 1559 最大子矩阵 (简单dp)
- 内容 超链接_excel中如何设置目录与返回目录超链接?这两种方法很简单
- linux svn 自动启动,Linux下设置svn开机自启动
- 模块 datetime
- dubbo 分布式服务框架 介绍
- 软件测试成熟度与自动化测试
- 机器学习笔记----(1)什么是机器学习
- ios安全机制不支持antofocus
- python安装包错误的问题
- 戴尔服务器2012系统密钥,戴尔恢复密钥在哪里找
- 观点 | 滴滴 AI Labs 负责人叶杰平教授:深度强化学习在滴滴的探索与实践+关于滴滴智能调度的分析和思考+滴滴派单和Uber派单对比
- 在LR字符串中交换相邻字符
- 相机的对焦是什么意思?为什么需要对焦?
- LNMP架构搭建编译安装详细部署