/*
Beispielprogramm für einen 5-stelligen reziproken Frequenzzähler mit
7-Segment-Anzeigen im Multiplexbetrieb. Ohne Vorteiler werden vom ATmega48/88
Eingangssignale von 0,050 Hz - ca. 130 kHz verarbeitet.

Die Messrate ist auf 4 Messungen/s eingestellt. Jede fertige Messung wird durch
den Dezimalpunkt ganz rechts angezeigt, der für ca. 1/8 s aufblinkt.

Funktionsanwahl:
Am Steckverbinder ST2 sind freie Eingänge PB1 und PB2 des µC herausgeführt, mit denen sich 
die Messfunktionen auswählen lassen. Ein einpoliger Schalter EIN-MITTE-EIN reicht dafür aus.

Funktion:         Eingang S-1   Eingang S-2 
Frequenz          offen         offen       
Periode           GND           offen    
Drehzahl          offen         GND    
             
Spannungsanzeige  GND           GND    
1 Hz Abgleich     offen         offen         ADC7 auf GND


S-1 und S-2 können nach Bedarf offen bleiben oder mit einer Steckbrücke gegen GND geschaltet 
werden. Die Spannungsanzeige wird aktiviert, sobald beide Eingänge auf GND liegen. 
Ein einpoliger Taster mit einer Doppeldiode BAT54C wäre eine Möglichkeit, die Kontrolle 
der Versorgungsspannung (Batterie) zu aktivieren.

Abgleich:
Die einfache Methode für einen schnellen und genauen Abgleich ist, ein hinreichend 
genaues 1 Hz Signal an den Signaleingang zu legen (1 pps GPS-Signal) und anschließend 
ADC7 auf GND zu schalten. Damit wird ein Korrekturwert errechnet, der ins EEPROM geschrieben 
auch nach Unterbrechung der Versorgungsspannung wieder zur Verfügung steht. Der Abgleich wird 
durch kurze Anzeige von "88888" inkl. aller Dezimalpunkte quittiert.

Eine weitere Möglichkeit wäre, diesen Korrekturwert nachträglich direkt ins interne EEPROM 
zu schreiben oder schon bei der Übersetzung des Programmes die Taktfrequenz entsprechend 
anzupassen. Da bei 5-stelliger Anzeige die kleinste Änderung in der letzten Stelle einer 
Abweichung von 10 ppm entspricht, können die Korrekturwerte recht grob angepaßt werden. 
Die Drift der Quarzfrequenz wirkt sich deutlich weniger stark aus als bei einer 
6- oder 7-stelligen Auflösung der Messung.

Ganz ohne Programmierung läßt sich die Quarzfrequenz auch durch Änderung der Lastkondensatoren 
C4 und C5 abgleichen.

Das Schaltbild findet sich hier:
http://mino-elektronik.de/bilder/Fmeter_5x7/fmeter_5x7.png
weitere Hinweise zur Funktion/Aufbau:
http://mino-elektronik.de/fmeter/fm_software.htm#bsp13

2016-03-30
http://www.mino-elektronik.de
Alle Angaben ohne Gewaehr !
*/


#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <stdlib.h>



#ifndef EEAR
#define EEAR _SFR_IO16(0x21)            // für Mega88 Kompatibilität
#endif

#ifndef BODS                            // falls kein P-Typ verwendet wird
#define BODS      6                     // dummy Bits verwenden
#define BODSE     5             
#endif
 
#define BIT(x)  (1<<x)

// Pegel je nach Anzeige/Treibern anpassen
// ggf. auch dynamisch mit PortPin umschalten
#define SEGM_PEGEL   1                  // 1-Pegel für aktives Segment
#define DIGIT_PEGEL  0                  // 0-Pegel für aktives Digit

#define F_CLOCK             12000000L   // 12 MHz ggf. anpassen
#define VORTEILER_FAKTOR    1024        // 10 bit-Vorteiler
#define MPX_FREQUENZ        600         // eff. 100Hz bei 6-stell. Anzeige
#define MPX_TEILER (F_CLOCK/MPX_FREQUENZ)    // Intervall für Multiplex

