Raspberry PiのPWMハードウェアには、シリアライザモードがあります。これはビット列を単に出力するもので、クロックに同期して32bitのデータをMSB側からGPIOへ出力します。
ベアメタルRaspberry Pi用MicroPythonで、この機能を使ってNeoPixelを点灯させてみました。
MicroPythonからPWMのハードウェアをたたくだけで、5つのNeoPixelを制御することができました。
NeoPixelの制御は、以前Raspbianや、ESP32のMicroPythonで試しています。
Raspberry PiのGPIO操作(3)NeoPixelを光らせる – 楽しくやろう。
MicroPythonのNeoPixelライブラリを使ってみた – 楽しくやろう。
制御には既存のライブラリを使用していますが、制御プロトコルの調査は一応していました。非常に単純なプロトコルで、GRB各8bitの値を、以下のようにエンコードして信号線1本で送信します。
標準的には1ビットあたり1.25usecで、
・0を送る場合はH:0.25us + L:1us
・1を送る場合はH:1us + L:0.25us
となります。HとLの持続時間は、それぞれ±0.15usが許容されます。
従って、Hの区間とLの区間の長さの比は、1:4が標準なのですが、最悪の場合は
(0.25+0.15) : (1-0.15) = 4:8.5
および
(0.25-0.15) : (1+0.15) = 1:11.5
となりますので、1:3~1:11までと結構許容範囲は広いです。
ちなみに上記はWS2812Bの場合ですが、最近Adafruitが使っているSK6812というチップでは、
・0を送る場合はH:0.3us + L:0.9us
・1を送る場合はH:0.6us + L:0.6us
となっているようです。許容誤差は±0.15usで変わりありません。
シリアライザの出力で制御する場合、HとLの持続時間の比を1:3として
0: HLLL
1: HHHL
というようにエンコードしてやれば良さそうです。
すると、シリアライザ4ビットでNeoPixelの1ビット分の情報を送ることができます。
32bitなら8bit分の情報になりますので、3つでNeoPixel一つ分のGRB信号を送れます。
HまたはLを1つ出力する時間は0.29us~0.38usとすれば規格の範囲に収まります。
これをシリアライザのクロック周波数に換算すると、2.7MHz~3.4MHzになります。
クロックマネージャで19.2MHzを6分周すれば上記を満たす3.2MHzになります。
MicroPythonの実行速度では、このスピードでデータを連続して送り出すのは無理ですが、ハードウェアPWMにはFIFOバッファが16個(×32bit)ありますので、用意したデータを16個連続して出力することは可能です。
これを使用すれば3つでNeoPixel一個分として、5連のNeoPixelまでFIFOで出力できそうです。
ですので、Cのプログラムを書かなくてもMicroPythonだけでNeoPixelの制御を実現できる(5個までなら)ことになります。
というわけで書いてみたのが以下のコードです。
このコードで、若干トリッキーなのがuint32()という関数です。
MicroPythonには符号無し整数というものがないので、unsignedな32bit整数を処理系に渡したいときは、signed 32bit整数に変換して送る必要があります。例えば0xffffffffを渡したければ-1を渡すということです。
uint32はこのための変換を行っています。
ちなみに、NeoPixelの制御信号を作り出すには、他にSPIを使う方法とI2Sを使う方法があります。
よく用いられているRaspbian用のNeoPixelドライバは、この3つ全てをサポートしています。
コメント