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_cycles
、pulse_cycles
を引数に与えます。
pulse_cycles / period_cycles
がデューティ比になります。
次は、PWM出力を停止する方法です。 ZephyrのPWM Driver APIは、PWMを停止する方法を提供していません。
Driverの実装を見ていると、デューティ比を0%
にすると、GPIOピンをclearしてくれるようになっています。
(上記issueにもそう書いてあります)
ということで、次のpwm_pin_set_usec
の呼び出しでPWM出力が停止します。
pwm_pin_set_usec(pwm_dev, PWM_CHANNEL, period, 0)