//-----------------------------------------------------------------------------------------------------------
// 赤外線リモコンの受信(PIC12F1822-IRRX基板-2012.6.29版用)
// 2012/07/01-2012/07/06
//-----------------------------------------------------------------------------------------------------------
// ■＜リモコンデータの記憶方法＞
// 　SW長押しで、記憶モードに移行(PORT0点灯、LED遅点滅)
// 　SW長押しで、PORT0記憶モードに移行(PORT0点灯、LED速点滅)
// 　リモコンのボタンを押すと、PORT0をHIにするボタンとして登録される(LEDが300mS消灯した後に速点滅)
// 　リモコンのボタンを押すと、PORT0をLOWにするボタンとして登録される(LEDが300mS消灯した後に速点滅)
// 　リモコンのボタンを押すと、PORT0を反転するボタンとして登録される(LEDが300mS消灯した後に速点滅)
// 　リモコンのボタンを押すと、PORT0に300mSのパルスを出すボタンとして登録される(LEDが300mS消灯した後に遅点滅)
// 　SW短押しで、PORT1記憶モードに移行する(PORT1点灯、LED速点滅)
// 　上記を繰り返して、PORT1/PORT2の記憶を行う。
// 　PORT2点灯、LED遅点滅の状態で、SW短押しで、記憶モードを抜ける(LED点灯)
// ■＜ポート初期データの記憶方法＞
// 　SW長々押しで、現在のポート状態を記憶する(PORT0点灯、LED遅点滅)
// ■＜記憶データの全消去方法＞
// 　SWを15秒以上長押しする(15秒押し続けるとLEDが消える)
//-----------------------------------------------------------------------------------------------------------
// 　SW短押し　：1秒以下で押す
// 　SW長押し　：1秒以上10秒以下で押す
// 　SW長々押し：10秒以上15秒以下で押す
// 　遅点滅　　：1秒間に5回点滅
// 　速点滅　　：1秒間に10回点滅
//-----------------------------------------------------------------------------------------------------------

#include <htc.h>
#include <stdio.h>

__CONFIG (FOSC_INTOSC & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF &  CPD_OFF & BOREN_OFF & FCMEN_ON);
__CONFIG (LVP_OFF & WRT_OFF & PLLEN_OFF & STVREN_ON & BORV_19);

#define	BAUDRATE			2400			// RS-232Cのボーレート
#define	EE_PORT_DEFAULT	0			// 初期ポートデータのEEPROM記憶アドレス
#define	EE_IR_OFFSET		1			// リモコンデータのEEPROM記憶先頭アドレス
#define	IR_BYTE			6			// リモコン １コマンドのバイト数
#define	IR_COM			4			// リモコン ポートに対するコマンド番号(0:on 1:off 2:invert 3:pulse)
#define	IR_PORT			3			// ポート数
#define	PUSH1			1000			// スイッチ短押し時間(ミリ秒) 
#define	PUSH2			10000		// スイッチ長押し時間(ミリ秒) 
#define	PUSH3			15000		// スイッチ長々押し時間(ミリ秒) 
#define	BLINK1			100			// LED長点滅時間(ミリ秒) 
#define	BLINK2			50			// LED短点滅時間(ミリ秒) 

#define	IR1T			TRISA			// 赤外線受信(赤外線センサーのポート)
#define	IR1P			PORTA			// ↓
#define	IR1A			ANSELA			// ↓
#define	IR1B			0x20				// ↓
#define	IR1			(IR1P&IR1B)		// ↓
#define	LED			0b010000			// 状態表示用LED
#define	LED0			LATA&=~LED;		// ↓
#define	LED1			LATA|=LED;		// ↓
#define	LED2			LATA^=LED;		// ↓
#define	SW1			(PORTA&0b001000)	// スイッチ

unsigned const char bb[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};	// BCDからビットへの変換テーブル
unsigned char dat[IR_BYTE],err;	// リモコンのコードセット
unsigned int w;					// スイッチの長押し時間
unsigned char DebugFlag;			// デバックモードフラグ(0:通常モード、1:デバックモード)

