4桁7セグLEDモジュール(制御ChipTM1637)

2023年2月24日新規作成
2023年2月28日最終更新

今更ですが4桁7セグLEDモジュールについて調査しました。というのも時刻表示のためによく使うのですが、余り意識せず、AliExplessで0.56"のモジュールを購入しているとほぼ間違いなく上記のモジュールがヒットし購入できました。
しかしコロナ禍、電子部品不足等々でAliExplessからの購入がなかなか難しくなった頃から亜種を間違って購入してしまうことが発生しました。原因はサイトでの商品写真と実際の購入品が異なるという事が主な理由です。056"を購入したつもりが0.36"だったり、時刻分離のコロンが無かったり、ドットが全くない商品だったりと、いろいろなバリエーションがあるようです。
そして、コントローラはTM1637なのにライブラリによってドットコントロールが出来る出来ない等々の違いもあることが判ってきました。
このことから、この話題はちゃんと記録に残すべきだと感じました。

※コロン点滅/ドット点滅を共通バイナリで実現するには「Grove_4Digital_Display」のpoint(ON/OFF)を使用する事を想定していますが、ちょっと問題があるため、対策方法論を提案します。

四川?特?光?科技有限公司のサイトからピックアップしました。手元のLEDの側面に記載されている記号から、多分このメーカのモノだと判断しています。

外観 カソードコモン アノードコモン コメント
ELF-5463A ELF-5463B 0.56"高さ
ピン数6×2
ELF-5462C ELF-5462D 0.56"高さ
ピン数7×2
ELF-5461A ELF-5461B 0.56"高さ
ピン数6×2
コロン無し
ELF-4401A ELF-4401B 0.4"高さ
ピン数6×2
コロンあり
ELF-4402A ELF-4402B 0.4"高さ
ピン数6×2
コロンあり
ELF-3491A ELF-3491B 0.39"高さ
ピン数6×2
コロンあり
ELF-3461A ELF-3461B 0.36"高さ
ピン数6×2
コロンなし
ELF-3462A ELF-3462B 0.36"高さ
ピン数6×2
コロンあり
ドットなし
ELF-2481A ELF-2481B 0.28"高さ
ピン数7×2
コロンあり
ELF-2481C ELF-2481D 0.28"高さ
ピン数6×2
コロンあり

モジュールとして流通している7セグLEDは限られるようです。
0.56”モデルは5463が多いような気がします。
0.4”モデルはなかなかないです
0.39”モデルはなかなかないです
0.36”モデルは3461が多いようです。3462もあるにはあるようです。
0.28”モデルは3桁のものであれば表示用ではないもののありますね。


Arduino用のTM1637のライブラリはたくさんあります。癖のあるものもありますし、中途半端なものもあります。UNOに適用はできてもSTM32'には適用できないとかいろいろあります。

Grove_4Digital_Display #include "TM1637.h" 単純なファイル名のためバッティングします
avishorp/TM1637 #include "TM1637Display.h" 使いにくいと感じた
jasonacox/TM1637TinyDisplay #include "TM1637TinyDisplay.h" このAPIは使いやすいといえるのか?
reeedstudio/libraries/DigitalTube #include "TM1637.h" かなり古いライブラリ
TM1637 #include "TM1637Display.h" avishorp/TM1637と同じ
TM1637 Driver #include <TM1637.h> AKJ7/TM1637と同じ
RobTillaart/TM1637_RT #include "TM1637.h" APIが独自
sevensegmentTM1637 #include "SevenSegmentTM1637.h" あまり使いたいとは思わない
GyverTM1637 #include "GyverTM1637.h" Grove_4Digital_Displayがベースのようです
サンプルスケッチが興味深いです。
ロシア語というのが難点です

自分は「Grove_4Digital_Display」を使っていますが、

というメリットでメリットがあります。ただ、AVR/ESP32/STM32/RP2040で動きました。
このライブラリのAPIを確認したところ、時刻用のコロン【:】の点滅には point(TRUE) をCallすることで表示更新する度にON/OFFを繰り返すようになっています。ON/OFFするセグメントのコードは0x80です。モジュール側でこの0x80に対する配線によって、コロン表示なのかドット表示なのかという事になります。
LEDモジュールが3461のようなコロンが無いモジュールの場合、0x80は各桁のドットに割当たっているようです。
「Grove_4Digital_Display」は4桁の表示器という事も有り必ず4桁分表示更新します。その際にドットは各桁に用意されているため全て表示されてしまうようです。
しかし、4桁表示するAPI Display()が呼び出すcoding()でpointFlagがTrueなら0x80が付与されてしまいます。
2桁目のみpointFlagを有効にすれば対策出来ます。ライブラリを弄ることは問題がありますが。


