皆さん こんにちは。
今回は PWM出力を使ってみます。
PWMとは
PWMとは、Pulse Width Modulation の略で、日本語ではパルス幅変調といいます。
GPIOからパルスを出力する機能です。
パルス幅を変えることでモーターを制御したり、LEDの明るさ調節を行ったりすることができます。
今回はラズピコに載っている緑色のLED(D2)をPWM出力で制御して明るさを調節してみます。
詳細については RP2040データシート の4.5項をご覧ください。
PWMのチャンネル
全てのGPIOでPWMを使うことができます。
LED(D2)がつながっているGPIOは25番ですからチャンネルは 4B を使うことになります。
以下の表はRP2040データシートの 4.5.2 で確認することができます。

プロジェクトの作成
以下の設定でプロジェクトを作成します。
Project Name: picoPwm
Console Options の Console over UART をチェックする
IDE Options の Create VSCode project をチェックする
Debugger: は PicoProbe を選択する

VSCodeを起動する
起動後、何かファイルが開いてたら File – Close Folder で閉じた後、File – Open Folder から picoPwm のフォルダーを辿って選択します。
こちらの環境では C:\Users\m3925\Documents\Pico\pico-project-generator\picoPwm です。
m3925の部分は皆さんのユーザー名に置き換えてください。
vscode関連の json ファイルは前回作成済のプロジェクトのものをドラッグ&ドロップでコピー&ペーストすると楽です。
ソースコードを書く
picoPwm.c を以下の通りに編集します。
#include <stdio.h>
#include "hardware/pwm.h"
#include "pico/stdlib.h"
#define PWM_GPIO 25
#define WRAP_MAX 65535
int main() {
stdio_init_all();
puts("Hello, PWM!");
gpio_set_function(PWM_GPIO, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(PWM_GPIO);
pwm_config cfg = pwm_get_default_config();
// プリスケーラーの初期値 (1) をあえて設定する
pwm_config_set_clkdiv_int(&cfg, 1);
pwm_init(slice_num, &cfg, false);
pwm_set_wrap(slice_num, WRAP_MAX);
int period_of_high = WRAP_MAX;
pwm_set_chan_level(slice_num, PWM_CHAN_B, period_of_high);
pwm_set_enabled(slice_num, true);
bool dir_negative = true;
while (true) {
sleep_ms(5);
pwm_set_enabled(slice_num, false);
if (dir_negative) {
period_of_high -= 100;
if (period_of_high < 100) {
period_of_high = 100;
dir_negative = false;
}
} else {
period_of_high += 100;
if (WRAP_MAX < period_of_high) {
period_of_high = WRAP_MAX;
dir_negative = true;
}
}
pwm_set_chan_level(slice_num, PWM_CHAN_B, period_of_high);
pwm_set_enabled(slice_num, true);
}
return 0;
}
PC側の準備
I2C通信の動作確認用として Tera Term を使います。
API関数がうまく動作したか否かなどの結果をUARTのputs()やprintf()でログ出力するようにしてみました。
Tera Termをお持ちでない方は こちら からダウンロードしてお使いください。
インストール後、 Tera Term を起動し、シリアルポートでPicoprobeのCOMポートを選択します。
もしCOMポートが複数ある場合には、Picoprobe以外の通信ケーブルをはずしてデバイスマネージャーでCOMポート番号を確認しておきます。

起動後、設定メニューのシリアルポートから以下の設定を行います。
スピート : 115200
データ : 8 bit
パリティ : なし
ストップビット : 1
フロー制御 : none
(設定値からスピードだけ変更すれば良いはずです)
と言っても今回は puts("Hello, PWM!"); するだけでした。
お手数おかけしました。
今回使ったPWMのAPI
pwm_gpio_to_slice_num()
GPIO番号からスライス番号を取得します。
第1引数:GPIO番号を指定します。
戻り値:スライス番号
GPIO25を指定した場合、チャンネル4Bの "4" が戻ります。
pwm_set_wrap()
PWMのカウンターがゼロに戻る前に到達する最大値を設定します。
第1引数:スライス番号を指定します。
第2引数:最大値を指定します。
カウンターは16ビットなので設定する値は最大で 65535 になります。
pwm_set_chan_level()
PWM出力がHighレベルである期間を指定します。
第1引数:スライス番号を指定します。
第2引数:チャンネル記号(PWM_CHAN_A or PWM_CHAN_B)を指定します。
今回は上で説明した通り PWM_CHAN_B を指定します。
第3引数:Highレベルの期間を指定します。
ここでpwm_set_wrap()で指定した値の半分を指定すると duty比が 50% になります。
pwm_set_enabled()
PWM出力の有効、無効を設定します。
第1引数:スライス番号を指定します。
第2引数:有効(true)、無効(false)を指定します。
pwm_get_default_config()
PWMの初期設定値を取得します。
戻り値: pwm_config構造体
pwm_config_set_clkdiv_int()
プリスケーラーの整数部に値を設定します。
第1引数:pwm_config構造体へのポインタ
第2引数:プリスケーラーの値(0-255)
初期値は 1 です。
以下の式から fPWM を変更することができます。ただし CSR の PH_CORRECT = 0, DIV_FRASC = 0 (それぞれ初期値)としています。
fPWM = fsys / (TOP + 1) * DIV_INT [Hz]
ここで TOP は pwm_set_wrap() の設定値です。
周期を変更してみたい方は、こちらで試してみてください。
波形では確認していないのですが、ここで fsys = 125MHz だと思われます。
pwm_init()
PWMを初期化します。
第1引数:スライス番号
第2引数:pwm_config構造体へのポインタ
第3引数:trueで初期化後にPWMを開始します。
なお、プリスケーラーを使わない(初期値 1 で良い)場合、以下のコードは不要です。
pwm_config cfg = pwm_get_default_config();
pwm_config_set_clkdiv_int(&cfg, 1);
pwm_init(slice_num, &cfg, false);
動作させてみる
それでは F5キーを押してプログラムを動作させてみます。
LEDの明るさが変われば成功です。
うまく明るさ調節できましたか。
お疲れさまでした。