TM1637-LEDをライブラリーなしで簡単表示
4桁の7セグメントLEDを直接ダイナミック点灯する場合は、少なくとも11本以上のピンを使います。このTM1637というICを用いたディスプレイなら2ピンしか使わずにすみ、見やすく廉価。色々な機会にこれを使っていますが、使い方を質問されることが結構多い部品です。
外出自粛の週末ですが、おまけにすごい雨です。こういう機会に、ごく簡単ですがこれの要点と簡単なプログラムの例を書いておこうと思います。
次の写真は10年ほど前にPCの元電源オン表示の代わりとして、ちょこっと作ってみた温湿度計です。8ピンマイコンで作りましたが今でも明るく動いています。8ピンなのに温湿度センサーの他に、明るさ表示変更用のボタンなどもつけています。
TM1637というICはずいぶん前に出たものですが、最初の頃はとくにライブラリーもなく、中国語のデータシートしかなくて苦労しましたが、その後は文書類もLED用のライブラリーなども整ったので世界で普及したと思います。
第一に4桁LED+TM1637IC込みで値段が1ドル程度と圧倒的に安いし、Duty比が可変で電流消費も少なくできたり、接続やプロトコルがシンプルだったり。7セグメントが自由自在(これはあたりまえか^^;)。
とはいえ、デバイス1つ毎にIOピンを2つも(!?)使うので、本当はI2C接続のものが欲しかったわけですが、最近になってHT16K33+4桁LEDのモジュールが出回り始めたので早速注文中。単価はこの約3倍ほどします。そのうちに記事で紹介したいと思いますが。
TM1637を用いた4桁LED表示器の種類(大きさ、表示色、コロンとピリオドの有無など)は色々あります。
今eBayを覗いてみると、現時点はコロナの影響もあって送料がかかるものが多く少々高いかもしれませんが、それなりに経済的。
Arduinoとは試作では例えば次のようにつなぎます。
各7セグメント表示用のデータは次の形のバイナリー形式です。
pで示したものはピリオドだけの場合もコロンだけの場合もありますが、両方表示できても予めハードウェア回路で固定され、変更できないものが殆どです。ちなみにピリオドだけのは次のもの。写真がちょっとぼけていてすみませんが。
Arduinoでプログラムを組むときは、今ではTM1637用ライブラリー(例えばTM1637Display)が数種類揃っています。しかし、ライブラリーの使い方を確認するのが面倒なら、必要な機能だけを自分でコーディングしてもこのデバイスなら比較的単純にできます。例えば次です。
/* *********************************************************
Simple roll-your-own TM1637 operations without library
Sample sketch V00 July 25, 2020 (c) Akira Tominaga
* ********************************************************/
#define lDIO 6 // DIO for TM1637 LED
#define lCLK 7 // CLK for TM1637 LED
#define lBrt 0x02 // duty-r 1/16x 02:4,03:10,05:12,07:14
#define lTu 50 // time unit in micro-second
byte lChr; // single byte sent to LED
byte Data[] = { 0x00, 0x00, 0x00, 0x00 }; // LED initial value 0000
void setup() { // ***** Arduino setup *****
pinMode(lCLK, OUTPUT);
pinMode(lDIO, OUTPUT);
digitalWrite(lCLK, HIGH);
digitalWrite(lDIO, HIGH);
lDispData(); // display 0000
delay(1000);
}
void loop() { // ****** Arduino Loop *****
// example
for (int k = 0; k < 4; k++) {
uint8_t Rand = random(0, 15);
Data[k] = Rand;
}
lDispData();
delay(1000);
}
/**************************************
User defined functions
* *********************************** */
// *** lDispData *** display data to 4dig LED
void lDispData(void) {
#define showDat 0x40 // show this is data
#define showAd0 0xC0 // LED data addr is zero
#define showDcB 0x88+lBrt // show dCtl + brightness
lStart(); // start signal
lChr = showDat; // identifier for data
lSend(); // send it
lStop(); // stop signal
lStart(); // and restart
lChr = showAd0; // identifier for address
lSend(); // send it
for (int j = 0; j < 4; j++) { // for Data[0] to Data[3]
byte edChr = Data[j]; // set a byte to edChr for edit
switch (edChr) {
case 0x00: lChr = 0x3F; break; // 0
case 0x01: lChr = 0x06; break; // 1
case 0x02: lChr = 0x5B; break; // 2
case 0x03: lChr = 0x4F; break; // 3
case 0x04: lChr = 0x66; break; // 4
case 0x05: lChr = 0x6D; break; // 5
case 0x06: lChr = 0x7D; break; // 6
case 0x07: lChr = 0x07; break; // 7
case 0x08: lChr = 0x7F; break; // 8
case 0x09: lChr = 0x6F; break; // 9
case 0x0A: lChr = 0x77; break; // A
case 0x0B: lChr = 0x7C; break; // b
case 0x0C: lChr = 0x39; break; // C
case 0x0D: lChr = 0x5E; break; // d
case 0x0E: lChr = 0x79; break; // E
case 0x0F: lChr = 0x71; break; // F
case 0x10: lChr = 0x00; break; // blank
default: lChr = 0x7B; // e for error
}
lSend(); // send each byte continuously
} // end of four bytes
lStop(); // stop signal
lStart(); // restart
lChr = showDcB; // identifier for display brightness
lSend(); // send it
lStop(); // stop signal
}
// *** lSend *** send a charater in lChr
void lSend(void) {
#define LSb B00000001 // Least Significant bit
for (int i = 0; i < 8; i++) { // do the following for 8bits
if ((lChr & LSb) == LSb) { // if one then
digitalWrite(lDIO, HIGH); // set lDIO high
} else { // else
digitalWrite(lDIO, LOW); // set lDIO low
}
lCLKon(); // clock on to show lDIO
lChr = lChr >> 1; // shift bits to right
}
digitalWrite(lDIO, LOW); // pull down during Ack
lCLKon(); // clock on to simulate reading
}
// *** lStart *** send start signal
void lStart(void) {
digitalWrite(lDIO, LOW);
delayMicroseconds(lTu);
digitalWrite(lCLK, LOW);
delayMicroseconds(lTu);
}
// *** lStop *** send stop signal
void lStop(void) {
digitalWrite(lCLK, HIGH);
delayMicroseconds(lTu);
digitalWrite(lDIO, HIGH);
delayMicroseconds(lTu);
}
// *** lCLKon ** clock on to show data
void lCLKon(void) {
digitalWrite(lCLK, HIGH);
delayMicroseconds(lTu);
digitalWrite(lCLK, LOW);
delayMicroseconds(lTu);
}
/* End of program */
上では表示するコードを7セグに変えるためswitch-case-defaultのロジックで行っていますが、固定byte列をコード値でインデクシングしてもよいでしょうね。その場合は、インデクシング前に0x0Fでandをとるなどしてコード値を制限しないといけませんね。
こういう勝手で単純な”Roll-Your-Own"と呼ばれるプログラムは、ライブラリーの使い方を調べなくてよいのと、マイコン容量消費の最小化ができる利点があるかも^^; 上のプログラムは、pのセグメントを使う場合には適当に加工して応用いただくと良いかと思います。
この場合、CLKとDIOは次の図のように動かしています。下は0000を表示している時のロジックアナライザー表示です。簡便法としてI2Cとして分析していますが、I2Cとは逆に、TM1637での約束は各バイトを下位ビット(つまりLSb)から順に送るので、各バイトごとにビットの並びが逆転しているので注意がいります。またスレーブアドレスなどはありません。
TM1637側は受信バイトに対してAck以外の応答をしませんから、DIOピンのモードをいちいち送信から受信に切り替えなくとも、早めのタイミングで自分でLOWにすれば送信モードのまま変えずにいけます。もしLOWにするタイミングが遅すぎて、スレーブが先にLOWを出す場合は正負で引きあうので注意。もし仮にそういう間違いをしても数マイクロセカンドだけですが心配ならDIOへの接続に数百オームの抵抗をいれれば安心でしょう。上のコーディングの場合は不要です。
その他もろもろの情報が必要な場合、TM1637のDatasheetをググってご確認いただくと良いと思います。
今回は超簡単な解説記事でしたが、このへんまでにしたいと思います。TM1637をお知りになりたい方々へ、少しでもお役に立つなら幸いです。
©2020 Akira Tominaga, All rights reserved.