Raspberry PiのMicroPythonでNeoPixelを制御する


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つ全てをサポートしています。

コメント