LEDドライバ TM1630 を使ってみる (2) LED表示

秋月電子通商で購入した「7 セグメント用 LED ドライバー IC TM1630」、データシートを一通り確認してみましたので、今回は LED 表示の動作を確認してみたいと思います。

LED 表示回路

4 桁の 7 セグメント LED が部品箱にありましたので、こいつを点灯させてみましょう。制御は Arduino NANO で行います。

LED 表示回路

Arduino との接続は DIO – D5 、CLK – D6 、STB – D7 です。データの受渡しは shiftOut() で十分でしょう。SPI はセンサーなどのために空けておきます。
Arduino の出力を入れるので、データシートの回路のようにプルアップする必要はありません。干渉軽減のためだという 100pF のコンデンサは部品箱に、ないです。特に必要もないでしょ (^_^;)

データシートにはありませんが、LED の電流制限抵抗は付けましょう。
SEG ピンに出力される電圧が 5V 、LED の順方向電圧を 2V とすると、150Ω で 20mA 流れます。実測値では約 17mA でした。

ちなみに、50Ω のときの実測値が約 36mA で、8 セグメントを全点灯させると 288mA になり、GRID ピンの最大電流 200mA を超えてしまいます。
計算上 60mA 流れるはずが実測で 36mA しか出ないのは、出力電圧がヘタっていた? 長時間やると壊れるんじゃね? それとも抵抗無しで使えるテクノロジ? データシート 11 ページの電気特性が、意味不明だけど気になる、気になる。

あ、PWM 制御してても平均電流値が下がるだけで、最大電流値は変わりませんからね。

SEG が 20mA とするとGRID に 160mA 流れます。GRID は二つ以上同時には LOW になりませんが、5V 電源は十分な容量のものを用意しましょう。電源の 100μF のコンデンサは付けたほうが良いです。無いと 5V 電源がパルスになります (;゚Д゚)
Arduino の 5V 出力を使うのはやめたほうがよいかも、です。

SEG と GRID の基本的な動作は、Arduino のスケッチ例「RowColumnScanning」と同じだと思われます。meyon’s STUDY では「Arduino スケッチ例「8×8 LED Matrix」で Lチカしてみた」で実験しています。

簡単に言うと、点灯させる SEG を HIGH にし、GRID(n) を LOW に。点灯時間 (表示制御のパルス幅) が過ぎたら GRID(n) を HIGH に戻す。次の数値の SEG を HIGH にして GRID(n+1) を LOW に。点灯時間が過ぎたら GRID(n+1) を HIGH にする。これを順番に繰り返す、ということ。
そのために、LED には 5V の逆電圧がかかる (GRID が HIGH 、SEG が LOW のとき) ことになります。LED の最大逆電圧ってだいたい 5V なもんです。壊れないのかねぇ、いまのところ壊れてないけど (^_^;)

表示制御で設定されるパルス幅ですが、この値を変更すると、GRID が LOW になっている時間が変わります。値を大きくすると時間が長くなり、LED が明るく点灯します。
実測値では、1/16 で約 30μs 、10/16 で約 300μs 、最大の 14/16 のとき約 420μs でした。周期は約 2460μs でしたので、デューティ比はそれぞれ約 1.2% 、12% 、17% となります。つまり最大時でもスタティック点灯の 1/6 の明るさってこと。20mA 流しているけど、実質 3mA 程度の明るさなのです。

LED 表示のスケッチ

4 桁 LED を点灯させるためのスケッチです。小数点も点灯できます。5 桁もいけます、はずです、たぶん。

  1. // LED Driver TM1630 v.1.1 2021.02.27 by meyon
  2. #define DISPLAY_MODE 0x00
  3. #define WRITE_REG_AUTO_ADDR 0x40
  4. #define WRITE_REG_FIXED_ADDR 0x44
  5. #define READ_KEY_SCAN 0x42
  6. #define SET_DISPLAY_ADDR 0xC0
  7. #define DISPLAY_OFF 0x80
  8. #define DISPLAY_ON 0x88 | 0x84
  9. #define STB_ENABLE LOW
  10. #define STB_DISABLE HIGH
  11. #define DOT_POSITION -1

