ちょっと間が空きましたが、Raspberry Piをベアメタルでプログラムするシリーズの続きです。
今回はPWMを動かしてみました。コードは以下に置いてあります。
bare_matal_rpi_zero/pwm at master · boochow/bare_matal_rpi_zero
PWMは、伝達したい値を矩形波の1と0の時間の比で表現する変調方法です。
以前サーボモータを動かすときに使用しました。
Raspberry PiのGPIO操作(2)サーボモータを動かす – 楽しくやろう。
このほか、Raspberry Piでは標準のオーディオ出力にもPWMを使用しています。
オーディオにおけるPWMは、デジタルアンプで使われている方式と同様に数MHz程度の矩形波をPWM変調し、それをローパスフィルタに通すことで音声帯域の信号を生成しています。
ちなみに、他にRaspberry Piのオーディオ出力は、HDMI経由(GPUへの制御が必要)とI2S経由(外付けDACが必要)という方法もあります。
今回は、上記のサーボモータ用の信号の生成をベアメタルで行ってみました。
Raspberry PiのPWM
Raspberry PiではハードウェアPWMを2系統備えています。
解説はマニュアルの9章(P.138)にあります。
クロックを与えてPWMを行うほか、FIFOでデータを与えて、クロックに合わせてそのデータを1ビットずつ出力する(シリアライズ)こともできます。
今回は、シリアライズではなく普通のPWMを使用します。
PWMでは変調を制御するためのクロックが必要になりますが、PWMが使用するクロックの供給方法はマニュアルに記載されていません。
しかし、リバースエンジニアリングの結果、他のクロックと同様のレジスタがPWM用にも存在することが分かっています。
GPIOのクロックマネージャの解説はマニュアルのP.105にあります。PWMのクロックマネージャは、レジスタのアドレスが異なるだけで構成は同じです。
レジスタ(CM_PWMCTL、CM_PWMDIV)のアドレスは以下のページに記載があります。
BCM2835 registers – eLinux.org
また、実際にこれらを使ってPWM制御を行っている例が以下のページにありました。
Raspberry pi で AP のレジスタをC言語で直接変更して PWM を操作する – いかにして問題を解くか
Raspberry PiをEWARM使ってBareMetalで動かす(13): Embedded Workbenchマニアのページ
実装・テスト
まずはRaspberry PiベアメタルのMicroPythonから直接レジスタを書き換えて、動作を確認しました。
こんな感じです。
なお、マニュアルにはレジスタのアドレスが記載されていません。エラッタを参照して下さい。
import mcu mcu.mem32[0x20200004] |= (2<<24) # GPIO18をALT5に mcu.mem32[0x201010a0] = 0x5a000021 # CM_PWMCTLでクロックをdisable、ソースはオシレータに設定 mcu.mem32[0x201010a4] = 0x5a000000 | (192<<12) # オシレータクロック(19.2MHz)を192分周=100KHz mcu.mem32[0x201010a0] = 0x5a000211 # CM_PWMCTLでクロックをenable mcu.mem32[0x2020c000] = 0 # PWM1をdisable mcu.mem32[0x2020c010] = 2000 # PWM1 range = 2000 mcu.mem32[0x2020c014] = 1000 # PWM1 data = 1000(デューティ比50%) mcu.mem32[0x2020c000] = 129 # PWM1をenable、mark:spaceモードで使用 mcu.mem32[0x2020c014] = 1500 # PWM1 data = 1500(デューティ比75%) mcu.mem32[0x2020c014] = 500 # PWM1 data = 500(デューティ比25%)
これでちゃんと波形が生成されていることが確認できたので、Cで実装しました。
サーボの動作信号に合わせて、50Hz、パルス幅0.5ms~2.4msの信号を周期的に生成するようにしています。
コードは冒頭のリポジトリに置いてあります。
コメント