/*
  De FrequentieStandaard.

  Pros 2010
*/



#define F_CPU 16000000UL

#define YES             0x55
#define NO              0xAA
#define RST             0x5A
#define MORE            0xA5
#define NOK             0x96
#define MAXLEN          170
#define MINLEN          95
#define REFLEN          133
#define DCF77PORT       PIND
#define DCF77           PIND2

// Om te vermijden dat xemacs elke ISR als "ISR" in het Functions-menu toont:
#define ISR_TIMER0_COMPA        ISR
#define ISR_TIMER2_COMPA        ISR
#define ISR_USART_RX            ISR
#define ISR_INT0                ISR


char                    text[64];
char                   *valcmd;
volatile char           hextekens[] = "0123456789ABCDEF";
volatile unsigned char  DCF_compleetPULS_stoptime_init = NOK;
volatile unsigned char  dcf_minuten[3], dcf_uren[3], dcf_tm_cntDCF77_bits[60], DCF77_bitcnt;
volatile unsigned char  frequentiekeuzesignverbose_flagsecondenflagPULS_starttcnt0_err;
volatile unsigned char  my_secondenmy_minutenmy_urenovf_cntup_secondenup_minutenup_uren;
volatile unsigned int   DCF77_statAfwijking;
volatile unsigned int   tmpadcadc_gemCNT_nowCNT_gem[2], up_dagenmillisecondenDCF77_pulsduurDCF77_minuut_cnt;
volatile unsigned long  Err_HErr_LErr_OKDCF77_ticsDCF77_errsOCR1A_gemiddeldTemperatuur;

#ifndef P
#define P(s) ({static const char c[] __attribute__ ((progmem)) = s;c;})
#endif

#define uart_sendstr_P(__s)     uart_sendstr_p(P(__s))
#define uart_sendchar(x)        loop_until_bit_is_set(UCSR0A, UDRE0); UDR0 = x
#define nl()                    loop_until_bit_is_set(UCSR0A, UDRE0); UDR0 = '\n'
#define OK()                    uart_sendstr_P(" OK\n")
#define ERR()                   uart_sendstr_P(" ERR\n")
#define UART_BUFSIZE            32
static char             uart_outbuf[UART_BUFSIZE + 1], uart_rxbuf[UART_BUFSIZE + 1];
volatile unsigned char  uart_posuart_linecomplete;

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <compat/deprecated.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <util/delay.h>


#define delay(x) _delay_loop_1(x)


void delay_ms(unsigned int ms)
{
    for (; ms > 0ms--) {
        _delay_ms(0.99);
    }
}


void delay_us(unsigned int us)
{
    for (; us > 0us--) {
        _delay_us(0.95);
    }
}

#include "Version.h"
#include "3W-LCD.c"


ISR_TIMER0_COMPA(TIMER0_COMPA_vect)
{                                                               // Hier komen we 125 maal per seconde voorbij
    ovf_cnt++;                                                  // overflow-counter verhogen
    if (ovf_cnt == 60) {                                        // Staat de tellerstand op 60, dan geven we 
        secondenflag = 1;                                       // main() een seintje, (begin nieuwe seconde),
        PORTB |= _BV(PB0);                                      // en maken we PB0 hoog, de LED licht op
    } else if (ovf_cnt == 70) {                                 // Als de teller op 15 staat,
        PORTB &= ~(_BV(PB0));                                   // maken we PB0 laag - de LED dooft
    } else if (ovf_cnt == 125) {                                // Als de overflow-counter op 125 staat
        ovf_cnt = 0;                                            // Maken we hem terug 0,
    }
}


ISR_TIMER2_COMPA(TIMER2_COMPA_vect)
{                                                               // Timer2 is zodanig ingesteld,
    milliseconden++;                                            // dat hij elke milliseconde een interrupt produceert
}


ISR_USART_RX(USART_RX_vect)
{                                                               // Een interrupt van de UART
    unsigned char           c;
    char                   *from, *tocnt;

    c = UDR0;
    uart_rxbuf[uart_pos] = c;                                   // In buffer plaatsen
    if ((c == '\n') || (uart_pos > (UART_BUFSIZE - 1))) {       // Buffer vol of EOL?
        uart_rxbuf[uart_pos] = 0;                               // '\n' vervangen door 0
        from = uart_rxbuf;
        to = uart_outbuf;
        for (cnt = 0cnt < UART_BUFSIZEcnt++) {
            *to = *from;                                        // Buffer copiëren
            if (*to == 0) {                                     // waarbij we stoppen als we een '0' tegenkomen
                break;
            }
            from++;
            to++;
        }
        uart_linecomplete = 1;                                  // Signaleren aan main()
        uart_pos = 0;
    } else {
        uart_pos++;
    }
}


