源码地址: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。
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中。
2.新建接口文件
在工程用户目录下新建shell_port.c和shell_port.h文件,当然也可以放在user目录。
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#endifshell_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的设计思想。
东芝3相直流无刷电机控制预驱IC TC78B011FTG简介
华为nova 8系列有望在12月发布,延续OLED挖孔全面屏的设计
乐视网股份预计将于3月31日前完成交割
基于大语言模型辩论的多智能体协作推理分析
苹果iPad Pro2曝光 将配双摄像头“碾压”小米平板3!
一个小而美的嵌入式shell - letter shell
我国正在加快5G工业应用等关键技术的应用来推动工业互联网的发展
土壤微量元素测定仪的功能特点
十余家上市公司辟谣室温超导
卫生快速检测系统的应用及特性
GKN多模变速器技术的多方面解读
智能硬件时代,如何才能开启你的创客之路?
这个电路真的是没啥用吗?不一定!
思科拟1.25亿美元购自动化管理软件商Cloupia
程京谈生物芯片是什么?医疗不再依赖专业人员
折叠屏技术尚未成熟,Mate X要推迟到9月份上市?华为内部人士辟谣
2019上海汽车展|AI on Horizon,我们一路同行
英特尔计划出售专利组合中的8500项资产买家或为苹果
因故意降低iPhone性能,韩国判处苹果赔偿每人7万韩元
高智能测土配方施肥仪的产品特点