出力するピンが不足した場合に出力ピンを増やす方法

〔マイコン使用による電子部品の使い方に戻る〕


マイコン回路を製作していると出力するピンが足りないって言う場面が良くありますね。
そんな時に74HC595(3ステート・シフトレジスタ/ラッチ)のICを使えば簡単に増やす事が出来ます。

概念図  この左図を見て下さい、
 74HC595が1個辺り8ピンの出力を増やす事が可能です、
 しかも74HC595を何個増やそうとマイコンからの制御は3ピン
 のみです。
 マイコンからは出力データをシリアルで送信し74HC595は
 それをパラレルに変換して出力すると言う事です。
 74HC595の動きは
 @SCK信号の立ち上がりでSIの信号を読み取ります。
 ASI信号の内容をQAに移動、QAの内容をQBに移動、
  QB→QC...(次々にデータを送るのでシフトレジスタですね)
 B最後のQHの内容を次の74HC595のSI信号として出力、
  次の74HC595のQAへ
 CRCK信号の立ち上がりでQA〜QHの内容を実際に端子に
  出力します。(これがラッチ動作ですね)
ですから1バイト(8ビット)の内容をマイコンから74HC595の端子に送るには@〜Aを8回繰り返して CのRCK信号をONさせればOKと言う事になります。
だから、マイコンのI/Oから通常出力するよりも74HC595の端子から出力すれば動作が遅くなると言う事で、 高速にON/OFFさせる制御には不向きと言う事になります。
また、SCKの立ち上がりで動作するのだから立上りのタイミング間隔を短くすればそれだけ早く動作させる事が可能です。 (VCC=4.5Vで25MHz位はいけそうです。)
あ、それとぉ、マイコンの出力端子は20mA〜25mA程しか流せませんが、74HC595の端子は35mA程流せます。

尚、74HC595が出力ピンを増やすなら、入力ピンを増やすのが74HC165ですが今手元にないしぃ、
手に入れたら書くと言う事でそのうちですね。
手に入れたので書きました、「入力するピンが不足した場合に入力ピンを増やす方法」を見て下さい。

TC74HC595APのデータシートはこちらからダウンロードして下さい。
74HC595は2012/3/15日現在マルツパーツで136円、共立エレショップで126円です。
74HC595の様に複数個増やす事は出来ないですが、NJU3711が同じように操作できる様です、
これは秋月電子で80円と安いので今度手に入れたら実験してみたいと思います。
手に入れました、下記の方に記事を追記しました。 *2)

《Arduino(74HC595)》

配線図1
 74HC595が反対になっていますが、
 ピン1番が右上です間違えない様に。

 74HC595の4番ピンから出ている抵抗は
 ブレッドボード配線的には空中配線の
 様に見えますが配線図が描きにくい為に
 その様になってしまいました、実際に配線
 する時は、あんじょう配線してやぁ。

 実験風景1
普通のLEDを持っている人はそれに変更しても良いでしょう、配線スペースが確保できるかもね。
 (Fritzing用のLEDバー部品ダウンロードはこちらからお願いします。)

RGB LED バーについて

通常のLEDアレイと異なり、このRGBのLEDアレイは発光色ごとにカソードコモン接続となっているので配線に注意が必要でしょう。
でもぉ、コモンになっているのでぇ、配線少なくなりべんりよねぇ、ちょっとぉ高いけどね。
尚、LEDアレイは秋月電子のこちらから購入しています。
上記配線図の黄色い線を見て下さい1番ピンに接続されています、この配線で全LEDは赤色点灯です、 2番ピンに接続で青色、3番ピンに接続で緑色になります。
もちろん1と2番ピン共に接続すれば紫などの様になりますが、でもたぶん電流を上手く制御しないと混ぜるのは難しそうですね。(そのうちに覚えていたら実験してみよっとぉ)
仕様 赤→VF:2.1V IF:25mA  青→VF:3.1V IF:25mA  緑→VF:3.2V IF:25mA

実験T