#define MESSZEIT        MPX_FREQUENZ/4  // ca. 4 Messungen/Sek. 
#define LED_EIN         MESSZEIT/2      // 50%
#define LED_STELLEN     5               // angezeigte Stellen
#define TOUT            20              // "tout"-Anzeige nach 20 Sekunden
#define ABSCHALTZEIT    3               // Power Down nach TOUT*ABSCHALTZEIT in 10 Sekunden
#define UPM_SKALIERUNG  60.0            // ggf. vermindern falls mehr Impulse/Umdrehung kommen

#define EE_ADR_OFFSET   2               // Platz für Korrekturwert

#define V_BANDGAP       1.050           // interne Referenz in kann +/- 10% abweichen!
#define ADC_MAX         1023
#define VCC_FILTER      10

enum status       {MESSEN=0, AUSLESEN, AUSWERTEN};    // die Phasen der Messung
enum anzeige_wert {FREQUENZ, PERIODE, DREHZAHL, VCC}; //anzuzeigender Wert

char f_dim[]={0x41,0x42,0x44,0x48,0x50, // Status-LEDs für Frequenz,
              0x30,0x28,0x24,0x22,0x21, // Periode
              0x81,0x82,0x84,0x88,0x90, // und Drehzahl
             };

float frequenz;                         // float-Ergebnis
float f_clock;                          // korrigierte Taktfrequenz nach Abgleich

volatile uint8_t messwert_vorhanden,    // nur gueltige Messungen anzeigen
        mess_status,                    // Ablaufsteuerung
        vorteiler_aktiv,                // zur Berechnung der Frequenz
        vt_cnt,                         // grobe Frequenzbestimmung hinter dem Vorteiler
        led_cnt,                        // LED kurz aufleuchten lassen
        t1_ovf_sync;                    // wird alle 5,5 ms gesetzt

volatile uint16_t zeit_low,             // Timer1-Anteil
        zeit_high,                      // T1 Ueberlauf-Anteil
        mess_dauer,                     // minimale Wartezeit
        ueberlauf;                      // von T1

volatile uint32_t start_ereignis,       // Impulse zu Beginn der Messung
        end_ereignis,                   // Impulse am Ende der Messung
        start_zeit,                     // rel. Zeit: Beginn der Messung
        end_zeit,                       // Zeitpunkt der Auswertung
        mess_zeit,                      // genaue Zeit der Messung
        mess_ereignisse;                // Impulse der Messung


// Segmente an PORTD
#define AUSGABE_PORT    PORTD
#define AUSGABE_DDRX    DDRD
#define DIGIT_PORT      PORTC
#define DIGIT_DDRX      DDRC

// weitere Ein- Ausgänge für Schalter
#define SCHALTER_1      BIT(1)          // PORTB.1
#define SCHALTER_2      BIT(2)          // PORTB.2


// Digit-Ausgabe an PORTC: PC0 - PC4
#define DIGIT1          BIT(4)
#define DIGIT2          BIT(3)
#define DIGIT3          BIT(2)
#define DIGIT4          BIT(1)
#define DIGIT5          BIT(0)
#define DIGIT_LED       BIT(3)          // an PortB

uint8_t stellen[]  = {DIGIT1, DIGIT2, DIGIT3, DIGIT4, DIGIT5, DIGIT_LED};

// bei abweichendem Layout Segmente entsprechend zuordnen
#define SEGA    0x01
#define SEGB    0x02
#define SEGC    0x04
#define SEGD    0x08
#define SEGE    0x10
#define SEGF    0x20
#define SEGG    0x40
#define SEGP    0x80
#define DP_SEGM         BIT(5)        // auf PC5

#define ZAHL8   0xff-SEGP             // ALLE SEGMENTE AUSSER '.'
#define ZAHL0   ZAHL8-SEGG

uint8_t zeichensatz[] = {
    ZAHL8-SEGG,             // 0
    SEGB+SEGC,              // 1
    ZAHL8-SEGF-SEGC,        // 2
    ZAHL8-SEGE-SEGF,        // 3
    ZAHL8-SEGA-SEGD-SEGE,   // 4
    ZAHL8-SEGB-SEGE,        // 5
    ZAHL8-SEGB,             // 6
    SEGA+SEGB+SEGC,         // 7
    ZAHL8,                  // 8
    ZAHL8-SEGE,             // 9
    ZAHL8-SEGD,             // 'A'
    ZAHL8-SEGA-SEGB,        // 'b'
    ZAHL8-SEGB-SEGC-SEGG,   // 'C'
    ZAHL8-SEGA-SEGF,        // 'd'
    ZAHL8-SEGB-SEGC,        // 'E'
    ZAHL8-SEGB-SEGC-SEGD    // 'F'
};