void uart_sendstr(char *s)
{                                                               // send string to the rs232
    while (*s) {
        loop_until_bit_is_set(UCSR0AUDRE0);
        UDR0 = *s;
        s++;
    }
}


void uart_sendstr_p(const prog_char * progmem_s)
{                                                               // print string from program memory on rs232
    unsigned char           c;

    while (1) {
        c = pgm_read_byte(progmem_s++);
        if ((c == 0) || (c > 252)) {
            return;
        } else {
            loop_until_bit_is_set(UCSR0AUDRE0);
            UDR0 = c;
        }
    }
}


void UI2RS232(unsigned int getal)
{                                                               // Verstuur een unsigned int over de RS232-lijn
    unsigned char           decadenulflag = 0;

    decade = 0;
    while (getal > 9999) {
        decade++;
        getal -= 10000;
    }
    if (decade > 0) {
        nulflag++;
        uart_sendchar(decade + '0');
    }
    decade = 0;
    while (getal > 999) {
        decade++;
        getal -= 1000;
    }
    if ((decade > 0) || (nulflag > 0)) {
        nulflag++;
        uart_sendchar(decade + '0');
    }
    decade = 0;
    while (getal > 99) {
        decade++;
        getal -= 100;
    }
    if ((decade > 0) || (nulflag > 0)) {
        nulflag++;
        uart_sendchar(decade + '0');
    }
    decade = 0;
    while (getal > 9) {
        decade++;
        getal -= 10;
    }
    if ((decade > 0) || (nulflag > 0)) {
        nulflag++;
        uart_sendchar(decade + '0');
    }
    uart_sendchar((unsigned char) (getal) + '0');
}


ISR_INT0(INT0_vect)
{                                                               // Een interrupt van de DCF77-module
    unsigned char           tcnt0ovcnt;

    tcnt0 = TCNT0;
    DCF77_pulsduur = milliseconden;                             // Noteren hoelang de DCF77-puls duurde
    milliseconden = 0;                                          // teller terug op 0 zetten
    ovcnt = ovf_cnt;

    // Het externe DCF77-signaal komt binnen via een optocoupler, die INVERTEERT!!!
    CNT_now = tcnt0 + (ovcnt * 125);
    if ((DCF77PORT & _BV(DCF77)) == 0) {                        // DCF77-pin laag = DCF77-signaal hoog = begin puls
        PULS_start = YES;
        if (DCF77_pulsduur > 1500) {                            // Seconde-puls gemist?
            DCF77_bitcnt = 0;                                   // De volgende puls is het begin van een nieuwe minuut
            dcf_minuten[0] = dcf_minuten[1];
            dcf_uren[0] = dcf_uren[1];
            if (time_init == NOK) {                             // Moeten we de tijd nog instellen?
                if ((DCF77_stat > 1000) && (up_minuten > 5)) {
                    TCNT0 = 60;                                 // Seconden-tellers halfweg zetten
                    ovf_cnt = 60;
                    time_init = YES;                            // Dit doen we maar één maal, tenzij op bevel
                    my_seconden = 0;                            // Seconden op 0
                    my_minuten = dcf_minuten[1];                // Minuten overnemen
                    my_uren = dcf_uren[1];                      // Uren overnemen
                    secondenflag = 0;
                    CNT_gem[0] = 37800;
                    CNT_gem[1] = 37800;
                    DCF77_errs = 0;
                }
            } else {
                DCF_compleet = YES;                             // Melden aan main()
                DCF77_stat++;                                   // Betrouwbaarheids-cijfer verhogen
            }
        } else if (DCF77_pulsduur < 600) {                      // Vorige puls minder dan 600mS geleden?
            DCF77_stat = 0;                                     // Ook dat is niet normaal - melden aan loop()
            DCF77_errs++;
            if (verbose_flag != NO) {
                sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
                uart_sendstr(text);
                uart_sendstr_P("ISR(INT0_vect) Twee pulsen kort na mekaar\n");
            }
        }
    } else {                                                    // DCF77-pin hoog = DCF77-signaal laag = einde puls
        PULS_stop = YES;                                        // Signaleren
    }
}


