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

投稿者: | 2018年4月11日

はじめに

Arduino UNO Rev3 は電源やGND以外に入出力ピンとして、デジタル14ピン、アナログ/デジタルの兼用6ピンが利用できます。1個のLチカならばデジタル1つとGNDの2つで行えます。しかし、LEDが8個なら8ピン、16個なら16ピンと、LEDの数だけピンが必要となり、他の用途に使うピンが足りなくなります。こんな時に出力を増やす方法として、シフトレジスタが利用できます。

現在、実際に購入して利用できる製品である汎用ロジックIC(SN74HC595N)を使用して利用方法を調べてまとめます。

Arduinoで使えるデジタルピンとアナログピンの数を間違えてたので訂正。アナログピンはデジタルピンとしても使えます(ATmega328の仕様)

概要

シフトレジスタの難しい説明はwikipediaの「シフトレジスタ」にありますが、工学的なことは後にまわして要点だけをまとめます。

シフトレジスタには大きく4種類あり、今回の用途で利用できるのは直列入力並列出力型(SIPO)だけです。

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

単体で利用できるシフトレジスタは汎用ロジックICとして販売されています。現在入手可能で扱いやすい製品として「SN74HC595」があります。

他にもありますが利用例が見つかりやすいので、SN74HC595Nを対象としました。

説明

データシートの入手

Texas Instrumentsの公式サイトからデータシートを取得して利用方法を調べます。

http://www.ti.com/product/SN74HC595

各ピンの役割

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

図は切り欠きを上にしてICを見た状態です。ピンは全部で16つあり各役割は以下のように説明されています。

ピン名前入出力説明
8GNDグラウンド
10SRCLR入力シフトレジスタのクリア
11SRCLK入力シフトレジスタのシフトを行う
12RCLK入力シフトレジスタからストアレジスタへ値を反映させる
13OE入力出力の有効化
14SER入力入力(シリアル)
16Vcc電源
15,1~7QA、QB〜QH出力出力(パラレル)
9QH’出力QHと同じ状態が出力

上線が付いている「SRCLROE」は負論理なので与える信号を反転させます(有効にする場合にLOW)。

入力と出力があるのはイメージ出来ますが、利用方法はシフトレジスタの動きが分からないとイメージし難いと思います。

SRCLR

シフトレジスタはレジスタなので値を記憶しています。SRCLRは、その記憶している値をクリア(全てLOW)にします。

OE

出力の制御だけを行うピンです。

OEピンの信号がLOWの場合は、ストアレジスタの内容がQA〜QHのピンに出力されます。

OEピンの信号がHIGHの場合は、QA〜QHのピンが未接続状態(LOWでもHIGHでもなくハイインピーダンス)になります。

OEピンの状態が変化してもストアレジスタの内容は変化しません。

SER、SRCLK

このピンの動作を理解するためには、少し内部の仕組みを知る必要があります。

SN74HC595にはシフトレジスタ(shift-register)とストアレジスタ(store-register)の2種類があります。

  1. SRCLKピンの信号がLOWからHIGHに変化した時に、シフトレジスタの値が下位から上位のレジスタへ1つシフトします。
  2. 最上位のレジスタの値は捨てられます。
  3. 最下位のレジスタにはSERピンの信号が記憶されます。

以上がシフトレジスタへ値を記憶させる手順になりますが、まだ、QA〜QHピンの出力には反映されません。

RCLK

RCLKピンの信号がLOWからHIGHに変化した時に、シフトレジスタの値がストアレジスタにコピーされます。コピー元のシフトレジスタの値は変化しません。

シフトレジスタが変化するたびにRCLKピンを変化させる必要はなく、シフトレジスタが出力したい状態になってからRCLKピンの信号を変化させて出力を行います。

QH'

Qから始まっているので紛らわしいですが、このピンはストアレジスタの状態ではなく、シフトレジスタの最上位のレジスタの状態が出力されています。なので、OEピンの状態に関係なく常に出力されています。

このピンは複数のSN74HC595(シフトレジスタIC)を利用する場合などで利用します。

備考

SRCLKピンとRCLKピンはCLK(CLOCK)ですが、規則正しい間隔の信号でなくても動作します。但し、速すぎるとシフトレジスタ側の出力信号が安定しません。ナノ秒レベルでの話なのでArduinoで利用するなら問題なさそうですが気に留めておきます。

実機検証

注意事項

多くのLEDを利用するので実機検証を行う前に考えておく事があります。

最大出力電流について

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

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

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

他のメーカーや機器により異なるのであまり大きな電流が流れないように計算が必要です。

SN74HC595の最大出力電流

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

LEDの制限抵抗値

LEDは5mm赤色LED OSDR5113Aを利用し、1つのLEDを8mAで点灯させる事にします。

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

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

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

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

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

Arduino UNO Rev.3と、1つのSN74HC595Nを使って8個のLEDをチカチカさせて、SN74HC595Nを利用する練習を行います。

接続方法

Arduino側のピンを減らすためクリアの機能は使用せず、SRCLRピンは5Vへ接続し常にHIGHしています。同じ理由で、OEピンはGNDへ接続し常にLOWとして、Qの出力を有効にしています。

パターン1 – digitalWrite

右から順番に点灯させます。

スケッチ
#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関数があります。この関数を使って複数のパターンで繰り返し点灯させます。

スケッチ
#define SRCLK   (5)
#define RCLK    (6)
#define SER     (7)

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

// 点灯パターン(1=点灯)
const uint8_t PATTERNS[] = {
  0b00011000,
  0b00110100,
  0b01110010,
  0b11110001,
  0b01110010,
  0b00110100
};

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を参照。・・・したらSN74HC595での使用例が載ってる!_(:3 」∠)ノ

結果

※ 結果の動画は、録画の終わりが中途ハンパなので繰り返し再生をすると少し飛んだ感じがします。

パターン3- SPI(非推奨)

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

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

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

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

スケッチ
#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=点灯)
const uint8_t PATTERNS[] = {
  0b11101000,
  0b11010100,
  0b10110010,
  0b01110001,
  0b10110010,
  0b11010100
};

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);
  }
}
結果

結果はパターン2と同じになります。

まとめ

Arduinoのみだと、8個のLEDを点灯させるために8つのピンが必要でしたが、シフトレジスタを利用することで3つのピンで処理できることが確認できました。

シフトレジスタに送る信号は、手動でも可能なくらいタイミングの制約が緩いのでプログラムを組むのはとても楽でした。汎用ロジックICは、今回利用したSN74HC595以外にも色々あるので、使えそうで安いのから試してみるのも面白そうです。