INDEX(各項目ごとの目次)

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

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

7/24/2008

Processing サウンド2/逆再生

前回は音源を用意し再生/停止(ポーズ)のプログラムをしました。今回もまた「Sonia」ライブラリを使用しますが、用意した音源を逆再生するプログラムをしてみます。前回少し触れたように、音源データはフレームレートの数値で再生箇所や経過時間などを制御できます。時間の経過に合わせて、連続する音のデータを次々と読み込んで再生していく仕組みになっています。逆再生の場合は、配列を用意し音源データの最終フレームから先頭フレームまでを読み込んで、逆方向に配置されたデータをつくり、それを再生させます。

クリックで順再生と逆再生が切り替えられるプログラムを以下に書きます。音源は予め用意して、sketchフォルダ内のdataフォルダ(なければ新規作成)にいれておいて下さい。
尚、プログラムをランさせた時に「Exception in thread "Thread-2" java.lang.OutOfMemoryError:...」のような赤文字のエラーが出たら、メモリーが足りないということなので、MacosXの場合はメニューバーのProcessing>環境設定を選択し(ウィンドウが現れる)、「Set maximum available memory to ... MB」の欄に256MBなど充分なメモリー(曲や音源のサイズ)を記入し、チェック欄にチェックを入れて下さい。Windowsの場合は、File>Preferencesを選択し、同様に充分なメモリー数を記入しチェックを入れて下さい。


//ライブラリの取り込み
import pitaru.sonia_v2_9.*;

//順再生/逆再生の二つのオブジェクトを用意
Sample forwardTune;
Sample reverseTune;

//順再生/逆再生の切替フラグを用意
//trueで順再生、falseで逆再生
boolean direction=true;

void setup() {
size(200,200);

Sonia.start(this);

//使う音源を指定する
forwardTune = new Sample("music.wav");
//音源データの全体の長さ(フレーム数)を取得する
int dataLength=forwardTune.getNumFrames();

//順再生データ用の配列を全データの長さ分だけ用意
float[] forwardArray=new float[dataLength];
//順再生データを配列として読み込む
forwardTune.read(forwardArray);

//逆再生データ用の配列を全データの長さ分だけ用意
float[] backwardArray=new float[dataLength];
//順再生データを逆配列に置き換える
backwardArray=reverse(forwardArray);

//逆再生音源を用意
reverseTune= new Sample(dataLength);
//逆再生データを逆再生音源として書き出す
reverseTune.write(backwardArray);

//まず順再生音源を再生
forwardTune.play();
}

void draw(){
//特になし
}

void mousePressed(){
if(direction==false){
direction=true;
reverseTune.stop();//逆再生停止
forwardTune.play();//順再生
}else{
direction=false;
forwardTune.stop();//順再生停止
reverseTune.play();//逆再生
}
}

public void stop(){
Sonia.stop();
super.stop();
}


手順としては、用意した音源をSample()で指定し、全体のフレーム数(全データの長さ)をgetNumFrames()で数えます。データを変換するために順再生と逆再生の二つの配列を用意します。全体のフレーム数だけ配列のデータが必要になります。read()を用いて配列として全データを読み込みます。読み込んだ全データをreverse()で逆方向の配列に置き換えます。置き換えられた配列をwrite()で書き出し、逆再生の音源として取り込みます。今回は使いませんでしたが、getCurrentFrame()を用いて、反転する箇所のフレーム数を記憶させておけば、クリックするごとに行ったり来たりするような再生が可能になります。

関連:
Processing サウンド4/スクラッチ」--曲をスクラッチ演奏する
Processing サウンド3/テンポ」--音源再生のテンポ変換をする
Processing サウンド1/Sonia」--音源の再生/停止/ポーズする

*MacOSX(Intel)の場合、新たにJSynプラグインをインストールする必要があります。ここをクリックすると、インストール画面に移動します。移動先のページ上の「Click Here to Install JSyn Plugin」のボタンを押してインストールしてください。

 iTunes Music Store(Japan)

Processing サウンド1/Sonia

今回は、Processingを使ってサウンド制御をしてみます。「Sonia」という音のライブラリを使用します。音に関してはこの他に「Minim」や「Ess」というライブラリもあります。

