/* Configuration of the ports:                                                  *
 * PORTD pins 0-5 are connected as the source pins of the LEDs:                 *
 * Pin 0        1                                                               *
 * Pin 1        2                                                               *
 * Pin 2        4                                                               *
 * ...                                                                          *
 * Pin 5        32                                                              *
 *                                                                              *
 * PORTD pin 6 is the sink for the LEDs displaying the seconds, so it           *
 * has to be pulled low to make them turn on and high to turn them off.         *
 * PORTD pin 7 is the sink for the minute-LEDs.                                 *
 * PORTB pin 0 is the pin used as sink for the hour-LEDs.                       *
 * PORTB pins 1 and 2 are connected to buttons. With PB2 you cycle through the  *
 * variables to edit, it cycles through None-Seconds-Minutes-Hours. Each press  *
 * of PB1 increments the respective variable by one.                            *
 * I've used a crystal oscillator with a frequency of 16MHz, adjust F_CPU       *
 * according to the clock source you're using. :)                               */

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000          // Clock frequency
#define COMP F_CPU/256/1        // Timer overflow value
#define SECOND_ON  128          // PB7 is pulled high, minutes are off.
#define MINUTE_ON  64           // PB6 high, seconds off
#define HOURS_ON   192          // PB6 and 7 high...

void init_timer1(void);
void init_timer0(void);
void init_interupts(void);
int  main(void);

volatile unsigned int flag = 0;
volatile unsigned int s = 0;
volatile unsigned int m = 0;
volatile unsigned int h = 0;
volatile unsigned int cols = 128;
volatile unsigned int colm = 64;
volatile unsigned int colh = 192;
volatile unsigned int overflows;
unsigned int b2pressed,b4pressed;
unsigned int edit = 0;

int main(void) {
    init_timer1();
    init_timer0();
    init_interupts();
    DDRB = 0xF9;
    DDRD = 0xFF;

    PORTC = 0x20;

    while (1) {

        flag=0;
        s++;
        if(s==60) {
            s=0;
            m++;
            if(m==60) {
                m=0;
                h++;
                if(h==24) h=0;
            }
        }
        cols = SECOND_ON | s;
        if((edit==1) && (s%2)) cols = SECOND_ON;
        colm = MINUTE_ON | m;
        if((edit==2) && (s%2)) colm = MINUTE_ON;
        colh = HOURS_ON  | h;
        if((edit==3) && (s%2)) colh = HOURS_ON;

        while(!flag) {
            PORTD=0;
            PORTB=1;
            PORTD=cols;
            asm volatile ("nop");
            asm volatile ("nop");
            asm volatile ("nop");
            asm volatile ("nop");
            PORTD=0;
            PORTB=1;
            PORTD=colm;
            asm volatile ("nop");
            asm volatile ("nop");
            asm volatile ("nop");
            asm volatile ("nop");
            PORTD=0;                // Displaying the hours now, so PD0 has to be low.
            PORTB=0;
            PORTD=colh;
        }
    }
    return 0;
}

ISR (TIMER1_COMPA_vect) { flag++; }

ISR (TIMER0_OVF_vect) {
    overflows++;
    if (overflows==625) {
        overflows = 0;
        if ((PINB==4) && (b4pressed==0)) {
            b4pressed=1;
            edit=(edit+1)%4;
        }
        else if((PINB==2) && (b2pressed==0)) {
            b2pressed=1;
            if(edit==1) {
                s++;
                if(s==60) s=0;
                cols = 128 | s;
            }
            else if(edit==2) {
                m++;
                if(m==60) m=0;
                colm = 64 | m;
            }
            else if(edit==3){
                h++;
                if(h==24) h=0;
                colh = 192 | h;
            }
        }
        else if(PINB==0) {
            b4pressed=0;
            b2pressed=0;
        }
    }
}

void init_interupts() {
    TIMSK |= (1 << OCIE1A);                       // Timer1 CompareA interput
    TIMSK |= (1 << TOIE0);
    sei();                                        // global interupt enable
}

void init_timer1() {
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS12);          // CTC mode and prescaler 256
    OCR1A  = COMP;                                // Calculated to give a frequency of 1 hz. :)
}

void init_timer0() {
    overflows=0;
    TCCR0 |= 1;
}

