/*
   Este programa reconoce el protocolo RC5 de los mandos a distancia.
   El protocolo RC5 usa una modulación bifase, lo que significa que cada bit está dividido en 2
   partes:
      LOW  -> 10
      HIGH -> 01
      
   Una señal está compuesta por 14 bits:
      - 2 bits de inicio (11)
      - 1 bit de cambio (cambia cuando se suelta y se vuelve a pulsar el botón)
      - 5 bits de dirección (única para cada dispositivo)
      - 6 bits de comando (para cada tecla)

   Cada bit dura 1.778ms y una señal completa dura 24.889ms. La señal completa es repetida cada 114ms, 
   es decir, que hay una separación de 89.111ms entre dos señales repetidas
*/


#include <16F88.h>
#device adc=8

#FUSES WDT                      //Watch Dog Timer
#FUSES HS						//High speed Osc (> 4mhz)
#FUSES PUT                      //Power Up Timer
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOFCMEN                  //Fail-safe clock monitor disabled
#FUSES NOIESO                   //Internal External Switch Over mode disabled
#FUSES CCPB3					//PWM en pin B3

#use fast_io(A)
#use fast_io(B)
#use delay(clock=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_A0,rcv=PIN_A1,bits=8)

#define PIN_IR  PIN_B4

const int  BUFFER_SIZE = 28;
const long SHORT_PULSE = 555;
const long PULSE_ERROR = 100;
const long TIMER1_LOAD = 34286;   				//50ms con el prescaler 1:8 (para la separación entre comandos)
const long TIME_BUZZER = 3;       				//3 * 50ms = 150ms

const long DUTY_CICLE = 22700;					//aprox. 50%
const int  T2_PRESC = T2_DIV_BY_16;
const long C_NOTE[2] = {239, 119}; 				// 262Hz;
const long D_NOTE[2] = {213, 106}; 				// 294Hz;
const long E_NOTE = 189; 						// 330Hz;
const long F_NOTE = 179; 						// 349Hz;
const long G_NOTE = 159; 						// 392Hz;
const long A_NOTE = 142; 						// 440Hz;
const long B_NOTE = 127; 						// 494Hz;

static long  data;
static int   cmd, timeCount;
static short flagTimeOverflow, flagDataAvailable, flagH_L;
static long  bufferTime[BUFFER_SIZE];
static long  currentTime;
static int   index;

void  processData(void);
void  processNote(void);
void  printCmd(void);
short isShort(long);



#int_TIMER1
void  TIMER1_isr(void) {   						// 50ms
	disable_interrupts(GLOBAL);					//creo que se desactivan las interrupciones automáticamente, pero por si acaso!
   	flagTimeOverflow  = TRUE;
   	if (index > 0){
      	flagDataAvailable = TRUE;
   	}
   
	//apaga el buzzer
	if (timeCount-- == 0){
		set_pwm1_duty(0);
	}
	enable_interrupts(GLOBAL);
}


#int_RB
void RB_isr(void) {   
	disable_interrupts(GLOBAL);	
   	currentTime  = get_timer1();
   	set_timer1(TIMER1_LOAD);      //50ms
   
   	if (flagH_L){
	  	ext_int_edge(L_TO_H);
   	} else {
   	  	ext_int_edge(H_TO_L);
   	}
   	flagH_L = !flagH_L;

   	output_b(input_b());			//sólo para evitar reentrada en interrupción     
   
   	if (flagTimeOverflow){
      	index = 0;
      	flagTimeOverflow = FALSE;
      	ext_int_edge(H_TO_L);
      
   	} else{
      	bufferTime [index++] = currentTime - TIMER1_LOAD;
   	}
   	enable_interrupts(GLOBAL);   
}



