使用 RT-Thread 的 FinSH 對硬體進行編程
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 命令主要由四個部分組成。
- 命令名(可執行文件):命令行程序的名稱;
- 子命令:命令程序的子函數名稱。
- 選項:子命令函數的配置選項。
- 參數:子命令函數配置選項的相應參數。
你可以在任何命令中看到這一點。以 Git 為例:
git reset --hard HEAD~1
這一點可以分解為:
![GNU command line standards](/data/attachment/album/202009/29/233147h3c5qxp3x58qccaz.png "GNU command line standards")
可執行的命令是 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 終端的人來說都是熟悉的。
選項種類
選項的種類很多,按長短可分為兩大類。
- 短選項:由一個連字元加一個字母組成,如
pkgs -h
中的-h
選項。 - 長選項:由兩個連字元加上單詞或字母組成,例如,
scons- --target-mdk5
中的--target
選項。
你可以把這些選項分為三類,由它們是否有參數來決定。
- 沒有參數:該選項後面不能有參數。
- 參數必選:選項後面必須有參數。
- 參數可選:選項後可以有參數,但不是必需的。
正如你對大多數 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]nn");
rt_kprintf("optional arguments:n");
rt_kprintf(" -h, --help show this help message and exitn");
rt_kprintf(" --force-update force update and clean packages, install or remove then");
rt_kprintf(" packages by your settings in menuconfign");
rt_kprintf(" --update update packages, install or remove the packages by yourn");
rt_kprintf(" settings in menuconfign");
rt_kprintf(" --list list target packagesn");
rt_kprintf(" --wizard create a new package with wizardn");
rt_kprintf(" --upgrade upgrade local packages list and ENV scripts from git repon");
rt_kprintf(" --printenv print environmental variables to checkn");
}
下一步是解析。雖然你還沒有實現它的功能,但解析後的代碼框架是一樣的:
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 = %cn", options.optopt);
rt_kprintf("optarg = %sn", options.optarg);
rt_kprintf("optind = %dn", options.optind);
rt_kprintf("option_index = %dn", option_index);
}
rt_kprintf("n");
return RT_EOK;
}
這裡是函數頭文件:
#include "optparse.h"
#include "finsh.h"
然後,編譯並下載到設備上。
![Output](/data/attachment/album/202009/29/233154xqmusussoo9vq90s.png "Output")
硬體黑客
對硬體進行編程似乎很嚇人,但隨著物聯網的發展,它變得越來越普遍。並不是所有的東西都可以或者應該在樹莓派上運行,但在 RT-Thread,FinSH 可以讓你保持熟悉的 Linux 感覺。
如果你對在裸機上編碼感到好奇,不妨試試 RT-Thread。
via: https://opensource.com/article/20/9/hardware-command-line
作者:Alan Smithee 選題:lujun9972 譯者:wxy 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive