2354538_ja-JP

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

2354538_ja-JP

2354538_ja-JP

[Zephyr®シリーズ] 第4回 Kconfigとデバイスツリーの概要と実践的活用法 (日本語ブログ)
Zephyr-series-4-title.png
 
今回からZephyrの応用編に突入していきます。
今回は、Kconfigとデバイスツリーの概要とそしてそれを活用できるようハンズオンによるプログラミングの実践を行っていきます。
 
Zephyr RTOSの特徴として前回紹介したソフトウェアのスケーラビリティ(再利用性)により一度開発したソフトウェアを別のプロジェクトや派生製品に再利用することが容易に行えるようになり迅速な開発が可能になります。
 
そして、幅広いハードウェアプラットフォームに対応するために、Zephyrには「Kconfig」 と 「デバイスツリー (Devicetree)」 という強力な構成システムを採用しています。
 
これにより、同じC/C++言語のソースコードを書き換えることなく、設定ファイルの変更だけで異なるマイコンボードにプログラムを移植することができます。

本記事では、Kconfigとデバイスツリーの基本的な仕組みを解説します。そして実践的な活用法として、第3回で作成したハードウェアに依存しないLチカプログラムを修正して、それを「FRDM-MCXA153」と「FRDM-MCXN947」という異なるマイコンのボードで動作するようにします。
 
異なるボード(マイコンやプロセッサ)で同じアプリケーションを動作させるため、Kconfigとデバイスツリーを使って実践的プログラミング方法を説明していきます。
 
 

目次

 

前準備


 

HWの準備

 
本記事では、主に以下のボードを使用してプログラムの作成と動作確認を行います。

また、作成したプログラムのポータビリティ(移植性)を確認するための補足として、以下のボードも使用します。

 

SWの準備

 
Zephyrの開発環境(Zephyr SDK, Westコマンドなど)がすでに構築されていることを前提とします。構築がまだの場合は、第2回の環境構築の記事を参照してください。
 
Zephyrシリーズ第3回で作成したLチカプログラムを使用します。まだ作成していない方は、前回の記事を参考に作成することをおすすめします。

 

Kconfigの基礎

 
 
Kconfigは、Linuxカーネルでも採用されている設定システムです。Zephyrでは、カーネル機能、デバイスドライバー、サブシステム、アプリケーションの固有設定など、ソフトウェア機能を「有効にするか無効にするか」、あるいは「どのようなパラメータを設定するか」を管理するために使用されます。
Kconfigには以下2つのファイルが重要になります。

  • 「Kconfig」 ファイル: 選択可能な設定項目(シンボル)とそのデフォルト値、依存関係を定義します。
  • 「prj.conf」 ファイル*: アプリケーションの開発者が、「Kconfig」 で定義された項目に対して実際に設定したい値(「y」 で有効化、あるいは具体的な数値など)を記述するファイルです。

Kconfigを使用することで、不要なコードをコンパイルから除外し、メモリ使用量を最適化することができます。

そして、Kconfigやprj.confファイルはすべてテキストで記述されています。


機能を有効にする方法


prj.confファイルではプロジェクト全体に対して機能の有効化を行っています。

例えば、ADCとDAC、そしてOPAMPドライバはKconfigで定義されています。このKconfigで定義されている機能を利用する場合に、prj.confに「CONFIG_」を先頭に追加して宣言します。


Kconfig:ADCの定義Kconfig:ADCの定義Kconfig:ADCの定義


prj.conf例prj.conf例prj.conf例


 

デバイスツリー(Devicetree)の基礎

 
Devicetree例Devicetree例Devicetree例
 
デバイスツリーは、マイコンがどのようなハードウェア(CPU、メモリ、周辺機器、ピン設定など)に対応しているか、それらのハードウェアの設定や構成を記述するテキストファイルです。
そして、こられ設定や構成はマクロに展開されます。
 
C言語のコードの中にハードウェアのアドレスを直接書き込む(ハードコーディングする)代わりに、デバイスツリーに記述された情報をZephyrのマクロを通じて読み込むことで、ハードウェア非依存のプログラミングが可能になります。

  • ノードとプロパティ: ハードウェアの各要素は「ノード」として階層構造で表現され、レジスタアドレスや割り込み番号などが「プロパティ」として記述されます。
  • 「.dts」 と 「.dtsi」: マイコンやボードごとの標準的なハードウェア構成は 「.dts」 (Devicetree Source) や 「.dtsi」 (Include) ファイルでZephyrリポジトリ内にあらかじめ定義されています。
    • dts: ボードのデバイスツリーとして記述
    • dtsi: SoC・マイコンのデバイスツリーとして記述され、デバイスメーカーから提供されます。
  • 「.overlay」 ファイル: アプリケーション固有の配線(例:特定のGPIOピンにLEDを繋いだ)や、標準設定に対して上書きを行いたい場合に作成するファイルです。

 