まずは、IDEに下記のスケッチプログラムをコピーペーストして貼り付けて下さい。
IDEツールバーの「Upload」ボタンをクリックしてコンパイルとarduinoボードに書込みを行います。
---------------------------------------------------------------------
#define SCK 13                // クロック信号出力ピン
#define RCK 10                // ラッチ動作出力ピン
#define SI  11                // データ信号出力ピン

void setup() {
     pinMode(SCK, OUTPUT) ;   // 制御するピンは全て出力に設定する
     pinMode(RCK, OUTPUT) ;
     pinMode(SI,  OUTPUT) ;
     delay(3000) ;            // 3S後開始
}
void loop() {
     digitalWrite(RCK, LOW) ;
     shiftOut(SI, SCK, MSBFIRST, 5) ; // 5=0b00000101(0x05) 8ビット出力する
     digitalWrite(RCK, HIGH) ;        // ラッチ信号を出す
     while(1) ;                       // 処理中断
}
---------------------------------------------------------------------
Arduinoの標準関数でshiftOut()関数が有るのでそれを利用したスケッチです。
スケッチが動くとLEDバーの一番右端と右から3番目が点灯します。
SCKの13番ピンは他のピンに変更した方が良いかもですね、 プログラムをArduinoに書き込みしている時などにこのピンは出力されるのでゴミデータが表示されたりのでちょっとぉあれだなぁ。

秋月電子のLCDオシロキットで波形を見てみましょう

shiftOut()関数で出力した時の波形です。
 波形表示1  波形表示2
左がSCK(Arduinoの13番ピン)の出力で、右がRCK(Arduinoの10番ピン)の出力です。
左写真からビットのシフトは約15us毎だと分かります、8ビットなので15us x 8 = 120us程ですかね。
で右写真からはRCKが約140us毎に発生しているとすれば、 ArduinoのI/Oから直接出力する場合に比べて74HC595からの出力が約140us遅れて出力される事になりますね。

実験U

次のスケッチです、IDEに下記のスケッチプログラムをコピーペーストして貼り付けて下さい。
IDEツールバーの「Upload」ボタンをクリックしてコンパイルとarduinoボードに書込みを行います。
---------------------------------------------------------------------
#include "pins_arduino.h"
#include <SPI.h>

void setup() {
     SPI.begin() ;                         // SPIを行う為の初期化
     SPI.setBitOrder(MSBFIRST) ;           // ビットオーダー
     SPI.setClockDivider(SPI_CLOCK_DIV16) ;// クロックをシステムクロックの1/16で使用(16MHz/16)
     SPI.setDataMode(SPI_MODE0) ;          // クロック極性0(LOW) クロック位相0(LOW)
     digitalWrite(SS,LOW) ;                // SS(CS)ラインをLOWにする

     delay(3000) ; // 3S後開始
}
void loop() {
     digitalWrite(SS, LOW) ;
     SPI.transfer(5) ;            // 5=0b00000101(0x05) 8ビット出力する
     digitalWrite(SS, HIGH) ;     // ラッチ信号を出す
     while(1) ;                   // 処理中断
}
---------------------------------------------------------------------
今度はSPIでスケッチしてみました。
スケッチが動くと上と同様にLEDバーの一番右端と右から3番目が点灯します。
loop()関数を下の様に変更するとLEDバーの一番右端から左に向かってLEDが流れる様に表示されます。
---------------------------------------------------------------------
void loop() {
     int i ;

     for (i = 0 ; i < 8 ; i++) {
          digitalWrite(SS,LOW) ;
          SPI.transfer(1 << i) ;
          digitalWrite(SS,HIGH) ;
          delay(100);
     }
}
---------------------------------------------------------------------
秋月電子のLCDオシロキットで波形を見てみましょう

波形表示3  SCK(Arduinoの13番ピン)の出力です。
 SPI速度はシステムクロック/16だから16000000/16で1MHzです
 (1MHzは1秒間に1000000サイクル)
 って事は1サイクルは1000000us/1MHz(1000000)=1usです。
 左の写真からも2usに山が2個入っているので1usですね。
 で、8ビットのシフトだから山が8個ですね。
 このLCDオシロは1MHzまででこれ以上早い波形は無理です