uint8_t ausgabe_register[LED_STELLEN+1];        // register zur formatierten Ausgabe
uint8_t mpx_register[LED_STELLEN+1];            // aktuell angezeigte Digits


uint16_t lese_adc(uint8_t kanal)
{
  ADMUX =  BIT(REFS0)| kanal;                   // Vcc als Vref, rechtsbuendig
  ADCSRA |= BIT(ADSC);                          // starten
  while(ADCSRA & BIT(ADSC));                    // warten
  return(ADC);                                  // fertig  
}

void led_ausgabe(void)                          // ausgabe_register[] -> mpx_register[]
{
uint8_t n;
  cli();
  for(n=0; n <= LED_STELLEN; n++)
    mpx_register[n] = ausgabe_register[n];      // segmente 1:1 kopieren
  sei();
}


void loesche_ausgabe_register(void)             // löscht nur das ausgabe_register[]
{
uint8_t n;
  for(n=0; n <= LED_STELLEN; n++)
    ausgabe_register[n] = 0; 
}

void loesche_anzeige(void)                      // Anzeige direkt löschen
{
  loesche_ausgabe_register();                   // ausgabe_register[] löschen
  led_ausgabe();                                // und Anzeige auffrischen
}


void init_led(uint8_t muster)
{
uint8_t n;
  for(n=0; n < LED_STELLEN; n++) {
    ausgabe_register[n] = muster;               // Muster nach Vorgabe
  }
  led_ausgabe();                                // und anzeigen
}

void zeige_tout(void)
{
  ausgabe_register[0] = 0;
  ausgabe_register[1] = SEGD+SEGE+SEGF+SEGG;    // t
  ausgabe_register[2] = SEGD+SEGE+SEGC+SEGG;    // o
  ausgabe_register[3] = SEGD+SEGE+SEGC;         // u
  ausgabe_register[4] = SEGD+SEGE+SEGF+SEGG;    // t
  ausgabe_register[5] = 0;                      // LEDs ausschalten
  led_ausgabe();                                // anzeigen
}


// Routine zur Wandlung und Ausgabe eines Meßwertes
void zeige_x(float x, char typ)
{
int8_t i,j,k,dimension;
  k=0,dimension=3;
  if(x!=0) {
    if(typ == PERIODE) {
      x = 1/x;                                  // Kehrwert bilden
      dimension=5;                              // und in Sekunden als Dimension
    }
    else if(typ == DREHZAHL) {
      x *=60.0;                                 // UPM
      dimension=13 ;                     
    } 
    while(x >= 1e3) {x*=0.001;dimension--;}
    while(x<1.0) {x*=1000;dimension++;}
// x liegt im Bereich 1.0 - 999.9
    while(x>=10.0) {
      x*=0.1;
      k++;
    }
// x liegt im Bereich 1.0 - 9.999
    x += 5e-5;                                  // aufrunden
    if(x >= 10.0) {                             // und ggf. Bereich korrigieren
      x *= 0.1;
      k++;
      if(k > 2) {
        dimension--;
        k=0;
      }
    }
  }

#ifdef mHz_SPERRE
  if(dimension >= 4) {                          // mHz-Anzeige verhindern und auf
    dimension--;
    while(k++<=2) x *= 0.1;                     // Hz-Anzeige reduzieren, z.B.: 0.123 Hz
    k=0;
  }
#endif

  for(i = 0; i < LED_STELLEN; i++) {
    j = x;
    if(i == k) {                                // Dez.-Punkt ergänzen
      ausgabe_register[i] = (zeichensatz[j] | SEGP);    
    } else {
      ausgabe_register[i] = zeichensatz[j];     // ohne punkt
    }
    x-=j;
    x*=10.0;
  }
  ausgabe_register[LED_STELLEN] = f_dim[dimension];

// ggf. Betriebsspannung anzeigen
  if(typ == VCC) {                              // Vcc anzeigen im Format: "U 4.53"
    ausgabe_register[4] = ausgabe_register[2];  // wert nach rechts schieben
    ausgabe_register[3] = ausgabe_register[1]; 
    ausgabe_register[2] = ausgabe_register[0]; 
    ausgabe_register[0] = ZAHL0 -SEGA;          // 'U' anzeigen
    ausgabe_register[1] = 0;                    // ' ' anzeigen
    ausgabe_register[LED_STELLEN-1] &= ~SEGP;   // '.' ganz rechts löschen
    ausgabe_register[LED_STELLEN] = 0;          // Status-LEDs löschen
  }
  led_ausgabe();
}


