2354538_en-US

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

2354538_en-US

2354538_en-US

[Zephyr ® Series] Part 4: Overview and Practical Applications of Kconfig and Device Trees (Japanese Blog)
Zephyr-series-4-title.png
 
From this point on, we'll be moving on to the advanced topics of Zephyr.
This session will cover an overview of Kconfig and device trees, followed by hands-on programming exercises to help you utilize them effectively.
 
One of the key features of Zephyr RTOS, as mentioned previously, is its software scalability (reusability), which makes it easy to reuse software developed once in other projects or derivative products, enabling rapid development.
 
Furthermore, to support a wide range of hardware platforms, Zephyr employs a powerful configuration system called "Kconfig" and "Devicetree".
 
This allows you to port programs to different microcontroller boards simply by changing configuration files, without having to rewrite the source code in the same C/C++ language.

This article explains the basic mechanisms of Kconfig and the device tree. As a practical application, we will modify the hardware-independent LED blinking program created in Part 3 to run on two different microcontroller boards: " FRDM-MCXA153 " and " FRDM-MCXN947 ".
 
To run the same application on different boards (microcontrollers and processors), we will explain practical programming methods using Kconfig and the device tree.
 
 

table of contents

 

Preparation


 

Hardware preparation

 
This article will primarily use the following boards to create and test programs.

Additionally, the following boards will be used as supplementary tools to verify the portability of the program you have created.

 

SW preparation

 
This guide assumes that you have already set up the Zephyr development environment (Zephyr SDK, West command, etc.). If you haven't set it up yet, please refer to the second article on environment setup.
 
We will be using the LED blinking program created in the third installment of the Zephyr series. If you haven't created it yet, we recommend creating it by referring to the previous article.

 

Kconfig Basics

 
 
Kconfig is a configuration system also used in the Linux kernel. In Zephyr, it is used to manage whether to "enable or disable" software features, or "what parameters to set," such as kernel functions, device drivers, subsystems, and application-specific settings.
The following two files are important for Kconfig:

  • The "Kconfig" file defines the selectable configuration items (symbols), their default values, and dependencies.
  • "prj.conf" file *: This file is where application developers specify the values they want to set for items defined in "Kconfig" (such as enabling them with "y" or providing specific numerical values).

Using Kconfig, you can exclude unnecessary code from compilation and optimize memory usage.

Furthermore, the Kconfig and prj.conf files are all written in text format.


How to enable the feature


The prj.conf file enables features for the entire project.

For example, the ADC, DAC, and OPAMP drivers are defined in Kconfig. When using the functions defined in Kconfig, you declare them by adding "CONFIG_" to the beginning of prj.conf.


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


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


 

Device tree fundamentals

 
Devicetree例Devicetree例Example of a device tree
 
The device tree is a text file that describes what hardware (CPU, memory, peripherals, pin settings, etc.) a microcontroller supports, as well as the settings and configuration of that hardware.
These settings and configurations are then expanded into macros.
 
Instead of directly writing hardware addresses into C code (hardcoding), information described in the device tree can be read through Zephyr macros, enabling hardware-independent programming.

  • Nodes and Properties: Each element of hardware is represented as a "node" in a hierarchical structure, and register addresses, interrupt numbers, etc., are described as "properties."
  • ".dts" and ".dtsi": Standard hardware configurations for each microcontroller and board are predefined within the Zephyr repository in ".dts" (Devicetree Source) and ".dtsi" (Include) files.
    • dts: Described as the device tree of the board.
    • dtsi: Describes the device tree of an SoC/microcontroller and is provided by the device manufacturer.
  • ".overlay" file: This file is created when you want to override application-specific wiring (e.g., connecting an LED to a specific GPIO pin) or default settings.

 

Best practices for improving software reusability

 
To enhance software reusability in Zephyr, it is important to follow the following design principles:

  1. Separation of hardware-dependent and hardware-independent parts: C code (`main.c`) For example, avoid directly writing about specific microcontroller register operations or pin numbers.
  2. Leverage device tree aliases: Instead of directly referencing actual hardware nodes (e.g., `&red_led` or `&gpioa`), applications should reference abstract names defined in the `aliases` node (e.g., `led0`). This allows you to adapt to different boards simply by changing what the alias points to.
  3. Prepare a board-specific device tree (overlay) : When there are differences in some functions, such as in derivative products, you can overwrite (overlay) only the parts with hardware differences for each board.
  4. Switching features with Kconfig : Application behavior parameters and the on/off status of specific features are controlled using Kconfig symbols instead of C language "#define".

 

Kconfig and the Device Tree in Practice

 
From here, we will learn how to use Kconfig and the device tree by creating a simple program so that we can actually use them in practice .
 

 

Create a simple program (hands-on)


The program will be created by modifying the LED blinking program we created last time, and will have the following specifications.
 
Here, as a practical exercise, we will create a common application that runs on both "FRDM-MCXA153" and "FRDM-MCXN947".

1. Program Specifications


  • Source code : Use the code from "Part 3: Your First LED Blinking Program" and make the following modifications.
  • LED blinking speed: The blinking interval can be set using Kconfig.
  • Button Function (Enable/Disable): The button function can be enabled or disabled via Kconfig settings. When enabled, pressing the button will toggle between blinking and constant illumination of the LED.
  • Outputting board name: At startup, the "device (board) name" configured in Kconfig will be output to standard output (terminal).

