6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ArduinoCore-zephyrについて半分くらい中の人が雑に語る。

Posted at

ArduinoCore-zephyr (beta) published!

12/5にArduino本家より

のアナウンスが出て、ZephyrOS based Arduinoがベータ版として公開されました。
MbedOSのEOLを受けて夏ごろに発表されましたが、その実体がついに登場したという形です。

ArduinoCore-zephyr前史

このArduinoCore-zephyr、実はその母体はかなり前から開発が始まっています。

image.png

このfork元 からもわかるように、この活動はGSoC2022, 2022年のGoogle Summer of Codeの採択プロジェクトとして、 Dhruva Goalさん(現在TI所属)が立ち上げました。

さらに、これ以前は、

の記事で紹介されているように、

Arduino Core is licensed under the GNU Lesser General Public License and Zephyr is licensed under Apache 2. That means this project will most likely need to be developed out of tree and in a separate repo to keep code and license separation.See #22247 for a historic discussion & soburi/arduino-on-zephyr for an earlier attempt prior to the Arduino Core architecture.

私のProof of concept的プロジェクトの arduino-on-zephyrが元になっています。
このarduino-on-zephyrは履歴を見ると2018に着手しており、実に足掛け6年でArduinoのメインストリームに投入されたということになります。

ということで、概ね私も「原案・設定協力」ぐらいの遠巻きポジションで関わっているような恰好になっています。

ディレクトリ構成を観察する

image.png

ディレクトリ構成を見てみると、ArduinoCore由来のcores, libraries, variantsがあるのがわかります。
ファイルもborads.txt, platoform.txt, programmers.txtなどArduinoのcore特有のものが見られます。
また逆にzephyrのディレクトリや、west.yml, Kconfig, CMakeLists.txtはzephyr由来のものです。

設計意図としてはArduinoのcoreとしてもZephyrのmoduleとしてもどちらでも使えるディレクトリ構成になっています。ただし、それぞれの配布元で検証している用途でしか(多分)動かないです。

Arduinoとしての動作

Arduinoのcoreとして動作する場合、ArduinoのIDEがborads.txt, platoform.txt, programmers.txtを見て、それに書かれているビルド設定でこのArduinoCore-zephyrに含まれるcores, variants, libraries、それに加えてユーザーの作成した.ino、インストールしたライブラリ群をビルドします。

ポイントとしては、これらにZephyrそのものは含まれていないということです。つまり、Arduinoの部分しかビルドしていません。
では、Zephyrの実体がどこにいるかといえば、どうやらfirmwareの配下にサポートされているボードごとに作られているのがそれで、見てのとおりのprebuiltのバイナリになります。
これはどうやらloaderにあるプログラムをビルドしたもので、これはZephyrのプログラムとして作られています。

では、こいつは何者か、ということでざっくり眺めてみますと、

https://github.com/arduino/ArduinoCore-zephyr/blob/be17eabb1e2e196e9913affb8009ff383424df61/loader/main.c#L120-L126
120行目あたりで

	res = llext_load(ldr, "sketch", &ext, &ldr_parm);
	if (res) {
		printk("Failed to load sketch, rc %d\n", res);
		return res;
	}

	void (*main_fn)() = llext_find_sym(&ext->exp_tab, "main");

こんなことをやっています。 llextというのはZephyrの動的リンクライブラリの仕組みで、DLLみたいなやつです。
これで、"sketch"という名前のモジュールを読み込んで、それの"main"関数を実行しています。
つまり、プログラム部分はまるっとライブラリになっていて、そのライブラリのmainをキックする部分がここに入っています。

ZephyrのLLEXTの機能は、ArduinoのLuca Burelliさんも色々contributeしており、これの実現も一つの
ターゲットだったんだな、とリリースされて納得するところです。

また、この構成だと、Zephyrでビルドした構成は変わらないということになります。
つまり、Zephyrのセンサーのドライバとかは使わずに、ベタでSPI, I2CのドライバのI/Fだけ使って、「Arduinoの世界」をその上に構築している構成になっています。

Zephyrの側からみると、アレコレできないという不満にもなるのですが、これはこれで明確なユースケースの実現を達成する設計で、この取捨選択がArduinoらしい、という感じもします。

Zephyrの側から見る

Zephyrの方から見ると、これはArduino風のAPIをZephyrに追加するモジュールとなります。
中身は概ね単なるAPIのWrapperで、特に気の利いたこともやってない、というのが真相です。
概ねZephryの側に十分な機能があるので、特に驚くところではないはずです。

ただし、このモジュールはZephyr全般に適用できることを目指して設計しているので、
ボード毎の個体差をよしなに隠蔽して、「どのボードもだいたいArduinoっぽく使える」必要があります。

