エレキ素人が何か考える（その12）：シフトレジスタで7セグメントLED

# シフトレジスタで7セグメントLEDを操作
[前回](https://qiita.com/infinite1oop/items/23942d33bfb2e0dbc8e4)では、ArduinoのGPIOピン7つを7セグメントLEDにアサインしていた。通常、マイコンのGPIO数は限られており、潤沢には存在しない。7セグメントLED制御には、通常、専用のチップを使い、マイコンの使用GPIO数を減らすらしい。ここでは、シフトレジスタを用いて、それを試みる。

## シフトレジスタ
シフトレジスタの説明は、[Wiki](https://ja.wikipedia.org/wiki/シフトレジスタ)などにおまかせしたい。ここでは、Arduinoで使用するGPIOの数を減らすという目的で利用している。秋月電子で入手した[８ビットシフトレジスタ　Ｕ７４ＨＣ５９５ＡＧ－Ｄ](https://akizukidenshi.com/catalog/g/gI-14053/)を用いる。データシートは[こちら](https://akizukidenshi.com/download/ds/unisonic/u74hc595.pdf)にある。

結論としては、3つのGPIOを利用して、7セグメントLEDの8個のLEDを制御している。

## ソースコード
実現することは、[前回](https://qiita.com/infinite1oop/items/23942d33bfb2e0dbc8e4)と同じ。キーボード（シリアル）入力した数値を表示するのみ。次のサイトを参照した。
- [74HC595を使ってarduinoの１６ビットパラレル出力に挑んでみた](https://suisaihannda.blogspot.com/2017/02/74hc595arduino.html)
- [シフトレジスタ (74HC595) と Arduino を使って複数の LED を制御する方法](https://iot.keicode.com/arduino/arduino-shift-register-sn74hc595.php)

### 準備（面倒）
7セグメントLEDの配線は適当に行ったが、[前回](https://qiita.com/infinite1oop/items/23942d33bfb2e0dbc8e4)と同じ。LSBFirstで8ビットを1ビットずつ出力、LEDへの配線を考慮して、0から9までの数値をビット列化する。7セグメントLEDの各LEDの位置（AからG）およびピン番号は下記のとおり。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/347e52bb-16b0-9d28-c9a3-f8afa06d18d6.png)<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/f37172c1-b655-1f7f-809e-787c49e58a02.png", width=30%>
シフトレジスタのピンアサインは下記のとおり。
![シフトレジスタピン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/4eceb2ab-f948-5527-aa54-3c6d289fa19d.png)
LSBFirstではb0からb7の順番に、ピンアサインではQh->Qg->Qf->Qe->Qd->Qc->Qb->Qaの順番に出力される。適当に行った配線では、下記の対応となった。（配線を行っていない人にはわかりにくいのは容赦。）

```
// b1:E b2:D b3:C b4:B b5:A b6:F b7:G  (b0:DP)
// Qa:10 Qb:9 Qc:7 Qd:6 Qe:4 Qf:2 Qg:1  wired connection
//    G     F    A    B    C    D    E
//
//     A   B   C   D   E   F   G
// bit 5   4   3   2   1   6   7 
```
これらをもとに、各数値に対応するビット列を作成。
```
/*
uint8_t disp_ary[][sizeof(ary)/sizeof(uint8_t)] = {
  // A   B   C   D   E   F   G
  {LOW, LOW, LOW, LOW, LOW, LOW, HIGH},     // 0  B10000000
  {HIGH, LOW, LOW, HIGH, HIGH, HIGH, HIGH}, // 1  B11100110
  {LOW, LOW, HIGH, LOW, LOW, HIGH, LOW},    // 2  B01001000
  {LOW, LOW, LOW, LOW, HIGH, HIGH, LOW},    // 3  B01000010
  {HIGH, LOW, LOW, HIGH, HIGH, LOW, LOW},   // 4  B00100110
  {LOW, HIGH, LOW, LOW, HIGH, LOW, LOW},    // 5  B00010010
  {HIGH, HIGH, LOW, LOW, LOW, LOW, LOW},    // 6  B00110000
  {LOW, LOW, LOW, HIGH, HIGH, HIGH, HIGH},  // 7  B11000110
  {LOW, LOW, LOW, LOW, LOW, LOW, LOW},      // 8  B00000000
  {LOW, LOW, LOW, HIGH, HIGH, LOW, LOW}     // 9  B00000110
};
*/
```

### 実際のコード
準備に時間はかかったが、前回と比べて、コード量は激減（GPIO削減だけの効果ではなく）。

```c++
#define SER   5
#define RCLK  6
#define SRCLK 7

uint8_t num_disp[] = {
  B10000000, B11100110, B01001000, B01000010, B00100110,
  B00010010, B00110000, B11000110, B00000000, B00000110};

void setup() {
  Serial.begin(9600);
  pinMode(SER, OUTPUT);
  pinMode(RCLK, OUTPUT);
  pinMode(SRCLK, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    String data = Serial.readStringUntil('\n');
    uint8_t val = data.toInt();
    Serial.println(val);
    digitalWrite(RCLK, LOW);
    shiftOut(SER, SRCLK, LSBFIRST, num_disp[val]);
    digitalWrite(RCLK, HIGH);
  }  
}
```

## 配線状況と結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/221f27b2-7ac0-c021-b5bc-63055e887279.png)
ブレッドボードの右に見えるものがシフトレジスタである。[前回](https://qiita.com/infinite1oop/items/23942d33bfb2e0dbc8e4)の写真と比べると、一目瞭然で、Arduinoと接続されているワイヤが少ない。

## 最後に
７セグメント用ＬＥＤドライバーＩＣを使うともっと汎用的なことができるらしい。