/*===========================================================================================
Project : IR 적외선리모콘 신호 제어
Version : LK-PIC16F-UK
Date    : 2017-10-16
Author  : Kyung Nam Lee
Company : LKEMBEDDED
Compiler: MPLAB+Hitech-C
Comment : IR 적외선신호를 읽어 들여 텍스트LCD로 정보를 출력하고 있다.
1. Lead code : 리모콘 데이터의 시작을 알려준다.
2. Custom code : 2바이트의 데이터로 구성되어 있다. (주로 리모콘 제작 회사에 따라
다르다)
3. Data code : 2바이트의 데이터로 구성되어 있다. 첫 번째 바이트에 리모콘 버
튼에 따른 정보를 가지고 있다. 그리고 두 번째 바이트는 첫 번째 바이트에 대한 데이
터를 비트 반전 시킨 데이터이다. 예를 들어 첫 번째 data code가 2진수로 11110011
이라면 두 번째 data code는 2진수로 00001100이 된다.
=========================================================================================*/
#include <pic18f452.h>
#include <stdio.h>
#define _XTAL_FREQ 16000000
#include <htc.h>
#define LED_CLK 0X00
#define FNDDB_CLK 0X01
#define FND_BUZZER_CLK 0X02
#define LCDCON_CLK 0X03

#define DC_SERVO_MOTOR_Relay_CLK 0X05
#define DOT_LED_A_CLK 0X06
#define DOT_LED_K_CLK 0X07
#define CP_Sig 0X10  //Active Low Cp

#define Tlcd_RS 0x01
#define Tlcd_RW 0x02
#define Tlcd_EN 0x04

#define On             1
#define Off            0

#define ir_ready        0    //리모콘 데이터 수신 대기 상태
#define ir_lead            1 //리모콘 Lead code의 처음 에지 검출 상태
#define ir_data            2    //리모콘 Custom code, Data code 저장하는 상태
volatile unsigned char ir_rx_data[4] = {0};;//4byte의 수신 데이터를 저장하기 위한 변수
volatile unsigned char Value_rx_data[4] = {0};//4byte의 수신 데이터를 저장하기 위한 사용자 변수
volatile unsigned char ir_state =0;;        //리모콘 수신 데이터에 따른 현재의 상태 표시
volatile unsigned int ir_timer_cnt =0;//리모콘 수신 데이터의 길이를 위한 카운터 변수
volatile unsigned char ir_bit_cnt =0;;    //수신된 비트의 갯수를 저장하기 위한 변수
volatile unsigned char ir_rx_flag =0;;//리모콘 데이터 수신 상태를 표시, 1: 데이터 수신
volatile unsigned char ir_rx_temp =0;;//1byte의 데이터를 저장하기 위한 임시 데이터 변수
volatile unsigned char disp_led =0;;