ソフトウェアの再利用性を高めるためのベストプラクティス

 
Zephyrでソフトウェアの再利用性を高めるためには、以下の設計原則に従うことが重要です。

  1. ハードウェア依存部分と非依存部分の分離: Cコード(`main.c` など)には、特定のマイコンのレジスタ操作やピン番号を直接書かないようにします。
  2. デバイスツリーの「エイリアス」を活用する: アプリケーションからは、実際のハードウェアノード(例:「`&red_led`」 や 「`&gpioa`」)を直接参照するのではなく、「aliases(エイリアス)」 ノードで定義した抽象的な名前(例:「`led0`」など)を参照するようにします。これにより、ボードが変わってもエイリアスの指す先を変更するだけで対応できます。
  3. ボード別デバイスツリー(オーバーレイ)を準備する :派生製品など一部の機能に違いがある場合に、ボード別にハードウェアの差分がある部分のみ上書き(オーバーレイ)することができます。
  4. Kconfigによる機能の切り替え : アプリケーションの動作パラメータや特定の機能のオン・オフは、C言語の 「#define」 ではなく Kconfig シンボルを使用して制御します。

 

Kconfigとデバイスツリーの実践

 
ここから、実際に実践で使用できるようになるために簡単なプログラムを作成しながらKconfigとデバイスツリーの使い方を学んでいきます。
 

 

簡単なプログラムを作成する(ハンズオン)


プログラムは、前回作成したLチカプログラムを変更して以下のような仕様のプログラムを作成します。
 
ここでは実践として、「FRDM-MCXA153」と「FRDM-MCXN947」の両方で動く共通アプリケーションを作成します。

1. プログラムの仕様


  • ソースコード: 「第3回 始めてのLチカプログラム〜」を利用し、以下修正を加える
  • LEDの点滅速度: Kconfigを用いて点滅間隔を設定可能にする。
  • ボタン機能(機能の有効/無効化): Kconfigの設定により、ボタン機能の有効/無効を切り替えられるようにする。有効な場合は、ボタンを押すたびにLEDの「点滅」と「常時点灯」が切り替わる。
  • ボード名の出力: 起動時に標準出力(ターミナル)へ、Kconfigで設定した「デバイス(ボード)名」を出力する。

2. ディレクトリ構成

 
プロジェクトのディレクトリ構成を以下のようにします。
 
前回作成したLチカプログラムの「my_hello」フォルダにKconifgファイルやboardsフォルダを追加します。ファイルの追加はどのような方法でも問題ありません。
WindowsではPowerShellの`ni`コマンドやテキストエディターで「新規ファイル作成」をして、my_helloフォルダに保存してください。
LInuxでは、`touch`コマンドを使用することで空の新規ファイルを作成することが可能です。
 
「boards/」 以下のファイルによって、ボードごとのハードウェア差異や固有設定を吸収します。
 
 

my_hello/
├── CMakeLists.txt
├── Kconfig <- 新規追加:アプリ独自のKconfig
├── prj.conf <- アプリの共通設定
├── src/
│ └── main.c <- ハードウェア非依存の共通コード
└── boards/ <- 新規作成フォルダ
 ├── frdm_mcxa153.overlay <- 新規作成:FRDM-MCXA153用のデバイスツリー設定
 ├── frdm_mcxa153.conf <- 新規作成:FRDM-MCXA153用のKconfig設定
 ├── frdm_mcxn947_cpu0.overlay <- 新規作成:FRDM-MCXN947用のデバイスツリー設定
 └── frdm_mcxn947_cpu0.conf <- 新規作成:FRDM-MCXN947用のKconfig設定
 

参考: アプリケーションのディレクトリ内に特定のファイルを作成することで、Zephyrのビルドシステム(West)は自動的にそれらを認識し、設定を適用します。
  • Kconfigの追加:  アプリケーションフォルダに 「Kconfig」 ファイルを置くことで、独自のコンフィグシンボルを追加できます。
  • ボードごとの設定 (「boards/」 ディレクトリ): アプリケーション内に 「boards」 ディレクトリを作成し、そこに 「[ボード名].overlay」 や 「[ボード名].conf」 を配置すると、そのボードをターゲットとしてビルドした時にのみ、自動的にオーバーレイやKconfigの上書きが適用されます。

 

3. Kconfig と prj.conf の作成


まず、アプリケーションルートに独自の 「Kconfig」 を作成し、アプリケーション専用のパラメータを定義します。

my_hello/Kconfig
 
mainmenu "my LED blink"

config CUSTOM_BLINK_RATE_MS
int "LED blink rate in milliseconds"
default 1000
help
        Set LED blink frequency. #LEDの点滅周期(ミリ秒)を設定します

