INDEX(各項目ごとの目次)

[HOME]  [Processing関係]  [Arduino関係]  [マテリアル関係]  [秋葉原工作マップ]

2008年用ですが、部分的に内容を更新しています(2010/06/14)。
また、[建築農業工作ゼミ2009-2010]とも連動していますので、そちらにも幾つかサンプルがあります。
:

ラベル LED の投稿を表示しています。 すべての投稿を表示
ラベル LED の投稿を表示しています。 すべての投稿を表示

8/11/2008

Arduino+Processing マトリクスLED+Webカメラ

今回は、Webカメラから取り込んだ映像をArduinoに接続した8×8マトリクスLEDに映す実験を行います。まず、ProcessingでWebカメラからの映像を8×8ピクセルで取り込み、合計64個のピクセルの明るさの値(0〜255)を調べてから、その個々の値をシリアル通信でArduinoに送ります。Arduino側では、受け取った64個分の値をマトリクスLEDの個々の明るさに反映させます。Arduino基盤とマトリクスLEDとは、ICを使わず直結することにします(接続方法は「Arduino マトリクスLED1」を参照)。
Processingの画面では、マトリクスLEDの表示シミュレーション(モニタリング)を同時に行うことにします(前回行ったモザイク処理のような方法で赤い円を64個映し出すことにします)。


「Processingの画面(モニタリング)」

Processingのプログラム:

//ビデオライブラリを取り込む
import processing.video.*;
//キャプチャ用オブジェクトを用意
Capture video;

//シリアル通信ライブラリを取り込む
import processing.serial.*;
//シリアル通信オブジェクトを用意
Serial port;

//64個分のピクセル色の配列を用意
int[] pixelValue=new int[64];

//シリアル通信開始用フラグ
boolean start=false;

void setup(){
//画面を160角に設定
size(160,160);
//描画を滑らかにする
smooth();

//映像キャプチャの設定(幅8,高さ8ピクセル,フレームレート30)
video = new Capture(this, 8, 8, 30);

//ポートの設定
port=new Serial(this,"/dev/tty.usbserial-A40014iU",9600);

//外形線なし
noStroke();
}

void draw(){
//背景を黒で塗る
background(0);

//64個分のピクセルの処理
for(int i=0;i<64;i++){
//映像の各ピクセルの色の値を
//明るさの値に変換する
pixelValue[i]=int(brightness(video.pixels[i]));

//円の塗色(赤の値)に代入
fill(pixelValue[i],0,0);
//円を描画
ellipse((i%8)*20+10,(i/8)*20+10,15,15);

//値を送信
if(start){
port.write(pixelValue[i]);
}
}
}

//キャプチャ映像読み込み
void captureEvent(Capture video) {
video.read();
}

クリックでシリアル通信開始
void mousePressed(){
start=true;
}


Processingの方では、VideoライブラリSerialライブラリの二つを取り込む必要があります。マトリクスLEDが8×8の解像度なので、Webカメラから取り込む映像の解像度も8×8にしておきます(カメラ映像の横縦比は4:3なので、少し縦長の映像になってしまいます)。
*Windowsの場合、そのままの設定ではこのVideoライブラリを使用することができません。WinVDIG 1.0.1をインストールする必要があります。
「pixelValue[i]=int(brightness(video.pixels[i]))」では、まず映像の各ピクセルの色をpixels[]で読み込みます。pixels[]は、RGBの三色の値(三つの値)を含んでおり、brightness()で括ると明るさの値(一つの値)に変換されます(0〜255)。変換された値はfloat(小数)なのでint()で括って整数に変換しておきます。この値を、fill()の赤の値に代入し(緑と青は0)、LEDのような赤い円をellipse()を使って64個描画します。8×8を160×160の画面で表示しているので、20×20ピクセルのグリッド状に配置されます。ellipse()の直径はとりあえず15にしておきました。ellipse()のXとY座標は、「%」と「/」を使って計算します(X座標となる「(i%8)*20+10」は、iを8(横幅)で割った余りに20ピクセル掛けて、さらに10ピクセル足すことでellipse()が20×20のグリッドの中心に来るように位置調整しています)。最後に「port.write(pixelValue[i])」で、Arduinoへ各ピクセルの明るさの値を送信します。今回は画面をクリックしたらシリアル通信が開始されるようにしています。

次にArduinoの方に移ります。冒頭で書いたように、今回はマトリクスLEDを、ICを使わず直結します。個々のLEDはダイナミック点灯しているので、点灯時間の長さによって明るさを調整することになります。点灯時間が短ければ暗くなり、長くなれば明るくなります。つまり、Processingから送られて来た明るさの値(0〜255)を、個々のLEDの点灯時間に反映させるプログラムになるということです。マトリクスLEDとの接続方法や詳細については「Arduino マトリクスLED1」を参照してください。

Arduinoのプログラム:

//8x8の二次元配列を用意
byte matrix[8][8];

void setup(){
//出力ピンの設定、すべてオフにする
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
//シリアル通信開始
Serial.begin(9600);
}

void loop(){
//シリアル通信(64個分のデータ)
if(Serial.available()>63){
for(int k=0;k<8;k++){
for(int l=0;l<8;l++){
//読み込んだ値を配列に代入
matrix[k][l]=Serial.read();
}
}
}

//各LEDの点灯制御
for(int i=2;i<=9;i++){
//列の点灯
digitalWrite(i,HIGH);

for(int j=10;j<=17;j++){
//行の点灯
digitalWrite(j,LOW);
//行の点灯継続時間
delayMicroseconds(1+matrix[i-2][j-10]);
//行の消灯
digitalWrite(j,HIGH);
//行の消灯継続時間
delayMicroseconds(256-matrix[i-2][j-10]);
}
//列の消灯
digitalWrite(i,LOW);
}
}