const char dec[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'} ;
char str01[]={ "IR Remote Test  "};
char str02[]={ "VALUE:"};

unsigned char TlcdCon = 0; //tlcd control 변수이다. PORTD레지스터를 공용으로 사용해야 하므로
// 컨트롤 신호의 상태를 저장하는 임시버퍼로 활용 된다.
char Number[]={'0','1','2','3','4','5','6','7','8','9'};
void flilflop_Enable(unsigned char Add, unsigned char OuputData)
{
	PORTD = OuputData;
	PORTB |= CP_Sig; //74hc574 cp sig HIGH
	PORTC = Add; //74hc574 cp sig HIGH and 74HC138 E3 HIGH
	PORTB &= ~CP_Sig; //74hc574 cp sig LOW
	PORTB |= CP_Sig; //74hc574 cp sig HIGH
}
//--Text LCD----------------------------------------------------------
void dsp_str_TLCD(unsigned char n)
{
	static unsigned char buff_con = 0;
	buff_con = (n&0xf0)|Tlcd_RS;
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con |= Tlcd_EN;
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con &= ~Tlcd_EN;
	flilflop_Enable(LCDCON_CLK,buff_con);
	__delay_ms(1);
	buff_con &= ~Tlcd_RS ;
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con = ((n<<4)&0xf0)|Tlcd_RS;
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con |= Tlcd_EN;
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con &= ~Tlcd_EN;
	flilflop_Enable(LCDCON_CLK,buff_con);
	__delay_ms(1);
	buff_con &= ~Tlcd_RS ;
	flilflop_Enable(LCDCON_CLK,buff_con);
}
void dsp_cmd_TLCD(unsigned char n)
{
	static unsigned char buff_con = 0;
	buff_con = (n&0xf0);
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con |= Tlcd_EN;
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con &= ~Tlcd_EN;
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con = ((n<<4)&0xf0);
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con |= Tlcd_EN;
	flilflop_Enable(LCDCON_CLK,buff_con);
	buff_con &= ~Tlcd_EN;
	flilflop_Enable(LCDCON_CLK,buff_con);
	__delay_ms(1);
}
void clcd_line1()
{
	dsp_cmd_TLCD(0x80);
} //line1
void clcd_line2()
{
	dsp_cmd_TLCD(0XC0);
} //line2
void Tlcd_init() //16x2line
{
	__delay_ms(30);
	dsp_cmd_TLCD(0x30);
	__delay_ms(5);
	dsp_cmd_TLCD(0x30);
	__delay_ms(1);
	dsp_cmd_TLCD(0x30);
	dsp_cmd_TLCD(0x20); //lcd function set (16x2 line, 4bit 설정 , 5x8 dot 설정)
	__delay_ms(1);
	dsp_cmd_TLCD(0x0c); //Display on, Cursor off, Blank off
	dsp_cmd_TLCD(0x14); //cursor/display
	dsp_cmd_TLCD(0x06); //Entry mode set 문자의 진행 방향 결정
	dsp_cmd_TLCD(0x01); // 화면 클리어
}
void string_out(char *str) //문자열 출력 함수
{
	unsigned int i=0;
	do{
		dsp_str_TLCD(str[i]); //-----문자 출력
	}
	while(str[++i]!='\0');
}
void int_display(int i) //텍스트 LCD에 정수를 표현 하기 위한 함수
{
	int j, k, l = 0;
	j = (i % 1000) / 100;
	k = (i % 100) / 10;
	l = i % 10 ;
	//if((j == 0) && (k == 0)){ lcd_putchar(0x20); } //공백 표시는 생략 한다.
	dsp_str_TLCD(Number[j]); //100의 자리
	dsp_str_TLCD(Number[k]); // 10의 자리
	dsp_str_TLCD('.'); // 1의 자리
	dsp_str_TLCD(Number[l]); // 1의 자리
}
void textLcdDisplay(unsigned char d1, unsigned char d2, unsigned char d3, unsigned char d4) //16x2 라인 텍스트LCD 디스플레이 함수
{
	int i=0;
	clcd_line1(); //첫번째 라인
	string_out("IR Recived Test");
	clcd_line2(); //두번째 라인
	int_display(d1); // Custom code 상위 바이트
	string_out("_");
	int_display(d2); // Custom code 상위 바이트
	string_out("_");
	int_display(d3); // Custom code 상위 바이트
	string_out("_");
	int_display(d4); // Custom code 상위 바이트
}

unsigned int cnt_int = 0;
void interrupt HIGH_ISR(void)
{
	if(INT0IF == 1) // 외부인터럽트 플래그(폴링엣지)가 들어오면?
	{
		INT0IF=0;
		TMR0IE = 0;
		switch(ir_state){
			case ir_ready :  // 이상태는 인터럽트 걸리기 전의 상태이다.
			ir_state = ir_lead; // 대기상태에서 Lead code의 하강 에지 검출상태로 변경
			break;
			case ir_lead :   //Lead code 의 처음 하강 에지 검출 상태
			//하강 에지간의 시간 간격이 8.5msec~9.5msec 사이라면
			if((ir_timer_cnt>=134) && (ir_timer_cnt<138)) //133
			{
				ir_state = ir_data;     //Lead code로 인식하여 데이터 저장상태로 변경
			}
			//else ir_state = ir_lead;    //아니면 다시 Lead code 처음 하강 에지 검출 상태로 변경
			ir_bit_cnt = 0;   //수신된 비트수를 초기화
			ir_rx_temp = 0;
			break;

			case ir_data : // 비트를 분석하여 32개 비트(4바이트)를 저장하는 상태
			//0.9msec~1.3msec 사이의 범위라면 수신 비트의 값이 0이다.
			if((ir_timer_cnt>=9) && (ir_timer_cnt<25))
			{
				if((ir_timer_cnt>=9) && (ir_timer_cnt<13)){
					//ir_rx_temp = ir_rx_temp|0x00;
				}
				//1.9msec~2.5msec 사이의 범위라면 수신 비트의 값이 1이다.
				else if((ir_timer_cnt>=19) && (ir_timer_cnt<25)){
					ir_rx_temp = ir_rx_temp|0x80;
				}
				else { //수신된 신호가 데이터가 아니라면
					ir_state = ir_lead;
					break;
				}
				ir_bit_cnt++;
				if((ir_bit_cnt%8)==0)
				{
					ir_rx_data[(ir_bit_cnt / 8) - 1] = ir_rx_temp; //8개의 데이터가 수신되면 값을 저장한다.
					ir_rx_temp = 0;
					if (ir_bit_cnt >= 32)
					{
						ir_state = ir_ready; //모든 데이터가 수신되면 리모콘 신호 수신 대기 상태로 변경
						ir_bit_cnt = 0;
						ir_rx_flag = 1;
						Value_rx_data[2] = ir_rx_data[2];
						textLcdDisplay(ir_rx_data[0],ir_rx_data[1],ir_rx_data[2],ir_rx_data[3]);
					}
				}

				ir_rx_temp = ir_rx_temp>>1;
			}
			else { //수신된 신호가 데이터가 아니라면
				ir_state = ir_ready;
				break;
			}

			break;
		}
		TMR0IE = 1;
		TMR0 = 156; //100uS

		ir_timer_cnt = 0;
	}
	if(TMR0IF  == 1 )//  // 타이머 오버플로우 인터럽트 플래그(100uS마다)가 들어오면?
	{
		TMR0IF = 0;
		ir_timer_cnt++;
		TMR0+=156; //100uS
	}
}
void main(void) // 초기화
{
	//-- 입출력 레지스터 설정 초기화---------------------------------
	TRISA = 0X0f;
	TRISB = 0X01;
	PORTB = 0X00;
	TRISC = 0x80;
	PORTC = 0X00;
	TRISD = 0X00;
	PORTD = 0X00;
	TRISE = 0X00;
	T0CON = 0X01;;   //--  T0PS2:T0PS0=001 4분주비 PIC MCU는 4클럭에 1사이클럭
	INTCON = 0xb0; //GIE=1, INTE=1 설정
	//TMR0IE: TMR0 Overflow Interrupt Enable bit
	TMR0 = 156; //100uS

	Tlcd_init();			//--  Text형LCD 초기화
	ir_timer_cnt = 0;
	ir_bit_cnt = 0;
	ir_rx_flag = 0;
	textLcdDisplay(ir_rx_data[0],ir_rx_data[1],ir_rx_data[2],ir_rx_data[3]);
	flilflop_Enable(LED_CLK,0);
	while(1)
	{
	}
}