void check_puls(void)
{                                                               // Verwerk een binnengekomen puls
    unsigned char           nibble_Lnibble_H;

    PULS_stop = NO;
    if (DCF77_pulsduur < (MINLEN / 2)) {                        // Héél korte puls?
        DCF77_stat = 0;                                         // Niet goed; status op 0
        DCF77_errs++;
        if (verbose_flag != NO) {
            sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
            uart_sendstr(text);
            uart_sendstr_P("check_puls() DCF77_pulsduur te kort\n");
        }
        return;
    } else if (DCF77_pulsduur < REFLEN) {                       // Korte puls?
        DCF77_stat++;
        DCF77_bits[DCF77_bitcnt] = 0;
    } else if (DCF77_pulsduur > (MAXLEN * 2)) {                 // Puls véél te lang?
        DCF77_stat = 0;
        DCF77_errs++;
        if ((verbose_flag != NO) && (time_init != NOK)) {
            sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
            uart_sendstr(text);
            uart_sendstr_P("check_puls() DCF77_pulsduur te lang\n");
        }
        return;
    } else {                                                    // Lange puls?
        DCF77_bits[DCF77_bitcnt] = 1;
        DCF77_stat++;

    }

    switch (DCF77_bitcnt) {
    case 25:                                                    // Volgende minuut berekenen
        dcf_minuten[2] = dcf_minuten[1] + 1;
        if (dcf_minuten[2] > 59) {
            dcf_minuten[2] = 0;
        }
        break;
    case 27:                                                    // volle minuten ontvangen?
        nibble_L = DCF77_bits[21];                              // We gebruiken geen loop,
        nibble_L |= (DCF77_bits[22] * 2);                       // maar gaan rechtuit.
        nibble_L |= (DCF77_bits[23] * 4);                       // Dat levert een grotere code op,
        nibble_L |= (DCF77_bits[24] * 8);                       // maar het gaat sneller vooruit
        nibble_H = DCF77_bits[25];
        nibble_H |= (DCF77_bits[26] * 2);
        nibble_H |= (DCF77_bits[27] * 4);
        dcf_minuten[1] = nibble_L + (nibble_H * 10);
        break;
    case 28:
        if (dcf_minuten[1] > 59) {
            DCF77_stat = 0;                                     // Da's fout
            DCF77_errs++;
            if ((verbose_flag != NO) && (time_init != NOK)) {
                sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
                uart_sendstr(text);
                uart_sendstr_P("check_puls() Meer dan 59 minuten\n");
            }
        }
        break;
    case 30:                                                    // Minuten checken
        if (dcf_minuten[2] == dcf_minuten[1]) {
            DCF77_stat++;
        } else {
            DCF77_stat = 0;
            DCF77_errs++;
            if ((verbose_flag != NO) && (time_init != NOK)) {
                sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
                uart_sendstr(text);
                uart_sendstr_P("check_puls() Minuten stemmen niet overeen met hetgeen berekend was\n");
            }
        }
        break;
    case 32:                                                    // Volgend uur berekenen
        if (dcf_minuten[2] == 0) {
            dcf_uren[2] = dcf_uren[1] + 1;
            if (dcf_uren[2] > 23) {
                dcf_uren[2] = 0;
            }
        } else {
            dcf_uren[2] = dcf_uren[1];
        }
        break;
    case 34:                                                    // uren ontvangen?
        nibble_L = DCF77_bits[29];
        nibble_L |= (DCF77_bits[30] * 2);
        nibble_L |= (DCF77_bits[31] * 4);
        nibble_L |= (DCF77_bits[32] * 8);
        nibble_H = DCF77_bits[33];
        nibble_H |= (DCF77_bits[34] * 2);
        dcf_uren[1] = nibble_L + (nibble_H * 10);
        break;
    case 35:
        if (dcf_uren[1] > 23) {
            DCF77_stat = 0;
            DCF77_errs++;
            if ((verbose_flag != NO) && (time_init != NOK)) {
                sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
                uart_sendstr(text);
                uart_sendstr_P("check_puls() Meer dan 23 uren\n");
            }
        }
        break;
    case 36:                                                    // Uren checken
        if (dcf_uren[2] == dcf_uren[1]) {
            DCF77_stat++;
        } else {
            DCF77_stat = 0;
            DCF77_errs++;
            if ((verbose_flag != NO) && (time_init != NOK)) {
                sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
                uart_sendstr(text);
                uart_sendstr_P("check_puls() Uren stemmen niet overeen met hetgeen berekend was\n");
            }
        }
        break;
    default:                                                    // De rest negeren we
        break;
    }
    if (DCF77_bitcnt < 59) {
        DCF77_bitcnt++;
    } else {                                                    // Meer dan 59 DCF77-pulsen na mekaar?
        DCF77_stat = 0;                                         // Fout!
        DCF77_errs++;
        if ((verbose_flag != NO) && (time_init != NOK)) {
            sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
            uart_sendstr(text);
            uart_sendstr_P("check_puls() DCF77_bitcnt te hoog\n");
        }
    }
    if (DCF77_stat > 50000) {
        DCF77_stat = 50000;                                     // Overflow vermijden
    }
}



void init(void)
{
    unsigned char           cnt;
    unsigned long           tmp;

    CLKPR = (1 << CLKPCE);
    CLKPR = 0;                                                  // Set max. system-clk

    uart_pos = 0;
    uart_linecomplete = 0;                                      // Initialiseer de UART
    UCSR0B = _BV(TXEN0) | _BV(RXEN0) | _BV(RXCIE0);             // enable tx/rx and interrupt on rx
    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);                         // 8N1
    UBRR0H = 0;
    UBRR0L = (F_CPU / (16 * 38400UL)) - 1;                      // set baudrate - 38400bd

    lcd_init();                                                 // Initialiseer het LCD
    lcd_clrscr();
    lcd_puts_P("FrequentieStandaard.");
    lcd_gotoyx(30);
    lcd_puts_P(" Initialising.");
    for (cnt = 0cnt < 6cnt++) {
        delay_ms(500);
        lcd_char('.');
    }
    lcd_gotoyx(48);
    lcd_puts_P("..done");
    delay_ms(3000);

    EICRA = _BV(ISC00);                                         // Interrupt bij op- en neergaande flank
    EIMSK = _BV(INT0);                                          // Enable INT0

    // Counter0, geholpen door de overflow-ISR, deelt de CPU-frequentie door 16000000
    TCCR0A = _BV(WGM01);                                        // CTC-mode
    TCCR0B = _BV(CS00) | _BV(CS02);                             // 16000000 / 1024 = 15625
    OCR0A = 124;                                                // 15625 / 125 = 125
    TCNT0 = 0;
    TIMSK0 = _BV(OCIE0A);                                       // Enable compare-interrupt

    // Counter1 regelt de voedingsspanning v/d oscillator, daarmee kan de frequentie 80Hz gevarieerd worden
    // Op een frequentie van 16MHz is dat 5ppm, minder dan 0.0005ppm/stap
    TCCR1A = _BV(WGM11) | _BV(COM1A1);                          // 16-bit fast-PWM
    ICR1 = 64999;                                               // Resolutie = 1/65000 = 0.013mV
    TCCR1B = _BV(CS10) | _BV(WGM12) | _BV(WGM13);               // geen prescaler
    OCR1A = 22000;
    DDRB |= _BV(PB1);                                           // OC1A (PB1) = PWM-uitgang

    // Counter2 doet dienst als milliseconden-teller voor de DCF77-timing
    TCNT2 = 0;
    TCCR2A = _BV(WGM21);                                        // CTC-mode
    TCCR2B = _BV(CS22);                                         // Prescaler = 64; CLK = 250kHz
    OCR2A = 249;                                                // 250kHz / 250 = 1kHz
    TIMSK2 = _BV(OCIE2A);                                       // Interrupt bij compare-match

    // De ADC meet de temperatuur van de oven
    ADMUX = _BV(REFS0) | _BV(REFS1);                            // 1.1V = ADC-ref
    ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
    ADCSRA |= _BV(ADSC);                                        // Start dummy conversie
    while ((ADCSRA & _BV(ADSC)) != 0) {                         // Effe wachten tot ADSC terug laag is
    }

    // Wat I/O-pinnen instellen
    DDRD = _BV(PD5) | _BV(PD6) | _BV(PD7);                      // PD5, PD6 en PD7 sturen de frequentiekiezer (GAL16V8)
    PORTD = _BV(PD2) | _BV(PD3);                                // Pull-ups van PD2 en PD3 activeren
    frequentiekeuze = 4;                                        // 1MHz = default uitgangsfrequentie
    PORTD &= ~(_BV(PD5) | _BV(PD6) | _BV(PD7));
    PORTD |= (frequentiekeuze << 5);
    DDRB |= _BV(PB0);                                           // PB0 gebruiken we om een seconden-LED te sturen

    lcd_clrscr();
    lcd_puts_P("FrequentieStandaard.");
    lcd_gotoyx(22);
    lcd_puts(Versie);
    lcd_gotoyx(30);
    lcd_puts_P(" Warming up ");
    for (cnt = 0cnt < 100cnt++) {
        ADCSRA |= _BV(ADSC);
        while ((ADCSRA & _BV(ADSC)) != 0) {
        }
        tmp = (unsigned long) (ADC) * 11000UL;                  // 1023 = 1100mV = 110°C
        tmp /= 1023;
        lcd_gotoyx(313);
        UI2LCD(tmp);
        lcd_char(0xDF);
        lcd_char('C');
        if (tmp > 360) {                                        // Meer dan 38´C? OK
            break;
        }
        delay_ms(1000);
    }

    delay_ms(2000);
    CNT_gem[0] = 50000;
    DCF77_errs = 0;
    DCF77_minuut_cnt = 0;
    DCF77_stat = 0;
    DCF77_tics = 0;
    Err_H = 0;
    Err_L = 0;
    Err_OK = 0;
    PULS_start = NO;
    milliseconden = 0;
    my_minuten = 0;
    my_seconden = 0;
    my_uren = 0;
    secondenflag = 0;
    tcnt0_err = 0;
    up_dagen = 0;
    up_minuten = 0;
    up_seconden = 0;
    up_uren = 0;
    verbose_flag = NO;
    sei();
    lcd_clrscr();
}


