/* Program PIC RGB
 * 
 * This program generates random colors with a RGB LED
 *
 *   LED pins should be connected to pins GPIO2, GPIO4 and GPIO5 in RGB order
 *   PIC12 should be configured to work with IntOSC + PWRT + BODEN 
 *   MCLR has to be turned off if you want to leave the pin floating and thus
 * save a resistor
 *
 * 2007/08 (c) Joao Figueiredo
 * Based on TinyRGB from www.floery.net - Elektor 2007/08
 *
 * Version 2 uses randomly chosen pre-defined colors.
 * The brightness is also random
 */

#include<pic.h>

#define RED		GPIO2
#define GREEN	GPIO4
#define BLUE	GPIO5

#define ON	1
#define OFF 0
	
#define PWM_FREQ	100
#define PWM_STEPS	256

#define FOSC		(4000000UL)
#define TMR_FREQ	(PWM_FREQ*PWM_STEPS)	// 12800
#define TMR_COUNT	(FOSC/4/TMR_FREQ)	// value to count 78.125

#define TMR1V	(65535-(TMR_COUNT)+1)	// value to load to timer

// ----- Fuses -----
__CONFIG(/*MCLRDIS &*/ PWRTEN & WDTDIS & INTIO);

// ----- Globals -----
unsigned char gRed = 0;
unsigned char gGreen = 0;
unsigned char gBlue = 0;
volatile unsigned char gT1Tick = 0;
volatile unsigned char gDelay = 0;

unsigned char r, g, b;


/* **********************************************
 * Interrupt service routine
 * */
void interrupt isr(void)
{
	if(TMR1IF)
	{
		TMR1H = (TMR1V>>8)&0xFF;		
		TMR1L = (TMR1V&0xFF);	// reload timer

		gT1Tick++;

		if(gT1Tick==0)	// if 256 ticks have elapsed, reset and turn ON
		{
			if(gRed) RED = ON;
			if(gGreen) GREEN = ON;
			if(gBlue) BLUE = ON;
		}
		else			// turn OFF as values remain less than current tick
		{
			if(gT1Tick>gRed)
				RED = OFF;

			if(gT1Tick>gGreen)
				GREEN = OFF;

			if(gT1Tick>gBlue)
				BLUE = OFF;
		}

		// this is the counter of the delay function
		if(gDelay>0) gDelay--;

		TMR1IF = 0;
	}
}

/* **********************************************
 * PIC initialization
 * */
void initPic()
{
	GIE = 0;
	PEIE = 0;

	// LED OFF
	RED=0;
	GREEN=0;
	BLUE=0;

	// IO
	TRISIO = 0x0B;		// GP2, GP4 and GP5 as outputs 0b 0000 1011
	ANSEL = 0x00;		// 12f675 all pins digital
	
	OPTION = 0xD0;		// 0b 1101 0000  T0CS=0, PSA=0, 1:1

	// Timer 1
	TMR1H = (TMR1V>>8)&0xFF;		
	TMR1L = (TMR1V&0xFF);		// (Fosc/4)/(78) = 12800Hz
	TMR1IF = 0;
	TMR1IE = 1;
	TMR1ON = 1;

	PEIE = 1;
	GIE  = 1;
}

/* **********************************************
 * Random char generator
 * */
char getRandomChar(void)
{
	static unsigned short shiftregister = 0x9500;

	unsigned short value;

	/* feedback based on 1+x10+x11 polynomial function */
	value = (shiftregister&0x0200)>>9 ^ (shiftregister&0x0400)>>10;

	/* feedback */
	shiftregister <<= 1;
	shiftregister |= value;

	return (char)shiftregister;
}

/* **********************************************
 * Generates a delay of aproximately 20 ms
 * */
void delay20ms()
{
	unsigned int i;

	for(i=0; i<PWM_FREQ/50; i++)
	{
		gDelay = PWM_STEPS;
		while(gDelay>0);	// wait
	}
}

/* **********************************************
 * Generates a delay of aproximately sec seconds
 * */
void delay(char sec)
{
	unsigned int i;

	for(i=0; i<50*sec; i++)
	{
		delay20ms();
	}
}

/* **********************************************
 * Updates the color to its new value
 * */
void update(unsigned char final, unsigned char *cur)
{
	if( final-*cur > 0 )
	{
		(*cur)++;
	}
	else if( final-*cur < 0 )
	{
		(*cur)--;
	}
}

/* **********************************************
 * Fades from current (gRed, gGreen, gBlue) color
 * to (r, g, b) color 
 * */
void fade()
{
	// Apply fade
	do
	{
		update(r, &gRed);
		update(g, &gGreen);
		update(b, &gBlue);

		delay20ms();
	}
	while(r!=gRed || g!=gGreen || b!=gBlue);
}

/* **********************************************
 * Lights up Red, then Green and finally Blue
 * before exiting turns them off
 * */
void testLed()
{
	/*
	gRed=255;gGreen=255;gBlue=255;
	delay(5);
	gRed=0;gGreen=0;gBlue=0;
	delay(5);
	*/
	
	r=255;g=0;b=0;
	fade();
	r=0;g=0;b=0;
	fade();
	delay(1);

	r=0;g=255;b=0;
	fade();
	r=0;g=0;b=0;
	fade();
	delay(1);

	r=0;g=0;b=255;
	fade();
	r=0;g=0;b=0;
	fade();
	delay(1);

	r=0;g=0;b=0;
	fade();
	delay(2);
}

void fillRGB(unsigned char color, unsigned char power, unsigned char *red, unsigned char *green, unsigned char *blue)
{
	switch(color)
	{
		case 1:
			*red=(unsigned char) (255*(signed int)power/100);
			*green=0;
			*blue=0;
		break;
			
		case 2:
			*red=0;
			*green=(unsigned char) (255*(signed int)power/100);
			*blue=0;
		break;
			
		case 3:
			*red=0;
			*green=0;
			*blue=(unsigned char) (255*(signed int)power/100);
		break;

		case 4:
			*red=(unsigned char) (255*(signed int)power/100);
			*green=(unsigned char) (255*(signed int)power/100);
			*blue=0;
		break;

		case 5:
			*red=(unsigned char) (255*(signed int)power/100);
			*green=0;
			*blue=(unsigned char) (255*(signed int)power/100);
		break;

		case 6:
			*red=0;
			*green=(unsigned char) (255*(signed int)power/100);
			*blue=(unsigned char) (255*(signed int)power/100);
		break;

		case 7:
			*red=(unsigned char) (255*(signed int)power/100);
			*green=(unsigned char) (255*(signed int)power/100);
			*blue=(unsigned char) (255*(signed int)power/100);
		break;
	}
}

/* **********************************************
 * Main of PicRGB
 * */
void main(void)
{
	unsigned char waitTime;
	unsigned char color;
	unsigned char power;
	
	initPic();

	testLed();

	while(1)
	{
		// Get color index between 1 and 7
		while( (color = getRandomChar() & 0x07) == 0 );

		// Get intensity
		power = getRandomChar();
		if(power>100) power = 100;


		// fill r g b with final values
		fillRGB(color, power, &r, &g, &b);

		// get pause time
		waitTime = getRandomChar();			// up to 4 minutes

		fade();

		delay(waitTime);
	}
}

