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


今回は16チャンネルLED専用ドライバーのTLC5940を 使って8セグメントLEDを表示する方法を紹介します。
このチップは8セグメントLED専用ではなく、そもそもPWMを使って16個のLEDの明るさを制御するためのチップですが、
無理やり8セグメントLEDで使ってみます。

以下のページで使い方が紹介されています。
http://asumism.hatenablog.com/entry/2013/09/27/014202
http://garretlab.web.fc2.com/arduino/lab/led_driver/index.html

いずれもArduinoでの使い方が紹介されていますが、逆に言うとRaspberryでの使い方を紹介したページは
英語のページを含めてほとんど見つけられませんでした。

TLC5940

TLC5940
TLC5940

結線は以下の通りです。
20番ピン(IREF)とGNDの間には、「逆方向電流」制限用の抵抗を挟んで使用し ます。
最初はてっきりカソードコモンのLEDで使える(つまりOUTnには2Vとか3Vが出力される)と思っていたのですが
以下のページの回路図を見てアノードコモン用であることに気が付きました。
http://tronixstuff.com/2013/10/21/tutorial-arduino-tlc5940-led-driver-ic/

使用する抵抗値は以下の式で求めます。
R=1.24/逆方向電流*31.5
私は2KΩの抵抗を使いましたので、逆方向電流として20mAぐらいが流れるはずです。

TLC5940ピン番号 LEDへの接続(ピン番号) Raspberryへの接続(ピン番号)
1(OUT1) Segment b
2(OUT2) Segment c
3(OUT3) Segment d
4(OUT4) Segment e
5(OUT5) Segment f
6(OUT6) Segment g
7(OUT7) Segment D.P
8(OUT8)

9(OUT9)

10(OUT10)

11(OUT11)

12(OUT12)

13(OUT13)

14(OUT14)

15(OUT15)

16(XERR)

17(SOUT)

18(GSCLK)
18(GPIO24)
19(DCPRG)
3.3V
20(IREF)
2KΩの抵抗を介してGNDに接続
21(VCC)
3.3V
22(GND)
GND
23(BLANK)
16(GPIO23)
24(XALT)
15(GPIO22)
25(SCLK)
11(GPIO17)
26(SIN)
12(GPIO18)
27(VPRG)
13(GPIO27)
28(OUT0) Segment a

このチップはアノードコモンの8セグメントLED専用ですが、3桁以上のLEDを使う場合、
2個のチップをカスケードして使うか、自力でSegment制御を行う必要があります。
(2桁のLEDの場合は、このチップだけで制御が可能です)
今回3桁のLEDを使いましたので、桁の制御としては以下のピンを使います。

LEDへの接続(ピン番号) Raspberryへの接続(ピン番号)
Digit 3 Common 24(GPIO8)
Digit 2 Common 26(GPIO7)
Digit 1 Common 19(GPIO10)

ソースコードは以下の通りです。

