1. はじめに
前回は、Device Tree の overlay を事前に作ったものを設定を変える運用で、いろんな設定を試しました。
今回は、その overlay の書き方の実践編として進めていきたいと思います。
この記事を読むことで、他のボードの overlay をある程度自前で設定できるようになるかと思います。
前回の記事は読まなくても進められる内容になると思いますが、読み物としてどうぞ。
2. 前回利用した overlay
今回の記事でも前回紹介した overlay をベースに話を進めます。
nucleo_g431kb 向けの overlay
/*
* Copyright (c) 2026 Takayuki Goto <tkg.develop@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
chosen {
zephyr,console = &usart1;
zephyr,shell-uart = &usart1;
};
};
&lpuart1 {
status = "disabled";
current-speed = <115200>;
// lpuart1_tx_pa2
// lpuart1_rx_pa3
pinctrl-0 = <&lpuart1_tx_pa2 &lpuart1_rx_pa3>;
pinctrl-names = "default";
// LL_DMAMUX_REQ_LPUART1_TX, LL_DMAMUX_REQ_LPUART1_RX
// dmas = <&dmamux1 0 35 STM32_DMA_PERIPH_TX>,
// <&dmamux1 1 34 STM32_DMA_PERIPH_RX>;
// dma-names = "tx", "rx";
// fifo-enable;
};
&usart1 {
status = "okay";
current-speed = <115200>;
// usart1_tx_pa9, usart1_tx_pb6
// usart1_rx_pa10, usart1_rx_pb7
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
pinctrl-names = "default";
// LL_DMAMUX_REQ_USART1_TX, LL_DMAMUX_REQ_USART1_RX
dmas = <&dmamux1 2 25 STM32_DMA_PERIPH_TX>,
<&dmamux1 3 24 STM32_DMA_PERIPH_RX>;
dma-names = "tx", "rx";
fifo-enable;
};
&usart2 {
status = "disabled";
current-speed = <115200>;
// usart2_tx_pa2, usart2_tx_pa14, usart2_tx_pb3
// usart2_rx_pa3, usart2_rx_pa15, usart2_rx_pb4
pinctrl-0 = < &usart2_tx_pa2 &usart2_rx_pa3 >;
pinctrl-names = "default";
// LL_DMAMUX_REQ_USART2_TX, LL_DMAMUX_REQ_USART2_RX
// dmas = <&dmamux1 4 27 STM32_DMA_PERIPH_TX>,
// <&dmamux1 5 26 STM32_DMA_PERIPH_RX>;
// dma-names = "tx", "rx";
};
&dma1 {
status = "okay";
};
&dmamux1 {
status = "okay";
};
&i2c1 {
status = "okay";
clock-frequency = <I2C_BITRATE_STANDARD>;
// i2c1_scl_pa13, i2c1_scl_pa15, i2c1_scl_pb8
// i2c1_sda_pa14, i2c1_sda_pb7
pinctrl-0 = < &i2c1_scl_pa15 &i2c1_sda_pb7 >;
pinctrl-names = "default";
};
&i2c2 {
status = "disabled";
clock-frequency = <I2C_BITRATE_STANDARD>;
// i2c2_scl_pa9
// i2c2_sda_pa8, i2c2_sda_pf0
pinctrl-0 = < &i2c2_scl_pa9 &i2c2_sda_pa8 >;
pinctrl-names = "default";
};
&i2c3 {
status = "disabled";
clock-frequency = <I2C_BITRATE_STANDARD>;
// i2c3_scl_pa8
// i2c3_sda_pb5
pinctrl-0 = < &i2c3_scl_pa8 &i2c3_sda_pb5 >;
pinctrl-names = "default";
};
&spi1 {
status = "disabled";
clock-frequency = <8000000>;
// spi1_sck_pa5, spi1_sck_pb3
// spi1_miso_pa6, spi1_miso_pb4
// spi1_mosi_pa7, spi1_mosi_pb5
pinctrl-0 = <&spi1_sck_pa5 &spi1_miso_pa6 &spi1_mosi_pa7>;
pinctrl-names = "default";
// cs-gpios = <&gpiob 7 GPIO_ACTIVE_LOW>;
};
&spi2 {
status = "disabled";
clock-frequency = <8000000>;
// spi2_sck_pf1
// spi2_miso_pa10
// spi2_mosi_pa11
pinctrl-0 = <&spi2_sck_pf1 &spi2_miso_pa10 &spi2_mosi_pa11>;
pinctrl-names = "default";
// cs-gpios = <&gpioa 1 GPIO_ACTIVE_LOW>;
};
&spi3 {
status = "okay";
clock-frequency = <8000000>;
// spi3_sck_pb3
// spi3_miso_pb4
// spi3_mosi_pb5
pinctrl-0 = <&spi3_sck_pb3 &spi3_miso_pb4 &spi3_mosi_pb5>;
pinctrl-names = "default";
cs-gpios = <&gpiob 0 GPIO_ACTIVE_LOW>;
sdhc0: sdhc@0 {
compatible = "zephyr,sdhc-spi-slot";
reg = <0>;
status = "okay";
mmc {
compatible = "zephyr,sdmmc-disk";
disk-name = "SD";
status = "okay";
};
spi-max-frequency = <8000000>;
};
};
&timers1 {
status = "disabled";
pwm1: pwm {
status = "disabled";
// tim1_ch1_pa8, tim1_ch1n_pa7, tim1_ch1n_pa11
// tim1_ch2_pa9, tim1_ch2n_pa12, tim1_ch2n_pb0
// tim1_ch3_pa10, tim1_ch3n_pf0
// tim1_ch4_pa11,
pinctrl-0 = <&tim1_ch1_pa8 &tim1_ch4_pa11>;
pinctrl-names = "default";
};
};
&timers2 {
status = "disabled";
pwm2: pwm {
status = "disabled";
// tim2_ch1_pa0, tim2_ch2_pa1, tim2_ch3_pa2, tim2_ch4_pa3
// tim2_ch1_pa5, tim2_ch3_pa9, tim2_ch4_pa10, tim2_ch1_pa15
// tim2_ch2_pb3
pinctrl-0 = <&tim2_ch1_pa0>;
pinctrl-names = "default";
};
};
&timers3 {
status = "disabled";
pwm3: pwm {
status = "disabled";
// tim3_ch2_pa4, tim3_ch1_pa6, tim3_ch2_pa7, tim3_ch3_pb0
// tim3_ch1_pb4, tim3_ch2_pb5, tim3_ch4_pb7
pinctrl-0 = <&tim3_ch2_pa4>;
pinctrl-names = "default";
};
};
&timers4 {
status = "disabled";
pwm4: pwm {
status = "disabled";
// tim4_ch1_pa11, tim4_ch2_pa12, tim4_ch3_pa13, tim4_ch1_pb6
// tim4_ch2_pb7, tim4_ch3_pb8
pinctrl-0 = <&tim4_ch2_pa12>;
pinctrl-names = "default";
};
};
&timers8 {
status = "disabled";
pwm8: pwm {
status = "disabled";
// tim8_ch1n_pa7, tim8_ch2_pa14, tim8_ch1_pa15, tim8_ch2n_pb0
// tim8_ch1n_pb3, tim8_ch2n_pb4, tim8_ch3n_pb5, tim8_ch1_pb6
// tim8_ch2_pb8
pinctrl-0 = <&tim8_ch2_pa14>;
pinctrl-names = "default";
};
};
&timers15 {
status = "disabled";
pwm15: pwm {
status = "disabled";
// tim15_ch1n_pa1, tim15_ch1_pa2, tim15_ch2_pa3
pinctrl-0 = <&tim15_ch1n_pa1>;
pinctrl-names = "default";
};
};
&timers16 {
status = "disabled";
pwm16: pwm {
status = "disabled";
// tim16_ch1_pa6, tim16_ch1_pa12, tim16_ch1n_pa13, tim16_ch1_pb4
// tim16_ch1n_pb6, tim16_ch1_pb8
pinctrl-0 = <&tim16_ch1_pb8>;
pinctrl-names = "default";
};
};
&timers17 {
status = "disabled";
pwm17: pwm {
status = "disabled";
// tim17_ch1_pa7, tim17_ch1_pb5, tim17_ch1n_pb7
pinctrl-0 = <&tim17_ch1_pa7>;
pinctrl-names = "default";
};
};
/*
&adc1 {
status = "disabled";
// adc1_in1_pa0, adc1_in2_pa1, adc1_in3_pa2
// adc1_in4_pa3, adc1_in15_pb0, adc1_in10_pf0
pinctrl-0 = <&adc1_in1_pa0>;
#address-cells = <1>;
#size-cells = <0>;
st,adc-clock-source = "SYNC";
st,adc-prescaler = <4>;
};
&adc2 {
status = "disabled";
// adc2_in1_pa0, adc2_in2_pa1, adc2_in17_pa4
// adc2_in13_pa5, adc2_in3_pa6, adc2_in4_pa7
// adc2_in10_pf1
// pinctrl-0 = <&>;
};
&dac1 {
status = "disabled";
// dac1_out1_pa4, dac1_out2_pa5
// pinctrl-0 = <&>;
};
*/
3. overlay を書く際に参照すべきファイル群
公式の手順通りに環境を構築した場合、ディレクトリ構成は以下の様になっています。
$ ls -a
. .. .venv .west bootloader modules tools zephyr
アプリ開発に於いては、その殆どは zephyr ディレクトリを参照することになるかと思いますが、
Devicetree に関しては zephyr と同じ階層にある modules ディレクトリを追いかける必要が出てきます。
これは、STM や NXP などの各ベンダーが提供している HAL 層やファイルシステム(fs)、暗号化やセキュアブートなどが含まれており、Zephyr とは別で管理されている外部モジュール群です。
overlay を記述する際、このMCUはどんなペリフェラルを持っていてどんな選択肢があるかを確認するために、ここで管理されている HAL を参照することになります。
4. 実際に参照されているファイルの確認方法
CMakeList.txt を追いかけるのが正攻法なのですが、v4.2.0 から追加された機能、build 時に 生成される zephyr.dts に参照した相対ファイルパスと行数がコメントとして付与される ようになっており、こちらを使ってショートカットします。
west build -p -b nucleo_g431kb zephyr/samples/hello_world
grep modules build/zephyr/zephyr.dts
/* node '/soc/pin-controller@48000000/i2c2_scl_pa9' defined in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:299 */
pinmux = < 0x124 >; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:300 */
bias-pull-up; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:301 */
drive-open-drain; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:302 */
/* node '/soc/pin-controller@48000000/i2c2_sda_pa8' defined in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:325 */
pinmux = < 0x104 >; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:326 */
bias-pull-up; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:327 */
drive-open-drain; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:328 */
/* node '/soc/pin-controller@48000000/tim4_ch3_pb8' defined in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:862 */
pinmux = < 0x302 >; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:863 */
/* node '/soc/pin-controller@48000000/lpuart1_rx_pa3' defined in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:993 */
pinmux = < 0x6c >; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:994 */
/* node '/soc/pin-controller@48000000/lpuart1_tx_pa2' defined in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:1019 */
pinmux = < 0x4c >; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:1020 */
bias-pull-up; /* in modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi:1021 */
defined in 以降に書かれている通り modules/hal/stm32/dts/st/g4/stm32g431k(6-8-b)tx-pinctrl.dtsi ファイルを参照していることがわかります。
5. 利用できるペリフェラルの名称とピンの確認
例えば nucleo_g431kb の I2C1 は以下のように記述していました。
&i2c1 {
status = "okay";
clock-frequency = <I2C_BITRATE_STANDARD>;
// i2c1_scl_pa13, i2c1_scl_pa15, i2c1_scl_pb8
// i2c1_sda_pa14, i2c1_sda_pb7
pinctrl-0 = < &i2c1_scl_pa15 &i2c1_sda_pb7 >;
pinctrl-names = "default";
};
ここで pinctrl-0 に利用できる名称は共通して i2c1_ が付いていることから、これをファイルから検索すればこのMCUで利用できるピンが見つかります。
grep -r i2c1_ modules/hal/stm32/dts/st/g4/stm32g431k\(6-8-b\)tx-pinctrl.dtsi
/omit-if-no-ref/ i2c1_scl_pa13: i2c1_scl_pa13 {
/omit-if-no-ref/ i2c1_scl_pa15: i2c1_scl_pa15 {
/omit-if-no-ref/ i2c1_scl_pb8: i2c1_scl_pb8 {
/omit-if-no-ref/ i2c1_sda_pa14: i2c1_sda_pa14 {
/omit-if-no-ref/ i2c1_sda_pb7: i2c1_sda_pb7 {
/omit-if-no-ref/ i2c1_smba_pb5: i2c1_smba_pb5 {
同様に spi1 で利用できる名称を探したい場合は以下のように探せます。
grep -r spi1_ modules/hal/stm32/dts/st/g4/stm32g431k\(6-8-b\)tx-pinctrl.dtsi
/omit-if-no-ref/ spi1_miso_pa6: spi1_miso_pa6 {
/omit-if-no-ref/ spi1_miso_pb4: spi1_miso_pb4 {
/omit-if-no-ref/ spi1_mosi_pa7: spi1_mosi_pa7 {
/omit-if-no-ref/ spi1_mosi_pb5: spi1_mosi_pb5 {
/omit-if-no-ref/ spi1_nss_pa4: spi1_nss_pa4 {
/omit-if-no-ref/ spi1_nss_pa15: spi1_nss_pa15 {
/omit-if-no-ref/ spi1_sck_pa5: spi1_sck_pa5 {
/omit-if-no-ref/ spi1_sck_pb3: spi1_sck_pb3 {
あとは、過去の下記記事と組み合わせれば、Devicetree を組み立てるために必要な情報が一通り揃うはずです。
あとがき
直近の解説系の記事は Devicetree ばかりになってしまいましたが、ここまで書いている記事もあんまりなさそうですし、走りきった感はあります。
ですがいまだに Devicetree 周りは苦手意識が強く、何度もトライアルアンドエラーで解消させているのが現状です。
その背景として、Devicetree は、STM や NXP などベンダーそれぞれの設計思想や文化に強く引っ張られたデータ構造や階層になっているからだと思っています。
各ベンダーの異なる文化を柔軟に表現できる Devicetree の凄さとも言えますが、「データを編集するために設計思想を理解する」をある程度求められるため、「mbed の程良さ」を身にしみている今日この頃です。