unsigned char eeread(unsigned char adr){				// EEPROMの読み込み
	EEADRL=adr;
	RD=1;
	while(RD);
	return EEDATL;
}

void eewrite(unsigned char adr,unsigned char dat){		// EEPROMの書き込み
	WREN=1;
	EEADRL=adr;
	EEDATL=dat;
	EECON2=0x55;
	EECON2=0xaa;
	WR=1;
	WREN=0;
	while(WR);
}

void rs232c_init(void){					// RS232Cの初期化(送信のみ)
 	BRG16=1;
	SPBRGH = (8000000L/BAUDRATE-1)>>8;		// set the baud rate  ((FOSC/4)/baud)-1)
	SPBRGL = (8000000L/BAUDRATE-1)&0xff;	// set the baud rate  ((FOSC/4)/baud)-1)
	TXSTA=0b00100110;						// TRANSMIT STATUS AND CONTROL REGISTER
	RCSTA=0b10000000;						// RECEIVE STATUS AND CONTROL REGISTER
	//RXDTSEL=0;
	//TXCKSEL=0;
}

void putch(unsigned char byte){			// RS232Cへ1バイト送信
	while(!TXIF);
	TXREG = byte;
	return;
}

void wait1ms(int t){						// 1*nミリ秒の時間待ち
	PR2=125;
	T2CON=0b00000111;		// 8us
	TMR2=0;
	while(t){
		TMR2IF=0;
		while(!TMR2IF);
		t--;
	}
}

void irread(unsigned char *dat,unsigned char *err){	// IR(赤外線)の受信
	unsigned char i,d;
	unsigned int  t1;				// スタートマークのon時間
	unsigned int  t2;				// スタートマークのoff時間
	unsigned char tt[(IR_BYTE)*8];	// 受信データ格納
	unsigned char max0,min0;			// IR-ONのデータ時間の最大値・最小値
	unsigned char max1,min1;			// IR-OFFのデータ時間の最大値・最小値
	unsigned char num;				// IRのデータ数(ビット数)

	T2CON=0b00000111;		// 8us
	PR2=255;
	TMR2=0;

	*err=1;

	//for(i=0; i<IR_BYTE; i++) dat[i]=0;
	for(i=0; i<IR_BYTE*8; i++) tt[i]=0;

	TMR2IF=0; t1=0;		// --------スタートマークのon時間計測
	while(!IR1){
		if(TMR2IF==1){ TMR2IF=0; t1+=255; }
	}
	t1+=TMR2; TMR2=0;

	TMR2IF=0; t2=0;		// --------スタートマークのoff時間計測
	while(IR1){
		if(TMR2IF==1){ TMR2IF=0; t2+=255; }
		if(t2>1000) { *err=1; return; }
	}
	t2+=TMR2; TMR2=0;

	TMR2IF=0;
	num=0;
	while(num<IR_BYTE*8){		// --------データ受信 最大48bit(6byte)
		while((!IR1));				// IR-ONの間ループ
		tt[num]=(TMR2&0xf0); TMR2=0;	// IR-ON時間を上位4ビットに格納(8us*16*n)
		while((IR1)&&(!TMR2IF));		// IR-OFFの間ループ
		if(TMR2IF) break;				// 2048us(8us*256)以上経ったらループを抜ける
		tt[num]+=(TMR2/16); TMR2=0;	// IR-OFF時間を下位4ビットに格納(8us*16*n)
		num++;
	}

	min0=255; max0=0;		// --------データの幅の最大値・最小値を取得
	min1=255; max1=0;
	for(i=0; i<num; i++){
		d=tt[i]/16;
		if(d<min0) min0=d;	// IR-ON時間の最小値
		if(d>max0) max0=d;	// IR-ON時間の最大値
		d=tt[i]&0x0f;
		if(d<min1) min1=d;	// IR-OFF時間の最小値
		if(d>max1) max1=d;	// IR-OFF時間の最大値
	}

	if((max0-min0)>(max1-min1)){	// --------IR-ONとIR-OFFでバラツキが多い方をデータと判断
		d=0;				// --------IR-ON時間の長さで0/1を判定(SONY)
		num++;
		for(i=0; i<IR_BYTE*8; i++){
			dat[i>>3] >>= 1;
			if((tt[i]/16)>((min0+max0)/2)) dat[i>>3]+=0x80;
		}
	}else{
		d=1;				// --------IR-OFF時間の長さで0/1を判定(家電協/NEC)
		for(i=0; i<IR_BYTE*8; i++){
			dat[i>>3] >>= 1;
			if((tt[i]&0x0f)>((min1+max1)/2)) dat[i>>3]+=0x80;
		}
	}	

	//-----------(家電協/Panasonic)
	if((400<t1)&&(t1<500)&&(150<t2)&&(t2<250)&&(num==48)&&(d==1)){		// スタートマーク・ビット数チェック
		if((dat[2]^dat[3]^dat[4]) == dat[5]){						// チェックサム
			*err=0;
		}
	}

	//-----------(NEC/BUFFALLO)
	if((1000<t1)&&(t1<1200)&&(500<t2)&&(t2<600)&&(num==32)&&(d==1)){	// スタートマーク・ビット数チェック
		if((dat[2]+dat[3]==0xff)){									// チェックサム
			*err=0;
		}
	}

	//-----------(SONY)
	if((200<t1)&&(t1<400)&&(50<t2)&&(t2<90)&&(d==0)&&((num==13)||(num==15)||(num==20))){	// スタートマーク・ビット数チェック
		*err=0;
	}

	if(DebugFlag){			// ---------デバッグモードの場合RS232Cへ受信データを出力
		for(i=0; i<48; i++){
			if(i%8==0) printf("\r");
			printf(" %02X",tt[i]);
		}
		printf("\r[num:%02d]",num);
		printf(" [hi:%d]",t1);
		printf(" [low:%d]",t2);
		printf(" [data:%02X",dat[0]);
		printf(" %02X",dat[1]);
		printf(" %02X",dat[2]);
		printf(" %02X",dat[3]);
		printf(" %02X",dat[4]);
		printf(" %02X]",dat[5]);
		printf("\r");
	}
}

