メモ - シリアル通信で制御するLED(NeoPixel系)をSPI+外付け回路でうまくやる方法
http://ww1.microchip.com/downloads/jp/AppNotes/00001606A_JP.pdf
このドキュメントは、現代的な8bitPICが大体持ってるSPI・PWM・CLC(色々設定可能な内蔵ロジック回路)の機能を使って、信号タイミングがシビアなシリアルLED(NeoPixelの親戚)を扱う方法が書かれている。とは言えこれってSPIの(短くしたCLK) | (CLK & DATA)
を作れればいいよねってことなので、実はロジックIC1枚で受ければできてしまうんじゃないかと思ったのでやったらうまく行ってしまった。というのがこの記事。
冒頭の記事はWS2811を対象にしているが、この記事はWS2812を対象にする(部品の定数とSPIの速度を変えれば同じ構成で動くと思うけど)。
テスト回路
- マイコンは3.3V、ロジックも3.3V、WS2812だけ5V(マイコンとロジックのレベルがあってたら同じ定数で行けるのではなかろうか)。
- CとRは、時定数を考えると
22pF * 10kΩ = 220ns
で、Hiレベルになるには63%よりはちょっと高いだろうから、おそらく妥当。 - WS2812のHigh Voltage InputのMinは2.7V、Low Voltage InputのMaxは0.7Vなので、出てくる信号が3.3Vでも動く。ただ、WS2812自体のVccは3.7Vが下なので、5Vを入れている(3.3Vでも動いちゃうことあるけどね)。
- 多分シュミットトリガなICを使うのが筋だと思うが、とりあえずそこらへんに転がってた74HC00を使った。動いたのでヨシ!
- SPIの発生源にはRaspberry Pi Picoを使用している(まあこいつ自体はPIOあるからやるとしたらそれでやれって話になるが……)。
テストプログラム
#include <hardware/spi.h> void setup() { gpio_set_function(2, GPIO_FUNC_SPI); gpio_set_function(3, GPIO_FUNC_SPI); gpio_set_function(4, GPIO_FUNC_SPI); spi_init(spi0, 800 * 1000); } void loop() { uint8_t buffer[4][3] = { { 0x0a, 0x0a, 0x0a }, { 0x00, 0x00, 0x0a }, { 0x00, 0x0a, 0x00 }, { 0x0a, 0x00, 0x00 } }; while (1) { for (int i = 0; i < 4; i++) { spi_write_blocking(spi0, buffer[i], 3); delay(500); } } }
雑なテスト。Arduino IDE 2.0→Pico Probe→テストで使ったRaspberry Pi Picoという接続をしている。オシロで波形を見るには0xaaとかが0,1交互に出て都合が良いが、LEDがめっちゃ眩しくなる。
テスト風景
- 22pFは手持ちにリードが生えているやつがなかったのでチップをピンヘッダにはんだ付けして使っている。
- WS2812Cは無理やりユニバーサル基板の切れ端に乗っけてピンヘッダを生やした。
WS2812に入ってくる信号線にオシロを当ててみた図。メモリは時間軸が1250ns、電圧が1Vになっている。0が240ns、1が610ns程度なのでデータシート上の要件は守れている。
オーバーシュートしてるのでダンピング抵抗入れたほうがいいのかな?
まとめ
- なんかできた。
- できそうだからやってみた、という記事なので実用性を問われると困る。一応貧弱なマイコンとか、冒頭のドキュメントみたいにやりたいけどSPI+CLCは割けてもPWM(というかタイマー)が割けないとかの状況で役に立ちそうだとは思う。
- 特にArduinoとかはライブラリ用意されてはいるが、アセンブリ書いてなんとかやってるみたいなところあるし、そもそも制御だけでいっぱいいっぱいになるから、そうならないSPI方式はありかも。
- なんかこう、NAND1枚でできたり、22pF+10KΩという定番定数が使えたりするのすごく都合が良くてビビった。まさかそこまで考えて作られているんだろうか?
- チップセレクトもうまく組み込めたら良かったけどなぁ……