秋月電子で、赤外線リモコンが\300であった。面白そうなので、PICマイコンで受信するプログラムを書いたが、意外と苦戦したので、備忘録として書いておこう。
卓球の練習マシンを作っていたが、上ローラー、下ローラー、発射角度、発射周期、首振りなど調整項目が多く、アナログボリュームだと配線が大変だし、なによりマイコンのアナログ入力のピン数が増える。赤外線リモコンが使えれば、入力はデジタル×1で済むし、パラメーターを記録しておける。
で、使用したPICは16F1503 14pinDIPパッケージ。PWMが4つあり、モーター制御に向いている。ただ、DCモーターとラジコンサーボでは、PWM周期が違いすぎて両立はできない。よって、サーボはタイマ割り込みを使うこととする。
上記のことから、赤外線の受信には16F1503を内蔵オシレーターでの最高周波数16MHzで使用、タイマ1のゲート機能を使うこととする。赤外線受信モジュールは、常時Highで受信するとLowと逆転するが、かえって都合がいい。Highの部分だけを見れば受信可能だ。
リーダーが4500us、0が560us、1が1600us、リピートが2300usということで、16ビットタイマの上位8ビットのみを使いカウントする。
//リモコン受信データ unsigned char IRBUFF[4]; unsigned char read_state=0; unsigned char ir_repeat; void interrupt isr(void){ unsigned static char byte_pos; unsigned static char bit_pos; unsigned static char skip=0; unsigned char cnt; if (TMR1IF){ //タイマ1がオーバーフローした場合。 read_state = 0; //読み取り状態をリセットする。 ir_repeat = 0; skip = 1; // タイマがオーバーフローしたら、そのときのパルスは無効。 TMR1IF = 0; } if (TMR1GIF){ cnt = TMR1H; //パルス High時間を取得 2us/count(L) 512us/count(H) TMR1L = 200; //タイマの値を初期化。0.5ms程度のパルスでカウントできるよう下駄はかせ TMR1H = 0; if (skip) skip = 0; else switch (read_state){ case 0: //ヘッダ待ちモード if(7<cnt && 11>cnt){ //3.8ms~4.9ms ならヘッダ IRBUFF[0]=IRBUFF[1]=IRBUFF[2]=IRBUFF[3]=0; read_state = 1; bit_pos = 0; byte_pos = 0; ir_repeat = 0; } break; case 1: //ヘッダ完了。データ待ちモード if(1 > cnt || 4 < cnt){ read_state = 0; }else{ IRBUFF[byte_pos] |= (cnt > 2) << bit_pos++; if (bit_pos==8){ //8bit読んだら、次のバイトへ byte_pos++; bit_pos = 0; }; if (byte_pos == 4){ //4byte読んだら終了、次のパルスは無視 skip = 1; read_state = 2; } } break; case 2: //読み込み完了。リピート待ち。 if (6 > cnt && cnt > 3){ skip=1; ir_repeat = 1; }else ir_repeat = 0; break; } TMR1GIF = 0; //割り込みフラグを解除して T1GGO_nDONE = 1;//ゲート待機モードに } }