/*
    Title:    Countdown Timer
    Author:   Daniel Schramm
    Date:     3/2002
    Purpose:  Timer up to 99min 59sec, aprox. 2% tollerance
    needed
    Software: AVR-GCC
    needed
    Hardware: ATS90S2313/mega on selfmade board
    Note:     To contact me, mail to
                  daniel.schramm@gmx.de
*/

#define RELAY_OFF 0
#define RELAY_ON 0x10
#define INPUTS 0xe0

#include <io.h>
#include <ina90.h>
#include <interrupt.h>
#include <signal.h>


/*    abcdefg	inv	rot	hex
 * 0: 1111110	0000001	1000000	0x40
 * 1: 0110000   1001111	1111001	0x79
 * 2: 1101101   0010010 0100100	0x24
 * 3: 1111001   0000110 0110000	0x30
 * 4: 0110011   1001100 0011001	0x19
 * 5: 1011011   0100100 0010010	0x12
 * 6: 1011111   0100000 0000010	0x02
 * 7: 1110000   0001111 1111000	0x78
 * 8: 1111111   0000000 0000000	0x00
 * 9: 1111011   0000100 0010000	0x10
 * _: 0001000   1110111 1110111	0x77
 * d: 0111101   1000010 0100001	0x21
 * o: 0011101	1100010 0100011 0x23
 * n: 0010101   1101010 0101011	0x2b
 * E: 1001111   0110000 0000110	0x06
 * off:				0x7f
*/

const uint8_t symbol[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0x77,0x21,0x23,0x2b,0x06,0x7f};
const uint8_t position[]={0x08,0x04,0x02,0x01,};

struct display_t{
	uint8_t val[4];
	uint8_t position;
	uint8_t cursor;
	uint8_t counter;
	uint8_t blinkstatus;
	uint8_t blink;
};

struct key_t{
	uint8_t cursor;
	uint8_t set;
	uint8_t start;
};

struct display_t display;
struct key_t key;
struct key_t newkey;
struct key_t oldkey;

uint16_t time;
uint16_t i,j;
uint8_t relay;
uint8_t counter;
uint8_t done;
uint8_t tastatur_verz;

void time2display(void) {
	uint16_t rest;
	display.val[0]=time/600;
	rest=time%600;
	display.val[1]=rest/60;
	rest=rest%60;
	display.val[2]=rest/10;
	rest=rest%10;
	display.val[3]=rest;
}

void display2time(void) {
	time=display.val[0]*600 + display.val[1]*60 + display.val[2]*10 +display.val[3];
}

void eeprom_wait(void) {
	while ( ! eeprom_is_ready() );
}

void eeprom_read(void) {
	eeprom_wait();
	display.val[0]=eeprom_rb(0x10);
	eeprom_wait();
	display.val[1]=eeprom_rb(0x20);
	eeprom_wait();
	display.val[2]=eeprom_rb(0x30);
	eeprom_wait();
	display.val[3]=eeprom_rb(0x40);
	display2time();

}

void eeprom_write(void) {
	eeprom_wait();
	if ( display.val[0] != eeprom_rb(0x10) ) {
		eeprom_wait();
		eeprom_wb(0x10,display.val[0]);
	}
	eeprom_wait();
	if ( display.val[1] != eeprom_rb(0x20) ) {
		eeprom_wait();
		eeprom_wb(0x20,display.val[1]);
	}
	eeprom_wait();
	if ( display.val[2] != eeprom_rb(0x30) ) {
		eeprom_wait();
		eeprom_wb(0x30,display.val[2]);
	}
	eeprom_wait();
	if ( display.val[3] != eeprom_rb(0x40) ) {
		eeprom_wait();
		eeprom_wb(0x40,display.val[3]);
	}
}

void sleep (void) {
	for (i=0; i<255; i++) {
		asm volatile("nop");
	}
}

void longsleep (void) {
	for (j=0; j<3000; j++) {
		sleep();
	}
}

void init (void) {
	relay=RELAY_OFF;
	outp (0x1f, DDRB);
	outp (0xff, DDRD);
	outp(0x00 | INPUTS ,PORTB);
	outp(0x00,PORTD);
	display.position=0;
	display.cursor=0;
	display.counter=0;
	display.blinkstatus=0;
	display.blink=0;
	display2time();
	eeprom_read();
	newkey.start=0;
	newkey.set=0;
	newkey.cursor=0;
	oldkey.start=0;
	oldkey.set=0;
	oldkey.cursor=0;
	key.start=0;
	key.set=0;
	key.cursor=0;
	counter=0;
	done=0;
	tastatur_verz=0;
}


