はじめに
2年ほど前からRTOSの Zephyr に注目してます。注目しているだけで何もしてなかったのでついにLチカをしてみました。例によっていくつかハマりどころがありましたので記事にしておきます。
- 母艦:Macbook Pro M1/macOS Tahoe 26.4.1
- ターゲットボード:Nordic Semiconductor nRF54L15-DK
Zephyr RTOS と Nordic ボードに注目した理由
IoT 向けの低消費電力無線通信規格 IEEE802.15.4 上の IPv6 スタックに 6loWPAN があります。IoT 関係はいまだ private IPv4 アドレスで済ませてることが多いようですが、どっかで IPv6 に移行することになりますし、それの準備もしておきたいところです。
加えてこのところ IoT 用の Pub/Sub 通信として Zenoh に注目しているところです。Zenoh は Rust で書かれていますが、組込み用として C による実装 Zenoh pico があります。これ見ると、RTOS では Zephyr が 6loWPAN に対応してます。
そこで Zephyr がいごくボードでかつ無線系に力を入れているところはどこかなと探すと Nordic Semiconductor が力を入れているようです。今回は nRF54L15-DK を使う機会があってこれでチャレンジすることにしました。
環境を整える
公式ガイド Getting Started Guide を見ながら進めます。
以下では公式ガイドのまま ~/zephyrproject ディレクトリ下で作業する前提です。
母艦OSの選択肢として Ubuntu, macOS, Windows があるので macOS を選択します。なお CPU は Apple Silicon だけが対応してます。Intel X86-64 には対応していません。
以下は、公式ドキュメントの指示を掲載しているだけです。全部やり切るのは結構時間かかります。公式ガイドのまま進めて問題ありませんでした。
-
Install dependencies
- Homebrew のインストール
- Homebrew のインストール後の設定
- 必要なコマンドの brew によるインストール
- python/pip への実行パスの設定
-
Get Zephyr and install Python dependencies
- Python の仮想実行環境 venv を作成
- 仮想環境を有効化
- Zephyr の開発ツール
westをインストール(以降 west が大活躍) - Zephyr のソースコードを取得
- CMake の自動化をする
- Python の依存関係を解決
ここで「pip の新しいバージョンがあるよ:26.1 -> 26.1.1」とか言って来たので、指示される通り pip install --upgrade pip で更新しました。
以上で、環境準備は完了です。
アプリケーションを作って・焼いて実行する
この先は、つまづきながら進めました。一通り終わった後にターゲット基板 nRF55L15-DK の情報 に当たっておけばよかったことに気づきました。良い子は先に読んでから実装を進めましょう。躓いたのは以下の3点です。
- コンパイルのオプションで指定するターゲット基板の指定
- ターゲット基板への母艦からの書込み
- Lチカの動作確認
ターゲット基板の指定方法
公式ドキュメントのBuild the Blinky Sample に従って進めます。
まず、作業ディレクトリ ~/zephyrproject/zephyr に移動します。そして west コマンドを使ってターゲットプログラムのバイナリを作成します。
バイナリを作成するには west build -p always -b <your-board-name> samples/basic/blinky を実行します。この <your-board-name> には焼こうとしている基板の種類を入れるのですが、何を書けばよいかで戸惑いました。
当てずっぽうで nrf54l15 と入れるとこうなりました。
% west build -p always -b nrf54l15 samples/basic/blinky
# ... snip ...
No board named 'nrf54l15' found. Did you mean:
nrf54l15dk
nrf54l15tag
nrf54l15bsim
ならばと dk まで指定してやるとこうなります(余分な空行削除)。
% west build -p always -b nrf54l15dk samples/basic/blinky
# ... snip ...
Board qualifiers `` for board `nrf54l15dk` not found. Please specify a valid board target.
Valid board targets for nrf54l15dk are:
nrf54l15dk/nrf54l05/cpuapp
nrf54l15dk/nrf54l05/cpuflpr
nrf54l15dk/nrf54l10/cpuapp
nrf54l15dk/nrf54l10/cpuapp/ns
nrf54l15dk/nrf54l10/cpuflpr
nrf54l15dk/nrf54l15/cpuapp
nrf54l15dk/nrf54l15/cpuapp/ns
nrf54l15dk/nrf54l15/cpuflpr
nrf54l15dk/nrf54l15/cpuflpr/xip
これで nrf54l15dk/nrf54l15 のどれなのか迷いますが、結論から言うと nrf54l15dk/nrf54l15/cpuapp で出来ました。
west build -p always -b nrf54l15dk/nrf54l15/cpuapp samples/basic/blinky
他のオプションについて Programming and Debugging には cpuflpr を使う場合の記載があります。
ターゲット基板への書込み
ここで一番時間を溶かしました。
公式ドキュメントの Flash the Sample に従い出来たバイナリを焼こうとするとこんなエラーが。
% west flash
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner nrfutil
-- runners.nrfutil: reset after flashing requested
FATAL ERROR: required program nrfutil not found; install it or add its location to PATH
nrfutil なんて入れた記憶がありません。
% nrfutil
bash: nrfutil: command not found
これ、Nordic Semiconductor 社の出しているツールです。
公式ドキュメントの Programming and Debugging を読むと、この他に jlink というのもあります。
nrfutil のインストール
nrfutil は3通りのインストール方法がありました。
- Homebrew で入れる
- pip で入れる
- メーカの公式ホームページからダウンロードする
nrfutil を brew でインストールする
まず、brew で入れてみました。これ、MacOS が認識してないコマンドになるのでそのままでは実行させてくれません。「システム設定」の「セキュリティ」で許可してやる必要があります。が、それでも私の環境ではうまく動きませんでした。
west flash
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner nrfutil
-- runners.nrfutil: reset after flashing requested
# ここから先に進まない
nrfutil を pip でインストールする
pip install nrfutil でインストールを試すと、途中で赤い ERROR 表示が出ます。それでもコマンド自体は出来ました。
% west flash
# おぞましい量の大量のエラー出力
nrfutil のインストール時に Python の環境に悪さしたようで、west コマンドも同様の大量のエラー出力を吐くようになりました。
pip uninstall nrfutil と pip uninstall west そして pip install west したところなんとか戻ったようでした。
nrfutil を公式サイトからゲットする
ターゲット基板のメーカの nRF54L15 DK ページに nRF Util のページがありました。ここから nrfutilのインストーラ MacOS (Appleシリコン)版 をダウンロードできます。インストーラの指示に従ってインストールすると出来上がりです。
ただしまだ不十分でまだやることがあります。インストールしたてではサブコマンドとして core しか入ってません。
% nrfutil search
Command Installed Latest Status
91 0.5.0 Not installed
ble-sniffer 0.19.0 Not installed
completion 1.5.0 Not installed
core 8.2.0 8.2.0 Installed
device 2.18.1 Not installed
mcu-manager 0.10.1 Not installed
npm 0.3.1 Not installed
nrf5sdk-tools 1.1.0 Not installed
sdk-manager 1.12.0 Not installed
suit 0.9.0 Not installed
toolchain-manager 0.15.0 Not installed
trace 4.7.1 Not installed
Found 12 installable command(s)
これ device というサブコマンドを nrfutil install device でインストールする必要があります。
nrfutil search
Command Installed Latest Status
91 0.5.0 Not installed
ble-sniffer 0.19.0 Not installed
completion 1.5.0 Not installed
core 8.2.0 8.2.0 Installed
device 2.18.1 2.18.1 Installed
mcu-manager 0.10.1 Not installed
npm 0.3.1 Not installed
nrf5sdk-tools 1.1.0 Not installed
sdk-manager 1.12.0 Not installed
suit 0.9.0 Not installed
toolchain-manager 0.15.0 Not installed
trace 4.7.1 Not installed
Found 12 installable command(s)
ようやく nrfutil の準備ができましたので、フラッシュに書いてみます。
% west flash
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner nrfutil
-- runners.nrfutil: reset after flashing requested
FATAL ERROR: Unable to find a board; is the board connected?
基板が見つからないと言ってます。ここでかなり途方に暮れました。
jlink をインストールする
nrfutil がダメならもう一つの手段 jlink を使うしかありません。
Segger社の公式サイトにダウンロード用ページ
J-Link / J-Trace Downloadsがあるのでダウンロードしてインストールしました。
ついにターゲット基板に焼く
この後は nrfutil と jlink のどちらでもバイナリを基板に焼くことが出来ました。
以下で west flash は nrfutil を使います。明示的に west flash -r nrfutil とするのと同じです。
west flash
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner nrfutil
-- runners.nrfutil: reset after flashing requested
Using board 001057770738
-- runners.nrfutil: Flashing file: /Users/yu/zephyrproject/zephyr/build/zephyr/zephyr.hex
-- runners.nrfutil: Connecting to probe
-- runners.nrfutil: Programming image
-- runners.nrfutil: Verifying image
-- runners.nrfutil: Reset
-- runners.nrfutil: Board(s) with serial number(s) 1057770738 flashed successfully.
以下は jlink を使った場合です。
west flash -r jlink
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner jlink
-- runners.jlink: reset after flashing requested
-- runners.jlink: JLink version: 9.40
-- runners.jlink: Flashing file: /Users/yu/zephyrproject/zephyr/build/zephyr/zephyr.hex
こちらは基板の reset はしてくれてないようです。
MacがNordicのボードをUSBクライアントとして認識してるのか
上の作業の途中で母艦がターゲット基板を認識してなさそうだなとは思ってました。
Nordic のボードに限って言うと nrfutil で見ることができます。
% nrfutil device list
1057770738
Product J-Link
Board version PCA10156
Ports /dev/tty.usbmodem0010577707381, vcom: 0
/dev/tty.usbmodem0010577707383, vcom: 1
Traits boardController, devkit, jlink, seggerUsb, serialPorts, usb
Supported devices found: 1
ioreg -p IOUSB -l を実行してみましたが、USBのクライアントがいくつか接続されているのは見えますが、Nordic のボードがそれに含まれてるかはわかりません。
Lチカの動作確認
せっかくボードに焼けるようになったのに、基板のLEDの点灯のしかたが全く変わりません。
ボード自体は、開封直後に電源入れた状態で LD1? なる LED が点灯です。そしてLED0が1秒程度の間隔で点滅です。
west flash で焼いて west reset でリセットしても何も変わりません。鬼ガッカリです。
そこで実のところLチカのサンプルプログラムは何をしているのかとソースコードを見てみました。
# ... snip ...
#define SLEEP_TIME_MS 1000
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)
#... snip ...
これ led0 を1000msごとにon-offのトグルをしてます。なんと、工場出荷時と同じLEDをブリンクしてたのでした。そりゃわからない。達成感のないLチカになってしまいました。
Lグルをやってみる
これでは面白くないので自分でプログラムを書いてみることにしました。nRF54L15-DKはユーザが使えるボタンとLEDとがそれぞれ4個あります。LED4個を全部Lチカします。4つが 2x2 で並んでいるのでぐるぐると回って点くようにします。
まず、もともとのは LED0 しか有効になってないので4つとも有効にします。
ボード上のLEDがどのGPIOにつながってるかは Nordic の公式サイトの Buttons and LEDs ページにあります。これに準拠した DTSi ファイルが ~/zephyrproject/zephyr/boards/nordic/nrf54l15dk/nrf54l15dk_common.dtsi にありますので、それを参考にデバイスの overlay ファイルを作ります。
/ {
aliases {
led0 = &led0;
led1 = &led1;
led2 = &led2;
led3 = &led3;
};
leds {
compatible = "gpio-leds";
led0: led_0 {
gpios = <&gpio2 9 GPIO_ACTIVE_HIGH>;
label = "Green LED 0";
};
led1: led_1 {
gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
label = "Green LED 1";
};
led2: led_2 {
gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>;
label = "Green LED 2";
};
led3: led_3 {
gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
label = "Green LED 3";
};
};
};
あとはCでプログラムを書きます。GPIOを制御する関数は以下を使います。
以下はちょっとベタですが。
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS 200/*0*/
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)
#define LED1_NODE DT_ALIAS(led1)
#define LED2_NODE DT_ALIAS(led2)
#define LED3_NODE DT_ALIAS(led3)
/*
* A build error on this line means your board is unsupported.
* See the sample documentation for information on how to fix this.
*/
static const struct gpio_dt_spec led_0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec led_1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
static const struct gpio_dt_spec led_2 = GPIO_DT_SPEC_GET(LED2_NODE, gpios);
static const struct gpio_dt_spec led_3 = GPIO_DT_SPEC_GET(LED3_NODE, gpios);
int main(void)
{
int ret;
bool led_state = true;
if (!gpio_is_ready_dt(&led_0)) { return 0; }
if (!gpio_is_ready_dt(&led_1)) { return 0; }
if (!gpio_is_ready_dt(&led_2)) { return 0; }
if (!gpio_is_ready_dt(&led_3)) { return 0; }
ret = gpio_pin_configure_dt(&led_0, GPIO_OUTPUT_ACTIVE);
if (ret < 0) { return 0; }
ret = gpio_pin_configure_dt(&led_1, GPIO_OUTPUT_ACTIVE);
if (ret < 0) { return 0; }
ret = gpio_pin_configure_dt(&led_2, GPIO_OUTPUT_ACTIVE);
if (ret < 0) { return 0; }
ret = gpio_pin_configure_dt(&led_3, GPIO_OUTPUT_ACTIVE);
if (ret < 0) { return 0; }
while (true) {
ret = gpio_pin_set_dt(&led_2, false);
if (ret < 0) { return 0; }
ret = gpio_pin_set_dt(&led_0, true);
if (ret < 0) { return 0; }
k_msleep(SLEEP_TIME_MS);
ret = gpio_pin_set_dt(&led_0, false);
if (ret < 0) { return 0; }
ret = gpio_pin_set_dt(&led_1, true);
if (ret < 0) { return 0; }
k_msleep(SLEEP_TIME_MS);
ret = gpio_pin_set_dt(&led_1, false);
if (ret < 0) { return 0; }
ret = gpio_pin_set_dt(&led_3, true);
if (ret < 0) { return 0; }
k_msleep(SLEEP_TIME_MS);
ret = gpio_pin_set_dt(&led_3, false);
if (ret < 0) { return 0; }
ret = gpio_pin_set_dt(&led_2, true);
if (ret < 0) { return 0; }
k_msleep(SLEEP_TIME_MS);
}
return 0;
}
あとは
west build -p always -b nrf54l15dk/nrf54l15/cpuapp samples/basic/blinky
west flash
するとこんな風になります。Lグルと命名しました。
まとめ
Zephyr over nRF54L15-DK で最初の一歩を踏み出せました。ついでに device overly もかじって、プログラムも書くことが出来ました。もう少しデバイスをいじれるようにして、次に Zenoh over Zephyr で遊んでみたいと思います。
