🦀

Arduino UNO R4 WiFi上のLEDマトリックスをRustで動かす

に公開

春先にarduino uno r4 wifiを購入してからずっと引き出しの奥で眠らせていたので、今回は久しぶりに引っ張り出してなにかしようと思い,LEDマトリックスに絵を描くプログラムをRustで書こうと思いました。(やり方の部分はほとんど日記みたいになっちゃってます🙏)

こんな感じのをRustでできるようにしました!

成果物のリポジトリ

https://github.com/Tom-game-project/arduino-uno-r4-wifi-example00/tree/LED_MATRIX

とりあえず動かせるプログラムをどっかから入手

最近ずっとRustを書きたい気分が続いていました。しかしいざやろうとして、ネットを調査しても、ChatGPT,Geminiに聞いてもRustで開発するために必要な答えがなかなか返って来ませんでした。

しばらく調査を進めるとelfmimiさんの書いたrust-rtt-sampleを発見しました。実際にクローンして動かしてみると、arduino uno r4 wifi上で動作することが確認できました。

今回は、そのコードを参考にしつつ、情報の少ないRustでの開発を自分で模索することにします。

Day1

最近,Gemini 2.5 Proが使えることになったので、geminiとおしゃべりをしつつ書いていました。(自分でミスを修正したりして、本当にすごかった...)。仕様から、LEDマトリックスへアクセスするためのレジスタを見つけて実際に出力するという素晴らしい成果を挙げてくれたのですが、そこからがどうもうまく行きません。具体的には、操作しているレジスタと実際に光るLEDの座標の関係がちぐはぐな状態でした。トークンも体力も使い果たしDay1は終了しました。

Day2

ここまでだと、「Geminiをうまく使えなかった人」で終わってしまうのでDay2はがんばります。
まず、Geminiを始めとしたAIツールは画像を含んだりしてる仕様書やデータシートを苦手としてるんじゃなかろうかということで、しかたがないので自分で一次情報に当たります。

問題になっているのは、Rustのクレートとして用意されたRA4M1用halでどんなアクセスの仕方をすれば目的の座標のLEDを操作できるのかということ。更には、複数のLEDを同時に点灯するにはどのようにすればいいのかということです。

しばらく、調べているうちに、どうやらLEDは同時についているのではなく、人の目ではわからない程の速さで高速に一つずつ表示されているということがわかりました。

そうなると、とりあえずの目標は一つめに書いた特定の座標のLEDを一つ点灯させる関数を作成することになります。

そうして更に調べていくうちに以下のソースを発見しました!

https://docs.arduino.cc/resources/schematics/ABX00087-schematics.pdf

どうやら、このように少し(かなり??)複雑に見えるのは、Charlieplexingという、複数のLEDを制御する際にポートの数を減らすための工夫のようです。

上のソースで特に重要なのは、1ページ目のLED MATRIXと2ページめの階段みたいな図です。
2ページ目の内容を表でまとめると以下のようになります

回路図(表中の数字は下の「LEDを表現している図」のLEDの番号に対応)

0列 1列 2列 3列 4列 5列 6列 7列 8列 9列 10列 11列 12列 13列 14列 15列 16列 17列
0 行(ROW_TOP1) 1 2
1 行(ROW_TOP2) 3 4 5 6
2 行(ROW_TOP3) 7 8 9 10 11 12
3 行(ROW_TOP4) 13 14 15 16 17 18 19 20
4 行(ROW_TOP5) 21 22 23 24 25 26 27 28 29 30
5 行(ROW_TOP6) 31 32 33 34 35 36 37 38 39 40 41 42
6 行(ROW_TOP7) 43 44 45 46 47 48 49 50 51 52 53 54 55 56
7 行(ROW_TOP8) 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
8 行(ROW_TOP9) 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
9 行(ROW_TOP10) 91 92 93 94 95 96

LED MATRIXを表現している図

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

2ページ目の階段みたいな回路図をよく見てみると、偶数奇数同士で隣り合う数のペア(例:(1,2) , (3,4), (93,94))は同じピンに接続されています。

偶奇のペアになっているLEDを一つの単位とみて下側に接続されているものをROW_BOT、上側に接続されているものをROW_TOPとします。
例えば、LEDの番号が1,2のものはROW_BOTがROW0,ROW_TOPがROW1,LEDの番号が17,18のものはROW_BOTがROW2,ROW_TOPがROW4になります。目視で確認していってもいいのですが、規則正しく並んでいるので、任意のLEDの番号と接続されているピンの関係性を調べて、できるだけラクしましょう。

結論の数式

LEDの番号を N としたとき、行番号 r と列番号 c は次のように計算できます。

  1. 行番号 r を求める

    r = \left\lfloor \frac{\sqrt{4N - 3} - 1}{2} \right\rfloor

    \lfloor x \rfloor は、床関数(x を超えない最大の整数)を表します。

  2. 列番号 c を求める

    c = N - (r(r+1) + 1)

数式の導出方法

なぜこの式で求められるのかを、ステップごとに解説します。

1. 各行の性質を分析する

まず、表の各行 r にどのような性質があるかを確認します。

  • 各行に含まれる数字の個数

    • 行 0: 2個 (=2 \times (0+1))
    • 行 1: 4個 (=2 \times (1+1))
    • 行 2: 6個 (=2 \times (2+1))
    • このパターンから、行 r には 2(r+1)の数字が含まれることがわかります。
  • 各行の最初の数字 (S_r)

    • r の最初の数字は、行 r-1 までに現れたすべての数字の総数に 1 を足したものです。
    • r-1 までの数字の総数は、2(0+1) + 2(1+1) + \dots + 2((r-1)+1) という等差数列の和で求められ、これは r(r+1) となります。
    • したがって、行 r の最初の数字 S_rr(r+1) + 1 となります。

2. 数式を組み立てる

上記の性質を利用して、数式を組み立てます。

  • r の特定
    ある数 N は、行 r の最初の数 S_r 以上で、次の行 r+1 の最初の数 S_{r+1} より小さい範囲にあります。

    S_r \le N < S_{r+1}

    先ほどの式を代入すると、
    r(r+1) + 1 \le N < (r+1)(r+2) + 1

    この不等式のうち、r(r+1) + 1 \le Nr について解くと、先に示した行番号 r を求める数式が得られます。

  • c の特定
    列番号 c は、その行の何番目に位置するかを示します(0から始まるインデックス)。これは単純に、数 N とその行の最初の数 S_r との差で計算できます。

    c = N - S_r

    S_r = r(r+1) + 1 を代入すると、列番号 c を求める数式が完成します。

LEDの番号から行番号、列番号を判明させるための式がわかったのでRustのコード(点灯させたいLEDとそれに対応するピンへのアクセスを対応させる配列)を生成するpythonのプログラムを書きます。

https://github.com/Tom-game-project/arduino-uno-r4-wifi-example00/blob/772114e298e7b77d67ccff0e8493173bff89b4a5/pins.py

ここで判明した対応関係をRustのプログラムに貼り付けて実行すると...

やったね🩷

感想

シグマなんて久しぶりに使った、、

Discussion