16Seg LEDで遊ぶ

#はじめに
16Seg LEDで遊んでみたくなりAliでモジュールを購入し、作成してみた。基本的には[武蔵野電波のプロトタイパーズ](https://pc.watch.impress.co.jp/docs/column/musashino_proto/300483.html)のそのままなのだが、Aliのモジュールは素直な作りではないので、その辺りを中心に説明したいと思います。
![Clipboard01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/b9439c4c-e04a-0564-3a35-ff3d2dd5f7f8.jpeg)

#必要なもの
##16Seg Module - KYX-5441AS
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/79172b21-045f-b744-aa3a-914ece5f3f44.png)
Aliで購入できる16SegLEDです。実際には14Segですが便宜上16Segと書きます。

##シフトレジスタ 74HC164 x 2
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/81077d09-cecf-d896-7e1d-8a1a27bb7562.png)
シリアルパラレル変換ロジックICです。Arduinoの出力信号だけでは足りませんので、シフトレジスタを使用して信号線を増やします。

##トランジスタアレイ TD62083AP x 2
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/639f83d5-14d0-3abb-782a-c24f26a27f9d.png)
トランジスタアレイです。配線が簡単になるのでお勧めです。
型番によって仕様が違います。5Vで使用するのでTD62083APを選択します。

| 品種        | 入力抵抗 | 推奨使用回路、条件 |
|:----------:|:--------:|:----------------:|
| TD62081AP  | 無  | 汎用(外付け抵抗使用)  |
| TD62082AP  | 10.5kΩ+7Vツエナー  | 14-25V PMOS  |
| TD62083AP  | 2.7kΩ  | TTL, 5V CMOS  |
| TD62084AP  | 10.5kΩ  | 6-15V  PMOS, CMOS  |

##Arduino Uno
PCとお手軽に通信できるUnoを使用します。

##その他
330Ω x 12
コネクタ、配線材
基板

#回路図
##全体の回路図
![16Seg Schematic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/8807bc20-b505-f870-c5bb-0cab376cc99a.png)
##16Segモジュールの内部配線です。
![16Seg回路図.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/68233138-ff78-e4a4-50e3-454b4fae0dc3.png)
4桁なのに5本分あるのはなんでよ?と思ってチェックしたら、各桁の右上右下のセグメントがDIG5で制御するようになっています。これはAliに掲載されている図ですが、LとNが反対になっていることに注意。

##基板
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/a3c4c667-d5f3-9e15-7e46-5b1115c060d2.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/3408cef0-809b-abb4-1625-72999ffca586.png)
配線はUEWを使用しています。綺麗ではありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/5719d286-2470-d4fc-a107-44bbd76dab95.png)
スタックして使います。

#プログラム