2. Directory structure

 
The project directory structure should be as follows:
 
Add the Kconfig file and boards folder to the "my_hello" folder of the LED blinking program you created last time. Any method of adding the files is fine.
On Windows, use the PowerShell `ni` command or a text editor to create a new file and save it in the `my_hello` folder.
In Linux, you can create a new, empty file using the `touch` command.
 
The files under "boards/" handle hardware differences and unique settings specific to each board.
 
 

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設定
 

Note : By creating specific files within your application's directory, Zephyr's build system (West) will automatically recognize them and apply the settings.
  • Adding Kconfig: You can add your own configuration symbols by placing a "Kconfig" file in your Applications folder.
  • Board-specific settings ("boards/" directory): By creating a "boards" directory within your application and placing "[board name].overlay" or "[board name].conf" files there, the overlay and Kconfig overrides will be automatically applied only when building with that board as the target.

 

3. Create Kconfig and prj.conf


First, create your own "Kconfig" in the application root directory and define application-specific parameters.

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"
 
 

Next, as a common setting for the entire application, there is "prj.conf". This will be written.
In this prj.conf file, use the Kconfig symbol you just created to configure it as follows:

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


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

4. Creating device tree overlays and board-specific settings


Create a directory called "boards" and prepare the necessary files for each board.

Main board for FRDM-MCXA153

 
 
We map the button on the board (`sw2`) so that it can be accessed from the application using the standard alias `sw0`. `led0` is already in the board definition so it can be omitted here, but it can be explicitly overridden if needed.
 
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"
 
By referencing the symbols in this .conf (board-specific Kconfig) within the application (main.c), it becomes possible to output the board name to standard output using the printf function.

For FRDM-MCXN947

Similarly, we define "sw0" in the FRDM-MCXN947. This handles any differences in the hardware names of the buttons.
 
In fact, if the hardware names (which differ depending on the peripheral or instance) vary across boards, you assign the actual hardware to the alias node in the device tree.

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

my_hello/ boards/frdm_mcxn947_cpu0.conf
 
I'll try overriding the LED blinking speed to 250ms only when building with MCXN947.
 
CONFIG_BOARD_NAME_STRING="FRDM-MCXN947 Board"
CONFIG_CUSTOM_BLINK_RATE_MS=250

The application code is the same for FRDM-MCXA153 and FRDM-MCXN947, but you can configure the blinking behavior of the LED separately here.

5. Hardware-independent common code (main.c) Create


Write the following in "my_hello/src/main.c":
 
The LED control section reuses the hardware-independent code (using the "led0" alias) created in the previous article, and incorporates Kconfig and button control into it.

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. Build and run

Now, let's actually test its functionality. Please refer to the previous article for instructions on setting up the build environment and enabling the west command.

First, navigate to the directory of your installed zephyrproject repository as shown in the command instructions below, enable west, and then proceed.


Operation confirmed with FRDM-MCXA153 (main)

Build using the following command and write it to the 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
 

 

Execution result

 
コンソール出力コンソール出力Console output
 
LED点滅、点灯モード切り替えLED点滅、点灯モード切り替えLED flashing and steady light mode switching
 
  • The message "Starting application on FRDM-MCXA153 Board" will appear in the terminal.
  • The LED blinks at 500ms intervals ("prj.conf"). (Settings).
  • Pressing SW2 ("custom-sw") will turn it on, and pressing it again will return it to blinking.

Operation confirmed with FRDM-MCXN947


We'll build the project using the exact same C source code, only changing the board specification.
 
# -pオプションを使用し、frdm_mcxa153のビルド情報をクリーンしてビルドします。
west build -p -b frdm_mcxn947//cpu0 my_hello
west flash
 

Execution result

 
  • The contents of "boards/frdm_mcxn947_cpu0.conf" will be automatically applied, and the terminal will display "Starting application on FRDM-MCXN947 Board".
  • The LED blinks rapidly at 250ms intervals (configured in "prj.conf").
  • Pressing SW3 (the button mapped to "user_button_3" on the MCXN947) will similarly switch between LED illumination and blinking.

While the FRDM-MCXA153 and FRDM-MCXN947 use different GPIOs for controlling LEDs and switch buttons, the device tree effectively absorbs these differences, demonstrating how cleanly the application program and hardware are separated.


 

summary

 
 
In this session, we learned the basics of Kconfig and device trees in Zephyr, and practiced techniques to separate hardware-dependent parts from C code by utilizing them.
 
I believe you've experienced a powerful mechanism for reusing the same source code across multiple different boards, where hardware settings are absorbed by the device tree overlay (".overlay"), application parameters can be flexibly changed and features can be easily enabled or disabled using board-specific Kconfig files (".conf").
 

==========================

We are currently unable to respond to comments left in the "Comment" section of this post.
We apologize for the inconvenience, but please refer to "Technical Questions to NXP - How to Contact Us (Japanese Blog)" when making inquiries.
(If you are already an NXP distributor or have a relationship with NXP, you may ask your representative directly.)

Zephyr-series-4-title.png

This document provides an overview of the device tree and Kconfig, features designed to enhance Zephyr's software reusability. It then outlines the steps required to utilize these features in practice.

After reading through this Zephyr series, from the first installment to the fourth, you will be able to write programs using the Zephyr RTOS.

General Purpose MicrocontrollersMCXJapanese Blog
Tags (1)
No ratings
Version history
Last update:
2 weeks ago
Updated by: