/*	nixie.c
 *
 *	DCF77 radio controlled Nixie clock software
 *	for Atmel 4433 or mega8 microcontrollers
 *
 *	(c) 2003 by daduke <daduke@daduke.org>
 *
 *	GPL license - use at your own risk
 */

//#include <iom8.h> 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

volatile void out2(uint8_t group, uint8_t value);	//output 2 digits
volatile void out1(uint8_t digit, uint8_t value);	//output 1 digit

volatile uint8_t ictr, bitctr, date, newsec, first15;
volatile uint8_t sec, min, hour, day, mon, year, lmin, lhour;
volatile uint8_t bit, dcf_in, last_one, duration, time_ready;
volatile uint16_t dctr;

volatile uint8_t bits[38];		//hilarious waste of memory, but wtf...


//SIGNAL (SIG_OVERFLOW0)	//generates 250 interrupts/sec
SIGNAL(SIG_OUTPUT_COMPARE1A) {
//	outp(0,TCNT0);    /* reload timer with initial value */

	if (++ictr==250) {	//if one second has passed
		ictr = 0;	//reset

		if (++sec==60) {	//new minute
			sec = 0;		//inc sec and reset if 60
			if (++min==60) {
				min = 0;
				if (++hour==24) {
					hour = 0;
				}
			}
		}
		newsec = 1;
	}
	dctr++;	//inc pulse length counter


	dcf_in = (uint8_t)bit_is_clear(PINB,1);	//probe DCF signal
	if (dcf_in) {		//DCF input is high
			  if (!last_one) {	//beginning of a pulse
						if (dctr > 450 && dctr < 550) {		//one pulse was missing -> new minute
							ictr = 0;			//reset interrupt counter: new sec starts NOW
							sec = 0;				//and it's the first
							bitctr = 0;			//reset bit counter
							first15 = 0;		//the first 15 bits in the DCF signal are 0, and we use them as a sort of quality indicator
						 }
						 dctr = 0;	//reset pulse length counter

			  }
	} else {				//DCF is low
			  if (last_one) {		//a pulse just ended
						 duration = dctr;		//length = # ticks
						 if (duration > 19 && duration < 31) {		//we got a low
									bit = 1;
						 } else if (duration > 44 && duration < 56) {	//got a hi
									bit = 2;
						 } else bit = 0;	//something else -> bad signal?
						 if (bit) {			//for hi or low
									if (bitctr < 15 && bit == 1) {	//sum up the first 15 bits
										first15++;
									} else if (bitctr > 20 && bitctr < 59) {		//store the bit
										bits[bitctr-21] = bit-1;
									}
									bitctr++;		//inc bit counter
						 }
			  }
	}
	if ((first15 == 15) && (bitctr == 58)) {		//if all bits are received and the first 15 were 0
			  time_ready = 1;	//we can compute and update the time
			  first15 = 0;
	}
	last_one = dcf_in;		//store the state of the DCF signal for the next iteration

//	if ((hour == 0) && (min == 2) && (sec == 35)) { cbi(PORTC, 3); sbi(PORTB, PB0); }	//trigger the DCF77 receiver 2 min after midnight
//	if ((hour == 0) && (min == 2) && (sec == 45)) cbi(PORTB, PB0);
	
	
}


volatile int main( void )
{
	outp(0xFF,DDRD);    /* use all pins on PORTD for output: 2xBCD */
	outp(0x0F,DDRC);    /* use lower 4 pins on PORTC for output: latch select + HV control */
	outp(0x01,DDRB);    /* use lowest pin on PORTB for output: sync DCF77 */

//	outp((1<<TOIE0),TIMSK); /* enables the T/C0 overflow interrupt in the T/C interrupt mask register for */
//	outp(0,TCNT0);    /* start value of T/C0 */
//	outp(3,TCCR0);    /* prescale clk/64 */
//	sbi(TWCR, TWEN);	//enable I2C for temp sensor

  TIMSK = _BV(OCIE1A);
  TCCR1B = _BV(CS12)    // 256 prescale
         | _BV(WGM12);  // CTC mode, TOP = OCR1A
  OCR1A = 63;        // count up to TOP 1Hz with 4.096 meg system clock


	sbi(PORTC, 3);		//turn on HV supply

	sbi(PORTC, 1);
	outp(255,PORTD);
	cbi(PORTC, 1);
	for (ictr=0; ictr<10; ictr++) {	//power-on tube test, counting 0..9 and back
		for (bitctr=0; bitctr<6; bitctr++) {
				  out1(bitctr, ictr);
				  for (dctr=0; dctr<20000; dctr++) sec++;
		}
	}
/*	for (ictr=8; ictr=2; ictr--) {
		for (bitctr=0; bitctr<6; bitctr++) {
				  out1(bitctr, ictr);
				  for (dctr=0; dctr<20000; dctr++) sec++;
		}
	} */

	for (ictr = 0; ictr < 37; ictr++) bits[ictr] = 0;		//set all bits to 0
	sec = dctr = ictr = bitctr = last_one = time_ready = 0;
	first15 = 0;

	sei();    /* set global interrupt enable */

	while(1) {	
		if (time_ready) {		//compute and update time
		  lmin = bits[0] + (bits[1]<<1) + (bits[2]<<2) + (bits[3]<<3)
					 + ( bits[4] + (bits[5]<<1) + (bits[6]<<2))*10;
		  lhour = bits[8] + (bits[9]<<1) + (bits[10]<<2) + (bits[11]<<3)
					 + ( bits[12] + (bits[13]<<1))*10;
		  day = bits[15] + (bits[16]<<1) + (bits[17]<<2) + (bits[18]<<3)
					 + ( bits[19] + (bits[20]<<1))*10;
		  mon = bits[24] + (bits[25]<<1) + (bits[26]<<2) + (bits[27]<<3)
					 + bits[28]*10;
		  year = bits[29] + (bits[30]<<1) + (bits[31]<<2) + (bits[32]<<3)
					 + ( bits[33] + (bits[34]<<1) + (bits[35]<<2) + (bits[36]<<3))*10;
		  hour = lhour;
		  min = lmin - 1;
		  time_ready = 0;
		}
		if (newsec) {
			date = (sec > 30 && sec < 35 && min % 2);	//show the date every odd minute for some seconds
				if (!date) {
					  out2(2, hour);
					  out2(1, min);
					  out2(0, sec);
				} else {
					  out2(2, day);
					  out2(1, mon);
					  out2(0, year);
				}
			newsec = 0;
		}
	}
}

volatile void out2(uint8_t group, uint8_t value) {
	uint8_t byte;

	if (group == 2 && value < 10) byte = (uint8_t)(10<<4) + (uint8_t)(value % 10); else		//leading zero suppression
			  byte = (uint8_t)((value / 10)<<4) + (uint8_t)(value % 10);
	sbi(PORTC, group);
	outp(byte,PORTD);
	cbi(PORTC, group);
}

volatile void out1(uint8_t digit, uint8_t value) {
	uint8_t byte, group;

	group = 2 - (uint8_t)(digit / 2);
	if (!(digit %  2)) {
			  byte = (uint8_t)(value<<4) + (uint8_t)(11);
	} else {
			  byte = (uint8_t)(10<<4) + (uint8_t)(value);
	}
	sbi(PORTC, (group+1) % 3);
	outp(255,PORTD);
	cbi(PORTC, (group+1) % 3);
	sbi(PORTC, group);
	outp(byte,PORTD);
	cbi(PORTC, group);
}