void init_timer(void)
{
  ACSR = BIT(ACIC) | BIT(ACBG);                 // Eingang AIN1 mit Bandgap-Pegel auswerten
  TCCR1B =  BIT(CS10);                          // Timer1 starten
  OCR1B = MPX_TEILER;                           // 1/MPX_FREQUENZ
  TIMSK1 = BIT(ICIE1) |                         // ICP1 Int-enable
           BIT(TOIE1) |                         // OVFL Int-enable 
           BIT(OCIE1B);                         // COMP1B Int-enable für mpx-timing
}

ISR(TIMER1_COMPB_vect)                          // wird mit MPX_FREQUENZ aufgerufen
{
static uint8_t digit_nr, vt_counter, vt_intervall;
uint8_t temp;
  TIMSK1 &= ~BIT(OCIE1B);                       // Interrupt T1_COMPB abschalten
  OCR1B += MPX_TEILER;                          // Intervall nachladen
  sei();                                        // und gleich wieder freigeben

// ab hier unkritisches Timing
// zunächst alle Treiber abschalten
  if(SEGM_PEGEL == 1) {                         // alle Segmente ausschalten
    AUSGABE_PORT = 0x80;                        // auf 0 aber Pullup eingeschaltet lassen
    PORTC &= ~DP_SEGM;  
  } else {
    AUSGABE_PORT = 0xff;                        // oder auf 1
    PORTC |= DP_SEGM;
  }
  if(DIGIT_PEGEL == 1) {                        // alle Digit-Treiber ausschalten
    DIGIT_PORT &= ~(DIGIT1 | DIGIT2 | DIGIT3 | DIGIT4 | DIGIT5); // alle auf 0
    PORTB &= ~DIGIT_LED;
  } else {
    DIGIT_PORT |= DIGIT1 | DIGIT2 | DIGIT3 | DIGIT4 | DIGIT5;    // oder auf 1
    PORTB |= DIGIT_LED;
  }


// neue Segemente einschalten
  temp = mpx_register[digit_nr];                // Zugriff verkuerzen
  if(SEGM_PEGEL == 1) {
    if(temp & SEGP) PORTC |= DP_SEGM;
    AUSGABE_PORT = temp | 0x80;                 // 1:1
  } else {
    if(temp & SEGP) PORTC &= ~DP_SEGM;
    AUSGABE_PORT = (temp ^ 0xff) | 0x80;        // oder invertiert
  }

// aktuellen Digit Ausgang aktivieren
  if(digit_nr < LED_STELLEN) {
    if(DIGIT_PEGEL == 1)           
      DIGIT_PORT |= stellen[digit_nr];          // mit '1'
    else 
      DIGIT_PORT &= ~stellen[digit_nr];         // oder mit '0'
  } else {
    if(DIGIT_PEGEL == 1)           
      PORTB |= DIGIT_LED;                       // mit '1'
    else    
      PORTB &= ~DIGIT_LED;                      // oder mit '0'
  }


  digit_nr++;                                   // nächste Stelle vorbereiten
  if(digit_nr > LED_STELLEN) digit_nr = 0;
  mess_dauer++;
  if(led_cnt) {                                 // dez.-p. ganz rechts blinken lassen
    led_cnt--;                                  // mit 1/2 Messdauer
    mpx_register[LED_STELLEN-1] |= SEGP;    
  } else {  
    mpx_register[LED_STELLEN-1] &= ~SEGP;       // wieder abschalten
  }

// Impulse vom Vorteiler erfassen
  if(PCIFR & 1) {                               // Flanke erkannt
    vt_counter++;                               // Flanken zaehlen
    PCIFR |= 1;                                 // und flag loeschen
  }
  vt_intervall++;
  if(vt_intervall >= MPX_FREQUENZ/10) {         // 10 Messungen/s
    vt_cnt = vt_counter;                        // Wert uebernehmen
    vt_counter = vt_intervall = 0;              // und neu messen
  }

  TIFR1 |= BIT(OCF1B);
  TIMSK1 |= BIT(OCIE1B);                        // Interrupt T1_COMPB wieder freigeben

}

