アノードコモンの8セグメントLEDへ表示する(TM1637を使う)


今回はアノードコモンのLED専用ドライバーのTM1637を 使って 8セグメントLEDを表示する方法を紹介します。
このチップも電流制限機能を持っていますので、電流制限用の抵抗はまったく必要ありません。
また各LEDのON/OFFはチップ内のトランジスタが高速で行いますので、チラつきも無く非常に綺麗な表示です。


このチップは内部に6個の8Bitレジスターを持っていて、このレジスターに値を設定することでLEDをON/OFFします。
最大6桁の8セグを制御することができます。
データシートを見るとVDDのTypicalは5Vとなっていますが、3.3Vでも動きます。
3桁の8セグを使う場合は以下のようになります。
チップとの通信にRPIのSCL,SDAピンを使っていますが、完全なi2c通信ではありません。
他のピンでもOKです。
TM1637のピン番号 セグメントLEDへの接続 Raspberryへの接続(ピン番号)
1(GND)
GND
2(SEG1) Segment a

3(SEG2) Segment b
4(SEG3) Segment c
5(SEG4) Segment d
6(SEG5) Segment e
7(SEG6) Segment f
8(SEG7) Segment g
9(SEG8) Segment D.P
10(GRID6)

11(GRID5)

12(GRID4)

13(GRID3) Digit1 Common
14(GRID2) Digit2 Common
15(GRID1) Digit3 Common
16(VDD)
3.3V(*1)
17(DIO)
SDA(3)
18(CLK)
SCL(5)
19(K1)

20(K2)


(*1)
TM1637は3.3Vでも5Vでも動きますが、送信完了を判断するために、DIOピンからデータを読み込みます。
5Vで駆動すると、DIOピンから5Vが飛び込んできて、Raspberryを壊します。

このチップにはFix addressモードとAutomatic address addingモードが有ります。
Fix addressモードでは[Set data][Set address][Display data][Control display]の順番にデータを送信し、
[Set address]でデータを格納するレジスターのアドレス(0xC0から0xC5)を指定します。
ソースコードは以下の通りです。
/*
 Raspberry Pi driving the TM1637
 port from here
 https://github.com/TinyTronics/TM1637_6D
 cc -o tm1637-fix tm1637-fix.c -lwiringPi
*/

#include <stdio.h>
#include <stdint.h>
#include <wiringPi.h>

//0~9, 10=blank digit, 11=dash/minus character
static int8_t TubeTab[] = {
        0x3f, // 0
        0x06, // 1
        0x5b, // 2
        0x4f, // 3
        0x66, // 4
        0x6d, // 5
        0x7d, // 6
        0x07, // 7
        0x7f, // 8
        0x6f, // 9
        0x00, // space
        0x40  // dash(minus)
};

#define BLANK 10
#define DASH  11

int brightness;
int gpio_clk;
int gpio_dio;

#define CLK  9 //Pin#5
#define DATA 8 //Pin#3

//***** definitions for TM1637
#define ADDR_AUTO  0x40
#define ADDR_FIXED 0x44

//**** definitions for the clock point of the digit tube
#define POINT_ON  0x80
#define POINT_OFF 0x00

//**** definitions for brightness
#define BRIGHT_DARKEST 0
#define BRIGHT_TYPICAL 2
#define BRIGHTEST 7

static void bitDelay(void)
{
        //delayMicroseconds(5);
        delayMicroseconds(1);
}

//send segment signal to TM1637
static void writeByte(uint8_t wr_data)
{
        uint8_t i;
        // Sent 8bit data
        for(i=0;i<8;i++) {
                digitalWrite(gpio_clk,LOW);
                if(wr_data & 0x01)digitalWrite(gpio_dio,HIGH);//LSB first
                else digitalWrite(gpio_dio,LOW);
                digitalWrite(gpio_clk,HIGH);
                wr_data >>= 1;
                delay(1);
        }

        digitalWrite(gpio_clk,LOW);
        digitalWrite(gpio_dio,HIGH);
        digitalWrite(gpio_clk,HIGH);
        pinMode(gpio_dio,INPUT);

        // Wait for ACK
        int retry = 0;
        while(1) {
                uint8_t ack = digitalRead(gpio_dio);
                // printf("ack[%d]=%d\n", retry, ack);
                if (ack == 0) break;
                retry++;
                if (digitalRead(gpio_dio)) {
                        pinMode(gpio_dio,OUTPUT);
                        digitalWrite(gpio_dio, FALSE);
                        pinMode(gpio_dio,INPUT);
                }
        }
        pinMode(gpio_dio,OUTPUT);
        return;
}

