【嵌入式】开源shell命令行的移植和使用(2)——letter-shell

目录

一 背景说明

二 移植准备

三 移植过程

四 自定义命令

五 实际使用


一 背景说明

        之前使用过一款开源shell工具 nr_micro_shell (【嵌入式】开源shell命令行的移植和使用(1)——nr_micro_shell-CSDN博客),感觉功能还不够强大,故换了一个类似的开源shell工具 letter-shell

        shell是一个C语言编写的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式设备,以C语言函数为运行单位,可以通过命令行调用,运行程序中的函数。相对2.x版本,letter shell 3.x增加了用户管理,权限管理,以及对文件系统的初步支持。此外3.x版本修改了命令格式和定义,2.x版本的工程需要经过简单的修改才能完成迁移。若只需要使用基础功能,可以使用letter shell 2.x版本。

        功能包括:

  • 命令自动补全
  • 快捷键功能定义
  • 命令权限管理
  • 用户管理
  • 变量支持
  • 代理函数和参数代理解析

二 移植准备

【1】letter-shell下载地址——gitee:

letter-shell: letter shell一个嵌入式小型shell,特别适用于单片机系统。

【2】letter-shell下载地址——github:

https://github.com/NevermindZZT/letter-shell

【3】提前准备好芯片UART串口的初始化以及收发接口。

三 移植过程

【1】目录移植:将下载好的letter-shell文件解压到自己的工程目录下,并在IDE中包含所需的src中的源文件以及头文件,另外将demo中的 shell_port.c 以及 shell_port.h 也移到src目录下一并处理:

        此时编译一下,可能会出现报错,自行处理一下。

【2】根据自己需要修改 shell_cfg.h 中的基础配置:

意义
SHELL_TASK_WHILE是否使用默认shell任务while循环
SHELL_USING_CMD_EXPORT是否使用命令导出方式
SHELL_USING_COMPANION是否使用shell伴生对象功能
SHELL_SUPPORT_END_LINE是否支持shell尾行模式
SHELL_HELP_LIST_USER是否在输入命令列表中列出用户
SHELL_HELP_LIST_VAR是否在输入命令列表中列出变量
SHELL_HELP_LIST_KEY是否在输入命令列表中列出按键
SHELL_ENTER_LF使用LF作为命令行回车触发
SHELL_ENTER_CR使用CR作为命令行回车触发
SHELL_ENTER_CRLF使用CRLF作为命令行回车触发
SHELL_EXEC_UNDEF_FUNC使用执行未导出函数的功能
SHELL_COMMAND_MAX_LENGTHshell命令最大长度
SHELL_PARAMETER_MAX_NUMBERshell命令参数最大数量
SHELL_HISTORY_MAX_NUMBER历史命令记录数量
SHELL_DOUBLE_CLICK_TIME双击间隔(ms)
SHELL_QUICK_HELP快速帮助
SHELL_MAX_NUMBER管理的最大shell数量
SHELL_GET_TICK()获取系统时间(ms)
SHELL_USING_LOCK是否使用锁
SHELL_MALLOC(size)内存分配函数(shell本身不需要)
SHELL_FREE(obj)内存释放函数(shell本身不需要)
SHELL_SHOW_INFO是否显示shell信息
SHELL_CLS_WHEN_LOGIN是否在登录后清除命令行
SHELL_DEFAULT_USERshell默认用户
SHELL_DEFAULT_USER_PASSWORD默认用户密码
SHELL_LOCK_TIMEOUTshell自动锁定超时
SHELL_USING_FUNC_SIGNATURE使用函数签名

        如无特殊需要,可以使用默认配置。

【3】修改接口文件 shell_port.c :

        (i)改写 userShellWrite 为自己的串口输出接口

/*** @brief 用户shell写* * @param data 数据* @param len 数据长度* * @return short 实际写入的数据长度*/
short userShellWrite(char *data, unsigned short len)
{int i;for(i=0;i<len;i++){Uart_SendDataPollTimeOut(M0P_UART0, (uint8_t)data[i], SystemCoreClock/DBG_PRINTF_BAUDRATE);}return 0;
}

        (ii)串口接收中断中嵌入 shellHandler(&shell, ch) 以接收输入字符

