シフトレジスタ【74HC595】の使い方を解説
今回はシフトレジスタIC:74HC595を使って7セグLEDに数字や文字を表示させてみたいと思います。
シフトレジスタの基本的な知識についてはこちらの記事をご覧ください。
74HC595の概要
74HC595は出力ラッチを持った8bitのシフトレジスタICです。
ICの端子配置図と内部の概略図はこのようになっています。
74HC595端子配置図
74HC595内部概略図
入力されたデータは8段のシフトレジスタに保存され、RCKによってストレージレジスタに転送されます。
ストレージレジスタの値は3ステートバッファによってQA~QHからパラレル出力されます。
各端子の機能をまとめます。
ピン番号 | 端子名 | 機能 |
---|---|---|
1~7、15 | QA~QH | データ出力端子 |
8 | GND | GND端子 |
9 | QH’ | 別の74HC595のSI端子に接続してシフトレジスタの段数を増やす事ができる(カスケード接続)。 |
10 | SCLR | シフトレジスタクリア端子。LOWにすると全ビットがLOWにセットされる。 |
11 | SCK | クロック入力端子。立ち上がりエッジでデータがシフトレジスタにセットされる。 |
12 | RCK | ラッチ端子。立ち上がりエッジでシフトレジスタのデータがストレージレジスタへ転送される。 |
13 | G | 出力イネーブル端子。LOWで全出力をHi-Z状態にする。 |
14 | SI | シリアルデータ入力端子。 |
16 | VCC | 電源端子 |
シフトレジスタの使い方
それでは、ラズパイとシフトレジスタICを使って7セグディスプレイに数字を表示してみましょう。
7セグLEDについてはこちらの記事をご覧ください。
回路図
今回使った7セグLEDディスプレイはアノードコモンなので、シフトレジスタの出力はカソード側へ接続しています。
したがって、シフトレジスタの出力がLOWの時にLEDが点灯することになります。
Pythonコード
7セグディスプレイに数字の2を表示させるプログラムです。
import RPi.GPIO as GPIO # GPIO用のモジュール
import time # 時間制御用のモジュール
dataPin = 11 # GPIO 17(74HC595の14ピンと接続)
latchPin = 13 # GPIO 27(74HC595の12ピンと接続)
clockPin = 15 # GPIO 22(74HC595の11ピンと接続)
def setRegister(dPin,cPin,val):
for i in range(0,8):
GPIO.output(cPin,GPIO.LOW); # クロックをLOWに
GPIO.output(dPin,val&(0x01<<i)) # 最下位bitから順にデータを入力
GPIO.output(cPin,GPIO.HIGH); # クロックをHIGHに
GPIO.setmode(GPIO.BOARD) # GPIOを物理ピン番号で指定
GPIO.setup(dataPin, GPIO.OUT) # dataPinを出力モードに設定
GPIO.setup(latchPin, GPIO.OUT) # latchPinを出力モードに設定
GPIO.setup(clockPin, GPIO.OUT) # clockPinを出力モードに設定
GPIO.output(latchPin,GPIO.LOW) # latchPinをLOWに
setRegister(dataPin,clockPin,0x25) # valに16進数で引数を渡す
GPIO.output(latchPin,GPIO.HIGH) # latchPinをHIGHに
GPIO.cleanup()
2を表示するためには、7セグLEDの[A,B,D,E,G]を点灯させる、つまりLOWにする必要があります。
したがって、SIに入力するデータは00100101となります。
16進数にすると0x25です。
このプログラムの一番のポイントは下記の部分です。
def setRegister(dPin,cPin,val):
for i in range(0,8):
GPIO.output(cPin,GPIO.LOW); # クロックをLOWに
GPIO.output(dPin,val&(0x01<<i)) # 最下位bitから順にデータを入力
GPIO.output(cPin,GPIO.HIGH); # クロックをHIGHに
この関数はシフトレジスタにデータをセットするためのものです。
まずSCK(cPin)をLOWにします。
次にSI(dPin)にデータをセットします。
ビット演算子:<<は左シフトを示し、0x01、つまり0b00000001の最下位ビットをi個左にシフトします。
i=0の場合は、0個シフトなのでそのまま、i=1の場合は1個シフトするので0b00000010となります。
まとめると、次のようになります。
i | シフト後の値 |
---|---|
0 | 00000001 |
1 | 00000010 |
2 | 00000100 |
3 | 00001000 |
4 | 00010000 |
5 | 00100000 |
6 | 01000000 |
7 | 10000000 |
このシフトされた値とvalの&を取ります。
&演算子は各ビットの掛け算となるので、i=0の場合は、
0b00100101
0b00000001
0b00000001
となり、SI(dPin)に1(HIGH)が入力されます。
この後、SCK(cPin)がHIGHとなりシフトレジスタに1がセットされます。
i=1の場合は1つシフトされるので、
0b00100101
0b00000010
0b00000000
となり、0(LOW)がセットされることになります。
この動きをi=0~7まで繰り返すので、シフトレジスタには00100101がセットされることになります。
シフトレジスタにデータがセットされると、RCK(latchPin)をLOWからHIGHに切り替えるので、シフトレジスタの値はストレージレジスタへ転送され、パラレル出力が更新されます。
このプログラムを走らせると2が表示されることが確認できました。
オシロでSCKとSIの波形を確認するとこのようになっていて、想定通りの動きが確認できました。
7セグLEDに0~Fを順番に表示させる
少しレベルアップさせて、7セグLEDに0~9、A~Fを順番に表示させてみます。
Pythonのコードは次のようにしました。
import RPi.GPIO as GPIO #GPIO用のモジュール
import time #時間制御用のモジュール
LSBFIRST = 1 #数字は何でもよい
MSBFIRST = 2 #数字は何でもよい
dataPin = 11 #GPIO 17(74HC595の14ピンと接続)
latchPin = 13 #GPIO 27(74HC595の12ピンと接続)
clockPin = 15 #GPIO 22(74HC595の11ピンと接続)
#0~9、A~Fを表示させるための値(MSBFIRSTの場合)
num = [0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e]
def setup():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(dataPin, GPIO.OUT)
GPIO.setup(latchPin, GPIO.OUT)
GPIO.setup(clockPin, GPIO.OUT)
def shiftOut(dPin,cPin,order,val):
for i in range(0,8):
GPIO.output(cPin,GPIO.LOW);
if(order == LSBFIRST):
GPIO.output(dPin,val&(0x01<<i))
elif(order == MSBFIRST):
GPIO.output(dPin,val&(0x80>>i))
GPIO.output(cPin,GPIO.HIGH);
def loop():
while True:
for i in range(0,16):
GPIO.output(latchPin,GPIO.LOW) #RCKをLOWにする
shiftOut(dataPin,clockPin,MSBFIRST,num[i]) #MSBFIRST、numに格納された配列を順に渡していく
GPIO.output(latchPin,GPIO.HIGH) #RCKをHIGHにする
time.sleep(0.5)
def destroy():
GPIO.output(latchPin,GPIO.LOW) # latchPinをLOWに
shiftOut(dataPin,clockPin,MSBFIRST,0xff) # LEDを全オフ
GPIO.output(latchPin,GPIO.HIGH) # latchPinをHIGHに
GPIO.cleanup()
if __name__ == '__main__':
print ('Program is starting...' )
setup()
try:
loop()
except KeyboardInterrupt:
destroy()
shiftOut関数を作る
下記の部分でshiftOut関数を定義しています。
def shiftOut(dPin,cPin,order,val):
for i in range(0,8):
GPIO.output(cPin,GPIO.LOW);
if(order == LSBFIRST):
GPIO.output(dPin,val&(0x01<<i))
elif(order == MSBFIRST):
GPIO.output(dPin,val&(0x80>>i))
GPIO.output(cPin,GPIO.HIGH);
引数のorderはLSBFIRSTかMSBFIRSTかを指定します。
LSBとはLeast Significant Bitの略で、最下位ビットのことです。
0b10110000の場合なら一番右の0がLSBです。
MSBとはMost Significant Bitの略で、最上位ビットのことです。
0b10110000の場合なら一番左の1がMSBです。
LSBFIRSTは最下位ビットから順にセットしていくことを示し、MSBFIRSTは最上位ビットから順にセットしていくことを示します。
よって、orderにLSBFIRSTを指定した場合は、0x01のLSBからシフト演算子で左へシフトしてvalとの&を取るようになっています。
逆にMSBFIRSTを指定した場合は、0x01のMSBからシフト演算子で右へシフトしてvalとの&を取るようになっています。
今回はMSBFIRSTでセットしていきます。
MSBFIRSTで3を表示させる場合の動きは以下のようなイメージになります。
0~Fまでを表示させる
次のコードで0~9とA~Fまでの16文字を順番に表示させます。
def loop():
while True:
for i in range(0,16):
GPIO.output(latchPin,GPIO.LOW) #RCKをLOWにする
shiftOut(dataPin,clockPin,MSBFIRST,num[i]) #MSBFIRST、numに格納された配列を順に渡していく
GPIO.output(latchPin,GPIO.HIGH) #RCKをHIGHにする
time.sleep(0.5)
RCKをLOWにしてからshiftOut関数を実行し、numに格納している数字、文字を表示させます。
これを16回繰り返し、numに格納した配列を左から順番に表示していきます。
while True:なので無限ループとなっており、0~Fの表示をずっと繰り返します。
プログラム実行結果
狙い通り0~Fの表示を繰り返していることを確認できました。
7セグLEDに0~Fまでを繰り返し表示 pic.twitter.com/I6iMsYujKC
— りょうのすけ (@ryo_analog) 2022年7月18日