Sonia」を利用するには、
・「Sonia」サイトへ行く。
・「download」をクリック。
・Sonia 2_9 for Processing V90 [ZIP]の[ZIP]部分をクリック。
・ダウンロードが開始され、デスクトップなどに展開する。
・展開したファイル(sonia_v2_9)をMacosXなら/Application/Processing0135/libraries内に入れる。
 WindowsならC:¥Program File¥processing-0135-expert¥libraries内に入れる。
JSynプラグインをインストールする(画面の指示に従って自動インストール)。

・インストール完了
*Processingを開いたままインストールした場合は、Processingを再起動すれば使うことができます。

「Sonia」では、wavまたはaiffのフォーマットを読み込むことができます。まず、これらのフォーマットの音源を用意します。今回はwavフォーマットを使用することにします。

wavフォーマットの音源がない場合は、既にiTunesに登録してある曲(音源)をwavフォーマットに変換し直して使用することができます。

iTuneで音源をwavフォーマットに変換する方法:

・iTunesを起動し、「iTunes/環境設定」を開く。
・環境設定ウィンドウ内の「詳細」をクリック。
・「詳細」設定の「読み込み」をクリック。
・「読み込み方法:」を「WAVエンコーダ」にする。
・「設定」は「自動」でも構いません。
・「OK」ボタンをクリック。
・iTunesのもとの画面に戻り、曲(音源)を選択する。
・メニューバー/詳細から「選択項目をWAVに変換」を選ぶ。

以上で選択した音源はwavフォーマットに変換され、iTunes内に保存されます。
iTunes外の音源も、一旦iTunesに取り込んで変換すれば使うことができます。

用意した音源は、プログラムを書いたファイルのsketchフォルダ内のdataフォルダ内(ない場合は新規作成)に入れておきます。(dataフォルダについては、以前の「Processing 文字と画像」を参照)。
新規sketchを開いたら、一旦保存して、そのsketch名が「sketch_08724a」なら、MacosXの場合/User/username/Documents/Processing/sketch_08724a内にdataフォルダを作成し音源を入れておく必要があります。Windowsの場合、C:¥Documents and Setting¥username¥My Documents¥Processing¥sketch_08724a内となります。

音源は各自用意するか、無料音源サイトなどからダウンロードして下さい。
無料音源サイト:音の杜 http://mmworks.info/otonomori/

まずは、単純に音源を再生するプログラムです。プログラムをランさせれば、音源が再生されます。
尚、プログラムをランさせた時に「Exception in thread "Thread-2" java.lang.OutOfMemoryError:...」のような赤文字のエラーが出たら、メモリーが足りないということなので、MacosXの場合はメニューバーのProcessing>環境設定を選択し(ウィンドウが現れる)、「Set maximum available memory to ... MB」の欄に256MBなど充分なメモリー(曲や音源のサイズ)を記入し、チェック欄にチェックを入れて下さい。Windowsの場合は、File>Preferencesを選択し、同様に充分なメモリー数を記入しチェックを入れて下さい。


//ライブラリを取り込む
import pitaru.sonia_v2_9.*;
//音源tune(名前は任意)を用意
Sample tune;

void setup() {
//とりあえず画面を200角に設定
size(200,200);
//Sonia開始
Sonia.start(this);
//括弧内に音源名を指定し設定する
tune = new Sample("music.wav");
//音源の再生
tune.play();
}

void draw(){
//特になし
}

//Soniaの使用停止
public void stop(){
Sonia.stop();
super.stop();
}


まず、ライブラリを取り込んで、音源のオブジェクト名tune(名前は任意)を設定します。setup(){...}内で、Soniaの使用開始をしたら、tune = new Sample("music.wav");で、予め用意しておいたdataフォルダ内の音源を指定します。Sample()の括弧内に音源名を「"」マークで両端を括っていれておきます。tune.play()で音源の再生をします。今回の場合はsetup(){...}内にtune.play()が書かれているので、プログラムをランさせると同時に再生が開始されます。画面表示は特に使わないので、とりあえず200ピクセル角に設定してあります。最後のpublic void stop(){...}は、ウィンドウを閉じたときにSoniaの使用を停止するプログラムです。これを書いておかなければ、ウィンドウを閉じてもSonia自体のプログラムはランし続けてしまうことがあるので、忘れずに書いておいてください。

次に、クリックしたら再生し、もう一度クリックしたら停止するプログラムを書いてみます。


//ライブラリを取り込む
import pitaru.sonia_v2_9.*;
//音源tune(名前は任意)を用意
Sample tune;