/**************************************************************************
* 函数名称: Uart0_IRQHandler
* 功能描述: 串口接收中断
**************************************************************************/
void Uart0_IRQHandler(void)
{uint8_t ch;if(Uart_GetStatus(M0P_UART0, UartRC))       //UART0数据接收{Uart_ClrStatus(M0P_UART0, UartRC);      //清中断状态位ch = Uart_ReceiveData(M0P_UART0);       //接收数据字节shellHandler(&shell, ch);}if(Uart_GetStatus(M0P_UART0, UartTC))         //UART0数据发送{Uart_ClrStatus(M0P_UART0, UartTC);        //清中断状态位}
}

        (iii)用户shell初始化 userShellInit

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

【4】在程序初始化的时候调用串口的初始化以及shell初始化接口(userShellInit()),上电之后即可看到letter_shell的logo,按TAB键即可看到注册的几个命令:

#include "shell_port.h"int main(void)
{//串口初始化Dbg_Init();Dbg_Cfg();//命令行初始化userShellInit();while(1){}
}

四 自定义命令

        在 shell.c 中编辑以及自定义命令。自定义命令分为两步,第一步定义命令函数,第二步使用SHELL_EXPORT_CMD 导入命令列表

【1】定义命令函数

        letter shell 3.x同时支持两种形式的函数定义方式,形如main函数定义的func(int argc, char *agrv[])以及形如普通C函数的定义func(int i, char *str, ...),两种函数定义方式适用于不同的场景。

        (i)main函数形式(属性为SHELL_TYPE_CMD_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

        (注1:main函数形式下,如果要导入 flaot 类型的参数,可以使用 sscanf 来格式化输入:)

void shellSet(int argc, char *argv[])
{float t_vset;if(argc > 1){if(!strcmp("v", argv[1])){if(argc > 2){sscanf(argv[2], "%f", &t_vset);printf("Change V-USET to %f V!\r\n", t_vset);}}}
}

        (ii)普通C函数形式(属性为SHELL_TYPE_CMD_FUNC):使用此方式,shell会自动对参数进行转化处理,目前支持二进制,八进制,十进制,十六进制整形,字符,字符串的自动处理,如果需要其他类型的参数,请使用代理参数解析的方式(参考代理函数和代理参数解析),或者使用字符串的方式作为参数,自行进行处理,例子如下:

int func(int i, char ch, char *str)
{printf("input int: %d, char: %c, string: %s\r\n", i, ch, str);
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), func, func, test);

           终端调用:

letter:/$ func 666 'A' "hello world"
input int: 666, char: A, string: hello world

        (注2:普通C函数形式下,如果要导入 flaot 类型的参数,可以使用代理函数命令导出宏定义命令:

void test(int a, float b, int c, float d)
{printf("%d, %f, %d, %f \r\n", a, b, c, d);
}
SHELL_EXPORT_CMD_AGENCY(SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
test, test, test,
p1, SHELL_PARAM_FLOAT(p2), p3, SHELL_PARAM_FLOAT(p4));

        (iii)letter_shell还提供了诸如变量使用在函数中获取当前shell对象执行未导出函数等其他应用,具体可以参考gitee中的使用说明自行发掘。

        

【2】使用 SHELL_EXPORT_CMD 导入命令列表,其参数定义如下:

参数

描述

_attr

命令属性

_name

命令名

_func

命令函数

_desc

命令描述
/*** @brief shell 命令定义* * @param _attr 命令属性* @param _name 命令名* @param _func 命令函数* @param _desc 命令描述* @param ... 其他参数*/#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, \##__VA_ARGS__ \}

        至此,移植结束。

五 实际使用

        实际使用的效果如下:

        补充:自己调试的时候遇到一个问题:实际使用shell的时候,有时候按空格或者退格键会使得程序直接重启,更换了串口波特率9600为115200之后,问题解决。具体原因不明,希望有大神看到能够帮忙解惑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/225921.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux fork笔试练习题

1.打印结果&#xff1f; #include <stdio.h> #include <unistd.h> #include <stdlib.h>int main() {int i0;for(;i<2;i){fork();printf("A\n");}exit(0); } 结果打印 A A A A A A 2.将上面的打印的\n去掉,结果如何? printf("…

统计学中两组数据如何进行差异性(相关性)分析?

变量说明&#xff1a; 在确定分析方法前&#xff0c;我们需要了解手中的数据类型&#xff0c;这是最基础也是有必要的&#xff0c;在所有的数据类型中&#xff0c;我们将数据类型分为分类变量也为定类变量和连续变量也称为定量变量&#xff0c;那么什么是定类变量&#xff1f;…

文章解读与仿真程序复现思路——电力系统保护与控制EI\CSCD\北大核心《基于深度强化学习的城市配电网多级动态重构优化运行方法》

这个标题涉及到城市配电网&#xff08;Urban Power Distribution Network&#xff09;的优化运行方法&#xff0c;其中使用了深度强化学习&#xff08;Deep Reinforcement Learning&#xff09;技术&#xff0c;并且特别强调了多级动态重构。 解读每个关键部分&#xff1a; 基…

Selenium 学习(0.14)——软件测试之测试用例设计方法——因果图法2【基本步骤及案例】

1、因果图法的基本步骤 2、案例分析 1&#xff09;分析原因和结果 2&#xff09;关联原因和结果 投入1元5角或2元&#xff0c;按下“可乐”&#xff0c;送出“可乐”【暂时忽略找零】 投入2元&#xff0c;按下“可乐”或“雪碧”。找零5角&#xff0c;送出“可乐”或“雪…

人工智能_AI服务器安装清华开源_CHATGLM大语言模型_GLM-6B安装部署_人工智能工作笔记0092

看到的这个开源的大模型,很牛,~关键让我们自己也可以部署体验一把了,虽然不知道具体内部怎么构造的但是,也可以自己使用也挺好. 可以部署在自己的机器上也可以部署在云服务器上. 安装以后,是可以使用python代码进行提问,然后返回结果的,这样就可以实现我们自己的chat应用了, …

【多线程】-- 05 Lambda表达式

多线程 4 Lambda表达式 λ是希腊字母表中排序第十一位的字母&#xff0c;英语名称为Lambda是为了避免匿名内部类定义过多实质属于函数式编程的概念 为什么要使用Lambda表达式&#xff1f; 避免匿名内部类定义过多可以让代码看起来很简洁去掉了一堆没有意义的代码&#xff0…

GOAT:多模态、终身学习、平台无关的机器人通用导航系统

机器人应用中涉及到的核心技术包括&#xff1a;环境感知与理解、实时定位与建图、路径规划、行为控制等。GOAT通过多模态结合终生学习的方式让你的机器人可以在未知环境中搜索和导航到任何物体。小白也可以零门槛上手。 项目地址&#xff1a;https://theophilegervet.github.i…

centos服务器扩容

centos服务器扩容 我的情况是&#xff0c;原服务器是一个80g磁盘&#xff0c;管理员又追加了120G到这块磁盘上&#xff0c;需要把这120G重新追加使用。 请确认你遇到的情况是否和我初始截图一致&#xff0c;再往下看&#xff0c;免得浪费时间与精力 服务器中有120G尚未使用&…

数据治理框架和成熟度模型

数据治理成熟度模型 一个企业的数据治理能力越高&#xff0c;所享受到数据治理带来的价值也会越多&#xff0c;如增加收入、减少成本、降低风险等。于是&#xff0c;很多企业想要准确地评估本公司的数据治理能力&#xff0c;可以利用数据治理成熟度模型方法&#xff0c;包括 D…

fastjson和jackson序列化的使用案例

简单记录一下一个fastjson框架和jackson进行序列化的使用案例&#xff1a; 原json字符串&#xff1a; “{“lockCount”:”{1:790,113:1,2:0,211:0,101:1328,118:8,137:0,301:0,302:0}“,“inventoryCount”:”{1:25062,113:2,2:10000,211:2,101:11034,118:9,137:40,301:903914…

MySQL安装与配置教程

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

建筑工程模板包工包料价格

在建筑工程中&#xff0c;模板是承载混凝土浇筑的重要支撑结构&#xff0c;起到保持混凝土形状和支撑荷载的作用。对于建筑项目而言&#xff0c;了解建筑工程模板包工包料的价格范围是非常重要的。在本文中&#xff0c;我们将以混凝土粘模面积为基础&#xff0c;根据实际情况提…