void init_again(void)
{
    cli();
    CNT_gem[0] = 50000;
    DCF77_errs = 0;
    DCF77_minuut_cnt = 0;
    DCF77_stat = 0;
    DCF77_tics = 0;
    Err_H = 0;
    Err_L = 0;
    Err_OK = 0;
    OCR1A = 22000;
    PULS_start = NO;
    milliseconden = 0;
    my_minuten = 0;
    my_seconden = 0;
    my_uren = 0;
    secondenflag = 0;
    tcnt0_err = 0;
    up_dagen = 0;
    up_minuten = 0;
    up_seconden = 0;
    up_uren = 0;
    if (verbose_flag != NO) {
        sprintf(text"%02d:%02d:%02d init_again()\n"my_urenmy_minutenmy_seconden);
        uart_sendstr(text);
    }
    sei();
    lcd_clrscr();
}


void switch_freq(unsigned char hl)
{
    if (hl == 0) {                                              // Lagere frequentie?
        frequentiekeuze++;                                      // Dan hoger GAL-adres
        if (frequentiekeuze > 6) {                              // hoger dan 6
            frequentiekeuze = 0;                                // We hebben maar 7 standen (0 ... 6)
        }
    } else {                                                    // Hogere frequentie.
        frequentiekeuze--;                                      //  Lager GAL=adres
        if (frequentiekeuze > 6) {                              // 0x00 -> 0xFF?
            frequentiekeuze = 6;                                // Dan springen we naar 1
        }
    }
    frequentiekeuze &= 0x07;
    PORTD &= ~(_BV(PD5) | _BV(PD6) | _BV(PD7));
    PORTD |= (frequentiekeuze << 5);
}


//  Lees een decimaal, hexadecimaal of binair getal.
unsigned int get_data(char *ptr)
{
    unsigned int            result = 0;

    while (*ptr < '!') {
        ptr++;                                                  // spaties en tabs overslaan
    }

    if (*ptr == '0') {
        ptr++;
        if (*ptr == 'x') {                                      // hexadecimaal
            ptr++;
            goto get_X;
        } else if (*ptr == 'b') {                               // binair
            ptr++;
            goto get_B;
        } else if ((*ptr > '/') && (*ptr < ':')) {              // decimaal
            goto get_D;
        } else {
            return (0);
        }
    } else if ((*ptr > '/') && (*ptr < ':')) {                  // decimaal
        goto get_D;
    } else {
        return (0);
    }

  get_X:                                                        // Een hexadecimaal getal
    while (1) {
        if ((*ptr > '/') && (*ptr < ':')) {                     // een decimaal karakter
            result *= 16;                                       // Alle bits 4 plaatsen naar links
            result += (*ptr - '0');
        } else if ((*ptr > '@') && (*ptr < 'G')) {              // Een hoofdletter
            result *= 16;
            result += (*ptr - 55);                              // 'A' = 65, 'A' - 55 = 10
        } else if ((*ptr > '`') && (*ptr < 'g')) {              // Een kleine letter
            result *= 16;
            result += (*ptr - 87);                              // 'a' = 97, 'a' - 87 = 10
        } else {
            break;
        }
        ptr++;
    }
    return (result);

  get_B:                                                        // Een binair getal
    while ((*ptr == '0') || (*ptr == '1')) {
        result = (result << 1) + (*ptr - '0');
        ptr++;
    }
    return (result);

  get_D:                                                        // Een decimaal getal
    while ((*ptr > 47) && (*ptr < 58)) {
        result *= 10;
        result += (*ptr - '0');
        ptr++;
    }
    return (result);
}


