/*
	Copyright (C) 2012 by Oscar Rodriguez Parra <oscar@sistemasorp.es>
	http://www.sistemasorp.es
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <pic12f683.h>
#include <stdint.h>

#define BOUNDARY 869 // Equal or greater than that value will activate the servos and leds
#define COUNTER 176 // every 0.1 ms

static __code uint16_t __at(0x2007) configword1 = _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOD_OFF & _IESO_OFF &  _FCMEN_OFF ;

volatile int8_t state = 0;
volatile uint16_t ticks = 0;

// Reads the ADC values
static void readADC(void)
{
	uint16_t value = ((ADRESH & 3) << 8) + ADRESL; // Get the ADC value;
		
	if(value > BOUNDARY) // if value is higher than the calibrated value, then the LDR is illuminated
	{
		switch(CHS0) // What ADC Channel is active?
		{
			case 0:
				state |= 1; // Update the state xxxxxxx1
				GP2 = 1; // Turn on the left led and turn off the right led
				break;
			case 1:
				state |= 2; // Update the state xxxxxx1x
				GP2 = 0; // Turn on the right led and turn off the left led
				break;
		}
	}
	else // the value is lower than the calibrated value, so the LDR is not illuminated
	{
		switch(CHS0)
		{
			case 0:
				state &= 0xFE; // Update the state xxxxxxx0
				break;
			case 1:
				state &= 0xFD; // Update the state xxxxxx0x
				break;
		}

	}

	if(state == 0) // If there isn't activated any LDR
	{
		TRISIO2 = 1; // GP2 is configured as input (leds disabled)
	}
	else
	{
		TRISIO2 = 0; // GP2 is configured as output (leds enabled)
	}
	
	switch(CHS0)
	{
		case 0:
			CHS0 = 1; // Change ADC channel to AN1
			break;
		case 1:
			CHS0 = 0; // Change ADC channel to AN0
			break;
	}

	GO = 1; // Start reading ADC port
	
}

// Interruption code
void Intr(void) __interrupt 0
{
	if(T0IF == 1) // If the interruption comes from timer0
	{
		TMR0 = COUNTER; // The timer0 starts to count from the same beginning
		ticks++; // We increment
		T0IF = 0; // Clear timer0 flag
	}

	if(ADIF == 1) // If the interruption comes from ADC
	{
		readADC();
		ADIF = 0; // Clear ADC flag
	}
}

// The function returns 1 if the total amount of time has been elapsed, 0 otherwise
static char delay(uint16_t before, uint16_t amount)
{
	uint16_t result = ticks;
	if(before > result) // If the variable has been overflowed
	{
		result += 65535 - before;
	}
	else
	{
		result -= before;
	}

	if(result >= amount) // If the total time has been exceeded
	{
		return 1;
	}
	
	return 0;
}

void main(void)
{
	uint8_t index;
	uint16_t before1 = 0;
	uint16_t before2 = 0;
	int8_t servo1 = 0;
	int8_t servo2 = 0;

	OSCCON = OSCCON | 0x70;  //Select 8MHz Internal oscillator


	CMCON0 = 7; // Disable comparators

	PIE1 = 0x40; // ADC Interrupt enabled
	PIR1 = 0; // All peripherical flags cleared
	INTCON = 0xE0; // Enable Global, peripherical and Timer0 interrupts; interrupt flags cleared

    	TRISIO = 0x0B; // GP0, GP1 and GP3 as inputs; GP2, GP4 and GP5 as outputs
    	GPIO = 0x00; // All outputs are low.

	OPTION_REG = 0x80; // pull-ups disabled, internal instruction cycle clock, Timer0 prescaler, prescaler 1:2
	TMR0 = COUNTER; // timer0 begins to count from a specified number
	

	for(index = 0; index < 10; index++) // Blink the leds each 0,1s during ten times
	{
		GP2 = 1; // Turn on the left led and turn off the right led
		before1 = ticks; // Start again the count
		while(!delay(before1, 1000)); // Wait 100 ms.
		GP2 = 0; // Turn on the right led and turn off the left led
		before1 = ticks;
		while(!delay(before1, 1000));
	}

	ANSEL = 0x23; // FOSC / 32 and GPIO0 & GPIO1 as analog
	ADCON0 = 0x81; // Right justified, VDD voltage reference and enable ADC
	GO = 1; // Start ADC reading (can not enable ADC channel and start conversion at the same time as written in point 9.2.6 of datasheet)
	
	for(;;) // Infinite loop and main routine running the most part of time
	{
		if(state & 1) // If left LDR is activated
		{
			switch(servo1) // Servo1 is high or low?
			{
				case 0:
					GP5 = 1; // Enable right servo
					if(delay(before1, 10)) // Check if 1 ms. has been elapsed
					{
						servo1 = 1; // Change to next servo state
						before1 = ticks; // Start again the count
					}
					break;
				case 1:			
					GP5 = 0; // Disable right servo
					if(delay(before1, 190)) // Check if 19 ms. has been elapsed
					{
						servo1 = 0; // Change to next servo state
						before1 = ticks;
					}
					break;
			}
		}
		else
		{
			GP5 = 0; // Disable right servo
			servo1 = 0; // Change to servo state 0
			before1 = ticks;
		}

		if(state & 2) // If right LDR is activated
		{
			switch(servo2) // Servo2 is high or low?
			{
				case 0:
					GP4 = 1; // Enable left servo
					if(delay(before2, 20)) // Check if 2 ms. has been elapsed
					{
						servo2 = 1; // Change to next servo state
						before2 = ticks; // Start again the count
					}
					break;
				case 1:			
					GP4 = 0; // Disable left servo
					if(delay(before2, 180)) // Check if 18 ms. has been elapsed
					{
						servo2 = 0; // Change to next servo state
						before2 = ticks;
					}
					break;
			}
		}
		else
		{
			GP4 = 0; // Disable left servo
			servo2 = 0; // Change to servo state 0
			before2 = ticks;
		}

	}
}


