/* m88flash - power-controlled slave trigger for a Vivitar 728
   Copyright (C) 2007  Ingo Korb <m88flash@mail.snowcat.de>

   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; version 2 of the License only.

   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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

   Version 1.0: Initial release

*/

#define F_CPU 128000L

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

#define STATE_INIT     0
#define STATE_IDLE     1
#define STATE_PREFLASH 2
#define STATE_FLASHING 3

volatile uint8_t state;

const uint16_t flashdurations[] = {2000,175,88,44,18,10,5,3};

ISR(PCINT1_vect) {
  if (PIND & _BV(PD4) && PINC & _BV(PC0) && state == STATE_IDLE) {
    /* Ignore pre-flash */
    state  = STATE_PREFLASH;
    loop_until_bit_is_set(PINC,PC1);
    PORTB = 0x80;
    power_timer0_enable();
    TCNT0  = 0x80;
    TIMSK0 = _BV(TOIE0);
    TCCR0B = _BV(CS02);
  } else {
    if (state != STATE_FLASHING && state != STATE_INIT) {
      /* Disable main-flash timeout */
      if (state == STATE_PREFLASH) {
	TCCR0B = 0;
	TIMSK0 = 0;
	power_timer0_disable();
      }
      
      /* Trigger the flash */
      power_timer1_enable();
      OCR1A  = 1;
      OCR1B  = flashdurations[PIND & 7];
      TCCR1A = _BV(COM1A1) | _BV (COM1A0) | _BV(COM1B1) | _BV(COM1B0);
      PCICR  = 0;
      TCNT1  = 0;
      TIMSK1 = _BV(TOIE1);
      TCCR1B = _BV(CS10);
      state  = STATE_FLASHING;
    }
  }
  PCIFR = _BV(PCIF1);
}

ISR(TIMER0_OVF_vect) {
  /* No main flash received after ~0.25s */
  state  = STATE_IDLE;
  TCCR0B = 0;
  TIMSK0 = 0;
  power_timer0_disable();
}

ISR(TIMER1_OVF_vect) {
  /* Timer overflow -> Flash certainly done */
  state  = STATE_INIT;
  TCCR1B = 0;
  TIMSK1 = 0;
}

void main(void) {
  state = STATE_INIT;

  /* Dip switches */
  DDRD  = 0xe0;
  PORTD = 0x1f;

  /* Trigger inputs */
  DDRC  = 0x7c;
  PORTC = 0x03;
  PCMSK1 = _BV(PCINT8) | _BV(PCINT9);

  /* Trigger/Quench output */
  DDRB  = 0xff;
  PORTB = 0;

  /* Timer 1 */
  TCCR1A = 0;   
  TCCR1B = 0;

  /* Disable all unused peripherals */
  ACSR = 0x80;
  power_all_disable();
  power_timer1_enable();

  /* Main loop */
  while (1) {
    /* Reset output compare pins */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1);
    TCCR1B = 0;
    TCCR1C = _BV(FOC1A)  | _BV(FOC1B);
    TCNT0  = 0;

    /* Disable output compare */
    TCCR1A = 0;
    OCR1A  = 1;

    /* enable interrupts */
    PCICR  = _BV(PCIE1);
    PCIFR  = _BV(PCIF1);
    sei();

    /* go to sleep to conserve power */
    power_timer1_disable();
    state = STATE_IDLE;
    
    while (state != STATE_INIT) {
      switch (state) {
	case STATE_IDLE:
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	break;

      case STATE_PREFLASH:
      case STATE_FLASHING:
	set_sleep_mode(SLEEP_MODE_IDLE);
	break;
      }
      sleep_cpu();
    }
    cli();
  }
}
    