波形表示4  SPIの速度をSPIライブラリで設定可能な最高の速度での観測
 SPI.setClockDivider(SPI_CLOCK_DIV2)
 システムクロック/2(8MHz)です、
 上の理由でSCKの波形観測は無理
 なのでRCKの波形を観測した写真が左です。
 約14us程の周期なので74HC595からの出力が約14us遅れるが
 shiftOut()関数の約1/10早いと言う事ですかね。


ただ、SPIにするとArduinoの12番端子(MISO)は使用しませんが、 他のピンとしての利用が出来ないので1ピン無駄になります。

《PIC 12F1822(74HC595)》

配線図2
 74HC595と12F1822が反対に
 なっていますが、
 ピン1番が右上です間違えない様に。

ピン構成図


実験風景2
 SPIで動作させる場合も配線は同じです
 また、SPIで動作させた場合はRA2(SDI)
 は使用しませんが、他のピンとして使用
 出来ません、注意して下さい。


実験V

@MPLAB X(v2.15)を起動させます。 *3)

A下記がプログラムソースです、
  MPLAB(R) XC8 C Compiler Version 1.32コンパイラを使用しています。 *3)
  プロジェクトを作成して新規ファイルにコピーペーストして貼り付けて下さい。

BコンパイルPIC書き込みを実行して下さい。 *3)
---------------------------------------------------------------------
#include <xc.h>

#define _XTAL_FREQ 16000000   // delay用に必要(クロック16MHzを指定)
#define RCK        RA4        // ラッチ信号出力ピン
#define SCK        RA1        // クロック動作出力ピン
#define SI         RA0        // データ信号出力ピン

char shiftData ;              // 74HC595に出力するシフトデータ

// コンフィギュレーション1の設定
#pragma config FOSC     = INTOSC   // 内部クロック使用する(INTOSC)
#pragma config WDTE     = OFF      // ウオッチドッグタイマー無し(OFF)
#pragma config PWRTE    = ON       // 電源ONから64ms後にプログラムを開始する(ON)
#pragma config MCLRE    = OFF      // 外部リセット信号は使用せずにデジタル入力(RA3)ピンとする(OFF)
#pragma config CP       = OFF      // プログラムメモリーを保護しない(OFF)
#pragma config CPD      = OFF      // データメモリーを保護しない(OFF)
#pragma config BOREN    = ON       // 電源電圧降下常時監視機能ON(ON)
#pragma config CLKOUTEN = OFF      // CLKOUTピンをRA4ピンで使用する(OFF)
#pragma config IESO     = OFF      // 外部・内部クロックの切替えでの起動はなし(OFF)
#pragma config FCMEN    = OFF      // 外部クロック監視しない(OFF)

// コンフィギュレーション2の設定
#pragma config WRT    = OFF        // Flashメモリーを保護しない(OFF)
#pragma config PLLEN  = OFF        // 動作クロックを32MHzでは動作させない(OFF)
#pragma config STVREN = ON         // スタックがオーバフローやアンダーフローしたらリセットをする(ON)
#pragma config BORV   = HI         // 電源電圧降下常時監視電圧(2.5V)設定(HI)
#pragma config LVP    = OFF        // 低電圧プログラミング機能使用しない(OFF)