//再生/停止の切替フラグを用意
//falseの時停止、trueの時再生とする
boolean start=false;

void setup() {
//とりあえず画面を200角に設定
size(200,200);
//Sonia開始
Sonia.start(this);
//音源名を指定し設定する
tune = new Sample("music.wav");
}

void draw(){
//特になし
}

//クリックした場合
void mousePressed(){
if(start==false){//フラグが停止の時
start=true; //フラグを再生にする
tune.play(); //音源再生

}else{ //フラグが再生に時
start=false; //フラグを停止にする
tune.stop(); //音源停止
}
}

//Soniaの使用停止
public void stop(){
Sonia.stop();
super.stop();
}


再生と停止を切り替えるためにboolean型の変数(フラグ)start(名前は任意)を用意し、falseなら停止、trueなら再生という状態をクリックするごとに記憶させておきます。同時にそれに応じて停止/再生をtune.stop()とtune.play()で設定します。
ただ、このままでは再生する度に音源の冒頭部分に戻ってしまいます。効果音のような短い音源の場合なら、この設定でもいいのですが、曲にようにもう少し長い音源の場合は、ポーズ(一時停止)させたほうがいいかもしれません。
以下では、クリックして停止したときに、曲の停止箇所を記憶させておき、もう一度クリックした時に、その続きから再生されるようにしてみます。


//ライブラリを取り込む
import pitaru.sonia_v2_9.*;
//音源tune(名前は任意)を用意
Sample tune;

//再生/停止の切替フラグを用意
//falseの時停止、trueの時再生とする
boolean start=false;

//音源全体の長さの変数
int totalFrames;
//再生箇所(停止箇所)の変数
int playHead;

void setup() {
//とりあえず画面を200角に設定
size(200,200);
//Sonia開始
Sonia.start(this);
//音源名を指定し設定する
tune = new Sample("music.wav");
//音源の長さを取得する
totalFrames=tune.getNumFrames();
}

void draw(){
//特になし
}

//クリックした場合
void mousePressed(){
if(start==false){//フラグが停止の時
start=true; //フラグを再生にする

//再生箇所指定で音源再生
tune.play(playHead,totalFrames);

}else{ //フラグが再生に時
start=false; //フラグを停止にする

//停止箇所を記憶させる
playHead=tune.getCurrentFrame();
tune.stop(); //音源停止
}
}

//Soniaの使用停止
public void stop(){
Sonia.stop();
super.stop();
}


音の場合、通常サンプルレートと呼ばれる数値でデータ量が扱われます。CDなどの場合44100Hzであり、1秒間に44100個の信号が扱われます。2秒ならその倍の88200個の信号になるので、基準のサンプルレートが44100Hzであれば、その数値を数えれば、再生時間(サンプル数)が計算できます。上のプログラムでは、まずtotalFramesという変数を用意して、getNumFrames()という関数で音源全体の時間の長さ(サンプル数)を取得しておきます。10秒間の音源であれば、44100×10=441000となります。そして、playHeadという一時停止させる箇所を記憶させておく変数を用意し、getCurrentFrame()によって、現在の再生箇所を取得します。つまり、マウスをクリックして停止するときに、その停止箇所を記憶させることになります。つぎに、tune.play(playHead,totalFrames)というように括弧内に再生開始箇所と終了箇所を指定して再生させます。こうすることで、クリックで停止させるごとに、停止箇所を記憶しておき、もう一度再生させるときに、前回の停止箇所から再生させることが可能になります。

関連:
Processing サウンド4/スクラッチ」--曲をスクラッチ演奏する
Processing サウンド3/テンポ」--音源再生のテンポ変換をする
Processing サウンド2/逆再生」--逆再生の音源をつくる

*MacOSX(Intel)の場合、新たにJSynプラグインをインストールする必要があります。ここをクリックすると、インストール画面に移動します。移動先のページ上の「Click Here to Install JSyn Plugin」のボタンを押してインストールしてください。

 iTunes Music Store(Japan)

7/08/2008

7/12 講評会/前期最終授業

次回7/12は前期最終授業となります。
Wooden Stick」、「Fablic Square」の講評会を行います。
前期では、プログラミングや電子工作のスキルを身につけることを中心に行って来ました。幾つかの技術を組み合わせて、ひとつの表現物としてまとめあげられればいいのですが、まだ作品的にまとまらない実験段階のものでも構いませんので、いままでの成果を各自持参して来て下さい。

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)を用いる方法