以前の「Arduino マトリクスLED1」とほぼプログラム内容は同じです。異なる部分は、シリアル通信と各LEDを点灯/消灯させる継続時間の部分です。用意する二次元配列は、boolean型ではなくbyte型(0~255の値なので)にしています。
シリアル通信上で干渉しないようにするため、Arduino基盤の0番ピンと1番ピンには何も接続しないことにしています(2〜17番ピンを使用)。シリアル通信では、Processingから送られてくる64個のデータをSerial.available()でカウントして、それぞれの値を予め用意しておいた二次元配列matrixに保存しておきます。送られてくるデータは0〜255(明るさの値)となります。そしてダイナミック点灯していく際に、明るさの値をdelayMicroseconds()に代入して、点灯継続時間と消灯継続時間に割り当てます。delayMicroseconds(0)としてしまうと、0マイクロ秒としては扱ってくれないので、delayMicroseconds()の括弧内に入れられる最小値は1にしてあります。最小1マイクロ秒の点灯時間かつ最大256マイクロ秒の消灯時間のときが最も暗くなるときです。その逆で、最大256マイクロ秒の点灯時間かつ最小1マイクロ秒の消灯時間のときが最も明るくなります。個々のLEDは257マイクロ秒ごとにダイナミック点灯していることになります。個々のLEDの点滅を300マイクロ秒程度にすると点滅しているようには見えないので、今回の257マイクロ秒周期で点滅させれば、ほぼ問題ないでしょう。

関連:
・「シリアル通信1〜5
・「Arduino マトリクスLED1
・「Processing Video(Webカメラ)

ロジクールストア(ウェブカメラカテゴリ)

8/09/2008

Arduino マトリクスLED2/MAX7219

以前の「Arduino マトリクスLED1」では、8×8のマトリクスLEDをArduino基盤に直接接続して点灯させましたが、今回はMAX7219というLEDディスプレイドライバICを使って点灯してみます。Arduino基盤にマトリクスLEDを直結すると16個の端子が必要でしたが、MAX7219を使えば5V電源とGND以外に3本の端子で制御することが可能になります。原理的には、シリアルデータを送り、マトリクスLEDを表示させることになりますが、ArduinoにはマトリクスLED用のライブラリ(Wiringのライブラリ)があるので今回はそれを使ってプログラムしてみます。今回使用する8×8のマトリクスLEDは、以前同様、秋月電子で購入したものです。
以下のように、マトリクスLEDの各端子とMAX7219の端子が対応します(マトリクスLEDモジュールの側面に小さな凹凸部があり、MAX7219には端部に半円状の凹部や小さな丸印があるので、それらを手掛かりに向きを合わせてください)。



その他のMAX7219の端子については、
DIN:Arduinoからのシリアルデータ入力端子
DOUT:複数のMAX7219を接続する端子
VCC:5V電源
I_SET:接続する抵抗によって輝度を変えます
GND:AduinoのGNDと共有
LOAD:Arduinoからのデータ入力のロード用端子
CLK:Arduinoからのクロック信号入力端子

Arduino基盤との接続は以下のようになります。I_SETとLOAD端子には、10KΩの抵抗を接続して下さい(I_SETの抵抗値を上げれば暗くなり、下げれば明るくなります)。画像をクリックすれば大きくなります。



既存のサンプルを使って点灯実験してみます。Arduinoの画面からFile>Sketchbook>Examples>Library-Matrix>hello-matrixを選ぶと、プログラムが現れますが、ピン設定に少し変更を加えます。
既存サンプルでは、

pin 0: data (din)
pin 1: load (load)
pin 2: clock (clk)

となっていますが、0番ピンと1番ピンは使わず、

pin 2: data (din)
pin 3: load (load)
pin 4: clock (clk)

に変更することにします。こうすることで、プログラムのデータをアップロードする時やシリアル通信の際に信号が干渉されずにすみます。


//ライブラリを取り込む
#include <Sprite.h>
#include <Matrix.h>

//各ピンの設定
//pin 2: DIN
//pin 3: LOAD
//pin 4: CLK
Matrix myMatrix = Matrix(2, 4, 3);

void setup(){
}

void loop(){
myMatrix.clear(); //表示内容をクリアする

delay(1000);

//ピクセルを指定し表示する
myMatrix.write(1, 5, HIGH);
myMatrix.write(2, 2, HIGH);
myMatrix.write(2, 6, HIGH);
myMatrix.write(3, 6, HIGH);
myMatrix.write(4, 6, HIGH);
myMatrix.write(5, 2, HIGH);
myMatrix.write(5, 6, HIGH);
myMatrix.write(6, 5, HIGH);

delay(1000);
}


以下のように表示(1秒おきに点滅)されれば、配線などに問題ないことになります。write()で、X座標(0〜7)とY座標、ならびにHIGHかLOW(あるいは1か0)を代入し個々のLEDの点灯/消灯を指定します。



最初のArduino側のピンの設定(既存サンプルのコードに変更を加える)と、配線を間違わなければ、座標指定で点灯/消灯を制御できるので、それほど難しくはないと思います。今回のマトリクスLED用のライブラリを使うには、メニューバーのSketch>Import Library>MatrixとSpriteを選択すればインクルードされます。

次は、Spriteという基準となる絵柄を設定し、それが動く(横に流れる)表示方法を行います。Arduinoの画面からFile>Sketchbook>Examples>Library-Matrix>sprite-animationを選択し、このサンプルを使うことにします。前回同様、ピン設定は、

pin 0: data (din)
pin 1: load (load)
pin 2: clock (clk)

となっていますが、0番ピンと1番ピンは使わず、

pin 2: data (din)
pin 3: load (load)
pin 4: clock (clk)

に変更します。


//ライブラリを取り込む
#include <Sprite.h>
#include <Matrix.h>

//各ピン設定
//DIN:2
//LOAD:3
//CLK:4
Matrix myMatrix = Matrix(2, 4, 3);//DIN,CLK,LOAD

//スプライトの指定
//幅、高さ、絵柄を二進数で指定
Sprite wave = Sprite(
8, 4,
B00011000,
B00100100,
B01000010,
B10000001
);

void setup()
{
}

//X座標の変数を用意
int x = 0;

void loop(){
//スプライトの配置
myMatrix.write(x, 2, wave);
//もうひとつのスプライトの配置
myMatrix.write(x - 8, 2, wave);
//点灯時間設定
delay(75);
//画面をクリア
myMatrix.clear();

//アニメーション(カスケーディング)
if(x == 8){//8になったら0に戻す
x = 0;
}
x++;//X座標の変数を増加させる
}


Sprite()で、絵柄の幅、高さを指定し、二進数で点灯させる絵柄(0は消灯箇所、1は点灯箇所)をつくり設定します。write()で、絵柄の配置座標(左上の座標)と絵柄のオブジェクトを指定し表示させます。write()の括弧内のX座標やY座標は変数を用いて変化させることができるので、カスケーディング(絵柄が流れて表示される)の制御も簡単にできます。このサンプルでは、8コマ前にもうひとつの同じ絵柄を用意しておき、二つの絵柄を使用して途切れなく連続して絵柄が流れていくように表示しています。

また以下のようにSpriteを複数用意しておけば、入れ替わりで異なる絵柄を表示できます。


//ライブラリを取り込む
#include <Sprite.h>
#include <Matrix.h>

//各ピンの設定
//pin 2: DIN
//pin 3: LOAD
//pin 4: CLK
Matrix myMatrix = Matrix(2, 4, 3);

//スプライトの指定
//幅、高さ、絵柄を二進数で指定
Sprite pattern1 = Sprite(
8, 8,
B11111111,
B00000000,
B11111111,
B00000000,
B11111111,
B00000000,
B11111111,
B00000000
);

Sprite pattern2 = Sprite(
8, 8,
B10101010,
B10101010,
B10101010,
B10101010,
B10101010,
B10101010,
B10101010,
B10101010
);

Sprite pattern3 = Sprite(
8, 8,
B11110000,
B11110000,
B11110000,
B11110000,
B00001111,
B00001111,
B00001111,
B00001111
);

Sprite pattern3 = Sprite(
8, 8,
B00001111,
B00001111,
B00001111,
B00001111,
B11110000,
B11110000,
B11110000,
B11110000
);


void setup()
{
}

//X座標の変数を用意
int x = 0;

void loop(){
//スプライトの配置
myMatrix.write(0, 0, pattern1);
//点灯時間設定
delay(200);
//画面をクリア
myMatrix.clear();

myMatrix.write(0, 0, pattern2);
delay(200);
myMatrix.clear();

myMatrix.write(0, 0, pattern3);
delay(200);
myMatrix.clear();

myMatrix.write(0, 0, pattern4);
delay(200);
myMatrix.clear();
}


関連:
・「Arduino マトリクスLED1」(ICを使わずマトリクスLEDを制御する方法)
MAX7219データシート(MAXIM JAPAN)

7/05/2008

Arduino マトリクスLED1

今回はマトリクス LEDの表示実験をします。秋月電子で購入した8×8マトリクスLEDを使用します。8×8なので合計64個のLEDが搭載されています。それぞれのLEDを直接点灯させるためには、64個分の端子が必要であり、Arduinoの端子の数以上になってしまいますが、ダイナミック点灯(説明以下)という方法で可能になります。
ArduinoにはマトリクスLED用ライブラリWiringのライブラリを利用)を使う方法もありますが、MAX7219というLEDディスプレイドライバICを必要とします。このICを使えば、Arduinoからはシリアル通信を通して3本の線で制御することができます(MAX7219との接続サンプル)。また、74HC595というICを二つ使う方法(サンプル)もあります。
今回はICを使わずに、マトリクスLEDの16個の端子にArduinoを接続する方法で制御します。16個の端子のうち8個がアノード(プラス)で残り8個がカソード(マイナス)の端子になります。LEDの点灯箇所と端子の対応は以下のようになります。LEDモジュールの4辺(側面)には小さな凹凸があるので、それを手掛かりに向きを合わせて下さい。



Arduinoからはダイナミック点灯という方法で制御することになります。そうすることで、合計16本の端子で64個のLEDを個別に制御することができます。ダイナミック点灯は、順番に一列(8個のLED)ずつ高速点灯させ、人間の目には8列全部が同時に点灯しているように見せる方法です。列ごとに点灯させる順番やタイミングをdigitalWrite()のHIGH/LOWの組合わせで制御します。基本的には、横方向の端子にプラスを、縦方向の端子にマイナスを接続することで、その交差した部分のLEDが点灯する仕組みになっています。
Arduinoのデジタル出力ピンは、通常0から13番までしかないのですが、pinMode()で設定することで、アナログ入力の6個のピン(0から5番ピン)もデジタル出力用に切り替えることができます。その場合、順番にデジタル出力14から19番のピンとして扱うことができ、合計で20個のデジタル出力が可能になります。もともとデジタル入出力ピンの0番と1番はシリアル通信などで使うので(プログラムをアップロードするときにも干渉することがあるので)、できれば接続しないほうがいいでしょう。今回は2番ピンから17番ピンまでを使うことにします。
LEDのカソード側(マイナス側)には抵抗(1KΩ)を取付けます。Arduinoのデジタル出力13番ピンには既に1KΩの抵抗が内蔵されているので、それ以外の7端子に取付けることとします。抵抗をつけなくても多少負荷はかかりますが実験はできます。ただし、Arduinoの13番ピンを接続している列(4列目)だけが、暗くなってしまいます。
接続方法は以下のようになります(画像をクリックすれば大きくなります)。ブレッドボードで実験する場合、大きなもの(幅のあるもの)を用意するか、小さなブレッドボードを2枚用意して、2枚にまたがるようにLEDモジュールを差し込むと作業しやすいと思います。



それぞれのArduinoの出力ピンで行と列で表せば、以下のようになります。



