#include <c8051F000.h>		//Include register definitions

//Define a name and function for each button
#define	PLAY		0x0A
#define STOP 		0x0B
#define PAUSE 		0x0C
#define PREVTRACK 	0x0D
#define NEXTTRACK 	0x0E
#define SKIPBACK 	0x0F
#define SKIPFORWARD 0x10
#define SHUFFLE 	0x11
#define REPEAT 		0x12
#define VOLUMEUP 	0x13
#define VOLUMEDOWN 	0x14
#define BALANCE_L	0x15
#define BALANCE_R	0x16
#define MUTE 		0x17
#define RECORD		0x18
#define UNUSED		0x19
#define	POWER		0x1A
#define DRIVE		0x1B
#define EJECT		0x1C
#define ADDRESS		0x0F
#define FALSE		0
#define TRUE		1

sbit RC5_DATA_OUT = P1^0;
sbit RC5_MODULATED_OUT = P0^7;
sbit ROW_1 = P0^2;
sbit ROW_2 = P0^3;
sbit ROW_3 = P0^4;
sbit ROW_4 = P0^5;

//Function prototypes
void configure_chip(void);
void scan_switches(unsigned char, unsigned char);
void send_code(unsigned char);
void transmit(unsigned char);

unsigned char bit_mask = 0x10;	//used to create a serial data stream
unsigned int k = 0;				//a for loop counter
unsigned int count = 0;			//used with the Timer1 interrupt function to get the correct data rate on the RC5 output pin (P1^0)
unsigned char row = 0;

void main(void)
{
	RC5_MODULATED_OUT = FALSE;	//make sure the LEDs are off, otherwise we'll burn them out	

	// setup all the registers in the 8051
	configure_chip();

	RC5_DATA_OUT = FALSE;  //so we don't burn the LEDs
	
	while(TRUE)		//do all this forever, or until we lose power
	{
		//scan the buttons
		ROW_1 = TRUE;
		row = P1 & 0xFE;	//mask out P1.0 as that is the unmodulated RC5 output
		scan_switches(1, row);	//check P1.1 to P1.7 on column 1
		// and if any of P1.1 to P1.7 are high, send associated code for that button on column 1
		ROW_1 = FALSE;

		ROW_2 = TRUE;
		row = P1 & 0xFE;	//mask out P1.0 as that is the unmodulated RC5 output
		scan_switches(2, row);	//check P1.1 to P1.7 on column 2
		//and if any of P1.1 to P1.7 are high, send associated code for that button  on column 2
		ROW_2 = FALSE;

		ROW_3 = TRUE;
		row = P1 & 0xFE;	//mask out P1.0 as that is the unmodulated RC5 output
		scan_switches(3, row);	//check P1.1 to P1.7 on column 3
		//and if any of P1.1 to P1.7 are high, send associated code for that button  on column 3
		ROW_3 = FALSE;

		ROW_4 = TRUE;
		row = P1 & 0xFE;	//mask out P1.0 as that is the unmodulated RC5 output
		scan_switches(4, row);	//check P1.1 to P1.7 on column 4
		//and if any of P1.1 to P1.7 are high, send associated code for that button  on column 4
		ROW_4 = FALSE;

		RC5_DATA_OUT = FALSE;
	};
}

void scan_switches(unsigned char column, unsigned char row)
{
	switch(column)
	{
		case(1):
			//figure out code to send to send_code()
			switch(row)
			{
				case(0x02):
					send_code(PLAY);
					break;
				case(0x04):
					send_code(STOP);
					break;
				case(0x08):
					send_code(PAUSE);
					break;
				case(0x10):
					send_code(RECORD);
					break;
				case(0x20):
					send_code(EJECT);
					break;
				case(0x40):
					send_code(VOLUMEUP);
					break;
				case(0x80):
					send_code(VOLUMEDOWN);
					break;
			}
			break;
		case(2):
			//figure out code to send to send_code()
			switch(row)
			{
				case(0x02):
					send_code(BALANCE_L);
					break;
				case(0x04):
					send_code(BALANCE_R);
					break;
				case(0x08):
					send_code(PREVTRACK);
					break;
				case(0x10):
					send_code(SHUFFLE);
					break;
				case(0x20):
					send_code(MUTE);
					break;
				case(0x40):
					send_code(NEXTTRACK);
					break;
				case(0x80):
					send_code(REPEAT);
					break;
			}
			break;
		case(3):
			//figure out code to send to send_code()
			switch(row)
			{
				case(0x02):
					send_code(DRIVE);
					break;
				case(0x04):
					send_code(UNUSED);
					break;
				case(0x08):
					send_code(POWER);
					break;
				case(0x10):
					send_code(0);
					break;
				case(0x20):
					send_code(1);
					break;
				case(0x40):
					send_code(2);
					break;
				case(0x80):
					send_code(3);
					break;
			}
			break;
		case(4):
			//figure out code to send to send_code()
			switch(row)
			{
				case(0x02):
					send_code(4);
					break;
				case(0x04):
					send_code(5);
					break;
				case(0x08):
					send_code(6);
					break;
				case(0x10):
					send_code(7);
					break;
				case(0x20):
					send_code(8);
					break;
				case(0x40):
					send_code(9);
					break;
			}
			break;
	}
}