SIGNAL(SIG_OVERFLOW0)        /* signal handler for tcnt0 overflow interrupt */
{
	counter++;
	if (counter > 30){   /* 8MHz -> 1s */
		counter = 0;
		time--;
		time2display();
		if ( ! time ) {
			relay=RELAY_OFF;
			done=1;
			tastatur_verz=1;
			display.val[0]=11;
			display.val[1]=12;
			display.val[2]=13;
			display.val[3]=14;
			outp((0<<TOIE0), TIMSK); /* disable TCNT0 overflow */
		}
	}
	if ( relay )
		outp(0, TCNT0);
}

void update_display (void) {
	
	display.position++;
	if (display.position > 3)
		display.position = 0;
	
	outp(position[display.position] | relay | INPUTS ,PORTB);

	if ( display.blink )
		if ( display.position == display.cursor ) {
			display.counter++;
			if (display.counter > 250) {
				if ( display.blinkstatus == 1)
				 	display.blinkstatus=0;
				else
					display.blinkstatus=1;
				display.counter = 0;
			}
			if ( display.blinkstatus == 1) {
				outp(symbol[10],PORTD);
				return;
			}
		}
	
	outp(symbol[display.val[display.position]],PORTD);
}

/*
void check_keyboard(void) {
	uint8_t read;
	oldkey=newkey;
	read=inp(PINB);
	newkey.cursor=! (read & 0x20);
	newkey.set  =! (read & 0x40);
	newkey.start=! (read & 0x80);
	if ( (! newkey.cursor) && oldkey.cursor )
		key.cursor=1;
	else
		key.cursor=0;
	if ( (! newkey.set) && oldkey.set )
		key.set=1;
	else
		key.set=0;
	if ( (! newkey.start) && oldkey.start )
		key.start=1;
	else
		key.start=0;
}
*/

void check_keyboard(void) {
	uint8_t read;
	oldkey=newkey;
	read=inp(PINB);
	
	if (read & 0x20)
		newkey.cursor=0;
	else
		newkey.cursor++;
	if (read & 0x40)
		newkey.set=0;
	else
		newkey.set++;
	if (read & 0x80)
		newkey.start=0;
	else
		newkey.start++;

	
	if ( (! newkey.cursor) && ( oldkey.cursor > 5 ))
		key.cursor=1;
	else
		key.cursor=0;
		
	if ( (! newkey.set) && ( oldkey.set > 5 ))
		key.set=1;
	else
		key.set=0;
	if ( (! newkey.start) && ( oldkey.start > 5 ))
		key.start=1;
	else
		key.start=0;
}

	
void take_keyboard(void) {
	if ( relay) {
		if ( newkey.start && newkey.cursor && newkey.set ) {
			init();
			outp((0<<TOIE0), TIMSK); /* disable TCNT0 overflow */
			longsleep();
		}
	} else {
		if ( done && ( newkey.start || newkey.cursor || newkey.set ) )
			init();
		if ( key.start ) {
			relay=RELAY_ON;
			display.blink=0;
			counter=0;
			eeprom_write();
			outp((1<<TOIE0), TIMSK); /* enable TCNT0 overflow */
			outp(5,   TCCR0);        /* count with cpu clock/1024 */
			outp(0,   TCNT0);        /* reset TCNT0 */
			sei();                   /* enable interrupts */
		}
		if ( key.cursor ) {
			display.cursor++;
			display.blink=1;
			if ( display.cursor > 3 )
				display.cursor=0;
		}
		if ( key.set ) {
			display.val[display.cursor]++;
			display.blink=1;
			if ( display.cursor == 2 ) {
				if ( display.val[display.cursor] > 5 )
					display.val[display.cursor]=0;
			} else {
				if ( display.val[display.cursor] > 9 )
					display.val[display.cursor]=0;
			}
			display2time();
		}
	}
}
	

int main(void) {
	init();
	time2display();
	while (1) {
		check_keyboard();
		take_keyboard();
		for (j=0;j<10;j++) {   // slow down keyboard 
			if ( tastatur_verz ) {
				j=0;
				tastatur_verz=0;
			}
			update_display();
			sleep();
		}
	}
}

