#include <16F684.h>
#device adc=10

#FUSES INTRC_IO, NOMCLR, WDT, NOPROTECT, PUT, NOIESO, NOFCMEN, NOCPD
#use delay(clock=125000)


#use fast_io(a)
#use fast_io(c)

#define LED_STATUS  PIN_C5
#define VSENSE      PIN_C3
#define VSENSE_ADC  7
#define SW0         PIN_A1
#define SW1         PIN_A2
#define SW2         PIN_A0
#define SW3         PIN_A5
#define SW_OVERRIDE PIN_A4
#define SER_RX      PIN_A3
#define SER_TX      PIN_C1
#define POWER_CTRL  PIN_C4
#define ATE_SIGNAL  PIN_C0
#define VGOOD       PIN_C2



#define EE_THRESHOLD 0x00
#define EE_DELAY     0x08
#define EE_SN        0x10
#rom 0x2100 = { 0x02, 0xf2, 0x03, 0x05, 0x03, 0x2c, 0x03, 0x42, // voltage thresholds
                0x00, 0x01, 0x00, 0x90, 0x01, 0x1f, 0x02, 0x3d, // shutdown delays
                0x00, 0x00, 0x00, 0x01 } // serial number


/* state globals */
int16 timer_countdown;
#define TIMER_NOT_RUNNING 65535 /* occurs when we are above threshold */
int8 state;
#define STATE_OFF       0
#define STATE_ON        1

/* configuration globals */
int16 configuration_threshold;
int16 configuration_delay;

#int_timer0
void isr_timer0 (void) {
//	output_toggle(LED_STATUS);

	if ( timer_countdown > 0 && timer_countdown != TIMER_NOT_RUNNING ) {
		timer_countdown--;
	}

	if ( 0 == timer_countdown && 0 != input(SW_OVERRIDE) ) {
		state=STATE_OFF;
	} else {
		state=STATE_ON;
	}
}

int16 read_eeprom_int16(int8 address) {
	return make16(read_eeprom(address),read_eeprom(address+1));
}

void get_threshold(void) {
	int8 sw;

	/* read our DIP switches */
	sw=!input(SW0);
	sw=sw<<1;
	sw |= !input(SW1);

	configuration_threshold = read_eeprom_int16(EE_THRESHOLD+sw*2);
}

void get_delay(void) {
	int8 sw;

	/* read our DIP switches */
	sw=!input(SW2);
	sw=sw<<1;
	sw |= !input(SW3);

	configuration_delay = read_eeprom_int16(EE_DELAY+sw*2);
}

/* take the average of 8 ADC readings */
int16 read_adc_average(int8 channel) {
	int16 value;
	int8 i;

	set_adc_channel(channel); 
	value=0;
	for ( i=0 ; i<8 ; i++ ) {
		value += read_adc(); 
		delay_ms(20);
	}
	/* divide by 8 */
	value = value / 8;

	return value;
}

/* setup the PIC */
void init_hardware(void) {
	setup_oscillator(OSC_125KHZ);
	setup_wdt(WDT_DIV_65536);

	restart_wdt();

	/* setup analog to digital converter */
	setup_adc_ports(sAN7); /* battery input */
	setup_adc(ADC_CLOCK_INTERNAL);

	/* turn analog comparator off */
	setup_comparator(NC_NC_NC_NC);
	setup_vref(FALSE);

	/* set the direction of port_a */
	set_tris_a(0b00111111);
	/* setup pullups for switches (individually addressable) */
	port_a_pullups(0b00111111);
	/* set the direction of port_c */
	set_tris_c(0b00001001);

	/* setup our timer to be 1.048576 second */
	setup_timer_0(RTCC_INTERNAL|RTCC_8_BIT|RTCC_DIV_256);
	enable_interrupts(INT_TIMER0);
	enable_interrupts(GLOBAL);

	/* read our configuration from EEPROM */
	get_threshold();
	get_delay();

	/* initial values */
	output_low(POWER_CTRL);
	output_low(LED_STATUS);
	output_low(VGOOD);
}


void main(void) {
	int8 i;
	int16 t;

	timer_countdown=0;
	state=STATE_OFF;

	/* initialize hardware */
	init_hardware();
	
	/* flash the LED on startup */
	for ( i=0 ; i<10 ; i++ ) {
		restart_wdt();
		output_high(LED_STATUS);
		delay_ms(200);
		output_low(LED_STATUS);
		delay_ms(200);
	}



	/* main loop */
	for ( ; ; ) {
		restart_wdt();

		/* get a copy of our timer */
		disable_interrupts(GLOBAL);
		t=timer_countdown;
		enable_interrupts(GLOBAL);

		/* check our voltage and set our timer */
		if ( read_adc_average(VSENSE_ADC) > configuration_threshold ) {
			/* above our threshold */
			output_high(VGOOD);

			/* not counting down */
			disable_interrupts(GLOBAL);
			timer_countdown=TIMER_NOT_RUNNING;
			enable_interrupts(GLOBAL);
		} else {
			/* below our threshold voltage */
			output_low(VGOOD);
			if ( TIMER_NOT_RUNNING == t ) {
				/* we start the timer at our shutdown delay */
				disable_interrupts(GLOBAL);
				timer_countdown=configuration_delay;
				enable_interrupts(GLOBAL);
			}
		}

		restart_wdt();

		/* set our outputs */
		if ( STATE_OFF == state ) {
			/* output off */
			output_low(POWER_CTRL);
			output_low(LED_STATUS);
		} else {
			/* output on */
			output_high(POWER_CTRL);
			if ( t > 0 && TIMER_NOT_RUNNING != t ) {
				/* counting down */
				output_high(LED_STATUS);
			} else {
				/* not counting down */
				output_low(LED_STATUS);
			}
		}
	}
}