// Set up the registers required for this project
// Refer C8051F005 data sheet for a detailed discription of each register
void configure_chip(void)
{
	int i = 0;

	//Disable the WatchDog
	WDTCN = 0xDE;
	WDTCN = 0xAD;
	
	//timer 3 - counts to 278 then raises an interrupt
	// at 20MHz, one count is 50ns.  278 x 50 = 13.9us
	TCON = 0x40;
	TMOD = 0x20;
	TMR3RLH = 0xFE;	//65535 - 278
	TMR3RLL = 0xEA;
	TMR3H = TMR3RLH;
	TMR3L = TMR3RLL;
	TMR3CN = 0x06;

	//UART init
	// setup the UART for 2400, 8, N, 1 - no need to go super fast here
	SCON = 0x50;
	CKCON = 0x30;
	T2CON = 0x34;
	RCAP2H = 0xFE;
	RCAP2L = 0xFC;
	TH2 = 0xFE;
	TL2 = 0xFC;
	TI = TRUE;
	RI = FALSE;

	//port IO init
	XBR0 = 0x04;	//UART assigned to Port0
	XBR1 = 0x00;
	XBR2 = 0x40;	//Weak pull ups enabled for Open Drain outputs
	PRT0CF = 0xFC;
	PRT1CF = 0xFF;	
	
	//oscillator init	
	OSCXCN = 0x67;
	for (i = 0; i < 3000; i++);  // Wait 1ms for initialization
	while ((OSCXCN & 0x80) == FALSE);
	OSCICN = 0x08;
	
	//interrupts init
	IE = 0x80;	//enable global interrupts
	EIE2 = 0x01;
	EIP2 = 0x01;
}

void send_code(unsigned char button)
{       
	//This routine is, basically, a parallel to serial converter
	//Each bit in the RC5 data to be sent is AND-ed with a Mask
	//if the result is 1, send a 1 out the RC5 port, otherwise send a 0
	
	//Do this twice, once for button down
	//Two start bits, a "button toggle bit", the 5 bit device address (I've choosen 0x0F), then the 6 bit command
	//Then for the button up - the only difference is that the toggle bit has changed.

    //BUTTON DOWN
	bit_mask = 0x10;
	// Send two start bits
	transmit(TRUE);
	transmit(TRUE);

	// Send a toggle bit - 1
	transmit(TRUE);

	// Send address - 5 bits
	for (k = 0;  k < 5; k++)
	{
		// determine whether to put a 0 or 1 on to the DATA pin
		if ((ADDRESS & bit_mask) > 0) transmit(TRUE);
		else transmit(FALSE);

		// Shift bit_mask right 1 bit 0x10->0x08->0x04->0x02->0x01
		bit_mask = bit_mask >> 1;   
	}  

	//Send button command
	bit_mask = 0x20;
	// 6 data bits, MSB first       
	for (k = 0;  k < 6; k++)
	{
		// determine whether to put a 0 or 1 on to the DATA pin
		if ((button & bit_mask) > 0) transmit(TRUE);
		else transmit(FALSE);

		// Shift bit_mask right 1 bit 0x20->0x10->0x08->0x04->0x02->0x01
		bit_mask = bit_mask >> 1;   
	}  

	//BUTTON UP
	bit_mask = 0x10;                    
    // Send two start bits
	transmit(TRUE);
	transmit(TRUE);

	// Send a toggle bit - 0
	transmit(FALSE);

	// Send address - 5 bits
	for (k = 0;  k < 5; k++)
	{
		// determine whether to put a 0 or 1 on to the DATA pin
		if ((0x0F & bit_mask) > 0) transmit(TRUE);
		else transmit(FALSE);

		// Shift bit_mask right 1 bit 0x10->0x08->0x04->0x02->0x01
		bit_mask = bit_mask >> 1;   
	}  

	//Send button command
	bit_mask = 0x20; 
	// 6 data bits, MSB first       
	for (k = 0;  k < 6; k++)
	{
		// determine whether to put a 0 or 1 on to the DATA pin
		if ((button & bit_mask) > 0) transmit(TRUE);
		else transmit(FALSE);

		// Shift bit_mask right 1 bit 0x20->0x10->0x08->0x04->0x02->0x01
		bit_mask = bit_mask >> 1;   
	}
}

void transmit(unsigned char level)
{
	//In RC5, a logic 1 is represented by the data line going low for 889us, then high for 889us
	// a logic 0 is represented by the data line going high for 889us, then low for 889us

	switch(level)
	{
		case 0:
		//go high for 889us then low for 889us
		RC5_DATA_OUT = TRUE;
		while(count < 63);  //64 x 13.9us = 889
		RC5_DATA_OUT = FALSE;
		while(count < 125);	//(126 - 64) x 13.9us = 889
		break;
		case 1:
		//go low for 889us then high for 889us
		RC5_DATA_OUT = FALSE;
		while(count < 63);
		RC5_DATA_OUT = TRUE;
		while(count < 125);
		break;
	}
	count = 0;

}

/************************************ INTERRUPT SERVICE ROUTINES **********************************************/


void Timer3_Isr (void) interrupt 14 //interrupt every 13.9uS					  
{ 
	if (RC5_DATA_OUT == TRUE)
	{
		RC5_MODULATED_OUT = ~RC5_MODULATED_OUT; 		 
	}
	else RC5_MODULATED_OUT = FALSE;
	count++;
	TMR3CN = 0x06;
}