config ENABLE_BUTTON_TOGGLE
bool "Enable button to toggle LED state"
default y
help
        Enable button to toggle LED state. # ボタン入力によるLEDの点滅/点灯状態>の切り替え機能を有効にします。

config BOARD_NAME_STRING
string "Board Name String"
default "Unknown Board"
help
        Set board name for printf. # 標準出力に表示するボード名を設定します。

source "Kconfig.zephyr"
 
 

次に、アプリケーション全体の共通設定として 「prj.conf」 を記述します。
このprj.confに先ほど作成したKconfigシンボルを使って以下のように設定します。

my_hello/prj.conf
 
# GPIOの有効化
CONFIG_GPIO=y


# アプリケーションの共通設定
CONFIG_CUSTOM_BLINK_RATE_MS=500
CONFIG_ENABLE_BUTTON_TOGGLE=y
 

4. デバイスツリーオーバーレイとボード別設定の作成


「boards」ディレクトリを作成し、各ボードに向けたファイルを用意します。

FRDM-MCXA153用 (メインボード)

 
 
アプリケーションからは `sw0` という標準的なエイリアスでアクセスできるように、ボード上のボタン(`sw2`)をマッピングします。`led0` はすでにボード定義にあるためここでは省略できますが、必要に応じて明示的に上書きすることも可能です。
 
my_hello/boards/frdm_mcxa153.overlay
/ {
  aliases {
    sw0 = &user_button_2; /* FRDM-MCXA153のユーザーボタン */
  };
};
 

my_hello/boards/frdm_mcxa153.conf
 
CONFIG_BOARD_NAME_STRING="FRDM-MCXA153 Board"
 
アプリケーション(main.c)内でこの.conf(ボード別Kconfig)のシンボルを参照して、printf関数でボード名を標準出力させることが可能になります。

FRDM-MCXN947用 

FRDM-MCXN947でも同様に 「sw0」 を定義します。ボタンのハードウェア名が異なる場合でも、ここで吸収します。
 
実際、ボードによりハードウェア(ペリフェラルやインスタンスが異なる)名が異なる場合は、デバイスツリー内のエイリアスノードに実際のハードウェアを割当てます。

my_hello/boards/frdm_mcxn947_cpu0.overlay
 
/ {
  aliases {
    sw0 = &user_button_3; /* FRDM-MCXN947のユーザーボタン */
  };
};
 

my_hello/boards/frdm_mcxn947_cpu0.conf
 
MCXN947でビルドした時だけ、LEDの点滅速度を250msに上書き変更してみます。
 
CONFIG_BOARD_NAME_STRING="FRDM-MCXN947 Board"
CONFIG_CUSTOM_BLINK_RATE_MS=250

FRDM-MCXA153とFRDM-MCXN947では、アプリケーションコードは同じですが、Lチカの点滅の挙動をここでそれぞれ設定することができます。

5. ハードウェア非依存の共通コード (main.c) の作成


「my_hello/src/main.c」を以下のように記述します。
 
LED制御部分は、前回の記事で作成したハードウェア非依存のコード(「led0」 エイリアスを使用)を再利用し、そこにKconfigとボタンの制御を組み込んでいます。

my_hello/src/main.c
 
#include 
#include 
#include 


/* Devicetreeのエイリアスを参照する */
/* どのボードでも、一番目のLEDは通常 "led0" と定義されています */
#define LED0_NODE DT_ALIAS(led0)
#define SW0_NODE DT_ALIAS(sw0)


/* エイリアスからGPIO仕様(ポート、ピン、フラグ)を取得 */
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);


/* ボタン機能がKconfigで有効化されている場合のみコンパイルされる部分 */
#ifdef CONFIG_ENABLE_BUTTON_TOGGLE
static const struct gpio_dt_spec sw = GPIO_DT_SPEC_GET(SW0_NODE, gpios);
static struct gpio_callback button_cb_data;
static bool is_blinking = true;


void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    is_blinking = !is_blinking;
    if (!is_blinking) {
        /* 点滅オフ時はLEDを点灯させた状態にする */
        gpio_pin_set_dt(&led, 1);
        }
}
#endif //CONFIG_ENABLE_BUTTON_TOGGLE


int main(void)
{

    int ret;


    /* Kconfigで設定されたボード名を出力 */
    printf("Starting application on %s\n", CONFIG_BOARD_NAME_STRING);


    /* デバイスの準備確認 */
    if (!gpio_is_ready_dt(&led)) {
        return -1;
    }


    /* ピンの設定 (Devicetreeで定義された初期状態などを考慮して設定) */
    ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);

    if (ret < 0) {
        return -1;
    }


