通过 Linux 命令行编程硬件

由于物联网 (IoT) 的兴起,硬件编程变得越来越普遍。RT-Thread 允许您通过 FinSH 从 Linux 命令行连接设备。
130 位读者喜欢这篇文章。
Command line prompt

Opensource.com

RT-Thread 是一个开源的 实时操作系统,用于物联网 (IoT) 设备编程。FinSH 是 RT-Thread 的命令行组件,它提供了一组操作界面,使用户能够从命令行连接设备。它主要用于调试或查看系统信息。

通常,开发调试使用硬件调试器和 printf 日志显示。然而,在某些情况下,这两种方法不是很有效,因为它与正在运行的内容是抽象的,并且可能难以解析。但是,RT-Thread 是一个多线程系统,当您想知道正在运行的线程的状态或手动控制系统的当前状态时,它很有帮助。由于它是多线程的,因此您能够拥有一个交互式 shell,因此您可以输入命令、直接在设备上调用函数以获取所需的信息,或控制程序的行为。如果您只习惯于 Linux 或 BSD 等现代操作系统,这可能看起来很普通,但对于硬件黑客来说,这是一种极大的奢侈,并且与直接将串行电缆连接到板上以瞥见错误相比,这是一个巨大的进步。

FinSH 有两种模式

  • C 语言解释器模式,称为 c-style
  • 传统的命令行模式,称为 msh(模块 shell)

在 C 语言解释模式下,FinSH 可以解析执行大多数 C 语言的表达式,并使用函数调用访问系统上的函数和全局变量。它还可以从命令行创建变量。

msh 模式下,FinSH 的操作方式与 Bash 等传统 shell 类似。

GNU 命令标准

当我们开发 FinSH 时,我们了解到,在编写命令行应用程序之前,您需要熟悉 GNU 命令行标准。这个标准实践框架有助于为界面带来熟悉感,这有助于开发人员在使用它时感到舒适和高效。

一个完整的 GNU 命令由四个主要部分组成

  1. 命令名称(可执行文件): 命令行程序的名称
  2. 子命令: 命令程序的子函数名称
  3. 选项: 子命令函数的配置选项
  4. 参数: 子命令函数配置选项的相应参数

您可以在任何命令中看到这一点。以 Git 为例

git reset --hard HEAD~1

分解如下

可执行命令是 git,子命令是 reset,使用的选项是 --head,参数是 HEAD~1

另一个例子

systemctl enable --now firewalld

可执行命令是 systemctl,子命令是 enable,选项是 --now,参数是 firewalld

想象一下,您想使用 RT-Thread 编写一个符合 GNU 标准的命令行程序。FinSH 拥有您所需的一切,并且将按预期运行您的代码。更好的是,您可以依赖这种合规性,以便您可以自信地移植您最喜欢的 Linux 程序。

编写一个优雅的命令行程序

这是一个 RT-Thread 运行 RT-Thread 开发人员每天都使用的命令的示例。

usage: env.py package [-h] [--force-update] [--update] [--list] [--wizard]
                      [--upgrade] [--printenv]

optional arguments:
  -h, --help      show this help message and exit
  --force-update  force update and clean packages, install or remove the
                  packages by your settings in menuconfig
  --update        update packages, install or remove the packages by your
                  settings in menuconfig
  --list          list target packages
  --wizard        create a new package with wizard
  --upgrade       upgrade local packages list and ENV scripts from git repo
  --printenv      print environmental variables to check

正如您所见,它看起来很熟悉,并且行为方式与您可能已经在 Linux 或 BSD 上运行的大多数 POSIX 应用程序类似。当使用不正确或不充分的语法时,会提供帮助,支持长选项和短选项,并且通用用户界面对于任何使用过 Unix 终端的人来说都很熟悉。

选项的种类

选项有很多不同的种类,可以按长度分为两大类

  1. 短选项: 由一个连字符加上一个字母组成,例如 pkgs -h 中的 -h 选项
  2. 长选项: 由两个连字符加上单词或字母组成,例如 scons- --target-mdk5 中的 --target 选项

您可以将这些选项分为三类,具体取决于它们是否带有参数

  1. 无参数: 选项后面不能跟参数
  2. 必须包含参数: 选项后面必须跟参数
  3. 参数可选: 允许选项后跟参数,但不是必需的

正如您对大多数 Linux 命令所期望的那样,FinSH 选项解析非常灵活。它可以根据空格或等号作为分隔符来区分选项和参数,或者只是提取选项本身并假设随后的任何内容都是参数(换句话说,根本没有分隔符)

  • wavplay -v 50
  • wavplay -v50
  • wavplay --vol=50

使用 optparse

如果您曾经编写过命令行应用程序,您可能知道通常有一个库或模块供您选择的语言调用 optparse。它提供给程序员,以便可以将作为命令一部分输入的选项(例如 -v--verbose解析为与命令的其余部分相关的。它可以帮助您的代码区分选项与子命令或参数。

为 FinSH 编写命令时,optparse 包期望以下格式

MSH_CMD_EXPORT_ALIAS(pkgs, pkgs, this is test cmd.);

您可以使用长格式或短格式或两者都实现选项。例如

static struct optparse_long long_opts[] =
{
    {"help"        , 'h', OPTPARSE_NONE}, // Long command: help, corresponding to short command h, without arguments.
    {"force-update",  0 , OPTPARSE_NONE}, // Long comman: force-update, without arguments
    {"update"      ,  0 , OPTPARSE_NONE},
    {"list"        ,  0 , OPTPARSE_NONE},
    {"wizard"      ,  0 , OPTPARSE_NONE},
    {"upgrade"     ,  0 , OPTPARSE_NONE},
    {"printenv"    ,  0 , OPTPARSE_NONE},
    { NULL         ,  0 , OPTPARSE_NONE}
};

创建选项后,编写每个选项及其参数的命令和说明

static void usage(void)
{
    rt_kprintf("usage: env.py package [-h] [--force-update] [--update] [--list] [--wizard]\n");
    rt_kprintf("                      [--upgrade] [--printenv]\n\n");
    rt_kprintf("optional arguments:\n");
    rt_kprintf("  -h, --help      show this help message and exit\n");
    rt_kprintf("  --force-update  force update and clean packages, install or remove the\n");
    rt_kprintf("                  packages by your settings in menuconfig\n");
    rt_kprintf("  --update        update packages, install or remove the packages by your\n");
    rt_kprintf("                  settings in menuconfig\n");
    rt_kprintf("  --list          list target packages\n");
    rt_kprintf("  --wizard        create a new package with wizard\n");
    rt_kprintf("  --upgrade       upgrade local packages list and ENV scripts from git repo\n");
    rt_kprintf("  --printenv      print environmental variables to check\n");
}

下一步是解析。虽然您还不能实现其功能,但解析代码的框架是相同的

int pkgs(int argc, char **argv)
{
    int ch;
    int option_index;
    struct optparse options;

    if(argc == 1)
    {
        usage();
        return RT_EOK;
    }

    optparse_init(&options, argv);
    while((ch = optparse_long(&options, long_opts, &option_index)) != -1)
    {
        ch = ch;

        rt_kprintf("\n");
        rt_kprintf("optopt = %c\n", options.optopt);
        rt_kprintf("optarg = %s\n", options.optarg);
        rt_kprintf("optind = %d\n", options.optind);
        rt_kprintf("option_index = %d\n", option_index);
    }
    rt_kprintf("\n");

    return RT_EOK;
}

这是函数头文件

#include "optparse.h"
#include "finsh.h"

然后,编译并下载到设备上。

硬件黑客

硬件编程可能看起来令人生畏,但随着物联网的兴起,它变得越来越普遍。并非所有东西都可以在 Raspberry Pi 上运行,或者应该在 Raspberry Pi 上运行,但借助 RT-Thread,得益于 FinSH,您可以保持熟悉的 Linux 感觉。

如果您对在裸机上编码感到好奇,请尝试一下 RT-Thread。

接下来阅读什么
Avatar
我喜欢我的隐私。

1 条评论

一篇好文章 - 感谢提供有用的信息

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.