2010年11月12日金曜日

赤外線リモコンの受信

概要:赤外線リモコンの信号受信
使用PIC:PIC16F88
赤外線リモコン受信モジュール:PL-IRM-2161-C438(秋月電子通商で購入)
使用リモコン:NECのテレビリモコン

参考サイト
PICとMikroCのTVリモコン信号の受信機 のコーナーは非常に参考になりました。
リモコンのフォーマットは 秋月電子通商のダウンロードコーナーの資料も大いに役立ちます。
手元にNECのテレビリモコンがあってラッキーでした。



タイマTMR0を使用しての時間の計測
内部クロックの周期は(1/4MHz)*4=1usとなる。
TMR0は8bitのカウンタであるため,
TMR0は,256us(1us * 256)までの時間を計測することができる。
これではリーダー部の9msを計測できないのでプリスケーラを設定する。

プリスケーラ1/64に設定
(1/4MHz)*4*64=64us=0.0664ms
TMR0は,16.384ms(64us * 256)までの時間を計測することができるようになる.
0.064msから16.384ms の間の時間を計測できる。

リーダ部の9ms を計測したい場合に必要なタイマーカウント、
9ms = 0.064ms * x
x = 9ms/0.064ms
x = 140.625 = 141回
/**
概要:TVリモコン信号(赤外線)受信器
(ボタンと押している間だけLED点灯、ボタンをはなすとLED消灯)

Device Flags: _BODEN_OFF _BOREN_OFF _CP_OFF _PWRTE_ON _WDT_OFF
       _LVP_OFF _MCLRE_OFF _INTRC_OSC_NOCLKOUT

PICとMikroCさん(http://kuri6005.sakura.ne.jp/pic/)のソースを
PIC16F88  内蔵4MHzに変更
RB0:赤外線リモコン受信モジュール PL-TRM2161-C438 二個で100円
LED: RB2, RB3, RB4, RB5 動作確認用
電源:乾電池2本(3V)
コンパイラ:MikroC 8.2.0.0
*/
unsigned short int flag = 0;
unsigned short int custom_a = 0;
unsigned short int custom_b = 0;
unsigned short int data_a = 0;
unsigned short int data_b = 0;
unsigned short int old_F2 = 0;
unsigned short int old_F3 = 0;
unsigned short int old_F4 = 0;
unsigned short int old_F5 = 0;