```c:16SegAscii.c
// 14セグメントLEDコントローラ
// by 武蔵野電波
// https://pc.watch.impress.co.jp/docs/column/musashino_proto/300483.html
//
// MDF for KYX-5441AS 2019/08/10

#include <MsTimer2.h>  // ライブラリのインストールが必要

#define NUMDIGIT (4*2)  // lenght of str
#define NUMOUTPUT (4*2+2)  // lenght of str

#define SPACE ' '
#define CLK 3
#define SD 2

#define KANSUJI   // 漢数字フォントにする場合に有効。アラビア数字にする場合はコメントアウト

//      **  0b0123456789ABCDEF
#define K1  0b0000000000100000
#define K2  0b0000000010000000
#define K3  0b0000000001000000
#define K4  0b0000100000000000
#define N1  0b0000000000010000
#define N2  0b1000000000000000
#define N3  0b0000010000000000
#define N4  0b0000001000000000

// 14セグ・フォントデータ
const unsigned int font[] = {
//  FJLMDGGHBCAE NK
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
  0b0000000000000000,  // space 0x20
  0b0101000000000000,  // !
  0b1100000000000000,  // "
  0b1000111000010000,  // #
  0b1101111001100000,  // $
//  FJLMDGGHBCAE NK
  0b1111011001000010,  // %
  0b1010110000110110,  // &
  0b0000000100000000,  // '
  0b0000000000000110,  // (
  0b0010000100000000,  // )
  0b0111011100000110,  // *
  0b0101011000000000,  // +
  0b0000000000000010,  // ,
  0b0000011000000000,  // -
  0b0000000000000100,  // .
  0b0010000000000010,  // /
#ifdef KANSUJI
//  FJLMDGGHBCAE NK
  0b0000111001010000,  // 〇
  0b0000011000000000,  // 一
  0b0000111000000000,  // 二
  0b0000111000100000,  // 三
  0b0010111001010100,  // 四
  0b0101111001100000,  // 五
  0b0110011000000100,  // 六
  0b0010111000000010,  // 七
  0b0000000000010100,  // 八
  0b0101011001000000,  // 九
#else
  0b1010100011110010,  // 0
  0b0101000000000000,  // 1
  0b0000111010110000,  // 2
  0b0000101011100000,
  0b1101011000000000,
  0b1000111001100000,
  0b1000111001110000,
  0b0001000000100010,   // 7 16segtype
  0b1000111011110000,
  0b1000111011100000,  // 9
#endif
  0b0101000000000000,  // :
  0b0101000000000000,  // ;
  0b0000000000000110,  // <
  0b0000111000000000,  // =
  0b0010000100000000,  // >
  0b1001010010100000,  // ?


  0b0000110010110100,  // @ 0x40
  0b1000011011110000,  // A
  0b0101101011100000,  // B
  0b1000100000110000,  // C
//  FJLMDGGHBCAE NK
  0b0101100011100000,  // D
  0b1000111000110000,  // E
  0b1000011000110000,  // F
  0b1000101001110000,  // G
  0b1000011011010000,  // H
  0b0101100000100000,  // I
//  FJLMDGGHBCAE NK
  0b0000100011010000,  // J
  0b1000010000010110,  // K
  0b1000100000010000,  // L
  0b1000000111010010,  // M
//  FJLMDGGHBCAE NK
  0b1000000111010100,  // N
  0b1000100011110000,  // O
  0b1000011010110000,  // P
  0b1000100011110100,  // Q
  0b1000011010110100,  // R
  0b1000111101100100,  // S
//  FJLMDGGHBCAE NK
  0b0101000000100000,  // T
  0b1000100011010000,  // U
  0b1010000000010010,  // V
  0b1010000011010100,  // W
//  FJLMDGGHBCAE NK
  0b0010000100000110,  // X
  0b1010011010000000,  // Y
  0b0010100000100010,  // Z

//  FJLMDGGHBCAE NK
  0b0101000000000110,  // [
  0b0000000100000100,  // '\'
  0b0111000100000000,  // ]
  0b0000000000100000,  // ^
  0b0000100000000000,  // _

  0b0000000100000000,  // ` 0x60
  0b0000110000010100,  // a
  0b1000111001010000,  // b
  0b0000111000010000,  // c
//  FJLMDGGHBCAE NK
  0b0000111011010000,  // d
  0b0010111000010000,  // e
  0b1000010000110000,  // f
  0b0000101111100000,  // g
  0b1000011001010000,  // h
  0b0001000000000000,  // i
//  FJLMDGGHBCAE NK
  0b0010100011000000,  // j
  0b1000011000010100,  // k
  0b0101000000000000,  // l
  0b0001011001010000,  // m
//  FJLMDGGHBCAE NK
  0b0000011001010000,  // n
  0b0000111001010000,  // o
  0b1000010000110010,  // p
  0b0000001111100000,  // q
  0b0000011000010000,  // r
  0b0000101000000100,  // s
//  FJLMDGGHBCAE NK
  0b1000110000010000,  // t
  0b0000100001010000,  // u
  0b0000000001000100,  // v
  0b0010000001010100,  // w