ISR(TIMER1_OVF_vect)            
{
  ueberlauf++;                                  // Ueberlaeufe von T1 ergeben obere 16bit der Messzeit
  t1_ovf_sync = 1;
}

#ifndef ext_CAPTURE                             // ggf. durch Assemblerroutine ersetzen
ISR(TIMER1_CAPT_vect)                           // Eingangsimpulse mit genauem Zeitpunkt erfassen
{
static unsigned long count;                     // Impulse per Interrupt                
  count++;
  if(mess_status == AUSLESEN) {                 // Ergebnisse synchron zum Eingangsimpuls auslesen+ablegen
    end_ereignis = count;                       // Anzahl der Impulse lesen
    zeit_low = ICR1;                            // capture-reg lesen: untere 16bit
    zeit_high = ueberlauf;                      // dazu die oberen 16bit
    if((TIFR1 & BIT(TOV1)) && (zeit_low < 0x8000))    // evtl. Ueberlauf T1 noch offen?
      zeit_high++;                              // nur, wenn capture-int + overflow-int gleichzeitig !
    mess_status = AUSWERTEN;                    // Daten fertig fuer Auswertung
  }

}
#endif

void t1_ovf_wait(uint16_t time)                 // time * 5,5 ms
{
  while(time--) {   
    t1_ovf_sync = 0;                            // bis Zeit abgelaufen
    while(!t1_ovf_sync);                        // 5,5 ms abwarten
  }    
}

uint8_t ee_read_byte(uint16_t adr)
{
  while(EECR & BIT(EEPE));      // abwarten
  EEAR  = adr;
  EECR |= BIT(EERE);            // lesebit setzen
  return(EEDR);
}

void ee_write_byte(uint8_t data, uint16_t adr)
{
  if(data != ee_read_byte(adr)) {
    cli();
    EEAR = adr;
    EEDR = data;
    EECR  = BIT(EEMPE);         // master schreibbit setzen, EEPMx löschen
    EECR |= BIT(EEPE);          // schreibbit setzen
    sei();
  }
}

void ee_write_word(int16_t data, uint16_t adr)
{
  ee_write_byte((uint16_t)(data)%256, adr);
  ee_write_byte((uint16_t)(data)/256, adr+1);
}

int16_t ee_read_word(uint16_t adr)
{
int16_t temp;
  temp = ee_read_byte(adr);
  temp += ee_read_byte(adr+1) * 256;
  return(temp);
}


/* Test auf gedrückte Taste an ADC7 und externes Referenzsignal von 1Hz.
Wenn beides vorliegt, wird die Referenzfrequenz 'f_clock' um den 
Korrekturwert 'f_offset' angepasst. 'f_offset' wird im EEPROM gespeichert
und nach einem Reset erneut geladen und mit F_CLOCK verrechnet.
*/

void teste_abgleich(void)
{
static uint8_t temp_alt;                        // zur Ermittelung der aktiven Flanke
uint16_t temp;                                       
int16_t f_offset;                               // Abweichung zu F_CLOCK
  temp = lese_adc(7);                           // Pegel an ADC7 messen
  if(temp <= 100) temp = 1;                     // kleiner Pegel = Taste gedrückt
  else temp = 0;                                // sonst: Taste offen
  if((temp ^ temp_alt) & temp) {                // Taste wurde gedrückt
    if(frequenz > 0.999 && frequenz < 1.001) {  // Frequenzbereich eingrenzen
      f_offset = mess_zeit - F_CLOCK;           // Differenz ist-soll ermitteln
      f_clock = (float) (F_CLOCK + f_offset);   // korrigierter Takt
      init_led(0xff);                           // "88888" als Fertigmeldung
      ee_write_word(f_offset, EE_ADR_OFFSET);   // Korrekturwert speichern
      messwert_vorhanden = 0;                   // nächte Messung verwerfen
    }
  }
  temp_alt = temp;                              // fürs nächste Mal merken
}

