当前位置: 首页 > article >正文

【嵌入式】开源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.kler.cn/news/147916.html

相关文章:

  • 贪心 376. 摆动序列
  • 顶象s_v3滑块
  • 设计师不可错过的免费素材网站,快快收藏!
  • 数据结构与算法之美代码:二分查找2
  • 分享几个可以免费使用GPT的网站
  • docker常见问题汇总
  • 建筑木模板厂家批发
  • Linux fork笔试练习题
  • C++STL——string类详解及其模拟实现
  • 【深度学习】学习率及多种选择策略
  • 前端学习网站推荐
  • c/c++ header_only 头文件实现的关键点
  • Spring加载Bean的多种方式
  • 红旗Asianux Server Linux V8 安装万里数据库(GreatSQL)
  • Spring Cloud,注册中心,配置中心,原理详解
  • 社区新零售:重塑零售业的全新模式
  • 使用Python+Redis实现文章投票网站后端功能
  • 【文献阅读笔记】关于GANomaly的异常检测方法
  • 【开源威胁情报挖掘1】引言 + 开源威胁情报挖掘框架 + 开源威胁情报采集与识别提取
  • C#,《小白学程序》第十九课:随机数(Random)第六,随机生成任意长度的大数(BigInteger)
  • PTA:百钱买百鸡 - C/C++ 数组及字符串
  • Vue组件的自定义事件$emit
  • ArcGIS10.x系列 Python工具箱教程
  • TypeScript和JavaScript有什么不同
  • 实战Flask+BootstrapTable最实用服务端分页查询动态表头及数据(ajax方式)
  • 群晖NAS配置之自有服务器ngrok实现内网穿透
  • bluez inquiry 流程梳理--从代码层面理解bluez架构
  • opencv-医学图像预处理
  • LeetCode算法题解(动态规划)|LeetCode198. 打家劫舍、LeetCode213. 打家劫舍 II、LeetCode337. 打家劫舍 III
  • 小程序中的大道理--综述