void main(){
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF);
   setup_spi(SPI_SS_DISABLED);
   //setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
   //setup_timer_2(T2_DIV_BY_16,255,16);
   setup_wdt(WDT_288MS);				//tendría que ser suficiente con 36ms
   setup_ccp1(CCP_PWM);
   enable_interrupts(INT_TIMER1);
   enable_interrupts(INT_RB);   
   ext_int_edge(H_TO_L);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   set_pwm1_duty(0);
   
   set_tris_a(0x02);
   set_tris_b(0xF0);

   index = 0;
   data  = 0;
   cmd   = 0;
   flagTimeOverflow  = TRUE;
   flagDataAvailable = FALSE;  
   flagH_L			 = TRUE; 
   
   printf ("\n\rInit... OK\n\r");
   
   enable_interrupts(GLOBAL);
      
      
   	while(true){
	    restart_wdt();
	    
    	if (flagDataAvailable){
	     	disable_interrupts(GLOBAL);
         	flagDataAvailable = FALSE;
         
         	processData();   
                  	
         	index = 0;
         	flagTimeOverflow  = TRUE;
         	flagDataAvailable = FALSE;
         	flagH_L		   = TRUE; 
         	enable_interrupts(GLOBAL);			//activamos las interrupciones antes de tocar la nota
         
         	processNote();       
      }            
   }
}


void processData(){
   short flagPar=TRUE, flagHigh=FALSE, flagShort=TRUE;
   int i=0, b=13; 
   data=0,cmd=0; 
  	
  	//procesa los datos recibidos
	for (i=0; i<index; i++){
   		flagHigh 	= !flagHigh;
   	  	flagShort	= isShort(bufferTime[i]);
   	  
      	if (flagPar){
			if (flagHigh){					// TO DO: 
				bit_set(data,b--);			// meter esto 
			} else {						// en una función 
				bit_clear(data,b--);   		// para no repetir código
			}		
      	}else{
      		if (!flagShort){
	    		if (flagHigh){
					bit_set(data,b--);
				} else {
					bit_clear(data,b--);  
				}
			}
		}
		       
      	if (flagShort){
        	flagPar = !flagPar;
      	}
   }
   
   if (b == 1){
		if (!flagHigh){
			bit_set(data,0);
		} else {
			bit_clear(data,0); 
		}
   }      
   
   //procesa el comando
   cmd = (int)(data & 0x3F);
}


void processNote(){
	short notaCorrecta=FALSE;
	
	switch(cmd){
		case 1:
			setup_timer_2(T2_PRESC, C_NOTE[0], 1);
			printf("C");
			notaCorrecta=TRUE;				
	      	break;
	   	case 2:
	    	setup_timer_2(T2_PRESC, D_NOTE[0], 1);
			printf("D");
			notaCorrecta=TRUE;				
	      	break;
	   	case 3:
			setup_timer_2(T2_PRESC, E_NOTE, 1);
			printf("E");
			notaCorrecta=TRUE;				
	      	break;
	   	case 4:
			setup_timer_2(T2_PRESC, F_NOTE, 1);
			printf("F");
			notaCorrecta=TRUE;				
	      	break;
	  	case 5:
			setup_timer_2(T2_PRESC, G_NOTE, 1);
			printf("G");
			notaCorrecta=TRUE;				
	      	break;
	   	case 6:
			setup_timer_2(T2_PRESC, A_NOTE, 1);
			printf("A");
			notaCorrecta=TRUE;				
	      	break;
	   	case 7:
			setup_timer_2(T2_PRESC, B_NOTE, 1);
			printf("B");
			notaCorrecta=TRUE;				
	      	break;
	   	case 8:
			setup_timer_2(T2_PRESC, C_NOTE[1], 1);
			printf("C");
			notaCorrecta=TRUE;				
	      	break;
	   	case 9:  
			setup_timer_2(T2_PRESC, D_NOTE[1], 1);
			printf("D");
			notaCorrecta=TRUE;				
	      	break; 	
	}
	
	if (notaCorrecta){
		set_pwm1_duty(DUTY_CICLE);				//enciende el buzzer
		timeCount = TIME_BUZZER;
	}					
}


short isShort (long t){
   if (t<(SHORT_PULSE+PULSE_ERROR)){
      return TRUE;
      
   } else {
      return FALSE;
   }   
} 
