nRF Connect SDK:PWMを使ったSoftblinkの実装

#NCSにはSoftblinkが(今のところ)ない
nRF SDKにはライブラリとして用意されていました。ちなみにSoftblink(ソフトブリンク)とはLEDはボワ～ッと点いたり消えたりする動作のことです。以前、FPGAをいじっていたころはDimmingとかDimmerとか言っていましたがイマイチ通じにくかった気がします。ええ、どんな呼び方でも通じれば別にいいんですよ、ということでSoftblinkはなんとなく通じやすい気がするので今後はこれでいこうと思います。
#Softblinkの中身はPWM
ボワ～ッと点いたり消えたりするLEDの実態はPWMです。PWMの1スパンのデューティをダイナミックに変えることでだんだん明るくなったりだんだん暗くなったりします。![pwm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/23d57786-3d88-2819-5a3e-24c5a225dca3.png)
こんな感じでデューティを高速で切り替えていくことでそのように見せかけるわけです。あと、PWMの1スパンは短くしないとチラチラしてしまいます。
(図中のデューティは10段階になっていますがサンプルではもっと細かいです)
#実装
実装してみます。ZephyrのTimerとPWMを使って実装します。

```main.c
/*
 * Copyright (c) 2016 Intel Corporation
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr.h>
#include <sys/printk.h>
#include <device.h>
#include <drivers/pwm.h>

#define PWM_LED0_NODE	DT_ALIAS(pwm_led0)

#if DT_NODE_HAS_STATUS(PWM_LED0_NODE, okay)
#define PWM_CTLR	DT_PWMS_CTLR(PWM_LED0_NODE)
#define PWM_CHANNEL	DT_PWMS_CHANNEL(PWM_LED0_NODE)
#define PWM_FLAGS	DT_PWMS_FLAGS(PWM_LED0_NODE)
#else
#error "Unsupported board: pwm-led0 devicetree alias is not defined"
#define PWM_CTLR	DT_INVALID_NODE
#define PWM_CHANNEL	0
#define PWM_FLAGS	0
#endif

const struct device *pwm;
uint16_t m_pwm_velocity, m_pwm_keep_high, m_pwm_keep_low;

void control_pwm_timer_handler(struct k_timer *timer)
{
    enum DIR
    {
        UP,
        KEEP_HIGH,
        DOWN,
        KEEP_LOW,
    };

    static uint16_t count_width = 0, count_keep = 0;
    static enum DIR dir = UP;

    // PWM
    pwm_pin_set_usec(pwm, PWM_CHANNEL, 5000, count_width, PWM_FLAGS);

    // PWM width(Counter)
    switch (dir)
    {
        case UP:
            count_width += m_pwm_velocity;
            if (count_width >= 5000)
            {
                dir = KEEP_HIGH;
            }
            break;

        case KEEP_HIGH:
            count_keep += m_pwm_keep_high;
            if (count_keep == 1000)
            {
                count_keep = 0;
                dir = DOWN;
            }
            break;

        case DOWN:
            if (count_width > m_pwm_velocity)
            {
                count_width -= m_pwm_velocity;
            }
            else
            {
                count_width = 0;
                dir = KEEP_LOW;
            }
            break;

        case KEEP_LOW:
            count_keep += m_pwm_keep_low;
            if (count_keep == 1000)
            {
                count_keep = 0;
                dir = UP;
            }
            break;

        default:
            count_width = 0;
            count_keep = 0;
            dir = UP;
            break;
    }
}
K_TIMER_DEFINE(control_pwm_timer, control_pwm_timer_handler, NULL);

void main(void)
{
    pwm = DEVICE_DT_GET(PWM_CTLR);
    if (!device_is_ready(pwm)) {
            printk("Error: PWM device %s is not ready\n", pwm->name);
            return;
    }

    // Start timer handler
    m_pwm_velocity = 50;
    m_pwm_keep_high = 10;
    m_pwm_keep_low = 10;
    k_timer_start(&control_pwm_timer, K_NO_WAIT, K_MSEC(5));
}
```

```prj.conf
CONFIG_PWM=y
```

##解説
5ミリ秒毎にタイマーワークを起動してPWMのデューティを設定します。デューティの増減速度は設定で変えることができます。
最初はだんだん明るくなっていき、最大輝度で一定時間ホールドしたのちにだんだんと暗くなっていきます。消灯したら同じく一定時間ホールドしてまただんだん明るくなっていきます。以下、繰り返しになります。
#余談
nRF SDK時代のPWMはGPIO0にしか使えませんでした。それを知らずにGPIO1にLEDを繋いで設計したらSoftblinkが使えない・・・と泣いたこともあります(笑)
まあ、nRF SDKでも自分でPWMドライバーで実装すればいいんですけどね。