/*****************************************************
 This program was produced by the
 CodeWizardAVR V1.25.3 Standard
 Automatic Program Generator
 ? Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
 http://www.hpinfotech.com
 
 Project : 
 Version : 
 Date    : 4-7-2010
 Author  :                            
 Company :
 Comments: 
 
 
 Chip type           : ATmega88
 Program type        : Application
 Clock frequency     : 4,000000 MHz
 Memory model        : Small
 External SRAM size  : 0
 Data Stack size     : 256
 *****************************************************/

#include <mega88.h>
#include <delay.h>
#include <stdio.h>

unsigned long int crystal_freq = 4000000; //crystal frequentie in Hz

unsigned int dc_dice_open_us = 750;   //us
unsigned int dc_dice_closed_us = 1250;  //us

unsigned int OCR1AL_closed;
unsigned int OCR1AL_open;

unsigned int adc_value[4]; 

unsigned int adc_fit_cnt[7], adc_fit_cnt2[7];
unsigned char gyr_index, dice_index;
unsigned char dice_state, dice_state2, dice_state_final, dice_state_cnt;

unsigned int dice0_adc[7][4] = {{128,323,700,893},{337,323,337,893},{128,323,822,822},{128,512,512,893},{510,323,700,510},{200,200,700,893},{128,685,700,685}};

unsigned int k,k2,k3;

bit init_servo, init_beep;


// Timer 1 output compare A interrupt service routine
//interrupt [TIM1_COMPA] void timer1_compa_isr(void)
//{        
//}

// Timer 1 output compare B interrupt service routine
//interrupt [TIM1_COMPB] void timer1_compb_isr(void)
//{
//}

#define ADC_VREF_TYPE 0x00

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
	ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
	// Start the AD conversion
	ADCSRA|=0x40;
	// Wait for the AD conversion to complete
	while ((ADCSRA & 0x10)==0);
	ADCSRA|=0x10;
	return ADCW;
}

//Define dice state
//Function to determine which side of the dice is pointing upwards. This state is returned.
//state=0 means that none of the gyro conductors are connected (ball is 'floating')
//state1:6 is the side that is pointing upwards
//state7 is none of the above. It is the else statement of the function and is the error state
unsigned char ReadDiceSensor()
{
	for(dice_index=0;dice_index<7;dice_index++)
	{
		for(gyr_index=0;gyr_index<4;gyr_index++)
		{
			if(adc_value[gyr_index] > (dice0_adc[dice_index][gyr_index] - 50) &&  adc_value[gyr_index] < (dice0_adc[dice_index][gyr_index] + 50))
			{
				adc_fit_cnt[dice_index]++;
			}
		}
		adc_fit_cnt2[dice_index] = adc_fit_cnt[dice_index];
		adc_fit_cnt[dice_index] = 0;
		
		if(adc_fit_cnt2[dice_index] == 4)
		{
			dice_state = dice_index;
			dice_state_cnt++;
		}
	}
	
	if(dice_state_cnt>1)
	{
		dice_state2 = 7;
	}               
	else
	{
		dice_state2 = dice_state;
	}
	
	dice_state_cnt = 0;
	
	return dice_state2;
}

//function determines if the servo needs to moved to the open or closed situation.
void servo_state(unsigned char state)
{
	//initiate servo shutdown sequence (power saving)
	init_servo = 1;
	k2=0;
	
	if(state==1)
	{
	        //PORTB.4 = 1;      //Red LED
                //PORTD.6 = 0;
                PORTD.7 = 1;
		OCR1AH=0;
		OCR1AL=OCR1AL_open;
	}
	else
	{
		//PORTB.4 = 0;      //Red LED
                //PORTD.6 = 1;
                PORTD.7 = 0;
		OCR1AH=0;
		OCR1AL=OCR1AL_closed;
	}
}

inline void Beep(void) {
        init_beep=1;
        k3=0;
        PORTD.6=1;
}

