Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Zephyr RTOS Tutorial & MeetUp @ OSC2025Tokyo/Sp...

soburi
February 21, 2025

Zephyr RTOS Tutorial & MeetUp @ OSC2025Tokyo/Spring 駒澤大学種月館

soburi

February 21, 2025
Tweet

More Decks by soburi

Other Decks in Technology

Transcript

  1. Zephyr RTOS Tutorial & MeetUp 常田 裕士 Open Source Conference

    2025 Tokyo/Spring 2025/2/21 駒澤大学 駒澤キャンパス 種月館 1
  2. 自己紹介  常田 裕士  1978年生まれ  2001年日本大学理工学部物理学科卒業、 同年、株式会社富士通プログラム技研入社、現在富士通に所属 携帯電話、車載機開発を経て現在は組み込みLinuxサポートを担当

     2018年よりZephyrRTOSへのコミットを始める。 現在Renesas RA、RaspberryPi Pico, GD32, LED Stripのメンテナ/副メンテナを担当  掲載記事等  トランジスタ技術 2024年 9月号 (今月号!) オリジナルArduino互換! KiCadプリント基板作り入門  インターフェース 2021年 6月号 第2特集 C/C++でPython拡張 第1部 ハードウェア効率化…C/C++で拡張モジュール作り  https://www.slideshare.net/slideshow/kicad-53622272/53622272 これ見てくれた方も居られるかも? 2
  3. Zephyrの特徴  オープンソースのリアルタイムOS  ArduinoとかESP32など主に「Linux が動かない」領域  軽量 (メモリ/フットプリントが小さい) 

    BluetoothやMatterなど、IoTの分野に強 い  メカの制御にも使える  対応しているSoC、開発ボードが非常に多 い  POSIX対応をはじめとして多機能  OSとしてHALを定義している 4
  4. 誰がどこでZephyrを使ってる? #1  Google Chromebook の電源・ボタン制御  電源回りのハードウェアの インターフェースを統一して 汎用的に使えるようにしている。

     メインのCPUでは動いていない。  システムのスキマを埋めている 6 メインのCPU Zephyrでデバイ スを制御してい る
  5. 誰がどこでZephyrを 使ってる? #4  電子工作  Mbed OSがEOLとなるため、Zephyrへ移 行する。 

    Introducing Arduino cores with ZephyrOS (beta): take your embedded development to the next level https://blog.arduino.cc/2024/12/05/in troducing-arduino-cores-with-zephyros- beta-take-your-embedded- development-to-the-next-level/ 9
  6. ネットワーク(Connectivity)が強い  Bluetooth  Bluetoothの「盟主」のNordic Semiconductorが精力的にコミット  TCP/IP  OSにインテグレートされて「第1級」のサポートとなっている点がLWIPよりも優位

     Matter/OpenThread  スマートホームもBluetooth同様手厚い  ModBus  FA系も通信規格もスコープに入っている。  CANBUS  もちろん車載も 10
  7. westコマンド  Zephyrの総合的なフロントエンドを提供するコマンド  Zephyrの操作はほぼすべてこのコマンド経由で実行する  以前はcmake & ninjaを直接使っていたが、全部west経由に。 

    いまではZephyrの特徴の一つと言えるほどプロセスの中核に居 座っている  GitHubのghコマンドやVueやflutterのコマンドなど、 Web系の文脈から見ると自然な導入に見える  Zephyrは組み込みの文脈だけがルーツとなっていない! 11
  8. DeviceTree  DeviceTreeはZephyrを特徴づける最大の要素  「汎用RTOS」であるため、カーネルの基本機能で強い「色」はついていな い。  Linuxと違って、DeviceTreeを静的にコンパイルして組み込む。  組み込み用途で必要なフットプリント最小化と、

    実装と構成の分離を同時に実現している。  このトレードとして、 大変煩雑なC言語のマクロの記述が必要になる  このpros/consのトレードの判断こそが Zephyrを特徴づけている 12
  9. Raspberry Pi Pico/Pico2  安い。  私がメンテナなので手前味噌ながら。  Raspberry Pi

    Debug Probeも用意する。  対応ボード一覧 https://docs.zephyrproject.org/latest/boards/index.html 15
  10. 環境設定(Windows) #2  WSL USB Manager  https://gitlab.com/alelec/wsl-usb-gui  USBデバイスのWSL環境への

    接続・切断を行う。  コマンドラインで行ってもいいが、 これがあると敷居がかなり低くなる 19
  11. 環境のセットアップ  素直にGetting Started Guideに乗っかってaptの更新 sudo apt update sudo apt

    upgrade sudo apt install --no-install-recommends git cmake ninja-build gperf ¥ ccache dfu-util device-tree-compiler wget ¥ python3-dev python3-pip python3-setuptools python3-tk ¥ python3-wheel xz-utils file ¥ make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1 python3-venv 20
  12. Python仮想環境のセットアップ  これも指示通りに python3 –m venv ~/zephyrproject/.venv # venvを作成 source

    ~/zephyrproject/.venv/bin/activate # アクティベート pip install west # Zephyrの万能ナイフのwestコマンドをインストール west init ~/zephyrproject # zephyrのリポジトリをcloneする cd ~/zephyrproject west update # DL量を減らす場合はmanifestファイルを用意する west zephyr-export # Cmakeの設定 west packages pip –install # pythonのモジュールのインストール 21
  13. Raspberry Pi Pico/Pico2 固有の設定  Pico-SDKをインストールする。  Raspberry Pi Pico/Pico2が新しいので、Zephyrのデバッグ

    ツール(OpenOCD)がまだ対応していない。 cd ~ wget https://raw.githubusercontent.com/raspberrypi/pico- setup/master/pico_setup.sh bash pico_setup.sh 23
  14. サンプルをビルドする  Hello World サンプルは samples/hello_world にある。  以下のコマンドでビルドする (ボード名は環境に合わせる)

    west build -p -b rpi_pico2/rp2350a/m33 samples/hello_world/ -- -DOPENOCD=/usr/local/bin/openocd west build -p -b rpi_pico samples/hello_world/ -- -DOPENOCD=/usr/local/bin/openocd 24
  15. 書き込みのための設定  デバッガをつかえるようにudevのルールを追加  wget https://raw.githubusercontent.com/openocd- org/openocd/refs/heads/master/contrib/60-openocd.rules  sudo cp

    60-openocd.rules /etc/udev/rules.d/  sudo udevadm control --reload-rules  つなぎ直す。  pyocdのプラグインを追加  boards/[使用しているボード]/board.cmake の board_runner_args(pyocd –target=”…”) で指定されているターゲットを確認  pyocd pack install [ターゲット] を実行 25
  16. サンプルをビルドして書き込む  ここで、WSL USB Managerを使って、WSL側に デバイスを接続する  焼き込み & 実行は以下のコマンド

     コンソールで出力を確認 west flash –-runner=pyocd screen /dev/ttyACM0 115200 ST-Link Debugを右ク リック、「Attach to WSL」で接続する 26
  17. 他のサンプルを試してみる  Lチカは samples/basic/blinky west build -p -b rpi_pico2/rp2350a/m33 samples/basic/blinky

    -- -DOPENOCD=/usr/local/bin/openocd west build -p -b rpi_pico samples/basic/blinky -- -DOPENOCD=/usr/local/bin/openocd 27
  18. デバッグする  デバッグもwestコマンドを叩くとコンソールのgdb が立ち上がる  gdbの簡単なコマンド  b: breakpoint を設定、b

    mainのようにしてみる。  c: プログラムの実行を継続  s: ステップ実行  n: 次の行まで実行 west debug 28
  19. まとめ  ZephyrはオープンソースのRTOSで、NucleoやArduino, RaspberryPi Picoのよう なマイコンで使える。対応ボードはとても多い。  IoT向けの通信機能が充実しているが、メカの制御などの従来的な組み込み、 制御の用途にも使える。 

    westというコマンドで統一的な操作が行える。  ビルド、書き込み、デバッグなどの操作はwestコマンドで行える。 他のマイコンでも同じノウハウで扱える。  Arduinoの代替のようなイージーな用途でも十分にメリットがある。 29
  20. Hello World.  samples/hello_world にあるので写経しましょう。  ファイル構成:  CMakeLists.txt: Cmakeの設定ファイル。コンパイル対象の設定に使う。

     prj.conf: ビルドオプションの設定  src/main.c: これが本体  README.rst: 気にしない。  単なるドキュメント。ZephyrではreStructuredTextが標準  Sample.yaml: 見なかったことにする。  自動試験用の設定ファイル 31
  21. CMakeLists.txt  CMakeの設定ファイルは基本的にはコンパイル対象とincludeパスの追加のみ  find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) はオマジナイとして必須。 

    Zephyr固有のCmakeの設定がまるっと読み込まれる。  ベタでcmakeコマンドは使わないので、ここで頑張らないこと。  だいたいの場合、このファイルのコピペで十分。 32
  22. prj.conf  設定のメインはこれで行う。  LinuxのKconfigシステムを移植して使っ ている。  CONFIG_~=y で各種オプションを設定 する。

     オプションは山程ある。(現状20000程 度)  west build –t menuconfigで設定ができ る。  west build –t guiconfig でいい感じの画 面で設定ができる。 (apt install python3-tk が必要) 33
  23. src/main.c  どこにでもある普通のHello World.  「どこにでもある普通のHello World」が 素直に動かないのが組み込み開発。 (最近はだいぶ減った) 

    ANSI Cがない、main()がない、謎のlinkerスクリプト…  Zephyrは組み込み、RTOSではあるが、かなり普通の「C言語」で書ける。  とはいえ、POSIXを意識するfopenあたりになると互換レイヤーを組み込む形に。  この辺はソニーのSpresenseが使ってるNuttXの方が上手にやっている。 #include <stdio.h> int main(void) { printf("Hello World! %s¥n", CONFIG_BOARD_TARGET); return 0; } 34
  24. Blinky 読解 #1 #define LED0_NODE DT_ALIAS(led0) static const struct gpio_dt_spec

    led = GPIO_DT_SPEC_GET(LED0_NODE, gpios); leds: leds { compatible = "gpio-leds"; green_led_2: led_2 { gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>; label = "User LD2"; }; }; aliases { led0 = &green_led_2; };  DT=DeviceTree. この定義を参照してハードウェアにアクセスしている。  これもLinux由来。  LEDの定義を参照して、GPIOのポートとピン番号の情報をまとめた gpio_dt_spec構造体の情報を引き出している。  フラグがGPIO_ACTIVE_LOWだと負論理になる。 RESETとかだとACTIVE_LOWを使う。 Nucleo_h503rbのdevicetreeより抜粋 36
  25. Blinky 読解 #2  DeviceTreeさえわかればあとはシンプル。  gpio_is_ready_dt: 状態チェック  gpio_pin_configure_dt:

    ピンの入出力の設定  gpio_pin_toggle_dt: ピン出力を反転  k_msleep: ミリ秒待つ  DeviceTreeを深追いすると大変なので、いまは 「動いているのでヨシ!」  それ以外は割と素直なプログラム。  しかし、DeviceTreeが追えないと、このコードは読 めない。 int main(void) { int ret; bool led_state = true; if (!gpio_is_ready_dt(&led)) { return 0; } ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); if (ret < 0) { return 0; } while (1) { ret = gpio_pin_toggle_dt(&led); if (ret < 0) { return 0; } led_state = !led_state; printf("LED state: %s¥n", led_state ? "ON" : "OFF"); k_msleep(SLEEP_TIME_MS); } return 0; } 37
  26. ZephyrでのDeviceTree 40  DeviceTreeの書式はLinuxと同じ(DeviceTreeの仕様通り)  ZephyrではDTBを使わない  DeviceTreeから巨大なC言語のコード(マクロ)を生成する  動的な構成の変更はできない。

     実装と構成を分離して、コードの再利用性を高めるのが目的。  Linuxでもこの目的で導入された。 LKML: Linus Torvalds: Re: [GIT PULL] omap changes for v2.6.39 merge window (ARM 関連のコードが激増し、Linus氏が激怒したという逸話)  Zephyrでも同様に効果が上がっている。
  27. 具体的な現れ方  LチカのソースでDeviceTreeの情報にアクセスしているのは以下の箇所。 #define LED0_NODE DT_ALIAS(led0) static const struct gpio_dt_spec

    led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);  以下のようなDeviceTree定義と対応する。 / { aliases { led0 = &myled0; }; leds { compatible = "gpio-leds"; myled0: led_0 { gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; }; }; }; 41
  28. DeviceTreeの闇  DeviceTreeでは、配列の定義があるので、Zephyrのマクロでも配列をループ処 理するためのマクロ(!)が生成される。  ~_FOREACH_…という名前で、マクロを引数に取って、それを各要素に適用する高 階関数(高階マクロ?)が作られる。  COND_CODE_1 

    #ifdefと似たような動きをするが、マクロの引数で与えた値でコードの有効無効を切 り替える機能。  中身は完全に黒魔術(##を使った多重のマクロ展開をうまく使っている。解析が非 常に面倒)  上記のFOREACHと組み合わせると、DeviceTreeの定義に合わせて、無駄なくコード を生成できる。 42
  29. 一例 #define SERIAL_DEFINED_0 1 #define EXTERN_SERIAL_N(i) extern arduino::ZephyrSerial Serial##i; #define

    DECLARE_EXTERN_SERIAL_N(n, p, i) COND_CODE_1(SERIAL_DEFINED_##i, (), (EXTERN_SERIAL_N(i))) /* Declare Serial1, Serial2, ... */ DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_EXTERN_SERIAL_N) 私が作成したZephyr向けArduinoAPIの中から。gsoc-2022-arduino-core/cores/arduino/zephyrSerial.h DeviceTree上の配列serialsを見て、0番目以降の要素に対してSerial1, Serial2, …と割り振って宣言する。ここで0番目はす でにSerialとして定義済みなので除外したい。 COND_CODE_1は第一引数が定義済みで真なら第二引数、そうでなければ第三引数をコードに置き換えられる。ただし、 第一引数は内部で別のマクロと連結して処理されるため、整数リテラルとして定義されている必要がある。 ここでは、序数を連結すると、整数に展開できるSERIAL_DEFINED_0を定義して、この動作に適応させて処理を行っている。 複雑なコードであるが、DeviceTreeの要素を適切に扱えており、また、すべてコンパイル時計算となるので効率的である。 DeviceTreeによる実装と構成の分離、HALはLinuxから受け継いだHeritageの部分にもあたる。 dirtyではあるが、この仕組みによって非常に巧く処理されており、Zephyrを特徴づけている部分でもある。 43