電子工作
Raspberrypi3
7セグメントLED
シフトレジスタ

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

前回記事で作成したデジタル時計ですが、いかんせん配線が多い。
ラズパイ使って別のもの作るのにトライしたいけれど、この配線を外したら二度と復元できない自信がある。
IMG_20180108_104950.jpg

そしたら購入してたこのセットに入ってるじゃないですか。
配線をスッキリさせれるという代物が。

8ビットシフトレジスタ

IMG_20180108_111016.jpg

やれること

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

ピンの位置と番号と役割

四角の縁がポコンと欠けてる所を目印にこんな感じで16個のピンが配置されてます。
IMG_20180108_111016_1.jpg

ピン番号 種類 役割 記号
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

シフトレジスタクロック(誘導員さん)
 clock.png

ラッチクロック(高いとこで旗振ってる人)
 latch.png

シフトレジスタ(出走ゲート)
 shiftregister.png

ストーリー

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

シーン 処理イメージ
これからレースが始まります。
参加する競争馬が1列に並んで出走ゲートに入ろうとしています。

これからシリアル入力からパラレル出力までの1サイクルが始まります。
入力されるデータはシリアル(直列)に送られてきます。
register1.png
           
誘導員さんが1番目のゲートに入るように指示しました。
最初の馬はそれに従いゲートに入りました。

シフトレジスタクロックは1番目に入力データを保持するよう指示しました。入力データはそれに従い1番に保持されました。
register2.png
誘導員さんが2番目のゲートに入るように指示しました。
次の馬はそれに従いゲートに入りました。

シフトレジスタクロックは2番目に入力データを保持するよう指示しました。
入力データはそれに従い2番に保持されました。
register3.png
その後も順次に馬が誘導員の指示に従いゲートに入りました。
これで8番目のゲートまで馬が入りました。

その後も順次に入力データがシフトレジスタクロックの指示に従い保持されました。
これで8番目まで入力データを保持できました。
register4.png
ここで満を持して旗が振られレースがスタートしました。
各馬一斉にゲートを飛び出しました。

ラッチクロックがパラレル出力を指示しました。
シフトレジスタに保持されたデータがパラレル出力として一気に送出されました。
register5.png

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

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

マスターリセットのピンについて

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

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

シフトレジスタと7セグメントLEDを使用しての時計表示スクリプト

こんな感じでLEDの左側に経由するシフトレジスタを用意しました。
IMG_20180124_215107.jpg

ラズパイ - シフトレジスタ - 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スクリプト

前回記事のコメント欄でクラスの利用についてアドバイスを頂いたので参考にしてみました。
GPIOピンの定義、ON/OFF動作はすべてクラスGpioOutputPinに持たせてやることでメイン処理がすっきりしました。

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の数字表現用
4: 7セグLEDの桁位置指定用





1: 7セグLEDの数字表現用
4: 7セグLEDの桁位置指定用
1: Vcc(電源)
1: グラウンド
1: マスターリセット
1: シフトレジスタクロック
1: ラッチレジスタクロック

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

IMG_20180126_003955.jpg