0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

IchigoJamAdvent Calendar 2023

Day 3

IchigoJam と WS2812B マトリックスで「大石泉すき」

Posted at

今日は12月3日。「いずみ」と読むことができ、大石泉の日。

WS2812B

WS2812B は、フルカラーLEDである。
マイコンが内蔵されており、信号線1本だけで色の設定が可能である。
さらに、複数のWS2812Bを数珠つなぎにして、まとめて色を設定することが可能である。

WS2812B を用いた様々な製品が発売されているが、今回は以下の16×16のマトリックスを用いる。

1~5Pcs WS2812B 8x8 16x16 RGB LED Digital Flexible Individually Addressable Panel Screen WS2812 8x32 Smart Pixel Module Matrix 5V - AliExpress

同じ「16×16のマトリックス」の製品でも複数の種類がある。
今回使用するのは、文字が普通の方向になるように置いたとき、以下の順番にLEDが並んでいる製品である。

  • 最下段の一番左のLEDが最初である
  • そこから、右に向かってLEDが配置されている
  • LEDは段ごとにジグザグに配置されている。すなわち、下から奇数番目の段は左から右に、偶数番目の段は右から左にLEDが配置されている

WS2812B と IchigoJam の連携

IchigoJam (1.4系) には、WS2812B を操作する機能がある。

※IchigoJamはjig.jpの登録商標です。

BASIC コマンド

WS.LED LEDデータ数
WS.LED LEDデータ数, リピート数

配列に格納されたデータを色データとして WS2812B に送信する。
「LEDデータ数」は、配列からLED何個分のデータを読み取るかを指定する。配列の3要素でLED 1個分のデータとなるので、参照する要素数はこの数の3倍である。データは配列の先頭 ([0]) から順に読み取られる。
「リピート数」は、「LEDデータ数」で指定した数のデータを1セットとして、何セット繰り返し送信するかを指定する。これにより、少ないデータで長い WS2812B の列を制御できる。「リピート数」は省略可能で、省略した場合は1となる。

IchigoJam の配列は102要素しか無いため、このコマンドで使用可能な最大のLEDデータ数は34である。

マシン語API

void ws_led (uint32_t countrepeat, const uint8_t* data, uint32_t gpiomask);

アドレス #EE から符号なしの2バイトを読み取り、これを関数のアドレスとして呼び出す。

countrepeat は、WS2812B に送信するデータ数を指定する。「LED数」を指定する BASIC コマンドとは異なり、送信する要素数をそのまま指定する。
data は、WS2812B に送信するデータが格納された先頭アドレスを指定する。IchigoJam の配列の要素は2バイトだが、ここで指定するデータは1バイトで1要素を表す。
gpiomask は、送信先のポートを表すデータを指定する。LED端子に出力する場合は、以下で定義された GPIO_LED を指定する。

#define GPIO1 0x50010000
#define GPIO_LED	(GPIO1 + (1 << (5 + 2))) // LED

IchigoJam の VRAM は 32×24=768 バイトあるので、これを利用して LED 256個の色情報を個別に設定できる。(3×256=768)

「大石泉すき」を描くプログラム

16×16の WS2812B マトリックスに「大石泉すき」をスクロールさせて表示するプログラムを作成した。

まずは画面に表示する

10 ' オオイシイズミ スキ (ガメン)
20 CLV:LET[0],0,64,64,64,64,8190,192,448,352,800,560,1552,3096,6156,4102,0,0,0,0,8190,384,192,96,48,8184,4140,4134,4128,4128,8160,0,0,0,256,128,8188,4100,8188,4100,8188
30 LET[40],128,3324,960,416,664,1156,6336,0,0,128,128,4092,128,128,240,144,144,240,128,192,96,48,16,0,0,32,96,3904,508,128,7296,2044,256,256,256,388,12,24,8176,0
40 CLT:T=0:FOR I=-1 TO 4:FOR O=0 TO 15:CLS
50 IF I<0 GOTO 90
60 FOR X=0 TO 15-O:FOR Y=0 TO 15
70 IF ([16*I+Y]>>(O+X))&1 LOCATE 8+X,4+Y:PRINT "*";
80 NEXT:NEXT
90 IF O=0 OR I=4 GOTO 130
100 FOR X=16-O TO 15:FOR Y=0 TO 15
110 IF ([16*I+16+Y]>>(X-(16-O)))&1 LOCATE 8+X,4+Y:PRINT "*";
120 NEXT:NEXT
130 T=T+10
140 X=T-TICK():LOCATE 0,0:PRINT X:IF X>0 WAIT X
150 NEXT:NEXT
160 GOTO 40