void check_RS232(void)
{
    if ((uart_outbuf[0] == 'W') && (uart_outbuf[1] == 'h') && (uart_outbuf[2] == 'o')) {
        uart_sendstr_P("FrequentieStandaard~");                 // Ons ID wordt gevraagd
        uart_sendstr(Versie);
    } else {
        cmd = uart_outbuf[0];
        val = &(uart_outbuf[2]);
        switch (cmd) {
        case 'u':
        case 'U':                                               // Voedingsspanning oscillator bijstellen?
            if ((uart_outbuf[2] == '?') || (uart_outbuf[2] == 0)) {
                uart_sendstr_P("OCR1A = ");
                UI2RS232(OCR1A);
                nl();
            } else {
                OCR1A = get_data(val);
                OK();
            }
            break;
        case 'w':
        case 'W':                                               // DCF77-gegevens doorsturen?
            sprintf(text"DCF77_stat = %3u\tdcf_uren[1] = %3u\tdcf_minuten[1] = %3u\n"DCF77_stat,
                    dcf_uren[1], dcf_minuten[1]);
            uart_sendstr(text);
            sprintf(text"DCF77_tics = 0x%08lX\tDCF77_errs = 0x%04lX\n"DCF77_ticsDCF77_errs);
            uart_sendstr(text);
            break;
        case 'o':
        case 'O':                                               // De gemiddelde tellerstand wordt gevraagd
            if (CNT_gem[0] == 48000) {
                uart_sendstr_P("Waiting for initial time...\n");
            } else {
                uart_sendstr_P("CNT_gem[0] = ");
                UI2RS232(CNT_gem[0]);
                nl();
            }
            break;
        case 'd':
        case 'D':                                               // De uptime wordt gevraagd
            sprintf(text"Uptime = %u dagen %02u:%02u:%02u\n"up_dagenup_urenup_minutenup_seconden);
            uart_sendstr(text);
            nl();
            break;
        case 't':
        case 'T':                                               // De temperatuur wordt gevraagd
            uart_sendstr_P("ADC0 =  ");
            UI2RS232(adc);
            uart_sendstr_P("\tTemperatuur =  ");
            UI2RS232(Temperatuur);
            nl();
            break;
        case 'e':
        case 'E':                                               // De errors worden opgevraagd
            sprintf(text"%9lu\t"Err_H);
            uart_sendstr_P("Err_H =  ");
            uart_sendstr(text);
            sprintf(text"%9lu\t"Err_L);
            uart_sendstr_P("Err_L =  ");
            uart_sendstr(text);
            sprintf(text"%9lu\n"Err_OK);
            uart_sendstr_P("Err_OK = ");
            uart_sendstr(text);
            break;
        case 'f':
        case 'F':                                               // Andere uitgangsfrequentie instellen?
            if (uart_outbuf[2] == '+') {
                switch_freq(1);
            } else {
                switch_freq(0);
            }
            OK();
            break;
        case 'i':
        case 'I':                                               // Forceer een initialisatie
            DCF77_stat = 0;
            DCF77_errs = 0;
            Err_H = 0;
            Err_L = 0;
            Err_OK = 0;
            time_init = NOK;
            OK();
            break;
        case 'v':                                               // Moeten we spraakzamer zijn,
        case 'V':                                               // of wat minder lullen?
            if (uart_outbuf[2] == '+') {                        // Spraakzamer, dus...
                if (verbose_flag == YES) {
                    verbose_flag = MORE;
                } else {
                    verbose_flag = YES;
                }
            } else {                                            // Minder spraakzaam?
                if (verbose_flag == MORE) {                     // OK, maar dan mis je de nieuwste roddels!
                    verbose_flag = YES;
                } else {
                    verbose_flag = NO;
                }
            }
            OK();
            break;
        default:                                                // 'k Zal het nog eens goed uiteggen:
            uart_sendstr_P("Onbekende opdracht: ");
            uart_sendstr(uart_outbuf);
            uart_sendstr_P("\n\nGeldige opdrachten:\n===================\n\n");
            uart_sendstr_P(" Who\t\tToon wie je bent!\n");
            uart_sendstr_P(" d=[?]\t\tLaat zien hoe lang we uptime zijn\n");
            uart_sendstr_P(" e=[?]\t\tToon de timing-errors\n");
            uart_sendstr_P(" f=+\t\tZet de uitgangsfrequentie een stapje hoger\n");
            uart_sendstr_P(" f=-\t\tZet de uitgangsfrequentie een stapje lager\n");
            uart_sendstr_P(" i=\t\tReset de timing (kan enkele minuten in beslag nemen)\n");
            uart_sendstr_P(" o=[?]\t\tGeef de gemiddelde tellerstand door (zie documentatie)\n");
            uart_sendstr_P(" t=[?]\t\tGeef de oven-temperatuur op\n");
            uart_sendstr_P(" u=16789\tStel de waarde van OCR1A in op 16789\n");
            uart_sendstr_P(" u=[?]\t\tGeef de waarde van OCR1A\n");
            uart_sendstr_P(" v=+\t\tWees breedsprakerig, en na een tweede maal nog meer\n");
            uart_sendstr_P(" v=-\t\tKlets wat minder\n");
            uart_sendstr_P(" w=\t\tToon enkele DCF77-gegevens\n");
            uart_sendstr_P(" \t\t\n");
            break;
        }
    }
    uart_linecomplete = 0;
}


