稼働中

マイクロビット(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’の送信です。

b'\xff\xff\xff'送信

3パルスを拡大してみると、Ton≒2.2us Toff≒3.8usでした。2.2usは8bit(0xff)の送信時間なので2.2us/8=0.275us(3.64MHz)になります。大雑把には合ってる感じです。

3パルスを拡大

同様に、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]
NeoPixelを点灯

まとめ

micro:bitのspiのbaudrateの変更が可能で8MHzまで設定できることを確認しました。またNeoPixelに送信する’0'(T0H)や’1'(T1H)にあたるSPIの送信値を確認しました。
それらをもとにneopixelモジュール(ライブラリー)無しでNeoPixelを動かしてみました。
シフト動作や点滅など機会があれば試してみようと思います。