/*****************************************************************************/
/*
                    Delay Element for High Speed Photography
	

                         Copyright Lukasz Panek, 2011

                         For non-commercial use only!

                              www.doc-diy.net


	min delay 12 us (led green, fine pot + coarse pot = 0)


  Version: 3
	Controller: ATtiny24/44/84

  avrdude -p t24 -c STK200 -u -U flash:w:delay.hex

*/
/*****************************************************************************/


#define F_CPU 2000000UL

#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>

#define FALSE 			0
#define TRUE 			1

/*****************************************************************************/
/*
	pinout
*/
/*****************************************************************************/

/*	1
	VCC		GND
	PB0		PA0	ADC0
	PB1		PA1	ADC1
	PB3		PA2	ADC2
	PB2		PA3
	PA7		PA4	SCK
MOS	PA6		PA5	MISO
*/

#define LEDPORT			PORTA
#define LEDDDR			DDRA
#define LED1			PA5
#define LED2			PA4
#define LED3			PA3

#define INPUTPORT		PORTB
#define INPUTPIN		PINB
#define INPUTSHUTTER 	PB2	// INT0

#define OUTPUTPORT		PORTB
#define OUTPUTDDR		DDRB
#define OUTPUT 			PB1

#define POTPOWERPORT	PORTA
#define POTPOWERDDR		DDRA
#define POTPOWER		PA2

#define POTICOARSE		PA1
#define POTIFINE		PA0

#define BUTTONPORT		PORTB
#define BUTTONPIN		PINB
#define BUTTON 			PB0

// todo: pin for 1s 2s switch

/*****************************************************************************/
/*
	prototypes, typedefs
*/
/*****************************************************************************/
void init(void);
void color(uint8_t);
void reset(void);
uint8_t button(void);

/*****************************************************************************/
/*
	constants etc
*/
/*****************************************************************************/


/*****************************************************************************/
/* 
	globals, EEPROM
*/
/*****************************************************************************/
volatile uint8_t adcvalue_coarse_g;	// value of coarse delay (left wheel)
volatile uint8_t adcvalue_fine_g;	// value of fine delay (right wheel)
volatile uint8_t state_g = 0;		// state of uC (red:long, yellow:medium, green:fast)

uint8_t EEMEM laststate_ee = 255;	// initial value of last state, inversed to	
									// avoid eeprom progging (erased = 255)

/*****************************************************************************/
/* 
	main
*/
/*****************************************************************************/
int main(void)
{
	uint8_t i;
	uint8_t j;
	uint8_t state_tmp;

	// init uC
	init();

	// enable interrupts (necessary for wakeup)
	sei();

	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	sleep_mode();

	clock_prescale_set(clock_div_4);

	GIMSK &=~(1<<PCIE1); 				// disable PC interrupt, beacause button 
										// is polled now by TIEMR_OVF
	INPUTPORT |= (1<<INPUTSHUTTER);		// pull-up input

	// verify button was pressed intentionally by checking again pin level after a while
	_delay_ms(100);
	if(button()==0)
	{
		reset();	// reset if button pressed less than 100ms
	}

	// read last state from eeprom
	state_g = 255 - eeprom_read_byte(&laststate_ee); // make 255 default to avoid progging eeprom
	color(state_g);

	// cycle through the states when button pressed
	i = 0;
	j = 0;

	state_tmp = state_g;
	while(button())
	{
		_delay_ms(100);
		j++;

		if (i<6)	// first phase: range
		{

			if(j>10)
			{	
				j = 0;
				i++;
				state_tmp++;
				if(state_tmp > 2) state_tmp = 0;
				color(state_tmp);
			}

		}
		else if (i>=6)	// second phase: config
		{
			if(j>3)
			{	
				j = 0;
				i++;
				state_tmp++;
				if(state_tmp > 2) state_tmp = 0;
				color(state_tmp);
			}
		}

		state_g = state_tmp;
	}

	ADCSRA |= (1<<ADEN)|(1<<ADSC);		// enable ADC and start conversion (must be done after 
										// wake-up to reduce stand-by power)
	POTPOWERPORT |= (1<<POTPOWER);		// trimmer voltage on (off in standby)

	TIMSK0 |= (1<<TOIE0);


	for(;;)
	{
		if(!(INPUTPIN & (1<<INPUTSHUTTER)) )	// test input pin
		{
      LEDPORT &= ~((1<<LED1)|(1<<LED2)|(1<<LED3));  // LEDs off

			// delay loop (blocking)
			if (state_g == 2)	// 10/1 ms range
			{
				for (i=0;i<adcvalue_coarse_g;i++)
				{
					_delay_ms(10.0/255.0);
				}
				for (i=0;i<adcvalue_fine_g;i++)
				{
					_delay_ms(1.0/255.0);
				}
			}
			else if (state_g == 1)	// 100/10 ms range
			{
				for (i=0;i<adcvalue_coarse_g;i++)
				{
					_delay_ms(100.0/255.0);
				}
				for (i=0;i<adcvalue_fine_g;i++)
				{
					_delay_ms(10.0/255.0);
				}
			}

			else	// 1s and 100ms range
			{
				for (i=0;i<adcvalue_coarse_g;i++)
				{
					_delay_ms(1000.0/255.0);
				}
				for (i=0;i<adcvalue_fine_g;i++)
				{
					_delay_ms(100.0/255.0);
				}
			} 

			// trigger
			OUTPUTPORT |= (1<<OUTPUT);			// trigger camera/flash
      LEDPORT |= (1<<LED1)|(1<<LED2)|(1<<LED3); // LEDs on
			_delay_ms(350);
			OUTPUTPORT &= ~(1<<OUTPUT);
      color(state_g);           // LEDs on
			_delay_ms(200);
		}
	}

	reset();

}


