Raspberry Pi 4Bでneopixel（WS2812B）をSPIで制御してみた

# 結論
* [adafruit-circuitpython-neopixel-spi](https://pypi.org/project/adafruit-circuitpython-neopixel-spi/)を使用する
    * 本家の[adafruit-circuitpython-neopixel](https://pypi.org/project/adafruit-circuitpython-neopixel/)はSPIに正式対応していない。
* NeoPixel_SPIのインスタンス化の際に、引数に"bit0=0b10000000"を入れる。
    * bit0はNeoPixel dataをSPI dataに変換するための値
```python
import board
import neopixel_spi
pixels = neopixel_spi.NeoPixel_SPI(
    board.SPI(),
    n = 10, # LEDテープの連結数を指定。今回は10個
    brightness=0.01,# 明るさの指定
    auto_write = True,# Trueにすることで、pixelsのlistを書き換えたら自動でLEDに伝達される。
    pixel_order=neopixel_spi.GRB,# RGBモード
    bit0=0b10000000 # デフォルト値は、0b11000000
)
pixels.fill((255, 0, 0))# LEDが真っ赤に光る。
```

# 目的と状況
* NeoPixel RGB TAPE LEDをRaspberry Piを使用して制御したい。
* GPIOの配線の都合上、SPI（[GPIO 10 (SPI0 MOSI)](https://pinout.xyz/pinout/pin19_gpio10)）を利用したい。
* 初めてラズパイでテープLEDを使用するので、ちんぷんかんぷんです🤷‍♂️
    * 勉強したことも含めて、記事を書いていきます。

# 実行環境
* 使用ラズパイ
    * Raspberry Pi 4 Model B
* OS
    * Raspberry Pi OS
* LEDテープ
    * [NeoPixel RGB TAPE LED](https://www.akiba-led.jp/product/959)
        * LEDのマイコンには、[WS2812B](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf)が使用されています。
# はまったこと
* exampleを起動した直後は正しく制御できますが、数秒経つと制御していたLEDが、明るさMAXで白色点灯します。
* この状態になると、一度スクリプトを停止して、再度実行しなければLEDの色を変更できません。

# 調べたこと
* 最初にSPI通信とはなんぞやについて調べました。
    * 以下のサイトが分かりやすかったです。
        *  [AnalogDialog SPIの基本を学ぶ](https://www.analog.com/jp/analog-dialogue/articles/introduction-to-spi-interface.html)
            *  ただし、今回はきちんとしたSPI通信はしていない（MOSIピンしか使用していない）ので、この事象に対しての直接的な解決策にはなりませんでした。
* そもそもどうやってLEDを制御しているのか？
    * こちらの公式サイトで勉強しました。
        * [NeoPixels on Raspberry Pi Over view](https://learn.adafruit.com/neopixels-on-raspberry-pi/overview)
            * PWMを用いてLEDの輝度、色などを制御しています。
            * LEDテープに使用されているマイコンによって、ON/OFFの周期が違います。
                * ↑最終的にこれは事象とは関係ありませんでした。
    * 連結通信についてはこちらがわかりやすかったです。
        * [ArduinoとシリアルLED（WS2812B）シリアルLEDの仕組み](https://101010.fun/arduino-serial-led.html#header-3)
    * SPIを使用してLEDを制御する場合のテクニックも公式サイトに掲載されていました。
        * [CircuitPython NeoPixel Library Using SPI](https://learn.adafruit.com/circuitpython-neopixels-using-spi?view=all)
            * NeoPixel dataをSPI dataに変換する際に、byte変換しています。
    * 上記から、当該現象は、送信しているデータがずれていることが原因のような気がしてきました。
# 解決策
* ついに同じ事象が発生している人を発見しました。
    * [github #25 Neopixel with SPI on Raspberry Pi OS (5.4.79)](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel_SPI/issues/25)
        * [bit0](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel_SPI/blob/caef6d0ec30f5bd85832e1942d20a8f84d6e3d40/neopixel_spi.py#L93)を指定し直せば治りました。
            * bit0はNeoPixel dataをSPI dataに変換するための値です。
                * ここの変換がうまくいっていなかったので、間違ったデータを送ってしまい、制御不能になったと思われます。
                * 環境によっては、[bit1](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel_SPI/blob/caef6d0ec30f5bd85832e1942d20a8f84d6e3d40/neopixel_spi.py#L94)も指定する必要があるかもしれません。
# まとめ
* Raspberry Pi 4Bを利用して、neopixel（WS2812B）をSPIピンで制御できました。
* 正直、SPIでやる人はあまりいないと思いますが、お役に立てれば幸いです。

# 参考
大変感謝しております。
* [AnalogDialog SPIの基本を学ぶ](https://www.analog.com/jp/analog-dialogue/articles/introduction-to-spi-interface.html)
* [NeoPixels on Raspberry Pi Over view](https://learn.adafruit.com/neopixels-on-raspberry-pi/overview)
* [ArduinoとシリアルLED（WS2812B）シリアルLEDの仕組み](https://101010.fun/arduino-serial-led.html#header-3)
*  [CircuitPython NeoPixel Library Using SPI](https://learn.adafruit.com/circuitpython-neopixels-using-spi?view=all)
* [github #25 Neopixel with SPI on Raspberry Pi OS (5.4.79)](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel_SPI/issues/25)