在线时间4067 小时
UID3441752
注册时间2017-11-21
NXP金币759430
TA的每日心情 | 开心 2024-3-26 15:16 |
---|
签到天数: 266 天 [LV.8]以坛为家I
管理员
- 积分
- 32003
- 最后登录
- 2024-4-9
|
清风徐来——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进行指令的创建和注册,例如:
- static int cmd_toggle(const struct shell *shell, size_t argc, char **argv)
- {
- ARG_UNUSED(argc);
- ARG_UNUSED(argv);
- led_on = !led_on;
- gpio_pin_set(dev, PIN, (int)led_on);
- return 0;
- }
- /* Creating root (level 0) command "toggle" */
- SHELL_CMD_REGISTER(toggle, NULL, "Toggle LED command", cmd_toggle);
复制代码 这样一来我们就定义了一个叫做toggle的指令,可以直接在控制台中执行toggle调用。
这个宏的原型是SHELL_CMD_REGISTER(syntax, subcmd, help, handler),我们来详细介绍下这个宏中的参数:
syntax:指令名称
subcmd:是否支持多级指令,即子指令
help:指令帮助
handler:指向实际的处理函数
既然提到了子指令,我们来看看如何创建一个子指令:
- /* Creating subcommands (level 1 command) array for command "demo". */
- SHELL_STATIC_SUBCMD_SET_CREATE(sub_demo,
- SHELL_CMD(params, NULL, "Print params command.",
- cmd_demo_params),
- SHELL_CMD(ping, NULL, "Ping command.", cmd_demo_ping),
- SHELL_SUBCMD_SET_END
- );
- /* Creating root (level 0) command "demo" */
- 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. 字典指令 顾名思义,就是我们可以定义个能够使用字符串+数值的组合形式的指令:
- static int gain_cmd_handler(const struct shell *shell,
- size_t argc, char **argv, void *data)
- {
- int gain;
- /* data is a value corresponding to called command syntax */
- gain = (int)data;
- adc_set_gain(gain);
- shell_print(shell, "ADC gain set to: %s\n"
- "Value send to ADC driver: %d",
- argv[0],
- gain);
- return 0;
- }
- SHELL_SUBCMD_DICT_SET_CREATE(sub_gain, gain_cmd_handler,
- (gain_1, 1), (gain_2, 2), (gain_1_2, 3), (gain_1_4, 4)
- );
- SHELL_CMD_REGISTER(gain, &sub_gain, "Set ADC gain", NULL);
复制代码 其展示效果如下:
即当我们将某个定义好的字典键(字符串)作为指令执行时,Shell系统在指令解析时,会将其对应的数指作为参数传入。
我们来分析下它的实现,命令注册部分依旧是通过调用SHELL_CMD_REGISTER实现,不同的是,我们调用了一个新的宏SHELL_SUBCMD_DICT_SET_CREATE,来进行字典指令集的注册,其原型是:SHELL_SUBCMD_DICT_SET_CREATE(_name, _handler, ...),前两个参数我们已经见过多次了,分别代表指令名和处理函数,而第三个参数依旧是个可变参数,代表字典对,其格式为(指令名,值)。
3. 动态指令 和静态指令不同的是,我们可以在程序运行期间,动态的添加指令。
例如,当我们使用scan指令搜索可用的蓝牙设备以通过connect指令以进行连接。
关于动态指令的添加,小编在这里就不扩展讲了,感兴趣的朋友门可以参考。
4. 参数获取 讲完如何创建一条Shell指令,我们来看看如何获取指令参数。
Shell支持通过argv的下标来访问相应参数,正的索引表示其指令参数,0表示指令本身,同时我们还可以通过负数索引来获取其父指令名称,例如刚才我们定义的子指令,demo params 10,那么argv[1] = 10, argv[0] = params,argv[-1] = demo:
- static int cmd_handler(const struct shell *shell, size_t argc, char **argv)
- {
- ARG_UNUSED(argc);
- /* If it is a subcommand handler parent command syntax
- * can be found using argv[-1].
- */
- shell_print(shell, "This command has a parent command: %s",
- argv[-1]);
- /* Print this command syntax */
- shell_print(shell, "This command syntax is: %s", argv[0]);
- /* Print first argument */
- shell_print(shell, "%s", argv[1]);
- return 0;
- }
复制代码 至此,关于Zephyr中的Shell系统小编就介绍完了,相信大家已经跃跃欲试想要去创建一个属于自己的专属指令了。
|
|