Raspberry Pi Pico C/C++ PWMを試してみる


[Raspberry Pi Pico C/C++ SDKのPWM用関数のまとめ ](https://qiita.com/keyyum/items/e38b5bfc0c16f8cbb462)でPWMの使い方が分かったので実際に動かしてみます

##基本的な使い方
用意されている関数から察するに基本的な処理の流れは

①GPIOにPWM割り当て
②PWMコンフィグを編集
③PWMコンフィグを反映しスタート
④レベル値(デューティカウント値)を設定

だと思われます。

コンフィグでまとめずに個々に設定した方が分かりやすいような気もしますが、
まずは作法？通りに実装してみます。

```c:pwm_test.c
#include "pico/stdlib.h"
#include "hardware/pwm.h"

#define PIN_PWM0  ( 0 )

void main( void )
{
    static pwm_config pwm0_slice_config;
    uint pwm0_slice_num;

    /* GPIOにPWMを割り当て */
    {
        gpio_set_function( PIN_PWM0, GPIO_FUNC_PWM );
        pwm0_slice_num = pwm_gpio_to_slice_num( PIN_PWM0 );
    }

    /* PWMコンフィグを編集 */
    {
        /* デフォルト値を取得 */
        pwm0_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */
    }

    /* PWMコンフィグを反映しスタート */
    {
        pwm_init( pwm0_slice_num, &pwm0_slice_config, true );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */
    }

    /* レベル値(デューティカウント値)を設定 */
    {
        pwm_set_gpio_level( PIN_PWM0, ( pwm0_slice_config.top * 0.20 ) );
            /* デューティ比20% */
    }

    while ( true )
    {

    }
}
```

![PWM_デフォルト.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398022/103723a1-8df8-b9c7-41a2-42ae7adc7acc.jpeg)
デフォルトだと1周期が525.0usでした。カウントクロックは125MHz(≒1/(525.0us*65535))程度のようです。
デューティは設定どおり20%になってますね。
極性通常の場合はデューティコンペアマッチでH→L,周期コンペアマッチでL→Hのようです。

##周期
周期は分周比とラップ値で調整できます。
クロック周波数が高い方が精度が良いのでラップ値がなるべく大きくなるように調整します。
ためしに1ms,2msに調整してみます。

```c:pwm_test.c
#include "pico/stdlib.h"
#include "hardware/pwm.h"

#define PIN_PWM0  ( 0 )
#define PIN_PWM1  ( 2 )

void main( void )
{
    static pwm_config pwm0_slice_config;
    uint pwm0_slice_num;

    static pwm_config pwm1_slice_config;
    uint pwm1_slice_num;

    /* GPIOにPWMを割り当て */
    {
        gpio_set_function( PIN_PWM0, GPIO_FUNC_PWM );
        pwm0_slice_num = pwm_gpio_to_slice_num( PIN_PWM0 );

        gpio_set_function( PIN_PWM1, GPIO_FUNC_PWM );
        pwm1_slice_num = pwm_gpio_to_slice_num( PIN_PWM1 );
    }

    /* PWMコンフィグを編集 */
    {
        /* デフォルト値を取得 */
        pwm0_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */
        /* ラップ値(一周期分のカウント値)を変更 */
        pwm_config_set_wrap( &pwm0_slice_config, 62500 );
        /* 分周比を変更 */
        pwm_config_set_clkdiv( &pwm0_slice_config, 2 );

        /* デフォルト値を取得 */
        pwm1_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */

        /* ラップ値(一周期分のカウント値)を変更 */
        pwm_config_set_wrap( &pwm1_slice_config, 62500 );
        /* 分周比を変更 */
        pwm_config_set_clkdiv( &pwm1_slice_config, 4 );
    }

    /* PWMコンフィグを反映しスタート */
    {
        pwm_init( pwm0_slice_num, &pwm0_slice_config, true );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */

        pwm_init( pwm1_slice_num, &pwm1_slice_config, true );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */
    }

    /* レベル値(デューティカウント値)を設定 */
    {
        pwm_set_gpio_level( PIN_PWM0, ( pwm0_slice_config.top * 0.20 ) );
            /* デューティ比20% */

        pwm_set_gpio_level( PIN_PWM1, ( pwm1_slice_config.top * 0.20 ) );
            /* デューティ比20% */
    }

    while ( true )
    {

    }
}

```

![PWM_分周.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398022/2653fd78-d5bb-adcd-0ed9-eb20439c2204.jpeg)
上が1msec,下が2msecです。

##極性
極性を変更してみます。

```c:pwm_test.c
#include "pico/stdlib.h"
#include "hardware/pwm.h"

#define PIN_PWM0  ( 0 )
#define PIN_PWM1  ( 2 )

void main( void )
{
    static pwm_config pwm0_slice_config;
    uint pwm0_slice_num;

    static pwm_config pwm1_slice_config;
    uint pwm1_slice_num;

    /* GPIOにPWMを割り当て */
    {
        gpio_set_function( PIN_PWM0, GPIO_FUNC_PWM );
        pwm0_slice_num = pwm_gpio_to_slice_num( PIN_PWM0 );

        gpio_set_function( PIN_PWM1, GPIO_FUNC_PWM );
        pwm1_slice_num = pwm_gpio_to_slice_num( PIN_PWM1 );
    }

    /* PWMコンフィグを編集 */
    {
        /* デフォルト値を取得 */
        pwm0_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */
        /* 極性を変更 */
        pwm_config_set_output_polarity( &pwm0_slice_config, false, false);

        /* デフォルト値を取得 */
        pwm1_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */
        /* 極性を変更 */
        pwm_config_set_output_polarity( &pwm1_slice_config, true, true);
    }

    /* PWMコンフィグを反映しスタート */
    {
        pwm_init( pwm0_slice_num, &pwm0_slice_config, true );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */

        pwm_init( pwm1_slice_num, &pwm1_slice_config, true );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */
    }

    /* レベル値(デューティカウント値)を設定 */
    {
        pwm_set_gpio_level( PIN_PWM0, ( pwm0_slice_config.top * 0.20 ) );
            /* デューティ比20% */

        pwm_set_gpio_level( PIN_PWM1, ( pwm1_slice_config.top * 0.20 ) );
            /* デューティ比20% */
    }

    while ( true )
    {

    }
}

```

![PWM_極性.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398022/07aa0a8c-c677-1ac2-3ae2-9bd6519b71a2.jpeg)
上が通常,下が反転です。


##位相補正

```c:pwm_test.c
#include "pico/stdlib.h"
#include "hardware/pwm.h"

#define PIN_PWM0  ( 0 )
#define PIN_PWM1  ( 2 )

void main( void )
{
    static pwm_config pwm0_slice_config;
    uint pwm0_slice_num;

    static pwm_config pwm1_slice_config;
    uint pwm1_slice_num;

    /* GPIOにPWMを割り当て */
    {
        gpio_set_function( PIN_PWM0, GPIO_FUNC_PWM );
        pwm0_slice_num = pwm_gpio_to_slice_num( PIN_PWM0 );

        gpio_set_function( PIN_PWM1, GPIO_FUNC_PWM );
        pwm1_slice_num = pwm_gpio_to_slice_num( PIN_PWM1 );
    }

    /* PWMコンフィグを編集 */
    {
        /* デフォルト値を取得 */
        pwm0_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */
        /* 位相補正を変更 */
        pwm_config_set_phase_correct( &pwm0_slice_config, false );

        /* デフォルト値を取得 */
        pwm1_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */
        /* 位相補正を変更 */
        pwm_config_set_phase_correct( &pwm1_slice_config, true );
    }

    /* PWMコンフィグを反映しスタート */
    {
        pwm_init( pwm0_slice_num, &pwm0_slice_config, true );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */

        pwm_init( pwm1_slice_num, &pwm1_slice_config, true );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */
    }

    /* レベル値(デューティカウント値)を設定 */
    {
        pwm_set_gpio_level( PIN_PWM0, ( pwm0_slice_config.top * 0.20 ) );
            /* デューティ比20% */

        pwm_set_gpio_level( PIN_PWM1, ( pwm1_slice_config.top * 0.20 ) );
            /* デューティ比20% */
    }

    while ( true )
    {

    }
}
```

![PWM_位相補正.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398022/99fd4282-2351-b872-44b5-dcc96a1f41c4.jpeg)

上が位相補正無し,下が位相補正有りです。
たしかに周期が2倍になっていますね。

##同時スタート
実は上記の実装では
PWM初期化+スタート処理pwm_initを個別に順に行いスタートさせているため
完全に同時にはスタートしていません。
![PWM_スタート.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398022/052a20d3-6c82-ebcf-4489-b117d64dc321.jpeg)
こんな感じで少しずれます。

制御によっては完全に同期させたい場合があります。
そのような場合はpwm_set_mask_enabledを使用すると全スライス同時スタートできます。

```c:pwm_test.c
#include "pico/stdlib.h"
#include "hardware/pwm.h"

#define PIN_PWM0  ( 0 )
#define PIN_PWM1  ( 2 )

void main( void )
{
    static pwm_config pwm0_slice_config;
    uint pwm0_slice_num;

    static pwm_config pwm1_slice_config;
    uint pwm1_slice_num;

    uint pwm_mask_enabled;

    /* GPIOにPWMを割り当て */
    {
        gpio_set_function( PIN_PWM0, GPIO_FUNC_PWM );
        pwm0_slice_num = pwm_gpio_to_slice_num( PIN_PWM0 );

        gpio_set_function( PIN_PWM1, GPIO_FUNC_PWM );
        pwm1_slice_num = pwm_gpio_to_slice_num( PIN_PWM1 );
    }

    /* PWMコンフィグを編集 */
    {
        /* デフォルト値を取得 */
        pwm0_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */

        /* デフォルト値を取得 */
        pwm1_slice_config = pwm_get_default_config();
            /* 位相補正：なし                           */
            /* 分周：1分周                              */
            /* カウントモード：フリーランニング         */
            /* 極性：通常                               */
            /* ラップ値(一周期分のカウント値)：0xFFFF   */
    }

    /* PWMコンフィグを反映.※ここではスタートしない */
    {
        pwm_init( pwm0_slice_num, &pwm0_slice_config, false );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */

        pwm_init( pwm1_slice_num, &pwm1_slice_config, false );
            /* PWMタイマカウンタ,レベル値(デューティカウント値)は0クリアされる */
    }

    /* レベル値(デューティカウント値)を設定 */
    {
        pwm_set_gpio_level( PIN_PWM0, ( pwm0_slice_config.top * 0.20 ) );
            /* デューティ比20% */

        pwm_set_gpio_level( PIN_PWM1, ( pwm1_slice_config.top * 0.20 ) );
            /* デューティ比20% */
    }

    /* 全スライス同時スタート */
    {
        pwm_mask_enabled = ( 0x01 << pwm1_slice_num ) | ( 0x01 << pwm0_slice_num );
        pwm_set_mask_enabled( pwm_mask_enabled );
    }

    while ( true )
    {

    }
}

```

![PWM_同時スタート.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398022/baf9d096-9b24-4420-af65-771d3ed2bea6.jpeg)