PIC16F18325の基本動作から応用プログラムまでを学びます。

ホーム
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
    
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
省電力 赤外線受信機

赤外線リモコンの信号を受信しLEDを点灯させるプログラムです。
リモコンの「1」ボタンを押すと「LED1(RA2)」 が点灯します。
再び「1」を押すとLEDは消灯します。
同様に、リモコンの「2」「3」「4」が
それぞれ LED2(RC0)」「LED3(RC1)」「LED4(RC2)」に対応します。

PICのペリフェラルは以下の用途に使用しています。

Timer1
信号の継続時間測定に使用
1MHzをカウントするので、1us単位となる。
赤外線センサー (OSRB38C9AA)
RA5で常にモニタしている。無信号時はPICがSLEEP状態になり、消費電力を最小に抑えているが、センサーからの信号が変位することで、SLEEPからWeak-up する。

赤外線リモコンの波形

この受信機は、JVCTVリモコンの使用を想定したプログラムになっています。JVCは、NECが設定した赤外線リモコン仕様に準拠しており、以下のフォーマット仕様です。

キャリア 赤外線(λp = 940nm) Data部 詳細 (T = 562μs)
サブキャリア fsc = 38kHz, 1/3duty Data の '1' Mark 1T + Space 3T
Leader 部 Mark 9mSec Space 4.5mSec Data の '0' Mark 1T + Space 1T
Data 部 固定長フレーム (16bit)
 8bitのカスタマーコード
 + 8bitのコマンドデータ
Stop bit Mark 1T
 
 

JVCの汎用リモコンをビクターに設定した時のキーと出力される16ビットコードの関係を示します。

出力される16bitコード
入力切換C0C8  大 C078  1 C084  7 C0E4
電源C0E8 C0F8 2C044 8C014
タイマーC0C0 消音C038 3C0C4 9C094
BS/e2C030 C098 4C024 10C054
地デジF0A8 C018 5C0A4 11C0D4
機能赤外線の出力なし 6C064 12C034

プログラム

2本のプログラム例を示します。
一番目は、赤外線コードが終了するまで、赤外線センサーの信号をメインプロブラムのフローで常時監視し、On/OffをTimer1で計測します。
2番目のプログラムは、センサーからの信号は、I-O-C (Interrupt-on-Change)を利用し、割り込み処理の中で処理し、データが正しく取得された時に、主プロブラムに通知し、LEDを点灯します。

/*********************************************************************
 * NECタイプの赤外線リモコン受信
 * LED1 RA2,  LED2 RC0,  LED3 RC1,  LED4 RC2
 * Sencer  RA5
 * 1MHz (内部クロック)
 *
 * PIC16F18325  MPLAB X IDE with XC8
 * Copyright (c) 2017 iwamoto All Rights Reserved
 * *******************************************************************/

#define _XTAL_FREQ  1000000     // delay_ms(x) のための定義

#define L_ON_Min    5000        // 5msec リーダーの最小継続時間
#define DataTH      1000        // 1msec データの「1」「0」境界時間
#define D_Off_Max   2000        // 2msec データ「0」の最大継続時間
#define IrIN        RA5         // 赤外線センサー入力
#define LED1        LATA2
#define LED2        LATC0
#define LED3        LATC1
#define LED4        LATC2

#include <xc.h>

//******************* コンフィグレーション ****************************
#pragma config FEXTOSC = OFF,RSTOSC = HFINT1  // HFINTOSC (1MHz)
#pragma config CLKOUTEN = OFF,CSWEN = ON,FCMEN = OFF
#pragma config MCLRE = OFF,PWRTE = OFF,WDTE = OFF,LPBOREN = OFF
#pragma config BOREN = OFF,BORV = LOW,PPS1WAY = OFF,STVREN = ON
#pragma config DEBUG = OFF
#pragma config WRT = OFF,LVP = OFF,CP = OFF,CPD = OFF

unsigned int IrCmdRcv(void);

// MAIN ********************************************************
void main(void){
    unsigned int revdData;
    ANSELA = 0;                 // 全てデジタルI/O
    ANSELC = 0;                 // 全てデジタルI/O
    TRISA = 0b111011;           // RA2 LED出力 他は入力
    TRISC = 0b111000;           // RC0-RC2 LED出力 他は入力
    LATA   = 0;                 // LED 初期化
    LATC   = 0;                 // LED 初期化
    WPUA  = 0b111111;           // 入力は全て弱プルアップ
    WPUC  = 0b111111;           // 入力は全て弱プルアップ

    // Pin 状態変化 初期化 -------------------------------------
    IOCANbits.IOCAN5 = 1;       // 負極性の変位でWake-up
    PIE0bits.IOCIE   = 1;       // Wake-up 許可

    // Timer 1 設定 -------------------------------------------
    // リモコンパルス幅の計測に使用する Timer は、1カウント 1uS
    T1CON  = 0b01000000;        // Timer 1 FOSC 1/1(66mSecごと)
    T1GCON = 0b00000000;        // Timer 1 Gate設定(使用せず)

    while(1){                   // 繰り返しループ
        TMR1ON = 0;                         // Timer1 OFF
        IOCAFbits.IOCAF5 = 0;               // 入力変位フラッグ クリア
        SLEEP();                            // 無信号時はスリープ
        //--------------------

        TMR1ON = 1;                         // Timer1 ON
        revdData = IrCmdRcv();              // データー取得
        if(revdData == 0) continue;         // データー無
        // ---- 受信データーの処理 -----------------------------------
        switch (revdData){
            case 0xC084: LED1 ^= 1; break;  // Key 1 LED1 反転
            case 0xC044: LED2 ^= 1; break;  // Key 2 LED2 反転
            case 0xC0C4: LED3 ^= 1; break;  // Key 3 LED3 反転
            case 0xC024: LED4 ^= 1; break;  // Key 4 LED4 反転
			default:     LATA = 0;
                         LATC = 0;  break;  // 他のKey 全消灯
        }
    }
}