void seting(void){		// --------リモコンボタンの記憶モード
	unsigned char i;
	unsigned char p;		// 設定するポート番号
	unsigned char n;		// ポートに対するコマンド番号(0:on 1:off 2:invert 3:pulse)

	p=0;
	while(p<=2){				// --------ポートループ(RA0-RA2)
		LATA=bb[p]+LED;
		LED0; wait1ms(200);
		while(1){
			if(TMR2IF){		// LED遅点滅
				TMR2IF=0;
				if(i++>BLINK1) { LED2; i=0; }
			}
			w=0;
			while(!SW1){		// SWが押された時
				if(TMR2IF){
					TMR2IF=0;
					if(w++<PUSH1){
						if(w%BLINK1==0) LED2;
					}else{
						if(w%BLINK2==0) LED2;	// SW長押しでLED早点滅(リモコンボタン記録モードへ)
					}
				}
			}
			if(w>=PUSH1){			// --------SW長押しでリモコンボタン記録モード
				LED0; wait1ms(200);
				n=0;
				while(n<=3){			// -------コマンドループ(No.0-No.3)
					if(TMR2IF){
						TMR2IF=0;
						i++;
						if(i>=BLINK2) { LED2; i=0; }
					}
					if(!IR1){			// --------IR受信すればボタンを記録する
						irread(dat,&err);
						if(err==0){
							for(i=0; i<IR_BYTE; i++){
								eewrite(i+(n+p*IR_COM)*IR_BYTE+EE_IR_OFFSET,dat[i]);
							}
							LED0;		// 次のコマンドへ
							wait1ms(300);
							LED1;
							n++;
						}
					}
					if(!SW1){		// --------SWが押されたらそのコマンドはクリアする
						for(i=0; i<IR_BYTE; i++){
							eewrite(i+(n+p*IR_COM)*IR_BYTE+EE_IR_OFFSET,0);
						}
						LED0;		// 次のコマンドへ
						wait1ms(300);
						LED1;
						n++;
					}
				}
				break;
			}
			if((0<w)&&(w<PUSH1)){		// --------SW短押しで次のポートへ
				p++;
				break;
			}
		}
	}
	LATA=eeread(EE_PORT_DEFAULT)|LED;	// ポート初期値の呼び出し
}

