赤外線リモコンの信号を受信しLEDを点灯させるプログラムです。
リモコンの「1」ボタンを押すと「LED1(RA2)」 が点灯します。
再び「1」を押すとLEDは消灯します。
同様に、リモコンの「2」「3」「4」が
それぞれ LED2(RC0)」「LED3(RC1)」「LED4(RC2)」に対応します。
PICのペリフェラルは以下の用途に使用しています。
この受信機は、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 | 2 | C044 | 8 | C014 |
| タイマー | C0C0 | 消音 | C038 | 3 | C0C4 | 9 | C094 |
| BS/e2 | C030 | + | C098 | 4 | C024 | 10 | C054 |
| 地デジ | F0A8 | − | C018 | 5 | C0A4 | 11 | C0D4 |
| 機能 | 赤外線の出力なし | 6 | C064 | 12 | C034 | ||
2本のプログラム例を示します。
一番目は、赤外線コードが終了するまで、赤外線センサーの信号をメインプロブラムのフローで常時監視し、On/OffをTimer1で計測します。
2番目のプログラムは、センサーからの信号は、I-O-C (Interrupt-on-Change)を利用し、割り込み処理の中で処理し、データが正しく取得された時に、主プロブラムに通知し、LEDを点灯します。
/*********************************************************************
* NECタイプの赤外線リモコン受信
* LED1 RA2, LED2 RC0, LED3 RC1, LED4 RC2
* Sencer RA5
* 1MHz (内部クロック)
*
* PIC16F18346 MPLAB X IDE with XC8
* Copyright (c) 2020 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 を使用して受信情報を割り込みで処理
*
* PIC16F18346 MPLAB X IDE with XC8
* Copyright (c) 2020 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を待つ
}
}