こんにちは、イシイです。

Arduino UnoのようにデジタルI/Oピンを14本しか持っていない基板で、ピン数以上のLEDを制御しようとしたとき、Arduino MegaのようにデジタルI/Oピンを54本も持っているようなものを使う方法もありますが、さらにそれ以上の数のLEDを制御する場合には、結局何かしらの仕組みが必要なので、ピンから枝葉のように分かれさせてアドレスみたいなのを割り当てて制御するような仕組みでもあるのかなと思い、ちょっと調べて試してみました。

シフトレジスタ

74HC595に代表されるような、1つのICで複数の出力に変換(シリアル→パラレル変換)できるシフトレジスタというものがあるようです。例えばこのICを1つ使うためには基板側の制御ピンを3つ使用することになりますが、3つのピンを使うことで8個の出力を行うことができます。さらに面白いことに、このICを直列に繋いでいくことで、制御ピンの数を変えずにICを繋いだ分だけ出力を増やすことができるようです。今回はこのICを利用しやすいように作られた74HC595搭載シフトレジスタボードというものを3つ使って、Arduino Unoから24個のLEDを制御してみます。

事前準備

使用するもの

74HC595搭載シフトレジスタボードのピンヘッダハンダ付け

ハンダ付けにあたっては、複数のシフトレジスタボードを連結して使いやすいように、チュートリアルページを参考にして、L型ピンソケット・L型ピンヘッダ・ピンヘッダの3種類を使ってみました。

74HC595搭載シフトレジスタボードにハンダ付け

配線図

配線図

Arduinoピン配置

ピン 接続先
5V ブレッドボード+
GND ブレッドボード-
D2 レジスタボード Clock
D4 レジスタボード L_Clock
D5 レジスタボード SER_IN

74HC595搭載シフトレジスタボードピン配置

ピン 接続先
SER_IN Arduino D5
L_Clock Arduino D4
Clock Arduino D2
/OE ブレッドボード-
/Reset ブレッドボード+
GND ブレッドボード-
VCC ブレッドボード+
A〜H 抵抗220Ωを挟んでLEDのアノード(足の長い方)

制御プログラム

今回LEDの制御については、openFrameworksで作ったアプリケーションから該当ピンのON/OFFデータをシリアル通信で受け取って制御するようにスケッチを作成してみました。

Arduinoスケッチの準備

チュートリアルページにあるShifterライブラリをArduinoのライブラリに格納しておきます。shifter.setPin(ピン番号, 状態)でLEDのON/OFFを設定し、shifter.write()で出力するようです。ピン番号は0から始まるのが注意点ですが、連結しているボードのピン番号は前のボードの続きの番号をセットすればいいみたいです。また、shifter.clear()ですべてのピンの状態をLOWに、shifter.setAll(1)ですべてのピンの状態をHIGHにできるようです。
そのため、テストしやすいようにシリアル通信で'@'を受け取ったらすべてのピンをLOWに、'+'を受け取ったらすべてのピンをHIGHに設定するようにし、その他の場合のみON/OFFデータを設定するようにしてみました。

#include <Shifter.h>

#define SER_Pin   5 //SER_IN
#define RCLK_Pin  4 //L_CLOCK
#define SRCLK_Pin 2 //CLOCK

#define NUM_REGISTERS 3 //74HC595の制御チェーン個数

//initaize shifter using the Shifter library
Shifter shifter(SER_Pin, RCLK_Pin, SRCLK_Pin, NUM_REGISTERS); 

#define ledTotalCount 24 //LED総数
char data[ledTotalCount]; //ONOFFビットデータ受け取り配列
int PinCount; //レジスタセットピン番号

void setup(){

  Serial.begin(115200);

  shifter.clear();
  shifter.write();

  setInit();

  Serial.println("Setup Finished");

}

void loop(){

  if (Serial.available() > 0) {

    data[PinCount] = Serial.read();

    if (data[PinCount] == '@') {

      setInit();
      shifter.clear();
      shifter.write();
      Serial.println("Data Cleared");

     } else if (data[PinCount] == '+') {

      shifter.setAll(1);
      shifter.write();
      Serial.println("Data All Set");

    } else {

      //LEDのON,OFFを設定
      setSignal(PinCount, data[PinCount]);
      
      PinCount++;

      //1行分のビットデータを設定し終わったので出力
      if (PinCount == ledTotalCount) {
        shifter.write();
        setInit();
      }

    }   

  }
  
}

//ピン番号の初期化
void setInit() {
  PinCount = 0;
}

