🐥

ZephyrRTOSでSTM32マイコン再入門~PWMでLED制御~

に公開

はじめに

これまでWindows上にZephyrRTOSの開発環境を構築し、NucleoF103RBボードを用いて学習を進めてきた。
https://zenn.dev/gotoooo/articles/894213f95372bd

今回は、PWM(パルス幅変調)を使ってNucleoF103RBのLEDを明るさ制御するサンプルを作成し、PWM動作の仕組みの理解に取り組みたい。

なおNucleoF103RBのオンボードLEDを直接PWM制御することはできないため、PA8に外付けLEDを接続して制御することにする。この場合、以下デバイスツリー定義に従い、TIM1のチャネル1を用いてPWM信号を生成することになる。

コード

今回はmain.cの実装に加えて、zephyr/prj.confの編集、そしてzephyr/boards/nucleo_f103rb.overlayのデバイスツリー定義が必要である。

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

#define PWM_LED_NODE DT_ALIAS(pwm_led0)
static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(PWM_LED_NODE);

#define PERIOD_USEC (20U * 1000U)

int main(void)
{
    uint32_t step = 100;           // デューティ比の刻み (usec)
    uint32_t delay_ms = 10;        // 1ステップごとの待ち時間
    uint32_t pulse = 0;            // デューティ比 (usec)
    bool increasing = true;        // フェード方向

    printk("Debug start\n");

	if (!pwm_is_ready_dt(&pwm_led0)) {
		printk("Error: PWM device %s is not ready\n",
		       pwm_led0.dev->name);
		return 0;
	}

    while (1) {
        // PWM出力設定 (周期20ms, デューティ比 pulse)
        pwm_set_dt(&pwm_led0, PWM_USEC(PERIOD_USEC), PWM_USEC(pulse));

        // フェード方向に応じてデューティ比を増減
        if (increasing) {
            pulse += step;
            if (pulse >= PERIOD_USEC) {
                pulse = PERIOD_USEC;
                increasing = false;
            }
        } else {
            if (pulse <= step) {
                pulse = 0;
                increasing = true;
            } else {
                pulse -= step;
            }
        }

        k_msleep(delay_ms);
    }

    return 0;
}

zephyr/prj.conf
CONFIG_PWM=y
CONFIG_PWM_STM32=y
zephyr/boards/nucleo_f103rb.overlay
/ {
    pwmleds {
        compatible = "pwm-leds";
        red_pwm_led: red_pwm_led {
            pwms = <&pwm1 1 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
        };
    };

    aliases {
        pwm-led0 = &red_pwm_led;
    };
};

デバイスツリーでのPWM設定

#define PWM_LED_NODE DT_ALIAS(pwm_led0)
static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(PWM_LED_NODE);

nucleo_f103rb.overlayで定義したpwm-led0エイリアスを用いて、PWMデバイスのノードを取得している。

メイン関数の説明


#define PERIOD_USEC (20U * 1000U)

int main(void)
{
    uint32_t step = 100;           // デューティ比の刻み (usec)
    uint32_t delay_ms = 10;        // 1ステップごとの待ち時間
    uint32_t pulse = 0;            // デューティ比 (usec)
    bool increasing = true;        // フェード方向

    printk("Debug start\n");

	if (!pwm_is_ready_dt(&pwm_led0)) {
		printk("Error: PWM device %s is not ready\n",
		       pwm_led0.dev->name);
		return 0;
	}

    while (1) {
        // PWM出力設定 (周期20ms, デューティ比 pulse)
        pwm_set_dt(&pwm_led0, PWM_USEC(PERIOD_USEC), PWM_USEC(pulse));

        // フェード方向に応じてデューティ比を増減
        if (increasing) {
            pulse += step;
            if (pulse >= PERIOD_USEC) {
                pulse = PERIOD_USEC;
                increasing = false;
            }
        } else {
            if (pulse <= step) {
                pulse = 0;
                increasing = true;
            } else {
                pulse -= step;
            }
        }

        k_msleep(delay_ms);
    }

    return 0;
}

main関数では、PWMのデューティ比を徐々に増減させることでLEDの明るさを変化させている。
pwm_set_dt関数を用いて、周期20ms、デューティ比pulse(マイクロ秒単位)でPWM信号を出力している。
inclreasingフラグに基づいてデューティ比を増減させ、k_msleep関数で各ステップ間に10msの遅延を挟んでいる。

おわりに

以上でZephyrRTOSを用いたSTM32マイコンのPWMによるLED制御の解説を終わる。
今回はPWMでLEDを制御したがサーボモータ制御などにも応用できるため、今後試す予定である。

Discussion