Pico PWMで440Hzを設定

このやり方が最良かどうかわかりません。私なりの方法です。

欲しい周波数を$f$、クロック周波数を$C_k$とします。また、ラップ値を$M$とすると、 分周費$D_r$は次式で求めることができます。

\[D_r=\frac{\frac{1}{f}*C_k}{M} = \frac{C_k}{f*M}\]

分周費は実数です。Raspberry Pi Picoは分周費を

pwm_set_clkdiv_int_frac(uint slice_number, uint8_t integer, uint8_t fract)
という関数で設定します。integerが整数部、fractが少数部です。 fractが4ビットですので少数部は$2^{-4}=0.125$の単位でしか設定することができません。 従って、$D_r$は実際には$RD_r$となり、本当に欲しい値とは少しだけずれてしまいます。

\[RD_r= \frac{INT(D_r*16)}{16}\]

今度はこの$D_r$からラップ値$RM$を求めます。

\[RM= INT(\frac{C_k}{f*RD_r})\]

ラップ値$M$と$RD_r$を使ってできる周波数と、ラップ値$RM$と$RD_r$を使った場合の周波数にどれほど差がでるか 計算してみます。 $f=440Hz$、 $C_k=125MHz$、 $M=32768$ とします。

\[D_r = \frac{125,000,000}{440*32768} = 8.669766512784091\]

16倍してINTを取り、16で割ると、$RD_r =8.625$となります。$RD_r$から作られる波形の周波数は

\[f = \frac{C_k}{M*RD_r} = \frac{125,000,000}{32768*8.625}=442.283740942028986\]

となります。一方$RM$を計算すると、

\[RM= INT(\frac{125,000,000}{440*8.625})= INT(32938.076416337285903) = 32938\]

こちらから求まる$f_r$は

\[f_r = \frac{C_k}{RM*RD_r} = \frac{125,000,000}{32938*8.625}=440.001020802368261\]

となり$f_r$の方が440Hzにより近い周波数となります。プログラムとしては以下のようになります。

#include "pico/stdlib.h"
#include "hardware/pwm.h"

int main() {
  gpio_set_function(0, GPIO_FUNC_PWM);
  gpio_set_function(1, GPIO_FUNC_PWM);
  uint slice_num = pwm_gpio_to_slice_num(0);

  pwm_set_wrap(slice_num, 32938);
  pwm_set_clkdiv_int_frac(slice_num, 8, 10);
  pwm_set_chan_level(slice_num, PWM_CHAN_A, 16469);
  pwm_set_chan_level(slice_num, PWM_CHAN_B, 8234);

  pwm_set_enabled(slice_num, true);

  while(1){}
}


4系統のPWMで演奏する

PicoのPWMの発信器を4系統を使って音源を作ります。USB-MIDIでPCとPico繋いで、PCから音を鳴らします。 PWMを4系統使うには、以下のプログラムのようにスライス番号を、pwm_gpio_to_slice_num、 それぞれのピン番号0,2,4,6を設定して獲得します。

#define CHNUM   4
uint slice_num[CHNUM];
  
void pwm_set() {
  
  for(uint8_t i=0; i<CHNUM*2; i+=2){
    gpio_set_function(i,    GPIO_FUNC_PWM);
    gpio_set_function(i+1,  GPIO_FUNC_PWM);
  }
  
  for(uint8_t i=0; i<CHNUM; i++){
    slice_num[i] = pwm_gpio_to_slice_num(i*2);
  }
}

Note Onの時
pwm_set_enabled(slice_num[ch], true);
Note Offの時
wm_set_enabled(slice_num[ch], false);
とするのとで音を出す、音を消すを行なっています。

サンプルプログラムをgithubに置きました。



s