ZephyrでPWM

はじめに

ZephyrでPWMしてみます。 ターゲットSoCはnRF52840です。

Zephyrプロジェクト設定

Zephyr PWM driver

CONFIG_PWMを有効にします。 nRF52840のPWM0を利用するので、CONFIG_PWM_0を有効にします。

|> prj.conf

# PWM
CONFIG_PWM=y
CONFIG_PWM_0=y

device tree

nRF5xシリーズでは、ハードウェアPWMとソフトウェアPWMが利用できます。 今回は、ハードウェアPWMを利用します。

ハードウェアPWMでは、割り当てたGPIOにPWM信号を出力します。 今回、GPIO0の27番ピンにPWMオーディオデバイスが搭載されているので、device treeを次のようにします。

&pwm0 {
    status = "ok";
    ch0-pin = <27>;
};

参考までに、大本のdevice treeノードを掲載します。

|> zephyr/soc/arm/nordic/nrf52840.dtsi

     pwm0: pwm@4001c000 {
            compatible = "nordic,nrf-pwm";
            reg = <0x4001c000 0x1000>;
            interrupts = <28 1>;
            status = "disabled";
            label = "PWM_0";
        };

nRFの仕様書を見ると、PWMペリフェラルが4つ搭載されており、1つのPWMペリフェラルにつき4チャネル利用できます。 chx-invertedを設定すると、負論理になります。

&pwm0 {
    status = "ok";
    ch0-pin = <13>;
    ch0-inverted;
};

アプリケーション

適当に4kHz出力すれば鳴るでしょう、という安易な考えでアプリケーションを作ります。

#include <zephyr.h>
#include <misc/printk.h>
#include <device.h>
#include <pwm.h>

#define PWM_DRIVER DT_NORDIC_NRF_PWM_PWM_0_LABEL
#define PWM_CHANNEL DT_NORDIC_NRF_PWM_PWM0_CH0_PIN

/* in milli second */
#define PWM_4000_HZ    (MSEC_PER_SEC / 4U)

void main(void)
{
    struct device *pwm_dev;
    u32_t period = PWM_4000_HZ;

    printk("Start PWM buzzer.\n");

    pwm_dev = device_get_binding(PWM_DRIVER);
    if (!pwm_dev) {
        printk("Cannot find %s!\n", PWM_DRIVER);
        return;
    }

    if (pwm_pin_set_usec(pwm_dev, PWM_CHANNEL,
                    period, period / 2U)) {
        printk("pwm pin set fails\n");
        return;
    }
    k_sleep(MSEC_PER_SEC);

    printk("Stop PWM buzzer.\n");
    if (pwm_pin_set_usec(pwm_dev, PWM_CHANNEL,
                    period, 0)) {
        printk("pwm pin set fails\n");
        return;
    }
}

これでピーっと鳴ります。

PWM出力を開始するAPIは下記です。

pwm_pin_set_usec(pwm_dev, PWM_CHANNEL,  period, period / 2U)

Driverオブジェクト、channel、period_cyclespulse_cyclesを引数に与えます。 pulse_cycles / period_cyclesがデューティ比になります。

次は、PWM出力を停止する方法です。 ZephyrのPWM Driver APIは、PWMを停止する方法を提供していません。

github.com

Driverの実装を見ていると、デューティ比を0%にすると、GPIOピンをclearしてくれるようになっています。 (上記issueにもそう書いてあります)

ということで、次のpwm_pin_set_usecの呼び出しでPWM出力が停止します。

pwm_pin_set_usec(pwm_dev, PWM_CHANNEL, period, 0)