/*

 tlc5940.c
 
 Raspberry Pi driving the TLC5940

 to compile : cc tlc5940.c -o tlc5940 -lwiringPi


*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <wiringPi.h>

#define    SCLK    0
#define    SIN       1
#define    VPRG    2
#define    XLAT    3
#define    BLANK    4
#define    GSCLK    5

#define Digs    3
#define D0      10

int firstCycleFlag=1;

static const uint8_t segmentDigits [] =
{
// a  b  c  d  e  f  g  p       Segments
// 0  1  2  3  4  5  6  7       wiringPi pin No.
   1, 1, 1, 1, 1, 1, 0, 0,      // 0
   0, 1, 1, 0, 0, 0, 0, 0,      // 1
   1, 1, 0, 1, 1, 0, 1, 0,      // 2
   1, 1, 1, 1, 0, 0, 1, 0,      // 3
   0, 1, 1, 0, 0, 1, 1, 0,      // 4
   1, 0, 1, 1, 0, 1, 1, 0,      // 5
   1, 0, 1, 1, 1, 1, 1, 0,      // 6
   1, 1, 1, 0, 0, 0, 0, 0,      // 7
   1, 1, 1, 1, 1, 1, 1, 0,      // 8
   1, 1, 1, 1, 0, 1, 1, 0,      // 9
   1, 1, 1, 0, 1, 1, 1, 0,      // A
   0, 0, 1, 1, 1, 1, 1, 0,      // b
   1, 0, 0, 1, 1, 1, 0, 0,      // C
   0, 1, 1, 1, 1, 0, 1, 0,      // d
   1, 0, 0, 1, 1, 1, 1, 0,      // E
   1, 0, 0, 0, 1, 1, 1, 0,      // F
   0, 0, 0, 0, 0, 0, 0, 0,      // blank
} ;

static void TLC5940pulse(int port)
{
  digitalWrite(port,HIGH);
  digitalWrite(port,LOW);
}

static void TLC5940sendDC(unsigned char * dcdata)
{
  int i,j;
  int pos;
  unsigned char wk[16];
  unsigned int mask;

  pos=15;
  for(i=0;i<16;i++) {
    wk[pos--]=dcdata[i];
  }

  digitalWrite(VPRG,HIGH);
  digitalWrite(XLAT,LOW);
  for(i=0;i<16;i++) {
    for(j=6;j>0;j--) {
      mask=1<<(j-1);
      if (wk[i] & mask)
        digitalWrite(SIN, 1);
      else
        digitalWrite(SIN, 0);
      TLC5940pulse(SCLK);
    }
  }
  TLC5940pulse(XLAT);
}

static void TLC5940sendGS(unsigned short * gsdata)
{
  int i,j;
  int pos;
  unsigned short wk[16];
  unsigned int mask;


  pos=15;
  for(i=0;i<16;i++) {
    wk[pos--]=gsdata[i];
  }

  digitalWrite(VPRG,LOW);
  digitalWrite(XLAT,LOW);
  for(i=0;i<16;i++) {
    for(j=12;j>0;j--) {
      mask=1<<(j-1);
      if (wk[i] & mask)
        digitalWrite(SIN, 1);
      else
        digitalWrite(SIN, 0);
      TLC5940pulse(SCLK);
      TLC5940pulse(GSCLK);
    }
  }

  digitalWrite(BLANK,HIGH);
  TLC5940pulse(XLAT);
  digitalWrite(BLANK,LOW);

  if (firstCycleFlag) {
    TLC5940pulse(SCLK);
    firstCycleFlag=0;
  }
}


static void TLC5940feedPorts(int cnt)
{
  int loop;
  int i;

  for (loop=0;loop<cnt;loop++) {
    TLC5940pulse(BLANK);
    for(i=0;i<4096;i++){
      TLC5940pulse(GSCLK);
    }
  }
}

static void DisplayDigit (char digit, unsigned short* gsdata)
{
  int i;
  uint8_t segment ;
  uint8_t index, d, segVal ;

  d = toupper (digit) ;
  if ((d >= '0') && (d <= '9'))        // Digit
    index = d - '0' ;
  else if ((d >= 'A') && (d <= 'F'))        // Hex
    index = d - 'A' + 10 ;
  else
    index = 16 ;                          // Blank

  for (segment = 0 ; segment < 8 ; ++segment) {
    segVal = segmentDigits [index * 8 + segment] ;
    if(segVal)
      gsdata[segment] = 0xfff;
    else
      gsdata[segment] = 0x0;
  }
}

static void DisplayDigits (char* digits, unsigned short* gsdata)
{
  uint8_t digit ;
  char    c1 ;

  for (digit = 0 ; digit < Digs; ++digit) {
    digitalWrite (D0 + digit, 1) ;
    c1 = digits [digit] ;
    DisplayDigit(c1,gsdata);
    TLC5940sendGS(gsdata);
    TLC5940feedPorts(1);
    digitalWrite (D0 + digit, 0) ;
  }
}

main(){
  int i,j;
  unsigned char dcval;
  unsigned int gsval;
  unsigned char dcdata[16];
  unsigned short gsdata[16];
  char digits[8];

  if (wiringPiSetup () == -1) exit(1);

  pinMode(SCLK, OUTPUT);
  pinMode(SIN, OUTPUT);
  pinMode(VPRG, OUTPUT);
  pinMode(XLAT, OUTPUT);
  pinMode(BLANK, OUTPUT);
  pinMode(GSCLK, OUTPUT);

  digitalWrite(SCLK,LOW);
  digitalWrite(XLAT,LOW);
  digitalWrite(BLANK,HIGH);
  digitalWrite(GSCLK,LOW);

  for(i=0;i<Digs;i++) {
    pinMode(D0+i, OUTPUT);
    digitalWrite(D0+i, 0);
  }

//Send DC Data
  dcval=0x0f;
  for(i=0;i<16;i++){
    dcdata[i]=dcval;
  }
  TLC5940sendDC(dcdata);

//Clear GS Data
  gsval=0x0;
  for(i=0;i<16;i++){
    gsdata[i]=gsval;
  }

//Set GS Data
  strcpy(digits,"123");
  for(i=0;i<1000;i++) DisplayDigits(digits,gsdata);

  strcpy(digits,"ABC");
  for(i=0;i<1000;i++) DisplayDigits(digits,gsdata);

  digitalWrite(SCLK,LOW);
  digitalWrite(XLAT,LOW);
  digitalWrite(BLANK,LOW);
  digitalWrite(GSCLK,LOW);

  exit(0);
}

次回はこのチップの最大の特徴であるPWM機能の使い方を紹介します。