/*********************************************
*	Author: Chris @ PyroElectro.com
*
*	Description: This firmware is made for the
*   PIC 18F452 microcontroller and goes with the
*	animatronic eyes tutorial on PyroElectro.com
*	The following firmware controls 6 servo motors
*   independently and has a special move command
*   so that the motors move slowly, with a varying
*   speed option.
*
*	Date: August 9th, 2011
*
*	For Full Details On This Firmware Please
*	Visit: http://www.pyroelectro.com/tutorials/animatronic_eyes/
*********************************************/

#include <p18f452.h>
#include <timers.h>
#include <delays.h>

/******Static Eyebrow Max/Min Locations********/
//Right Eyebrow
#define right_level 0xF100
#define right_down 0xF400
#define right_up 0xEB00
//Left Eyebrow
#define left_level 0xF000
#define left_down 0xED00
#define left_up 0xF600

/******Static Eye Max/Min Locations********/
//Right Eye
#define right_eye_straight 0xF259
#define right_eye_left 0xF600
#define right_eye_right 0xEC09

#define right_eye_level 0xEFA9
#define right_eye_up 0xF5C9
#define right_eye_down 0xEC59

//Left Eye
#define left_eye_straight 0xF259
#define left_eye_left 0xF600
#define left_eye_right 0xEC09

#define left_eye_level 0xEE59
#define left_eye_up 0xF2C9
#define left_eye_down 0xEC59


					//Declare all functions 
void move(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,unsigned int);
void slow_move(unsigned int);
void delay(unsigned int);
void InterruptHandlerHigh (void);

unsigned int count = 0;		 //Counts every Timer1 Interrupt

//The servos are all set to go to an initial 
			//state when the system is powered on
int servo0 = right_level; // Right Eyebrow
int servo1 = left_level;  // Left Eyebrow
int servo2 = left_eye_straight; // Left Eye Left/Right
int servo3 = left_eye_level;  // Left Eye Up/Down
int servo4 = right_eye_straight; // Right Eye Left/Right
int servo5 = right_eye_level;  // Right Eye Up/Down
		
unsigned int servo_0_dest = right_level;
unsigned int servo_1_dest = left_level;
unsigned int servo_2_dest = left_eye_straight;
unsigned int servo_3_dest = left_eye_level;
unsigned int servo_4_dest = right_eye_straight;
unsigned int servo_5_dest = right_eye_level;

unsigned int servo_0_current = right_level;
unsigned int servo_1_current = left_level;
unsigned int servo_2_current = left_eye_straight;
unsigned int servo_3_current = left_eye_level;
unsigned int servo_4_current = right_eye_straight;
unsigned int servo_5_current = right_eye_level;

unsigned int speed = 0xF9DF;

/******** Variable Movement Speed*********
//This speed can be changed at any time to
change how fast the servos are updated, thus
changing the servo's movement speed.
******************************************
[1] speed = 0xB1DF; // Slowest
[2] speed = 0xD1DF; // Slow
[3] speed = 0xE1DF; // Medium
[4] speed = 0xF1DF; // Fast
[5] speed = 0xF9DF; // Fastest
******************************************/

void main(void)
{
				//Set I/O ports
TRISD = 0x00;
				//Clear Output Ports
PORTD = 0x00;

				//Setup Interrupts
RCON = 0b000000000; 
INTCON = 0b10100000;

				//Setup Timers 1 & 2
OpenTimer0( TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_2 );
OpenTimer1( TIMER_INT_ON & T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_2 & T1_OSC1EN_OFF & T1_SYNC_EXT_OFF );
OpenTimer3( TIMER_INT_ON & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_1 & T3_SYNC_EXT_OFF );
				//Initialize Timers 1 & 2
WriteTimer0( 0x3CAF );
WriteTimer1( 0xF63B );
WriteTimer3( speed ); // 20,000 Instructions Until Interrupt

	while(1){
//Eyes Level/Straight || Eyebrows Straight
	move(right_level,		//Right Eyebrow
		 left_level,		//Left Eyebrow
		 left_eye_straight, //Left Eye Left/Right
		 left_eye_level,	//Left Eye Up/Down
		 right_eye_straight,//Right Eye Left/Right
		 right_eye_level,	//Right Eye Up/Down
		 0xFADF);			//Movement Speed
	delay(40);

//Eyes Left || Left Eyebrow Up
	move(right_level,		//Right Eyebrow
		 left_up,		//Left Eyebrow
		 left_eye_left, //Left Eye Left/Right
		 left_eye_level,	//Left Eye Up/Down
		 right_eye_left,//Right Eye Left/Right
		 right_eye_level,	//Right Eye Up/Down
		 0xFADF);			//Movement Speed
	delay(10);

//Eyes Right || Right Eyebrow Up
	move(right_up,		//Right Eyebrow
		 left_level,		//Left Eyebrow
		 left_eye_right, //Left Eye Left/Right
		 left_eye_level,	//Left Eye Up/Down
		 right_eye_right,//Right Eye Left/Right
		 right_eye_level,	//Right Eye Up/Down
		 0xFADF);			//Movement Speed
	delay(10);

//Eyes Straight || Eyebrows Level
	move(right_level,		//Right Eyebrow
		 left_level,		//Left Eyebrow
		 left_eye_straight, //Left Eye Left/Right
		 left_eye_level,	//Left Eye Up/Down
		 right_eye_straight,//Right Eye Left/Right
		 right_eye_level,	//Right Eye Up/Down
		 0xFADF);			//Movement Speed
	delay(10);

//Eyes Middle || Eyebrows Down
	move(right_down,		//Right Eyebrow
		 left_down,		//Left Eyebrow
		 left_eye_right, //Left Eye Left/Right
		 left_eye_level,	//Left Eye Up/Down
		 right_eye_left,//Right Eye Left/Right
		 right_eye_level,	//Right Eye Up/Down
		 0xFADF);			//Movement Speed
	delay(12);

//Eyebrows Up Quickly
	move(right_up,		//Right Eyebrow
		 left_up,		//Left Eyebrow
		 0, //Left Eye Left/Right
		 0,	//Left Eye Up/Down
		 0,//Right Eye Left/Right
		 0,	//Right Eye Up/Down
		 0xFD00);			//Movement Speed
	delay(7);
//Eyes Straight || Eyebrows Level
	move(right_level,		//Right Eyebrow
		 left_level,		//Left Eyebrow
		 left_eye_straight, //Left Eye Left/Right
		 left_eye_level,	//Left Eye Up/Down
		 right_eye_straight,//Right Eye Left/Right
		 right_eye_level,	//Right Eye Up/Down
		 0xFADF);			//Movement Speed
	delay(50);
	}

}

	//The move function sets all servo values
