IR Remote (5)

October 12th, 2006

赤外線信号を再生するには、パルス幅をまず測って、タイマーでそれを真似すればよいみたい。

IR Remote (3)のようなプログラムで信号を読み取り、各信号のパルス幅を記録。それを再生するようにタイマーを適当な間隔で動かす。

たとえば、ボクのテレビのリモコンは52回(26回HIGHで26回LOW)波をおくっていることがわかったので、それぞれのパルス幅をタイマーで測り、それを再生することにし、52のパルス幅をあらかじめリストに登録する。

 unsigned long ir_1[52] =  {

0×28, 0×26,
0×05, 0×13,  // 1 start
0×05, 0×13,  // 1 start
0×05, 0×09,  // 0 start
0×05, 0×13,  // 1 start
0×05, 0×09,  // 0 start
0×05, 0×09,  // 0 start
0×05, 0×13,  // 1 start
0×05, 0×13,  // 1 start
0×05, 0×09,  // 0 command 1
0×05, 0×09,  // 0 command 2
0×05, 0×09,  // 0 command 3
0×05, 0×13,  // 1 command 4
0×05, 0×09,  // 0 header
0×05, 0×09,  // 0 header
0×05, 0×13,  // 1 header
0×05, 0×09,  // 0 header
0×05, 0×13,  // 1 header
0×05, 0×13,  // 1 header
0×05, 0×09,  // 0 header
0×05, 0×09,  // 0 header
0×05, 0×13,  // 1 command 1
0×05, 0×13,  // 1 command 2
0×05, 0×13,  // 1 command 3
0×05, 0×09}; // 0 command 4

上は”1”を押したときに発信されるべき信号。数字はタイマーカウント。1列に2つずつ数字があるけれど、最初のほうがHIGHである時間、2つ目のほうがLOWである時間。このHIGHとLOWの組み合わせの違いでそのビットが0であるか1であるかが決まるとは前に書いた通り。(つまりボクのリモコンは26ビット分のデータを送っているといえる。)

headerとあるのはこのリモコンに特有のデバイスIDのようなものだと理解。このリモコンはcommand部分の4ビットでキーの情報を送っているみたい。commandが2回出てくるけれど、2回目は1回目の信号を反転させたものであることに注目。

プログラムでは、信号を送りたいときにタイマーのPeriodを設定し、タイマーをスタートし、タイマーの割り込みを待つ。

if(~PRT2DR & 0x02){
	Timer16_1_WritePeriod(ir_1[index++]-1);
	Timer16_1_Start();
}

Periodは当然上に書いたリストの最初のもの。あとは、タイマーの割り込みがかかるたびに、リストの次のPeriodを設定し、また割り込みを待つ。

Timer16_1_WritePeriod(ir_1[index++]-1);

PRT0DR ^= 0×02; // これがダーリントントランジスターアレイをON,OFFし、信号のHIGH,LOWを切り替える。

これを52回繰り返すことで、 HIGHとLOWを決まったタイミングで送ることができるというわけ。

ボクは見た目のよいリモコンの中身を自作のPCBをと入れ替えることで世界にひとつしかないボクだけのスペシャルリモコンをつくることに成功!

ir_emitter_pcb-1ir_emitter_pcb-2(PCBは前回の回路図をもとにつくる)

しかし、いざ使ってみると、見た目は良いのだけれど信号がやや弱いような感じで、テレビにかなり近寄らないとうまく信号を送れない。電池が切れているのだろうかと思い新し物と交換してみたけれど、あまり変わらない様子。むむむ。

しばらく頭をひねっていたのだけど、ふとひらめいてPWMの周波数を38KHZから40KHZに変えてみると、格段に動きが良くなる。どうやらボクのテレビは40KHZが好きなよう。受信は少々ずれていても良いようだけれど、送信はやはりそのリモコンにあったものを選んだほうが良いみたい。

このプロジェクトのファイル:  ir_emitter_101206.zip

IR Remote (4)

October 9th, 2006

前回までで赤外線信号を読み取ることができた。しばらく遊んでみたけれど読み取りを間違えることはほとんどないみたい。ただ蛍光灯の下だとノイズが入ることもあるようようなので、受光部は箱に入れて受光部分の目の前にだけ穴を開けるようにする。さらにその穴にもサングラスのような半透明のフィルムを貼りつけてノイズが入らないように工夫。

ここ1週間はpsoc.infoの更新もせずに読み取った信号をシリアル通信で自作のJAVAアプリケーションに送って楽しむ。簡単なゲームをしたり、QuickTimeビデオをコントロールしたり。 今考えているのはクリント・イーストウッド気分で楽しめる早撃ちゲーム。ランダムで表示される番号どおりにリモコンのキーを押すとか。ネットワークでつなげればオンライン対戦も可能なんて夢も膨らむ。

さて、読み取りができたので、次はその読み取った信号を再生してみようと思う。ちょっと動かしてみたいモノ(普段はテレビのリモコンで動かしてる)があるので、それを動かすために自家製リモコンを作ってみることにして、回路図を考える。

ir_emitter_schematics (回路図)

とりあえず使い慣れてる9Vバッテリーを電源に使うことを考えて、5Vのレギュレーターをつける。ボタン用に5つの小さなタクトスイッチをつけて、それぞれに違う信号を割り当てることにする。信号はPWMで38KHZの波をつくり、それをダーリントントランジスターアレイでON、OFF。あとは読み取った信号の通りにパルスを再生すれば動くはず。

<続く>

IR Remote (3)

October 3rd, 2006

前回のプログラムで信号を読むと、下のような感じ。

ir_signal(ボクのリモコンの「1」。パルス幅がそれぞれこのように表される。)

最初の方のパルス幅が長いのは、それがスタートを表すシグナルだからでなのだと推測。

何度か「1」を押してみるが大体同じ結果。05が06になったりくらいの変化はあるけれど、値は大きくは変わらない。 プリントされる数字の数も一定なので、おそらくはきちんと読み取れているのだろう。

というわけで、自信を持って今度はこのパルス幅の羅列を0と1の羅列に変えてみることにする。パルス幅をみてみると、最初の3つを除けば、04~06か0Fであることがわかる。前回までの下調べで、幅の大きいときが1、そうでないときは0ということがわかっているので、ここでは09以上を1として、その結果をシリアル通信で出力するコードを書く。

---------------------------------------------------------------------------------
counter = prev_counter - new_counter; // calculate the difference 		
if(counter>0x09){ 			
	bit = 1; 		
}else{ 			
	bit = 0; 		
} 		
TX8_1_PutChar(bit);
---------------------------------------------------------------------------------

このようにして信号を読み取り、出力した結果がこれ。

ir_signal_1_2_3 (上から、1,2,3を読み取ったところ。)

リモコンの1,2,3を読んでみて、その結果を分析。まずわかるのは、信号は常に34ビットで送られているということ。(注:もちろん、この辺の信号の長さはリモコンによって違う。ボクが使っているものがたまたまこういうフォーマットだというだけ。)最初の2ビットはスタート信号のようなもののようなので、それを除けばデータビットは32ビット。

この32ビットの前半の16ビットは1,2,3どれを押しても何の変わりもないので、このリモコンに特有のデバイスIDのようなものだと理解する。

そしてこのデバイスIDらしき16ビットを良く見てみると、最初の8ビットを反転させると後ろの8ビットになっていることがわかる。この辺も下調べの通り。

後半の16ビットをみてみても、これも最初の8ビットと後ろの8ビットは反転の関係にあることがわかる。この16ビットはリモコンのボタンによって値が変わっているので、これが実際のキーを決定するデータビットなのだと理解。

で、最初の8ビットに注目してみると、

0を押したときは、00000001

1を押したときは、10000001

2を押したときは、01000001

といった具合で、一番右の1を無視して、左からみていくと、2進法で数字が表されているのがわかる。

だから、

3を押したときは、11000001

4を押したときは、00100001

となるし、この8ビットに続く次の8ビットはこれを反転させたものになる。

わかってみると非常に簡単。何度やってもいつも同じ結果なので、間違いないだろう。

ここにプロジェクトファイルを載せておく。

このプロジェクトのファイル: ir_timer_capture_100306.zip

もちろんフォーマットはリモコンによって違うはずだから、 ボクが使った「09以上であれば」、なんて条件は適当に変える必要はあるかもしれない。まずはパルス幅を読み取って、そのパターンを分析するのがいいと思う。

<もう少し続く>

IR Remote (2)

October 1st, 2006

前回のリサーチで、以下のようにすればIR信号を読み取ることができることがわかった。