//*************** 赤外線データーを受信する *****************************
// 正常にデーターを受信したときは、そのデーターを返り値とし
// 無信号、エラー時は、0を返す。
// ビクターの信号体系(NEC系)を受信
// リーダ受信確認、データ受信(16bits)する
//*******************************************************************
unsigned int IrCmdRcv(void){
    unsigned int rcvData = 0;
    if(IrIN) return 0;                    // 無信号「1」時は復帰

    // ------- Start Bit (リーダー)の確認 -----------------------------
    //OnおよびOFFの時間を確認しノイズか有効データか判断する
    // ---------------------------------------------------------------
    TMR1 = 0;                            // -- リーダーのON 時間測定
    while(TMR1 < L_ON_Min){              // リーダーの最小継続に達しないのに
        if(IrIN)return 0;                // IR Space「1」無信号になったら
    }                                    // ノイズと判断し復帰
    while(!IrIN);                        // IR Space 待ち
    TMR1 = 0;                            // -- リーダーのOFF時間測定
    while(IrIN){                         // リーダーの終了時間を越えても待ち
        if(TMR1 > L_ON_Min)return 0;     // data開始(IrON)しなければ復帰
    }
    // ------- dataの確認 -----------------------------------
    for(char i = 0; i < 16; i++){        // 1ビットずつ16回繰り返す
        rcvData <<= 1;
        while(!IrIN);                    // space 開始を待つ
        TMR1 = 0;                        //    Bitのspace時間測定開始
        while(IrIN)                      // space 終了を待つ
            if(TMR1 > D_Off_Max)         //    2mS以上ならStopなので
              return 0;                  //    復帰
        if(TMR1 > DataTH)                // 時間取得「1」「0」判定
            rcvData |= 1;                //   「1」を立てる
    }
    return rcvData;                      // 受信データを持って復帰
}

赤外線センサーからの信号を全て割り込み(I-O-C)で処理するプログラム

/*********************************************************************
 * NECタイプの赤外線リモコン受信
 * LED1 RA2,  LED2 RC0,  LED3 RC1,  LED4 RC2
 * Sencer  RA5
 * 1MHz (内部クロック) I-O-C を使用して受信情報を割り込みで処理
 *
 * PIC16F18325  MPLAB X IDE with XC8
 * Copyright (c) 2017 iwamoto All Rights Reserved
 * *******************************************************************/

#define _XTAL_FREQ  1000000     // delay_ms(x) のための定義
// IR 閾値 ***********************************************
#define L_ON_Min    5000        // 5msec リーダーの最小継続時間
#define DataTH      1000        // 1msec データの「1」「0」境界時間
#define D_Off_Max   2000        // 2msec データ「0」の最大継続時間
// Pin設定 ***********************************************
#define IrIN        RA5         // 赤外線センサー入力
#define LED1        LATA2       // LED
#define LED2        LATC0
#define LED3        LATC1
#define LED4        LATC2
// ステート名 ***********************************************
#define leaderStart 0
#define leaderMkEnd 1
#define dataStart   2
#define dataMkEnd   3
#define dataBitEnd  4
#define waitGap     5

#include <xc.h>
#include <stdint.h>

//******************* コンフィグレーション ****************************
#pragma config FEXTOSC = OFF,RSTOSC = HFINT1  // HFINTOSC (1MHz)
#pragma config CLKOUTEN = OFF,CSWEN = ON,FCMEN = OFF
#pragma config MCLRE = OFF,PWRTE = OFF,WDTE = OFF,LPBOREN = OFF
#pragma config BOREN = OFF,BORV = LOW,PPS1WAY = OFF,STVREN = ON
#pragma config DEBUG = OFF
#pragma config WRT = OFF,LVP = OFF,CP = OFF,CPD = OFF
// プロトタイプ ***********************************************
void IrCmdRcv(void);
// 共通変数 ***********************************************
uint8_t state = 0;
uint8_t gapFlag = 0;
uint16_t revdData = 0;