void init_ports(void)
{
  DDRB  &= ~(SCHALTER_1 | SCHALTER_2);          // als Eingänge
  PORTB |= (SCHALTER_1 | SCHALTER_2);           // Pullup einschalten

  if(SEGM_PEGEL == 1) {                         // alle Segmente ausschalten
    AUSGABE_PORT = 0;                           // auf 0
    PORTC &= ~DP_SEGM;
  } else {
    AUSGABE_PORT = 0xff;                        // auf 1
    PORTC |= DP_SEGM;
  }
  AUSGABE_PORT |= 0x80;                         // pullup fuer AIN1
  AUSGABE_DDRX = 0x7f;                          // auf Ausgang für alle Segemente
  PCMSK0 |= BIT(PORTB0);                        // Flankenerkennung von Vorteiler freigeben

  if(DIGIT_PEGEL == 1) {                        // alle Digit-Treiber ausschalten
    DIGIT_PORT &= ~(DIGIT1 | DIGIT2 | DIGIT3 | DIGIT4 | DIGIT5);  // alle auf 0
  } else {                                                        // oder 
    DIGIT_PORT |= (DIGIT1 | DIGIT2 | DIGIT3 | DIGIT4 | DIGIT5);   // alle auf 1
  }
  DIGIT_DDRX = (DIGIT1 | DIGIT2 | DIGIT3 | DIGIT4 | DIGIT5);      // als Ausgänge
  DDRC |= DP_SEGM;   
  DDRB |= DIGIT_LED;
  init_led(SEGG);                               // "-----" ins mpx-register
  ADCSRA = BIT(ADEN) + 0x07;                    // ADC einschalten mit Teiler /128
}


// Zum Aufwecken über Vorteilereingang
ISR (PCINT0_vect)
{
  PCICR &= ~BIT(PCIE0);                         // PCINT PB0 wieder sperren
  PCICR &= ~BIT(PCIE2);                         // PCINT PD7 wieder sperren
}                                               // µC läuft wieder

// Zum Aufwecken über Signaleingang
ISR (PCINT2_vect)
{
  PCICR &= ~BIT(PCIE0);                         // PCINT PB0 wieder sperren
  PCICR &= ~BIT(PCIE2);                         // PCINT PD7 wieder sperren
}                                               // µC läuft wieder

void schlafen_legen(void)
{
  TCCR1B = 0;                                   // timer abschalten: keine Interrupts mehr
  init_ports();                                 // Grundzustand: Anzeige abgeschaltet
  ADCSRA = 0;                                   // ADC ausschalten
  ACSR = 0;                                     // bandgap abschalten

// Leckströme durch Eingänge mit pullup vermeiden daher:
  PORTB &= ~(SCHALTER_1 | SCHALTER_2);          // Pullup abschalten
  DDRB  |= (SCHALTER_1 | SCHALTER_2);           // und als Ausgänge auf '0' setzen

  PCMSK2 |= BIT(PORTD7);                        // PCINT2 zu Aufwecken aktivieren
  PCIFR |= BIT(PCIF2);                          // dazu das Flag löschen
  PCICR |= BIT(PCIE2);                          // und ISR freigeben
  PCMSK0 |= BIT(PORTB0);                        // Flankenerkennung von Vorteiler freigeben
  PCIFR |= BIT(PCIF0);                          // dazu das Flag löschen
  PCICR |= BIT(PCIE0);                          // und ISR freigeben

  MCUCR = BIT(BODS) | BIT(BODSE);               // nur bei neueren P-Typen
  MCUCR = BIT(BODS);                            // Brown Out Detector abschalten
  SMCR = BIT(SE) | BIT(SM1);                    // mit Power-Down
  sleep_cpu();                                  // stoppen und auf Eingangsimpuls warten

  init_ports();                                 // Neustart nach Aufwachen wie nach Reset
  init_timer();
}