//  FJLMDGGHBCAE NK
  0b0010000100000110,  // x
  0b1010000000000010,  // y
  0b0010110000000000,  // z
  0b0101010000000000,  // {
  0b0101000000000000,  // |
  0b0101001000000000,  // }
  0b0000000000100000,  // ~
  0b1010000000000010,  // " "
};
// 14セグ・フォントデータ(カナ)
const unsigned int font_kana[] = {
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // space 0xA0
  0b0000000000000000,  // 。
  0b1000000000110000,  // 「
  0b0000100011000000,  // 」
  0b0000000000000000,  // 、
  0b0000000000000000,  // ・
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // ヲ
  0b0000000000000000,  // ア
  0b0000000000000000,  // イ
  0b0000000000000000,  // ウ
  0b0000000000000000,  // エ
  0b0000000000000000,  // オ
  0b0000000000000000,  // ヤ
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // ユ
  0b0000000000000000,  // ヨ
  0b0000000000000000,  // ツ
  0b0000000000000000,  // ー 0xB0
  0b0000000000000000,  // ア
  0b0000000000000000,  // イ
  0b0000000000000000,  // ウ
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // エ
  0b0000000000000000,  // オ
  0b0000000000000000,  // カ
  0b0000000000000000,  // キ
  0b0000000000000000,  // ク
  0b0000000000000000,  // ケ
  0b0000000000000000,  // コ
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // サ
  0b0000000000000000,  // シ
  0b0000000000000000,  // ス
  0b0000000000000000,  // セ
  0b0000000000000000,  // ソ
  0b0000000000000000,  // タ 0xC0
  0b0000000000000000,  // チ
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // ツ
  0b0000000000000000,  // テ
  0b0000000000000000,  // ト
  0b0000000000000000,  // ナ
  0b0000000000000000,  // ニ
  0b0000000000000000,  // ヌ
  0b0000000000000000,  // ネ
  0b0000000000000000,  // ノ
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // ハ
  0b0000000000000000,  // ヒ
  0b0000000000000000,  // フ
  0b0000000000000000,  // ヘ
  0b0000000000000000,  // ホ
  0b0000000000000000,  // マ
  0b0000000000000000,  // ミ 0xD0
  0b0000000000000000,  // ム
  0b0000000000000000,  // メ
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // モ
  0b0000000000000000,  // ヤ
  0b0000000000000000,  // ユ
  0b0000000000000000,  // ヨ
  0b0000000000000000,  // ラ
  0b0000000000000000,  // リ
  0b0000000000000000,  // ル
  0b0000000000000000,  // レ
  0b0000000000000000,  // ロ
//  FJLMDGGHBCAE NK
  0b0000000000000000,  // ワ
  0b0000000000000000,  // ン
  0b0000000000000000,  // "
  0b0000000000000000,  // °
};

char driverPin[NUMOUTPUT] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; // カソードにつながるピン

char t[NUMDIGIT] = "ABCD0123"; // 最初にABCD0123と表示
char currentDigit = 0;
byte buf = 0;