各設定の定義です。

DISPLAY_MODE は表示モードの設定で、4 桁 8 セグメントと 5 桁 7 セグメントを選択できます。今回は 4 桁の LED ですので、0x00 を指定しています。5 桁の場合は 0x01 です。
WRITE_REG_AUTO_ADDR は、表示レジスタにデータを書き込むモードで、アドレス指定が自動インクリメントです。WRITE_REG_FIXED_ADDR は固定アドレスです。
READ_KEY_SCAN はキーデータの読み込みモードです。
SET_DISPLAY_ADDR は表示レジスタアドレスの指定を行ないます。デフォルトアドレスは 0x00 です。OR で続けてアドレスを指定します。
DISPLAY_OFF は表示を消しますが、データは変化しません。DISPLAY_ON で表示点灯、合わせて明るさ (パルス幅) の指定も行ないます。0x88 は表示オン、0x84 はパルス幅 11/16 を示します。
STB_ENABLE 、STB_DISABLE は STB の制御です。STB_ENABLE を送信してからコマンドを送り、終わったら STB_DISABLE とします。
以上が TM1630 の制御コマンドです。

DOT_POSITION は小数点の位置を指定します。0~3 で一の位〜千の位の小数点を点灯させますが、単純に点灯させているだけで、表示する数値はあくまでも 4 桁の整数です。
-1 を指定すると表示しません。4 以上とすると、小数点は表示せず、ゼロパディングになります。これはまぁ、副作用ですが (^_^;)

  1. const int dioPin = 5;
  2. const int clkPin = 6;
  3. const int stbPin = 7;
  4. const int numberOfDigits = DISPLAY_MODE & 1 ? 5 : 4;
  5. int gridData[numberOfDigits] = {0};
  6. const int digit[] = {
  7.   0b01111110, // 0
  8.   0b00001100, // 1
  9.   0b10110110, // 2
  10.   0b10011110, // 3
  11.   0b11001100, // 4
  12.   0b11011010, // 5
  13.   0b11111010, // 6
  14.   0b01001110, // 7
  15.   0b11111110, // 8
  16.   0b11011110, // 9
  17.   0b00000000, // 10:blank
  18.   0b00100000, // 11:dot
  19. };
  20. const int blank = 10;
  21. const int dot = 11;

ピン定義とか、セグメントのデータとか。
18 行は、DISPLAY_MODE 定義から桁数を取り出しています。

  1. void displayNumbers(int n) {
  2.   for (int i = 0; i < numberOfDigits; i++) {
  3.     int exponentialInDecimal = pow(10, i) + 0.5;
  4.     bool zeroSuppression = (0 != i) && (DOT_POSITION < i) && (exponentialInDecimal > n);
  5.     gridData[i] = zeroSuppression ? blank : n / exponentialInDecimal % 10 ;
  6.   }

ここが何やらわけわからんことやってますなぁ。明日には忘れそう (^_^;)
表示する整数をバラバラにして gridData[] に格納しています。ゼロサプレスもやってます。

たとえば 4 桁の整数 n から各桁の数字を取り出すには、

1 桁目 = n / 1 % 10
2 桁目 = n / 10 % 10
3 桁目 = n / 100 % 10
4 桁目 = n / 1000 % 10

なわけですから、for() 文でやるときゃ、i = 0 から始めると

(i+1) 桁目 = n / 10i % 10

となります。10i を計算したものが exponentialInDecimal です。

pow() 関数の戻り値を int にすると誤差がでますので、そのままでは exponentialInDecimal は [1, 10, 99, 999] になってしまいます。そこで 0.5 加算することで四捨五入し、[1, 10, 100, 1000] の数列を得ています。
ちなみに 100 = 1 です。0 ではありませんので、あしからず (^_^;)

zeroSuppression はゼロサプレスのフラグです。ゼロサプレスする条件は、「1 桁目でないこと」かつ「ドットポジションより大きいこと」かつ「10i より小さいこと」です。
zeroSuppression フラグが true ならばブランクを、false ならば表示する数字を digitData[] に代入します。

  1.   digitalWrite(stbPin, STB_ENABLE);
  2.   shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_MODE);
  3.   digitalWrite(stbPin, STB_DISABLE);