6/29/2008

Arduino ビデオ信号/バウンドするドット



前回のテレビ画面への出力実験では、縦縞や矩形という静止画を扱いましたが、今回はすこし応用して小さなドットがバウンドする映像をArduinoによって出力します。水平同期信号だけでなく、垂直同期信号も用います。今回はできるだけ細かく信号を扱うため、より精度を出す方法を用います。前回のようにdelayMicroseconds()のかわりに、_delay_us()を用いますが、_delay_us()の括弧内には変数ではなく固定した値である定数を入れます。変数を入れると演算に時間がかかってしまうために、画面は乱れてしまいます。そのかわり、ドットの動きを反映させる変数には、delayMicroseconds()を使ってもいいのですが、今回は_delay_loop_1()というコマンドを使います。これもまたArduinoのリファレンスには載っていないのですが、_delay_loop_1()を使えば、より細かなディレイを扱えます。垂直同期信号以外に、等価パルスという信号(走査線1〜3と7〜9本目に挿入)もあるのですが、今回は使わずにプログラムしました。必要な部品や配線については、前回の記事を参照してください。

バウンドするドットのサンプル:

#include <util/delay.h>

//それぞれの定数を定義する
//同期信号、3色の出力設定
#define SYNC (PORTB=0) //B00000000 //0.0V
#define BLACK (PORTB=1) //B00000001 //0.3V
#define GRAY (PORTB=2) //B00000010 //0.6V
#define WHITE (PORTB=3) //B00000011 //1.0V

//水平ブランキング期間の各時間設定
#define FRONTPORCH 1.5 //フロントポーチ
#define HSYNCPULSE 4.7 //水平同期信号
#define BACKPORCH 4.7 //バックポーチ

//走査線1本の周期
#define FULLLINE 63.5
//走査線半分の時間
#define HALFLINE 31.75
//垂直同期信号の時間
#define VSYNCPULSE (HALFLINE-FRONTPORCH-HSYNCPULSE)//25.55us
//水平ブランキング期間を除いた描画範囲の時間
#define IMAGEWIDTH (FULLLINE-FRONTPORCH-HSYNCPULSE-BACKPORCH)//52.6us
//走査線全体の数
#define ENDLINE 262

//水平同期信号(水平ブランキング期間)
void hsync(){//10.9us
BLACK;
_delay_us(FRONTPORCH);//1.5us
SYNC;
_delay_us(HSYNCPULSE);//4.7us
BLACK;
_delay_us(BACKPORCH);//4.7us
}

//垂直同期信号
void vsync(){//31.75us
BLACK;
_delay_us(FRONTPORCH);//1.5us
SYNC;
_delay_us(VSYNCPULSE);//25.55us
BLACK;
_delay_us(HSYNCPULSE);//4.7us
}

void setup(){
//出力ピン設定
//PIN8(with 1000 Ohm):0.3V
//PIN9(with 330 Ohm):0.6V
pinMode(8,OUTPUT);
pinMode(9,OUTPUT);
//割り込み禁止設定
noInterrupts();
}

//走査線の数の変数
int line=1;
//ドットの上下加速度の変数
int vAcc=2;
//ドットの上下動きの変数
int vSpeed=0;
//ドットの左右動きの変数(プラスだけの整数)
unsigned int hSpeed=10;
//ドットの左右方向の変数(右向き1、左向き-1)
int hDirection=1;

void loop(){
//4〜6本目の走査線で垂直同期させる
if(line>=4 && line<=6){
vsync();//垂直同期を2回送信
vsync();
}
else{ //描画するための走査線(4〜6本目以外)
hsync(); //水平同期信号
//ドットの上下背景を黒で塗りつぶす
//走査線75と76がドットの範囲(vSpeedが0の時)
if(line<=75+vSpeed || line>77+vSpeed){
//背景横幅の時間(52.6us)を調整のため
//二つの値に分割して設定
//48usは_delay_us()の最大値、
//3.6usは同期するように調整した値
//調整した合計時間は51.6usになる
BLACK;
_delay_us(48);
_delay_us(3.6);
}
else{ //ドットの描画
//余白調整用の黒背景
BLACK;
_delay_loop_1(15);
//ドットの左側背景部分を黒に塗る
BLACK;
_delay_loop_1(hSpeed);
//ドット本体を白に塗る
WHITE;
_delay_loop_1(1);
//ドットの右側背景部分を黒に塗る
BLACK;
_delay_loop_1(256-hSpeed);
}
}

//走査線のカウントアップ
line++;
//最後の走査線まで来たら
if(line>ENDLINE){
line=1; //最初に戻る

//ドットの上下運動の計算
vAcc+=1; //加速度(速度の加算)
vSpeed+=vAcc/8; //8で割って少し遅くする
if(vSpeed>180){//速度が180を超えたら減速
vSpeed=180;
vAcc*=-1; //加速度の向きを変える
}
//ドットの左右方向の計算
hSpeed+=hDirection; //加速
//画面両端まで行ったら向きを変える
if(hSpeed<=1 || hSpeed>=255){
hDirection*=-1;
}
}
}


