製作:迎山和司+原案:前田将来
WAVOはPSoCマイコンとLEDを用いて波を表現しています。
加速度センサと連動しているので、作品を動かせばLEDの光が波のように動きます。
この波の動きは数式で表現しています。作品を自由に動かし、LEDの淡い光のゆらぎをお楽しみください。
インタラクティブ発表プレミアム採択
03/01発表しました。体験ならびにコメントありがとうございました!
http://www.interaction-ipsj.org/catalogue/catalogue3.html
Make: Tokyo Meeting 04にも出してきました。ありがとうございました。
Make: Tokyo Meeting 03に(こっそり)出してきました。コメントいただいた方ならびにご鑑賞された方ありがとうございました。
またブースの一角をお貸しいただいたご関係者の皆様にもお礼申し上げます。
ついにここまで手を出してしまった。
kazushi? (2009-03-24 (火) 12:39:58)
PSoCデザイナープロジェクトファイル100208led_matrix_16x16.zip
PSoCデザイナープロジェクトファイル090326led_matrix_16x16.zip
回路図
大きなファイルはこちら。kirameki2.bmp
- 量産してみたい気もするが、プリント基板作るのがあれなのでたぶん気のせいだ。 -- kazushi? 2009-03-24 (火) 19:43:10
kazushi? (2009-03-17 (火) 21:52:25)
ちゃんと結線はできているはずなのに、一行目が常に点灯してしまうのと、最後が点灯しないのが謎なんですが、いちおう完成。
- おお。すばらしい。最初の1行と最後の1行は、ダイナミック駆動のところでタイミングを間違えていませんか?よくありがちなミスですので、ぜひ見返してみてください。 -- akita? 2009-03-20 (金) 16:20:07
- ↑タイミングは、見た感じ大丈夫そうですね。PWMのWritePulseWidth?は、いまのパルスが終わったあと(カウンタ=PulseWitdh?の条件が成立後)に、次のパルスでそのWidthが使われる、という仕様だったような記憶があります(PWMモジュールのデータシートに載っていたはず)。なので、このプログラムだと、割り込み後にPulseWidth?を更新しても、その幅が使われるのは次のパルス=次の割り込み発生時、となりませんかね? -- akita? 2009-03-20 (金) 16:28:50
- そのへんの立ち上がり立ち下がりってまだ良く理解できないのでまたしらべておきます!ただ、ブレッドボードならいまのプログラムでも最後の行は光っていたのでなんなんだろう?と思っています。 -- kazushi? 2009-03-20 (金) 21:30:49
- 何個かで、波紋が共振したりする、、、、というのを夢見てしまいます^^; -- kimura? 2009-03-22 (日) 10:11:13
- まぁ気が向いたらそのうち^^; -- kazushi? 2009-03-24 (火) 16:20:46
kazushi? (2009-03-09 (月) 12:30:05)
//---------------------------------------------------------------------------- // C main line //---------------------------------------------------------------------------- #include <m8c.h> // part specific constants and macros #include "PSoCAPI.h" // PSoC API definitions for all User Modules #include <math.h> #define W 16 //ROW in virtual #define H 16 //COL in virtual #define L 8 //array length of dots #define N 1 //number of dots void clear(); void update(); void moveDots(int, int); void ripple(); char array1[W*H]; char array2[W*H]; char *data; char *a1, *a2, *at; float dot[L*N]; //x,y,vx,vy,g void main() { //initialize for(int i=0; i<W*H; i++) { array1[i] = 0; array2[i] = 0; } a1 = array1; a2 = array2; data = a1; for(int d = 0; d < N; d++) { float v = (float)d; dot[d*L] = 8.0+v; dot[d*L+1] = 8.0; dot[d*L+2] = 0.0; dot[d*L+3] = 0.0; float vv = 0.8-(v*0.1); if(vv < 0.0) vv = 0.0; dot[d*L+4] = vv; dot[d*L+5] = 0.0; dot[d*L+6] = 0.0; dot[d*L+7] = 0.0; } // ALL LED OFF PRT0DR = 0xFF; PWM8_1_EnableInt(); PWM8_1_Start(); PWM8_2_Start(); PWM8_3_Start(); PWM8_4_Start(); PWM8_5_Start(); PWM8_6_Start(); PWM8_7_Start(); PWM8_8_Start(); PWM8_9_Start(); PWM8_10_Start(); PWM8_11_Start(); PWM8_12_Start(); PWM8_13_Start(); PWM8_14_Start(); PWM8_15_Start(); PWM8_16_Start(); PGA_1_Start(3); // Start AMP PGA_2_Start(3); // Start AMP //PGA_3_Start(3); // Start AMP SAR6_1_Start(3); // Start ADC SAR6_2_Start(3); // Start ADC //SAR6_3_Start(3); // Start ADC M8C_EnableGInt; // main while(1) { update(); //update dots ripple(); //wide a wave //exchange array at = a1; a1 = a2; a2 = at; data = a1; } } float px = 0.0; float py = 0.0; float tx = 0.0; float ty = 0.0; float u = 0.7; void update() { //get input value tx = SAR6_1_cGetSample()/4.0*-1.0; //-32 ~ +32 to 8 ~ -8 ty = SAR6_2_cGetSample()/4.0*-1.0; float cx = tx-px; float cy = ty-py; for(int d = 0; d < N; d++) { float vx = dot[d*L+2]; float vy = dot[d*L+3]; float g = dot[d*L+4]; vx += cx+(tx/4.0); vy += cy+(ty/4.0); vx *= u; vy *= u; dot[d*L] += vx; dot[d*L+1] += vy; if(dot[d*L] > 14.0) {dot[d*L] = 14.0; vx *= -g;} if(dot[d*L] < 1.0) {dot[d*L] = 1.0; vx *= -g;} if(dot[d*L+1] > 14.0) {dot[d*L+1] = 14.0; vy *= -g;} if(dot[d*L+1] < 1.0) {dot[d*L+1] = 1.0; vy *= -g;} dot[d*L+2] = vx; dot[d*L+3] = vy; char x = (char)round(dot[d*L]); char y = (char)round(dot[d*L+1]); moveDots(x, y); } px = tx; py = ty; } void moveDots(int x, int y) { //set area which can make waves if(x < 1 || x > 14 || y < 1 || y > 14) { return; } //clear(); int c = x+(y<<4); a1[c] += 16; a1[c-1] += 4; a1[c+1] += 4; a1[c-W] += 4; a1[c+W] += 4; } void clear() { for(int i=0; i<W*H; i++) { a1[i] = 0; } } void ripple() { int i = 0; for(int y=0; y<H; y++) { for(int x=0; x<W; x++) { int sum = 0.0; if(x > 0) {sum += a1[i-1];} if(x < W-1){sum += a1[i+1];} if(y > 0) {sum += a1[i-W];} if(y < H-1){sum += a1[i+W];} sum = sum << 4; // simulate float .0000 //wave equation int v = (sum >> 1) - (a2[i]<<4); //dissolve a wave v -= (v>>5); v = v >> 4; // back to int //cut out of brightness and set brightness to LED if(v > 31) v = 31; if(v < 0) v = 0; a2[i] = (char)v; i++; } } } int col = 0; int w = 0; #pragma interrupt_handler PWM8_1_INT void PWM8_1_INT() { if(w < -1){ w++; return; } w = 0; if(col >= 16){ col = 0; } // ALL OFF PRT0DR = 0b00010000; // port0_4 = High -> G1 // ON int a = col<<4; PWM8_1_WritePulseWidth(data[a]); PWM8_2_WritePulseWidth(data[a+1]); PWM8_3_WritePulseWidth(data[a+2]); PWM8_4_WritePulseWidth(data[a+3]); PWM8_5_WritePulseWidth(data[a+4]); PWM8_6_WritePulseWidth(data[a+5]); PWM8_7_WritePulseWidth(data[a+6]); PWM8_8_WritePulseWidth(data[a+7]); PWM8_9_WritePulseWidth(data[a+8]); PWM8_10_WritePulseWidth(data[a+9]); PWM8_11_WritePulseWidth(data[a+10]); PWM8_12_WritePulseWidth(data[a+11]); PWM8_13_WritePulseWidth(data[a+12]); PWM8_14_WritePulseWidth(data[a+13]); PWM8_15_WritePulseWidth(data[a+14]); PWM8_16_WritePulseWidth(data[a+15]); PRT0DR = col; col++; }
kazushi? (2009-03-02 (月) 18:14:05)
割り込みなんですが、タイマーは現在デジタルブロックが全部PWMモジュールで埋まっているので、独立して作ることができません。そこでPWM_1の割り込みを利用するわけなのですが、
VC1 = 10
VC3 Source = VC1
VC3 Divider = 75
としてVC3=32000を利用しています。
これで各PWMモジュールのPeriodを31(less than or equal)にしているので1000、つまり1kHz=1msで一行の切り替えをやっていると思っているのですがこれであってます?
ちなみに
VC1=10
VC2=2
VC3 Source VC2
VC3 Divider 240
にしてVC3 = 5000に変えてみました。PWMのPeriodは31のまま。これだと早く変化するようにはなったのですが、行方向にすごくちらつきました。
akita? (2009-03-02 (月) 13:53:12)
通りすがりのakitaです
いくつか質問をさせてください。
・タイマ割り込みが、PWM8_1の周期ごとに起こっているように見えますが、これは周期が短すぎませんか?グラデーションを表示するのにPWM8のデューティー比を変えるわけですが、1周期ごとに割り込みをかけてデューティー比を変えるのは、頻度が高すぎるように思えます。例えば1画面(16行)で毎秒30フレーム(33ms)とすれば、1行あたり2ms程度ですので、5kHz程度の周期で割り込みをかけるのが順当のように思えます。
・コンパイラの賢さにもよるのですが、i % W や i / W のような算術演算は、非常に時間がかかります。x, yを2重のfor文でまわす書き方にしたほうがよいように思います。同様の理由で c = x+y*W も、W=16なので、c = x + (y << 4) と書くほうが速くなるように思います。(<<4 は左4ビットシフトなので、×16と同じ意味)
・ADCの変換時間はどれぐらいになっているでしょう?意外と時間がかかっているのではないかと思います。
・以上を考慮して、まだイマイチであれば、製品版コンパイラでコンパイルして試してみますので、プロジェクトを一式、お送りください。
kazushi? (2009-03-02 (月) 12:49:11)
たしかにC言語「風」だった。
労をねぎらいつつ将来のために突っ込んでおくと、
int ad = (sx + x) * (sy + dy) * 16;
は
int ad = (sx + x) + (sy + y) * 16;
だろう。こちらでPSoC用に書き直したソースを貼り付けときます。
なんかおかしなところがあれば突っ込んでくださいませ。
一応動きますが256バイトの配列の読み書きは処理が重すぎるみたい。
この辺は確認中だけど、結論は16段階グラデーション表示は無理かもしれません。
//---------------------------------------------------------------------------- // C main line //---------------------------------------------------------------------------- #include <m8c.h> // part specific constants and macros #include "PSoCAPI.h" // PSoC API definitions for all User Modules #include <math.h> #define W 16 //ROW #define H 16 //COL void raiseWave(int, int); void ripple(); char array1[W*H]; char array2[W*H]; char* data; char *a1, *a2, *at; void main() { //initialize for(int i=0; i<W*H; i++) { array1[i] = 0; array2[i] = 0; } a1 = array1; a2 = array2; data = a1; // ALL LED OFF PRT0DR = 0xFF; PWM8_1_EnableInt(); PWM8_1_Start(); PWM8_2_Start(); PWM8_3_Start(); PWM8_4_Start(); PWM8_5_Start(); PWM8_6_Start(); PWM8_7_Start(); PWM8_8_Start(); PWM8_9_Start(); PWM8_10_Start(); PWM8_11_Start(); PWM8_12_Start(); PWM8_13_Start(); PWM8_14_Start(); PWM8_15_Start(); PWM8_16_Start(); PGA_1_Start(3); // Start AMP PGA_2_Start(3); // Start AMP PGA_3_Start(3); // Start AMP SAR6_1_Start(3); // Start ADC SAR6_2_Start(3); // Start ADC SAR6_3_Start(3); // Start ADC M8C_EnableGInt; // main raiseWave(8, 8); //invoke a wave from center int wait_count = 10; int w = 0; while(1) { //get input value //char v1 = (SAR6_1_cGetSample()+32)/2; //-32 ~ +32 to 0 ~ 32 //char v2 = (SAR6_2_cGetSample()+32)/2; //char v3 = (SAR6_3_cGetSample()+32)/2; //for(int i = 0; i < N; i++) //{ // data[i] = v1; //} if(w > wait_count){ w = 0; } ripple(); //wide a wave w++; } } void raiseWave(int x, int y) { //set area which can make waves if(x < 3 || x > 13 || y < 3 || y > 13) { return; } int c = x+y*W; a1[c] += 16; a1[c-1] += 8; a1[c+1] += 8; a1[c-W] += 8; a1[c+W] += 8; } void ripple() { for(int i=0; i<W*H; i++) { int x = i % W; int y = i / W; char sum = 0; if(x > 0) {sum += a1[i-1];} if(x < W-1){sum += a1[i+1];} if(y > 0) {sum += a1[i-W];} if(y < H-1){sum += a1[i+W];} //wave equation char v = (sum >> 1) - a2[i]; //dissolve a wave v -= (v >> 3); //cut out of brightness and set brightness to LED a2[i] = v&0x1f; // 0~31 } //exchange array at = a1; a1 = a2; a2 = at; data = a1; } int col = 0; #pragma interrupt_handler PWM8_1_INT void PWM8_1_INT() { if(col >= 16){ col = 0; } // ALL OFF PRT0DR = 0b00010000; // port0_4 = High -> G1 // ON int a = col*W; PWM8_1_WritePulseWidth(data[a+15]); PWM8_2_WritePulseWidth(data[a+14]); PWM8_3_WritePulseWidth(data[a+13]); PWM8_4_WritePulseWidth(data[a+12]); PWM8_5_WritePulseWidth(data[a+11]); PWM8_6_WritePulseWidth(data[a+10]); PWM8_7_WritePulseWidth(data[a+9]); PWM8_8_WritePulseWidth(data[a+8]); PWM8_9_WritePulseWidth(data[a+7]); PWM8_10_WritePulseWidth(data[a+6]); PWM8_11_WritePulseWidth(data[a+5]); PWM8_12_WritePulseWidth(data[a+4]); PWM8_13_WritePulseWidth(data[a+3]); PWM8_14_WritePulseWidth(data[a+2]); PWM8_15_WritePulseWidth(data[a+1]); PWM8_16_WritePulseWidth(data[a]); PRT0DR = col; col++; }
まさき? (2009-02-16 (月) 11:25:47)
波紋のプログラムをC言語風にしました。
/*==================================== 波紋・プログラム ====================================*/ #include <stdio.h> #include <math.h> #define N 256 //LEDの数 /*==================================== ウェイト関数 ====================================*/ void wait(unsinged long n) { while(n--){} //nが0になるまでループ } /*==================================== 波紋を起こす関数 ====================================*/ void raiseWave(int x, int y, short *array1) { int i; short array[N]; for(i=0; i<N; i++) { array[i] = array1[i]; } //波紋を起こせる範囲を指定 if(x < 3 || x > 13 || y < 3 || y > 13) { return; } int sx, sy; //半径4の大きさの波紋を起こす for(sx=-4; sx<4; sx++) { for(sy=-4; sy<4; sy++) { double r = sx * sx + sy * sy; int ad = (sx + x) * (sy + dy) * 16; if(r < 16) { array[ad] += (cos(sqrt(r) / 4 * pi) + 1) / 2; } } } } /*==================================== 波動関数 ====================================*/ void ripple(short *array1, short *array2) { int i, j; short led[N]; for(i=16; i<N-16; i++) { int x = i % 16; //左右の端の計算を除く if(x == 0 || x == 15) { continue; } //波動方程式 array2[i] = ((array1[i-1] + array1[i+1] + array1[i-16] + array1[i+16]) >> 1) - array2[i]; //波紋の消沈 array2[i] -= (array2[i] >> 5); //値をLEDの明るさに変換 double a = array2[i] + 10; //LEDの明るさの限度を指定 if(a > 15) { a = 15; } else if(a < 0) { a = 0; } //LEDに明るさを与える led[i] = a; } //配列の情報交換 short array0[N]; for(j=0; j<N; j++) { array0[j] = array1[j]; array1[j] = array2[j]; array2[j] = array0[j]; } } /*==================================== メイン関数 ====================================*/ int main(void) { int i; short array1[N]; short array2[N]; //配列の初期化 for(i=0; i<N; i++) { array1[i] = array2[i] = 0; } while(1) { wait(30000); //30000カウント待つ raiseWave(8, 8, array1); //中央に波紋を起こす ripple(array1, array2); //波紋を広げる } return 0; }