aimdevel’s blog

勉強したことを書きます

Raspberry Pi Pico + Zephyr OSでサーボモータを回す

概要

Zephyr OSのPWM APIサーボモータを動かすサンプルをRaspberry Pi Picoで動かしたメモ。

概要

使用するサーボモータは「RDS3115」。 以下のように配線して動かす。

サーボモータの赤色の線は電源の+に、黒は電源のーに、黄色はpicoのGPIO0につないでいる。 ソースコードは以下のサンプルを使用する。

zephyr/samples/basic/servo_motor/src/main.c at main · zephyrproject-rtos/zephyr · GitHub

作業手順

バイスツリーを追加する。

サーボモータを動かすには、上記の配線に合わせてデバイスツリーを書く必要がある。
ファイルはzephyrのソースコードに「samples/basic/servo_motor/boards/rpi_pico.overlay」として格納する。

今回はサンプルコードをベースに以下のように作成した。

/* SPDX-License-Identifier: Apache-2.0 */

/ {
    servo: servo {
        compatible = "pwm-servo";
        pwms = <&pwm 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
        min-pulse = <PWM_USEC(500)>;
                max-pulse = <PWM_USEC(2500)>;
    };
};

&pwm_ch4b_default {
    group2 {
        pinmux = <PWM_0A_P0>;
    };
};

&pwm {
    status = "okay";
    divider-frac-4 = <15>;
    divider-int-4 = <255>;
};

上記の内容を説明する。

ビルドする

通常通りビルドする。

west build -p always -b rpi_pico samples/basic/servo_motor

基板に転送して動作を確認する。

このプログラムをpicoで実行すると、サーボモータが1秒おきに角度を変えるように動く。

ソースコード確認

今回の変更箇所はデバイスツリーのみで、サンプルコード本体には手を加えていない。
バイスツリーのそれぞれのノードを見ていく。

バイスツリー

servo

/ {
    servo: servo {
        compatible = "pwm-servo";
        pwms = <&pwm 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
        min-pulse = <PWM_USEC(500)>;
                max-pulse = <PWM_USEC(2500)>;
    };
};

ここで設定する項目は以下。
- pwms
今回はpwmのチャネル0を使用するという指定になっている。チャネル0とはpicoのGPIO0番ピンを使用する指定のようだ。
- min-pulse
最小パルスを指定する。使用するサーボモータごとに異なるので調べる。今回は500とした。
- max-pulse
最大パルスを指定する。同上。

pwm_ch4b_default

 &pwm_ch4b_default {
    group2 {
        pinmux = <PWM_0A_P0>;
    };
};

使用するピンの設定を記述している。元から定義されているピン設定ノードに追加する形で作ったが、新たにノードを追加することもできると思う。
group1は初期設定されているので、ひとまずgroup2を使用した。多分何でもよい。
<PWM_0A_P0>が今回使用するピンの設定であり、0番ピン(GPIO0)をPWMの0Aチャネルで使用するという指定のようだ。

pwm

&pwm {
    status = "okay";
    divider-frac-4 = <15>;
    divider-int-4 = <255>;
};

status = "okey" でpwmを有効に設定する。
残りの二つの設定は、ほかのデバイスツリーの流用なのでよくわかっていない。また調べることにする。

12/3 追記
divider-frac-4とdivider-int-4は分周機構の設定値のようだ。
末尾についている数値がpwmのどのスライスの設定かを指している。
あれ?だとすると、上記の記述だとスライス0に設定できていないことになるな。なんで動いてるんだ。。。?
ちなみに、GPIO7などのピンでpwmを使おうとすると、スライス3用に上記の設定を書き換えないと動作しなかった。

サンプルコード

以下の流れでモータを制御している。サンプルコードでは3と4をループしている。
1. デバイスツリーから情報を取得
2. pwm構造体をバリデート
3. pwmのパルス幅を設定
4. 次のパルス幅を計算

1. デバイスツリーから情報を取得

static const struct pwm_dt_spec servo = PWM_DT_SPEC_GET(DT_NODELABEL(servo));
static const uint32_t min_pulse = DT_PROP(DT_NODELABEL(servo), min_pulse);
static const uint32_t max_pulse = DT_PROP(DT_NODELABEL(servo), max_pulse);

2. pwm構造体をバリデート

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

3. pwmのパルス幅を設定

ret = pwm_set_pulse_dt(&servo, pulse_width);

4. 次のパルス幅を計算

ソースコードは割愛。最大パルスと最小パルスの間を行ったり来たりするように制御している。

まとめ

Raspberry Pi Pico + Zephyr OSでサーボモータを回すサンプルコードをうごかした。
サンプルコードや基板固有のコードではなく、デバイスツリーの変更のみで動作可能だったので、基板間のコードの移植性が良いのではないかと感じた。
まあ、他のリアルタイムOSをあまり知らないので当たり前のことなのかもしれませんが。。。