hiro99ma blog

何か技術的なこと

ncs: DevAcademy bt-fund Lesson 4 Exer 3 が動かないのはUSB-CDC関係のようだ

2024/08/27

BLE Fundamentals Lesson 4 - Exercise 3をやっているのだが、うまくいかない。
MDBT53ボードには LED が載っているのだが、それが点滅しているのだ。 動画にしたが見えるかな?

YouTube

正しく動作する場合、デフォルトの LED はステータスLED ということで無限ループの中で点滅させるだけである。 なのでこれはerror()に陥っているのだろう。

いろいろログを出すようにしたり追加したりしたところuart_callback_set()-ENOSYSを返していた。
DeviceTree から ncs_nordic_uart の取得に失敗しているのかも?

USBシリアルからログが出たのでなんとかなるかと思ったが、DevAcademy Fundamentals(BLE ではない) の UART をやっておこう。

Fundamentals Lesson 5 UART

(UART は Lesson 5 だが URL は “lesson-4” なので間違えぬよう)

さて Zephyr には UART にアクセスする API が 3種類ある。

ちなみにこの Lesson の Exercise は Thingy:53 はサポートしていないらしい。 ブロック図を見るとセンサー類はたくさんあるが UART はなさそうだった。 もしかしたら UART でつないでいるセンサーがあるのかもしれんが、まあ深くは調べなくて良かろう。

ダメだった

で、この Exercise も MDBT53ボードでは動かなかった。 USB を有効にしているとuart_callback_set()-ENOSYSを返す。↑と同じだ。 ボード定義ファイル で USB を使うようにしていない場合は動いている。

commit

DeviceTree の扱い方がよくわかっていないせいか?

その Exercise の中だったと思うが、Lesson 2 へのリンクがあった。 「Reading buttons and controlling LEDs」というタイトルだったので中身も見ずにスルーしたのだが、DeviceTree の説明がここにあるじゃないか。。。 DevAcademy Intermediate の方にボード定義ファイルの Lesson があったから、てっきりそこしかないと思い込んでいたよ。。。

Devicetree Guide(これは ncs v2.6.1)というものはあるが、かなり膨大だ。 Lesson をやることにしよう。

Lesson 2 – Reading buttons and controlling LEDs

Devicetree

Devicetree source、以下 DTSファイルはだいたいこんな感じだ。

/dts-v1/;
#include <なんとか.dtsi>
#include "かんとか.dtsi"
/ {
        a-node {
                subnode_label: a-sub-node {
                        foo = <3>;
                };
        };
};

&gpio0 {
	status = "okay";
};

...

/ はルートで、全部この中に含まれるのかと思いきや、上に書いた&gpio0のようにその外側に書かれるものもある。

#include<...>"..."があるのは C/C++ と同じだろう。 拡張子は、DTSファイル本体は.dts、includeされる方は.dtsiが一般的なようだが、別に.dtsを includeしてもよいし .h を include しているファイルもあった。

プリプロセス的なものはそのくらいか。 これがあるせいで、といっては変だが nRF Connect for VScode で Visual Devicetree Editor で編集するのが難しい。 単一ファイルならよいけど、編集した内容をどのファイルに反映させるかなんてツールでは分からんしね。

ラベル

なんか、ノード1つに複数のラベルを付けられるなら配置として ノード: ラベルたち となりそうなのだけど違うのだ。 最近の言語みたいに変数名: 型と思っておけば良いか。

ラベルの役割だが、Devicetree 内の他の場所にあるノードを参照するためのものだそうだ(intermediateにも書いてあった気がする)。 何か理由があるのだろうけど、なんでノード名で参照できないのだろうか。 サブノードだと/a-node/a-sub-nodeみたいに書くのが長くなるからか?

ラベルとは別に aliases というノードもあるが、英語そのままなら「別名」だがラベルと何が違うのか。
どうも、別名はノードに付けるのではなくラベルに付けるようだ。 別名の方は Cソースから使うものだそうだ。そうすることで、DTSファイルの変更をすれば Cソースの方はそのままいける、というメリットがあるそうだ。
DTSファイルを見てどれが有効な設定なのか調べるのは大変なので、Visual Devicetree Editor で Alias のところだけ見れば良いというのがよいのかもしれん。 まあ、そのうちわかるだろう。

  aliases {
          subnode_alias = &subnode_label;
  };

サブノードにしかラベルを付けられない、とは書いていないのでノードにも付けられるのだろう(付けてた)。 プロパティも同様。

ノード名が led_0、そのラベル名が led0、エイリアスも led0
DK 上では LED1~4 だけど定義では led03 なのでエイリアスはそちらにあわせるのか。 なんとなくだが、dk_buttons_and_leds では LED1~4 を使っているのでエイリアスも 1 から始めた方がわかりやすいような? まあ、ここら辺の感覚は人それぞれか。
pinctrl を見ているとノード名とラベル名が同じになっていたので、そうなると何の名前を使っているのかさっぱりわからん。。

&は参照のマーク。
他のノードを参照する場合はラベル名を使うようだし、&ラベル名 というパターンしかないのか? aliases&ラベル名 の形で使うようだから、

Devicetree bindings

ときどき出てくる compatible

