※なにか気になる点がありましたらコメント欄にご記入ください。また、工作や回路を製作する場合には、細かい作業などに対して、細心の注意を払われるようお願いいたします。
目次
1.はじめに
Adafruit 社から販売されているNeopixel というフルカラーLEDを多数つなげた製品があります。
今回はこれを利用して色相を変化させて表示してみました。
訂正 rev.1 2023/05/29
その1:プログラムのバグを発見し、修正しました。
具体的内容~リスト1内、71-72行 変数の型が合っていませんでした。
同87行 コメント文がただの文字になっていました。
その2:adafruite が提供しているのはPython ではなく、circuitPythonでした
訂正 rev.2 2023/08/20
リスト1の42行目のコメント文の手前に全角スペースが入っていました。
2.回路構成
図1に今回用いた回路の実体配線図を示します。制御にはRaspberry Pi Pico(以下Picoと表記します) を利用しました。すでに発表されてから2年ほど経っているので、よく知られているマイコンかと思います。(以下参照)
Buy a Raspberry Pi Pico – Raspberry Pi
Picoの電源は39番ピンで1.8~5.5Vの範囲内で動作します。GNDは合計8本ありますが、どれか1本つないでおけば動くかと思います。
また、今回はNeopixelへの出力ピンをGP16(物理的には21番ピン)に指定しました。
※Pico内にてコードのファイル名を "main.py" として電源をつないだ場合、このプログラムが動作を始めます。
Neopixelの配線は電源(3.5~5.5V)とGNDと信号線の3本です。安全のため2.2Ωの抵抗を電源ラインに入れてあります。信号線はPicoの21番の端子につなげました。
3.コード
Pico内部のコードをリスト1に示します。
リスト1 main.py
#必用なライブラリをインポートします import array, utime, math from machine import Pin import rp2 from rp2 import PIO, StateMachine, asm_pio #WS2812 のLEDの数を指定します NUM_LEDS = 100 # Pico特有のstate machine を用いてWS2812の制御を行っています #このあたりの処理はリスト1の下のリンク先の図書より引用しました # PIO State Machine to display ws2812 @asm_pio(sideset_init=PIO.OUT_LOW, out_shiftdir=PIO.SHIFT_LEFT, autopull=True,\ pull_thresh=24) def ws2812(): T1 = 2 T2 = 5 T3 = 3 label("bitloop") out(x, 1) .side(0) [T3 - 1] jmp(not_x, "do_zero") .side(1) [T1 - 1] jmp("bitloop") .side(1) [T2 - 1] label("do_zero") nop() .side(0) [T2 - 1] # Create the StateMachine with the ws2812 program, outputting on Pin(16)(). sm = StateMachine(0, ws2812, freq=8000000, sideset_base=Pin(16)) # start the StateMachine, it will wait for data on its FIFO. sm.active(1) # Display a pattern on the LEDs via an array of LED RGB values. ar = array.array("I", [0 for _ in range(NUM_LEDS)]) #position の位置のLEDをred, green, blue の値にて指定します def ar_color(position, red, green, blue): ar[position] = (green<<16) + (red<<8) + blue #全てのLEDを消灯する関数です def clear_all(): for i in range(NUM_LEDS): ar[i] = 0 sm.put(ar,8) # 色をカラフルに表示するための仕掛けが(ちょこっと)秘められています def triangle(x): if x < 20.0: y = int(1.5 * x) elif x < 40.0: y = 60 - int(1.5 * x) else: y = 0 return y # 6個のリストのメモリーを確保しています red = [0.0 for i in range(NUM_LEDS)] green = [0.0 for i in range(NUM_LEDS)] blue = [0.0 for i in range(NUM_LEDS)] y_red = [0.0 for i in range(NUM_LEDS)] y_green = [0.0 for i in range(NUM_LEDS)] y_blue = [0.0 for i in range(NUM_LEDS)] #ここから処理がスタートします if __name__ == '__main__': # Process arguments print('Press Ctrl-C to quit.') # Neopixelを同心円上に色を変化させて表示するための計算です r_max = math.sqrt(4.5 * 4.5 * 2) for i in range(NUM_LEDS): x = float(i % 10) - 4.5 # 左から何列目かを計算し、4.5を引いています y = float(i) / 10.0 - 4.5 # 下から何段目かを計算し、4.5を引いています # 中心(4.5, 4.5)からの距離を求め、最大値を1に規格化しています r = math.sqrt(x*x + y*y) / r_max # 同心円状に色を変えて表示するための処理です。20、0、40と値をずらしています red[i] = (r * 40 + 20.0) % 60.0 green[i] = (r * 40 + 0.0 ) % 60.0 blue[i] = (r * 40 + 40.0) % 60.0 try: # 無限ループです while True: #それぞれのLEDの色を計算し、表示しています for i in range(NUM_LEDS): # 1ステップ前から色を1つずらします red[i] = (red[i] - 1.0) % 60 green[i] = (green[i] - 1.0) % 60 blue[i] = (blue[i] - 1.0) % 60 # 関数triangle() により色を計算しています y_red[i] = triangle(red[i]) y_green[i] = triangle(green[i]) y_blue[i] = triangle(blue[i]) # 色を表示しています ar_color(i, y_red[i], y_green[i], y_blue[i]) sm.put(ar,8) utime.sleep_us(100) utime.sleep_us(1) # ctl-C が押されたときの処理です except KeyboardInterrupt: #### clear ws2812b clear_all()
・Raspberry Pi Pico の解説本です。巻末の付録にWS2812Bへの応用として10-29行目の処理が載っていました。(自分は理解できませんでした)
※詳しくは知らないのですが、 Adafruit 社からもNeopixel のためのPython circuitPython で利用できるライブラリが公開されているようです。
www.raspberrypi.com
3.1 コードの解説
以下に上のコードについて解説します。
10 ー29行:
WS2812B(Neopixel のうちの一つ)を制御するための一連の処理。Pico独自のState Machine という機能を用いています。※筆者はこの部分は理解していないです。
35ー36行:
関数 ar_color() の定義です。
Neopixel の番号と色を指定しています。番号は左下が0番、そのまま右に行って9番まで進み、ひとつ上の段に行くと一番左が10番・・・というようにして一番右上に行くと99番といった具合です。
色は赤、緑、青にて指定します。それぞれ0-255までの値で指定できます。
39-43行:
関数clear_all() の定義です。全てのLEDをOFFの状態にします。
45―52行:
関数 triangle() を定義しています。図2にグラフを示します。(ちょっとした工夫があります。後述します)
64行目の If 文、83行目のtry文、107行目の except 文:
これらの組み合わせにより、ctlーCを押すことによりプログラムを終了することができます。
69ー80行:
同心円状に色が異なる模様の初期値を計算し、それぞれのRGBの値を変数red[i]、green[i]、 bulue[i] に代入しています。
色を変えるのにちょっとした小技を使っているので、後ほど説明します。
85-104行:
メインループの処理です。色を指定する変数red[i]、green[i]、blue[i]から1を引き60の割ったときのあまり(剰余)を代入しています。
・101、102行 ~ i 番目のLEDにrgbを設定し、表示しています。
・103、104行~ 待ち時間を100μsec と1μsec 設けています。100μsec 空けておかないとNeopixel の表示がおかしくなるようです。
107行以降:
ctl-Cが押されたときの処理です。全てのLEDを消灯します。
3.2 色を変化させる処理
まず図2で示す関数を用意します。便宜上 triangle() と書いておきます。このとき、初期値として変数red に20.0、green に0.0、blue 40.0を与えます。すると初期の段階では図3で見ると、x=0の状態になります。
コード内の変数で表現すると、y_red がmax でy_green がこれから大きくなり、y_blue は少しの間0のままになります。つまりこの段階ではLEDは赤く光ります。
次にこれらの変数に同じ値を加え続けると、図3のグラフの右の方に移行していきます。なので、LEDの色は赤と緑が等しくなる(=黄色)のを通り過ぎ、緑になります。
さらに時間が経過すると、さらにグラフの右側に進み、シアン、青、マゼンタと徐々に色を変えていき、右端では元の赤に戻るという仕掛けです。
以上が自分が考えた色を変える仕掛けです。(大したことない)