void setup() {
  for (char i = 0; i < NUMOUTPUT; i++) pinMode(driverPin[i], OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(SD, OUTPUT);
  //  pinMode(13, OUTPUT);

  MsTimer2::set(2, refresh);   // リフレッシュレート
  MsTimer2::start();

  Serial.begin(9600);

  //  digitalWrite(13, HIGH);
}

void Send_Data(unsigned int out_data)
{
    shiftOut(SD, CLK, LSBFIRST, lowByte(out_data));
    shiftOut(SD, CLK, LSBFIRST, highByte(out_data));
}
void refresh() {
  int offset = 0;
  digitalWrite(driverPin[currentDigit], LOW);
  currentDigit ++;
  if ( currentDigit == NUMOUTPUT)
    currentDigit = 0;

  if ( currentDigit == 4 || currentDigit == 9 )  // for K & N segment
  {
    unsigned int dp = 0;
    offset  = ( currentDigit == 4 )? 0:0;
    offset += ( currentDigit == 9 )?-1:0;
    dp |= ( 0x02 & font[t[currentDigit - 4 + offset]] ) ? K1 : 0 ;
    dp |= ( 0x04 & font[t[currentDigit - 4 + offset]] ) ? N1 : 0 ;
    dp |= ( 0x02 & font[t[currentDigit - 3 + offset]] ) ? K2 : 0 ;
    dp |= ( 0x04 & font[t[currentDigit - 3 + offset]] ) ? N2 : 0 ;
    dp |= ( 0x02 & font[t[currentDigit - 2 + offset]] ) ? K3 : 0 ;
    dp |= ( 0x04 & font[t[currentDigit - 2 + offset]] ) ? N3 : 0 ;
    dp |= ( 0x02 & font[t[currentDigit - 1 + offset]] ) ? K4 : 0 ;
    dp |= ( 0x04 & font[t[currentDigit - 1 + offset]] ) ? N4 : 0 ;

    Send_Data(dp);
  }
  else
  {
    offset  = ( currentDigit > 4 )?-1:0;
    Send_Data( font[t[currentDigit + offset]]);
  }
  digitalWrite(driverPin[currentDigit], HIGH);
}


void loop() {
  static char c = 0;

  if (Serial.available() > 0)
  {
    buf = Serial.read();
    Serial.println(buf);
    if (c == 0)
    {
      for (char i = 0; i < NUMDIGIT; i++)
      {
        t[i] = SPACE;
      }
    }

    // 受け取った文字の処理
    if (buf == ';') // ;を受け取ったら行末と判断
    {
      c = 0;
    }
    else if ( isgraph( buf ) || buf == ' ' )  // Alphabet, number, mark & ' '
    {
      t[c++] = buf;
    }
    if (c >= NUMDIGIT) c = NUMDIGIT - 1;
  }
}
```


MsTimer2::set(2, refresh);にて refresh()を2msec毎呼出して、LED表示しています。8桁表示に対して10桁分を制御しているので、少々見にくくなっています。
Arduino IDEのシリアルモニターから文字を送信すれば表示されます。数値、アルファベット大文字、小文字、記号に対応しています。また、デフォルトでは漢数字にしています。カタカナはまだ対応していません。

#完成
![Clipboard01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/796065d3-260a-d6e6-6f25-22aa16613d0b.jpeg)

![Clipboard02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/20055e66-33e1-a892-a45f-319e44c3af59.jpeg)

#おわりに
14Seg表示というのはなかなか楽しいです。フォントデータを作るのも、いろいろ考えながら作るのが楽しかったですね。

次はこれで時計を作るつもりです。


#I2Cでの入出力に改造

シリアル入力からの文字入力をI2Cからの入力に変更してみました。
##用意するもの
Arduino Unoもう一つ

##配線
A4, A5同士を接続します。電源が違うならGNDも接続

##マスター側のプログラム

マスター側のプログラムです。Serialより文字を読みこみ、I2Cへ出力します。

```c:Master.c
////////////////////////////////////////////////////////////////////
//
// Wireでマスタースレーブ制御する
// 2019/08/12
//
// https://qiita.com/UT-HACKs/items/540e9dddcd1d2d8b2be2
//
//
#include <Wire.h>
#define I2C_DEVICE_NO (0x10)  // I2C device address

void setup() {
  Serial.begin(9600);
  Wire.begin();
  pinMode(13,OUTPUT);
}

void loop() {
  byte incomingByte = 0;
  if( Serial.available() > 0 ) // read data有り
  {
    incomingByte = Serial.read();
    
    digitalWrite(13,HIGH);
    Wire.beginTransmission(I2C_DEVICE_NO) ;             // 通信の開始処理、スレーブのアドレスは８とする
    Wire.write( incomingByte );  // 通信データ送信
    Wire.endTransmission();
    digitalWrite(13,LOW);
  }
}
```


##スレーブ側(LEDを付けた方)
上にincludeと#defineを追加

```
#include <Wire.h>

#define I2C_DEVICE_NO (0x10)  // I2C device address
```

setup()に下記追加

```
  Wire.begin(I2C_DEVICE_NO) ; // Set address
  Wire.onReceive(CharReadFromI2C); // Function for Int.
```

I2C受信時にコールされる関数追加

```
void CharReadFromI2C(int a)
{
  static char c = 0;
  if (Wire.available() > 0)
  {
    buf = (char)Wire.read();
    Serial.println(buf);
    if (c == 0)
    {
      for (char i = 0; i < NUMDIGIT; i++)
      {
        t[i] = SPACE;
      }
    }

    // 受け取った文字の処理
    if (buf == ';') // ;を受け取ったら行末と判断
    {
      c = 0;
    }
    else if ( isgraph( buf ) || buf == ' ' )  // Alphabet, number, mark & ' '
    {
      t[c++] = buf;
    }
    if (c >= NUMDIGIT) c = NUMDIGIT - 1;
  }
}
```

loop()内は全消去

```
void loop() {
  // deleted
}
```

##完成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211703/56d2b8d7-83f6-8eb7-aceb-9a8e85558c76.png)

I2C通信は初めて使用しましたがすごく簡単ですね。ライブラリに感謝です。