zephyr_udc0: &usbd {
  compatible = "nordic,nrf-usbd";
  status = "okay";
};

“Devicetree bindings”というもので、DTSファイルだけでは足りない情報を補足する機構らしい。 DTSファイルは半分で bindings は残り半分というくらい重要な情報とのこと。

compatible の例が載っていたが、ボード定義ファイルの中にあるYAMLファイルcompatibleが入っていないが同類だろう。 プロパティ名やプロパティが取り得る値の説明をひとまとめにして名前を付けている。

定義済みの Devicetree bindings は ここ にある。

すべてのノードについてcompatibleがあってもおかしくないと思う。 しかし UART なんかはない。Zephyr の中にもない。 arm,cmsdk-uartだろうか? とにかく compatible の指定がない。

&uart0 {
  status = "okay";
  current-speed = <115200>;
  pinctrl-0 = <&uart0_default>;
  pinctrl-1 = <&uart0_sleep>;
  pinctrl-names = "default", "sleep";
};

pinctrlには description に出てきはしているが “default” とか “sleep” とかよくわからん。

Pin control devicetree file

LED とボタン以外のピンマッピングは pinctrl ノードでやるようだ。
それ用に -pinctrl.dtsi というファイルに分けることが多いようだ。

pinctrlも説明が長い。。。 すまんがスルーだ。

nRF5340DKの pinctrl で uart0 はこうだった。

	uart0_default: uart0_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 20)>,
				<NRF_PSEL(UART_RTS, 0, 19)>;
		};
		group2 {
			psels = <NRF_PSEL(UART_RX, 0, 22)>,
				<NRF_PSEL(UART_CTS, 0, 21)>;
			bias-pull-up;
		};
	};

	uart0_sleep: uart0_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 20)>,
				<NRF_PSEL(UART_RX, 0, 22)>,
				<NRF_PSEL(UART_RTS, 0, 19)>,
				<NRF_PSEL(UART_CTS, 0, 21)>;
			low-power-enable;
		};
	};

Raytac MDBT53 ではこうなっていてuart0_defaultのグループ数が違った。

	uart0_default: uart0_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 20)>,
				<NRF_PSEL(UART_RX, 0, 22)>,
				<NRF_PSEL(UART_RTS, 0, 19)>,
				<NRF_PSEL(UART_CTS, 0, 21)>;
		};
	};

	uart0_sleep: uart0_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 20)>,
				<NRF_PSEL(UART_RX, 0, 22)>,
				<NRF_PSEL(UART_RTS, 0, 19)>,
				<NRF_PSEL(UART_CTS, 0, 21)>;
			low-power-enable;
		};
	};

グループは、グループ単位でプロパティの設定ができるらしい。 なので 2つに分かれていたらそれぞれ細かく設定できるともいえるし、それぞれに設定しないといけないとも言えるしで、深く考えなくても良いのかな。

アクセス

先ほど aliases は Cソースから使うと書いたが、DT_NODELABEL()でラベルも使えるらしい。 なんじゃそりゃ。
まあ、ラベルはノードを参照するために使うし、aliasesで元になるのもラベルだからおかしくはないのか。

DT_NODELABEL(ラベル名)DT_ALIAS(別名)で得られるのはノードIDというものらしい。
ちなみにラベル名や別名は"..."で囲まない。 まあ、間違えたらコンパイルエラーになるだけだ。

DEVICE_DT_GET(ノードID)で取得したデバイスポインタを汎用APIに与えて実際のデバイスドライバを経由してハードウェアにアクセスする。
つまり、アプリにはあまりハードウェアへのアクセスを直接記述しないということだ。
こういうフレームワークを使う以上、アプリとそれより下の階層を分離するくらいは負荷にならないということだろう。

ただ、こういうマクロやdevice_is_ready()のような汎用なものだけでなく、各ドライバ(Peripherals)が提供しているものもあるそうだ。
そして推奨は後者の方とのこと。 Devicetree からより多くの情報を取ってくるからという理由だ。 なんとなく「汎用の方を使った方が変更になったときも対応できるので推奨します」になるかと思ったので意外だった。

ダメな点の振り返り

Fundamentals Lesson 5 - Exercise 1を振り返る。

MDBT53のボード定義をそれぞれ試した。

となると USBシリアルに対応したどこかがよろしくないということだ。

USBシリアル対応commit

大きな差分はこれくらいだと思う。

/ {
	chosen {
		zephyr,console = &cdc_acm_uart0;
	};
};

&zephyr_udc0 {
	cdc_acm_uart0: cdc_acm_uart0 {
		compatible = "zephyr,cdc-acm-uart";
	};
};
CONFIG_USB_DEVICE_STACK=y
CONFIG_UART_LINE_CTRL=y

&zephyr_udc0 は pinctrl だからそんなに影響ないと思ったのだが、そのブロックをコメントアウトして zephyr,consoleuart0にすると動作した。 zephyr,consoleだけuart0にしてもダメだった。

まさか pinctrl だけで UART がうまくいかなくなるとは。。。 しかしなぜ?

CDC ACM

CDC ACM は UART driver API を使うとか 4 つまではインスタンスをサポートするなどは書いてあるが UART が使えなくなるようなことは書かれていない。

わからん。。。

< Top page