//send start signal to TM1637
static void start(void)
{
        digitalWrite(gpio_clk,HIGH);
        digitalWrite(gpio_dio,HIGH);
        digitalWrite(gpio_dio,LOW);
        digitalWrite(gpio_clk,LOW);
}

//send end signal to TM1637
static void stop(void)
{
        digitalWrite(gpio_clk,LOW);
        digitalWrite(gpio_dio,LOW);
        digitalWrite(gpio_clk,HIGH);
        digitalWrite(gpio_dio,HIGH);
}

//convert numver tu tube data
static int8_t coding(int DispData, int DispPointData)
{
        if(DispData == 0x7f) DispData = 0x00 + DispPointData;//The bit digital tube off
        else DispData = TubeTab[DispData] + DispPointData;
        return DispData;
}

void tm1637_begin(int Clk, int Data)
{
        gpio_clk = Clk;
        gpio_dio = Data;
        pinMode(gpio_clk,OUTPUT);
        pinMode(gpio_dio,OUTPUT);
}

void tm1637_brightness(int Bright)
{
        // Set the brightness and it takes effect the next time it displays.
        brightness = 0x88 + Bright;
}

void tm1637_display(int BitAddr,int DispData,int DispPointData)
{
        int8_t SegData;
        SegData = coding(DispData, DispPointData);
        start();
        writeByte(ADDR_FIXED);
        stop();
        start();
        writeByte(BitAddr|0xc0);
        writeByte(SegData);
        stop();
        start();
        writeByte(brightness);
        stop();
}

int main(void) {
        if(wiringPiSetup() == -1) return 1;
        // clock & data pin
        tm1637_begin(CLK, DATA);

        // You can set the brightness level from 0(darkest) till 7(brightest)
        // BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
        tm1637_brightness(BRIGHT_TYPICAL);

        // Clear all segment
        tm1637_display(2, BLANK, POINT_OFF);
        tm1637_display(1, BLANK, POINT_OFF);
        tm1637_display(0, BLANK, POINT_OFF);
        delay(500);

        int number;
        int digit;
        for (number=0;number<12;number++) {
                for (digit=0;digit<3;digit++) {
                        // We send the entire array to the display along with the points array
                        tm1637_display(digit, number, POINT_OFF);
                        // Execute this loop every 300 milliseconds
                        delay(100);
                }
                delay(500);
        }

        tm1637_display(2, 3, POINT_OFF);
        tm1637_display(1, 2, POINT_OFF);
        tm1637_display(0, 1, POINT_OFF);
        delay(500);

        tm1637_display(2, 3, POINT_ON);
        tm1637_display(1, 2, POINT_ON);
        tm1637_display(0, 1, POINT_ON);
}

Automatic address addingモードでは[Set data][Set address][Display data][Display data]----[Display data][Control display]の順番にデータを送信し、
最初のアドレスへのデータの格納が終了すると、自動的に次のアドレスに移動してくれます。
つまり、アドレスに0xC0を指定して3バイトのDisplayDataを送信すると、0xC0→0xC1→0xC2の順番にデータが格納されま す。
この機能を利用すると疑似的なスクロール表示を行うことができます。
ソースコードは以下の通りです。
/*
 Raspberry Pi driving the TM1637
 port from here
 https://github.com/TinyTronics/TM1637_6D
 cc -o tm1637-auto tm1637-auto.c -lwiringPi
*/

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <wiringPi.h>

//0~9, 10=blank digit, 11=dash/minus character
static int8_t TubeTab[] = {
        0x3f, // 0
        0x06, // 1
        0x5b, // 2
        0x4f, // 3
        0x66, // 4
        0x6d, // 5
        0x7d, // 6
        0x07, // 7
        0x7f, // 8
        0x6f, // 9
        0x00, // space
        0x40  // dash(minus)
};

#define BLANK 10
#define DASH  11

uint8_t brightness;
uint8_t gpio_clk;
uint8_t gpio_dio;

