// Simple timer with 2-digit multiplex by Joonas Pihlajamaa, joonas.pihlajamaa@iki.fi
// (C) 2012 All rights reserved.

#include <avr/io.h>
#include <avr/interrupt.h>

#define SEG_ALL 0x7F
#define SEG_A 1
#define SEG_B 2
#define SEG_C 4
#define SEG_D 8
#define SEG_E 16
#define SEG_F 32
#define SEG_G 64

// display given digit in seven-segment display
void display(uint8_t n) {
	switch(n) {
		case 0: PORTD = SEG_ALL-SEG_G; break;
		case 1: PORTD = SEG_B+SEG_C; break;
		case 2: PORTD = SEG_ALL-SEG_F-SEG_C; break;
		case 3: PORTD = SEG_ALL-SEG_F-SEG_E; break;
		case 4: PORTD = SEG_F+SEG_G+SEG_B+SEG_C; break;
		case 5: PORTD = SEG_ALL-SEG_B-SEG_E; break;
		case 6: PORTD = SEG_ALL-SEG_B; break;
		case 7: PORTD = SEG_A+SEG_B+SEG_C; break;
		case 8: PORTD = SEG_ALL; break;
		case 9: PORTD = SEG_ALL-SEG_E; break;
		default:PORTD = 0; break;
	}
}

volatile uint8_t timer_seconds = 0;

ISR(TIMER1_COMPA_vect) { // 1 Hz counter
    timer_seconds++;
} 

volatile uint8_t active_display = 0;

ISR(TIMER0_OVF_vect) { // multiplex code
	PORTB |= (1 << active_display); // display OFF (VCC at base)

    active_display ^= 1; // toggle between 0 and 1
    
    if(active_display == 0) // prepare next digit
        display(timer_seconds % 10);
    else
        display((timer_seconds/10) % 10);
	
	PORTB &= ~(1 << active_display); // display ON (GND at base)
}

int main(void) {	
	// initialize ports
	DDRD = SEG_ALL; // PD0-PD6 as outputs
	DDRB = (1<<PB0) + (1<<PB1); // PB0+PB1 as segment selectors
	
	// init timer 0 (multiplex)
	TIMSK |= (1 << TOIE0); // timer 0 (8-bit) overflow interrupt
	
	// init timer 1 (100 Hz clock and logic)
	TCCR1B |= (1 << WGM12); // configure for CTC mode
	TIMSK |= (1 << OCIE1A); // CTC interrupt
	OCR1A = 15625; // with prescaler 64, results in 1 Hz @ 1 MHz

	sei(); //  enable global interrupts

	// start timers
	TCCR0B |= (1 << CS01); // timer 0 at clk/8
	TCCR1B |= (1 << CS11)+(1 << CS10); // timer 1 at clk/64

	while(1) {} // loop forever

	return 1;
}