//シフトレジスタにLEDのON,OFFを設定
void setSignal(int pos, char data) {

  int pinState;

  if (data == '1') {
    pinState= 1; //HIGH
  } else if (data == '0'){
    pinState= 0; //LOW
  }
  
  shifter.setPin(pos, pinState);

}

ofApp.h [oF側]

#ifndef __ShiftRegisterLED__ofApp__
#define __ShiftRegisterLED__ofApp__

#include "ofMain.h"

const unsigned int LIGHT  = 24; //ライトの個数
const unsigned int BAUDRATE = 115200; //シリアル通信速度

class ofApp : public ofBaseApp{
    
public:
    ~ofApp();
    void exit();
    void setup();
    void update();
    void draw();
    void keyReleased(int key);
    void outputCheckFile();    
    
    ofFile bitTextFile; //ビットテキストファイル    
    bool simurationMode;
    ofBuffer bitFileBuffer;
    
    ofSerial serial; //シリアル通信
    
};

#endif /* defined(__ShiftRegisterLED__ofApp__) */

ofApp.cpp [oF側]

openFrameworks側のプログラムで、ON/OFFのビットデータを読み込んでArduino側にシリアル通信で送り込みますが、LEDの順次点灯チェックがすぐにできるようなテキストデータを出力できる機能も入れておきます。

#include "ofApp.h"

//--------------------------------------------------------------
ofApp::~ofApp(){
}

//--------------------------------------------------------------
void ofApp::exit(){
}

//--------------------------------------------------------------
void ofApp::setup(){
    
    //環境設定
    ofSetFrameRate(30);
    ofBackground(0);
    ofSetVerticalSync(true);
    
    simurationMode = false;
    
    //シリアル通信
    serial.listDevices();
    vector <ofSerialDeviceInfo> deviceList = serial.getDeviceList();
    serial.setup(0, BAUDRATE);
    
}

//--------------------------------------------------------------
void ofApp::update(){
    
    //データシミュレーション用ON,OFFビットファイル読み込み
    if (simurationMode) {
        
        string line = bitFileBuffer.getNextLine();
        if(line.empty() == false) {
            
            //StringからCharにキャストして、1ビットづつ処理
            line.c_str();
            
            //ライトインスタンスにON,OFFを割当
            for (int i = 0; i < 24; i++) {
                
                char bit = line[i];
                serial.writeByte(bit);
                
            }
            
        } else {
            simurationMode = false;
        }
        
    }
    
}

//--------------------------------------------------------------
void ofApp::draw(){
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){
    
    switch (key)
    {
        case 'p'://シミュレーションモード
            
            bitFileBuffer = ofBufferFromFile("frameLightBit.txt");
            
            if(!bitFileBuffer.size()) {
                ofLog() << "BitFile can not read.";
            } else {
                simurationMode = !simurationMode;
            }
          
            break;
            
        case 'u'://ALL LED ON
            
            serial.writeByte('+');
            break;
            
        case 'c'://ALL LED OFF
            
            serial.writeByte('@');
            break;
            
        case 't'://LEDテスト用ビットファイル出力
            
            outputCheckFile();
            break;
            
    }
    
}

//--------------------------------------------------------------
void ofApp::outputCheckFile(){
    
    //ビットファイル出力
    bitTextFile.open("frameLightBit.txt",ofFile::WriteOnly);
    
    //LED1つ毎に3フレーム点灯するデータの生成
    for (int i = 0; i < LIGHT; i++) {
        
        for (int k = 0; k < 3; k++) {
            
            for (int j = 0; j < LIGHT; j++) {
                if (i == j) {
                    bitTextFile << "1";
                } else {
                    bitTextFile << "0";
                }
            }
            
            //ビットファイル1行につき、1フレームのビットデータを格納
            bitTextFile << "\r\n";
            
        }
        
    }
    
    //LEDテスト用ビットファイル出力終了
    bitTextFile.close();
    cout << "LED CheckFile Output End" << endl;
    
}

制御用ビットファイルデータ [oF側]

oF側で生成できるようにした、1個づつ順番にLEDを点灯させていくLEDテスト用ビットファイル

100000000000000000000000
100000000000000000000000
100000000000000000000000
010000000000000000000000
010000000000000000000000
010000000000000000000000
    ・
    ・
    ・
000000000000000000000001
000000000000000000000001
000000000000000000000001

実演動画

ちょっとアレンジしたON/OFFデータを使ってLED制御してみた動画がこちらです。

なんか、表現の場で使えそうな感じがしてきました。