inline void Open(void) {
	servo_state(1);
}

inline void Close(void) {
	servo_state(0);
}

// just include the state machine using here
#include "codevision-dice.c"

// Timer 0 overflow interrupt service routine
//f = 3,6Khz, 8bit=256 --> t = 71.5ms
//Interupt routine is used for two things:
//- Zero Order Hold (ZOH) for dice state. Is only necesary for troubleshoot purposes to display the right state
//- Power save mode for the servo control. If interupt routine has been ran more then 30 times (approx 2 seconds), 
//the dutycycle of the PWM output for the servo is set to zero
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{                  
	k++;
	
	if(k>5)
	{
		k=0;
		
		//PORTB.3 = !PORTB.3;
		dice_state_final = ReadDiceSensor(); //ZOH filter for LCD display (only necesary for troubleshooting)
	}
	
	if(init_servo == 1)
	{
		k2++;
		if(k2>30)
		{
			//k2=0;
			
			OCR1AH=0;
			OCR1AL=0;
		}
	}
	
	if(init_beep == 1)
	{
	        k3++;
	        if(k3>7)
	        {
	                //k3=0;
	                PORTD.6=0;
	        }
	}
	
	ProcessSensorValues();
}

void main(void)
{
	// Declare your local variables here
	
	// Crystal Oscillator division factor: 1
#pragma optsize-
	CLKPR=0x80;
	CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
		
	// Input/Output Ports initialization
        // Port B initialization
        // Func7=In Func6=In Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=In 
        // State7=T State6=T State5=0 State4=0 State3=0 State2=0 State1=0 State0=P 
        //B0: jumper
        //B1: servo aansturing
        //B2: led groen
        //B3: led geel
        //B4: led rood
        PORTB=0x01;
        DDRB=0x3E;
	
	// Port C initialization
	// FOR ADC STATES NEED TO BE T(OGGLE). WRONG ADC RESULTS IF STATE=P(ULL-UP)
	// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
	// State6=T State5=P State4=T State3=T State2=T State1=T State0=T 
	//C0: ADC0
	//C1: ADC1
	//C2: ADC2
	//C3: ADC3
	PORTC=0x20;
	DDRC=0x00;
	
	// Port D initialization
	// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=In 
	// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=P 
	PORTD=0x01;
	DDRD=0xFE;
	
	// Timer/Counter 0 initialization
	// Clock source: System Clock
	// Clock value: 3,600 kHz
	// Mode: Normal top=FFh
	// OC0A output: Disconnected
	// OC0B output: Disconnected
	TCCR0A=0x00;
	TCCR0B=0x05;     //00000101  
	//CS02:0 --> 101: clkIO/1024 (From prescaler) --> 3.6864MHz/1024 = 3.6KHz
	
	TCNT0=0x00;
	OCR0A=0x00;
	OCR0B=0x00;
	
	delay_ms(5000);   //wait 5 seconds before close servo
	
	// Timer/Counter 1 initialization
	// Clock source: System Clock
	// Clock value: 14,400 kHz
	// Mode: Normal top=FFFFh
	// OC1A output: PWM
	// OC1B output: Discon.
	// Noise Canceler: Off
	// Input Capture on Falling Edge
	// Timer 1 Overflow Interrupt: Off
	// Input Capture Interrupt: Off
	// Compare A Match Interrupt: Off
	// Compare B Match Interrupt: Off
	
	TCCR1A=0x80;       //10000000
	TCCR1B=0x14;       //00010100
	//COM1A1:0 --> 10: Clear OC1A on Compare Match when upcounting. Set OC1A on Compare Match when downcounting.
	//COM1B1:0 --> 00: Normal port operation, OC1B disconnected.  
	
	//ICNC1    --> 0: Input Capture Noise Canceler   
	//ICES1    --> 0: falling (negative) edge is used as trigger a capture event
	
	//******WGM******// 
	//"control the counting sequence of the counter, the source for maximum (TOP) counter value, and what type of waveform
	//generation to be used"
	//==> WGM13:0  --> 1000: PWM, Phase and Frequency Correct  
	//TOP: ICR1
	//Update of OCR1x at BOTTOM
	//TOV1 Flag Set on BOTTOM
	
	//CS12:0   --> 100: clkIO/256 (From prescaler) --> 3.6864MHz/256 = 14.4KHz
	
	//Timer1 counter values
	TCNT1H=0x00;
	TCNT1L=0x00;
	
	//PWM frequency: f_pwm = clkIO/(2*prescaler*ICR1) = 3.6864Mhz/(2*256*144) = 50Hz
	ICR1H=0x00;
	ICR1L = crystal_freq/(256*100);     //256 is prescaler (CS12:0 from TCCR1B register)
	//ICR1L=144;
	
	OCR1AL_closed = (dc_dice_closed_us * (crystal_freq / 256))/1000000;   //256 is prescaler (CS12:0 from TCCR1B register)
	OCR1AL_open   = (dc_dice_open_us * (crystal_freq / 256))/1000000;     //256 is prescaler (CS12:0 from TCCR1B register)
	
	//PWM duty cycle
	OCR1AH=0;
	OCR1AL = OCR1AL_closed;    
	//OCR1AL=dc_dice_closed;   //dice is initialy closed  
	
	OCR1BH=0;
	OCR1BL=0;
	
	
	// Timer/Counter 2 initialization
	// Clock source: System Clock
	// Clock value: Timer 2 Stopped
	// Mode: Normal top=FFh
	// OC2A output: Disconnected
	// OC2B output: Disconnected
	ASSR=0x00;
	TCCR2A=0x00;
	TCCR2B=0x00;
	TCNT2=0x00;
	OCR2A=0x00;
	OCR2B=0x00;
	
	// External Interrupt(s) initialization
	// INT0: Off
	// INT1: Off
	// Interrupt on any change on pins PCINT0-7: Off
	// Interrupt on any change on pins PCINT8-14: Off
	// Interrupt on any change on pins PCINT16-23: Off
	EICRA=0x00;
	EIMSK=0x00;
	PCICR=0x00;
	
	// Timer/Counter 0 Interrupt(s) initialization
	TIMSK0=0x01;
	// Timer/Counter 1 Interrupt(s) initialization
	TIMSK1=0x00;
	// Timer/Counter 2 Interrupt(s) initialization
	TIMSK2=0x00;
	
	// Analog Comparator initialization
	// Analog Comparator: Off
	// Analog Comparator Input Capture by Timer/Counter 1: Off
	ACSR=0x80;
	ADCSRB=0x00;
	
	// ADC initialization
	// ADC Clock frequency: 28,800 kHz
	// ADC Voltage Reference: AREF pin
	// ADC Auto Trigger Source: None
	// Digital input buffers on ADC0: Off, ADC1: Off, ADC2: Off, ADC3: Off
	// ADC4: On, ADC5: On
	DIDR0=0x0F;
	ADMUX=ADC_VREF_TYPE & 0xff;  
	ADCSRA=0x87;
	
	// Global enable interrupts
#asm("sei")
	

	//initial servo_state is closed
	Close();
	
	Initialize();	
	
	while (1)
	{      
		if(PINC.5==0)
        {
			//devboard functionality. If switch (PINC5) is pushed, servo moves to open state 
			servo_state(1);
        } 
		
		//ADC values are read continiously. Maybe this is overkill, timer can be used. Future redesign 
		adc_value[0] = read_adc(0);
		adc_value[1] = read_adc(1);
		adc_value[2] = read_adc(2);
		adc_value[3] = read_adc(3);
		
                //PORTB.2 = (dice_state_final & 4)/4;
                //PORTB.3 = (dice_state_final & 2)/2;
                //PORTB.4 = (dice_state_final & 1);
	};
}