void main(){
	unsigned char i,j,k;

	//OSCCON = 0b01010000;				//  1MHz (発振 1MHz)
	//OSCCON = 0b01100000;				//  2MHz (発振 2MHz)
	//OSCCON = 0b01101000;				//  4MHz (発振 4MHz)
	//OSCCON = 0b01110000;				//  8MHz (発振 8MHz)
	//OSCCON = 0b01111000;				// 16MHz (発振16MHz)
	OSCCON = 0b11110000;				// 32MHz (発振 8MHz x 4PLL)
									// <7>   1:4xPLL
									// <6-3> 1110:8MHz(IRCF)
									// <1-0> 00:IRCF

	OPTION_REG &= 0b01111111;			//グローバル弱プルアップON
	//WPUA=0b11111111;

	ANSELA=0;						// PORTAをデジタルI/O設定
	TRISA=0b101000;					// PORTA入出力設定(0:出力 1:)
	LATA=0;

	wait1ms(1000);					// 1秒時間待ち
	//for(i=0; i<4; i++){				// LED点滅
	//	LED1; wait1ms(20);
	//	LED0; wait1ms(480);
	//}

	if(!SW1){
		DebugFlag=1;		// ------電源ON時にSW押下でデバッグモード
		while(!SW1){
			if(TMR2IF){
				TMR2IF=0;
				i++;
				if(i>=BLINK2) { LED2; i=0; }
			}
		}
		rs232c_init();
		printf("\rDebug mode [Version 2012.7.6]\r");
	}

	LATA=eeread(EE_PORT_DEFAULT)|LED;	// ポート初期値の呼び出し

	while(1){			// ------------------------------------------メインループ
		LED1;
		if(!IR1){					// ---------------赤外線受信処理
			irread(dat,&err);
			if(err==0){				// ---------------正常なIRが受信できた場合修理実行
				LED0;
				for(i=0; i<IR_PORT; i++){
					for(j=0; j<IR_COM; j++){
						for(k=0; k<IR_BYTE; k++){
							if(eeread(k+(j+i*IR_COM)*IR_BYTE+EE_IR_OFFSET)!=dat[k]) break;
						}
						if(k==IR_BYTE){
							if(j==0) LATA|=bb[i];								// 該当ポートのビットをonにする。
							if(j==1) LATA&=~bb[i];								// 該当ポートのビットをoffにする。
							if(j==2) LATA^=bb[i];								// 該当ポートのビットを反転する。
							if(j==3) { LATA^=bb[i]; wait1ms(300); LATA^=bb[i]; }	// 該当ポートのビットにパルスを出力する。
						}
					}
				}
				wait1ms(200);
			}
		}
		if(!SW1){					// --------スイッチを押した時
			LED0;
			w=0;
			wait1ms(0);
			while(!SW1){				// --------押し続けた時間を計測
				if(TMR2IF){
					TMR2IF=0;
					w++;
					if(w>=PUSH3){
						LED0;					// LED消灯
					}else if(w>=PUSH2){
						if(w%BLINK2==0) LED2;		// LED速点滅
					}else if(w>=PUSH1){
						if(w%BLINK1==0) LED2;		// LED遅点滅
					}
				}
			}
			if((PUSH1<=w)&&(w<PUSH2)) { LED0; wait1ms(300); seting(); }			// SW長押しで、記憶モードへ
			if((PUSH2<=w)&&(w<PUSH3)) eewrite(EE_PORT_DEFAULT,LATA&0b00000111);		// SW長々押しで、ポート初期値記憶
			if(PUSH3<=w){														// SWを20秒以上長押しで、全記憶初期化
				LATA=LED;
				eewrite(EE_PORT_DEFAULT,0);
				for(i=0; i<IR_PORT; i++){
					for(j=0; j<IR_COM; j++){
						for(k=0; k<IR_BYTE; k++){
							eewrite(k+(j+i*IR_COM)*IR_BYTE+EE_IR_OFFSET,0);
						}
					}
				}
			}
		}
	}
}
