#define F_CPU 20000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>

unsigned long int crystal_freq = 20000000; //crystal frequentie in Hz
unsigned int OCR1AL_closed;
unsigned int OCR1AL_open;
unsigned int adc_value[3];
unsigned int dice0_adc[7][3] = {{338,338,338},     //X0 Y0 Z0
								{338,338,174},     //X1 Y1 Z1
								{174,338,338},     //X2 Y2 Z2
								{338,174,338},     //X3 Y3 Z3
								{338,502,338},     //X4 Y4 Z4
								{502,338,338},     //X5 Y5 Z5
								{338,338,502}};    //X6 Y6 Z6

unsigned int adc_fit_cnt[7], adc_fit_cnt2[7];
unsigned char gyr_index, dice_index;
unsigned char dice_state, dice_state2, dice_state_cnt, dice_state_final;
unsigned int k, k1, k2, k3, k4, k5;
unsigned char enable_acc_sleep;
unsigned char init_servo, init_beep;
unsigned char portc_tmp;

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input;
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;//p265:"ADIF is cleared by writing a logical one to the flag"
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<3;gyr_index++)
		{
			if(adc_value[gyr_index] > (dice0_adc[dice_index][gyr_index] - 30) &&  adc_value[gyr_index] < (dice0_adc[dice_index][gyr_index] + 30))
			{
				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] == 3)
		{
			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;
}

void OnboardLed(unsigned char state)
{
if(state==1)
  {
  PORTD |= (1 << 1);    //onboard LED (red) turned ON
  }
else
  {
  PORTD &= ~(1 << 1);   //onboard LED (red) turned OFF
  }
}

//function determines if the servo needs to moved to the open or closed situation.
void servo_state(unsigned char state)
{
	//Start up PWM signal
	TCCR1A=0x80;       //10000000
	TCCR1B=0x14;       //00010100

	//initiate servo shutdown sequence (power saving)
	init_servo = 1;
	k2=0;
	
	//Open servo
	if(state==1)
	  {
	  OCR1AH=0;
	  OCR1AL=OCR1AL_open;
 	  }
	//Close servo
	else
	  {
	  OCR1AH=0;
	  OCR1AL=OCR1AL_closed;
	  }
}

inline void Beep(void) {
        //initiate buzzer shutdown sequence
		init_beep=1;
        k3=0;
		//Buzzer on (via Hbridge)
		PORTD |= (1 << 6);    
		OnboardLed(1);

		//reset auto powerdown sequence (new dice state found)
		k5=0;
}

inline void Open(void) {
	servo_state(1);
}
inline void Close(void) {
	servo_state(0);
}

//state machine ('beautyfulli designed machine by Dr.F')
#include "codevision-dice.c"

// Timer 0 overflow interrupt service routine
//f = 19.5Khz, 8bit=256 --> t = 13.1ms
ISR(TIMER0_OVF_vect)
{                  

k1++;
if(k1>10)    //131ms
  {
  k1=0;

  //Disbale power reduction of ADC
  PRR &= ~(1 << 0);

  //Get dice state
  adc_value[0] = read_adc(0);
  adc_value[1] = read_adc(1);
  adc_value[2] = read_adc(2);
  
  //Enable power reduction of ADC
  PRR |= (1 << 0);  

  //determine dice state (0,1,2,3,4,5,6,7)
  dice_state_final = ReadDiceSensor();

  //write dice state to status leds
  portc_tmp = (PORTC & 7);   //store last three bits
  //write adc_value to bit c3, 4 and 5. Keep c0, 1 and 2 intact
  PORTC = 8 * dice_state_final + portc_tmp;
  }

//Servo signal shut down sequence
if(init_servo==1)
	{
	k2++;
	if(k2>152)     //2 seconds
	  {	  
	  //Stop timer1
	  TCCR1A=0x00;       //10000000
	  TCCR1B=0x00;       //00010100
	  PORTD &= ~(1 << 5);
	  //OCR1AH=0;
	  //OCR1AL=0;
	  }
	}

//Buzzer shut down sequence
if(init_beep == 1)
	{
	k3++;
	if(k3>19)       //0.25 seconds
	  {
	  //PORTD &= ~(1 << 0);    //led (buzzer) uit
	  PORTD &= ~(1 << 6);     //buzzer off (via Hbridge)
	  OnboardLed(0);
	  }
	}

//Process dice state value (with state machine)
k4++;
if(k4>5)
  {
  k4=0;
  ProcessSensorValues();
  }

//Auto Powerdown sequence
k5++;
if(k5>4580)     //1 minute
  {
  PORTB &= ~(1 << 2);  //Auto powerdown
  }
}