// MAIN ********************************************************
void main(void){
    ANSELA = 0;                 // 全てデジタルI/O
    ANSELC = 0;                 // 全てデジタルI/O
    TRISA = 0b111011;           // RA2 LED出力 他は入力
    TRISC = 0b111000;           // RC0-RC2 LED出力 他は入力
    LATA   = 0;                 // LED 初期化
    LATC   = 0;                 // LED 初期化
    WPUA  = 0b111111;           // 入力は全て弱プルアップ
    WPUC  = 0b111111;           // 入力は全て弱プルアップ

    // Pin 状態変化 I-O-C設定 -----------------------------------
    IOCANbits.IOCAN5 = 1;       // 立下がり検出
    IOCAPbits.IOCAP5 = 1;       // 立上がり検出
    IOCAFbits.IOCAF5 = 0;       // 入力変位フラッグ クリア
    PIE0bits.IOCIE   = 1;       // 割り込み許可

    // Timer 1 設定 -------------------------------------------
    // リモコンパルス幅の計測に使用する Timer は、1カウント 1uS
    T1CON  = 0b01000000;        // Timer 1 FOSC 1/1(66mSecごと)
    T1GCON = 0b00000000;        // Timer 1 Gate設定(使用せず)
    PIE1bits.TMR1IE = 1;        // Timer 1 割込みの許可
    PEIE = 1;                   // 周辺機能割込みの使用許可
    GIE  = 1;                   // 全割込みの使用許可
    gapFlag = 1;                // Gap フラグ セット
    while(1){                   // 繰り返しループ ------------------
        if(gapFlag){                // Gap なら
            gapFlag = 0;            //   Ir信号受信まで
            // ------------         // ----------
            SLEEP();                //   Sleep
            // ------------         // ----------
        }
        if(revdData == 0) continue; // 受信完了までループして待つ
        // ---- 受信データーの処理 -----------------------------------
        switch (revdData){
            case 0xC084: LED1 ^= 1; break;  // Key 1 LED1 反転
            case 0xC044: LED2 ^= 1; break;  // Key 2 LED2 反転
            case 0xC0C4: LED3 ^= 1; break;  // Key 3 LED3 反転
            case 0xC024: LED4 ^= 1; break;  // Key 4 LED4 反転
			default:     LATC = 0;  break;  // 他のKey 全消灯
        }
        revdData = 0;
    }
}

//----------------------割込み処理------------------------
void interrupt IOC_isr(void){
    // Ir信号がOnOffした ---------------------------------
    if(IOCIF){              //IRsencerからの割込みを確認
        IOCAF = 0;          // 割込みフラッグをクリア
        IrCmdRcv();         // 受信処理
    }
    // Gapを検出した --------------------------------------
    if(TMR1IF){             //Timer1からの割込みを確認
        TMR1IF = 0;         // 割込みフラッグをクリア
        TMR1ON = 0;         // Timer1 OFF
        gapFlag = 1;        // Gap 検出
        state = leaderStart;
    }
}

void IrCmdRcv(void){
    static uint8_t num;         // 受信Bit数を計測
    static uint16_t IrData;     // 受信データを蓄積
    uint16_t timeIntvl = TMR1;  // データ間隔
    TMR1 = 0;                   // 次の間隔測定のため
    // ステート制御 *****************************************************
    switch(state){
        case leaderStart:           // leader Mark 開始時 ⥥ -------
            TMR1ON = 1;                 // Timer1 ON
            if(!IrIN)                   // Markなら
                state = leaderMkEnd;    // leader Mark 終了 待ちへ
            break;
        case leaderMkEnd:           // leader Mark 終了 ⥣ ----------
            if(timeIntvl > L_ON_Min)    // リーダー最小継続を超えるなら
                state = dataStart;      //   data Mark 開始 待ちへ
            else                        // 最小継続 未満なら
                state = waitGap;        //   Timer1 count up 待ちへ
            break;
        case dataStart:             // data Mark 開始時 ⥥ ------------
            num = 16;                   // 受信Bit数リセット
            IrData = 0;                 // 受信dataリセット
            state = dataMkEnd;          // data Mark 終了 待ちへ
            break;
        case dataMkEnd:             // data Mark 終了 ⥣ ------------
            state = dataBitEnd;         // data Bit 終了 待ちへ
            break;
        case dataBitEnd:            // data Bit 終了 ⥥ ------------
            if(timeIntvl > DataTH)      // スペース時間で「1」「0」判定
                IrData |= 1;            //   長ければ「1」
            num--;                      // 受信Bit数の確認
            if(num){                    // 途中なら
                IrData <<= 1;           //   次 Bitの準備
                state = dataMkEnd;      //   次 data bit 受信へ
            }else{                      // 終了なら
                revdData = IrData;      //   結果を保存
                state = waitGap;        //   Timer1 count up 待ちへ
            }
            break;
        case waitGap:               // Gap 待ち ------------
            break;                      // Timer1 timeUpを待つ
    }
}