#define CLK  9 //Pin#5
#define DATA 8 //Pin#3

//***** definitions for TM1637
#define ADDR_AUTO  0x40
#define ADDR_FIXED 0x44

//**** definitions for the clock point of the digit tube
#define POINT_ON  0x80
#define POINT_OFF 0x00

//**** definitions for brightness
#define BRIGHT_DARKEST 0
#define BRIGHT_TYPICAL 2
#define BRIGHTEST 7

static void bitDelay(void)
{
        //delayMicroseconds(5);
        delayMicroseconds(1);
}

//send segment signal to TM1637
static void writeByte(uint8_t wr_data)
{
        uint8_t i;
        // Sent 8bit data
        for(i=0;i<8;i++) {
                digitalWrite(gpio_clk,LOW);
                if(wr_data & 0x01)digitalWrite(gpio_dio,HIGH);
                else digitalWrite(gpio_dio,LOW);
                digitalWrite(gpio_clk,HIGH);
                wr_data >>= 1;
                delay(1);
        }

        digitalWrite(gpio_clk,LOW);
        digitalWrite(gpio_dio,HIGH);
        digitalWrite(gpio_clk,HIGH);
        pinMode(gpio_dio,INPUT);

        // Wait for ACK
        int retry = 0;
        while(1) {
                uint8_t ack = digitalRead(gpio_dio);
                // printf("ack[%d]=%d\n", retry, ack);
                if (ack == 0) break;
                retry++;
                if (digitalRead(gpio_dio)) {
                        pinMode(gpio_dio,OUTPUT);
                        digitalWrite(gpio_dio, FALSE);
                        pinMode(gpio_dio,INPUT);
                }
        }
        pinMode(gpio_dio,OUTPUT);
        return;
}

//send start signal to TM1637
static void start(void)
{
        digitalWrite(gpio_clk,HIGH);
        digitalWrite(gpio_dio,HIGH);
        digitalWrite(gpio_dio,LOW);
        digitalWrite(gpio_clk,LOW);
}

//send end signal to TM1637
static void stop(void)
{
        digitalWrite(gpio_clk,LOW);
        digitalWrite(gpio_dio,LOW);
        digitalWrite(gpio_clk,HIGH);
        digitalWrite(gpio_dio,HIGH);
}

//convert numver tu tube data
static int8_t coding(int8_t DispData, int8_t DispPointData)
{
        if(DispData == 0x7f) DispData = 0x00 + DispPointData;//The bit digital tube off
        else DispData = TubeTab[DispData] + DispPointData;
        return DispData;
}

void tm1637_begin(int8_t Clk, int8_t Data)
{
        gpio_clk = Clk;
        gpio_dio = Data;
        pinMode(gpio_clk,OUTPUT);
        pinMode(gpio_dio,OUTPUT);
}

void tm1637_brightness(int8_t Bright)
{
        // Set the brightness and it takes effect the next time it displays.
        brightness = 0x88 + Bright;
}

void tm1637_display(int BitAddr,int DispData,int DispPointData)
{
        int8_t SegData;
        SegData = coding(DispData, DispPointData);
        start();
        writeByte(ADDR_FIXED);
        stop();
        start();
        writeByte(BitAddr|0xc0);
        writeByte(SegData);
        stop();
        start();
        writeByte(brightness);
        stop();
}

void tm1637_display_segmnts(int8_t *SegmentData, int8_t SegmentLength, int MillSec)
{
        start();
        writeByte(ADDR_AUTO);
        stop();

        start();
        //writeByte(BitAddr|0xc0);
        writeByte(0xc0);
        for (int i=0;i<SegmentLength;i++) {
                writeByte(SegmentData[i]);
                delay(MillSec);
        }
        stop();

        start();
        writeByte(brightness);
        stop();
}

void tm1637_display_sliding(int8_t *DispData, int8_t DispDataLen, int MillSec) {
        int8_t segments[3] = {0,0,0};

        for (int i=0;i<DispDataLen;i++) {
                segments[0] = segments[1];
                segments[1] = segments[2];
                segments[2] = coding(DispData[i], POINT_OFF);
                tm1637_display_segmnts(segments, 3, MillSec);
        }
        for (int i=0;i<3;i++) {
                segments[0] = segments[1];
                segments[1] = segments[2];
                segments[2] = 0;
                tm1637_display_segmnts(segments, 3, MillSec);
        }
}

