Project 15.1 シフトレジスター/Flowing Water Light

以前、バーLEDアレイを使って流れるようなライトを作りました。

あわせて読みたい
Project 3.1 配列と繰り返し/Flowing Light 前回までで単独のLEDを制御することを学びました。 https://kaneshige.org/?p=203 今回は複数のLEDを制御する方法を学びます。 このプロジェクトでは、複数のLEDを使用...

この時の実装ではESP32-S3の GPIOポートが10個使われました。 GPIOポートは多ければ多いほど、ESP32-S3に接続できる周辺機器の数が増えるため、貴重な資源です。では、GPIOの数を節約して流れるライトを作成するにはどうしたら良いでしょうか?その答えの一つがシフトレジスター「74HC595」です。

目次

74HC595について

74HC595チップは、シリアルデータ(直列データ)をパラレルデータ(並列データ)に変換するのに用いられます。74HC595チップは、1バイトのシリアルデータを8ビットに変換し、各ビットに対応する信号レベルを8つの出力ポートへそれぞれ出力することができます。この特徴から、74HC595チップはESP32-S3のIOポートを拡張するのに活用できます。74HC595チップの8つの出力ポートを制御するには、最低でも3つのGPIOポートが必要となります。限られた数の出力ポートを効率的に活用し、LEDマトリックスやモーター制御など、多くの出力信号が必要な場面で活躍します。

74HC595の主な機能

  • シリアルデータ(1ビットずつ)をパラレルデータ(8ビット同時)に変換
  • 8つの出力ポート(Q0~Q7)で制御
  • データ保持機能(ラッチ機能)搭載
  • 3つの制御ピンで最大64個の74HC595をカスケード接続可能

74HC595の動作の仕組み

  1. データ入力: シリアルデータ(SRCLKピン)とデータ(SDINピン)を用いて、1ビットずつシリアルデータを入力します。
  2. シフト: 各ビットは、SH_CPピン(シフトクロック)のLOWからHIGHへの遷移(上昇エッジ、立ち上がり、クロックとも)で次のレジスタ段へシフトされます。
  3. ラッチ: ST_CP(ラッチクロック)の上昇エッジで、現在のシフトレジスタのデータが保持されます。このラッチされたデータが出力ポート(Q0~Q7)に出力されます。

74HC595の利用例

  • LEDマトリックス制御: 74HC595を複数個カスケード接続することで、64×64個など大規模なLEDマトリックスを制御できます。
  • モーター制御: 8つのステッピングモーターを独立制御したり、複数のDCモーターを同時に駆動したりできます。
  • センサーデータ出力: 温度センサーや湿度センサーなどのアナログセンサーデータを、8ビットADCでデジタル化し、74HC595を使って出力できます。

74HC595のデメリット

  • シリアルデータ入力とラッチ操作が必要なため、マイコン側でプログラムによる制御が必要
  • 高速なデータ転送には不向き(高速なデータ転送が必要な場合は、高速動作が可能な74HC595Hなどの種類を選択する必要があります)

74HC595のピンアサイン

Pin nameGPIO number説明
Q0-Q715, 1-7パラレルデータのアウトプット
VCC16電源のプラス極、電圧は2~6Vです。
GND8電源のマイナス極
DS14シリアルデータのインプット
OE13出力イネーブル:
このピンがハイレベルの場合、Q0-Q7はハイインピーダンス状態になります。
このピンがローレベルの場合、Q0-Q7は出力モードになります。
ST_CP12STorage register Clock Pin/ラッチピン:このポートの状態が立ち上がりを検知するとパラレルデータをアウトプットします。
SH_CP11シリアルシフトクロック:このポートの状態が立ち上がりを検知するとシリアルデータ入力レジスタをシフトします。
MR10シフトレジスタクリア:このピンがローレベルの場合、シフトレジスタの内容がクリアされます。
Q7′9シリアルデータ出力:さらに多くの74HC595を直列接続することができます。

より詳細な情報はデータシートを確認して下さい。みんな大好き秋月さんのサイトに同等品のデータシートがあります。

回路図

接続図

動作イメージ

コード

/**********************************************************************
  Filename    : FlowingLight02
  Description : Use 74HC575 to drive the ledbar to display the flowing light.
  Auther      : www.freenove.com
  Modification: 2022/10/24
**********************************************************************/

int latchPin = 13;          // Pin connected to ST_CP of 74HC595(Pin12)
int clockPin = 14;          // Pin connected to SH_CP of 74HC595(Pin11)
int dataPin = 12;           // Pin connected to DS of 74HC595(Pin14)