// Arduinoの様にプログラムで処理して見た
// bitOrder: 0=LSBFIRST 1=MSBFIRST 
void shiftOut(int bitOrder, char value)
{
     int x , i ;
  
     for (i=0 ; i < 8 ; i++)  {
          SCK = 0 ;
          if (bitOrder == 0) x = i ;    // 0ビット目から順次出力
          else               x = 7-i ;  // 7ビット目から順次出力
          if (value & (1 << x)) x = 1 ;
          else                  x = 0 ;
          SI  = x ;                     // データビットのセット
          SCK = 1 ;                     // データ読み取り開始の指示
     }
}
// 出力するシフトデータのビットを操作する処理    *1)
void bitShiftData(int bitPosition, int bitValue, char *value)
{
     if (bitValue == 0) *value = *value & ~(1 << bitPosition) ;  // ビットリセット
     else               *value = *value | (1 << bitPosition) ;   // ビットセット
}
// メインの処理
void main()
{
	int i ;

     OSCCON = 0b01111010 ;     // 内部クロックは16MHzとする
     ANSELA = 0b00000000 ;     // アナログは使用しない(すべてデジタルI/Oに割当てる)
     TRISA  = 0b00001100 ;     // RA2ピン以外を全て出力に割当てる(RA3は入力専用)
     PORTA  = 0b00000000 ;     // 出力ピンの初期化(全てLOWにする)

     __delay_ms(3000) ;        // 3秒後に開始

     RCK = 0 ;
     shiftOut(1,5) ;     // MSBFIRST 5=0b00000101(0x05) 8ビット出力する
     RCK = 1 ;           // ラッチ信号を出す
     while(1) ;          // 処理中断
}
---------------------------------------------------------------------
PIC用shiftOut()関数を作成して見ました。
起動させると上の実験風景写真の様に(Arduinoと同じ動作)LEDが点灯します。

秋月電子のLCDオシロキットで波形を見てみましょう

 波形表示5
 SCK(RA1の6番ピン)の出力です。
 写真からビットのシフトは約23us毎だと分かります、
 8ビットなので23us x 8 = 184us程ですかね。
 ちなみに、動作クロック4MHzでは約90us程でした。
 PICのI/Oから直接出力する場合に比べて74HC595からの
 出力が約230us位は遅れて出力される事になりますね。


波形表示7  同じ16MHz動作でもArduinoはSCK出力が15usで
 PICは23usですねぇ。("Lite mode"だからか?)
 なら、MPLABでのコンパイルオプションを[Speed]にチェックを
 入れて実行させた波形が左です。
 約20us程ですか、気持ち早くなったのかな?。
 プログラムサイズは変化なかったのですがぁ、
 この程度のプログラムでは変化しないのかな?
 (そのうち、PROでもやって見るかなぁ?....)

早い気持ちになりたい人は、
MPLAB IDE(V8.63)のメニューバーから「Project」→「Build Options」→「Project」をクリック
表示されたウインドウの[Compiler]タブを選択し左下にある"Optimization settings"の"Speed"
オプションをチェックして下さい。(プログラムサイズが変化するかもです、注意ですね)

bitShiftData()関数について *1)

bitShiftData(bitPosition,bitValue,*value)
 74HC595に出力するシフトデータのビットを操作します。
 実際のプログラムで利用する場合はこの関数を使用した方が使いやすいでしょう。
  bitPosition :操作するビットの位置(0-7)を指定、右端(LSB)から数えて何ビット目か
  bitValue  :セットするビット値(0または1)
  *value   :シフトデータを保存している変数値を指定(この値を出力値として管理します)

  例)右から2個目のLEDが点灯します。
     char shiftData ;               // 74HC595に出力するシフトデータ
     
     shiftData = 0 ;

     bitShiftData(1,1,&shiftData) ; // 1ビット目をセットする
     RCK = 0 ;
     shiftOut(1,shiftData) ;        // 8ビット出力する
     RCK = 1 ;                      // ラッチ信号を出す
 ※ Arduinoもこの関数をこのまま利用出来ます。

《Arduino(NJU3711)》  *2)

NJU3711は複数個接続してのピン数は増やせません、1個の8ピンのみでの動作です。
74HC595はラッチ動作信号(RCK)の立ち上がりで出力しますが、
NJU3711はラッチ動作信号(STB)の立ち下がりで出力します。(下記スケッチ参照)
それと、NJU3711の動作可能クロック信号は5MHzと74HC595よりだいぶん低いです。
後の電気特性は両方ともだいたい同じ様です。