#ifdef CONFIG_ENABLE_BUTTON_TOGGLE
    if (!gpio_is_ready_dt(&sw)) {
        return -1;
    }

    ret = gpio_pin_configure_dt(&sw, GPIO_INPUT);

    if (ret < 0) {
        return -1;
    }

    ret = gpio_pin_interrupt_configure_dt(&sw, GPIO_INT_EDGE_TO_ACTIVE);

    if (ret < 0) {
        return -1;
    }

    gpio_init_callback(&button_cb_data, button_pressed, BIT(sw.pin));
    gpio_add_callback(sw.port, &button_cb_data);

#endif //CONFIG_ENABLE_BUTTON_TOGGLE

    while (1) {


#ifdef CONFIG_ENABLE_BUTTON_TOGGLE
        if (is_blinking) {
            ret = gpio_pin_toggle_dt(&led);
        }
#else
/* ピンの状態を反転 (ボタン機能が無効な場合は常に点滅) */
        ret = gpio_pin_toggle_dt(&led);
#endif //CONFIG_ENABLE_BUTTON_TOGGLE

/* Kconfigで設定された点滅間隔で待機 */
        k_msleep(CONFIG_CUSTOM_BLINK_RATE_MS);
    }

    return 0;
}
 

 

6. ビルドと実行

さて、実際に動作を確認してみます。ビルド環境セットアップやwestコマンド有効化については、前回の記事を参考にしてください。

まずは、下記のコマンド手順の様にインストールしているzephyrprojectリポジトリのディレクトリに移動し、west 有効化を行ってから進めます。


FRDM-MCXA153 での動作確認(メイン)

以下のコマンドでビルドし、FRDM-MCXA153に書き込みます。
 
## ホームディレクトリからZephyrprojectディレクトリに移動
cd ~/zephyrproject

## west環境を有効化
source .venv/bin/activate

## zephyr v4.3をチェックアウトしていない場合は、前回(第3回 初めてのLチカとソフトウェアの再利用性)を参考にv4.3をチェックアウトしてください。

west build -b frdm_mcxa153 my_hello
west flash
 

 

実行結果

 
コンソール出力コンソール出力コンソール出力
 
LED点滅、点灯モード切り替えLED点滅、点灯モード切り替えLED点滅、点灯モード切り替え
 
  • ターミナルに 「Starting application on FRDM-MCXA153 Board」 と表示されます。
  • LEDは 500ms 間隔で点滅します(「prj.conf」 の設定)。
  • SW2(「custom-sw」)を押すと点灯状態になり、再度押すと点滅に戻ります。

FRDM-MCXN947 での動作確認


全く同じC言語のソースコードのまま、ボード指定だけを変えてビルドします。
 
# -pオプションを使用し、frdm_mcxa153のビルド情報をクリーンしてビルドします。
west build -p -b frdm_mcxn947//cpu0 my_hello
west flash
 

実行結果

 
  • 「boards/frdm_mcxn947_cpu0.conf」 の内容が自動的に反映され、ターミナルには 「Starting application on FRDM-MCXN947 Board」 と表示されます。
  • LEDは 250ms 間隔で速く点滅します(「prj.conf」の設定)。
  • SW3(MCXN947側のボタンにマッピングされた 「user_button_3」)を押すことでLED点灯、点滅動作が同様に切り替わります。

FRDM-MCXA153とFRDM-MCXN947では、LED制御しているGPIOやスイッチボタンに繋がっているGPIOが異なりますが、デバイスツリーによりその差分が吸収され、アプリケーションのプログラムとハードウェアがきれいに分離できていることが実感できたと思います。


 

まとめ

 
 
今回は、ZephyrにおけるKconfigとデバイスツリーの基本を学び、それらを活用してハードウェア依存部分をCコードから分離する手法を実践しました。
 
デバイスツリーのオーバーレイ(「.overlay」)によってハードウェア設定を吸収し、ボード別Kconfigファイル(「.conf」)によってアプリケーションのパラメータを柔軟に変更したり、機能を簡単に有効、無効にすることができ、同一のソースコードを複数の異なるボードで再利用する強力な仕組みを体験できたかと思います。
 

=========================​

本投稿の「Comment」欄にコメントをいただいても、現在返信に対応しておりません。​
お手数をおかけしますが、お問い合わせの際には「NXPへの技術質問 - 問い合わせ方法 (日本語ブログ)」をご参照ください。​
(既に弊社NXP代理店、もしくはNXPとお付き合いのある方は、直接担当者へご質問いただいてもかまいません。)​

Zephyr-series-4-title.png

Zephyrのソフトウェア再利用性を高めるための特徴であるデバイスツリーとKconfigの概要を説明します。そして、それらを実践で利用するためにステップを踏んで説明していきます。

このぜZephyrシリーズ第1回から今回の第4回までを読み終えると、Zephyr RTOSを利用してプログラムが書けるようになります。

General Purpose MicrocontrollersMCX日本語ブログ
Tags (1)
No ratings
Version history
Last update:
2 weeks ago
Updated by: