シフトレジスタ(74HC595)の使い方

今回はシリアルデータをパラレル出力するためのICである、74HC595の使い方を説明します。

このICは最低3本の制御線で、8本の出力ができます。また、74HC595を直列に繋ぐことにより制御線はそのままに、一個追加するごとに8本の出力を増設できます。

用途はというとマイコンの出力端子が足りないときに増設するときに利用します。ただ、マイコンから直接出力するよりも時間がかかるので、速度の必要な用途には厳しいかもしれませんが、LEDへの出力など低速(内蔵IOポートに比べて)でも大丈夫な用途に利用できます。

さらに、74HC595は普通のシフトレジスタとは異なり、データの送信中は出力端子の状態を変えずに、全てのデータを送信し終わってから任意のタイミングで一気に出力ポートを変化させることができるようにラッチが内蔵されています。これにより、遅い動作で駆動させても出力端子の余計な変化は起こりません。

今回はAVRのATtiny2313から74HC595を通して3本の制御線で8個のLEDを点灯させてみることにします。

2010年3月4日追記:
ATmega等のSPI機能がついたAVRを利用する場合はSPI機能を利用することをおすすめします。SPIをつかったシフトレジスタの操作はこちら。SPIを使うことにより、シフトレジスタへの送信がハードウエアで行われ倍近くの動作速度が期待できます。しかし、動作原理を知るには以下のようにポートを直接操作するプログラムのほうがわかりやすいので、こちらも読んでいただくことをおすすめします。

シフトレジスタ(74HC595)の実験

シフトレジスタ(74HC595)の実験

回路図

今回実験するために組んだ回路です。(電源等は省略)

74HC595実験回路図

74HC595実験回路図

74HC595の端子説明

SER : シリアルデータ入力。
SCK : シフトレジスタクロック。LoからHiになるときに内部シフトレジスタをシフトし、最下位ビットにSERの内容を追加。
RCK : ラッチクロック。内部のシフトレジスタを実際に外の端子に出力。
SCL : Loのままクロックを送信すると、シフトレジスタの中身が全て0になる。
G : Hiにすると出力端子がHi-Z(ハイインピーダンス:無接続とほぼ同じ状態)になる。
QA~QH : パラレル出力。
QH* : 直列に74HC595を接続する際に次の74HC595のSER端子に接続。(常にレジスタの最上位ビットを出力)

利用する際は、出力したい8ビットのデータを上位ビットからSERとSCKを使い74HC595に出力。
そして、RCKを Lo -> Hi にした時に出力端子に反映されます。

直列に74HC595を追加するときは、次のシフトレジスタのSERに前シフトレジスタのQH*を接続し、残りのクロック等は共通にするだけで、8ビットのシフトレジスタ以上に16ビットや、24ビット…..とビット数を増やすことができます。

AVRのプログラム

次のようなプログラムにしてみました。

重要なのはshift_out関数です。この関数が引数の8ビットのデータを74HC595に送信する部分です。
これを利用していろいろLEDを点滅させてみました。

#include <avr/io.h>
#include <util/delay.h>

#define SHIFT_PORT PORTB
#define SHIFT_DATA PB0
#define SHIFT_SCK PB1
#define SHIFT_RCK PB2

void delay_ms(uint16_t ms){
	while(ms--){
		_delay_ms(1);
	}
}

void _shift_sck(){	//シフトレジスタクロックを一つ送信
	SHIFT_PORT |= (1<<SHIFT_SCK);
	SHIFT_PORT &= ~(1<<SHIFT_SCK);
}
void _shift_rck(){	//ラッチクロックを一つ送信
	SHIFT_PORT &= ~(1<<SHIFT_RCK);
	SHIFT_PORT |= (1<<SHIFT_RCK);
}
void _shift_data(uint8_t bit){	//シリアルデータをSERに出力
	if(bit){
		SHIFT_PORT |= (1<<SHIFT_DATA);
	}else{
		SHIFT_PORT &= ~(1<<SHIFT_DATA);
	}
}

void shift_out(uint8_t data){
	int8_t i;
	for(i=7;i>=0;i--){	//上位ビットから8個送信
		_shift_data((data>>i)&1);
		_shift_sck();
	}
	_shift_rck();	//ラッチを更新
}

int main(){
	uint16_t i;
 	uint8_t t;
 	DDRB = 0b00000111;	//入出力設定
 	while(1){
 		//0から255までをカウントアップして出力
 		for(i=0;i<=0xff;i++){
 			shift_out(i);
 			delay_ms(30);
 		}

 		//バーを延ばしたり縮めたり
 		for(t=0;t<5;t++){
 			for(i=0;i<9;i++){
 				shift_out((0xff>>(8-(i%9))));
 				delay_ms(70);
 			}
 			for(i=0;i<9;i++){
 				shift_out((0xff>>(i%9)));
 				delay_ms(70);
 			}
 		}

 		//左右にスクロール
 		for(t=0;t<5;t++){
 			for(i=0;i<8;i++){
 				shift_out((1<<(i%8)));
 				delay_ms(70);
 			}
 			for(i=0;i<8;i++){
 				shift_out((0x80>>(i%8)));
 				delay_ms(70);
 			}
 		}

 	}
 	return 0;
}

実験風景

上記の回路図とプログラムを実際に動かしてみました。写真では伝わらないので動画を載せておきます。
LEDは左2つは余っており、左が下位ビット、右が上位ビットです。

この記事はAVRに投稿されました タグ: , , , , , . このパーマリンクをブックマークする。 コメントを投稿するか、トラックバックをどうぞ: トラックバック URL.

6 コメント

  1. kawana
    公開日時: 2010年1月6日 - 6:24 AM | パーマリンク

    私が、今研究しようとしたテ-マ-と、ばっちり一致するものでした。大変参考に
    成りました。なを
    58. for(i=0;i<9;i++){ がERRORに成ります。
    偏見的、希望
    LineNo.は削除したほうが、ビギナ-では、良いと思います。

    • 公開日時: 2010年1月6日 - 12:09 PM | パーマリンク

      コメントありがとうございます。
      少しでも参考になったのなら光栄です。

      58行目間違ってますね、訂正しておきました。
      確かに行番号はない方がコピーもしやすいですね。

  2. 公開日時: 2010年1月15日 - 11:17 PM | パーマリンク

    はじめまして。
    そら。と言います。

    分からない事があり、74HC595を検索していてここにたどり着きました。

    74HC595のデータシートで出力電流の最大定格が±35mAと書かれていますが、
    これは、1ポート毎なのでしょうか?

    もし、ご存知でしたら教えて下さい。

    では。

    • 公開日時: 2010年1月16日 - 8:59 PM | パーマリンク

      他のサイトの情報より、この問題は解決いたしました。
      失礼しました。

  3. tetsu
    公開日時: 2010年7月12日 - 10:08 PM | パーマリンク

    RCLK(ST_CP)の論理は逆ではないでしょうか?↑でラッチをホールドするとデータシート(および他のサイト)では読めます

    • 公開日時: 2010年7月12日 - 11:31 PM | パーマリンク

      ありがとうございます。確かに立ち上がりエッジでホールドですね。
      プログラムではクロックの方はどちらでもいいような、ラッチの方は正しいクロックにしていたので気づきませんでした。
      訂正しておきます。

5 トラックバック

コメントする

あなたのメールは 絶対に 公開されたり共有されたりしません。 * が付いている欄は必須項目です

*
*

次の HTML タグと属性が使用できます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>