int main(void) {
        if(wiringPiSetup() == -1) return 1;
        // clock & data pin
        tm1637_begin(CLK, DATA);

        // You can set the brightness level from 0(darkest) till 7(brightest)
        // BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
        tm1637_brightness(BRIGHT_TYPICAL);

        // Clear all segment
        tm1637_display(2, BLANK, POINT_OFF);
        tm1637_display(1, BLANK, POINT_OFF);
        tm1637_display(0, BLANK, POINT_OFF);
        delay(500);

        int8_t DispData[10];
        for (int i=0;i<10;i++) DispData[i] = i;
        tm1637_display_sliding(DispData, sizeof(DispData), 200);
}



TM1637を使ったこのようなモジュールを入手しました。


裏にSOPタイプのTM1637が実装されています。


なぜかブレッドボードで動いたコードが、このモジュールでは動きません。
ブレッドボード+TM1637単体では動くのに不思議です。
さらに、不思議なことに、python-peripheryライブラリに移植したコードでは問題なく動きます。
python-peripheryライブラリを使ったサンプルコードは、こちらで 公開しています。
python-peripheryは非常に汎用性が高いpythonのGPIOライブラリで、GPIOのON/OFFは/sys/class /gpioデバイスを操作します。
RaspberryPiだけでなく、様々なPiボードで使うことができます。

こちらにArduinoのライブラリが公 開されています。
digitaWrite()ではなく、pinMode()でCLKピンとDIOピンのLOW/HIGHを切り替えています。
このライブラリ+Arduinoでは問題なく動くので、微妙な信号ON/OFFのタイミングのずれが有るのかもしれません。
そこで、こちらのコードを参 考にGPIOの操作を少し変えてみました。
以下のコードで動くようになりました。
/*
 Raspberry Pi driving the TM1637
 port from here
 https://github.com/TinyTronics/TM1637_6D
 cc -o tm1637-4digit-fix tm1637-f4digit-ix.c -lwiringPi
*/

#include <stdio.h>
#include <stdint.h>
#include <wiringPi.h>

//0~9, 10=blank digit, 11=dash/minus character
static int8_t TubeTab[] = {
        0x3f, // 0
        0x06, // 1
        0x5b, // 2
        0x4f, // 3
        0x66, // 4
        0x6d, // 5
        0x7d, // 6
        0x07, // 7
        0x7f, // 8
        0x6f, // 9
        0x00, // space
        0x40  // dash(minus)
};

#define BLANK 10
#define DASH  11

int brightness;
int gpio_clk;
int gpio_dio;

#define CLK  9 //Pin#5
#define DATA 8 //Pin#3

//***** definitions for TM1637
#define ADDR_AUTO  0x40
#define ADDR_FIXED 0x44

//**** definitions for the clock point of the digit tube
#define POINT_ON  0x80
#define POINT_OFF 0x00

//**** definitions for brightness
#define BRIGHT_DARKEST 0
#define BRIGHT_TYPICAL 2
#define BRIGHTEST 7

static void bitDelay(void)
{
        delayMicroseconds(1);
}

//send segment signal to TM1637
static void writeByte(uint8_t wr_data)
{
        uint8_t i;
        // Sent 8bit data
        for(i=0;i<8;i++) {
                digitalWrite(gpio_clk,LOW);
                bitDelay();
                if(wr_data & 0x01)digitalWrite(gpio_dio,HIGH);
                else digitalWrite(gpio_dio,LOW);
                wr_data >>= 1;
                bitDelay();
                digitalWrite(gpio_clk,HIGH);
                bitDelay();
        }

        pinMode(gpio_dio,INPUT);
        digitalWrite(gpio_clk,LOW);
        bitDelay();
        digitalWrite(gpio_clk,HIGH);
        bitDelay();
        digitalWrite(gpio_clk,LOW);
        bitDelay();
        pinMode(gpio_dio,OUTPUT);
        return;
}

//send start signal to TM1637
static void start(void)
{
        digitalWrite(gpio_dio,LOW);
        bitDelay();
}

//send end signal to TM1637
static void stop(void)
{
        digitalWrite(gpio_dio,LOW);
        bitDelay();
        digitalWrite(gpio_clk,HIGH);
        bitDelay();
        digitalWrite(gpio_dio,HIGH);
        bitDelay();
}