// External Interrupt 0 service routine
 //16bit timer1 is needed for 50Hz PWM. PORTB.1 and PORTB.2 (PWM uitgangen van timer1)
 //are not connected to Baby Orangutan Hbridge. For that PORTB.1 is wired to input
 //PIND.2 and via software fed through to PORTD.5 which is connected to the Hbridge.

ISR(INT0_vect)
{
//If interupt is caused by rising edge, switch to falling edge mode
if(EICRA==0x03)
  {
  EICRA=0x02;
  PORTD |= (1 << 5);    //switch D5 (BIN1 of Hbridge) to HIGH
  }
//If interupt is caused by falling edge, switch to rising edge mode
else if (EICRA==0x02)
  {
  EICRA=0x03;
  PORTD &= ~(1 << 5);    //switch D5 (BIN1 of Hbridge) to LOW
  }
}

int main()
  {

//Power Reduction
//disabled:
//TWI
//Timer/Counter2
//SPI
//USART
PRR = 0xC6;

//Sleep mode
//SMCR = 0x07;  //Power-save

// Port B initialization
//B0:I(p) B1:O(0) B2:O(1) B3:I(t) B4:I(p) B5:I(t) B6:I(t) B7:I(t) 
//B0: Push button
//B1: shortcircuit to D2 (input)
//B2: auto powerdown
PORTB =0x15;
DDRB  =0x06;
// Port C initialization
//C0:I(t) C1:I(t) C2:I(t) C3:O(0) C4:O(0) C5:O(0) C6:I(t) 
//C0: ADC0 (X axis)
//C1: ADC1 (Y axis)
//C2: ADC2 (Z axis)
//C3: led1
//C4: led2
//C5: led3
PORTC=0x00;
DDRC =0x38;
// Port D initialization 
//D0:O(0) D1:O(0) D2:I(p) D3:I(t) D4:I(t) D5:O(0) D6:O(0) D7:O(0) 
//D1: led (onboard)
//D2: shortcircuit to B1 (output)
//D5: Hbridge output for servo (single transistor)
//D6: Hbridge output for buzzer (single transistor)
//D7: sleep mode accelerometer
PORTD=0x04;
DDRD =0xE3;

// Timer/Counter 0 initialization
TCCR0A=0x00;
TCCR0B=0x05;     //00000101  
//CS02:0 --> 101: clkIO/1024 (From prescaler) --> 20MHz/1024 = 19.5KHz

TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;

_delay_ms(5000);   //wait 5 seconds before close servo

// Timer/Counter 1 initialization
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) --> 20MHz/256 = 78.1KHz
	
//Timer1 counter values
TCNT1H=0x00;
TCNT1L=0x00;
	
//PWM frequency: f_pwm = clkIO/(2*prescaler*ICR1) = 20Mhz/(2*256*ICR1) = 50Hz
ICR1H = 3;
ICR1L = 180;

OCR1AL_open = 75;
OCR1AL_closed = 31;

//PWM duty cycle
OCR1AH=0;
OCR1AL = OCR1AL_closed;    
	
OCR1BH=0;
OCR1BL=0;

// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Rising Edge
// INT1: Off
EICRA=0x03;       //0x02: Falling edge of INT0, 0x03: Rising edge of INT0
EIMSK=0x01;	      //INT0 enabled, INT1 disabled
EIFR=0x01;        //External Interrupt Flag 0

// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x01;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;

// ADC initialization
// ADC prescaler: 128
// ADC Voltage Reference: AREF pin
// ADC Auto Trigger Source: None
DIDR0=0x07;  //Digital input buffers disabled on ADC0, 1 and 2
ADMUX=0x00; 
ADCSRA=0x87;

sei();

Close();

Initialize();

  while (1)
     {   
	 
	 if((PINB & 1) == 0)
	   {
	   Open();
	   }
	 
     };
  }