void interrupt() {            //割込み関数
    unsigned short int i, b;

    if(INTCON.INTF) {         //割込み種がRB0/INT割込みの場合
        INTCON.INTE = 0;      //RB0/INT割込みの禁止

        //リーダ部の確認        時間の計測にはタイマー0を割込みなしで使用
        TMR0 = 0;            //timer0リセット、タイマーは常に動作
        
        while(PORTB.F0 == 0);   //0Vの状態をカウント、5Vになるまでループ
                                //カウンタの比較
        if(TMR0 < 120) {       // < 141 (=9.0ms * 4MHz/4 /64) 誤差を見越して120
            flag = 1;           //120より少なかったらヘッダではないので受信を終了し、
            return;             //メインルーチンに戻り、受信待ち状態にします
        }                       //120以上であれば次に進みます。

                   //リーダー部の残りの部分を判定
        TMR0 = 0;            //timer0リセット
        while(PORTB.F0 == 1);   //5Vの状態をカウント
        if(TMR0 < 27) {        // < 35 (=2.250ms * 4MHz/4 /64)
            flag = 1;
            return;
        }else if(TMR0 < 60) { // < 71 (=4.5ms * 4MHz/4 /64)
            //リピートリーダを受信した時
            PORTB.F2 = old_F2;
            PORTB.F3 = old_F3;
            PORTB.F4 = old_F4;
            PORTB.F5 = old_F5;
            Delay_ms(96);
            flag = 1;
            return;
        }
        //リーダー部の確認終了

        //custom codeの取得
        custom_a = 0;
        for (i = 0; i < 8; i++) {   //繰返し0-7になるまで
            TMR0 = 0;         //timer0リセット
            while(PORTB.F0 == 0);     //0Vの期間をカウント
            while(PORTB.F0 == 1);     //5Vの期間をカウント
            if(TMR0 < 27)       // < 18 (=1.125ms * 4MHz/4 /64) 誤差を見越して27(35より小さい値)
                b = 0;
            else                // < 35 (=2.250ms * 4MHz/4 /64)
                b = 1;
            custom_a |= (b << i);     //custom_aと(b << i)の論理和をcustom_aに代入する
                     //bを左にiビットずらす
        }
        custom_b = 0;
        for (i = 0; i < 8; i++) {
            TMR0 = 0; //timer0リセット
            while(PORTB.F0 == 0);
            while(PORTB.F0 == 1);
            if(TMR0 < 27) // < 18 (=1.125ms * 4MHz/4 /64)
                b = 0;
            else          // < 35 (=2.250ms * 4MHz/4 /64)
                b = 1;
            custom_b |= (b << i);
        }
        //custom codeの取得終了

        //data codeの取得
        data_a = 0;
        for (i = 0; i < 8; i++) {
            TMR0 = 0; //timer0リセット
            while(PORTB.F0 == 0);
            while(PORTB.F0 == 1);
            if(TMR0 < 27) // < 18 (=1.125ms * 4MHz/4 /64)
                b = 0;
            else          // < 35 (=2.250ms * 4MHz/4 /64)
                b = 1;
            data_a |= (b << i);
        }

    data_b = 0;
        for (i = 0; i < 8; i++) {
            TMR0 = 0; //timer0リセット
            while(PORTB.F0 == 0);
            while(PORTB.F0 == 1);
            if(TMR0 < 27) // < 18 (=1.125ms * 4MHz/4 /64)
                b = 0;
            else          // < 35 (=2.250ms * 4MHz/4 /64)
                b = 1;
            data_b |= (b << i);
        }
        //while(PORTB.F0 == 0); //ストップビット受信

        //data_aとdata_bの各ビットを反転しその結果が同じか比較
        if (data_a == ~data_b) {  //data誤りのチェックOKの場合
            switch (data_a) {
                case 0x11:     //2チャンネルのボタンが押されている場合
                    PORTB.F2 = 1;   //RB2ポートのLEDを点灯する。
                    PORTB.F3 = 0;
                    PORTB.F4 = 0;
                    PORTB.F5 = 0;
                    break;

                case 0x13:      //4チャンネルのボタンが押されている場合
                    PORTB.F2 = 0;
                    PORTB.F3 = 1;
                    PORTB.F4 = 0;
                    PORTB.F5 = 0;
                    break;

                case 0x15:      //6チャンネルのボタンが押されている場合
                    PORTB.F2 = 0;
                    PORTB.F3 = 0;
                    PORTB.F4 = 1;
                    PORTB.F5 = 0;
                    break;

                case 0x17:      //8チャンネルのボタンが押されている場合
                    PORTB.F2 = 0;
                    PORTB.F3 = 0;
                    PORTB.F4 = 0;
                    PORTB.F5 = 1;
                    break;

                default:
                    PORTB.F2 = 0;
                    PORTB.F3 = 0;
                    PORTB.F4 = 0;
                    PORTB.F5 = 0;
                    break;
            }
            old_F2 = PORTB.F2;
            old_F3 = PORTB.F3;
            old_F4 = PORTB.F4;
            old_F5 = PORTB.F5;
        }
    }
    Delay_ms(40);
    flag = 1;
}

void main() {
    //使用変数の定義
    unsigned short int i;

    PORTB  = 0b00000000;  //PORTBの中身をきれいにする
  OSCCON = 0b01100000;  //内臓クロック4MHzに設定
    TRISB  = 0b00000001;  //RB0を1:入力、他は0:出力に設定

    //PIC稼働確認(LED点滅)
    for (i=0; i < 5; i++) {
        PORTB = 0b11111110;
        Delay_ms(50);
        PORTB = 0b00000000;;
        Delay_ms(50);
    }

    //timer0プリスケーラ64回に設定
    OPTION_REG = 0b10000101;

    INTCON.INTE = 1;    //RB0/INT割込みの許可
    INTCON.GIE = 1;    //全体割込み許可

    do {
        if(flag == 1) {
            flag = 0;
            PORTB.F2 = 0;
            PORTB.F3 = 0;
            PORTB.F4 = 0;
            PORTB.F5 = 0;
            INTCON.INTE = 1; //RB0/INT割込みの許可
        }
    }while(1);
}

参考:5Vになるまでと表現していますが、実際には、受信センサーからの出力される電圧です。

0 件のコメント:

コメントを投稿