まず、一行ずつ点灯させていきます。
PIN_2行目のPIN_10列目とPIN_12列目の二つを点灯させるためには、

PIN_2:HIGH
PIN_10:LOW
PIN_12:LOW

となりますが、同時に消灯させる列もあるので

PIN_11:HIGH
PIN_13:HIGH
PIN_14:HIGH
PIN_15:HIGH
PIN_16:HIGH
PIN_17:HIGH

とします。行(PIN_2〜PIN_9)、列(PIN_10〜PIN17)とすれば、
点灯させるには、

行:HIGH、列:LOW

という組合わせになり、
消灯させるには、

行:HIGH、列:HIGH
行:LOW、列:HIGH
行:LOW、列:LOW

という3つの組合わせがあります。列(縦)側の端子と行(横)の端子の両方をHIGH(5V)にすると消灯するということを覚えておいて下さい(電位差が0Vになるので)。それ以外の「LOW:HIGH」、「LOW:LOW」の組合わせでも消灯します。

次のPIN_3行目に移る前に(ある程度の時間点灯させた後に)PIN_2行目をLOWにすることで、次回PIN_3行目を制御するときにPIN_2行目が点灯しないように後処理しておきます。PIN_3行目についても同様の手順で行い、合計8回高速に繰り返すことで、全体が点灯しているように見えます。

まずは、以下のプログラムで、64個のLEDが順番に個別に点灯するか実験してみます。


void setup(){
//16本のピン(2~17)を出力に設定
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
}
}
void loop(){
//行(横)の繰り返し処理
for(int i=2;i<=9;i++){ //行(2~9番ピン)
digitalWrite(i,HIGH); //HIGHで点灯

//列(縦)の繰り返し処理
for(int j=10;j<=17;j++){ //列(10~17番ピン)
digitalWrite(j,LOW); //LOWで点灯
delay(100); //点灯時間
digitalWrite(j,HIGH); //列をオフにする
}

digitalWrite(i,LOW); //行をオフにする
}
}


それぞれ一つずつ順番に点灯していけば、配線などに間違いがないということになります。プログラムの順番としては、1行目の中の1列目から8列目までを順番に点灯し、次に2行目の中の1列目から8列目までを順番に点灯し、同様に8行目まで繰り返します。delay(100)の部分が一つのLEDの点灯時間であり、0.1秒に設定されています。この点灯時間を短くしていくと、残像現象により一度に複数のLEDが点灯しているように見え始めます。次のサンプルでは、点灯時間を0.03秒に設定し、二次元配列を用いて、予め用意しておいた表に基づいて点灯させる方法を行います。


boolean matrix[8][8]={
{0,0,0,1,1,0,0,0},
{0,0,1,0,0,1,0,0},
{0,1,0,0,0,0,1,0},
{0,1,0,0,0,0,1,0},
{0,1,0,0,0,0,1,0},
{0,1,1,1,1,1,1,0},
{0,1,0,0,0,0,1,0},
{0,1,0,0,0,0,1,0}
};

void setup(){
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
}
void loop(){
for(int i=2;i<=9;i++){
digitalWrite(i,HIGH); //行:HIGHで点灯
for(int j=10;j<=17;j++){
if(matrix[i-2][j-10]==1){//点灯条件
digitalWrite(j,LOW); //列:LOWで点灯
}
//上のif文のかわりに以下でも可
//digitalWrite(j,!matrix[i-2][j-10]);

delayMicroseconds(300);//0.03秒点灯
digitalWrite(j,HIGH);//オフにする
}
digitalWrite(i,LOW);//オフにする
}
}


8×8の二次元配列matrix(名前は任意)を用意して、その配列内に0か1で消灯/点灯の表をつくります。matrix[行][列]という対応になります。matrix[0][3]であれば、0行3列目の値となります。
このフォーマットをもとに、とりあえず「A」という文字をつくってみました。表の「1」のところを点灯させるために、if文で条件設定し、表座標の値が「1」なら、その箇所をLOWで出力します。今回接続しているピンの番号と配列の順番の数値のつじつまを合わせるために、「matrix[i-2][j-10]」としています(行:2番ピンが0番目の内部配列に対応するので[i-2]、列:10番ピンが内部配列内の0個目の値に対応するので[j-10]になります)。if文を使わずに、digitalWrite(j,!matrix[i-2][j-10])と書いても同じことになります。今回は、LOWで点灯するので、「!」を使って表座標の値が「0」のとき「1」(HIGH)になり、「1」のとき「0」(LOW)になるように反転します。
delayMicroseconds(300)は、一つずつ高速に点滅する時間です。高速なので点滅しているようには見えませんが、もし点滅しているように見えてしまう場合は数値を低くして、点滅のスピードを上げて下さい。
このように二次元配列matrixを使うことで、64個分のLEDの点灯/消灯状態を指定して表示可能になります。

次は表示文字をカスケーディング(文字が流れるように動く)してみたいと思います。左向きに文字が流れるようにするには、

matrix[k][l]=matrix[k][l+1];

というように、配列内の縦一列の値を右隣の値(+1の列の値)に移し替えればいいことになります。さらに、左側へ流れた文字が再び右側から出てくるように繰り返して表示されるようにするためには、画面右端の8列目の値(配列内7番目の値)が、1列目(配列内0番目の値)になるようにします。使用している二次元配列は8×8ですが、余白をもう一列つけたして8×9にしておきます。それで、

matrix[k][8]=matrix[k][0];

とすれば、余白である9列目(配列内の8番目の値)に1列目(配列内の0番目の値)が代入され、繰り返し表示されることになります。しかしこのままでは、横に流れるスピードが速すぎるので、while文を用いて表示される時間を引き延ばします。while文では、以下のように()内に条件を入れ、その条件が満たされている限り繰り返し処理を行います。

int count=5;
while(count>0){
//繰り返される内容をここに書く

count--; //カウント数を減らしていく
}

という書き方をすれば、while(){...}内の処理を5回繰り返すということになります。つまり、先ほどのLEDを順番に点灯させるプログラム全体をwhile(){...}で括ってしまうということになります。以下のプログラムでは、1ループの中で、LEDを表示させる処理を5回繰り返し、それからカスケーディングのための処理を1回行う内容になります。


//余白の列を付けたし配列を8x9にしておく
boolean matrix[8][9]={
{0,0,0,1,1,0,0,0,0},
{0,0,1,0,0,1,0,0,0},
{0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0},
{0,1,1,1,1,1,1,0,0},
{0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0}
};

void setup(){
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
}
void loop(){
int count=5;//この値を大きくすればゆっくり流れる
while(count>0){
for(int i=2;i<=9;i++){
digitalWrite(i,HIGH); //行:HIGHで点灯
for(int j=10;j<=17;j++){
if(matrix[i-2][j-10]==1){//点灯条件
digitalWrite(j,LOW); //列:LOWで点灯
}
//上のif文のかわりに以下でも可
//digitalWrite(j,!matrix[i-2][j-10]);

delayMicroseconds(300);
digitalWrite(j,HIGH);//オフにする
}
digitalWrite(i,LOW);//オフにする
}
count--;//回数カウント1回減らす
}

//カスケーディング
for(int k=0;k<=7;k++){
//0列目から余白の8列目まで計算する
for(int l=0;l<=8;l++){
if(l==8){ //配列8列目は0列目の値を代入
matrix[k][8]=matrix[k][0];
}else{ //それ以外の列は+1列の値を代入
matrix[k][l]=matrix[k][l+1];
}
}
}
}


また、配列を大きくして以下のようにすれば、複数の文字を表示できます。


//8x25の配列にする(25列目は余白)
boolean matrix[8][25]={
{0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0},
{0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0},
{0,1,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0}
};

void setup(){
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
}
void loop(){
//変更なし
int count=5;
while(count>0){
for(int i=2;i<=9;i++){
digitalWrite(i,HIGH);
for(int j=10;j<=17;j++){
digitalWrite(j,!matrix[i-2][j-10]);
delayMicroseconds(300);
digitalWrite(j,HIGH); //LED OFF
}
digitalWrite(i,LOW); //LED OFF
}
count--;
}

//カスケーディング
for(int k=0;k<8;k++){
//以下の配列数の値を変更しておく
for(int l=0;l<=24;l++){
if(l==24){
matrix[k][24]=matrix[k][0];
}else{
matrix[k][l]=matrix[k][l+1];
}
}
}
}


今回のプログラムでは、loop(){...}内に、while(){...}という小さなループがあり、その中に、for(){...}で横1行ずつの繰り返し処理を行い、さらにその中にもうひとつのfor(){...}で縦1列ずつの繰り返し処理を行うというように、何重にも繰り返しループの処理が組み込まれています。結果的なコードを見ると分かりにくいかもしれませんが、最初から順を追って考えていけば、その仕組みが見えてくると思います。

関連:MAX7219(LEDディスプレイドライバIC)を用いる方法


5/26/2008

Arduino 圧電スピーカ

圧電スピーカは、ブザー(音が鳴る)として機能する一種のスピーカです。圧電スピーカについている2本の線に、そのまま電気を流しても音は鳴りません。音を出すためには、高速でオンとオフを繰り返し(パルス)、内部の金属板を振動させます。プログラム上では、digitalWrite()を用いてHIGHとLOWの切替を行い、delay()によってオン/オフの時間の間隔(周波数)をつくりだすことで制御することができます。
以下では、前回使用した可変抵抗器を用いて、可変的に周波数をつくりだし、圧電スピーカを鳴らす実験をしてみます。

Arduinoのプログラム:
int val=0;

void setup(){
pinMode(13,OUTPUT);
}

void loop(){
val=analogRead(0);
digitalWrite(13,HIGH);
delay(val);
digitalWrite(13,LOW);
delay(val);
}

音はでますが、あまりいい音ではないので、周波数を細かくするために、delay()のかわりにdelayMicroseconds()を用いて同様にテストしてみます。delayMicroseconds()は、delay()の1/1000の時間、つまり1マイクロ秒(1/1000000秒)が単位となります。高音領域が高周波になりすぎないように、valに予め+500のオフセットを設け、500〜1523までの値がdelayMicroseconds()に入ることにします。ちなみに、Arduinoサイトの説明によると、delayMicroseconds()の()内に入れられる数値は、最大で「16383」であり、delayMicroseconds(0)というように()内に「0」を入れると0秒ではなく、それよりも長い時間(~1020マイクロ秒)ディレイしてしまうと書いてあります。設定するときは注意して下さい。

int val=0;

void setup(){
pinMode(13,OUTPUT);
}

void loop(){
val=analogRead(0)+500;
digitalWrite(13,HIGH);
delayMicroseconds(val);
digitalWrite(13,LOW);
delayMicroseconds(val);
}

圧電スピーカは、音を鳴らす以外にセンサとして使うこともできます。圧電スピーカとLEDを直結し、圧電スピーカを指先でたたいて衝撃を与えると電源がなくてもLEDが一瞬発光します。

この発電原理を利用して、圧電スピーカをマイクのような衝撃センサとして用いることが可能となります。圧電スピーカからの電圧をanalogRead()で読み取って、どの程度の値が得られるかテストしてみます。
読み取り値などを画面に出力するには、シリアル通信機能を用いて以下のようなプログラムを付け足し、プログラムが開始したら、Arduinoの画面上のSerial Monitorボタンを押します。

初期設定のSerial.begin(9600)は、通信速度を9600に設定し、シリアル通信を開始するという意味です。Serial.println(val)は、モニタリングするためにvalの値をシリアル通信を用いて出力します。Serial.println()は、データを毎回改行しながら出力します。もうひとつSerial.print()という、改行せずにそのままデータを送り出すものもあります。今回はモニタリングするために、改行して出力したほうが見やすいので、Serial.println()の方を使います。