TM1630 へコマンドを送りましょう。

まず表示モード、4 桁を指定しています。
STB を有効 (LOW) にしてから、表示モードコマンドを送信します。終わったら STB を無効 (HIGH) にします。

  1.   digitalWrite(stbPin, STB_ENABLE);
  2.   shiftOut(dioPin, clkPin, LSBFIRST, WRITE_REG_AUTO_ADDR);
  3.   digitalWrite(stbPin, STB_DISABLE);

次はデータ設定です。
ここでは、表示レジスタ書き込み、自動インクリメント、通常モードを指定しています。

  1.   digitalWrite(stbPin,STB_ENABLE);
  2.   shiftOut(dioPin, clkPin, LSBFIRST, SET_DISPLAY_ADDR | 0x00);
  3.   for (int i = 0; i < numberOfDigits; i++) {
  4.     shiftOut(dioPin, clkPin, LSBFIRST, digit[gridData[i]]);
  5.     shiftOut(dioPin, clkPin, LSBFIRST, DOT_POSITION == i ? digit[dot] : 0);
  6.   }
  7.   digitalWrite(stbPin, STB_DISABLE);

表示レジスタ書き込みモードを指定したので、次は表示レジスタへデータを書き込みます。

55 行で表示レジスタアドレスを指定しています。自動インクリメントなので先頭アドレス 0x00 を指定しますが、デフォルトは 0x00 なのでアドレスは無くてもよいです。
ひとつの GRID に対して 2byte のデータを送ります。57 行が 1byte 目で表示させる数字のデータ、58 行が 2byte 目で小数点のデータです。これを桁数分繰り返します。

データシートの記述に相違がありましたが、動かしてみた結果、3 ページの表が正しかったです。「0」を表示するには 7EH (0b01111110) にする必要があり、6 ページの記述にある 3FH (0b00111111) は間違いです。
また、SEG1 というビットがありますが、物理的なピンは存在しません。ビットを立てても何も起きないようです。

  1.   digitalWrite(stbPin, STB_ENABLE);
  2.   shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_ON);
  3.   digitalWrite(stbPin, STB_DISABLE);
  4. }

コマンドとデータを送信したら、最後に表示をラッチします。

  1. void setup() {
  2.   pinMode(dioPin, OUTPUT);
  3.   pinMode(clkPin, OUTPUT);
  4.   pinMode(stbPin, OUTPUT);
  5. }

各ピンを出力モードに。
いつも思うけど、使ってないピンはどーするんだ? 放置はいかんだろ、放置は。

  1. void loop() {
  2.   for (int i = 0; i < 10000; i++) {
  3.     displayNumbers(i);
  4.     delay(500);
  5.   }
  6. }

0~9999 までの整数を、500ms 毎に表示させるということ。テスト用ね。
安直に delay() が使えるところが素晴らしい。

ブレッドボード

毎度のブレッドボードです。

TM1630 による LED 表示回路

解説するほどのものでもありませんが。

上の小さいブレッドボードは電源です。
下のブレッドボード、左から 4 桁 7 セグメント LED 表示器、電流制限抵抗、ドライバ IC TM1630 、Arduino NANO です。
ドライバ IC の下にある電解コンデンサが電源の 100μF です。
オシロスコープは GRID1 の出力をみています。

各部の波形

簡易オシロスコープですが、各部の波形を見てみましょう。
まずは、Arduino から TM1630 へ送っている信号の波形です。

波形 1 STB 出力

波形 1 は STB の出力です。

HIGH から最初のコマンドのために LOW になり、送信後に HIGH 。すぐに次のコマンドのために LOW になっています。
2 つコマンドを送って、3 つ目が表示レジスタへデータを送るために LOW のままになっています。

コマンドの間に待ち時間が必要かなと思っていたのですが、全然問題ないみたいですね。

波形 2 クロックパルス

