FrontPage

WAVO(ウェーボ)

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

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

数学者との対話

  • 波動現象はもっとも興味深い研究対象の一つである。
  • 数学は理想化された世界である。宇宙の果てや時間の始まりなど目の前の現実では表現できないことを扱える。
  • 数学者は式を作ることに興味はなく、式を証明することや導くことに興味がある。
  • 数学はある現象を代入や変換などによって導く。するとまた特定の現象を証明できる。
  • ただし、それは頭の中で抽象化して理想化された世界である。
  • なので、現実世界で導かれた式を使って証明通り現象が現れている様子を見ると安心する。
  • 興味があるのは値の変化である。
  • (私見)WAVOは理想世界の一部を強調して現実世界で実現した。この強調がフィジカル・コンピューティング+ジェネレイティブアートの利点といえる。

インタラクション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


  • 独楽のように回す人が多かった。 -- kazushi? 2009-05-24 (日) 23:42:12
  • 振動モータを入れて自分で動くようにしてはどう? -- kazushi? 2009-05-24 (日) 23:44:25
  • などたくさんのコメントをいただきました。 -- 2009-05-24 (日) 23:45:53

量産

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

printed_board.jpg


installation.jpg


final_product.jpg


  • 次は表面実装ですね:-D -- akita? 2009-05-07 (木) 21:29:51
  • 量産型と言えば緑色ですね。偉い人にはわからんのですよ。 -- kazushi? 2009-05-07 (木) 21:49:57
  • ところで表面実装ってなんすか?ちっさいおっさ・・・じゃない部品を注文してつけてもらえるんですかね? -- kazushi? 2009-05-07 (木) 21:54:48
  • しらべました。あー手でやれと!?http://www.soldering-guide.com/archives/50861782.html -- kazushi? 2009-05-07 (木) 22:52:39
  • 1.27mmぐらいはすぐにできますよ。ちょっと慣れれば0.65mmぐらいまでは。 -- akita? 2009-10-01 (木) 08:19:52
  • 最近目が霞むんですよね〜 -- kazushi? 2009-10-01 (木) 10:11:06

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

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

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

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

  • 量産してみたい気もするが、プリント基板作るのがあれなのでたぶん気のせいだ。 -- kazushi? 2009-03-24 (火) 19:43:10

できましたぁ

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

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

  • おお。すばらしい。最初の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++;
}
  • 無事動いたようでなによりです。floatの演算を使っていますが、今回は速度は間に合ったようですね。 -- akita? 2009-03-15 (日) 22:10:30
  • 速度を求めるのであれば、実数演算を使わず、たとえば*0.8ならば*8/10、とします。ちなみにこの*8は、3ビット左シフトなので、<<3とすると、より速くなるかと。 -- akita? 2009-03-15 (日) 22:11:52
  • たぶん演算速度の話は、そのマイコンのもっている演算命令の種類を理解しないと、よくわからないかと思います。(つまりアセンブラレベルの話ですね) -- akita? 2009-03-15 (日) 22:12:38
  • なるほどfloatなどでもビットシフトでやれるんですね。浮動小数点型のデータ構造っていまいちわかってない私です。ありがとうございます! -- kazushi? 2009-03-15 (日) 23:46:37
  • じゃあrippleではint型をわざわざ4ビットあげて小数点以下を残すための計算をしているのだけど、sumはfloatにしてそのままビットシフトで計算してもいいんだ。 -- kazushi? 2009-03-15 (日) 23:50:35
  • 最新のソースコードにしておきます。 -- kazushi? 2010-02-21 (日) 15:52:05

クロックの設定

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のまま。これだと早く変化するようにはなったのですが、行方向にすごくちらつきました。

  • 東京でお会いした時にコメントを直でもらいましたが、結局だめでした。スペックオーバーと割り切って方針変更です。 -- kazushi? 2009-03-07 (土) 21:43:11
  • ↑の理解で正しいと思います。ちなみに、GlobalResource?で設定するCPU CLKは、いくつにちていますか?デフォルトではSYSCLK/8(=24/8=3MHz)となっていますが、電源が5VであればSYSCLK(24MHz)まで上げられます。今回のように処理内容が多い場合は有効ではないかと思います。(PSoCの設計思想として、マイコン機能はあくまでもユーザモジュールの初期化などの補助的な機能、と位置づけられているので、消費電力を減らす観点から、デフォルトではSYSCLK/8、となっています) -- akita? 2009-03-13 (金) 07:39:06
  • おお!ビンゴです。早くなりました。システムクロックとCPUクロックは違うんですね。 -- kazushi? 2009-03-13 (金) 14:39:55

質問をいくつか

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 (月) 17:10:43
  • 算術演算は変えてみました。少し早くなりました。あと、ADCは空きブロックの関係上SAR6を使っていますが、データシートによると25usとなってました。タイマ割り込みについては別途書きましたのでご助言くださいませ・・・ -- kazushi? 2009-03-02 (月) 18:19:02

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;
}
  • お、ありがとう。あとでゆっくり見てみます。 -- kazushi? 2009-02-16 (月) 23:42:28

添付ファイル: fileIMG_0531.JPG 974件 [詳細] file100208led_matrix_16x16.zip 386件 [詳細] file44398973[2].jpg 554件 [詳細] file090524-112614.jpg 694件 [詳細] file090524-112518.jpg 754件 [詳細] file090524-112530.jpg 687件 [詳細] fileprinted_board.jpg 913件 [詳細] fileinstallation.jpg 724件 [詳細] filefinal_product.jpg 649件 [詳細] filekirameki2.jpg 1802件 [詳細] filekirameki2.bmp 1979件 [詳細] file090326led_matrix_16x16.zip 1680件 [詳細]

トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-09-10 (土) 12:20:40 (464d)