圧電スピーカに衝撃を与えると値が変化することが確認できます。出力される値が10以上であれば、衝撃を加えたことに反応しているとみなすこととします。以下に、圧電スピーカからの衝撃によってオン/オフするLEDのプログラムを書きます。boolean型の変数を用いて、以前Processingで用いたトグルスイッチのプログラムを付け足します。boolean型の変数checkがfalseの時はLEDがオフの状態、checkがtrueの時はオンの状態とします。
int val=0;
boolean check=false;

void setup(){
//13番ピンをLEDの出力に設定
pinMode(13,OUTPUT);
}

void loop(){
//圧電スピーカ0番ピンの読み取り値
val=analogRead(0);
//読み取り値が10以上の場合
if(val>10){
if(check==false){ //LEDオフ状態の場合
digitalWrite(13,HIGH); //オンに切替
check=true; //オンの状態として記憶
}else{ //LEDオン状態の場合
digitalWrite(13,LOW); //オフに切替
check=false; //オフの状態として記憶
}
}
delay(100);
}


Arduinoのサイト内のLearning/Examples/SoundページにもPlay Melodies with a Piezo Speakerという名前で、圧電スピーカのサンプルが掲載されています。

Arduino アナログ入出力

前回までは、LEDをオン/オフ(5V/0V)するプログラムでした。今回は、オン/オフの制御ではなく、外部からの入力によってLEDの明暗を変化させるプログラムをします。
analogWrite()を用いれば、0〜255の256段階でLEDの明るさが調節できます。モータの出力に使えば、スピード調節が可能となります。PWM(パルス幅変調)という方法で256段階を調整しますが、パルスについては後で説明したいと思います。まずはanalogWrite()の使い方からマスターしていくこととします。
analogWrite(ピン番号,出力値)というように二つの値を設定する必要があります。「ピン番号」は出力したいピンの番号を入れますが、Arduino基盤の0〜13番ピンのうちの3、5、6、9、10、11の6つのピン(基盤上のピン番号下にPWMと書かれている番号)のどれかになります。「出力値」は、0〜255(0V〜5Vに対応)の値をいれます。
analogWrite(3,0);

と書けば、3番ピンを0(0V)で出力となるので、LEDであれば消灯します。
analogWrite(3,255);

であれば、一番明るい状態となり、
analogWrite(3,127);

であれば、約半分の明るさとなります。

次に、外部からの入力(センサ入力)の際に使用するanalogRead()について説明します。analogRead()は、0〜1023の1024段階で値を読み取ることができ、0〜1023が0V〜5Vに対応しています。Arduino基盤の右下に「ANALOG IN」と書かれた0〜5番ピンを使用します。
analogRead(0);

と書けば、「ANALOG IN」の0番ピンに接続したワイヤからの電圧を読み取って0〜1023の値が得られることになります。
入力用に使われるセンサは様々なものがありますが、今回は「可変抵抗器(ボリューム)」を使用することにします。一般的な可変抵抗器には3つの端子があり、両端の二つの端子を0V(GND)と5Vにつなぎ、ツマミを回すと中央の端子から任意の電圧(0V〜5V)が出力されます。つまり、中央の端子からの可変電圧をanalogRead()で読み取って、その入力値をanalogWrite()の出力値に入れれば、LEDの明るさをツマミをひねることで調整できるようになります。
ひとつ注意しなければいけないことは、analogRead()によって得られる値は0〜1023に対して、analogWrite()の出力値が0〜255までなので、入力値(読み取り値)を4で割った値を出力値に入れないといけません。
尚、analogWrite()analogRead()の場合は、初期設定でpinMode()の入出力を設定せずに直接使うことが出来ます。

以下に、analogWrite()analogRead()を用いて、外部からの入力(可変抵抗器)によってLEDの明るさを変えるプログラムを書きます。
int val=0; //入力値の変数を用意し、0に設定

void setup(){
//pinMode()の設定は不要
}

void loop(){
//ANALOG INの0番ピンを読み取りvalに代入
val=analogRead(0);
//アナログ出力(PWM)の3番ピンを出力とし
//valを4で割った値を入れる
analogWrite(3,val/4);
//0.1秒ループにする
delay(100);
}


可変抵抗器のツマミの回し方(時計回り/半時計回り)と出力値の増減の向きを変えたい場合は、可変抵抗器の両端の端子(0V端子/5V端子)を入れ替えてください。

5/18/2008

Arduino 7セグLEDの点灯

7セグLEDは、数字の形を表示するための細長い7個のセグメントと数字右下にある「.(ドット)」の合計8個のLEDで構成されています。つまり8個のLEDを制御するプログラムになるということです。授業で用いた7セグLEDは、秋月電子で購入したものでデータシートは付属していましたが、部品単体で販売されていることもあるので、その際にはメーカーや型番をインターネットで検索しデータシートを探し出すか、自分自身でどのセグメントがどの端子に対応しているのかなどをテストする必要があります。今回の7セグLEDは、アノードコモンタイプ、8個のLED、10本の端子です。アノードコモンタイプは、8個のLEDのプラス側の端子を共有するつくりになっています。
Arduino基盤につなぐには、Arduinoの電圧が5Vに対してLEDの使用電圧が2.5V前後なので、前回同様、抵抗(220〜470Ω程度)が必要になります。一つの抵抗を8個のLEDに対して共有することもできないわけでもないのですが、数字を表示する際には、「0」の場合は6個のLEDセグメント、「1」の場合は2個のLEDセグメントを発光させるというように、その数字によって、発光させるLEDセグメントの数が異なるので、セグメント数が少ないほど明るく発光してしまいます。それぞれの発光を均一にするためにも、抵抗はセグメントの数と同じだけ必要となります。今回はアノードコモン(プラス側の端子は共有)なので、各抵抗はカソード側(マイナス)に接続する回路にします。
1秒ごとに0〜9までカウントアップしていき、それをループするプログラムにしたいと思います。数字右下の「.(ドット)」は今回使わないで、合計7個のLEDセグメントを制御します。