——————————————————————————————
(1) タイマーをキャプチャーモードで使う。
(2) 受光モジュールにつながったピンの立ち上がりで割り込み、タイマーのカウントを記録し、割り込みを立下りエッジに設定する。(立ち上がり、立下りの変更はDBBXXFNレジスターで行う。)
(3) 立下りを検出したら、タイマーの値を読んで、先に記録しておいたタイマーの値との差、つまりパルス幅を求める。このパルス幅が閾値よりも大きければこのデータビットは1、そうでなければ0となる。割り込みをまた立ち上がりエッジに設定しなおす。
(4) すべてのビットを受け取るまで2と3を繰り返す。

——————————————————————————————

以下は具体的にそれをPSoCで実現する方法。
(1)をするには、タイマーのCaptureにインプットピンを選んでやればよい。

timer_capture (Row_0_input_1とタイマーを結ぶの図。)

受光モジュールはAN2092で勧めてあった、Vishayのもの(ここでは38KHZのもの。mouser#782-TSOP1138)を使う。このアウトプットピンは信号を検知していないときはHIGH、信号を確認するとLOWになるので、をするためにまずはこの受光モジュールにつながったピンを立下りで割り込みが入るように設定する。

DBB00FN |= 0×80; // set the interrupt edge to falling edge

このDBBXXFNのレジスターの7番目のビットが割り込みの仕方を決定するビットのよう。立ち上がりで割り込むようにするには、

DBB00FN &= ~0×80; // change the interrupt edge to rising edge

となる。

ir timer interrupt (立ち上がり、立下りで割り込み。パルス幅を調べる。)

-----------------------------------------------------------------------------
int flag = 0;
char counter = 0;
long prev_counter = 0;
long new_counter = 0;
#pragma interrupt_handler TMR_INT
void TMR_INT(void){
	if(flag==1){
		DBB00FN |= 0x80; // change the interrupt edge to falling edge
		new_counter = Timer16_1_wReadCompareValue();
		counter = prev_counter - new_counter; // calculate the difference
		TX8_1_PutChar(counter);
		prev_counter = new_counter;
		flag = 0;
	}else{
		DBB00FN &= ~0x80; // change the interrupt edge to rising edge
		new_counter = Timer16_1_wReadCompareValue();
		counter = prev_counter - new_counter; // calculate the difference
		TX8_1_PutChar(counter);
		prev_counter = new_counter;
		flag = 1;
	}
}
-----------------------------------------------------------------------------

上のプログラムはHIGHとLOWが変わるたびにパルス幅の変化を調べ、それをTXでPCに送り出す。

立下りでタイマーのカウンターを記録、次に立ち上がりを検出したらまたタイマーの値を読んで、先に記録した値との差を求め、割り込みを立下りに再設定。割り込みでタイマーをカウント、差を調べ、値によってデータビットを1か0に決定していく。。。。。これを繰り返すことでリモコンの信号を読める。

<続く>

IR Remote (1)

September 29th, 2006

友達にリモコンの信号を読み取ってシリアル出力するようなものを作ってほしいといわれ、リサーチをはじめる。マイコンはもちろんPSoCだけど、まずはこのページで赤外線リモコンの一般を勉強。画像もあって理解がしやすい。

まず理解しなくてはならないのは、リモコンの信号は30から60KHZほど(よくあるのは36khzあたり)のキャリアウェーブにのって運ばれるということ。こうすることで、他の光源からのノイズと受信すべき信号を区別することができるみたい。

HIGHとLOWをこのキャリアウエーブに乗せて送るわけだけれど、HIGHを送るときにはある一定の周波数(30~60khz)で信号を送り、LOWの時には何も送らない。そして、このHIGHとLOWの信号の幅と組み合わせによって、データビットの1と0が決まる。

たとえば、01010101という8ビットのデータを送るとき、一定の長さでLOW,HIGH,LOW、HIGH…といった具合にLOWとHIGHを交互に送るのではなくて、HIGHとLOWの信号の幅と組み合わせによってデータビットを決定していく。つまり、HIGHをある一定の長さ(リモコンによってこの辺の使用は異なる)で送った後、同じ長さのLOWを送ると(つまりなにも送らないということなのだけど)そのデータビットは0、HIGHの3倍の長さのLOWを送ると1となる、といった具合。

フォーマットによってそれぞれプロトコルは違うみたいだけれど、どれにも共通して見えるのは、最初にヘッダーのような他よりも長い信号を送り、それに続いて任意のビット数のデータを送るという点。

データの中には他の機器と区別するためのデバイスコードのようなものと、実際に押されたボタンのデータが含まれていて、データと、それに続いてそのデータを反転したものを送っている。受け手のほうはそのデータを受け取って、もとのデータと反転したデータを照合することでエラーチェックを行っている模様。

では実際にPSoCでどうやって、リモコンの信号を受信して解読することができるだろうかとリサーチしてみると、CypressのページにAN2092というアプリケーションノートを発見。詳細なリモコンについての解説。残念ながらプロジェクトは公開していないようだけど、回路図もあるし、赤外線受光部品もいくつか勧めてくれている親切なファイル。

PSoCDeveloperのページは具体的にPSoCでどうプログラムを書けばいいのか教えてくれている。簡単に手順をまとめるとこんな感じ。

1.タイマーをキャプチャーモードで使う。
2.受光モジュールにつながったピンの立ち上がりで割り込み、タイマーのカウントを記録し、割り込みを立下りエッジに設定する。(立ち上がり、立下りの変更はDBBXXFNレジスターで行う。)
3. 立下りを検出したら、タイマーの値を読んで、先に記録しておいたタイマーの値との差、つまりパルス幅を求める。このパルス幅が閾値よりも大きければこのデータビットは1、そうでなければ0となる。割り込みをまた立ち上がりエッジに設定しなおす。
4. すべてのビットを受け取るまで2と3を繰り返す。

さらにいろいろ調べてみると、ボクの持っているリモコンは数あるフォーマットの中でもNECフォーマットである可能性が高いこともわかる。明日は実際にプログラムをつくってみることにする。

remote(リモコン)

DMX Receiver Schematic

September 28th, 2006

数日前のDMX Receiverの回路図。忘れないようにここに載せておく。

dmx receiver schematic (回路図。MAX481とあるけれど、ボクが実際につかったのはMAX485。)

PRS and Linear Feedback Shift Register

September 25th, 2006

この間野良犬を動かすのに、わけもわからず乱数を作ってみたけれど、PSoCDeveloperフォーラムで、PRSについて詳しく説明してくれている人を発見。あまりに詳細すぎて、ボクにはまだよくわからないのだけど、(しかし彼に言わせると非常に簡単なことらしい。)これをまじめに何度も読めばわかる日がくるような気がしないでもない。

忘れないようにメモとしてここに書いておく。

PRSについてのフォーラムでのトピック

PRSを理解する鍵のように見えるLinear Feedback Shift RegisterについてのWikipediaでの記述

DMX Receiver

September 24th, 2006

開発 9: DMX受信

あたらしいマイコンを勉強してなにが一番うれしいかというと、それまで別のマイコンではできなかったことができるようになるということだとは、マスターも似たようなことを言っていたけれど、ボクみたいな素人にとっては、サンプルコードやレファレンスが増えるということが大きな魅力。

PICでやろうと思っていながら力及ばずできなかったことのひとつがDMX受信。幸いなことにPSoCではアプリケーションノートAN2302このページがDMXについて解説してくれていて、ボクはなんとかコードを理解(たぶん)、換骨奪胎して3チャンネルのコントロールに成功できた。以下簡単にメモ。

以下がRXでDMXの信号を受信するメソッド。


------------------------------------------------------------------------------------------------
void waitBreakEnd(void){
	while(!(PRT0DR & 0x40)){}
}
void dmx_receiver_ISR (void){
	static BYTE bRxStatus = 0;
	static WORD bRxData = 0;
	bRxStatus = dmx_receiver_bReadRxStatus();	//read receiver status
	if(bRxStatus & dmx_receiver_RX_FRAMING_ERROR){	//check for start of dmx packet
		dmxcount = 0;
		dmx_receiver_Stop();
		waitBreakEnd();
		bRxData = dmx_receiver_bReadRxData();		//read receiver data
		dmx_receiver_Start(dmx_receiver_PARITY_NONE);
		return;
	}
	bRxData = dmx_receiver_bReadRxData();		//read receiver data
	channels(bRxData);

}
------------------------------------------------------------------------------------------------

読み取った値は次のメソッドで処理されてここではLEDの明るさを調整することができる。


----------------------------------------------------------------------------
void channels (WORD Data){
	if(dmxcount == 0){
		if(Data == 0x00){
			ch0 = Data;
			dmxcount++;
			return;
		}
	}
	if(dmxcount == 1){
		ch1 = Data;
	}
	if(dmxcount == 2){
		ch2 = Data;
	}
	if(dmxcount == 3){
		ch3 = Data;
	}
	dmxcount++;
}
----------------------------------------------------------------------------

このプロジェクトのファイル: dmx_receive_092406.zip

ランダム、乱数。人生行き当たりばったり

September 21st, 2006

開発 8: 野良犬。オレに首輪はいらないぜ。

dog (野良犬)

材料:ステッパーモーター x 1、 ULN2803 (Darlington transistor array) x 1

昨日の犬が走るプロジェクトを少し変更して、今回は乱数を使うことで犬にランダムな動きをさせてみる。

乱数はPRSモジュールで作るらしのだけど、情報が少なく苦戦。結局何をどう設定すればよいのか良くわからないけれど、とりあえずこのページを参考にさせていただいてがんばり、このようなコードを書けばよいことを確認する。

PRS8_1_WritePolynomial(0xb8);
PRS8_1_WriteSeed(0xff);
PRS8_1_Start();

ボクが理解する限り、0xB8や0xFFは任意の数字。ただしSeedは通常0xFFという記述もDesigners Guide to the Cypress PSoC中に発見。 さらに詳しく本を読んでみると、PRSは2重に使うとよりランダム度が上がるということもわかる。まあ今回はそこまでこだわることもないしと思って、ひとつだけにしておく。

モジュールによって作られた乱数を読むのは、

PRS8_1_bReadPRS();

これを昨日の犬のプログラムのmove(int i)関数に渡してやることで、犬の不規則な動きを再現。

move(randomNum);

このあてどなく、それでもしっかりと歩く犬の姿は見てて気持ちがいいです。

このプロジェクトのファイル:  stepper_motor_dog_092106.zip

ステッパーモーターと犬

September 20th, 2006

開発 7: 犬は自分の尾を追う

材料:ステッパーモーター x 1、 ULN2803 (Darlington transistor array) x 1、 ホールエフェクトセンサー x 1

インタラクション: 自分の尾を追って回る犬。マグネットをセンサーに近づけると、犬は一瞬立ち止まったあと、またしばらくうろちょろする。

そろそろPSoCにも慣れてきたので、なにか自分の作りたいものを作ってみようと思い、犬が走るおもちゃを作ってみた。

dog (ステップモーターで動く犬。コントロールはPSoCとトランジスターアレイで。)

ステッパーモーターのコントロールについてはこのページを参考にしながらがんばる。要は4本のワイヤーを順番に従ってコントロールすればよいわけで、組み合わせさえわかってしまえばあとは簡単。ボクの持っているモーターはこの4ステップを繰り返すことで動くことを突き止める。

int steps[] = {0×0A, 0×06, 0×05, 0×09};

ポート0の0~3ピンをモーターのコントロールピンにつないで、上の組み合わせを順番に実行してみる。

schematics (回路図)

ここでは動かしたいステップ数を渡してやって、その分だけ動かすようにする。

——————————————————————————–


void forward(int step){
	int i;
	for( i=0; i < step; i++){
		wait(WAIT_TIME);
		PRT0DR = steps[current_step_pos++];
		if(current_step_pos==4)current_step_pos=0;
	}
}

——————————————————————————–

反対に動かしたいときは4つの組み合わせを反対に実行していけばいいわけだから、こういう風になる。

——————————————————————————–


void backward(int step){
	int i;
	for(i=0;i<step;i++){
		wait(WAIT_TIME);
		PRT0DR = steps[current_step_pos--];
		if(current_step_pos<0) current_step_pos=3;
	}
}

——————————————————————————–

動かすだけならとても簡単。実際に動いている様子はここで。

今回はあまり意味がないけれど、将来的に犬のホームポジションを決めたり、決まった場所まで犬を動かすことができるといいなと思って、ホールエフェクトセンサーも取り付けておく。これはマグネットを近くに感知すると、アウトプットピンがLOWになるというもの。これを決まった位置に取り付けておいて、犬にマグネットをつければ、センサーの上に来た犬をPSoCが感知できるという仕組み。

今回はマグネットをセンサーに近づけると、犬が一瞬立ち止まり、またしばらくうろちょろするようにプログラムする。(ビデオはここ。)

机の上で走る犬をみるのはなかなかほのぼのとした気分になってよいと思う。

今回のプロジェクトのファイル: stepper_motor_092006.zip