追記:
プログラムの手順としては、
・同期信号、黒、グレー、白に対する出力ピンの設定(8番、9番ピン)
・水平同期信号(水平ブランキング期間)の設定
・垂直同期信号の設定(必要であれば、等価パルスの設定)
・それぞれに用いる時間の定数を#defineで定義
・1本ずつ走査線を出力(合計262回:262ループ)
となります。

_delay_us():
括弧内に小数点を含んだマイクロ秒の数値を入れることができます。
ただし、変数を入れると演算が遅れてしまうので、#defineなどで定義した定数を入れる方がいいとリファレンスなどには書いてあります。
最大値は48マイクロ秒(Arduinoクロック数が16MHzなので)。それ以上ディレイする場合は、二つに分けて書くなど工夫が必要となります。

_delay_loop_1():
括弧内に変数として256までの整数値をいれることができます。
_delay_loop_1(256)で48マイクロ秒(最大値)。
_delay_loop_1(1)で0.1875マイクロ秒(最小値)。
_delay_loop1()は、3クロック分が処理時間になります。Arduinoは16MHzに設定されているので、1クロックが0.0625マイクロ秒になります。3クロック分なので、最小0.1875マイクロ秒単位で設定できることになります。今回のドットの横幅は_delay_loop_1(1)に設定したので、0.1875マイクロ分の長さになっています。走査線1本で描画できる範囲は、水平ブランキング期間(10.9マイクロ秒)を除いて、残りの52.6マイクロ秒になるので、数値的には52.6/0.1875で約280個の点に分割(解像度)できますが、コマンドによる処理時間も加えると、解像度はそれ以下にならざるを得ません。

それぞれの走査線の周期が異なっても、ある程度はテレビのほうで信号を調整してくれるとは思います。ただし、プログラム上で合計時間が合っていても、コマンドの数が多ければそれだけ時間がずれていくので、多少の時間調整を画面を見ながら行う必要があります。AVRマイクロコントローラは基本的に一つのコマンドで1クロックの時間がかかりますが、小数点の計算や条件文の設定などによっては、1クロック数以上かかるときがあるので、プログラムの仕方によって処理速度が変わってきます。その点を踏まえて、多少の時間調整を走査線ごとに行う必要がでてきます。そのために、配列を使って画面をピクセル状に分割し、毎回の処理速度が一定になるように、プログラムすればいいのかもしれません。
簡単な計測を行ってみると、forループを使うと毎回のループで5クロック程度、ifを使うと4クロック前後、変数を使うと3クロック前後消費していました。プログラム上でこれらのコマンドを組み合わせて使おうとすると、少なくても一つの値に対して、11クロック位は消費してしまうので、改善の余地はまだあるかもしれませんが、解像度は精々70程度かその半分程度になってしまうかもしれません。同時にRAM容量の限界もあるので、それほどきめ細かい解像度はあまり期待できなさそうですが、シンプルな表現として使うのであれば、まだ工夫できそうです。

より複雑で完成度がある映像をつくるのであれば、Processingでプログラムしモニターやプロジェクターで投影した方がいいはずです。ただ、Arduinoによる映像出力は、わざわざコンピュータと接続する必要もなく、乾電池などの外部電源を用いればスタンドアロンの装置として、簡単に既存のテレビモニターに接続可能になります。さらに発展させれば、BlueToothやXbeeなどの無線モジュールと組み合わせて、遠隔的に映像を配信することも可能になるかもしれません。同時に、今後利用されなくなるであろうブラウン管テレビモニターの再利用方法にもつながるかもしれません。




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