まず、7セグLED裏面に10本端子があるので、どの端子がどのセグメントであるか、ひとつずつ点灯実験してみます(Arduino基盤の5V端子とGND端子に接続してテストしますが、この際にも抵抗を直列つなぎしてください)。ブレッドボードとジャンプワイヤがあると便利です。7セグLEDの場合、共有端子が真ん中にあることが多いので、データシートがない場合は、そのようなことを想定してテストしてみるといいかもしれません。



それぞれの端子は、上図のように各セグメントに対応していることが分かりました。cとhはアノード共有端子です。今回は、eのドットは使わず、a、b、d、f、g、i、jの7個を制御表示させることにします。例えば「1」を表示させるには、dとfをオンにし、残りはオフになるように設定します。アノードコモンであることから、cにはプラスの電圧がかかり、dとfを発光させるには、dとfがマイナスにならなければいけません。前回用いた図の「抵抗をカソード側につけた場合」を参考とし、a、b、d、f、g、i、jの7個の端子にそれぞれ別個の抵抗(220Ω)を取付け、Arduino基盤のデジタル出力の端子に接続します。
とりあえず、aの端子をArduino基盤の1番ピン、bを 2番ピンという順番で以下のようにつなげることにします。
a---1番ピン
b---2番ピン
d---3番ピン
f---4番ピン
g---5番ピン
i---6番ピン
j---7番ピン



「1」を表示するためには、dとfを0V(LOW)にすることで電位差が生じ発光します(cの端子から5V、dのLEDを通して2.5Vに降下し、抵抗によって2.5Vから0Vに降下する)。プログラム上では、
digitalWrite(3,LOW);
digitalWrite(4,LOW);

に設定すると3番ピン(dのLED)とが4番ピン(fのLED)が発光し、「1」の数字が表示されることになります。ただし、光らせたいピンだけではなく、残りのピンを光らせないプログラムも必要となります。残りのピンについては、すべてLOWのかわりにHIGHに設定します。よって、「1」を表示させるプログラムは以下のようになります。
void setup(){
//1〜7番ピンを出力に設定
pinMode(1,OUTPUT);
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
}

void loop(){
//「1」を表示
digitalWrite(1,HIGH);
digitalWrite(2,HIGH);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
delay(1000);
}

他の数字に関しても同様にプログラムしていきます(ここでは省略して、0〜3までの表示とします)。
void setup(){
//1〜7番ピンを出力に設定
pinMode(1,OUTPUT);
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
}

void loop(){
//「0」を表示
digitalWrite(1,LOW);
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,LOW);
digitalWrite(7,HIGH);
delay(1000);
//「1」を表示
digitalWrite(1,HIGH);
digitalWrite(2,HIGH);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
delay(1000);
//「2」を表示
digitalWrite(1,LOW);
digitalWrite(2,LOW);
digitalWrite(3,HIGH);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
delay(1000);
//「3」を表示
digitalWrite(1,HIGH);
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
delay(1000);
}

setup()の中のpinMode()for文という繰り返しの構文を用いた書き方にします。以下のfor()の中の「int i=1; i<=7; i++」は、1から7までを順番(+1ずつ)に7回分繰り返し処理するという意味です。もし「int i=3; i<=5; i++」と書いていれば、3から5までの3回分繰り返し処理することになります。この場合なら、3から5がpinMode()内のiに代入されるので、3〜5番ピンをOUTPUTに設定するということになります。「int i=1; i<=7; i+=2」と書けば、「i++」の部分を「i+=2」にしたので、1〜7までを+2ずつということになり、「1、3、5、7」だけを処理させるということになります。上記プログラムでは、1〜7番ピンを一気にOUTPUTに設定したいので、以下のように「int i=1; i<=7; i++」とします。
それから、loop()の中が長くなりすぎて見づらいので、それぞれの数字の表示に対してファンクション名(任意の名前)をつけて、以下のようにloop()の外に定義しておくことができます。
void setup(){
for(int i=1; i<=7;i++){ //iを1から7までの数とし
pinMode(i,OUTPUT); //1から7までのピンを出力に設定
}
}

void loop(){
zero();
delay(1000);
one();
delay(1000);
two();
delay(1000);
three();
delay(1000);
}

void zero(){
digitalWrite(1,LOW);
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,LOW);
digitalWrite(7,HIGH);
}

void one(){
digitalWrite(1,HIGH);
digitalWrite(2,HIGH);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
}

void two(){
digitalWrite(1,LOW);
digitalWrite(2,LOW);
digitalWrite(3,HIGH);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
}

void three(){
digitalWrite(1,HIGH);
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
}

このように各数字に対するファンクションを定義することで、必要に応じてloop()内でファンクション名を呼び出す使い方が可能になります。loop()内では、全体の流れが上から順に書かれており、loop()外には、それぞれの数字の内訳のプログラムが書いてあり、全体と部分という関係でプログラムしていくことができます。部分(数字の内訳のプログラム)を修正すれば、loop()内で処理される全体の流れにもすぐ反映され、全体の流れだけを修正したい時には、いちいち部分のプログラム内容に触れなくても済むという利点があります。

また、数字がカウントアップしていくプログラムであれば、更に以下のように配列(Array)を利用して、反復する内容をできるかぎり省略して書くこともできます。先ほどまでは、pinMode()、digitalWrite()、delay()が繰り返し書かれてきましたが、以下のようにすれば、それぞれを一回書くだけで済みます。
boolean table[4][7]={ {0,0,0,0,0,0,1},
{1,1,0,0,1,1,1},
{0,0,1,0,0,1,0},
{1,0,0,0,0,1,0} };

void setup(){
for(int i=1; i<=7;i++){
pinMode(i,OUTPUT);
}
}

