查看: 5113|回复: 2

清风徐来——Zephyr实战篇(5)之Shell

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3298

    主题

    6545

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32003
    最后登录
    2024-4-9
    发表于 2021-11-4 09:37:32 | 显示全部楼层 |阅读模式
    清风徐来——Zephyr实战篇(5)之Shell
    什么是Shell呢?熟悉Linux的朋友们可能并不陌生,当然,在Windows上也是可以看到他的身影的。
    先摘一段来自于某库的介绍:Unix shell,通常被称作“命令行”,为Unix和类Unix操作系统提供了传统的用户界面。用户通过输入shell所执行的命令,引导计算机的操作。在微软Windows操作系统平台,类似程序是command.com,或者基于Windows NT内核操作系统的cmd.exe。

    下面,让我们看看Zephyr中的Shell有哪些不同:
    Unix-like shell
    支持创建用户自定义指令集
    能够和ZephyrOS中的Logging系统相互配合
    支持Tab键的命令补齐
    多种内建指令:clear, shell, colors, echo, history, resize
    可通过Kconfig进行配置以优化内存占用
    支持通配符*和?
    支持显示历史

    当然,作为Shell,需要能够将其传输到我们的终端上进行显示,那么在Zephyr的2.6.0版本中,所支持的传输通信方式包括:Segger RTT, SMP, Telnet,UART, USB, DUMMY。

    了解了Zephyr中Shell的基本概念,我们来看看怎么手动创建一个自定义Shell指令,准备工作可以说非常简单,我们只需要在需要创建shell指令的文件中包含shell.h,即可通过其中提供的宏创建指令。

    1. 静态指令 实现方法非常简单,首先我们定义一个叫做cmd_toggle的函数,这就是我们在控制台调用指令时,所执行的函数,随后通过宏SHELL_CMD_REGISTER进行指令的创建和注册,例如:
    1. static int cmd_toggle(const struct shell *shell, size_t argc, char **argv)
    2. {
    3.     ARG_UNUSED(argc);
    4.     ARG_UNUSED(argv);
    5.     led_on = !led_on;
    6.     gpio_pin_set(dev, PIN, (int)led_on);
    7.     return 0;
    8. }
    9. /* Creating root (level 0) command "toggle" */
    10. SHELL_CMD_REGISTER(toggle, NULL, "Toggle LED command", cmd_toggle);
    复制代码
    这样一来我们就定义了一个叫做toggle的指令,可以直接在控制台中执行toggle调用。
    这个宏的原型是SHELL_CMD_REGISTER(syntax, subcmd, help, handler),我们来详细介绍下这个宏中的参数:
    syntax:指令名称
    subcmd:是否支持多级指令,即子指令
    help:指令帮助
    handler:指向实际的处理函数
    既然提到了子指令,我们来看看如何创建一个子指令:
    1. /* Creating subcommands (level 1 command) array for command "demo". */
    2. SHELL_STATIC_SUBCMD_SET_CREATE(sub_demo,
    3.     SHELL_CMD(params, NULL, "Print params command.",
    4.                                     cmd_demo_params),
    5.     SHELL_CMD(ping,   NULL, "Ping command.", cmd_demo_ping),
    6.     SHELL_SUBCMD_SET_END
    7. );
    8. /* Creating root (level 0) command "demo" */
    9. SHELL_CMD_REGISTER(demo, &sub_demo, "Demo commands", NULL);
    复制代码
    对比刚才的定义方式,在SHELL_CMD_REGISTER中我们将handler置为了NULL,然后将&sub_demo作为第二个参数,subcmd传入。

    我们再来详细看看这个sub_cmd是怎么生成的。

    同样我们还是依赖于一个shell.h中的宏SHELL_STATIC_SUBCMD_SET_CREATE,其原型是,SHELL_STATIC_SUBCMD_SET_CREATE(name, ...),这里要注意了,第二个参数代表他是一个可变参数,代表了一个指令列表,通过传入SHELL_SUBCMD_SET_END来表示指令集定义完毕,name是所定义的子指令的名字。

    而我们的子指令是通过宏SHELL_CMD进行创建的,其原型是SHELL_CMD(_syntax, _subcmd, _help, _handler),可以发现其与SHELL_CMD_REGISTER是类似的,但是功能就不一样了,这个宏仅仅是代表一个子指令的初始化,不会被注册进Shell的指令列表中。

    这样一来,我们就定义了一个叫做demo的根指令,其中包含了一个sub_demo指向的子指令集,其中包含了两条指令,分别是params和ping,调用方式略有不同,需要通过在控制台指令demo params或是demo ping进行调用。

    2. 字典指令 顾名思义,就是我们可以定义个能够使用字符串+数值的组合形式的指令:
    1. static int gain_cmd_handler(const struct shell *shell,
    2.                             size_t argc, char **argv, void *data)
    3. {
    4.     int gain;

    5.     /* data is a value corresponding to called command syntax */
    6.     gain = (int)data;
    7.     adc_set_gain(gain);

    8.     shell_print(shell, "ADC gain set to: %s\n"
    9.                        "Value send to ADC driver: %d",
    10.                        argv[0],
    11.                        gain);

    12.     return 0;
    13. }

    14. SHELL_SUBCMD_DICT_SET_CREATE(sub_gain, gain_cmd_handler,
    15.         (gain_1, 1), (gain_2, 2), (gain_1_2, 3), (gain_1_4, 4)
    16. );
    17. SHELL_CMD_REGISTER(gain, &sub_gain, "Set ADC gain", NULL);
    复制代码
    其展示效果如下:
    13.png
    即当我们将某个定义好的字典键(字符串)作为指令执行时,Shell系统在指令解析时,会将其对应的数指作为参数传入。

    我们来分析下它的实现,命令注册部分依旧是通过调用SHELL_CMD_REGISTER实现,不同的是,我们调用了一个新的宏SHELL_SUBCMD_DICT_SET_CREATE,来进行字典指令集的注册,其原型是:SHELL_SUBCMD_DICT_SET_CREATE(_name, _handler, ...),前两个参数我们已经见过多次了,分别代表指令名和处理函数,而第三个参数依旧是个可变参数,代表字典对,其格式为(指令名,值)。

    3. 动态指令 和静态指令不同的是,我们可以在程序运行期间,动态的添加指令。

    例如,当我们使用scan指令搜索可用的蓝牙设备以通过connect指令以进行连接。
    14.png
    关于动态指令的添加,小编在这里就不扩展讲了,感兴趣的朋友门可以参考。

    4. 参数获取 讲完如何创建一条Shell指令,我们来看看如何获取指令参数。


    Shell支持通过argv的下标来访问相应参数,正的索引表示其指令参数,0表示指令本身,同时我们还可以通过负数索引来获取其父指令名称,例如刚才我们定义的子指令,demo params 10,那么argv[1] = 10, argv[0] = params,argv[-1] = demo:
    1. static int cmd_handler(const struct shell *shell, size_t argc, char **argv)
    2. {
    3.     ARG_UNUSED(argc);

    4.     /* If it is a subcommand handler parent command syntax
    5.      * can be found using argv[-1].
    6.      */
    7.     shell_print(shell, "This command has a parent command: %s",
    8.                       argv[-1]);

    9.     /* Print this command syntax */
    10.     shell_print(shell, "This command syntax is: %s", argv[0]);

    11.     /* Print first argument */
    12.     shell_print(shell, "%s", argv[1]);

    13.     return 0;
    14. }
    复制代码
    至此,关于Zephyr中的Shell系统小编就介绍完了,相信大家已经跃跃欲试想要去创建一个属于自己的专属指令了。










    签到签到
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2024-4-9 17:01
  • 签到天数: 1478 天

    [LV.10]以坛为家III

    203

    主题

    2万

    帖子

    64

    超级版主

    Rank: 8Rank: 8

    积分
    92609
    最后登录
    2024-4-9
    发表于 2021-11-5 10:04:30 | 显示全部楼层
    去官网看了一下,这个系统居然可以支持M0的板子 TS1 - 副本 (2).jpg TS1 - 副本 (3).jpg TS1 - 副本 (4).jpg TS1 - 副本 (5).jpg TS1 - 副本 (10).jpg TS1 - 副本 (9).jpg TS1 - 副本 (6).jpg TS1 - 副本 (7).jpg TS1 - 副本 (8).jpg TS1.jpg

    搞得我有点想尝试一下了~~

    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3298

    主题

    6545

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32003
    最后登录
    2024-4-9
     楼主| 发表于 2021-11-5 10:10:27 | 显示全部楼层
    stm1024 发表于 2021-11-5 10:04
    去官网看了一下,这个系统居然可以支持M0的板子

    搞得我有点想尝试一下了~~

    还在等什么,搞起
    签到签到
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-20 08:50 , Processed in 0.119961 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表