#include <reg51.h> 

#define LED P0  //LED가 연결되어 있는 8051의 포트를 설정
#define ir_ready  0 //리모콘 데이터 수신 대기 상태 
#define ir_lead   1 //리모콘 Lead code의 처음 에지 검출 상태
#define ir_data   2 //리모콘 Custom code, Data code 저장하는 상태 

sbit BUZZ = P2^0; //부져를 제어하기 위한 변수 

unsigned char ir_rx_data[4];//4byte의 수신 데이터를 저장하기 위한 변수
unsigned char ir_state;  //리모콘 수신 데이터에 따른 현재의 상태 표시
unsigned char ir_timer_cnt; //리모콘 수신 데이터의 길이를 위한 카운터 변수
unsigned char ir_bit_cnt; //수신된 비트의 갯수를 저장하기 위한 변수 

unsigned char ir_rx_flag; //리모콘 데이터 수신 상태를 표시, 1: 데이터 수신
unsigned char ir_rx_temp; //1byte의 데이터를 저장하기 위한 임시 데이터 변수 

unsigned char disp_led;


//시간 지연을 위한 함수 d_cnt 값이 1이면 약 100msec를 의미한다. 
void delay( unsigned int d_cnt )
{
	unsigned int i;

	while(d_cnt--){
	  for(i=0; i<46000; i++);
	}
}

//약 100usec 마다 타이머 인터럽트 발생된다.
void Timer0_int() interrupt 1
{
	ir_timer_cnt++; //약 100usec 마다 숫자가 증가한다. 
}

//리모콘 수신 센서로부터 들어오는 데이터가 High에서 Low로 하강하는 순간
//인터럽트가 발생된다. 외부인터럽트가 발생되는 시간 간격을 통하여 
//수신된 데이터가 어떤것인지 알 수 있다. 
void EX0_int() interrupt 0 
{
	EA = 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>=85) && (ir_timer_cnt<95)) 
				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<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;
    }
   }   
   ir_rx_temp = ir_rx_temp>>1;
      break;

  default: break;
 }

 ir_timer_cnt = 0; 
// TH0 = TL0 = 0xA3; //100usec, 클럭 11.0592MHz 사용시
 TH0 = TL0 = 0x47; //100usec, 클럭 22.1184MHz 사용시

 EA = 1;                 //타이머, 외부 인터럽트 인에이블
}


void init_system(void)
{
 unsigned char l_cnt;

 BUZZ = 0;
 ir_rx_flag = 0;
 
 // 센서 수신을 위한 변수 초기화
 ir_state = ir_ready;
 ir_timer_cnt = 0;
 ir_bit_cnt = 0;
 
 disp_led = 0xFF;
 
 for(l_cnt=0; l_cnt<3; l_cnt++){
  LED = 0x00;
  delay(2);
  LED = 0xFF;
  delay(2);
 }

 //타이머 인터럽트와 외부 인터럽트 초기화
 TMOD = 0x02;   //타이머0 모드2
// TH0 = TL0 = 0xA3; //100usec, 클럭 11.0592MHz 사용시
 TH0 = TL0 = 0x47; //100usec, 클럭 22.1184MHz 사용시

 ET0 = 1;     //타이머0 인터럽트 인에이블
 IT0 = 1;     //외부 인터럽트의 하강에지에서 인터럽트 발생
 EX0 = 1;     //외부 인터럽트0 인에이블
 TR0 = 1;     //타이머0 스타트
 EA = 1;      //인터럽트 인에이블
}

void main(void)
{
 init_system();

 while(1)
 {
  if(ir_rx_flag == 1){ // 리모콘으로부터 신호(4byte)가 수신되면  
   EA = 0;

   BUZZ = 1;   //  부져소리를 On한다. 
   ir_rx_flag = 0;  //플래그 초기화    

   //첫번째 Code data의 값이 일치하면 각 비트에 해당되는 비트만 반전시킨다.
   switch(ir_rx_data[2]){   
    // 리모콘 숫자 0이 눌리면
    case 0x11 : disp_led = 0xFF; break;
    // 리모콘 숫자 1이 눌리면
    case 0x04 : disp_led = disp_led^0x01; break;
    // 리모콘 숫자 2가 눌리면
    case 0x05 : disp_led = disp_led^0x02; break;
    // 리모콘 숫자 3이 눌리면
    case 0x06 : disp_led = disp_led^0x04; break;
    // 리모콘 숫자 4가 눌리면
    case 0x08 : disp_led = disp_led^0x08; break;
    // 리모콘 숫자 5가 눌리면
    case 0x09 : disp_led = disp_led^0x10; break;
    // 리모콘 숫자 6이 눌리면
    case 0x0A : disp_led = disp_led^0x20; break;
    // 리모콘 숫자 7이 눌리면
    case 0x0C : disp_led = disp_led^0x40; break;
    // 리모콘 숫자 8이 눌리면
    case 0x0D : disp_led = disp_led^0x80; break;
    // 리모콘 숫자 9가 눌리면
    case 0x0E : disp_led = 0x00; break;

    default : break;
   }              
   LED = disp_led; //led로 표시한다.  

   delay(1);  
   BUZZ = 0;  //  부져소리를 Off한다. 
   EA = 1;
  } 
 }
}





