letter-shell | 一个功能强大的嵌入式shell
嵌入式开源项目精选专栏
本专栏由Mculover666创建,主要内容为寻找嵌入式领域内的优质开源项目,一是帮助开发者使用开源项目实现更多的功能,二是通过这些开源项目,学习大佬的代码及背后的实现思想,提升自己的代码水平,和其它专栏相比,本专栏的优势在于:
不会单纯的介绍分享项目,还会包含作者亲自实践的过程分享,甚至还会有对它背后的设计思想解读。
目前本专栏包含的开源项目有:
- SFUD | 一个简洁实用的开源项目,帮你轻松搞定SPI Flash
- cJSON | 一个轻量级C语言JSON解析器
- paho | 支持10种语言编写mqtt客户端,总有一款适合你!
- MultiButton | 一个小巧简单易用的事件驱动型按键驱动模块
如果您自己编写或者发现的开源项目不错,欢迎留言或者私信投稿到本专栏,分享获得双倍的快乐!
1. letter-shell
本期给大家带来的开源项目是 letter-shell,一个功能强大的嵌入式shell,作者NevermindZZT,目前收获 155 个star,遵循 MIT 开源许可协议。
letter shell 3.0是一个C语言编写的,可以嵌入在程序中的嵌入式shell,通俗一点说就是一个串口命令行,可以通过命令行调用、运行程序中的函数。
目前 letter-shell 3.0版本支持的功能有:
- 命令自动补全
- 快捷键功能定义
- 命令权限管理
- 用户管理
- 变量支持
项目地址:https://github.com/NevermindZZT/letter-shell
2. 移植letter-shell
2.1. 移植思路
① 看项目readme文件中的移植说明,一般都比较完善;
② 看项目中的demo,举一反三;
③ 看别人移植好的博客;
2.2. 移植过程
letter-shell的移植非常简单,自己实现串口读写一个字符的接口,自己编写一个初始化函数,完成。
本文中我使用的是小熊派IoT开发套件,主控芯片为STM32L431RCT6:
移植之前需要准备一份裸机工程,我使用STM32CubeMX生成,使用USART1的查询方式发送数据、使用USART1的中断方式接收数据,参考教程:
- STM32CubeMX_06 | 使用USART发送和接收数据(查询模式)
- STM32CubeMX_07 | 使用USART发送和接收数据(中断模式)
① 复制源码到工程中:
② 新建存放实现移植接口的文件:
③ 在keil中添加文件,添加头文件路径:
目录下还有头文件,添加到头文件路径中:
④ 编辑shell_port.c文件,实现向串口写入一个字符的接口,并编写shell的初始化函数:
/*** @brief shell移植到STM32L431时的接口实现* @author mculover666* @date 2020/03/27
*/#include "shell.h"
#include <stm32l4xx_hal.h>
#include "usart.h"
#include "shell_port.h"/* 1. 创建shell对象,开辟shell缓冲区 */
Shell shell;
char shell_buffer[512];/* 2. 自己实现shell写函数 *///shell写函数原型:typedef void (*shellWrite)(const char);
void User_Shell_Write(const char ch)
{//调用STM32 HAL库 API 使用查询方式发送HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF);
}/* 3. 编写初始化函数 */
void User_Shell_Init(void)
{//注册自己实现的写函数shell.write = User_Shell_Write;//调用shell初始化函数shellInit(&shell, shell_buffer, 512);
}
⑤ 编辑shell_port.h文件,声明自己编写的初始化函数:
#ifndef _SHELL_PORT_H_
#define _SHELL_PORT_H_#include "shell.h"/* 将shell定义为外部变量,在串口中断回调函数中还要使用 */
extern Shell shell;/* 声明自己编写的初始化函数 */
void User_Shell_Init(void);#endif /* _SHELL_PORT_H_ */
⑥ 在main.c
文件的末尾编写串口中断回调函数,在接收到一个字符之后调用 shellHandler 函数进行处理,首先把头文件包含进来:
/* USER CODE BEGIN Includes */
#include "shell_port.h"
/* USER CODE END Includes */
接着开辟一个串口接收缓冲区(一个字符):
/* USER CODE BEGIN PV */
uint8_t recv_buf = 0;
/* USER CODE END PV */
然后编写回调函数
/* USER CODE BEGIN 4 */
/* 中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{/* 判断是哪个串口触发的中断 */if(huart ->Instance == USART1){//调用shell处理数据的接口shellHandler(&shell, recv_buf);//使能串口中断接收HAL_UART_Receive_IT(&huart1, (uint8_t*)&recv_buf, 1);}
}
/* USER CODE END 4 */
⑥ 在main函数
中使能中断接收,调用自己编写的shell初始化函数:
/* USER CODE BEGIN 2 */
//使能串口中断接收
HAL_UART_Receive_IT(&huart1, (uint8_t*)&recv_buf, 1);
User_Shell_Init();
/* USER CODE END 2 */
至此,移植完成,编译、下载之后使用串口终端软件Mobaxterm即可看到效果:
2.3. 宏定义配置
letter-shell具备很多功能,可以通过宏定义来开启或者关闭,在shell_cfg.h
文件中根据需要进行配置:
在本次移植过程中,我将shell默认用户改为了mculover666,其它宏保持默认:
#define SHELL_DEFAULT_USER "mculover666"
3. 使用letter-shell
3.1. 应用场景
在嵌入式项目中,做出一个项目之后还需要用户在串口终端中进行操作,这样的情况很少,串口的作用基本都是用来在调试阶段打印信息。
但是在调试的模组比较复杂时,比如SPI Flash、LCD屏幕这些,我希望可以在串口直接调用某几个功能函数开始执行,当移植了shell之后,在代码中只需要添加一行宏定义,就可以在串口中调用此函数开始执行,多爽!
当然也可以在main函数中调用,然后打断点进入调试,根据个人喜好选择就行。
3.2. 可执行命令定义宏
这个宏可以实现将函数添加到shell的可执行命令列表中,使用时需要确保shell_cfg.h中的宏定义要开启:
#define SHELL_USING_CMD_EXPORT 1
该宏定义的定义如下:
/*** @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; \const ShellCommand \shellCommand##_name SECTION("shellCommand") = \{ \.attr.value = _attr, \.data.cmd.name = shellCmd##_name, \.data.cmd.function = (int (*)())_func, \.data.cmd.desc = shellDesc##_name \}
第一个参数attr表示该命令的属性,包括命令权限和命令类型等,但是对于目前这种应用场合下多用户没什么用,所以设置为下面的值就ok:
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)
最后需要注意一点,这个宏目前测试在MDK中可以正常使用,在IAR和GCC中请移步项目地址阅读文档。
3.3. 调用函数示例
在main.c文件中随便定义一个函数,然后使用宏定义导出到命令列表进行测试:
/* USER CODE BEGIN 0 */
int test(int i, char ch, char *str)
{printf("input int: %d, char: %c, string: %s\r\n", i, ch, str);return 0;
}//导出到命令列表里
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), test, test, test);/* USER CODE END 0 */
有两点需要注意:
① 目前支持的参数类型为整型、字符型、字符串型,不支持浮点型参数。
② 函数最大传入参数个数由shell_cfg.h中的宏定义配置:
#define SHELL_PARAMETER_MAX_NUMBER 8
所以在使用时导出到命令列表中的参数最大是 8 - 1 = 7
个,测试如下:
int test2(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8)
{printf("input int: %d, %d, %d, %d, %d, %d, %d, %d\r\n", i1, i2, i3, i4, i5, i6, i7, i8);return 0;
}
//导出到命令列表里
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), test2, test2, test2);
执行之后失败:
4. letter-shell设计思想解读
4.1. 终端软件交互原理
当我们使用诸如 mobaxterm 这类软件作为串口终端交互时,首先需要注意三点:
① 当在终端软件中输入一个字符时,这个字符会被直接通过串口发送,屏幕上不会显示任何东西;
② 如果单片机做了回显,终端软件中收到后才会显示出来;
③ 当在终端软件中按下回车键、退格键、删除键、四个方向键时,会直接发送其键值,单片机中靠这个键值来解析是否按下了按键。
在lettershell中输入keys
命令即可查看当前支持的按键键值解析:
解析这些键值的源码在shell.c
中,分别对应以下的函数:
void shellUp(Shell *shell);
……
void shellTab(Shell *shell);
void shellBackspace(Shell *shell);
void shellEnter(Shell *shell);
……
除此之外,lettershell还支持设置快捷键来快速调用函数执行,比如这里我设置在终端中按下Ctrl+A
之后调用hello函数,过程如下:
① 首先在串口中断中添加一行代码,查看按下Ctrl+A
快捷键之后串口发送的键值:
编译下载,在终端软件中按下Ctrl+A
,得到的键值如下:
此处需要注意,终端发送的字符串序列以大端模式表示,所以单片机解析的键值应该为0x01000000
。
② 一行代码搞定键值解析,在main文件中添加测试函数hello,并使用宏定义向lettershell中添加按键解析值:
int hello()
{printf("Hello World\r\n");return 0;
}
SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x01000000, hello, hello);
将之前串口接收中断函数中添加的printt注释,编译,下载:
4.2. 如何解析命令
在letter shell中,当它接收到一个字符后,首先判断是不是特殊的按键,否则直接扔进命令解析缓冲区,因为它对左右方向键、退格键处理的比较好,相当于不停的在维护命令解析缓冲区的内容,非常整齐,解析时就变得相对容易了。
当用户按下回车键时,首先去命令列表中匹配:
/*** @brief shell匹配命令* * @param shell shell对象* @param cmd 命令* @param base 匹配命令表基址* @param compareLength 匹配字符串长度* @return ShellCommand* 匹配到的命令*/
ShellCommand* shellSeekCommand(Shell *shell,const char *cmd,ShellCommand *base,unsigned short compareLength)
如果匹配到之后则开始执行这条命令:
/*** @brief shell运行命令* * @param shell shell对象* @param command 命令*/
static void shellRunCommand(Shell *shell, ShellCommand *command)
限于文章篇幅,具体解析的过程和方法不再深入讲述,如有兴趣可以自行研究学习,另外,=除了本文所讲述的自定义函数调用,自定义快捷键设置,letter shell还有非常多的功能,比如自定义变量查看,用户登录,用户权限,自动锁定shell等高级内容,也欢迎有兴趣的读者探索,研究!
5. 项目工程源码获取和问题交流
目前我将LetterShell源码、我移植到小熊派STM32L431RCT6开发板的工程、移植到STM32Nucleo-STM32G071RB开发板的工程源码上传到了QQ群里(包含好几份HAL库,QQ相对速度快点),可以在QQ群里下载,有问题也可以在群里交流,当然也欢迎大家分享出来自己移植的工程到QQ群里:
放上QQ群二维码:
接收更多精彩文章及资源推送,欢迎订阅我的微信公众号:『mculover666』。
letter-shell | 一个功能强大的嵌入式shell相关推荐
- WSH:一款功能强大的Web Shell生成器和命令行接口工具
关于WSH WSH是一款功能强大的Web Shell生成器和命令行接口工具.我们考虑到只用一个HTTP客户端来跟Webshell交互其实是一件很痛苦的事,我们需要在表格中输入命令,然后再点各种按钮.因 ...
- Shells:一款功能强大的反向Shell快速生成工具
关于Shells Shells是一款功能强大的反向Shell快速生成工具,该工具由4ndr34z负责开发和维护,可以帮助广大研究人员轻松生成常用的反向Shell.如果你需要一种简单的方法来生成格式化的 ...
- LFTP : 一个功能强大的命令行FTP程序
LFTP : 一个功能强大的命令行FTP程序 大家好,这篇文章是介绍Lftp以及如何在Linux操作系统下安装的.Lftp是一个基于命令行的文件传输软件(也被称为FTP客户端),由Alexander ...
- Android-如何开发一个功能强大的图片选择器
图片选择器是Android开发中会经常用到的一个功能,特别对于社交类的应用,比如头像设置,比如发图片.自然ImagePicker的轮子很多,今天介绍一个功能强大的轮子SImagePicker 介绍 首 ...
- 原生python自带的ide_python自带的IDE是一个功能强大的IDE
[多选题]关于炒炭存性说法正确的有( ) [单选题]( )是著作权实现其著作财产权的主要方式. [单选题]以下哪个不属于创新成果?( ) [单选题]下列哪一项不属于无人机配送的优势 [单选 ...
- 一个功能强大超级好用的图表组件Dundas Chart
最近因为项目需要,需要为客户的统计数据生成图表,包括柱状图和饼图.我找来了Dundas Software的Dundas Chart,大家可以上它的网站http://www.dundas.com/查看最 ...
- Ledger-复式记账的一个功能强大的命令行工具
无论你是一个电脑极客还是普通用户,记录账户开销总是必不可少的.虽然在Linux上有很多基于GUI的记账工具(比如 - GNUCash)受到大家欢迎,但是工作在命令行的记账工具是很多用户难以想象的.在本 ...
- Jcrop是一个功能强大的图像裁剪引擎
Jcrop是一个功能强大的图像裁剪引擎jQuery的. 它的设计使开发人员可以很容易地直接集成了先进的图像裁剪功能集成到任何基于Web的应用程序在不牺牲动力和灵活性(或编码,测试和调试的星期).Jcr ...
- 一个功能强大的开源简历生成器,太炫了!
OpenResume 是一个功能强大的开源简历生成器和简历解析器.目标是为每个人提供免费的现代专业简历设计,让任何人都能充满信心地申请工作. 核心优势 「实时UI更新」:当输入简历信息时,简历 PDF ...
最新文章
- 解决android 编译失败 Unexpected scopes found in folder
- mongodb web_MongoDB和Web应用程序
- dbunit使用_使用dbUnit,JSON,HSQLDB和JUnit规则进行数据库单元测试
- java dijkstra算法代码_[转载]Java实现dijkstra算法: 地图中任意起点寻找最佳路径...
- idea中浏览器打开页面404_深圳网站建设中的404页面有什么用
- python3实战练手项目_Python0基础练手项目有哪些值得推荐?附实战项目+学习图谱...
- 图像增广——图片旋转任意角度(python实现)
- 2020华为软挑热身赛 个人总结
- 在哪里买腾讯云服务器,在哪查看我的腾讯云服务器购买记录?
- 我的口琴之路(附c调简谱)--------一个命中注定音乐巅峰是小星星的男人
- win10锁定计算机后黑屏,win10锁定屏幕就黑屏怎么办
- 如何在macOS中重置字体集
- Android Studio OpenGL ES绘制三棱锥/四面体的多纹理贴图 每个面使用一张图片渲染
- 汇编语言实验一-《汇编语言-王爽老师》
- android手机短信备份软件,手机短信备份软件-SMS Backup Restore Pro(短信备份) 安卓版v7.13-PC6安卓网...
- 项目管理中的边边角角 之三
- JStorm和Storm比较
- lisp横断面数据文件_[求助]编一个将鸿业市政的横断面数据格式转化为EICAD横断面数据格式的文件...
- 贪心算法【区间调度】【背包问题】【集合覆盖】【旅行商问题】【哈夫曼构造价值树】
- [源码和文档分享]基于J2EE的JSH框架和百度语音识别接口实现的语音记账APP
热门文章
- Unity3D 制作调色板
- Python编程学习第七课之Python的数据类型
- 崩坏3服务器维护2月8号,崩坏3版本更新公告 2月8号2.1版本降临
- 圣斗士星矢ol服务器端文件夹,圣斗士星矢ol完整安装指南
- Unity项目技术方案Dots架构方案简介
- 蓝桥杯 —— Web前端(数据交互类)【标题即题目链接,点击查看具体要求】
- Windows Server 2008 R2 SP1中的具体改进
- Beyond Compare 4.2.10 zhuce
- php5.6 ecshop,PHP 5.6以上版本运行 ecshop不兼容问题解决方案
- dockers迁移容器数据的方法