20~30行目で、「大石泉すき」のデータを定義する。
それぞれの文字を16×16ピクセルで表す。
1行が16ビットなので、ちょうど配列の1要素で表すことができる。
1文字は上の行から下の行に向かう順番で表し、1行はLSBを左端、MSBを右端として表す。

40行目で、経過時間管理の初期化と文字配置のループを行う。
変数 I で、どの文字を表示するかを決める。
変数 O で、文字をどの位置に表示するかを決める。

50~80行目で、左側の文字の描画を行う。
スクロールさせるため、基本的に文字は2個を表示する。
変数 O に従って表示する文字の範囲を決定し、表示する。

90~120行目で、右側の文字の描画を行う。
条件やデータの位置は異なるが、基本的に50~80行目と同じである。

130~140行目で、画面の更新間隔を制御する。
130行目で「このフレームの終了時、経過しているべき時間」を計算する。
140行目で、130行目で計算した時間になるようにウェイトを入れる。

150~160行目で、40行目のループを回す。


OneFiveCrowdで実行すると、画面上に「大石泉すき」が流れていく。

WS2812B マトリックスに表示する

10 ' オオイシイズミ スキ (WS.LED)
20 CLV:LET[0],0,64,64,64,64,8190,192,448,352,800,560,1552,3096,6156,4102,0,0,0,0,8190,384,192,96,48,8184,4140,4134,4128,4128,8160,0,0,0,256,128,8188,4100,8188,4100,8188
30 LET[40],128,3324,960,416,664,1156,6336,0,0,128,128,4092,128,128,240,144,144,240,128,192,96,48,16,0,0,32,96,3904,508,128,7296,2044,256,256,256,388,12,24,8176,0
40 POKE#8A0,15,0,20,15,0,20,15,0,20,2,20,2,5,15,0:POKE#700,63,161,128,49,128,49,3,32,0,2,2,74,238,35,27,136,24,71,0,0,128,0,1,80
50 CLT:T=0:FOR I=-1 TO 4:C=#8A0+3*I:FOR O=0 TO 15:CLS
60 IF I<0 GOTO 100
70 FOR Y=0 TO 15:M=[16*I+Y]>>O:FOR X=0 TO 15-O:IF Y%2 THEN U=X ELSE U=15-X
80 IF M>>X&1 COPY#BD0-48*Y+3*U,C,3
90 NEXT:NEXT
100 IF O=0 OR I=4 GOTO 140
110 FOR Y=0 TO 15:M=[16*I+16+Y]:FOR X=16-O TO 15:IF Y%2 THEN U=X ELSE U=15-X
120 IF M>>(X+O-16)&1 COPY#BD0-48*Y+3*U,C+3,3
130 NEXT:NEXT
140 Z=USR(#700)
150 T=T+90
160 X=T-TICK():PRINT X:IF X>0 WAIT X
170 NEXT:NEXT
180 GOTO 50
マシン語のソースコード
R1 = @ARRAY
R1 += #80
R1 += #80
R0 = 3
R0 = R0 << 8
R2 = [@GPIO_LED]L
R3 = #EE
R3 = [R3]W
GOTO R3

ALIGN 4,0,0
@GPIO_LED
DATAL #50010080

ORGR #100
@ARRAY

「大石泉」を大石泉をイメージした青色で表示し、「すき」をニューウェーブの他の2人をイメージした赤色と黄色で表示するようにした。
画面に描画するプログラムと比べて、以下の点を変更している。

  • 配列の文字データの後に、LEDに設定する色のデータを追加した。各文字についてそれぞれ3バイトの色情報が並んでいる。
  • データを配置する位置を今回のLEDマトリックスの仕様に合わせて計算して配置するようにした。
  • 高速化のため、同じ計算の繰り返しを避けて変数に格納するようにした。
  • データを WS2812B に送信するためのマシン語コードを追加し、呼び出すようにした。

動くことは動くが、実機 (IchigoJam Q、1.4.3) でビデオ出力なし (VIDEO 0) のとき1フレームの描画に最大1.5秒弱かかる程度に低速である。

データの配置もマシン語で行う

10 'オオイシイズミ スキ(WS.LED マシンゴ)
20 CLV:LET[0],0,64,64,64,64,8190,192,448,352,800,560,1552,3096,6156,4102,0,0,0,0,8190,384,192,96,48,8184,4140,4134,4128,4128,8160,0,0,0,256,128,8188,4100,8188,4100,8188
30 LET[40],128,3324,960,416,664,1156,6336,0,0,128,128,4092,128,128,240,144,144,240,128,192,96,48,16,0,0,32,96,3904,508,128,7296,2044,256,256,256,388,12,24,8176,0,15,3860,5120,15,276,276,3845
40 POKE#700,240,180,114,160,6,138,135,139,54,178,61,165,160,53,173,25,173,25,173,25,0,36,58,160,113,1,64,24,0,25,0,25,0,35,54,66,0,212,3,136,0,34,4,46,0,218,2,140,18,4,155,24,251,64,15,34,1,33,11,66
50 POKE#73C,28,208,16,70,12,66,1,208,64,66,15,48,48,33,97,67,73,66,9,24,9,24,9,24,3,32,0,2,208,48,9,24,40,160,9,24,40,70,186,66,0,218,192,28,148,70,2,120,10,112,66,120,74,112,130,120,138,112,98,70
60 POKE#778,91,8,82,30,220,218,100,28,15,44,200,221,30,161,128,49,128,49,3,32,0,2,2,74,238,35,27,136,240,188,24,71,128,0,1,80
70 CLT:T=0:FORI=-1TO4:FORO=0TO15:CLS:Z=USR(#700)
80 T=T+10
90 X=T-TICK():?X:IFX>0WAITX:NEXT:NEXT:GOTO70
マシン語のソースコード
' R7 : O
' R6 : I
' R5 : 色データのアドレス (#8A0 + 3*I)
PUSH {R4,R5,R6,R7}
R0 = @VARS
R6 = [R0 + 8]W
R7 = [R0 + 14]W
R6 = SXTH(R6)
R5 = @ARRAY
R5 += #A0
R5 = R5 + R6
R5 = R5 + R6
R5 = R5 + R6
' R4 : Y
' R3 : 画像データ (≒M)
R4 = 0
@Y_LOOP
' 行の画像データを取得する
R0 = @ARRAY
R1 = R6 << 5
R0 = R0 + R1
R0 = R0 + R4
R0 = R0 + R4
R3 = 0
R6 & R6
IF MI GOTO @NO_FIRST_PART
R3 = [R0 + 0]W
@NO_FIRST_PART
R2 = 0
R6 - 4
IF GE GOTO @NO_SECOND_PART
R2 = [R0 + 16]W
@NO_SECOND_PART
R2 = R2 << 16
R3 = R3 + R2
R3 >>= R7
' 行の画像データをVRAMに反映する
' R2 : 15-X
R2 = 15
@X_LOOP
R1 = 1
R3 & R1
IF EQ GOTO @NO_PUT_DOT
' R0 : U
R0 = R2
R4 & R1
IF EQ GOTO @NO_REVERSE_X
R0 = -R0
R0 += 15
@NO_REVERSE_X
' R1 : #BD0-48*Y+3*U
R1 = 48
R1 *= R4
R1 = -R1
R1 = R1 + R0
R1 = R1 + R0
R1 = R1 + R0
R0 = #03
R0 = R0 << 8
R0 += #D0
R1 = R1 + R0
R0 = @ARRAY
R1 = R1 + R0
' R0 : C / C+3
R0 = R5
R2 - R7
IF GE GOTO @NO_SHIFT_COLOR
R0 = R0 + 3
@NO_SHIFT_COLOR
' 色データをコピーする
R12 = R2
R2 = [R0 + 0]
[R1 + 0] = R2
R2 = [R0 + 1]
[R1 + 1] = R2
R2 = [R0 + 2]
[R1 + 2] = R2
R2 = R12
@NO_PUT_DOT
R3 = R3 >> 1
R2 = R2 - 1
IF GE GOTO @X_LOOP
R4 = R4 + 1
R4 - 15
IF LE GOTO @Y_LOOP
' VRAM上の画像データをLEDに転送する
R1 = @ARRAY
R1 += #80
R1 += #80
R0 = 3
R0 = R0 << 8
R2 = [@GPIO_LED]L
R3 = #EE
R3 = [R3]W
POP {R4,R5,R6,R7}
GOTO R3

ALIGN 4,0,0
@GPIO_LED
DATAL #50010080

ORGR #100
@ARRAY
ORGR #1CC
@VARS

データを配置する処理もマシン語で行うようにし、BASICでは描画位置とタイミングの管理のみを行うようにした。
マシン語では、レジスタが32ビットあることを活かし、2文字分のデータをまとめて取得して処理するようにした。
更新間隔を調整しやすいよう、タイミングを決定する80行目 T=T+10 は独立した行にした。
実機 (IchigoJam Q、1.4.3) で1フレームの処理に1/60秒以下しかかからず、高速で処理できるようになった。

OneFiveCrowdで実行する

ライセンス

今回のプログラムは、CC BY 4.0 でライセンスする。
このプログラム (改造したものを含む) を公開の場で利用する際は、出典を示していただけると嬉しい。
これは、Qiitaの利用規約に基づくプログラムの利用を妨げるものではない。

結論

IchigoJam のマシン語APIを利用することで、BASICコマンドで制御できるより多くの WS2812B LED を制御し、マトリックスに「大石泉すき」を表示することができた。
大石泉すき。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?