Arduinoでシフトレジスタ(SN74HC595)を使用して8個のLEDをチカチカさせる

投稿者: | ↻ : 2019年5月1日

はじめに

Arduino Uno Rev.3は電源やGND以外に入出力ピンとして、デジタル14本、アナログ/デジタルの兼用6本が利用できます。

1つのLEDを点灯させるには、デジタル出力1本とGND1本の合計2本のピンが必要になります。

この方法で8つのLEDを点灯させるにはデジタル出力8本、16つのLEDを点灯させるにはデジタル出力16本が必要になります。デジタル入出力ピンを16ピンも使用したら他の用途に使えるピンが足りません。

こんな時に出力を増やす方法としてシフトレジスタが利用できます。

シフトレジスタの原理と仕組みは「しなぷすのハード製作記 – シフトレジスタ」で勉強して、SN74HC595Nの使用方法に限定してまとめます。

概要

シフトレジスタには大きく4種類あり、「少ないピンで多くの出力を制御する」ためには直列入力並列出力(SIPO:Serial In Parallel Out)だけです。

8ビットSIPO型シフトレジスタのイメージ(図1)

図1

単体で利用できるシフトレジスタは汎用ロジックICとして販売されています。入手しやすく使用例が多いので「SN74HC595N」を対象としました。

秋月電子「8ビットシフトレジスタ SN74HC595N」 – 1個40円(税込)(写真1)

写真1

説明

データシートの入手

Texas Instrumentsの公式サイトからデータシートを取得します(図2)。

図2

各ピンの役割について

「6 Pin Configuration and Functions」(図3)に各ピンの説明が書かれています。

図3

図は切り欠きを上にしてICを見た状態です。16本のピンがあり、各役割は以下のような役割を持っています。

ピン名前
入出力説明
8
GNDグラウンド
10SRCLR入力シフトレジスタのクリア
11SRCLK入力シフトレジスタのクロック信号
12RCLK入力ストレージレジスタのクロック信号
13OE入力出力の有効化
14SER入力シリアル入力
16Vcc電源
15,1〜7QA、QB〜QH出力パラレル出力
9QH’出力シフトレジスタの最上位ビットの状態を常に出力

シフトレジスタの原理を調べ直して分かったのですが、SN74HC595Nはシフトレジスタの出力部分に、同じ数のストレージレジスタを付けた少し複雑なものでした。そのため、シフトレジスタ以外にストレージレジスタの制御が必要になります。

電源やGND以外の各ピンを、シフトレジスタとストレージレジスタの2つの役割に分けて考えます。

シフトレジスタに関するピン

論理回路図(Logic Diagram)の赤で囲んだ部分(図4)を制御します。

図4

SER(serial input)、SRCLK(shift register clock)

8ビットシフトレジスタであるSN74HC595Nには、各ビットに対応する8つのフロップフロップ(FF)があり、下位ビットに対応するFFの出力が、上位ビットに対応するFFの入力へ数珠つなぎになっています。

SERピンは、下位のFFが存在しない最下位ビットに対応するFFへ信号を入力するピンです。

SRCLKピンは、シフトレジスタ側のフリップフロップの状態を更新するクロック信号を入力するピンです。このSRCLKピンの状態が、LOWからHIGHに変化したタイミング(立ち上がり)で更新されます。

SRCLR (shift register clear)

SRCLRピンは、クロック信号(SRCLK)の有無に関係なく、シフトレジスタ側のフリップフロップの状態をLOWにします。SRCLRピンにLOWを入力するとクリアされので、普段はHIGHにしておきます。

シフトレジスタ側に対応するフリップフロップだけをクリアし、後述するストレージレジスタ側のフリップフロップの状態は変化しません。

QH’

シフトレジスタ側の最上位ビットに対応するフリップフロップの状態を出力するピンです。

このQH’ピンを、別のSN74HC595NのSERピンへ接続すると、16ビットのシフトレジスタを作ることができます。

QH’ピンだけストレージレジスタ側ではなく、シフトレジスタ側からの出力なので、OEピンの入力に関係なく常に最上位ビットの状態が出力されています。

ストレージレジスタに関するピン

論理回路図(Logic Diagram)の青で囲んだ部分(図5)を制御します。

図5

RCLK(strage register clock)

ストレージレジスタ付きの8ビットシフトレジスタであるSN74HC595Nには、ストレージレジスタ側にも8つのフリップフロップがあります。

RCLKピンは、ストレージレジスタ側のフリップフロップの状態を更新するクロック信号を入力するピンです。このRCLKピンの状態が、LOWからHIGHに変化したタイミング(立ち上がり)で更新されます。

各ビットに対応するシフトレジスタ側のフリップフロップの出力が、ストレージレジスタ側のフリップフロップの入力に接続されてるので、RCLKピンの立ち上がりで、シフトレジスタ側のフリップフロップの状態が、ストレージレジスタ側のフリップフロップへ反映されます。

OE(output enable)

出力ピン(QA〜QH)の出力制御だけを行うピンです。

OEピンにLOWを入力すると、出力が有効となり、ストレージレジスタ側のフリップフロップの状態が出力ピン(QA〜QH)から出力されます。

OEピンにHIGHを入力すると、出力が無効となり、出力ピン(QA〜QH)はハイインピーダンス状態になります。

内部のフリップフロップの状態には影響をあたえないので、出力ピン(QA〜QH)を無効にしている間も、シフトレジスタやストレージレジスタの状態を更新できます。

QA 〜 QH

出力ピン(QA〜QH)は、出力が有効な場合に、ストレージレジスタ側のフリップフロップの状態を出力するピンです。

備考

SRCLKピンとRCLKピンは、規則正しい間隔のクロック信号でなくても動作します。ただし、クロック信号が速すぎると変化が追いつかず出力信号が安定しません。ナノ秒レベルの話なのでArduinoで利用するなら問題なさそうですが気に留めておきます。

検証の前に注意すること

多くのLEDを点灯させる前に考えなておく事があります。

最大出力電流について

Arduino UNO Rev.3がUSB端子から電力を得ていた場合、5Vピンの最大出力電源はリセッタブルヒューズ(MF-MSMF050-2)により500mAとなりますが、Arduino UNO Rev.3自体も電力を必要とするので最大では利用できません。

(5Vピンはマイコンを経由していないので他の入出力ピンの制約とは別になります)

また、電源供給側となるパソコンやUSBハブなどのUSB端子にも供給できる最大値があるので注意が必要です。USB2の端子を持ったmacだと、Appleの公式サイトで5V/500mAとなっています。

パソコンや機器により異なるので、あまり大きな電流が流れないように計算が必要です。

SN74HC595Nの最大出力電流

SN74HC595Nのデータシートの「10.2.2 Detailed Design Procedure」に、出力ピン一つあたりで35mAを超えてはならない、かつ、全体で70mAを超えてはならないと書かれています。

LEDの電流制限抵抗値

LEDは「5mm赤色LED OSDR5113A 秋月で500個1800円(税込)」を利用し、1つのLEDを8mAで点灯させる事にします。

このLEDの順方向電圧降下はVf = 2.0Vなので制限抵抗は以下の式から375Ωとなるので、近い値の390Ωを利用します。

( 電源電圧 – 順方向電圧降下 ) / 順方向電流 = 制限抵抗値

( 5V – 2V ) / 8mA = 375Ω

これで、8個のLEDを同時点灯させても70mA以下に抑えられます。

検証 – 1つのSN74HC595Nで8個のLEDをチカチカ

Arduino UNO Rev.3とSN74HC595Nを利用して、8個のLEDをチカチカさせます。

接続方法

Arduino UNO Rev.3側のピンを減らすため、SRCLRピンは5Vへ接続しHIGHの状態にしてクリアの機能は使用せず、OEピンはGNDへ接続しLOWの状態にして、出力ピンの出力を有効します(図6)。

※ LEDの点灯だけなのでノイズは無視することにして、バイパスコンデンサは省きます。

図6

ケース1 – digitalWrite

Arduino UNO Rev.3のデジタルピンを個別に制御して、右から左へ順番に1つずつ点灯させます。

スケッチ

#define SRCLK   (5)
#define RCLK    (6)
#define SER     (7)

void setup() {
  pinMode(SRCLK, OUTPUT);
  pinMode(RCLK,  OUTPUT);
  pinMode(SER,   OUTPUT);
}

void loop() {
  // 入力状態をHIGHにする
  digitalWrite(SER, HIGH);

  for (int i=0; i<8; i++) {
    // シフトレジスタをシフトさせてSERピンの状態を記憶させる
    digitalWrite(SRCLK, LOW);
    digitalWrite(SRCLK, HIGH);
    
    // シフトレジスタの状態をストレージレジスタへ反映させる
    digitalWrite(RCLK,  LOW);
    digitalWrite(RCLK,  HIGH);

    delay(200);

    // 以降の入力状態をLOWにする
    digitalWrite(SER, LOW);
  }
}

SRCLKとRCLKは、LOWからHIGHへの変化を作ればよく、こんな緩い制御でも利用できます。

結果

ケース2 – shiftOut

8ビットシフトレジスタを利用する場合にぴったりな、shiftOut関数があります。この関数を使うと、8ビット分のデータを一括でシフトレジスタに送れます。

ケース1では、1ビット送信するたびにストレージレジスタへ反映させていましたが、shiftOut関数を利用する場合は、8ビット送信してシフトレジスタの全ての状態を更新してから、ストレージレジスタへ反映させます。

ケース1と同じく、右から左へ順番に1つずつ点灯させます。

スケッチ

#define SRCLK   (5)
#define RCLK    (6)
#define SER     (7)

void setup() {
  pinMode(SRCLK, OUTPUT);
  pinMode(RCLK,  OUTPUT);
  pinMode(SER,   OUTPUT);
}

// 点灯パターン(1=点灯, 0=消灯)
const uint8_t PATTERNS[] = {
  0b00000001,
  0b00000010,
  0b00000100,
  0b00001000,
  0b00010000,
  0b00100000,
  0b01000000,
  0b10000000,
};

void loop() {
  
  // 点灯パターンの数
  int max_pattern = sizeof(PATTERNS)/sizeof(PATTERNS[0]);

  for (uint8_t i=0; i<max_pattern; i++) {
    // 8ビット分のデータをシフトレジスタへ送る
    shiftOut(SER, SRCLK, LSBFIRST, PATTERNS[i]); 

    // シフトレジスタの状態をストレージレジスタへ反映させる
    digitalWrite(RCLK,  LOW);
    digitalWrite(RCLK,  HIGH);

    delay(200);
  }
}

shiftOut関数の詳細はArduino 日本語リファレンス – shiftOutを参照。・・・したらSN74HC595Nでの使用例が載ってる!_(:3 」∠)ノ

ケース3 – SPI(非推奨)

ArduinoのSPIライブラリを利用してshiftOut関数と似たような処理を書けますが、信号が似ているから利用できるだけで、前準備が必要なのと貴重なハードウェアSPI機能を無駄に使用するのでオススメしません。

参考としてパターン2と同じ動作をSPI機能で行う場合のスケッチを載せます。

ハードウェアSPI機能は使用するピンが決まっているので、デジタル7番の白線を11番へ、デジタル5番の緑線を13番へ繋ぎ換えます。

SPIでのSS(SlaveSelect)ピンと、RCLKピンの操作は意味も極性も異なるのでRCLKの処理はそのままです。

ケース1と同じく、右から左へ順番に1つずつ点灯させます。

スケッチ

#include <SPI.h>

#define SRCLK   (13)    // SPI:CLOCK
#define RCLK    (6)
#define SER     (11)    // SPI:MOSI

void setup() {
  pinMode(SRCLK, OUTPUT);
  pinMode(RCLK,  OUTPUT);
  pinMode(SER,   OUTPUT);

  SPI.begin();
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
}

// 点灯パターン(1=点灯, 0=消灯)
const uint8_t PATTERNS[] = {
  0b00000001,
  0b00000010,
  0b00000100,
  0b00001000,
  0b00010000,
  0b00100000,
  0b01000000,
  0b10000000,
};

void loop() {

  // 点灯パターンの数
  int max_pattern = sizeof(PATTERNS)/sizeof(PATTERNS[0]);

  for (uint8_t i=0; i<max_pattern; i++) {
    // 8ビット分のデータをシフトレジスタへ送る
    SPI.transfer(PATTERNS[i]);

    // シフトレジスタの状態をストアレジスタへ反映させる
    digitalWrite(RCLK,  LOW);
    digitalWrite(RCLK,  HIGH);

    delay(200);
  }
}

まとめ

Arduino UNO Rev.3のみだと、8個のLEDを点灯させるためにデジタルピンが8本必要でしたが、シフトレジスタを利用することで3本のピンで制御できました。

shiftOut関数は複雑なパターンの出力を行う場合に有効です。しかし、毎回8ビットの更新となるため、8ビット以下の変化で出力するならば、digitalWrite関数による直接制御で効率あげられそうです。

1年前に初めてSN74HC595Nを使用した時は、この製品の仕様が一般的なシフトレジスタと思っていたけれど、「しなぷすのハード製作記 – シフトレジスタ」で、ストレージレジスタと出力制御は付加的な機能だと分かり、シフトレジスタへの理解が少し深まりました。