//convert numver tu tube data
static int8_t coding(int DispData, int DispPointData)
{
        if(DispData == 0x7f) DispData = 0x00 + DispPointData;//The bit digital tube off
        else DispData = TubeTab[DispData] + DispPointData;
        return DispData;
}

void tm1637_begin(int Clk, int Data)
{
        gpio_clk = Clk;
        gpio_dio = Data;
        pinMode(gpio_clk,OUTPUT);
        digitalWrite(gpio_clk,LOW);
        bitDelay();
        pinMode(gpio_dio,OUTPUT);
        digitalWrite(gpio_dio,HIGH);
        bitDelay();
        digitalWrite(gpio_clk,HIGH);
        bitDelay();
}

void tm1637_brightness(int Bright)
{
        // Set the brightness and it takes effect the next time it displays.
        brightness = 0x88 + Bright;
}

void tm1637_display(int BitAddr,int DispData,int DispPointData)
{
        int8_t SegData;
        SegData = coding(DispData, DispPointData);
        start();
        writeByte(ADDR_FIXED);
        stop();
        start();
        writeByte(BitAddr|0xc0);
        writeByte(SegData);
        stop();
        start();
        writeByte(brightness);
        stop();
}

int main(void) {
        if(wiringPiSetup() == -1) return 1;
        // clock & data pin
        tm1637_begin(CLK, DATA);

        // You can set the brightness level from 0(darkest) till 7(brightest)
        // BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
        tm1637_brightness(BRIGHT_TYPICAL);

        // Clear all segment
        tm1637_display(3, BLANK, POINT_OFF);
        tm1637_display(2, BLANK, POINT_OFF);
        tm1637_display(1, BLANK, POINT_OFF);
        tm1637_display(0, BLANK, POINT_OFF);
        delay(500);

        int number;
        int digit;
        for (number=0;number<12;number++) {
                for (digit=0;digit<4;digit++) {
                        // We send the entire array to the display along with the points array
                        tm1637_display(digit, number, POINT_OFF);
                        // Execute this loop every 300 milliseconds
                        delay(100);
                }
                delay(500);
        }

        tm1637_display(3, 4, POINT_OFF);
        tm1637_display(2, 3, POINT_OFF);
        tm1637_display(1, 2, POINT_OFF);
        tm1637_display(0, 1, POINT_OFF);
        delay(500);

        tm1637_display(3, 4, POINT_ON);
        tm1637_display(2, 3, POINT_ON);
        tm1637_display(1, 2, POINT_ON);
        tm1637_display(0, 1, POINT_ON);
}



TM1637はTITAN MICRO ELECTRONICSの製品です。
このメーカはいくつかのLED Driver製品を提供しています。
よく似たチップに以下のチップが有りますが、いずれもSOP24パッケージなので変換基盤が必要になります。
型番 GRID数(桁数) セグメント数
TM1620 4/6
10/8
TM1623 4/7 14/11
TM1624 4/7 14/11
TM1627 4/7 14/11
TM1628 4/7 14/11
TM1638 4/8 14/11
TM1639 8
10



カソードコモンに続いて、アノードコモンの8セグメントLEDでずいぶん楽しめましたが、
LEDと一緒に使えそうなチップは一通り試したようなので、このシリーズは今回で終わりの予定で す。
以下にチップ毎に必要なGPIOの数、必要な抵抗の数をまとめます。


必要なGPIOの数 表示できる最大桁数 必要な抵抗の数
専用チップを使わない場合 8本+桁数 4桁(※)
桁数
TLC5940(LED用PWMドライバー) 6本+桁数 4桁(※)*2 1本+桁数
DM13A(定電流LEDドライバー) 4本+桁数 4桁(※)*2 1本+桁数
74LS47 4本+桁数 4桁(※) 桁数
TM1637(定電流LEDドライバー) 2本 6桁 0本
(※)最大桁数に制限はありませんが、4桁以上のセグメントLEDを見たことがないので4桁としています。

色々試しましたが、アノードコモンの8セグメントLEDと一緒に使うチップとしては、
2本のGPIOと抵抗なしで6桁まで表示できて、輝度まで変えることができるTM1637が最強ですが、
ブレットボードとモジュールでコードを変える必要があるのが難点です。