
这次小编为大家带来的是ZephyrOS系列文章的第六篇,将为大家介绍Shell。
清风徐来前期回顾:
下面,让我们看看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,即可通过其中提供的宏创建指令。
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指令,我们来看看如何获取指令参数。
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系统小编就介绍完了,相信大家已经跃跃欲试想要去创建一个属于自己的专属指令了。
暂无评论哦,快来评论一下吧!