int16_t vcc_filter()                            // Vcc kontinuierlich messen und filtern
{
static int16_t summe;
   summe -= summe/VCC_FILTER;
   summe += lese_adc(14);
   return summe/VCC_FILTER;
}

// Hauptprogramm
int main(void)
{
uint16_t timeout;
int8_t  sleep_cnt = 0, temp;
  CLKPR = 0x80; CLKPR = 0x0;                    // CKDIV auf 0; voller CPU-Takt
  init_ports();

  mess_status = MESSEN;                         // zuerst messen
  messwert_vorhanden = 0;                       // 1.Ergebnis unterdruecken, da es falsch ist
  f_clock = F_CLOCK + ee_read_word(EE_ADR_OFFSET);  // eff. Referenzfrequenz
  init_timer();
  sei();

  for(;;) {
    vcc_filter();
// Schalter lesen
    temp = ((PINB ^ 0xff) & (SCHALTER_1 | SCHALTER_2)) >> 1;
    if(temp == VCC) {
      zeige_x((V_BANDGAP*ADC_MAX)/vcc_filter(),temp); // Vcc anzeigen
      t1_ovf_wait(60);                          // ~330 ms
      mess_status = MESSEN;
    }

    if(vorteiler_aktiv) {
      if(vt_cnt <= 5) {                         // Vorteiler unterhalb ca. 25 kHz abschalten
        ACSR |= 1<<ACIC;                        // analog Komparator verwenden
        mess_status = AUSLESEN;                 // aktuelle Ergebnisse lesen
        messwert_vorhanden = 0;                 // aber nicht anzeigen
        vorteiler_aktiv = 0;                    // wieder auf ANA-COMP umschalten   
      }
    } else {
      if(vt_cnt > 10) {                         // Vorteiler ab ca. 50 kHz zuschalten
        vorteiler_aktiv = 1;
        ACSR &= ~1<<ACIC;                       // ICP aktivieren
        mess_status = AUSLESEN;                 // aktuelle Ergebnisse lesen
        messwert_vorhanden = 0;                 // aber nicht anzeigen
      }  
    }
    cli();
    if(mess_status == MESSEN  && mess_dauer >= MESSZEIT) {
      mess_status = AUSLESEN;                   // Auswertung starten
      mess_dauer = 0;                           // neue Messzeit abwarten
    }
    timeout = mess_dauer;                       // für fehlendes Eingangssignal
    sei();

    if(mess_status==AUSWERTEN) {
      end_zeit = zeit_high*0x10000 + zeit_low;
      mess_zeit = end_zeit - start_zeit;        // Zeit-Differenz bilden
      start_zeit = end_zeit;                    // neue startzeit merken
      mess_ereignisse = end_ereignis - start_ereignis;  // Impuls-Differenz
      start_ereignis = end_ereignis;            // fuers naechste Intervall
      if(messwert_vorhanden) {                  // und gültigen Wert anzeigen
        frequenz = ((float)mess_ereignisse * f_clock) / mess_zeit;  // Frequenz berechnen
        if(vorteiler_aktiv) frequenz *= VORTEILER_FAKTOR;
        if(temp != VCC) zeige_x(frequenz,temp); // Frequenz, Periode oder Drehzahl anzeigen
        led_cnt = LED_EIN;                      // LED-Zeit vorgeben + update der Anzeige
        teste_abgleich();                       // nach gültiger Messung ggf. neu abgleichen
      }
      else messwert_vorhanden = 1;              // sperre wieder aufheben
      mess_status = MESSEN;                     // neue Messung beginnen
    }
    if(timeout > (MPX_FREQUENZ * TOUT)) {       // nach 10 Sekunden Timeout
      zeige_tout();                             // " tout" anzeigen
      messwert_vorhanden = 0;                   // nächstes Ergebnis verwerfen
      mess_status = MESSEN;
      cli();
      mess_dauer = 0;                           // und neue Zeit abwarten
      sei();
      sleep_cnt++;
      if(sleep_cnt >= ABSCHALTZEIT) {           // nach typ. 60s
        sleep_cnt = 0;
// ausmaskieren, wenn Power Down nicht stattfinden soll
        schlafen_legen();                       // power down mit Stromaufnahme < 1 mA.
      }
    }
  } // for
}
