送信機からのシリアルパルスを赤外線受光素子で受け、その出力を AVR に入力してそれぞれのチャンネルに分離する。それにはシリアルパルスの先頭をきちんと判別するルーチンを作る必要がある。
PIC Learning にも出てきたが、一般に送信機のシリアルパルスは 20msec から 24msec ほどの周期で繰り返されている。各チャンネルのとりうるパルス幅は 1msec から 2msec ほど。送信機によってチャンネル数に違いがあるが、すべてのパルス幅が最大幅になったとしてもチャンネル最後のパルスから次のシリアルパルスの先頭までに少なくとも 3msec 以上のギャップがある。このギャップを検出できれば各パルスの振り分けがきちんとできる。
ATtiny15L の 1.6MHz 内蔵発振回路で 3msec の時間をカウントするには 1600x3=4800 ステップを必要とする。一方 8 ビットのカウンターでは 28 = 256 までしかカウントできない。 8 ビット 2 桁なら 65536 までカウントできる。ギャップの検出ループのステップ数にもよるが、 3msec のギャップをカウントするには 8 ビットのカウンタが 2 桁必要になる。今回は 2 桁のカウンタを使って新たにギャップを検出するプログラムを考えてみた。
2 桁のギャップカウンタを用意して 1 桁目がオーバーフローしたら 2 桁目をカウントアップするプログラムも組まなくてはならない。レジスタがオーバーフローするとステータスレジスタ(SREG)の bit1 の Z フラグがセットされるので、このフラグがセットされたら 2 桁目のカウンタをインクリメントするようにプログラムしている。今回のギャップ検出ループは 8 ステップなので、1桁目のカウンタがオーバーフローするのに 2048 ステップを必要とする。 1.6MHz クロックなので 1.28msec の時間経過となる。 1 桁目のカウンタが 3 回オーバーフローすると( 2 桁目のカウンタが 3 になると) 3.84msec となるので、これ以上をギャップと判断している。
;-- ギャップ検出 --------------- loop: in temp, PINB ;PORTBから入力 (1step) sbrc temp, 2 ;入力がゼロなら次をスキップ (1step/2steps) rjmp out_h ;入力が1ならout_hへ (2steps) cpi gapcnt2, 3 ;今回のギャップ検出ループでは3.84msecとなる (1step) clr gapcnt1 (1step) clr gapcnt2 brlo loop ;ギャップが検出されなかったらloopへ (1step/2steps) rjmp output ;ギャップを検出したらoutput処理へ out_h: inc gapcnt1 ;gapcnt1が0xFF(255)を超えるとゼロになるが (1step) ;そのときにSREGのbit1のZフラグがセットされる brbs 1, lp1 ;Branch if Bit in SREG is Set (1step/2steps) ;Zフラグがセット(1)されたら次をスキップして rjmp loop lp1: inc gapcnt2 ;2桁目のgapcnt2に1をインクリメントする rjmp loop今回の AVR への入力は負論理なので、パルスの立ち下がりで処理を開始するが、次の立ち下がりパルスまでに途中立ち上がりパルスを通過する。 ATtiny15L の PB2 を入力とし、PB3 をチャンネル 1 の出力、 PB4 をチャンネル 2 の出力、 PB1 をチャンネル 3 の出力としてみた。
AVR では命令数が多いので覚えるのは大変だが、その場その場で最適な命令を使えばステップ数も短くなる。ギャップが検出された時点でチャンネル 1 の立ち下がりパルスを検出したことになるので、すぐにチャンネル 1 に割り当てられた PB3 を ON にする。あとは立ち下がりパルスを検出した都度、現在 ON になっているチャンネルを OFF にして次のチャンネルを ON にするという操作を必要チャンネル数だけ繰り返せばいい。
では出力プログラムを見てみよう。
;-- 出力 ----------------------- output: ;-- ch1 -- sbi PORTB, 3 ;チャンネル1のPB3をON (2steps) o1: in temp, PINB ;パルスの立ち下がりを検出したあと、パルスの立ち上がりが ;あるのでその立ち上がりを検出する sbrs temp, 2 ;立ち上がりを検出したら次をスキップして次の立ち下がりパルス ;検出ルーチンへいく rjmp o1 ;立ち上がりが検出されない間ループして待つ o2: in temp, PINB ;ここでパルスの立ち下がりを検出する sbrc temp, 2 ;立ち下がりが検出されたら次をスキップ rjmp o2 ;立ち下がりが検出されない間ループして待つ cbi PORTB, 3 ;チャンネル1のパルスが終了したのでPB3をOFF (2steps) ;-- ch2 -- sbi PORTB, 4 ;チャンネル2のPB4をON o3: in temp, PINB sbrs temp, 2 rjmp o3 o4: in temp, PINB sbrc temp, 2 rjmp o4 cbi PORTB, 4 ;PB4 OFF ;-- ch3 -- sbi PORTB, 1 ;PB1 ON o5: in temp, PINB sbrs temp, 2 rjmp o5 o6: in temp, PINB sbrc temp, 2 rjmp o6 cbi PORTB, 1 ;PB1 OFF rjmp loop ;ここでloopに戻り、これ以降のパルスはギャップ検出ルーチンで消化今回は 3 チャンネルのデコーダをプログラミングしたが、使用する送信機が 3 チャンネル以上でも、それ以降のパルスは、ギャップ検出ルーチンに戻してその中で消化する。 ATtiny15L ではあと PB0 が余っているので、必要なら簡単に 4 チャンネル目を追加することができる。
画像は実際に ATtiny15L にプログラムを書き込んで、リチウム電池を電源にして 5 チャンネル赤外線送信機からの信号を赤外線受光素子で受信し、その出力波形を PB2 に入力(画像上)して ch3 の PB1 出力波形(画像下)を確認したもの。シリアルパルスの先頭から 3 番目の立ち下がりパルスで出力を開始し、 4 番目の立ち下がりパルスで出力を停止している。オシロの横一目盛りが 1msec なので出力パルス幅は約 1.6msec ほどになっている。今回は Futaba の送信機を使っているのでこの出力パルスはスロットルコントロールに割り当てられている。
PIC12F629 入門 NO.4 で同じデコーダプログラムを PIC12F629 で組んでいる。 ATtiny15L では 1.6MHz での動作だが、 PIC12F629 では 4MHz 内蔵発信回路で動作している。同じ働きをするプログラムの両者を比較してみるのもおもしろい。
2003/09/16