/*****************************************************************************/
/* 
	init uC 
*/
/*****************************************************************************/
void init()
{
	// disable watchdog (must be done within the first 15ms after start)
	wdt_reset();          // reset watchdog counter (might be running after last wdt reset!)
	MCUSR &= ~(1<<WDRF);  // disable watchdog for power conservation (might be active after last watchdog reset)
	wdt_disable();        // disabling the watchdog is only possible if WDRF is 0

	// set output direction and pull-ups
	LEDDDR |= (1<<LED1)|(1<<LED2)|(1<<LED3);			// LEDs are output
	OUTPUTDDR |= (1<<OUTPUT);							// output
	BUTTONPORT |= (1<<BUTTON);							// pull-up button
	POTPOWERDDR |= (1<<POTPOWER);						// trimmer power


	// init timer
	TCCR0B |= (1<<CS02)|(0<<CS01)|(0<<CS00);			// activate timer

	// init adc (ADC should not be enabled before the sleep mode
	ADMUX |= (0<<MUX5)|(0<<MUX4)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
	ADCSRA = (1<<ADATE)|(0<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);	// continuous operation of ADC
	ADCSRB |= (1<<ADLAR);
	DIDR0 |= (1<<ADC0D);
	
	// init pin change interrupt						// disable digital input
	GIMSK |= (1<<PCIE1);								// just PC for waking up the uC
														// INT0 will be activated later after wake-up
	PCMSK1 |= (1<<PCINT8);								// set pin change mask
}



/*****************************************************************************/
/* 
	set led color
*/
/*****************************************************************************/

void color(uint8_t i)
{
	if(i==0)
	{
		LEDPORT |= (1<<LED1);
		LEDPORT &= ~((1<<LED2)|(1<<LED3));
	}
	else if(i==1)
	{
		LEDPORT |= (1<<LED2);
		LEDPORT &= ~((1<<LED1)|(1<<LED3));
	}
	else
	{
		LEDPORT |= (1<<LED3);
		LEDPORT &= ~((1<<LED1)|(1<<LED2));
	}
}

/*****************************************************************************/
/* 
	read button
*/
/*****************************************************************************/

uint8_t button()
{
	if (~BUTTONPIN & (1<<BUTTON))
	{
		return 255;
	}
	else
	{
		return 0;
	}
}


/*****************************************************************************/
/* 
	init uC 
*/
/*****************************************************************************/
void reset()
{
    wdt_enable(WDTO_15MS); // set WDE to generate chip reset (WDIE is insignificant here)
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
   	while(1);
}



/*****************************************************************************/
/* 
	interrupt service routines
*/
/*****************************************************************************/


/* pin change (PC) interrupt, just for wakeup */
EMPTY_INTERRUPT(PCINT1_vect)


/* timer overflow */
ISR(TIM0_OVF_vect)
{
	// read adc and toggle channel
	if(ADMUX==0)
	{
		adcvalue_fine_g = ADCH;	// read fine poti, change channel to coarse
		ADMUX = (0<<MUX5)|(0<<MUX4)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(1<<MUX0);
	}
	else
	{
		adcvalue_coarse_g = ADCH;	// read coarse poti, change channel to fine
		ADMUX = (0<<MUX5)|(0<<MUX4)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
	}

	// detect button press
	if(button())
	{
		eeprom_write_byte(&laststate_ee, 255 - state_g);	// save state to eeprom
		reset();											// good bye
	}


}

