狭ピッチ LED バーで SPRESENSE バーサライタを堪能
以前購入したじゃじゃ馬 128セルのLEDバーを用いてバーサライタしてみましたので、もう少しだけ詳しく記載いたします。
目次
LEDバー
使用したLEDバーはAdafruitの商品で、2mm角のSPI入力LEDが128個 3mmピッチで1列に配置されています。
こちらのチップの色輝度は600HzほどのPWMで調光されるため、高速点灯が必要なバーサライタには向きません。しかし色輝度を0かMAXでのみ使えば低周波PWMの影響を受けないのでバーサライタ表示が可能となります。故に8色しか表示できませんが。。。
グローバル輝度(5bit)は電流量で調光されておりましたので今回は0~31中の最も低い輝度1で使用します。それでもかなり明るいです。
LEDのリフレッシュレート等の詳細は以下ご参照ください。
構成
LED以外は過去に製作したSPRESENSE バーサライタと同じです。
部品
- マイコン SPRESENSE
- SPRESENSE用ミニ拡張ボード KASPI001
- フォトリフレクタ QTR-1A
- 狭LEDバー
- ワイヤレスチャージモジュール
- マブチモーター RS-540SH
- レベルシフタ TXB0104
LED加工
LED 128セルを半径として回すのは、物理的、信号的にもしんどいと考えLEDバーを半分に切りました。
切ってもちゃんと動きます。LED1個は切断してなくなりましたが。。
切っても生きている pic.twitter.com/pRYIFhER1E
— HomeMadeGarbage (@H0meMadeGarbage) September 6, 2019
これで64セルと63セルのLEDバーとなりました。それぞれSPRESENSEのSPI5とSPI4に接続しました。
Arduinoコード
マルチコアプログラミングで3つのCPUを使用しました。
- Mainコア:フォトリフレクタで回転を検出して2つのSubコアに発光タイミングを送信
- Sub1, 2コア:Mainコアからのタイミングを受けてSPIテープLEDを発光
コードは以前のものとほぼ同じでLEDのセル数のみ変えています。
画像色データ生成
画像をもとに表示映像データを生成したのですが今回は前述のLED駆動の問題によりRGBそれぞれ輝度0かMAX(255)しか表示できません。
そのため元画像のRGBを取得して閾値以上でMAX、以下で0に変換して、さらにバーサライタ用に極座標変換して表示データファイルを生成しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# -*- coding: utf-8 -*- import cv2 import os import math from PIL import Image #配列設定 Frame = 14 #抽出フレーム数指定 NUMPIXELS = 128 #LEDの数 Div = 100 #1周の分割数 #色閾値 colTh = 64 #ファイル作成 file = open('graphics.h', 'w') file.write('#define Frame ' + str(Frame) + '\n') file.write('#define NUMPIXELS ' + str(NUMPIXELS) + '\n') file.write('#define Div ' + str(Div) + '\n' + '\n') #file.write('#define Frame ' + str(Frame) + '\n' + '\n') file.write('uint32_t PROGMEM pic [Frame][Div][NUMPIXELS] = {' + '\n') # Gifファイルを読み込む # 参考 https://www.tech-tech.xyz/gif-divide.html gif_file_name = "original.gif" gif = cv2.VideoCapture(gif_file_name) #画像変換関数 def polarConv(pic, i): imgOrgin = cv2.imread(pic) #画像データ読み込み h, w, _ = imgOrgin.shape #画像サイズ取得 #画像縮小 imgRedu = cv2.resize(imgOrgin,(math.floor((NUMPIXELS * 2 -1)/h *w), NUMPIXELS * 2 -1)) #cv2.imwrite(str(i) + '-resize.jpg',imgRedu) #縮小画像中心座標 h2, w2, _ = imgRedu.shape wC = math.floor(w2 / 2) hC = math.floor(h2 / 2) #極座標変換画像準備 imgPolar = Image.new('RGB', (NUMPIXELS, Div)) #極座標変換 file.write('\t{\n') for j in range(0, Div): file.write('\t\t{') for i in range(0, hC+1): #座標色取得 #参考:http://peaceandhilightandpython.hatenablog.com/entry/2016/01/03/151320 rP = int(imgRedu[hC - math.ceil(i * math.cos(2*math.pi/Div*j)), wC + math.ceil(i * math.sin(2*math.pi/Div*j)), 2]) if rP > colTh : rP = 255 else: rP = 0 gP = int(imgRedu[hC - math.ceil(i * math.cos(2*math.pi/Div*j)), wC + math.ceil(i * math.sin(2*math.pi/Div*j)), 1]) if gP > colTh: gP = 255 else: gP = 0 bP = int(imgRedu[hC - math.ceil(i * math.cos(2*math.pi/Div*j)), wC + math.ceil(i * math.sin(2*math.pi/Div*j)), 0]) if bP > colTh: bP = 255 else: bP = 0 file.write('0x%02X%02X%02X' % (rP,gP,bP)) if i == hC: file.write('},\n') else: file.write(', ') imgPolar.putpixel((i,j), (rP, gP, bP)) file.write('\t},\n\n') # スクリーンキャプチャを保存するディレクトリを生成 dir_name = "screen_caps" if not os.path.exists(dir_name): os.mkdir(dir_name) for i in range(Frame): is_success, frame = gif.read() # 画像ファイルに書き出す img_name = str(i) + ".jpg" img_path = os.path.join(dir_name, img_name) cv2.imwrite(img_path, frame) #変換 polarConv(img_path, i) file.write('};' + '\n' + '\n') file.close() |
動作
LED127セル点灯を回転スピード約530rpm で1周100分割で実現できました。1周の分解能はもう少し上がってもいいように思いますがLEDの応用性が影響しているように思います。120分割以上にすると末端のLEDの色が正確ではない色になりました。
SPI周波数はコード上38MHzにしていますが、実際の出力は不明です。高速オシロ持っていないので。。。ただ信号スピード的にはまだ余裕あるはず(たぶん)なので恐らくLED自体の応答速度かと思われます。
8色しか出ないが その味わいは深い
と 思いたい#SPRESENSE #バーサライタ pic.twitter.com/l2BVc6McqZ
— HomeMadeGarbage (@H0meMadeGarbage) September 7, 2019
8色しか出ませんが縦方向の分解能が上がったおかげで絵として鮮明な表示が可能となりました。
たいへん喜ばしい限りです。回転方向の分解能はLEDの数量減らすなどの検討が必要そうです。
引き続きPOVりたいと思います。