void setup() {
  // set pins to output
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  // Define a one-byte variable to use the 8 bits to represent the state of 8 LEDs of LED bar graph.
  // This variable is assigned to 0x01, that is binary 00000001, which indicates only one LED light on.
  byte x = 0x01;    // 0b 0000 0001
  for (int j = 0; j < 8; j++) { // Let led light up from right to left
    writeTo595(LSBFIRST, x);
    x <<= 1; // make the variable move one bit to left once, then the bright LED move one step to the left once.
    delay(50);
  }
  delay(100);
  x = 0x80;       //0b 1000 0000
  for (int j = 0; j < 8; j++) { // Let led light up from left to right
    writeTo595(LSBFIRST, x);
    x >>= 1;    
    delay(50);
  }
  delay(100);
}
void writeTo595(int order, byte _data ) {
  // Output low level to latchPin
  digitalWrite(latchPin, LOW);
  // Send serial data to 74HC595
  shiftOut(dataPin, clockPin, order, _data);
  // Output high level to latchPin, and 74HC595 will update the data to the parallel output port.
  digitalWrite(latchPin, HIGH);
}

来ました!久しぶりにプログラムらしいプログラムですね!
ピンアサイン、setup()あたりは特に問題ないですね。

22行目でLEDアレイの光り方のパターンをxというバイト型変数に格納しています。『0x』は16進数を意味しています。『0b』は2進数を意味しています。1が光って0が消えている状態という前提でシリアルデータを用意しています。

byte x = 0x01;    // 0b 0000 0001

コメントをつけるくらいなら、最初から2進数表記で定義したほうが可読性が良いのではないかと思います。以下のように修正できますね。

byte x = 0b00000001;

23~27行目は8個のLEDの端から端まで光る場所を(xのビット列の中の1の場所を)ずらしながらwriteTo595()関数を実行しています。30~37行目は逆向きの動作ですね。

  for (int j = 0; j < 8; j++) { // Let led light up from right to left
    writeTo595(LSBFIRST, x);
    x <<= 1; // make the variable move one bit to left once, then the bright LED move one step to the left once.
    delay(50);
  }

いよいよシフトレジスターのメイン処理です!
38行目からのwriteTo595()関数を見てみましょう。
orderで受け取っているのはLSBFIRSTというマクロ定義です。実態としては『0』として定義されています。LSBFIRSTは『最下位ビットから送る』という意味です。最上位ビットから送る時は『MSBFIRST』です。shiftOut()関数の使い方という程度の理解で十分です。
40行目でラッチピンをLOWにしています。ラッチピンをHIGHにするとパラレルデータをアウトプットするという大事なピンですね。shiftOut()する前にLOWにしておくという所作です。
42行目で実際のシフトアウト処理ですね。これでシフトレジスターにデータが送られます。ピッピッピッピッピッピッピッピッ と、8ビット分がシリアル(直列)に送られます。データピンに正しくデータが届いたらクロックピンが1度反転します。シフトレジスターを直列に繋いでいる場合は、この動作によってデータが順送りされます。
44行目でいよいよラッチピンをHIGHにして、せーのでパラレルにデータをアウトプットします。

void writeTo595(int order, byte _data ) {
  // Output low level to latchPin
  digitalWrite(latchPin, LOW);
  // Send serial data to 74HC595
  shiftOut(dataPin, clockPin, order, _data);
  // Output high level to latchPin, and 74HC595 will update the data to the parallel output port.
  digitalWrite(latchPin, HIGH);
}

そしてここに来て唐突にビットシフトについての説明が書いてありました。もっと早く書いてほしかったですね。

シフトしたときの動作と、シフト結果を変数に戻すときの書き方について書いてありました。符号を一旦無視して、新しく追加されるビットには0が入るということ、『x=x>>1』と『x>>=1』が同じ意味だということが書いてあります。

今回は久しぶりにプログラミングをした感覚がありましたね。
次回は7セグメントディスプレイをシフトレジスターで制御してみましょう。

あわせて読みたい
Project 16.1 7セグメントLED/7-Segment Display 前回はシフトレジスター『74HC595』の使用方法を学びました。 https://kaneshige.org/?p=427 今回はシフトレジスターを使用して7セグメントディスプレイで0~Fまで表示...
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

金重総合研究所の主席研究員。
子供の頃から研究者を目指し、ライフワークとして日々様々な研究をしています。
経営・マネジメント・金融・DXあたりが本職です。
私を採用したい人、私と一緒に働きたい人、一緒に知識を肥やしていきたい人はぜひお声がけ下さい。

コメント

コメントする

目次