稼働中

マイクロビット(e_25)NeoPixel シリアルLED

NeoPixel

NeoPixelをmicro:bitで使ってみました。※thonny-microbitのMicroPythonを使っています。
「NeoPixel」とは通信でRGB値が制御できるLEDのことようです。シリアルLED、アドレッサブルLEDとも言うらしいです。
WorldSemi社のDriver IC(WS2811)を搭載したRGB LED(WS2812B)を連結した集まりでadafruitの商品名のようですがよくわかりません。
以降の記事はWS2811、WS2812Bのデータシート、「https://www.gammon.com.au/forum/?id=13357」のサイトなどを参考にしました。

外観

使用したNeoPixelの外観写真です。8個のRGBLED(たぶんWS2812B)が実装されています。

NeoPixel

DINとmicro:bitのMOSIをレベルコンバーターを介してつなぎます。NeoPixcelをさらに接続する場合はDOUTを次のNeoPixelのDINに接続します。単純に接続した場合はmicro:bitでRGB LEDが256個まで制御できるそうです。※以下はmicro:bitで使えるneopixelモジュールを使って点灯させています。neopixelモジュールの出力ピンはMOSI出力になってます。

裏面

RGB LED(WS2812B)の外観写真です。RGB LEDとDriver IC(WS2811)が見えます。右はRGB LEDを低電流で点灯させた写真です。

RGB LED(WS2812B) RGB LED 低発光

ちなみに他のNeoPixelの外観写真です。裏面のシルク印刷が文字が反転していました。そのまま使うと動きません。写真で左右が反対になっています。写真の左上がIN、右上がOUTです。接続すれば動作に問題は無いようでした。

他のNeoPixel  シルクが反転

接続例

接続例
WS2811、WS2812Bの電源電圧は3.5~5.5Vです。そのため5V動作させるので別途電源が必要です。
レベル変換回路を介してmicro:bitと接続しました。NeoPixelのGNDは面倒で1点しか接続していません。実用する場合にはレベルコンバーターも含めて空端末を処理した方が良いと思います。

neopixelモジュールをインポートして使う場合の接続例です。pin0をレベルコンバータを介してNeoPixelのDINと接続して制御します。

接続例

実体接続した写真です。
ちなみに5V電源、全LEDのPWMデータを(255,255,255)で全点灯すると約215mA流れました。また、全消灯時の待機電流は7.8mAと大きかったです。

実体図

スクリプト

micro:bitのMicroPythonにはneopixelモジュールがあります。このモジュールを使うと非常に簡単にLED点灯させることができます。
RGB LED8個をpin0で制御するには以下のようにします。これで配列ができるようです。


np = neopixel.NeoPixel(pin0, 8)

np[num]に(赤, 緑, 青)のPWM値(0~255)を代入して明るさ設定すれば点灯します。
neopixel.NeoPixelの関数には消灯のclear()と点灯のshow()があります。

スクリプト例です。


from microbit import *
import neopixel                 # neopixelモジュールを使います
# 色データ
RED=(255, 0, 0)
ORANGE=(0xff,0xfA,0)
YELLOW=(255, 150, 0)
GREEN=(0, 255, 0)
CYAN=(0, 255, 255)
BLUE=(0, 0, 255)
PURPLE=(180, 0, 255)
WHITE=(0xff,0xff,0xfd)
Colors=[RED,GREEN,RED,GREEN,RED,GREEN,RED,GREEN]        #点灯例1
#Colors=[RED,YELLOW,GREEN,BLUE,RED,YELLOW,GREEN,BLUE]   #点灯例2
#Colors=[RED,ORANGE,YELLOW,GREEN,CYAN,BLUE,PURPLE,WHITE]#点灯例3
# pin0>>DIN Neopixel-8leds
np = neopixel.NeoPixel(pin0,8) # pin0を使って8個のRGB-LEDを制御
np.clear()          # 全消灯
sleep(1000)
for i in range(8):
    # まぶしいのでPWM値を調整
    pwm=[[0]*3]*8
    for s in range(3):
        pwm[i][s]=int(Colors[i][s]/5) #PWM値を1/5に
    np[i]=pwm[i]
np.show()       # 点灯表示
sleep(5000)
np.clear()      # 全消灯

実行結果

実行結果です。以下のようになりました。非常に簡単にRGB-LEDを点灯できました。
#点灯例1 [RED,GREEN,RED,GREEN,RED,GREEN,RED,GREEN]
点灯例1
#点灯例2 [RED,YELLOW,GREEN,BLUE,RED,YELLOW,GREEN,BLUE]
点灯例2

#点灯例3 [RED,ORANGE,YELLOW,GREEN,CYAN,BLUE,PURPLE,WHITE]
点灯例3

制御方法を確認

WS2812B(RGB LED)のデータシートから制御の方法を確認してみたいと思います。雰囲気だけ説明します。詳しくは「https://www.gammon.com.au/forum/?id=13357」のサイトが参考になると思います。
大雑把に言うとR、G、BのPWMデータを’0’、’1’が分かるTon時間の違う波形を送るだけです。下図を参照下さい。
例えば、RGB(204,255,0)の’204’値を送信するには、’204’は0b11001100(0xcc)を上位から順番に送ります。

「’1’なるTon時間の波形>’1’なるTon時間の波形>’0’なるTon時間の波形>’0’なるTon時間の波形>’1’なるTon時間の波形>’1’なるTon時間の波形>’0’なるTon時間の波形>’0’なるTon時間の波形」の8個を送ります。

T0H、T1H
※WS2812Bのデータシートから引用

‘0’なるTon時間の波形 T0H 400ns±150ns(220~380ns)
‘1’なるTon時間の波形 T1H 800ns±150ns(580~1000ns)※括弧内はWS2811(Driver IC)の値

G、Bも同様です。上位からG(8bit)R(8bit)B(8bit)のRGB*8個の24個(24bit)を送信します。

24bit data
※WS2812Bのデータシートから引用

ちなみにWS2811(Driver IC)のデータシートではR(8bit)G(8bit)B(8bit)の順番になっていました。どちらが正しいのか使ってみてスクリプトを修正する必要があるかもしれません。

この300nsくらいの早いTon時間の波形をmicro:bitビットで得るにはSPI通信のMOSIを使うしか無いようです。
「https://www.gammon.com.au/forum/?id=13357」のサイトを参考にしました。

‘0’なるTon時間の波形(T0H)には’0b11100000(0xe0)’、’1’なるTon時間の波形(T1H)には’0b11111100(0xfc)’をMOSI端子から送信すれば良さそうです。

したがって、先のRGB(204,255,0)の’204’値(0b11001100)の場合なら、b’\0xfc\0xfc\0xe0\0xe0\0xfc\0xfc\0xe0\0xe0’をSPI送信すれば良いことになります。

ところが上手く行かずにハマってしまいました。
不具合の原因が分からず数日、悩みました。気が付くと当たり前のことでガックシ来ました。

neopixelモジュールをインポートして使うと簡単に動作していたので、SPIの設定はそのままで動くと勝手に思い込んでました。結局、問題解決は、SPIの設定のデフォルト値の
spi.init(baudrate=1000000, bits=8, mode=0, sclk=pin13, mosi=pin15, miso=pin14)
から
spi.init(baudrate=8000000, bits=8, mode=1, sclk=pin13, mosi=pin15, miso=pin14)
にするだけでした。要するに送信レートが遅すぎただけでした。

まとめ

micro:bitのneopixelモジュールを使えば簡単にNeoPixelの制御ができました。また制御方法について簡単に記載しました。次回はモジュール無しでNeoPixelを動かしたいと思います。