2016.08.16
- 人×技術
大量のLED制御で光の表現を模索してみる
こんにちは、イシイです。
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搭載シフトレジスタボード
- 高輝度LED
- 抵抗 220Ω
- ジャンパーワイヤ
74HC595搭載シフトレジスタボードのピンヘッダハンダ付け
ハンダ付けにあたっては、複数のシフトレジスタボードを連結して使いやすいように、チュートリアルページを参考にして、L型ピンソケット・L型ピンヘッダ・ピンヘッダの3種類を使ってみました。
配線図
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制御してみた動画がこちらです。
なんか、表現の場で使えそうな感じがしてきました。