※なにか気になる点がありましたらコメント欄にご記入ください。また、工作や回路を製作する場合には、細かい作業などに対して、細心の注意を払われるようお願いいたします。
【目次】
1.はじめに
前回投稿した記事でMCP3208を用いて、A/D変換のチャンネルを増やすことを試みました。
tanuki-bayashin.hatenablog.com
それは今回の取り組みと大きく関わっています。Raspberry Pi Pico(以下Picoと表記)では、自由に利用できるA/D変換のポートが3つまでだからです。
以前Neopixelに関する記事で、3つのVRでNeopixelのいろいろな要素を制御しましたが、今回はさらに2つVRを追加して新たな動きを付け加えたいと思います。
以前書いた記事:
tanuki-bayashin.hatenablog.com
2.ソースコード
今回製作したコードを以下に示します。
今までの記事で何度も取り上げて来たり、前回の記事と内容がかぶる部分は説明を省きます。
※お断り:
このソースコードの中で”cvt_col” というライブラリを使用しています。このライブラリは以下のサイトの方が制作されたものです。
#14-5 マイコン内蔵フルカラーLED ~DHAの電子工作教室~【Raspberry Pi Pico】 - YouTube
リスト1 今回のソースコード
#必用なライブラリをインポートします import array, utime, math, machine from machine import SPI, Pin, ADC import rp2 from rp2 import PIO, StateMachine, asm_pio import cvt_col # クロック周波数を180MHzにしています。(オーバークロックです) machine.freq(180_000_000) #SPI通信にてMCP3208から値を持ってくるインスタンスを生成します。 spi_sck = Pin(2) spi_tx = Pin(3) spi_rx = Pin(4) # 周波数 400kHz で SPI ペリフェラル 0 を作成 spi = SPI(0, baudrate=400000, sck=spi_sck, mosi=spi_tx, miso=spi_rx) cs = Pin(5, mode=Pin.OUT, value=1) # ピン 5 でチップセレクトを作成。 vr_data = [0.0, 0.0, 0.0, 0.0, 0.0] # 変換係数です K_speed = 6.0 / (4095) K_val = 20.0 / (4095) K_sat = 150.0 / (4095) K_x = 9.0 / (4095) K_y = 9.0 / (4095) #WS2812 のLEDの数を指定します NUM_LEDS = 100 # Pico特有のstate machine を用いてWS2812の制御を行っています # 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 SPI_read(): rxdata = bytearray(15) # 3bytes * 5 VRs vr = [] # ch0の電圧を測定 cs(0) rxdata[0] = spi.read(1, 0x06)[0] rxdata[1] = spi.read(1, 0x00)[0] rxdata[2] = spi.read(1, 0x00)[0] cs(1) utime.sleep_us(10) # ch1の電圧を測定 cs(0) rxdata[3] = spi.read(1, 0x06)[0] rxdata[4] = spi.read(1, 0x40)[0] rxdata[5] = spi.read(1, 0x00)[0] cs(1) utime.sleep_us(10) # ch2の電圧を測定 cs(0) rxdata[6] = spi.read(1, 0x06)[0] rxdata[7] = spi.read(1, 0x80)[0] rxdata[8] = spi.read(1, 0x00)[0] cs(1) utime.sleep_us(10) # ch3の電圧を測定 cs(0) rxdata[9] = spi.read(1, 0x06)[0] rxdata[10] = spi.read(1, 0xC0)[0] rxdata[11] = spi.read(1, 0x00)[0] cs(1) utime.sleep_us(10) # ch4の電圧を測定 cs(0) rxdata[12] = spi.read(1, 0x07)[0] rxdata[13] = spi.read(1, 0x00)[0] rxdata[14] = spi.read(1, 0x00)[0] cs(1) utime.sleep_us(10) for i in range(5): data = ((rxdata[i*3+1] & 0x0f) << 8) + rxdata[i*3+2] vr.append(data) return vr #ここから処理がスタートします if __name__ == '__main__': # Process arguments print('Press Ctrl-C to quit.') r_max = 4.5 * math.sqrt(2) color = 0.0 try: # 無限ループです while True: # A/D変換により値を読み取っています。 # 変数変換をしています。 vr_data = SPI_read() # speed: -3.0~3.0 speed = vr_data[0] * K_speed - 3.0 color = (color - speed) % 60.0 # v: 0~20 v = int(vr_data[1] * K_val) # s:105~255 s = int(vr_data[2] * K_sat) + 105 # center_x: 0.0~9.0 center_x = K_x * vr_data[3] # center_y: 0.0~9.0 center_y = K_y * vr_data[4] # Neopixelを同心円上に色を変化させて表示するための計算です for i in range(NUM_LEDS): # 左から何列目かを計算し、4.5を引いています x = float(i % 10) - center_x # 下から何段目かを計算し、4.5を引いています y = float(i) / 10.0 - center_y # 中心(4.5, 4.5)からの距離を求め、最大値を1に規格化しています r = math.sqrt(x*x + y*y) / r_max r_int = int(r * 40.0 + color) % 60 #それぞれのLEDの色を計算し、値を書き込んでいます (r, g, b) = cvt_col.hsv_to_rgb(r_int * 6, s, v) ar_color(i, r, g, b) # 色を表示しています sm.put(ar,8) utime.sleep_us(100) # ctl-C が押されたときの処理です except KeyboardInterrupt: #### clear ws2812b clear_all() cs(1) # ペリフェラルを選択解除。 spi.deinit()
解説:
SPI機能に関する部分を中心に見ていきます。(前回の記事も参考にしていただけるとありがたいです)
① 2行 SPI用のライブラリをimport しています。
② 11~17行 ピンを指定しSPI用のインスタンスを生成しています。
③ 18,19行 チップセレクトの指定と、読み取った値の格納用の変数を定義しています。
④ 64~111行 関数 SPI_read() の定義をしています。中を見ていきます。
65,66行 格納用と戻り値用の変数を定義しています。
67~72行 ch0のデータを読みだしています。
73行 10μsecだけ待ち時間を設けています。安全のため付け加えました。
75~105行 ch1~ch4のデータを読み取っています。
107~109行 生の値を12bitのデータに変換しています。
111行 戻り値を返しています。
⑤ 112行 メインループに入ります。
⑥ 125行 SPI_read()関数によりA/D変換の値を読み取っています。
⑦ 127~137行 読み取った値 vr_data をもとに、制御に必要な各値を計算しています。
⑧ 141~144行 この部分が、今回新たに付け加えたかった処理に当たります。center_x、center_yを中心に円は広がっていきます。
⑨ 160、161行 Ctrl‐Cが押された後の処理です。Neopixelを消した後、選択を解除し、SPIバスをオフにします。
処理の説明は以上です。
※9行目でクロック周波数を調整していますが(オーバークロック)、値を大きくしすぎるとPicoの調子が悪くなるようです。
3.制御している様子
実際に回路を動かしている様子です。
動画:
以前VR3つで制御していた(スピード、明るさ、鮮やかさ)のに付け加えて、光が広がる中心の位置が縦、横に移動しています。
(それだけだったりします)
( ゚Д゚)
ここまでお付き合い下さり、ありがとうございました。