Grove_4Digital_Display」を使ったまま表示部をカスタマイズする方法を検討しました。サンプルスケッチ「ClockDisplay」をレタッチしてみました。
ライブラリを弄ることは問題があります。そこでライブラリ内でprivate扱いの部分をソーススケッチに持ち出して関数を定義して使う事にしました。ドット用のセグメントは0x80のON/OFFで実現出来ます。
また、このバイナリコードのままLEDモジュールをELF-5463Bのモノに置き換えましたが、問題無くコロン表示点滅が出来るみたいです。

/*
 * 2023/02/27 T.Wanibe
 * 方法論が見つかったので記録に残す
 * 結局dot表示を制御するためには8bitのセグメント制御をするしかなさそう
 * 数字のセグメント情報はTM1637.hに記載があるので対応はできる
 * custom_display()というAPIを用意して対応する。
 * 
 */
#define          TIMER_INTERRUPT_DEBUG          1
#define         _TIMERINTERRUPT_LOGLEVEL_       4
#include        "RPi_Pico_TimerInterrupt.h"
#include        "TM1637.h"
#define         SEG_DP  0x80
#define         ON 1
#define         OFF 0
#define         TIMER0_INTERVAL_MS      1000
int8_t         TimeDisp[] = {0x00, 0x00, 0x00, 0x00};
unsigned char   ClockPoint = 1;
unsigned char   Update;
unsigned char   halfsecond = 0;
unsigned char   second;
unsigned char   minute = 0;
unsigned char   hour = 12;
static int8_t tube_tab[] = {0x3f, 0x06, 0x5b, 0x4f,
                            0x66, 0x6d, 0x7d, 0x07,
                            0x7f, 0x6f, 0x77, 0x7c,
                            0x39, 0x5e, 0x79, 0x71
                           }; //0~9,A,b,C,d,E,F
#define         CLK 18                  //pins definitions for TM1637 and can be changed to other ports
#define         DIO 19
//TM1637Display tm1637(CLK, DIO);
//GyverTM1637     tm1637(CLK, DIO);
TM1637          tm1637(CLK, DIO);
RPI_PICO_Timer  ITimer0(0);
//--------------
void custom_display(uint8_t bit_addr, uint8_t seg_data) {
        tm1637.start();
        tm1637.writeByte( 0x44 );
        tm1637.stop();
        tm1637.start();
        tm1637.writeByte(bit_addr | 0xc0);
        tm1637.writeByte(seg_data);
        tm1637.stop();
        tm1637.start();
        tm1637.writeByte(tm1637.cmd_disp_ctrl);
        tm1637.stop();
}
//--------------
bool TimingISR(struct repeating_timer *t) {
        halfsecond ++;
        Update = ON;
        if (halfsecond == 2) {
                second ++;
                if (second == 60) {
                        minute ++;
                        if (minute == 60) {
                                hour ++;
                                if (hour == 24) {
                                        hour = 0;
                                }
                                minute = 0;
                        }
                        second = 0;
                }
                halfsecond = 0;
        }
        // Serial.println(second);
        ClockPoint = (~ClockPoint) & 0x01;
        return true;
}
//--------------
void TimeUpdate(void) {
        tm1637.point(POINT_OFF);
        TimeDisp[0] = hour / 10;
        TimeDisp[1] = hour % 10;
        TimeDisp[2] = minute / 10;
        TimeDisp[3] = minute % 10;
        if (ClockPoint) {
                custom_display( 0, tube_tab[TimeDisp[0]]);
                custom_display( 1, tube_tab[TimeDisp[1]] | SEG_DP);
                custom_display( 2, tube_tab[TimeDisp[2]]);
                custom_display( 3, tube_tab[TimeDisp[3]]);
        } else {
                custom_display( 0, tube_tab[TimeDisp[0]]);
                custom_display( 1, tube_tab[TimeDisp[1]]);
                custom_display( 2, tube_tab[TimeDisp[2]]);
                custom_display( 3, tube_tab[TimeDisp[3]]);
        }
        Update = OFF;
}
//--------------
void setup() {
        tm1637.set(7);                           //0..7
        tm1637.clearDisplay();
        //Timer1.initialize(500000);//timing for 500ms
        //Timer1.attachInterrupt(TimingISR);//declare the interrupt serve routine:TimingISR
        //マイクロ秒単位の間隔
        if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 500, TimingISR)){
                if(Serial){
                        Serial.print(F("Starting ITimer0 OK, millis() = ")); Serial.println(millis());
                }
        }else{
                if(Serial){
                        Serial.println(F("Can't set ITimer0. Select another freq. or timer"));
                }
        }
}
void loop() {
    if (Update == ON) {
        TimeUpdate();
    }
}


戯言(nonsense)に戻る


問い合わせ頁の表示


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。

商標・登録商標

Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。