配線図3
 左が配線図です。
 NJU3711Dが反対になっています
 が、ピン1番が右上です間違えな
 い様に。

 SPIで動作させる場合も配線は
 同じです。

 NJU3711と74HC595のピン配置
 は異なります良く見て配線
 しましょう。

 あ、そうそう、
 NJU3711のデータシート等は
 こちらを参照下さい。


実験W

まずは、IDEに下記のスケッチプログラムをコピーペーストして貼り付けて下さい。
IDEツールバーの「Upload」ボタンをクリックしてコンパイルとarduinoボードに書込みを行います。
---------------------------------------------------------------------
#define CLK   13              // クロック信号出力ピン
#define STB   10              // ラッチ動作出力ピン
#define DATA  11              // データ信号出力ピン

void setup() {
     pinMode(CLK,  OUTPUT) ;  // 制御するピンは全て出力に設定する
     pinMode(STB,  OUTPUT) ;
     pinMode(DATA, OUTPUT) ;
     digitalWrite(STB, HIGH) ;
     delay(3000) ;            // 3S後開始
}
void loop() {
     shiftOut(DATA, CLK, MSBFIRST, 5) ; // 5=0b00000101(0x05) 8ビット出力する
     digitalWrite(STB, LOW) ;           // ラッチ信号を出す
     digitalWrite(STB, HIGH) ;
     while(1) ;                         // 処理中断
}
---------------------------------------------------------------------
Arduinoの標準関数でshiftOut()関数が有るのでそれを利用したスケッチです。
スケッチが動くとLEDバーの一番右端と右から3番目が点灯します。

Arduinoの電源投入時やリセット時又はスケッチプログラム書込み時等にゴミデータが出力されたり
します、それが嫌な場合は下の記事の様にしましょう。

配線図4
 上の配線図では、NJU3711の11番端子(橙色線:CLR)は+の電源に
 配線されていますが、これを左図の様に10KΩの抵抗でプルダウン
 させ、Arduinoのデジタル9番端子に接続(橙色線)します。
 それからスケッチを下記と入れ替えて実行して見て下さい、
 ゴミデータは出力されなくなります。
 でもぉ、これだとArduinoのI/Oを1本占有してしまうのですよねぇ......

---------------------------------------------------------------------
#define CLK   13              // クロック信号出力ピン
#define STB   10              // ラッチ動作出力ピン
#define DATA  11              // データ信号出力ピン
#define CLR    9              // リセット出力ピン

void setup() {
     pinMode(CLK,  OUTPUT) ;  // 制御するピンは全て出力に設定する
     pinMode(STB,  OUTPUT) ;
     pinMode(DATA, OUTPUT) ;
     pinMode(CLR,  OUTPUT) ;
     digitalWrite(STB, HIGH) ;
     digitalWrite(CLR, HIGH) ;// リセット解除
     delay(3000) ;            // 3S後開始
}
void loop() {
     shiftOut(DATA, CLK, MSBFIRST, 5) ; // 5=0b00000101(0x05) 8ビット出力する
     digitalWrite(STB, LOW) ;           // ラッチ信号を出す
     digitalWrite(STB, HIGH) ;
     while(1) ;                         // 処理中断
}
---------------------------------------------------------------------

PS. *4)
MCP23017と言うICが有ります、これは入出力を行ってくれる専用のICです。
16チャンネル有り其々のピンを入力/出力に割付ける事が可能です、又、16ピン全てで割込み入力と
プルアップも出来て通信はI2Cを利用します。
ですので出力を増やすならこれがお手軽です、この記事についてはこちらを参照下さい。

74HC595を使った、7セグメントLEDシリアルドライバモジュールの記事はこちらを参照下さい。



追記(*4) 2016/01/20
MPLAB X用に記事変更(*3) 2015/10/30
追記(*2) 2012/03/30
追記(*1) 2012/02/28


【きむ茶工房ガレージハウス】
Copyright (C) 2006-2016 Shigehiro Kimura All Rights Reserved.