#include <p18cxxx.h>
#include "main.h"

// Sample command address of the device and data
#define	ADDRESS		0x00
#define	DATA		0x05

unsigned char Buffer[29], index=0;

#ifdef INTERRUPT_PRIORITY
	//#pragma code HIGH_INTERRUPT_VECTOR = 0x08
	//#pragma interrupt YourHighPriorityISRCode
	//void YourHighPriorityISRCode()
	
	#pragma code LOW_INTERRUPT_VECTOR = 0x18
	#pragma interruptlow YourLowPriorityISRCode
	void YourLowPriorityISRCode()
#else
	#pragma code INTERRUPT_VECTOR = 0x08
	#pragma interrupt YourISRCode
	void YourISRCode()
#endif
{
	//if (PIR1 & 0x02)
	if (PIR1bits.TMR2IF)
	{
		Infrared_Blink_State (Buffer[index]);
		index++;
		
		if (index>=29)
		{
			Infrared_Transmit (OFF);
			DelayMs (3);
			for (index=0; index<28; index++)
				Buffer[index] = 0;
			index=0;
		}
		
		// Clearing the Interrupt Flag
		//PIR1 = PIR1 & (~0x02);
		PIR1bits.TMR2IF = 0;
	}
	return;
}

#pragma code

void DelayMs (volatile unsigned int Ms)
{
	volatile unsigned int x;
	while (Ms--)
		for (x=900; x; x--);
	return;
}

void System_Init (void)
{
	unsigned char i;
	for (i=0; i<29; i++)
		Buffer[i] = 0;
	Clock_Init ();
    Infrared_Init ();
	Digital_PIN_Init ();
    GPIO_Init ();
    Timer2_Init ();
	Infrared_Transmit (OFF);
    return;
}

// Clock initialize
void Clock_Init (void)
{
	OSCTUNEbits.PLLEN = 1;  //Enable the PLL and wait 2+ms until the PLL locks before enabling USB module
	DelayMs (50);
	return;
}

// Analog input/output --> general purpose input/output
void Digital_PIN_Init (void)
{
	// All outputs set as GPIO
    ANCON0 = 0xFF;
    ANCON1 = 0xFF;
    INTCON2bits.RBPU = 0;
    return;
}

void GPIO_Init (void)
{
	BUTTON_INPUT;					// Button --> Input
	
	// Infrared LED: 1 - ON; 0 - OFF
	//TRISC = TRISC & (~0x40);		// Infrared LED --> Output
	LATC = LATC & (~0x40);			// Infrared LED --> Off
	
	// LED: 1 - OFF; 0 - ON
	TRISA = TRISA & (~0x01);		// LED0 --> Output
	LATA = LATA | 0x01;				// LED0 --> Off
    return;
}


// Timer4 is used for Infrared LED to blink (transmitting) with frequency ~36KHz
void Infrared_Init (void)
{
	// SDA & SCK - Input, Low State
	TRISB = TRISB | 0x30;
	PORTB = PORTB & (~0x30);

	// Configure Infrared LED - OUTPUT
	TRISC = TRISC & (~0x40);
	
	RPOR17 = 14; // ECCP1 Compare or PWM Output Channel A
		
	// start TIMER4 - prescaler: 4, count: 83 -> 36145Hz
	// ECCP1 is used in compare mode to generate freq on output
	CCPR1L = HALF_PERIOD;
	CCP1CON = 0b00001100; // PWM mode; PxA and PxC active-high; PxB and PxD active-high
	TCLKCON = 0x02;
		
	PR4 = TOGGLE_PERIOD;
	T4CON = 0b00000101;
	return;
}

// Timer2 is used to start/stop blinking with frequency 1152 Hz (period of 868 us)
void Timer2_Init (void)
{
	int prescaler, temp;
	
    //RCON = RCON | 0x80;			// IPEN = 1;	interrupt priority enabled
    INTCON = INTCON | 0xC0;		// high and low priority interrupts enabled
    
    PIE1 = PIE1 | 0x02;		// Interrupt on Timer2 matching PR register
    IPR1 = IPR1 | 0x02;		// High priority interrupt
    T2CON = 0b01000110;		// setting of the Timer2
    temp = T2CON & 0x03;
    switch (temp)
    {
	    case 0:
	    	prescaler = 1;
	    break;
	    case 1:
	    	prescaler = 4;
	    break;
	    case 2:
	    	prescaler = 16;
	    break;
	    case 3:
	    	prescaler = 16;
	    break;
	    default:
	    break;
    }
    temp = ((T2CON & 0x78)>>3) + 1;
    prescaler = prescaler * temp;
    PR2 = (((FREQUENCY / 1000000) * PERIOD_US) / prescaler) - 1;		// 600 us --> 49; 868 -> 71
	return;
}

/*
ON - The LED is blinking.
OFF - The LED is off.
TOGGLE - Change between these states.
*/
void Infrared_Blink_State (char State)
{
	switch (State)
	{
		case ON:
    		LATA = LATA & ~0x01;
    		RPOR17 = 14; // ECCP1 Compare or PWM Output Channel A - ON
		break;
		case OFF:
    		LATA = LATA | 0x01;
    		RPOR17 = 0; // ECCP1 Compare or PWM Output Channel A - OFF
		break;
		case TOGGLE:
			LATA = LATA ^ 0x01;
			if (RPOR17 == 14)
	    		RPOR17 = 0; // ECCP1 Compare or PWM Output Channel A - OFF
			else
				RPOR17 = 14; // ECCP1 Compare or PWM Output Channel A - ON
   		break;
		default:
		break;
	}
	return;
}

/*
ON - The LED is ON and transmit signals.
OFF - The LED is OFF. No signals transmitting.
TOGGLE - Change between these states.
*/
void Infrared_Transmit (char State)
{
	Infrared_Blink_State (OFF);
	switch (State)
	{
   		case ON:
			PIE1 = PIE1 | 0x02;
			LATA = LATA & (~0x01);
   		break;
   		case OFF:
			PIE1 = PIE1 & (~0x02);
			LATA = LATA | 0x01;
   		break;
   		case TOGGLE:
			PIE1 = PIE1 ^ 0x02;
			LATA = LATA ^ 0x01;
   		break;
		default:
		break;
	}	
	return;
}

void Set_Command (int Address, int Data)
{
	static unsigned char Toggle=1;
	unsigned char Mask, Temp, i;
	index = 0;
	Buffer[0] = 0;
	Buffer[1] = 1;
	Buffer[2] = 0;
	Buffer[3] = 1;
	Buffer[4] = !Toggle;
	Buffer[5] = Toggle;
	Toggle = !Toggle;

	Mask = 0x10;
	for (i=6; i<=15; i=i+2)
	{
		Temp = !(Address & Mask);
		Buffer[i] = Temp;
		Buffer[i+1] = !Temp;
		Mask = Mask >> 1;
	}

	Mask = 0x20;
	for (i=16; i<=27; i=i+2)
	{
		Temp = !(Data & Mask);
		Buffer[i] = Temp;
		Buffer[i+1] = !Temp;
		Mask = Mask >> 1;
	}
	return;
}	

void main ()
{
	unsigned char Released=1;
	System_Init ();
	

	while (1)
	{
		if (BUTTON_PRESSED && Released)
		{
			Infrared_Transmit (OFF);
			Set_Command (ADDRESS, DATA);
			Infrared_Transmit (ON);
			Released = 0;
			DelayMs (100);
		}

		if (!BUTTON_PRESSED)
		{
			DelayMs (100);
			Released = 1;
		}	
	}
	return;
}
