IchigoJam の I2C 機能で TM1637 を制御し、「検温ごっこ」

[IchigoJam](https://ichigojam.net/) の I2C 機能を用いて TM1637 を制御し、LEDを点灯させてみた。
さらに、これを利用して「検温ごっこ」をやってみた。

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

# TM1637 の制御

## TM1637 とは

**TM1637** は、シリアル通信経由でLEDマトリクスのドライブおよびキーマトリクスの読み込みを行うことができるICである。
たとえば、[aitendo](https://www.aitendo.com/product-group/61?division=4) でIC単体および各種モジュールが販売されている。

今回は、[4桁7セグLED表示器キット - aitendo](https://www.aitendo.com/product/18648) を使用する。

## TM1637 との通信

TM1637 との通信は、I2C 通信に似ているが、I2C とは以下の2点が大きく異なる。

* I2C ではMSBからLSBの順に送受信をするが、TM1637 では**LSBからMSBの順**で送受信をする。
* I2C ではスタートコンディションの送信後最初に通信を行う周辺機器のアドレスと読み/書きの別を送信するが、TM1637では**最初からコマンドを送信する**。

そのため、I2C の機能を用いて TM1637 との通信を行うときは、

* ソフトウェア側でビットの順番を反転させ、送信する
* アドレスとしてコマンドを送る

ことになる。

今回は、以下のコマンドを用いる。
(詳しいコマンドの定義は、データシートを参照してほしい。データシートは[秋月電子通商](https://akizukidenshi.com/catalog/) (I-16679) やaitendoの商品ページなどに掲載されている)

ここでは、I2C の機能で用いるため、コマンドをデータシートの表記とは反転して掲載する。

|コマンド|意味|
|---|---|
|`0b00000010`|・LEDドライブ用のデータをTM1637に送信する<br>・アドレスを自動で進める<br>・(テストではなく)通常モードで動作させる|
|`0b00000011`|LEDドライブ用のデータを書き込むアドレスを C0H (先頭) に設定する|
|`0b11110001`|パルス幅 14/16 でLEDを点灯させる|

IchigoJam の I2C の機能では、コマンド(ここに掲載した反転表記)の上位7ビットをアドレスとして数値で指定し、下位1ビットを読み/書きの別として送信する。
下位1ビットが 0 のときは書き、1 のときは読みとして送信する。

## TM16337 モジュールの制御

たとえば、以下のプログラムにより、TM1637 モジュールのLEDを点灯させることができる。

```
10 ' ｱﾄﾞﾚｽ ｾｯﾄ
20 X=I2CR(`0000001,0,0)
30 ' ﾃﾞｰﾀ ｾｯﾄ
40 POKE #700,#80,#C0,#E0,#F0
50 X=I2CW(`0000001,#700,4)
60 ' LED ﾃﾝﾄｳ
70 X=I2CR(`1111000,0,0)
```

IchigoJam と TM1637 モジュールを以下のように接続し、プログラムを実行する。

|IchigoJam|TM1637 モジュール|
|:-:|:-:|
|3.3V or VCC|VCC|
|GND|GND|
|SDA|DIO|
|SCL|CLK|

TM1637 モジュールの IchigoJam の 5V に接続してもよい。5Vに接続したほうがLEDが明るく点灯する。

このプログラムを実行すると、モジュールのLEDは以下のように点灯した。

![モジュールの点灯状況](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/07bfcc31-7171-9277-8ca0-1881d392f889.jpeg)


送信したバイト中の「1」のビットの数と、点灯したLEDの数の対応より、このモジュールのLEDは**左から順に、送信したデータの順番に対応している**ことがわかる。

また、4個のLEDとビットの関係がわかった。
残りのLEDとビットの関係を調べるため、送信するデータを以下のように書き換えて実行する。

```
40 POKE #700,#08,#04,#02,#01
```

すると、モジュールのLEDは以下のように点灯した。

![モジュールの点灯状況 2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/4f48a26b-4f05-912d-f721-0d4c478cd7bd.jpeg)

よって、このモジュールのLEDと IchigoJam の I2C 機能で送信するデータのビットとの関係は、以下のようになっていることがわかる。

![セグメントとデータの対応](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/d671838d-8e2b-fbce-7f30-610540feec0e.png)

## キー状態の読み込みは難しそう

TM1637 の LED ドライブの制御は IchigoJam の I2C 機能を用いてできたが、キー状態の読み込みは難しそうである。
キー状態を読み込むためのコマンドはMSB (最後に送信するビット) が 0 であるが、これは I2C では書き込みを意味するため、読み込みを行うことができない。
さらに、データを読み込めたとしても、データの読み込み後に TM1637 側で ACK の送信が行われる。
I2C では ACK を送信するのはデータを読み込んだ側なので、ここでも衝突が起こることが予想される。

以下のプログラムにより、キー状態を読み込むコマンドを発行した後、アドレスを設定するコマンドによりデータの読み込みを試みた。

```
10 X=I2CW(`0100001,0,0)
20 L=-1:Y=-1
30 X=I2CR(`0000001,#700,1)
40 K=PEEK(#700)
50 IF X<>Y PRINT "X=";X:Y=X
60 IF K<>L PRINT HEX$(K,2):L=K
70 GOTO 30
```

しかし、結果は失敗だった。
このプログラムを実行すると、

```
X=1
80
```

と出力された。
`X=1` となっているので `I2CR` の実行に失敗し、点灯実験で `#700` に書き込んだデータがそのまま残っている。

さらに、TM1637 モジュールの LED が以下のように点灯した。

![キー読み込み失敗時の点灯](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/b852ece6-c9c0-4e4e-1f0c-4e17b669a840.jpeg)

[ロジックアナライザ](https://www.switch-science.com/products/7642)で通信を確認すると、以下の波形が観測できた。

![キー読み込み失敗時の波形](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/4293726a-98dd-4797-0195-16332dc4fadd.png)

ストップコンディションが無く、SCL が不自然に上がっている。
IchigoJam 側でバスの衝突が検出され、通信が中断されたと推測できる。
さらに、LED の点灯状況から、ここで送信している `#03` および `#FF` が LED ドライブ用のデータとして解釈され、表示に反映されたと考えられる。
最初の LED が全点灯しているため、より正確には、最初の `#03` は有効なアドレス設定コマンドとして解釈され、続く `#FF` 以降がデータとして解釈されているようである。

# 検温ごっこ

今回の TM1637 モジュールに加え、[LEDサイコロ基板v2 for IchigoJam](https://hello002.stores.jp/items/62a28f1e288b8069725fdf2b) を用い、「検温ごっこ」をやってみた。

LEDサイコロ基板v2の光センサに手をかざし (暗くし) て1秒待つと、ビープ音が鳴り、TM1637 モジュールに体温っぽいランダムな数値が表示される。

## プログラム

```
10 ' ｹﾝｵﾝ ｺﾞｯｺ
20 OUT 2,-1:R=0:POKE #700,0,0,0,0,0,0:LET[0],#FC,#60,#DA,#F2,#66,#B6,#BE,#E4,#FE,#F6
30 X=I2CR(#01,0,0):X=I2CW(#01,#700,3+3*!R)
40 IF R RETURN ELSE R=1:X=I2CR(#78,0,0)
50 T=700
60 IF ANA(2)<T GOTO 60
70 CLT
80 IF ANA(2)<T GOTO 60
90 IF TICK()<60 GOTO 80
100 BEEP
110 C=RND(100)
120 IF C<80 V=355+RND(15):GOTO 150
130 IF C<95 V=370+RND(10):GOTO 150
140 V=380+RND(40)
150 POKE #700,[V/100],[V/10%10]|#01,[V%10]
160 GOSUB 30
170 IF ANA(2)>=T GOTO 170
180 POKE #700,0,0,0
190 GOSUB 30
200 GOTO 60
```

このプログラムは、[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja) でライセンスする。
このプログラム (改造したものを含む) を公開の場で利用する際は、出典を示していただけると嬉しい。
これは、Qiitaの利用規約に基づくプログラムの利用を禁止するものではない。

## 実行結果例

<iframe width="560" height="315" src="https://www.youtube.com/embed/-a4k46uXrHM?si=PsQ8myadbQKNw0kN" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 解説

* 10行目：タイトル
* 20行目：入力ポート・LED表示・フォント・描画ルーチンの初期実行フラグ `R` の初期化
* 30行目：TM1637 のアドレスの設定とデータの送信
  * 最初は6バイト送信し、TM1637 のデータ全体を初期化する
  * その後は、今回のプログラムで用いる3バイトのみ送信する
* 40行目：サブルーチンとして実行している場合は戻り、最初の実行の場合は TM1637 に LED 点灯コマンドを送信する
* 50行目：光センサによる手をかざした判定のしきい値 `T` を設定する (値が大きいほど暗い)
* 60行目：光センサに手がかざされる (暗くなる) まで待機する
* 70～90行目：暗いまま1秒経ったら、次の処理に進む。1秒経つ前に明るくなったら、戻る
* 100～140行目：表示する値を決定する。まず値の範囲を選択し、その後具体的な値を決定する
* 150行目：決定した値をLED用のデータに変換する
* 160行目：変換したデータを TM1637 に送信する
* 170行目：明るくなるまで待機する
* 180～200行目：LEDの表示を消し、暗くなるまで待機する処理に戻る