前回は久しぶりにラズパイPico上でTinyGoのオブジェクトを走らせて吉例Lチカ。今回はPWM出力を使ってみます。ラズパイPicoのGPIOの全端子にPWM出力が可能です。ただしPWM周期的には最大8通り、各周期に対して2種類のDutyサイクルを指定可能です。今回は周期を司る8個のスライスを全て使ってみます。
※「GoにいればGoに従え」Go関連記事の総Index
※実機動作確認は Arm Cortex-M0+コアのRP2040チップ搭載、みんな大好き Raspberry Pi Pico機にTinyGoのオブジェクトを書き込んで行っています。ビルドはWindows11上で行ってます。
ラズパイPicoのPWM
多くのマイコンでは、タイマの横にキャプチャ/コンペア・チャネルなどというものが存在し、それらを使ってPWM出力をすることが多いと思います。一方、ラズパイPicoのマイコンRP2040は、キッパリとタイマとPWM機能を切り離しており、タイマはタイマ、PWMはPWMとハードが異なってます。
とはいえPWMするためにはカウンタ回路が必要なので以下のような構成になっています。
-
- PWM機能はスライスという独立した単位ハードウエアからなる
- 一つのスライスには1個の16ビットカウンタが1本存在する。
- 一つのスライスには2個のコンペアチャネルが存在し、それぞれ上記のカウンタとコンペアしてPWM出力信号を生成できる。
- スライスを合計8個搭載。
PWMの周期はカウンタによって制御されるので、各スライスは1つの周期で動作します。各スライスにコンペアチャネルは2つあるので、同一周期で異なるデューティサイクルの2種のPWM波形を同時生成できる、という仕組みです。8x2で16種の波形が生成できますが、うち14種の波形については異なる2端子の片方または両方に出力できるので、16+14で30本のGPIO端子全てになんらかのPWM信号を出力可能、ということになってます。
TinyGoにおけるRaspberry Pi Picoのサポート状況については御本家以下のページに記されております。TinyGo > Reference > Microcontrollers
また、PWMなどハードウエアの制御の詳細については、Machineパッケージについての記述が以下にあります。
コマケーこと言うと、Machineパッケージのページではただ pico と呼ばれてますが、上の方のページでは Raspberry Pi Pico と「フルネーム」で呼ばれているので、一覧を眺めているとちょっと戸惑います。大した話でないけれども。
また、PWMの使用方法については、以下のTutorialで説明されています。そのターゲットデバイスはラズパイPicoなので、動かないわけありません。
今回は、ほぼTutorialどおりっす。
全PWMスライスを使ってみる接続
今回は8本の全スライスを全て動かしてみます。チャネルについてはメンドイので「A」のみです。「システマティック」なラズパイPicoのこと、以下の回路図のように、各スライスのチャネルAは、ボードの片側にならんでいる偶数番号の端子に割り当てられてます。今回使用してないチャネルBは奇数端子デス。
PWMの動作そのものは、オシロをつなげて波形で見ているのですが、全端子を観察するのはメンドイので全てにLEDを接続してます。PWMの周波数を低くすれば人間にも見えるように点滅できるはず。
今回実験に使用したTinyGoソース
実験に使用したソースが以下に。Tutorialに掲載されているコードを「ふくらませた」だけですが、もろもろ手抜きです。チャネル設定のところでエラーが返ることもありですが、そういうメンドイ処理は踏みつぶしてます。ただただエラーなく動く筈としているもろ手抜きなコードっす。
なお吉例LチカのオンボードLEDのソフトウエアループはそのまま残してます。
package main import ( "machine" "time" ) var period500 uint64 = 1e9 / 500 var period1000 uint64 = 1e9 / 1000 var period10 uint64 = 1e9 / 10 func main() { led := machine.LED led.Configure(machine.PinConfig{Mode: machine.PinOutput}) pin0 := machine.GPIO0 pwm0 := machine.PWM0 pin2 := machine.GPIO2 pwm1 := machine.PWM1 pin4 := machine.GPIO4 pwm2 := machine.PWM2 pin6 := machine.GPIO6 pwm3 := machine.PWM3 pin8 := machine.GPIO8 pwm4 := machine.PWM4 pin10 := machine.GPIO10 pwm5 := machine.PWM5 pin12 := machine.GPIO12 pwm6 := machine.PWM6 pin14 := machine.GPIO14 pwm7 := machine.PWM7 pwm0.Configure(machine.PWMConfig{Period: period500}) pwm1.Configure(machine.PWMConfig{Period: period500}) pwm2.Configure(machine.PWMConfig{Period: period1000}) pwm3.Configure(machine.PWMConfig{Period: period1000}) pwm4.Configure(machine.PWMConfig{Period: period1000}) pwm5.Configure(machine.PWMConfig{Period: period1000}) pwm6.Configure(machine.PWMConfig{Period: period1000}) pwm7.Configure(machine.PWMConfig{Period: period10}) ch0A, _ := pwm0.Channel(pin0) ch1A, _ := pwm1.Channel(pin2) ch2A, _ := pwm2.Channel(pin4) ch3A, _ := pwm3.Channel(pin6) ch4A, _ := pwm4.Channel(pin8) ch5A, _ := pwm5.Channel(pin10) ch6A, _ := pwm6.Channel(pin12) ch7A, _ := pwm7.Channel(pin14) pwm0.Set(ch0A, pwm0.Top()/2) pwm1.Set(ch1A, pwm1.Top()/4) pwm2.Set(ch2A, pwm2.Top()/2) pwm3.Set(ch3A, pwm3.Top()/4) pwm4.Set(ch4A, pwm4.Top()/2) pwm5.Set(ch5A, pwm5.Top()/4) pwm6.Set(ch6A, pwm6.Top()/2) pwm7.Set(ch7A, pwm7.Top()/2) for { led.Low() time.Sleep(time.Second * 1) led.High() time.Sleep(time.Second * 1) } }
なお、period500は500Hz、periol1000は1kHzがPWMの周期です。それでは人の目では見えないので、1個だけperiod10という10Hz周期を用意してます。これなら何とか点滅が見える筈。
動作確認
上記のソースをビルドして、USB接続されたラズパイPicoに書き込むのは以下のコマンドラインで。なお、書き込み前にBOOTボタンを押して立ち上げてブートモードに入れておかないとなりません。
$ tinygo flash -target=pico
実機で動作している様子が以下に。GPIO14に接続したLEDのみ、点滅しているのが肉眼で確認できます。他は点灯しているようにしか見えませぬ。
人間の目で見えないところをオシロで見たのが以下に。黄色のC1がGPIO0に出力されている周期500Hz、デューティ50%の波形。青色C2が、GPIO14に出力の周期10Hz、デューティ50%の波形。
ラズパイPicoのカウンタにはスマートな小数点以下ありのプリスケーラなどもついているので周波数の設定はバッチリっす。