void move(unsigned int one, unsigned int two, unsigned int three, unsigned int four,
		  unsigned int five, unsigned int six, unsigned int variable_speed)
{

	speed = variable_speed;

	if(one){
		//servo0 = one;// Right Eyebrow
		servo_0_dest = one;
	}
	if(two){
		//servo1 = two;// Left Eyebrow
		servo_1_dest = two;
	}
	if(three){
		//servo2 = three;// Right Eyebrow
		servo_2_dest = three;
	}
	if(four){
		//servo3 = four;// Left Eyebrow
		servo_3_dest = four;
	}
	if(five){
		//servo2 = three;// Right Eyebrow
		servo_4_dest = five;
	}
	if(six){
		//servo3 = four;// Left Eyebrow
		servo_5_dest = six;
	}
}

//INTERRUPT CONTROL
#pragma code InterruptVectorHigh = 0x08		//interrupt pointer address (0x18 low priority)
void InterruptVectorHigh (void)
{
        _asm						//assembly code starts
        goto InterruptHandlerHigh	//interrupt control
        _endasm						//assembly code ends
}
#pragma code
#pragma interrupt InterruptHandlerHigh		//end interrupt control

void InterruptHandlerHigh()		// Declaration of InterruptHandler
{									//This gets run when either timer overflows from FFFF->0000
        if(INTCONbits.TMR0IF)		//check if TMR0 interrupt flag is set
        {
				WriteTimer0( 0x3CAF );  //Reset Timer0 for 20mS Delay
				WriteTimer1( 0xFC00 );  //Reset Timer1 for 1mS Delay
				count = 0;				//Reset Timer1 Interrupt Counter
			    INTCONbits.TMR0IF = 0;	//Clear TMR0 Flag               
        }
        if(PIR1bits.TMR1IF == 1)        
        {								//If Timer1 interrupts, do your thing...
		count++;						//Increment Timer1 Interrupt Counter
				switch(count){			//Choose which servo to modify
			case 1:     PORTD = 0x01; 
					    WriteTimer1( servo0 );	//Right Eyebrow
						break;
			case 2:		PORTD = 0x02;
						WriteTimer1( servo1 );	//Left Eyebrow
						break;
			case 3:		PORTD = 0x04;
						WriteTimer1( servo2 );	//Left Eye Left/Right
						break;
			case 4:		PORTD = 0x08;
						WriteTimer1( servo3 );	//Left Eye Up/Down
						break;
			case 5:		PORTD = 0x10;
						WriteTimer1( servo4 );	//Right Eye Left/Right
						break;
			case 6:		PORTD = 0x20;
						WriteTimer1( servo5 );	//Right Eye Up/Down
						break;
			case 7:		PORTD = 0x00;
						WriteTimer1( 0 );
						break;
				}

                PIR1bits.TMR1IF = 0;		//Clear Timer1 flag
        }
        if(PIR2bits.TMR3IF == 1)        
        {								//If Timer3 interrupts, do your thing...
			if(servo_0_dest < servo_0_current){
				servo_0_current--;
				servo0 = servo_0_current;
			}
			else if(servo_0_dest > servo_0_current){
				servo_0_current++;
				servo0 = servo_0_current;
			}
	
			if(servo_1_dest < servo_1_current){
				servo_1_current--;
				servo1 = servo_1_current;
			}
			else if(servo_1_dest > servo_1_current){
				servo_1_current++;
				servo1 = servo_1_current;
			}

			if(servo_2_dest < servo_2_current){
				servo_2_current--;
				servo2 = servo_2_current;
			}
			else if(servo_2_dest > servo_2_current){
				servo_2_current++;
				servo2 = servo_2_current;
			}

			if(servo_3_dest < servo_3_current){
				servo_3_current--;
				servo3 = servo_3_current;
			}
			else if(servo_3_dest > servo_3_current){
				servo_3_current++;
				servo3 = servo_3_current;
			}


			if(servo_4_dest < servo_4_current){
				servo_4_current--;
				servo4 = servo_4_current;
			}
			else if(servo_4_dest > servo_4_current){
				servo_4_current++;
				servo4 = servo_4_current;
			}


			if(servo_5_dest < servo_5_current){
				servo_5_current--;
				servo5 = servo_5_current;
			}
			else if(servo_5_dest > servo_5_current){
				servo_5_current++;
				servo5 = servo_5_current;
			}

				WriteTimer3( speed ); // Reset Instruction Speed Until Interrupt
                PIR2bits.TMR3IF = 0;		//Clear Timer3 flag
        }
    INTCONbits.GIE = 1;						//Re-enable all interrupts
}

//Delay delay_val*100mS
void delay(unsigned int delay_val){
unsigned int i=0;

for(i=0;i<delay_val;i++){
Delay10KTCYx(50);
}

}