📘

ZephyrRTOSでSTM32マイコン再入門~Blinkyで学ぶGPIO出力~

に公開

はじめに

前回の記事ではWindowsのVSCode+PlatformIOにZephyr RTOSの環境を構築し、NucleoF103RBボードでサンプルのBlinkyをビルドして動作するところまで確認した。
今回は「とりあえず点滅した」から一歩進んで、ZephyrがどのようにGPIOを扱っているのかを掘り下げていく。

Blinkyのコード

Zephyrのサンプルに含まれるmain.cは以下の通り。

main.cのコード全体
main.c
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>

/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS   1000

/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)

/*
 * 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 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);

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;
}

Zephyr特有の箇所について調べたことをまとめる。

デバイスツリーとエイリアス

/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)

Zephyrではハードウェアのピンや周辺機能をデバイスツリー (DTS)で定義している。
Blinkyサンプルではled0というエイリアスが用意されており、基板上のLEDにマッピングされている。
DT_ALIASでled0という名称のエイリアスから基板上のLEDのGPIO設定のノードを取得している。
これにより、アプリコード側は「どのポートの何番ピンか」を意識せずに 抽象化された名前 (led0) を使えるようになっている。

GPIO設定の取得

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);

ここでデバイスツリーから実際のGPIO設定を取得している。
GPIO_DT_SPEC_GETにはノードID, そのノードのプロパティ名を渡す。
< &gpioa 0x5 0x0 >はGPIOAポートの5番ピンをプルなしのハイアクティブで使う設定を意味する。

gpio_dt_specは<zephyr/drivers/gpio.h>で以下のように定義されている。

GPIOの初期化確認

	if (!gpio_is_ready_dt(&led)) {
		return 0;
	}

GPIOが使える状態になっているかを判定している。

  • デバイスツリーで有効化されているか(status = "okay";)
  • デバイスドライバが初期化済みか(DEVICE_DT_GET() で ready になっているか)

STM32ドライバの場合は起動時に以下処理が自動で実行されている。

  1. RCCで該当ポートへのクロックを有効化
  2. レジスタベースアドレスをセット
  3. "ready"状態に遷移

ピンの設定

	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
	if (ret < 0) {
		return 0;
	}

アプリケーション側で「このピンをどう使うか」を指定している。
第2引数で初期状態を定義する。

GPIO出力のトグル制御

	ret = gpio_pin_toggle_dt(&led);
	if (ret < 0) {
		return 0;
	}

この関数でGPIOの状態を反転させている。
内部の処理を見ていくと以下のようにZephyrのGPIO APIを使っていることがわかる。

GPIOのAPIは他に以下の処理が関数ポインタとして定義されている。

STM32向けのドライバの中でAPIの関数ポインタと実処理とを対応付けしている。

おわりに

今回はZephyrでのGPIO出力の仕組みについてBlinkyサンプルを通じて学んだ。

  • デバイスツリーでGPIO端子の設定を定義。
  • アプリのコードではGPIO端子の設定を取得し、APIを使って出力制御を行う。

引き続きサンプルコードを読み進めながらZephyrの仕組みを学び、ペリフェラルの使用方法をまとめていきたい。

Discussion