波形 2 は CLK クロックパルスです。

最初のコマンドで 8 ビット送って一休み。次のコマンドも 8 ビット送って一休み。3 つ目はデータを送るためにずーっとクロックが続いています。

ちなみに、shiftOut() のクロックは、立ち上がりで動作する場合は事前に LOW にしておかなければいけませんが、これも問題ないですね。

しっかり仕事してくれているようです。

波形 3 DIO データ出力

波形 3 は DIO へのデータ出力です。

STB の波形から、コマンド送信の周期が 113μs だとわかります。
DIO 出力の最初の 113μs はすべて LOW です。これは表示モードコマンドの 0x00 を送っていることを示しています。
次の 113μs では、その期間の後の方に HIGH が現れていますが、2 つ目のコマンドで 0x40 を LSB で送っているためです。
さらに次の 113μs でアドレス指定の 0xC0 を送り、続けてセグメントのデータを送っていることがわかります。

次は TM1630 の出力を見てみます。

波形 4 GRID 出力

波形 4 は GRID ピンの出力です。LOW のときにその桁のセグメントが点灯します。

回路の説明でも書きましたが、この出力の LOW の時間を調整することで LED の明るさを変化させています。
撮影時はパルス幅 11/16 (0x84) で動かしていますが、LED がオンしている時間は 340μs とわずかなもの。デューティー比で 14% です。

HIGH レベルは 5V ですが、波打っているのがわかります。電源に 100μF の電解コンデンサを入れてこんな程度です。無いとかなり凸凹になってしまいます。
とりあえず動作に直接的な影響はありませんが、他に変な影響を及ぼさないためにも入れておきましょう。

波形 5 SEG 出力

波形 5 は SEG ピンの出力です。

幅 0.5ms のパルスが、セグメントを ON にするためのパルスです。GRID が LOW のとき、これが HIGH になるとセグメントが点灯します。

左端から約 1.5ms を過ぎたところに、幅が 150μs 程の細いパルスがあります。このパルスは 5ms 周期で出力されています。
これを起点にして、このセグメントの 1~4 桁の信号が出力されます。波形 5 では H-H-L-H となっているので、100 の位が消灯、他は点灯しているとわかります。
0.5ms の LOW をはさんで、繰り返し 4 桁分の信号が出ます。ちなみに 5 桁モードではこのパルスが 5 個になります。

幅 150μs のパルスですが、これはキースキャンのための信号です。0.5ms の間に、SEG2~SEG8 から順番にパルスが出ます。そのタイミングによってどのキーがオンになっているかを検出しています。

波形 6 セグメント印加電圧

最後は、LED のセグメントにかかっている電圧です。

ちょっと複雑な波形ですが、最高電圧が約 +2V 、最低電圧が約 -5V であることに着目して下さい。
最高電圧 (+2V) は LED の順方向電圧です。SEG が HIGH 、GRID が LOW の状態で、LED に電流が流れ、点灯します。
最低電圧 (-5V) は SEG が LOW 、GRID が HIGH の状態で、LED に逆方向の電圧がかかり、消灯しています。

それ以外の状態? 考える必要、ないです (^_^;)
興味のある人はどうぞ。なんで -2.5V なのか、とかね。

回路の説明でも書いたように、LED には -5.11V の逆方向電圧がかかっています。LED の最大逆電圧は 5V なので、定格を超えてしまっています。この程度は問題ないってことなのか、LED の選定を間違っているのか、逆方向電流が流れれば抵抗で電圧降下しますので、保護してくれるのかも。俺にはわかりません。
まぁもし問題があれば、GRID ラインにダイオードを入れるとかすればいいんじゃないでしょうか。

キースキャンデータ読み取りはどうでしょう

さて、LED 表示モードはうまく動きましたね。ちょっと気になる点もありますが、ドライバにダイナミック点灯処理を任せてしまえるメリットは大きいです。

次は、キースキャンデータ読み取りの機能を試してみましょう。操作ボタンも同時に処理できるなら、ちょっとした入出力インターフェースとして便利だと思います。

タイトルとURLをコピーしました