これを実現するうえで問題となるのが、Arduinoの「端子名」「ペリフェラル名」をどうやって決定するか、です。

Arduinoではユーザーが利用可能なGPIOピンにD1, D2, ...、ADCのピンにA1, A2...と振られています。
また、Serialはユーザーが使えるものに、Serial, Serial1, ...、I2CはWire, Wire1, ...と番号が振られています。

オチとしては、これらに規則性はないので、自前で定義ファイルを書いて実現しています。
variantsに格納されているoverlayがそれで、それぞれに対応するGPIO, ADC, などを列挙しています。

これを、Zephyr特有の黒マクロと組み合わせて、以下のような形で定義を吐かせています。

enum digitalPins {
#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0
	DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, DN_ENUMS, (, )),
#endif
	NUM_OF_DIGITAL_PINS
};

これで、Arduino式の表示ピン名も正しくGPIO番号にマップできるようにしています。

実際のところこれだけだとまだ足りてなくて、
cores/arduino/zephyrCommon.cpp のあたりに汚い処理が書いてあるのですが、この辺は些末なので割愛します。

ここのGPIOピンの定義は各ボードの設定側にarduino_connectorの定義があり、
だいたいの場合それを引き写して作っています。

NordicのNRF52840-DKだとこのような感じに、ボード上にArduinoヘッダに対するマッピングを持っています。

	arduino_header: connector {
		compatible = "arduino-header-r3";
		#gpio-cells = <2>;
		gpio-map-mask = <0xffffffff 0xffffffc0>;
		gpio-map-pass-thru = <0 0x3f>;
		gpio-map = <0 0 &gpio0 3 0>,	/* A0 */
			   <1 0 &gpio0 4 0>,	/* A1 */
			   <2 0 &gpio0 28 0>,	/* A2 */
			   <3 0 &gpio0 29 0>,	/* A3 */
			   <4 0 &gpio0 30 0>,	/* A4 */
			   <5 0 &gpio0 31 0>,	/* A5 */
			   <6 0 &gpio1 1 0>,	/* D0 */
			   <7 0 &gpio1 2 0>,	/* D1 */
			   <8 0 &gpio1 3 0>,	/* D2 */
			   <9 0 &gpio1 4 0>,	/* D3 */
			   <10 0 &gpio1 5 0>,	/* D4 */
			   <11 0 &gpio1 6 0>,	/* D5 */
			   <12 0 &gpio1 7 0>,	/* D6 */
			   <13 0 &gpio1 8 0>,	/* D7 */
			   <14 0 &gpio1 10 0>,	/* D8 */
			   <15 0 &gpio1 11 0>,	/* D9 */
			   <16 0 &gpio1 12 0>,	/* D10 */
			   <17 0 &gpio1 13 0>,	/* D11 */
			   <18 0 &gpio1 14 0>,	/* D12 */
			   <19 0 &gpio1 15 0>,	/* D13 */
			   <20 0 &gpio0 26 0>,	/* D14 */
			   <21 0 &gpio0 27 0>;	/* D15 */
	};

ほんとはこれを使いたいのですが、ZephyrのDeviceTreeの処理では、これは処理の中で 「溶けて無くなる」
つまり、処理中にマッピングが解決されて、マッピング情報そのものが後の処理には見えない、という仕様に現状ではなっています。

この辺の仕様を変えられると、「Zephyrで動く奴、何もしなくてもだいたい全部Arduino相当」みたいな感じにできそうな感じでもあります。

Zephyrとの統合への課題

実際のところ、まだこのgsocで作られたArduinoAPIはZephyrには組み込まれていません。

背景として、ArduinoのコードはLGPLでライセンス互換性がないため組み入れることができないのです。

Arduino側では動的リンクを使ってLGPLの規定の中でこの問題を解決しています。
(これがなければリリースできなかったということでもあります。)

ならば、Zephyrに対するこれの組み込みはどうか、というと、実は簡単な解がないのが実際のところです。
LGPLを静的リンクするならばライセンス非互換になるし、Arduinoのapiの部分はリファレンス踏襲をせざるを得ない。
解決できるとすれば、ArduinoCore-apiをApache2で再実装するのを考えなければならないのですが、
なかなかうまい方法がないというのが現状です。

おわりに

そういった課題を抱えながらも、無事Arduinoの一部としてこのコードは世に放たれました。
こうなると、Arduino側の方が本家よりも本線になるので管理大変そうだなぁ、とか色々思うところではありますが、
とりあえず広く使われるようになりそうで何よりです。

是非試してみてください。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?