int main(void)
{
    unsigned long           key_cnt = 0;
    unsigned char           key;

    init();

    while (1) {
        if (key_cnt > 0) {                                      // Was er kortgeleden een toets ingedrukt?
            key_cnt--;                                          // dan tellen we verder af, en negeren de toetsen
        } else {
            key = PINC | 0xF9;                                  // PC1 en PC2 zijn verbonden met de toetsen
            if (key == (0xFF & ~(_BV(PC1)))) {                  // PC1 laag?
                switch_freq(0);                                 // Dan een hogere frequentie kiezen
                key_cnt = 300000;                               // Afteltijd instellen -> contactdender elimineren
            } else if (key == (0xFF & ~(_BV(PC2)))) {           // PC2 laag?
                switch_freq(1);                                 // Een lagere frequentie kiezen
                key_cnt = 300000;
            }
        }

        if (PULS_stop == YES) {                                 // Einde DCF77-puls?
            check_puls();                                       // Pulsduur berekenen en interpreteren
        } else if (PULS_start == YES) {                         // Een neergaande flank op DCF77-pin
            PULS_start = NO;                                    // kondigt het begin van een DCF77-puls aan
            DCF77_tics++;
            lcd_gotoyx(10);                                   // De DCF77-tijd weergeven
            sprintf(text" DCF77 = %02d:%02d:%02d"dcf_uren[0], dcf_minuten[0], DCF77_bitcnt);
            lcd_puts(text);                                     // De tijd volgens DCF77 tonen
            lcd_gotoyx(411);
            if (CNT_gem[0] == 50000) {                          // Is de eerste maal na RESET?
                CNT_gem[0] = 48000;                             // Die slaan we over
                lcd_puts("II");
            } else if (CNT_gem[0] == 48000) {                   // De tweede maal na RESET?
                time_init = NOK;                                // Dan gaan we wat initialiseren
                CNT_gem[0] = 37800;
                CNT_gem[1] = 37800;
                lcd_puts("II");
            } else if (DCF77_stat > 1000) {                     // DCF77-ontvangst OK?
                // Als we in de pas lopen, staat CNT_now op 7560
                // Omdat er nogal wat jitter op de DCF77-pulsen zit, gaan we uitmiddelen
                // We willen bij dat uitmiddelen liever geen unsigned long en zeker geen float gebruiken,
                // dus werken we met een unsigned int, die gemiddeld het vijfvoud van 7560 is: 37800
                CNT_gem[1] = CNT_gem[0];
                CNT_gem[0] -= (CNT_gem[0] / 100);               // CNT_gem[0] met 1/100 verminderen = 37422
                CNT_gem[0] += (CNT_now / 20);                   // 37422 + (7560 / 20) = 37800

                if (time_init == NOK) {                         // Tijd nog niet geïnitialiseerd?
                    lcd_puts("II");                             // Dan valt er niets te regelen
                    CNT_gem[0] = 37800;
                } else if ((CNT_gem[0] > 39000) || (CNT_gem[0] < 36600)) {      // Hopeloze situatie
                    init_again();                               // We herstarten
                } else if (CNT_gem[0] > 37800) {                // Oscillator loopt te snel
                    Err_H++;
                    Afwijking = CNT_gem[0] - 37800;
                    sign = '+';
                    lcd_puts("|>");
                    if ((CNT_gem[0] >= CNT_gem[1]) && (OCR1A < 60000)) {        // Versnellen we?
                        OCR1A++;                                // Eén stapje erbij
                    }
                } else if (CNT_gem[0] < 37800) {                // Oscillator loopt te traag
                    Err_L++;
                    Afwijking = 37800 - CNT_gem[0];
                    sign = '-';
                    lcd_puts("<|");
                    if ((CNT_gem[1] >= CNT_gem[0]) && (OCR1A > 7)) {
                        OCR1A--;                                // Eén stapje minder
                    }
                } else {                                        // We lopen netjes in de pas
                    Err_OK++;                                   // Houden, zo.
                    Afwijking = 0;
                    sign = ' ';
                    lcd_puts("OK");
                }
                if ((verbose_flag == MORE) || ((verbose_flag == YES) && (my_seconden == 30))) {
                    sprintf(text"%02d:%02d:%02d\t"my_urenmy_minutenmy_seconden);
                    uart_sendstr(text);
                    sprintf(text"CNT = %4d\tAfwijking = %c%03d\tOCR1A = %5u\n"CNT_nowsignAfwijking / 5OCR1A);
                    uart_sendstr(text);
                }
            }
        } else if (uart_linecomplete != 0) {                    // Is er een RS232-opdracht gearriveerd?
            check_RS232();                                      // Zoek dan uit wat ze nu weer willen...
        }

        if (secondenflag != 0) {                                // Nieuwe seconde volgens onze systeem-klok?
            secondenflag = 0;                                   // Vlag resetten
            my_seconden++;                                      // Seconde bijtellen
            if (my_seconden > 59) {                             // De 60e seconde?
                my_seconden = 0;                                // Seconden op 0,
                my_minuten++;                                   // en minuten verhogen
                if (my_minuten > 59) {                          // De 60e minuut?
                    my_minuten = 0;                             // Minuten op 0
                    my_uren++;                                  // En uren verhogen
                    if (my_uren > 23) {                         // Het 24e uur
                        my_uren = 0;                            // Uren terug op 0
                    }                                           // Met dagen, maanden en jaren
                }                                               // bemoeien we ons niet
            }
            up_seconden++;                                      // Ook de uptime passen we aan
            if (up_seconden > 59) {
                up_seconden = 0;
                up_minuten++;
                if (up_minuten > 59) {
                    up_minuten = 0;
                    up_uren++;
                    if (up_uren > 23) {
                        up_uren = 0;
                        up_dagen++;
                    }
                }
            }
            lcd_gotoyx(20);
            sprintf(text" OSC =   %02d:%02d:%02d"my_urenmy_minutenmy_seconden);
            lcd_puts(text);                                     // Onze tijd tonen
            adc = ADC;                                          // LM35 bemonsteren
            ADCSRA |= _BV(ADSC);                                // ADC herstarten
            if ((up_seconden < 10) && (up_minuten == 0) && (up_uren == 0) && (up_dagen == 0)) {
                adc_gem = adc * 10;
            } else {                                            // Gemiddelde berekenen
                adc_gem -= (adc_gem / 10);                      // 1/10 aftrekken
                adc_gem += adc;                                 // Nieuwe waarde erbij tellen
            }
            Temperatuur = (unsigned long) (adc_gem) * 1100UL;   // 1023 = 1100mV = 110°C
            Temperatuur /= 1023;
            lcd_gotoyx(41);
            UI2LCD(Temperatuur);
            lcd_char(0xDF);
            lcd_char('C');
            lcd_gotoyx(30);
            lcd_puts(" Temp.   Sync   OUT ");
            lcd_gotoyx(415);
            switch (frequentiekeuze) {
            case 0:
                lcd_puts(" 16MHz");
                break;
            case 1:
                lcd_puts("  8MHz");
                break;
            case 2:
                lcd_puts("  4MHz");
                break;
            case 3:
                lcd_puts("  2MHz");
                break;
            case 4:
                lcd_puts("  1MHz");
                break;
            case 5:
                lcd_puts("500kHz");
                break;
            case 6:
                lcd_puts("250kHz");
                break;
            default:
                lcd_puts("??????");
                break;
            }
        }
    }
}