FrontPage

WAVO(ウェーボ)

製作:迎山和司+原案:前田将来

WAVOはPSoCマイコンとLEDを用いて波を表現しています。
加速度センサと連動しているので、作品を動かせばLEDの光が波のように動きます。
この波の動きは数式で表現しています。作品を自由に動かし、LEDの淡い光のゆらぎをお楽しみください。

数学者との対話

インタラクション2010

インタラクティブ発表プレミアム採択
03/01発表しました。体験ならびにコメントありがとうございました!
http://www.interaction-ipsj.org/catalogue/catalogue3.html

IMG_0531.JPG

Make: Tokyo Meeting 04

Make: Tokyo Meeting 04にも出してきました。ありがとうございました。

44398973[2].jpg

Make: Tokyo Meeting 03

Make: Tokyo Meeting 03に(こっそり)出してきました。コメントいただいた方ならびにご鑑賞された方ありがとうございました。
またブースの一角をお貸しいただいたご関係者の皆様にもお礼申し上げます。

090524-112518.jpg
090524-112530.jpg
090524-112614.jpg


量産

ついにここまで手を出してしまった。

printed_board.jpg


installation.jpg


final_product.jpg




プロジェクトファイルと回路図

kazushi? (2009-03-24 (火) 12:39:58)

PSoCデザイナープロジェクトファイルfile100208led_matrix_16x16.zip
PSoCデザイナープロジェクトファイルfile090326led_matrix_16x16.zip

回路図
kirameki2.jpg
大きなファイルはこちら。filekirameki2.bmp


できましたぁ

kazushi? (2009-03-17 (火) 21:52:25)

ちゃんと結線はできているはずなのに、一行目が常に点灯してしまうのと、最後が点灯しないのが謎なんですが、いちおう完成。
http://www.kazushi.info/uploads/nmBlog_1237297207.JPG


修正コード

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の変換時間はどれぐらいになっているでしょう?意外と時間がかかっているのではないかと思います。
・以上を考慮して、まだイマイチであれば、製品版コンパイラでコンパイルして試してみますので、プロジェクトを一式、お送りください。


C言語その後

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++;
}

C言語

まさき? (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;
}


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS