エレキ素人が何か考える（その18）：７セグメントLEDドライバ

# ７セグメントLEDドライバで7セグメントLEDを操作
[エレキ素人が何か考える（その12）：シフトレジスタで7セグメントLED](https://qiita.com/infinite1oop/items/6937e3f9cae2cc059f09)の続編。７セグメントLEDドライバを用いて、7セグメントLEDを制御する。マイコンの使用GPIO数を減らすことについては、シフトレジスタと同じである。

## 7セグメントLEDドライバ
[７セグメント用ＬＥＤドライバーＩＣ　ＴＭ１６３０](https://akizukidenshi.com/catalog/g/gI-13223/)を利用。本ICは、カソードコモン専用となるため、カソードコモン用LEDである[C-551SRD](https://akizukidenshi.com/catalog/g/gI-00640/)を対象とした。いずれも、秋月電子で購入。

## LEDのピン配置
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/c6c592bf-7662-0fe3-ca01-01228014e747.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/daa9dac7-8578-616a-00dd-52a5f7cccbae.png)

## TM1630のデータシート
[データシート](https://akizukidenshi.com/download/ds/titanmicro/TM1630_V1.0.pdf)は中国語で書かれている。Googleで英訳したものを適宜PickUp。なお、次のサイトの説明が参考になる。
- [LEDドライバ TM1630 を使ってみる (1)　データシート](http://meyon.gonna.jp/study/electronic/2061/)
- [7セグLEDドライバTM1630で省配線制御を紹介します（自作ライブラリ付）](https://kurobekoblog.com/tm1630led)

### ピンアサイン、LEDとの接続
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/778a2e27-f1e4-410e-41dc-d6b93d204c74.png", width=70%>
![TM1630datasheet-Pin1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/6f0bd8f8-b82f-0cfd-ba5c-9348f6169d14.png)
![TM1630datasheet-Pin2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/d98d53c5-c53e-2a86-c8fb-59704e891487.png)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/ede07e6d-11de-6ab2-8221-2add81a26dc0.png", width=70%>
ここは説明割愛（ほぼ自明であろう）。

### コマンドやデータの書き込みシーケンス
固定アドレスモード（後述）のコマンドシーケンスは下記となる。(小さな字でゴメンナサイ)
![TM1630datasheet-FixedAddr1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/6a67afd8-5b21-a647-f076-b0626f09b726.png)
![TM1630datasheet-FixedAddr2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/1f6a8fe9-f4d4-9c24-d340-3d718a508b53.png)
1. Display Mode（5桁7セグメント or 4桁8セグメント）
1. Data Setting（固定アドレスモード）
1. Display Address（GRID1-4のいずれか選択）
1. Data（AddressへのLED表示データのセット）
(3,4の繰り返し)
1. Display Control（ON or OFF）

となっており、上記の1-5までのそれぞれのコマンド実行前にSTBをLowに、実行後にSTBをHighにする。コマンド実行（データ転送）中に、8ビット分のクロックを発生させるため、shiftOut()を利用する。ソースコード的には下記となる。
1. STB Low: digitalWrite(pin, LOW)
1. コマンド: shiftOut(DIO, CLK, LSBFIRST, データ)　（必要に応じて繰り返す）
1. STB High: digitalWrite(pin, HIGH)

データシートにはこのシーケンスとなっているが、実はこのとおりでなくても動作する。詳細は後述するソースコードを参照してほしいが、最初に
　1(Display Mode) -> 2(Data Setting) -> 5(Display Control)
を実施し、その後
　3(Display Address) -> 4(Data)
を繰り返しても問題ないようだ。また、途中で
　5(Display Control)
により、LED Off/Onを単独で行っても動作した。

### Display Mode
![TM1630datasheet-DisplayModeSetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/1b53203f-732e-964c-5908-50a8e626adc6.png)
5桁7セグメントまたは4桁8セグメントの設定。ここでは後者を選択（0b00xxxx01）。

### Data Setting
![TM1630datasheet-DataSetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/b1245fa1-4e85-87aa-4774-5db324074b85.png)
データ書き込み、固定アドレスモードの選択（0b01xx0100）。

### Display Address
![TM1630datasheet-DisplayRegisterAddress.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/9c38b8a3-d5f0-52c7-5ab5-009556161f84.png)
![TM1630datasheet-AddressCommand.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/f4d8b8e0-fc00-fcef-de7c-4d14dd8047d1.png)
自動翻訳後の内容が見えづらくなっているが、SEG2-8へのデータを書き込む場合、各Gridに対して次のデータをアドレスとして用いる。
- Grid1: 0b11xx0000 (00H)
- Grid2: 0b11xx0010 (02H)
- Grid3: 0b11xx0100 (04H)
- Grid4: 0b11xx0110 (06H)

### Data
LEDの各セグメント（A-G）を用いて、数字の0-9を作る。ソースコードのほうがわかりやすい。
```c++
uint8_t digit[] = {0b01111110, 0b00001100, 0b10110110, 0b10011110, 0b11001100,
                    0b11011010, 0b11111010, 0b01001110, 0b11111110, 0b11011110};
```
shiftOut()にて、LSB1stでデータを送出するので、bit7がセグメントGに、bit1がセグメントAに相当するようだ。

### Display Control
![TM1630datasheet-DisplayControl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/e7bc5481-40a1-7165-4671-f5a249595202.png)
ここでは、PWM-13/16を選択（2番目に明るい）。
- Display ON:  0b10xx1110
- Display OFF: 0b10xx0000

## ソースコード
１秒毎に０から９までを表示させる単純なもの。動作中でも、LEDのOFF/ON制御、テスト用のGRID選択をPCから行いたかったため、タスクを設けて制御（FreeRTOS利用）。

```c++
#include <Arduino_FreeRTOS.h>

// TM1630
#define DISP_MODE 0b00000001 // 5-digit 7-segments
#define FIX_MODE  0b01000100 // Increased Fixed Write Mode
#define DISP_ON   0b10001110 // Display ON & PWM-13/16
#define DISP_OFF  0b10000000 // Display OFF
#define GRID1     0b11000000 // 00H
#define GRID2     0b11000010 // 02H
#define GRID3     0b11000100 // 04H
#define GRID4     0b11000110 // 06H

// Arduino
#define DIO 5
#define CLK 6
#define STB 7
```
- データシートにそった各種コマンドなどの定義
- Arduino GPIO番号

```c++
boolean tgl;
uint8_t addr;
uint8_t grid[] = {GRID1, GRID2, GRID3, GRID4};
uint8_t digit[] = {0b01111110, 0b00001100, 0b10110110, 0b10011110, 0b11001100,
                    0b11011010, 0b11111010, 0b01001110, 0b11111110, 0b11011110};

TaskHandle_t xKbd, xDisp;
```
- Display On/OFF用変数：tgl
- GRID用変数：addr
- GRID定義
- 0-9までの数値の定義
- FreeRTOS用タスクハンドラ

```c++
void configure(uint8_t arg) {
  digitalWrite(STB, LOW);
  shiftOut(DIO, CLK, LSBFIRST, arg);
  digitalWrite(STB, HIGH);  
}

void disp_num(uint8_t num) {
  digitalWrite(STB, LOW);
  shiftOut(DIO, CLK, LSBFIRST, addr);
  shiftOut(DIO, CLK, LSBFIRST, digit[num]);
  digitalWrite(STB, HIGH);  
}
```
- コマンド制御関数：configure()
- 数値データ関数：disp_num()

```c++
TaskHandle_t xKeyboard() {
  for (;;) {
    if (Serial.available() > 0) {
      String data = Serial.readStringUntil('\n');
      uint8_t val = data.toInt();
      val = val%5;  // Avoid exception
      Serial.println(val);
      if (val == 0) {
        tgl = !tgl;
        configure((tgl? DISP_ON: DISP_OFF));
      } else {
        addr = grid[val-1];
      }
    }
    vTaskDelay(1);  
  }
}
```
- PCからのシリアル入力タスク
    - 入力値0：Display ON/OFF切り替え
    - 入力値1-4：GRID選択
（今回はLEDが１つだけだが、配線を変えてGRID1-4が機能することを確認するため、選択できるようにしている。）

```c++
TaskHandle_t xDisplay() {
  for (;;) {
    for (int i=0; i<10; i++) {
      disp_num(i);
      vTaskDelay(1000/portTICK_PERIOD_MS);
    } 
  }
}
```
- LED表示タスク（1秒毎に0-9までの数値を表示）

```c++
void setup() {
  Serial.begin(9600);
  pinMode(DIO, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(STB, OUTPUT);
  digitalWrite(DIO, LOW);
  digitalWrite(CLK, LOW);
  digitalWrite(STB, HIGH);
  tgl = true;
  addr = GRID1;
  delay(1);

  // Command Display
  configure(DISP_MODE);
  // Address Incremental
  configure(FIX_MODE);
  // Setting Display Control
  configure((tgl? DISP_ON: DISP_OFF));
  // Task
  xTaskCreate(xKeyboard, "Keyboard", 128, NULL, 1, &xKbd);
  xTaskCreate(xDisplay, "Display", 128, NULL, 1, &xDisp);
  vTaskStartScheduler();
}

void loop() {
  // nothing
}
```
- シリアル初期化
- GPIO初期値設定
- 各種変数初期化
- TM1630初期値設定
- タスク起動およびスケジューリング

## 検証
特に説明は不要かと。中央下にあるICが、TM1630である。
![IMG_20221120_090028.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/8d719007-c7b3-f8b6-6454-fcc052cf5758.jpeg)
なお、ここでは、Arduino Uno互換ボードを利用した。

EOF