void loop(){
for(int i=0;i<=3;i++){
for(int j=0;j<=6;j++){
digitalWrite(j+1,table[i][j]);
}
delay(1000);
}
}

配列によって複数の値をグループとして用いることが可能となります。配列は[]の括弧をもちいてあらわします。もし、数字の「0」についてのグループをつくるなら、

boolean zero[]={0,0,0,0,0,0,1};

となります。zeroというグループには7つの値が含まれており、それぞれが順番にLEDセグメントのLOWかHIGH(0か1)の設定に対応しています。0か1しかないので、boolean型を用いています。zero[6]という表記は、配列zeroの6番目の値ということになります。配列では、{}内に含まれる最初の値を0番目として扱います。よって、zero[0]からzero[5]までは0で、配列zeroに含まれる最後の値であるzero[6]は1となります。
zero[]と同様にして数字「1」、「2」、「3」に関しては
boolean one[]={1,1,0,0,1,1,1};
boolean two[]={0,0,1,0,0,1,0};
boolean three[]={1,0,0,0,0,1,0}

となります。
さらに、これらの各数字グループをまとめる全体グループをもう一つの配列を用いてつくります(その配列名をtableとします)。
一行で書くと横に長くなるので、改行して少し見やすくします。

boolean table[4][7]={ {0,0,0,0,0,0,1},
{1,1,0,0,1,1,1},
{0,0,1,0,0,1,0},
{1,0,0,0,0,1,0} };

配列tableは、4つ要素を持ち、それぞれの要素がさらに7つの値を含んでいるということになります。例えば、table[2][2]は、数字「2」の2番目の値で1(HIGH)であり、table[2][5]も1(HIGH)となります。
この全体の配列tableは、loop()内で処理される全体の流れと、それぞれの内訳を0と1だけを用いて書き直した表のようなものです。loop()内では、この表に従って順にカウントアップされる処理が行われます。
void loop(){
for(int i=0;i<=3;i++){
for(int j=0;j<=6;j++){
digitalWrite(j+1,table[i][j]);
}
delay(1000);
}
}

このloop()内のfor文では、iをint型の変数にして0〜3までの数字を順番に処理していき、その処理のさらに内部では、jをint型の変数にして各配列の0〜6番目までの値を順番に処理させ、digitalWrite()のピン番号とLOWかHIGHかを指定する部分に0か1が入るようになっています(digitalWrite()の中のj+1という部分は、配列の0番目が今回使用したArduino基盤のピン番号の1番目に対応するため+1してあります。本来、基盤の0番ピンから使えば、配列の0番目がピン番号の0番目に対応するので、このようなことをしなくても済んだはずです)。
配列(Array)for文に関しては、プログラミングではよく出てきますので、詳細については再び授業あるいはブログで説明していきたいと思います。

関連:
Arduino:7 セグ+照度センサNJL7502L」--照度センサで得た値を7セグを用いて表示する


Arduino LEDの点滅



LEDを点滅させるプログラム(上画像)です。
まずsetup()loop()を用いて、以下のようなフォーマットを用意します。ほぼProcessingと同じような感じです。
void setup(){
//初期設定のプログラム
}

void loop(){
//ループ処理のプログラム
}

初期設定setup()の中には、pinMode()を使って、基盤上のどのピンを出力または入力で使いたいのかを設定します。
pinMode()の()内には、数字でピンの番号(0〜13)とOUTPUTかINPUTのどちらかをコンマで区切って入れます。LEDを光らせる電源としてOUTPUT(出力)に設定します。
ループ処理loop()の中には、LEDを点滅させる一連の処理についてのプログラムを書きます。
digitalWrite()の()内には、数字でピンの番号(0〜13)とHIGHかLOWのどちらかをコンマで区切って入れます。HIGHは5Vを出力し、LOWは0Vを出力します。delay()は、()内に入れた数字の分だけ、時間を保持します。単位はミリ秒で、1秒は1000ミリ秒に相当します。上記のプログラムでは、delay(1000)を二回挿入しているので、2秒ループのプログラムになります。
プログラムを新たに書き直したら、一旦「Verify」ボタンを押してプログラムをコンパイルします。Arduino基盤とパソコンがUSB接続されていれば、「Upload to I/O Board」ボタンをクリックし、基盤にプログラムをアップロードします。


LEDは電流を一方向にしか流さない特性があるため、+と-を電源に対して正しくつながないと発光しません(逆につないでも壊れるということは、ほぼありません)。上図のように、通常二つの端子(アノードとカソード)があります。アノード側をプラスに、カソード側をマイナスに接続します。LEDの多くは2.5V前後の電源で発光するようにつくられているので、Arduino基盤上の5V電源とGND(0V)に直接つなぐと壊れる恐れがあります。そのため、抵抗をかませる必要があります。Arduino基盤の13番ピンには抵抗なしで直接つなぐことができますが、0〜12番ピンに接続する際には、抵抗(220〜470Ω程度)を直列つなぎしてください。LEDによって特性が異なりますので、より精確に行いたい場合は、購入する際に使用電圧や最大電流についてメモしておくか、付属のデータシートを参考にしてください。



アノード側に抵抗をつないだ場合は、抵抗を通して5Vの電圧が2.5Vまで下げられ、LEDによって2.5Vから0Vに下げられます。
カソード側に抵抗をつないだ場合は、LEDを通過することで5Vの電圧が2.5Vまで下げられ、抵抗によって2.5Vから0Vに下げられます。
最初にプラス側で電圧をさげておくか、最後にマイナス側で電圧を下げるかという違いです。もし、抵抗を取付けなければ、LEDにおいて5Vから0Vに一気に下げられ、電位差が5Vになってしまい使用電圧である2.5Vをはるかに上回り破損してしまうという感じです。抵抗の取付けについては、回路設計やLEDの特性に応じて使い分けてください。


[目次:Processing関係]  [HOMEへ戻る]  [目次:Arduino関係]