マイクロビット(e_26)NeoPixelをライブラリー無しで動かす(1)
モジュール(ライブラリー)無しでNeoPixelを動かす
neopixelモジュール無しでNeoPixelを動かしたいと思います。MOSIから送信して、その動作を確認したいと思います。NeoPixelの制御方法は前回のe_25記事を参照ください。※thonny-microbitのMicroPythonを使っています。
baudrate
前回のe_25記事に記したようにspiのbaudrateを変更する必要があります。
spiの初期設定値は以下の値でbaudrate=1000000(1M)です。
spi.init(baudrate=1000000, bits=8, mode=1, sclk=pin13, mosi=pin15, miso=pin14)
現在のマシンクロックを調べてみます。
>>> import machine
>>> machine.freq()
16000000 #16MHz
マシンクロックが16MHzなので、たぶんbaudrateは8MHzに変更できるだろうと思います。baudrate=8000000に変更してもとりあえずエラーは出ませんでした。
MOSI送信確認
MOSIからの送信の波形を確認してみました。確認のためのスクリプトは以下のようにしました。
3バイトb’\xff\xff\xff’を繰り返しSPI送信しているだけです。
from microbit import *
# bps を変えてOSCで波形を確認
# 1Mbbs default~8Mbbs
spi.init(baudrate=8000000, bits=8, mode=1, sclk=pin13, mosi=pin15, miso=pin14)
sleep(10)
while True:
spi.write(b'\xff\xff\xff') #これだけで測定 3バイト送信
先ず、baudrate=4000000 4MHzで確認しました。
Whileでの繰返しの周期は約95usでした。下図の先頭の3つがb’\xff\xff\xff’の送信です。
3パルスを拡大してみると、Ton≒2.2us Toff≒3.8usでした。2.2usは8bit(0xff)の送信時間なので2.2us/8=0.275us(3.64MHz)になります。大雑把には合ってる感じです。
同様に、baudrate=8000000 8MHzで確認しました。波形の写真などは省きますが約6.0MHzになりました。設定値と差が開きましたが変化はしているようです。ブラウン管のOSCなので読取り誤差が大きいのだと思います。たぶん8MHzになっていると思います。
以上のようにspiのbaudrateの値を変更すれば実際に波形が変わることが確認できました。
データシートの値
‘0’なるTon時間の波形、’1’なるTon時間の波形などの数値がWS2812B(RGB LED)とWS2811(Driver IC)のデータシートで異なっていました。
******************************************* WS2812B(RGB LED) W2811(Driver IC) T0H 250~550 220~380 nsec T0L 700~1000 580~1000 T1H 650~950 580~1000 T1L 300~600 580~1000 Treset >50us >280us *******************************************
220ns<T0H<380nsを満たすには4MHzか8MHzに設定する必要があります。
T0Hを得る 220~380nsの範囲は
計算上では
1/4MHz=250nsなので0b10000000(0x80)なら250nsで範囲内
1/8MHz=125nsなので0b11000000(0xc0)なら125×2=250nsと0b11100000(0xe0)125×3=375nsは範囲内
になります。
詳細はデータシートや「https://www.gammon.com.au/forum/?id=13357」のサイトなどを参考にしてください。
動作の実確認
実際にMOSIから送信して確認します。
接続例
接続は以下のようにしました。SPI送信のMOSIをレベルコンバータを介してNeoPixelのDINと接続します。※実用する場合には空き端子の処理をした方が良いと思います。
実体接続
MOSIから送信
MOSIからT0H、T1HのSPI送信動作の確認をしました。
spiのbaudrateを4MHZ、8MHzにした場合の動作を以下のようなスクリプトで確認しました。
from microbit import *
#
# 8Mbbs(1Mbbs default)
spi.init(baudrate=8000000, bits=8, mode=1, sclk=pin13, mosi=pin15, miso=pin14)
sleep(10)
def sendByte (b):
buf=bytearray(8)
for bit in range(8):
if (b & 0x80): # 最上位'1'の部分なら以下を送信
buf[bit]=0xfc # T1H ここの値を変えて確認する
else: # 最上位'0'の部分なら以下を送信
buf[bit]=0xc0 # T0H ここの値を変えて確認する
b <<= 1 # // shift next bit
return buf
# send G,R,B PWM Data
def pix_send(g,r,b):
buf=sendByte(g)+sendByte(r)+sendByte(b)
spi.write(buf)
pix_send(0,0,0) # g,r,b = 0 off
sleep(1000)
pix_send(0,123,0) # r pwm-ON
sleep(1000)
pix_send(0,0,0)
実行結果
buf[bit]の部分を変えて確認しました。送信するMOSIデータを変えながらLEDの点灯状態を確認しました。
ちなみに、pix_send(0,123,0)の送信値は'0'を’0xc0’、'1'を’0xfc’に置き換えた場合には以下のデータになります。
bytearray(b'
\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0
\xc0\xfc\xfc\xfc\xfc\xc0\xfc\xfc #bin(123)='0b1111011'なので
\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0')
baudrate 4MHzの場合
’0'(T0H)の送信値
0x80(250ns)、0xc0(500ns)はOFFしました。0xc0は計算外だが動作しました。
0xe0(750ns)~ はOFFしないで他色になりました。
’1'(T1H)の送信値
0xe0(750ns)、0xf0(1000ns)、0xf8(1250ns)、0xfc(1500ns)、0xfe(1750ns)、0xff(2000ns)はONしました。
0xc0(500ns)はONしませんでした。
baudrate 8MHzの場合
’0'(T0H)の送信値
0x80(125ns)、0xc0(250ns)、0xe0(375ns)、0xf0(500ns)はOFFしました。 0x80,0xf0は計算外だがオフしました。0xf8(625)~ OFFしないで不定の色になりました。
’1'(T1H)の送信値
0xf8(625ns)、0xfc(750ns)、0xfe(875ns)、 0xff(1000ns)はONしました。
0xf0(500ns)はONしませんでした。
かなり大雑把にまとめると、
’0'(T0H)が有効なTon時間は500ns以下、’1'(T0H)が有効なTon時間は500ns超えのような感じになりました。
データシートの記載ほどシビアでは無さそうな感じですが、OFFさせる’0'(T0H)の送信値が重要なようです。
結局、4MHz、8MHzでも動作するだろう’0'(T0H)の送信値は'0xc0'、’1'(T1H)の送信値は'0xfc'としました。別項で動作するのか確認したいと思います。
NeoPixelを点灯
実際にmicro:bitのSPI送信端子のMOSIを使ってNeoPixelを点灯させてみます。
先に得た’0'(T0H)の送信値は'0xc0'、’1'(T1H)の送信値は'0xfc'としました。baudrate=8000000です。
[RED,ORANGE,YELLOW,GREEN,CYAN,BLUE,PURPLE,WHITE]の順番で全点灯したいと思います。点灯するだけです。
スクリプト
スクリプトは以下のようにしました。
「neopixel_test_21_05b.py」
from microbit import *
# 8Mbbs default(baudrate=1Mbbs mode=0)
spi.init(baudrate=8000000, bits=8, mode=1, sclk=pin13, mosi=pin15, miso=pin14)
sleep(10)
# NeoPixel分のSPIデータを操作(N_LED_8xpwm3x8bit)
# 全て'0'の'0xc0'のデータ 送信するとクリア
def N_cls(num=8):
buf=bytearray(b'\xc0'*(num*3*8)) # 8個のRGB LED 'xc0' で全消
#0xc0 # bit L data
sleep(100)
spi.write(buf)
sleep(100)
return buf
# バイトデータのビットごとにMOSI用のデータを戻す
def sendByte(b):
buf=bytearray(8) # 8bit >> SPI(MOSI)Data
for bit in range(8):
if (b & 0x80): # 最上位'1'なら'0xfc'を送信
buf[bit]=0xfc
else: # 最上位'0'なら'0xc0'を送信
buf[bit]=0xc0
b <<= 1 # shift next bit return buf
# まぶしいのでPWMデータを1/dにして戻す 電流制限 d>=4?
def d_rgb(rgb,d):
r=int(rgb[0]/d)
g=int(rgb[1]/d)
b=int(rgb[2]/d)
return (r,g,b)
# 1個のLED 24bitのMOSI送信データ化 rgbの並び>送信はg,r,bの順番
def sig_sbuf(rgb): #rgb=(r,g,b)
sig_buf=sendByte(rgb[1])+sendByte(rgb[0])+sendByte(rgb[2])
sleep(10)
return sig_buf
# Color データ
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)
ALL=(255,255,255)
# LED 0 ~ 7 の色を設定
#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
N_cls()
send_buf=bytearray()
for i in range(8): # LEDの個数分
# まぶしいので d_rgb(rgb,d) でPWMデータを1/5にする
rgb=d_rgb(Colors[i],5) # 減明
send_buf+= sig_sbuf(rgb) # 8個分のMOSIデータを蓄える
print('LED ON')
sleep(100)
spi.write(send_buf)
sleep(3000) # ON 3sec
print('LED OFF')
N_cls() # OFF
実行結果
実行結果です。どうにか上手く行ったようです。
Colorsのデータは#3を使ってます。以下に点灯した写真を載せています。
ちなみにbaudrate=4000000にしても正常に動作しました。
>>> %Run neopixel_test_21_05b.py
LED ON
LED OFF
>>>
#3 [RED,ORANGE,YELLOW,GREEN,CYAN,BLUE,PURPLE,WHITE]
まとめ
micro:bitのspiのbaudrateの変更が可能で8MHzまで設定できることを確認しました。またNeoPixelに送信する’0'(T0H)や’1'(T1H)にあたるSPIの送信値を確認しました。
それらをもとにneopixelモジュール(ライブラリー)無しでNeoPixelを動かしてみました。
シフト動作や点滅など機会があれば試してみようと思います。