8ビットシフトレジスタで配線さっぱり

[前回記事](https://qiita.com/elyoshio92/items/ccb27786302983cd5057)で作成したデジタル時計ですが、いかんせん配線が多い。
ラズパイ使って別のもの作るのにトライしたいけれど、この配線を外したら二度と復元できない自信がある。
![IMG_20180108_104950.jpg](https://qiita-image-store.s3.amazonaws.com/0/228646/afa6f367-63d7-d244-2142-14f654e0e32d.jpeg)


そしたら購入してた[このセット](https://www.amazon.co.jp/%E5%88%9D%E5%BF%83%E8%80%85%E5%90%91%E3%81%91%E3%81%AE%E5%AD%A6%E7%BF%92%E3%82%AD%E3%83%83%E3%83%88-%E3%83%A9%E3%82%BA%E3%83%99%E3%83%AA%E3%83%BC%E3%83%BB%E3%83%91%E3%82%A4%EF%BC%93-%EF%BC%92model-B%E7%94%A8-UNO-%E4%BA%92%E6%8F%9B%E3%82%AD%E3%83%83%E3%83%88172-1%E5%9F%BA%E6%9C%AC%E9%83%A8%E5%93%81%E3%82%BB%E3%83%83%E3%83%88-%E5%AE%9F%E9%A8%93%E7%94%A8-Mega2560%E3%80%81%E3%83%96%E3%83%AC%E3%83%83%E3%83%89%E3%83%9C%E3%83%BC%E3%83%89%E3%80%81LED%E3%80%81LCD1602%E7%AD%89/dp/B075167Y4D)に入ってるじゃないですか。
配線をスッキリさせれるという代物が。

## 8ビットシフトレジスタ
![IMG_20180108_111016.jpg](https://qiita-image-store.s3.amazonaws.com/0/228646/5f08da07-3486-5246-2ef8-ee14727812a1.jpeg)

### やれること
シリアル→パラレル変換ができます。
・・・こちとら初心者。こんな説明じゃ何にも分かりません。
とりあえず各ピンが担っている役割を調べてみる。

### ピンの位置と番号と役割
四角の縁がポコンと欠けてる所を目印にこんな感じで16個のピンが配置されてます。
![IMG_20180108_111016_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/228646/fb658987-4191-d974-1900-531dd1ae4e85.jpeg) 

|ピン番号|種類|役割|記号|
|:--:|----|----|----|
|1|パラレル出力|2番目の出力|QB/Q1|
|2|パラレル出力|3番目の出力|QC/Q2|
|3|パラレル出力|4番目の出力|QD/Q3|
|4|パラレル出力|5番目の出力|QE/Q4|
|5|パラレル出力|6番目の出力|QF/Q5|
|6|パラレル出力|7番目の出力|QG/Q6|
|7|パラレル出力|8番目の出力|QH/Q7|
|8|グラウンド|接地(アース)|GND|
|9|シリアル出力|シフトレジスタを何個も繋げる時に使う|QH'|
|10|マスターリセット|シフトレジスタのリセット|SRCLR|
|11|シフトレジスタクロック|データの入力先を次のアドレスに移動|SCK/SHCP|
|12|ラッチクロック|8ビットのデータを送信順に出力|RCK/STCP|
|13|ハイインピーダンス状態設定|全パラレル出力が切断状態になる|G/OE|
|14|シリアル入力|唯一の信号入力|SER/DS|
|15|パラレル出力|1番目の出力|QA/Q0|
|16|電源接続|+5Vに接続する|Vcc|

たくさんピンがあるので混乱するけれど、シフトレジスタの肝となる動作で必要なのは以下だけです。

- シリアル入力
- パラレル出力
- シフトレジスタクロック
- ラッチクロック

基本的にはこの4種類を駆使してシリアルなデータをパラレルなデータに変換する事がシフトレジスタなるものの目的になります。

## シフトレジスタの処理イメージ
で、パラレルなデータというのは結局どんなもんなんだい？
色々なサイトで勉強させてもらって飲み込んだ結果、シフトレジスタの概念は競馬の出走ゲートみたいな感じだなと思うことにしました。
（詳しくないから実際の競馬とは違うかもです)

###登場人物
入力/出力データ（競走馬）
　![data.png](https://qiita-image-store.s3.amazonaws.com/0/228646/7902ed9b-477f-ac2f-7af9-ca8364f151a1.png)

シフトレジスタクロック（誘導員さん）
　![clock.png](https://qiita-image-store.s3.amazonaws.com/0/228646/fdcbe748-9ce2-851b-eacd-c2859ef96145.png)

ラッチクロック（高いとこで旗振ってる人）
　![latch.png](https://qiita-image-store.s3.amazonaws.com/0/228646/bc469964-597a-447d-b86f-cf743d14883b.png)

シフトレジスタ（出走ゲート）
　![shiftregister.png](https://qiita-image-store.s3.amazonaws.com/0/228646/80112645-9290-4155-db38-defef71083f2.png)

###ストーリー
競馬のレースが始まるまでを例に処理順序を解説します。
<b>太字</b>がシフトレジスタで行っている処理になります

|シーン|処理イメージ|
|:--|:--|
|これからレースが始まります。<br>参加する競争馬が1列に並んで出走ゲートに入ろうとしています。<br><b>↓<br>これからシリアル入力からパラレル出力までの１サイクルが始まります。<br>入力されるデータはシリアル(直列)に送られてきます。|![register1.png](https://qiita-image-store.s3.amazonaws.com/0/228646/01f9c7f8-1169-843d-9280-4f38687cdfd8.png)<br>　　　　　　　　　　　|
|誘導員さんが１番目のゲートに入るように指示しました。<br>最初の馬はそれに従いゲートに入りました。<br><b>↓<br>シフトレジスタクロックは1番目に入力データを保持するよう指示しました。<b>入力データはそれに従い1番に保持されました。<br>|![register2.png](https://qiita-image-store.s3.amazonaws.com/0/228646/3e8675e5-3964-4483-9807-3767d21a787a.png)|
|誘導員さんが2番目のゲートに入るように指示しました。<br>次の馬はそれに従いゲートに入りました。<br><b>↓<br>シフトレジスタクロックは2番目に入力データを保持するよう指示しました。<br>入力データはそれに従い2番に保持されました。<br>|![register3.png](https://qiita-image-store.s3.amazonaws.com/0/228646/ca4405dd-54a3-199b-a2f0-91990389ecf7.png)|
|その後も順次に馬が誘導員の指示に従いゲートに入りました。<br>これで8番目のゲートまで馬が入りました。<br><b>↓<br>その後も順次に入力データがシフトレジスタクロックの指示に従い保持されました。<br>これで8番目まで入力データを保持できました。|![register4.png](https://qiita-image-store.s3.amazonaws.com/0/228646/938e63c3-0a5e-4514-6ad7-85539a19257a.png)|
|ここで満を持して旗が振られレースがスタートしました。<br>各馬一斉にゲートを飛び出しました。<br><b>↓<br>ラッチクロックがパラレル出力を指示しました。<br>シフトレジスタに保持されたデータがパラレル出力として一気に送出されました。|![register5.png](https://qiita-image-store.s3.amazonaws.com/0/228646/905686bd-c1cf-09e8-b2be-650ffee6d094.png)|

つまり、シリアル→パラレル変換とは・・
**1列(直列)に来る入力データを蓄積し、並列データとして一気に出力する**という事になります。

このイメージに従い、出走した馬(送出されたデータ)を7セグメントLEDの数字を表すピンに接続すればこれまで通りに想定の数値を表示する事ができました。

###マスターリセットのピンについて
ここまでの説明に登場してませんが、10番ピンのマスターリセットが初心者にはクセモノだと思います。苦労しました。

こいつの性質はLOW(切断)にするとレジスタの内容をリセットするという事のようです。
逆に言うと、常に通電(HIGH)していないとレジスタにデータを保持する事ができません。
参考にしたサイトのお師匠さんたちは常識の範疇なのか「使用しないのでとりあえずVCCにつなぐ」という書き方をしているものばかりでしたが、このような仕組みのため**意識して使ってなくてもVCCにはつながないとダメ**です！
素人的には使わないなら線もつながなくていいんだべと思っちゃってハマリました。

##シフトレジスタと7セグメントLEDを使用しての時計表示スクリプト
こんな感じでLEDの左側に経由するシフトレジスタを用意しました。
![IMG_20180124_215107.jpg](https://qiita-image-store.s3.amazonaws.com/0/228646/6c8ce33e-61cf-061b-b463-edcf8964a91b.jpeg)

###ラズパイ - シフトレジスタ - 7セグメントLEDの接続
イカした接続図を載せたいとこですが、相変わらず初心者ゆえ分からず！
またまた表形式でご覧下さい。

|ラズパイGPIO|シフトレジスタ ピン|7セグLED ピン | 用途 |
|:--:|:--:|:--:|:--|
|(~~3.3~~ 5v)|16|-|VCC(電源)
|(GND)|8|-|グラウンド
|(~~5~~ 3.3v)|10|-|マスターリセット
|18|14|-|シリアル入力
|14|11|-|シフトレジスタクロック
|15|12|-|ラッチレジスタクロック
|-|15|1|パラレル出力 1番目
|-|1|2|パラレル出力 2番目
|-|2|3|パラレル出力 3番目
|-|3|4|パラレル出力 4番目
|-|4|5|パラレル出力 5番目
|-|5|7|パラレル出力 6番目
|-|6|10|パラレル出力 7番目
|-|7|11|パラレル出力 8番目
|-|9|-|シフトレジスタ増設用(未使用のため接続なし)
|-|13|-|ハイインピーダンス(未使用のため接続なし)
|4|-|6|7セグLED 1桁目指定
|17|-|8|7セグLED 2桁目指定
|27|-|9|7セグLED 3桁目指定
|22|-|12|7セグLED 4桁目指定


###pythonスクリプト
[前回記事](https://qiita.com/elyoshio92/items/ccb27786302983cd5057)のコメント欄でクラスの利用についてアドバイスを頂いたので参考にしてみました。
GPIOピンの定義、ON/OFF動作はすべてクラスGpioOutputPinに持たせてやることでメイン処理がすっきりしました。

```python3:led2.py
import RPi.GPIO as GPIO
import time

interval_time = 0.001

GPIO.setmode(GPIO.BCM)

class GpioOutputPin:
  def __init__(self, pin):
    self.pin = pin
    GPIO.setup(pin, GPIO.OUT)

  @property
  def val(self):
    return self.pin

  def low(self):
    GPIO.output(self.pin, GPIO.LOW)

  def high(self):
    GPIO.output(self.pin, GPIO.HIGH)

  def flash(self,interval=0):
    self.high()
    time.sleep(interval)
    self.low()

SCK  = GpioOutputPin(14)
RCK  = GpioOutputPin(15)
SI   = GpioOutputPin(18)
DEG1 = GpioOutputPin(22)
DEG2 = GpioOutputPin(27)
DEG3 = GpioOutputPin(17)
DEG4 = GpioOutputPin(4)

REGPINS = (SCK,RCK,SI)
DIGITS = (DEG1,DEG2,DEG3,DEG4)

NUMBERS = {
    ' ': (0, 0, 0, 0, 0, 0, 0, 0),
    '0': (1, 1, 1, 0, 1, 0, 1, 1),
    '1': (0, 0, 1, 0, 1, 0, 0, 0),
    '2': (1, 0, 1, 1, 0, 0, 1, 1),
    '3': (1, 0, 1, 1, 1, 0, 1, 0),
    '4': (0, 1, 1, 1, 1, 0, 0, 0),
    '5': (1, 1, 0, 1, 1, 0, 1, 0),
    '6': (1, 1, 0, 1, 1, 0, 1, 1),
    '7': (1, 1, 1, 0, 1, 0, 0, 0),
    '8': (1, 1, 1, 1, 1, 0, 1, 1),
    '9': (1, 1, 1, 1, 1, 0, 1, 0),
}

def reset():
  for pin in REGPINS:
    pin.low()
  for pin in DIGITS:
    pin.low()


try:
  # GPIO pins reset
  reset()

  while True:
    # get time and display number
    for timeStr, digit in zip(time.strftime('%I%M'), DIGITS):
      # set time number sign
      for i in range(8):
        if NUMBERS[timeStr][i] == 0:
          SI.high()
        else:
          SI.low()
        # flash shift clock
        SCK.flash()

      # flash latch clock
      RCK.flash()

      # flash 7segLED digit
      digit.flash(interval_time)

finally:
  # GPIO pins reset and cleanup
  reset()
  GPIO.cleanup()
```
###実際に配線はさっぱりしたか？
これにて今回の内容はおしまいになりますが、タイトルにした「配線さっぱり」は達成できたでしょうか？
ラズパイのGPIOピンの使用数の結果はこんな感じで言うほど削減されるわけじゃ無かった。。

||シフトレジスタ未使用|シフトレジスタ使用|
|:--:|:--|:--|
|合計|12|10|
|内訳|8: 7セグLEDの数字表現用<br>4: 7セグLEDの桁位置指定用<br><br><br><br><br><br>|1: 7セグLEDの数字表現用<br>4: 7セグLEDの桁位置指定用<br>1: Vcc(電源)<br>1: グラウンド<br>1: マスターリセット<br>1: シフトレジスタクロック<br>1: ラッチレジスタクロック|

で、でも限りあるGPIOなんだし、少しでも配線が省略できて空きのピンが増える事はすごく大事になるはず！

![IMG_20180126_003955.jpg](https://qiita-image-store.s3.amazonaws.com/0/228646/b165d486-5605-6968-6c2c-228ac50dee33.jpeg)