メモ:rp-hal(RustでRaspberry Pi Picoをプログラミングするやつ)でPWMを使う

rp-hal というのはこれ

RP2040のPWM機能の使い方は、データシートに詳しく書かれている。

https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf

  • PWMのsliceは8個あり、それぞれに2つのchannelがある。
  • channel Bは、DIVMODEによってinputにもoutputにもなる。あまりよくわからないけど、何か外部のクロック?と同期するような用途でなければ両方output(free-running mode)でよさそう。
  • 周波数はsliceごとだが、duty比はchannelごとに設定できる(free-running modeの場合)。
  • 各channelに割り当てられるピンはあらかじめ決まっている。たとえばPWM0のA channelであれば、GPIO 0のピンとGPIO 16のピン。どちらかということではなく、両方使うこともできる(設定は同じになる)

データシートには C のコードも載っている。

これをRustでどう書くかというと、こんな感じになるらしい(参考:公式ドキュメント

let mut pac = pac::Peripherals::take().unwrap();

// PWMの全slice
let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);

// PWM1を取ってくる
let mut pwm1 = pwm_slices.pwm1;
pwm1.set_ph_correct();          // phase-correctモードを有効にする(なくてもいい?)
pwm1.enable();                  // PWMを有効にする
pwm1.set_top(u16::MAX);         // TOP(PWMのカウンタの上限)を設定する

// PWM1をfree running modeにする
let pwm1 = pwm1.into_mode::<bsp::hal::pwm::FreeRunning>();
// channelを取り出す
let mut ch_a1 = pwm1.channel_a;
let mut ch_b1 = pwm1.channel_b;
// channelをピンに割り当てる
ch_a1.output_to(pins.gpio2);
ch_b1.output_to(pins.gpio3);

let d = u16::MAX as f32 * 0.5;
let mut phase = 0.0;
loop {
    // duty 比を設定
    // sinf() は libm crate から持ってくる
    ch_a1.set_duty((d + d * sinf((phase + 0.00) * 2.0 * 3.1415)) as _);
    ch_b1.set_duty((d + d * sinf((phase + 0.50) * 2.0 * 3.1415)) as _);

    delay.delay_ms(17);
    phase += 0.01;
}

どうすればいいのかよくわからないのは、このchannelを配列に入れて使いたいけど、型が

Channel<Pwm1, FreeRunning, A>
Channel<Pwm1, FreeRunning, B>
Channel<Pwm2, FreeRunning, A>
...

という感じになっていて、すべて型が違う。

これがピンの場合は、AnyPinという型が用意されてるのだけど、PWM channelの場合はそういうのがなさそうで、うまく扱えない。

まあ、16個以上のLEDを光らせたいので、どうせこの用意されてるPWM機能では足りないのでここでそんなにがんばらなくていいんだけど、正解がよくわからない。

実際のコードは今のところこんな感じ。