/* uhr-os07.c
Betriebssystem für Funk-Uhr
(c) 2004 by Malte Marwedel, www.marwedels.de/malte, m.marwedel@onlinehome.de
Diese Datei darf frei kopiert und compiliert werden.
It's allowed to copy and compile this file freely.
Änderungen an der Datei sind erlaubt, solange kenntlich gemacht wird,
dass es sich dann nicht mehr um die original Datei handelt.
It's allowed to modify this file as long it is mentioned that it isn't the
original file any longer.
Es muss immer erkennbar sein, dass die original Datei von mir stammt
It must always be recognizable that the original file is written by me
Die Benutzung des Programmes erfolgt auf eigene Gefahr, ich hafte nicht für
Schäden jeglicher art, falls dieses Programm welche verursachen sollte.
The use of this program it at your own risk, I'm not responsible for damage of
any kind, if this program should cause which.
Programm für gcc, MCU: ATMEGA16, 4MHZ Quarz
Program for gcc, MCU ATMEGA16 with a frequency of 4MHZ
Zum Kompilieren habe ich winavr20040404 verwendet, Optimierung -Os
I've used winavr20040404 for compiling, optimization -Os


Bedeutung der Status LEDs:
1: RS232 Komunikation aktiv
2: IR Komunikation aktiv
3: DCF77 Empfang aktiv
4: DCF77 Error
5: Display zeigt Datum an
6: Display zeigt nächste Weckzeit an
7: Ein Wecker wird innerhalb der nächsten 24 Std Alarm schlagen
8: Ein Wecker wird innerhalb der nächsten 2 Std Alarm schlagen

Pin Belegung des MCU:
Pin A0 : input, Taster 1
Pin A1 : input, Sensor 1
Pin A2 : input, Taster 2
Pin A3 : input, Sensor 2
Pin A4 : input, Taster 3
Pin A5 : input, nicht belegt
Pin A6 : input, Taster 4
Pin A7 : input, nicht belegt
Pin B0 : output, 1. Ziffer
Pin B1 : output, 2. Ziffer
Pin B2 : output, 3. Ziffer
Pin B3 : output, 4. Ziffer
Pin B4 : output, Status LEDs 5,6,7,8
Pin B5 : input, MOSI
Pin B6 : input, MISO
Pin B7 : input, SCK
Pin C0 : output, Segment a
Pin C1 : output, Segment f
Pin C2 : output, Segment e
Pin C3 : output, Segment b
Pin C4 : output, Segment g
Pin C5 : output, Segment c
Pin C6 : output, Segment d
Pin C7 : output, Status LEDs 1,2,3,4
Pin D0 : input, RS232 empfangen
Pin D1 : output, RS232 senden
Pin D2 : input, DCF 77
Pin D3 : input, IR Empfänger, TSOP 1736
Pin D4 : output, IR Sender, Daten Bit
Pin D5 : output, IR Sender, Frequenz für Modulation
Pin D6 : output, Beeper/Lautsprecher (für Wecker)
Pin D7 : output, LEDs für Punkte zwischen Std und Min Anzeige

Belegung der Tasten:
Taster 1: Nächste Weckzeit anzeigen
Taster 2: Datum Anzeigen
Taster 3: IR oder Rs232 Modus verlassen
Taster 4: Zuerst Taster 4 gedrückt halten, dann gleichzeitig Taster 1 drücken,
          Taster 4 loslassen und dann Taster 1 loslassen: Der nächse in 24 Std
          auftretene Wecker wird ignoriert
Beliebige Taste: Wenn ein Wecker gerade klingelt, wird er ausgeschaltet

Genauigkeit des Weckers ohne DCF Syncronisation:
Die Uhr benutzt Timer2, um ohne DCF77 Syncronisation weiter zu zählen.
Der Prescaler beträgt 32, der Comparewert 244, beim erreichen wird der Timer
automatisch zurückgesetzt
Bei 4MHZ Takt ergibt sich daraus:
4000000:64:244 = 512,295082
Der Timer2 wird also 512,295082 mal pro Sekunde aufgerufen. Die Uhr geht jedoch
davon aus, dass der Timer2 genau 512 mal pro Sekunde aufgerufen wird. Somit geht
die Uhr prinzip-bedingt schon mal vor. Diese Abweichung beträgt genau:
1/512*512,295082=1,000576332 -> Die Uhr zählt pro Sekunde 0,000576332 Sekunden
zuviel. Über 24 Std macht dies:
0,000576332*60*60*24 = 49,79 Sekunden. Rechnerisch müsste die Uhr also ungefähr
50 Sekunden pro Tag zu schnell gehen. Hinzu kommen noch mögliche Abweichungen
des Quarzes nach oben und unten von der Idealfrequenz.
Mein Praxistest zeigte, dass die Uhr mal etwas zu schnell und mal etwas zu
langsam ging. So ging meine Uhr nach 24 Std ohne DCF Signal 16 Sec nach, nach
48 Std waren es 17 Sec. Zwischenzeitlich ging die Uhr jedoch bis zu 27 Sec vor!
Wahrscheinlich ist der von mir verwendete Quarz etwas temperaturabhängig.
Abschließend muss noch darauf hingewiesen werden, dass während des IR Sendens
der Timer2 angehalten wird und sich daraus weitere Abweichungen ergeben können.
Um die Abweichungen somit nicht zu groß werden zu lassen, wird die IR und RS232
Übertragung nach 15 Minuten automatisch abgeschaltet um das DCF
Signal wieder zu empfangen.

Programm Version 3 ist die erste Version, die alle zu Beginn geplanten Features
enthält.
Diese Features sind:
- Das RS232 Menu
- Anzeigen von Daten auf den LED Anzeigen
- DCF Empfang
- Fehlerkorektur für DCF Empfang (Logische Überprüfung, Checkbits, zweimaliger
  Empfang mit Überprüfung, Startbit)
- Wecker abfrage
- Watchdog (benötigt 12 Byte Flash)
- Taster Abfrage
- Infrarot Übertragung

Programm Version 4 hat gegenüber der Version 3 folgende Änderungen erhalten:
- 1 Byte mehr RS232 Empfangsbuffer
- Code Optimierung
- Auslagerung einiger constanten Strings vom Flash in den EEprom
- Beheben einiger Bugs
- Nachts sind die LEDs nicht so hell wie tagsüber
- einige kleine Verbesserungen

Programm Version 5 hat gegenüber der Version 4 folgende Änderungen erhalten:
- Bug korrigiert, einstellen des Weckers beginnt immer mit Weckzeit 1, vorher
  war es die zuletzt von der SW abgerufene
- Verbessern des DCF Empfangs
- Feature um eine Weckzeit vorher zu deaktivieren
- Code Optimierung
- während des IR sendens bleibten die unteren 4 Status LEDs nicht mehr dunkel
- IR Senden verbessert
- Per IR können die beiden nach außen geführten A/D Ports abgefragt werden

Programm Version 6 hat gegenüber der Version 5 folgende Änderungen erhalten:
- Strings aus dem EEProm zuück in den Flash, da mit dem ATMGEA16 wieder genug
  Speicher zur Verfügung steht
- ADMUX wird vor dem Schreiben überprüft (da beim ATMEGA16 alle 8 Bit schreibbar
  sind)
- Länge eines Monats ist bekannt, auch Schalttjahre werden berücksichtigt
  (benötigte insgesammt 158 Byte Flash)
- Beim LCD ist es möglich nur die 1. Zeile zu übertragen -> Mehr Geschwindigkeit
- Automatisches Berechnen des Wochentages (c.a. 182 Byte Flash)

Programm Version 7 hat gegenüber der Version 6 folgende Änderungen erhalten:
- Helligkeitsregelung der LEDs (verbessert).
- Blinken der DCF77 Feler LED bei mehr als n Minuten ohne Syncronisation.
- Reboot Counter (wird im EEProm gespeichert).
- Einstellungen im EEProm speicherbar.
- Uptime wird im EEProm gespeichert, damit auch nach einem Stromausfall weiter
  gezählt wird.
- Sensor aufzeichnungs Funktion.
- Verbessertes Wecker deaktivieren: Vorheriges Abstellen deaktiviert einmalig
  klingelnde Wecker so, als ob sie normal geklingelt hätten.
- Small Bug found & fixed: Sollte ein Wecker mehrfach klingeln, so wurde die
  Wecklautstärke erst nach einer Minute über das Anfangsniveau erhöht.
- Optimieren des RS232 Sendens, Leerzeichen am Ende einer Zeile werden nicht
  mehr übertragen.
- Analog Comparator wird deaktiviert da er nicht benötigt wird,
  dies spart Strom.
- Annpassen der Meldungen welcher Reset aufgetreten ist an den ATMGEA16
  Erkannte Reset Arten: W: Watchdog; E: Externer; P: Power On; B: Brown Out

Geplante Features für Version 7b:
- Verbesserte Übersichtlichkeit durch Verteilen des Quelltextes auf mehrere
  Dateien

Bekannte Bugs/Probleme:
- DCF überträgt nur zweistellige Jahreszahlen. Intern wird jedoch vierstellig
  gerechnet. In 95 Jahren wird das Jahr somit falsch angezeigt werden :-)
  (nicht korrigierbar)
- Prinziepiell besteht das Risiko, dass die EEProm Adressen nicht richtig
  beschrieben werden. Dies könnte auftreten, wenn innerhalb eines
  Schreibbefehles ein Interrupt ausgelöst wird. In der Praxis ist dies bei mir
  aber noch nicht vorgekommen.

Welcher Programmteil benötigt wieviel Speicher?
Die Size Angaben über jeder Funktion spiegeln das grobe Verhältnis wieder.
"Size" mal zwei ergibt die ungefähre Größe des benötigten Flashspeichers in Byte
Die Initialisierung von globalen Variablen und Konstanten wird hierbei nicht
berücksichtigt.

Tasten Daten:
Die Tasten befinden sich am Analog-Eingang. Analoge Werte unter 800 werden als
"Taste gedrückt" identifiziert.
Die genaue Schwelle ist mit der Konstante "tastschwelle" einstellbar

Fuse Bytes:
Der ATMEGA16 besitzt zwei Fusebytes. Bei meiner Uhr sind die wie folgt
eingestellt:
Low Fusebyte:10111111
Bedeutet: Brownout Detektor mit 2,7V aktiviert, externer Quarz, große
Verzögerung beim Powerup.
High Fusebyte:11011001
Bedeutet: JTAG deaktiviert, serielles Programmieren aktiviert, EEProm wird beim
Programmieren gelöscht, kein Bootloader, Quarz Schwingsstärke reduziert

*/

#include <io.h>
#include <inttypes.h>
#include <interrupt.h>
#include <sig-avr.h>
#include <string.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

//Um zu wissen für was uint8_t u.s.w. steht siehe inttypes.h
typedef uint8_t  u08;
typedef int8_t   s08;//derzeit unbenutzt
typedef uint16_t u16;
typedef int16_t  s16;
typedef uint32_t u32;
typedef int32_t  s32;//derzeit unbenutzt

//Variablen für die Zeit: 8 Byte
u08 minutes, hours;
u08 dayofweek = 1, day = 1, month = 1;
u16 year = 2004;
u16 volatile presec;
u08 volatile seconds;
u08 isschaltjahr;
//Variablen für die Statistik: 7 Byte
u32 uptimeminutes;    //u32 benötigt gegenüber u08 62 Byte mehr Flash
u08 rebootstat; //Anzahl der Reboots
u16 uptimedays; //Wieviele Tage die urs seit der Programmierung gelaufen ist
u16 primaryuptimedays; //Verändert sich nicht, wird nach dem Start aus dem EEProm gelesen
//Variablen für die Anzeige: 9 Byte
u08 volatile segment[4], statusleds = 0xff;
u08 dispmode; //0 = zeit, 1 = datum, 2 = wecker
u08 punkteblinken;
u08 showwhour,showwminute;
u08 volatile ledhelligkeit = 4; //4: Hell, jede andere Zahl: dunkel
u08 dcferror = 1, dcferrorminutes = 255;
//Variabeln für Kommunikation: 47 Byte
u08 commtype;//0 = keine kommunikation, 1 = RS232 Communikation, 2 = IR Communikation
u08 rs232rausstring[36];
u08 volatile rs232reinstring[6];
u08 submenu232,newmenu;
u08 submenuir;
u08 volatile rs232echo;
//Variablen für die IR kommunikation: 7 Byte
u08 volatile empmode; //0: inaktiv, 1: es wird empfangen, 2: es wird gesendet
u08 volatile empfangir = 0xff;
u08 empdurch,precalc,empfang;
u08 volatile senddurch;
u08 lcdupdatecomplexity;
//Variablen für A/D Wandler: 19 Byte
u16 volatile adwerte[8] = {1023,1023,1023,1023,1023,1023,1023,1023};
u16 cconv; //current convertion
u08 convno;//convertion number
//Variablen für die A/D Aufzeichnung
u16 eepromindex;//Welcher Speicherbereich beschrieben werden soll
u32 adtempa, adtempb; //Werte zum Zwischespeichern
u08 recordermode;
u08 recorderspeed; //0 = aus
u08 captureno;
u32 lastcapture_uptimeminutes;
//Variablen für Wecker: 13 Byte
u08 whour,wminute, wdaysofweek, wsettings;
u08 wselected = 1;
u08 volatile wactive = 50; //Stärke des Klingelns (0= aus, 155= dauer)3
u08 weckdata[4];
u08 wdisabled;//deaktiviert den Wecker für die laufende Minute
u08 wdisabled24; //deaktiviert den nächsten auftretenden Wecker (deaktivierung geht nur für den nächsten Wecker innerhalb der nächsten 24 Std)
u08 wleds; //0: größer 24 Std 1: innerhalb 24 Std 2: innerhalb 2 Std
//Variablen für Sicherheit: 2 Byte
u08 eepromaway = 10;
u08 commtimeoutminutes;
//Variablen für DCF 77 Empfang: 18 Byte
u08 fastsample;//wird nur in timer2 Interrupt benötigt, daher kein volatile
u08 volatile currentdcfval;
u08 lastuptime,lastdowntime;//zur Bestimmung, ob 0,1 oder neue Minute
u08 dcfposition = 70;   //70 = nicht syncron
u08 rminutes[2],rhours[2],rdayofweek[2],rday[2],rmonth[2],ryear[2];
u08 checkbit;//um die empfangenen Daten zu testen
//Variablen für die Tasten: 1 Byte
u08 lastkeys; //zuletzt gedrückte Tasten
//Variable für Delays: 2 Byte
u16 volatile warteschleife;//temonäre Variable

// Konstanten
#define commtimeup 15      //nach 15 Minuten wird der Kommunikationsmodus automatisch verlassen
#define tastschwelle 800   //je größer der Wert (min: 0; max: 1023), desdo empfindlicher die Taster der Uhr
#define ledsdunkel 22      //ab 22 Uhr sind die LEDs dunkler
#define ledshell 6         //bis 6:59 bleiben die LEDs dunkler
#define maxweck 10         //es gibt maximal 10 Wecker (mindestens: 1, maximal:60);
#define maxweckx maxweck+1 //wird intern häufig benötigt
#define newledspeed 2      //Da der Timer Interrupt 2 mal häufiger erfolgt
#define RS232code 1234     //Der Code startet die RS232 Kommunikation, darf maximal 5 Stellig sein.
#define blinkdcferror 10   //Nach wie vielen Minuten (0..255) ohne DCF77 Signal die LED anfangen soll zu blinken
#define defaultweckh 6     //Wert der Stunde der beim erstmaligem Einschalten der Uhr in jeden Wecker geschrieben werden soll
#define defaultweckm 30    //Wie oben jedoch für die Minuten (die Wecker sind standartmäßig inaktiv)
#define maxeeprommem 512   //Maximale EEProm Größe, Bei ATMEGA16 512 Byte, bei ATEMGA32 1024 Byte
#define free_eeprom_for_ad (maxeeprommem -maxweck*4 -9) //Um zu ermitteln wie Groß der Speicherplatz für die A/D Daten ist
//Vordefinieren der Wecker: jeder Wecker steht auf 6:30 Uhr und ist an jedem Tag
//aktiv, der Wecker selbst ist jedoch aus, jeder Wecker benötigt 4 Byte.

u08 firsteeprombyte __attribute__ ((section (".eeprom"))) = {0}; /*Könnte bei Programmstart leicht überschrieben werden,
                                                                    daher nicht verwenden (siehe ATMEL Datenblatt) */
u08 weckzeiten[maxweck*4] __attribute__ ((section (".eeprom"))) = {0xff,0xff,0xff,0xff}; //Speicherplatz für die Wecker
u08 rebootnumber __attribute__ ((section (".eeprom"))) = {0};     //Speicherplatz für die Reboot Nummer
u16 globalupdays __attribute__ ((section (".eeprom"))) = {0};     //Speicherplatz für die Globaluptime
u08 globalconfig __attribute__ ((section (".eeprom"))) = {0};     //LED Blinken, RS232 Echo speichern
u08 adrecconfigeep  __attribute__ ((section (".eeprom"))) = {1};  //Mit welchen Einstellungen aufgezeichnet wurde
u08 adrecspeedeep   __attribute__ ((section (".eeprom"))) = {0}; //Mit welchen Einstellungen aufgezeichnet wurde
u08 adrecstarttime[2] __attribute__ ((section (".eeprom"))) = {0}; //Wann die Aufzeichnung gestartet wurde
u08 adrecdata[free_eeprom_for_ad]    __attribute__ ((section (".eeprom"))) = {0};     //Die eigentlichen Daten

//Prozedur Übersicht:
void adrecorder(void);
void eeprom_writeconfig_ad(void);
void eeprom_clear_ad(void);
u16  eeprom_free_ad(void);
u16  eeprom_size_ad(void);
u08  eeprom_write_ad(u16 adress,u16 data1,u16 data2);
s16  eeprom_read_ad(u16 adress,u08 adport);
void eeprom_daysup_update(void);     //Size:      Schreibt in das EEprom, wie lange die Uhr (in Tagen) bereits läuft
void speichereconfig(void);          //Size:      Streibt in EEProm ob die Punkte blinken sollen und ob ein RS232 Echo gesendet werden soll oder nicht
void initsystemeepromactions(void);  //Size:      Laden der EEProm Einstellungen
void calcweekday(void);              //Size:      Berechnet den aktuellen Wochentag aus dem Datum
void calcschaltjahr(void);           //Size:      Berechnet, ob das Jahr ein Schaltjahr ist
SIGNAL(SIG_OVERFLOW1);               //Size: 89   Routine zum Überprüfen von IR Daten
SIGNAL(SIG_ADC);                     //Size: 64   Aktualisiert ständig die A/D Werte. Jeder Wert wird c.a 9 mal pro Sek aktualisiert, dabei wir ein Mittelwert aus 32 Messungen verwendet
void zumsenden(u16 zeichen);         //Size: 147  Sendet ein zeichen per IR Schnittstelle
void tastentesten(void);             //Size: 86   Führt alle Aktionen aus, die die Tasten der Uhr betreffen
void testallwecker(void);            //Size: 93   Überprüft, ob ein Wecker klingen soll, die LEDs an/aus sein sollen, u.s.w.
u16  getreamingtime(u08 weckno);     //Size: 104  Gibt die Zeit bis zum Klingeln des Weckers in Minuten zurück (bis 24 Std)
void smallnottimecriticalcode(void); //Size: 68   Wird anstelle von "nop" in Warteschleifen ausgeführt. Darf daher selber keine Warteschleifen oder langen Code enthalten
void savewecktime(u08 weckno);       //Size: 45   Speichert die Weckzeit im EEprom unter der angegebenen Nummer
void getwecktime(u08 weckno);        //Size: 37   Liest die unter der Nummer im EEprom gespeicherte Weckzeit
SIGNAL(SIG_OUTPUT_COMPARE2);         //Size: 117  Zählt die Sekunden und steuert das Display, wird 256 mal pro Sekunde aufgerufen
SIGNAL(SIG_UART_RECV);               //Size: 44   Wenn ein Zeichen von der Seriellen Schnittstelle empfangen wurde, je nach Einstellung wird auch ein Echo gesendet
void rs232sendstring(void);          //Size: 23   Eendet den rs232rausstring an die Serielle Schnittstelle
void wordtostr(u32 nummer, u08 digits, u08 strposition); //Size: 64  Schreibt die "nummer" an die "strposition" in rs232rausstring. "digits" gibt die Anzahl der Stellen an
s16  strtoword(void);                //Size: 53   Convertiert angekommene RS232 Signale in Nummern
void initsystem(void);               //Size: 55   Wird nach einem Reset/ Power On ausgeführt, setzt Register u.s.w.
void setsegments(void);              //Size: 142  Berechnet die anzuzeigenden LEDs des Displays aus integer werten
void updatetime(void);               //Size: 121  Zählt die Uhr manuell weiter, muss mindestens alle 60sec einmal aufgerufen werden
void newscreen(void);                //Size: 118  Sendet an RS232 eine Leerzeile, eine Zeile voll Bindestriche und die aktuelle Uhrzeit mit Datum
void makewstatus(u08 weckno);        //Size: 84   Formatiert rs232rausstring so, dass er den aktuellen Wecker wiedergibt, der wird als Wecker Nummer "weckno" angezeigt
void strcpypandsend(u08 startstring, u08 numofstrings); //Size:19 Ruft mehrfach strcpypshort() und anschließend rs232sendstring() auf.
void strcpypshort (u08 stringno);    //Size: 12   Ruft strcpy_p() auf. Da das eine Argument immer das Gleiche ist, wird so beim Funktionsaufruf die Übergabe eines Argumentes gespart
void show232menu(void);              //Size: 223  Sendet je nach Untermenu die richtigen Texte per RS232
void rs232com(void);                 //Size: 383  Überprüft die RS232 Eingabe, legt das neue Untermenü fest, und reagiert auf die Eingaben (z.B. kann es die Uhrzeit setzen)
void warte(u16 zeit);                //Size: 30   Warteschleife für das IR Senden
void sendirstring(void);             //Size: 129  Sendet einen String per IR
void showirmenu(void);               //Size: 218  Stellt die Daten zusammen, die per IR gesendet werden sollen
void ircom(void);                    //Size: 340  Wie rs232com, jedoch für das IR LCD Interface
void analyzedcf77(void);             //Size: 417  Decodiert die ankommenden DCF77 Signale, überprüft sie auf Richtigkeit und setzt die Uhr
int main( void );                    //Size: 111  Ruft je nach Bedarf die verschiendenen Prozeduren auf


void adrecorder(void) {
if (eeprom_free_ad() == 0) {  //wenn kein Speicherplatz, dann
  recorderspeed = 0;          //stoppe die Aufzeichnung
}
 if (recorderspeed != 0) { //Wenn Aufzeichnung aktiv
   if ((uptimeminutes-lastcapture_uptimeminutes) != 0) { //Neue Minute
     lastcapture_uptimeminutes = uptimeminutes;
     adtempa += adwerte[1];
     adtempb += adwerte[3];
     captureno++;
     if (captureno == recorderspeed) { //Mal einen Wert schreiben
     //Die folgenden Divisionen kosten Speiche und Rechenzeit...
       eeprom_write_ad(eepromindex,adtempa/recorderspeed,adtempb/recorderspeed);
       eepromindex++;
       adtempa = 0;
       adtempb = 0;
       captureno = 0;
      }
   }
 }
}

void eeprom_writeconfig_ad() {
  if (eeprom_read_byte(&adrecconfigeep) != recordermode) {
    eeprom_write_byte(&adrecconfigeep,recordermode);
  }
  if (eeprom_read_byte(&adrecspeedeep) != recorderspeed) {
    eeprom_write_byte(&adrecspeedeep,recorderspeed);
  }
  if (eeprom_read_byte(&adrecstarttime[0]) != hours) {
    eeprom_write_byte(&adrecstarttime[0],hours);
  }
  if (eeprom_read_byte(&adrecstarttime[1]) != minutes) {
    eeprom_write_byte(&adrecstarttime[1],minutes);
  }
}

void eeprom_clear_ad(void) {
  u16 temp01;
  for (temp01 = 0;temp01 < free_eeprom_for_ad;temp01++) {
    asm volatile("wdr"); //Die Schleife könnte zu lange dauern
    if (eeprom_read_byte(&adrecdata[temp01]) != 0) {
      eeprom_write_byte(&adrecdata[temp01],0);
    }
  }
  eepromindex = 0; //Schließlich ist der EEProm jetzt ja wieder leer
}

u16 eeprom_free_ad(void) {
  return (eeprom_size_ad() - eepromindex);
}

u16 eeprom_size_ad(void) {
if (recordermode == 3) { //Wenn beide AD Pins gespeichert werden
  return (free_eeprom_for_ad / 3);
}else{ //Wenn nur ein Pin gespeichert wird
  return (free_eeprom_for_ad /2 );
}
}

u08 eeprom_write_ad(u16 adress,u16 data1,u16 data2) {
u08 firstbyte, secondbyte, thirdbyte;
u08 checkfirst, checksecond, checkthird;

if (adress < eeprom_size_ad()) {
 if (recordermode == 2) { //Optimierung, um Code zu sparen
  data1 = data2;
 }
 firstbyte = 0x80 + (data1 & 0x7f);
 secondbyte = 0x80 +((data1>>7) & 0x07);
 if (recordermode == 3) { //Wenn beide Aufgezeichnet werden
  adress *= 3;
  secondbyte += (data2>>3) & 0x70;
  thirdbyte = 0x80 +(data2 & 0x7f);
  if (eeprom_read_byte(&adrecdata[adress +2]) != thirdbyte) {
   eeprom_write_byte(&adrecdata[adress +2],thirdbyte);
  }
  checkthird = eeprom_read_byte(&adrecdata[adress+2]);
 } else { //Ansonsten wird nur einer aufgezeichnet
  adress *= 2;
  thirdbyte = 0;
  checkthird = 0;
 }
 if (eeprom_read_byte(&adrecdata[adress]) != firstbyte) {
   eeprom_write_byte(&adrecdata[adress],firstbyte);
 }
 checkfirst = eeprom_read_byte(&adrecdata[adress]);
 if (eeprom_read_byte(&adrecdata[adress +1]) != secondbyte) {
   eeprom_write_byte(&adrecdata[adress +1],secondbyte);
 }
 checksecond = eeprom_read_byte(&adrecdata[adress+1]);
 if ((checkfirst == firstbyte) && (checksecond == secondbyte)&&
    (checkthird == thirdbyte)) { //Wenn übereinstimmen
    return 1; //Korrekt
  }else{
    return 2; //Fehler
  }
} //Ende richtige Adresse
return 3; //3 = Ungültige Adresse
}

s16 eeprom_read_ad(u16 adress, u08 adport) {
u08 firstbyte, secondbyte,thirdbyte;
u16 readeddata;
if (adress < eeprom_size_ad()) {
 if (recordermode != 3) { //Nur ein Port wurde aufzezeichnet, adport unwichtig
  adress *= 2;
  firstbyte = eeprom_read_byte(&adrecdata[adress]);
  secondbyte = eeprom_read_byte(&adrecdata[adress +1]);
  if (firstbyte > 0x7f) { //Wenn höchstes bit gesetzt, dann auch inhalt!
   readeddata = (firstbyte & 0x7f) + ((secondbyte & 0x07)<<7);
   return readeddata;
  }else {
   return -1; //Keine Daten
  }
 } else { //Beide Daten aufgezeichnet
  adress *= 3;
  firstbyte = eeprom_read_byte(&adrecdata[adress]);
  secondbyte = eeprom_read_byte(&adrecdata[adress +1]);
  thirdbyte = eeprom_read_byte(&adrecdata[adress +2]);
  if (firstbyte > 0x7f) { //Wenn höchses Bit gesetzt ist, dann auch Inhalt!
   if (adport == 1) { //1. Kanal
    readeddata = (firstbyte & 0x7f) + ((secondbyte & 0x07)<<7);
   }else{ //2. Kanal
    readeddata = (thirdbyte & 0x7f) + ((secondbyte & 0x70)<<3);
   }
   return readeddata;
  } else { //Keine Daten
    return -1;
  }
 } //Ende Beide Daten aufzeichnen
} //Ende adress innerhalb des erlaubten Bereiches
return -1;
}


void eeprom_daysup_update(void) {
u16 readoutvalue;
uptimedays = primaryuptimedays;
uptimedays += uptimeminutes /(60*24);
readoutvalue = eeprom_read_word(&globalupdays);
//Verhindert zusätzlich zum zuhäufigem Beschreiben des EEProms einen Überlauf
if (readoutvalue < uptimedays)  {
eeprom_write_word(&globalupdays,uptimedays);
}
}

const char menu571[] PROGMEM = "Speichern im EEProm erfolgreich";
const char menu572[] PROGMEM = "Fehler beim Speichern im EEProm";

void speichereconfig(void) {
u08 dieconfig;
dieconfig = (punkteblinken & 0x01)<<1;
dieconfig += (rs232echo & 0x01)<< 2;
eeprom_write_byte(&globalconfig,dieconfig);
if (eeprom_read_byte(&globalconfig) == dieconfig) {
 strcpy_P(rs232rausstring,menu571); //Erfolgreich gespeichert
}else{
 strcpy_P(rs232rausstring,menu572); //Speichern fehlgeschlagen
}
rs232sendstring();
}

void initsystemeepromactions(void) {
/*Wenn diese Funktion aufgerufen wird, wurde noch kein sei() durchgeführt,
  Interrupts stören also nicht die Schreiboperationen*/
u08 wecktowrite, dieconfig;
//Laden der Reboots
rebootstat = eeprom_read_byte(&rebootnumber);
if (rebootstat < 254) {
 rebootstat++;
 eeprom_write_byte(&rebootnumber,rebootstat);//Aktualisieren
}
//Initialisieren der Wecker
if (rebootstat == 1) { //Erster Start
 getwecktime(1);       //Läd den ersten Wecker und überprüft ob leer
 if (whour == 0xff) {  //Bestätigung, dass erster Programmstart
  whour = defaultweckh;
  wminute = defaultweckm;
  wdaysofweek = 0xff;
  wsettings = 0;
  for (wecktowrite = 1;wecktowrite<maxweckx;wecktowrite++) {
   eepromaway = 0;
   savewecktime(wecktowrite);
   asm volatile("wdr"); //Die Schleife könnte lange dauern
  }
 }
}
//Laden der uptime stunden
primaryuptimedays = eeprom_read_word(&globalupdays);
//Laden der Config
dieconfig = eeprom_read_byte(&globalconfig);
if (dieconfig != 255) { //Um Fehlerhafte Werte nicht einzulesen
 if ((dieconfig & 0x02) != 0) { //2.Bit steht für Punkteblinken
  punkteblinken = 1;
 }
 if ((dieconfig & 0x04) != 0) {  //3.Bit steht für RS232 Echo
  rs232echo = 1;
 }
}
//Laden der Einstellungen für die A/D Aufzeichnung
recordermode = eeprom_read_byte(&adrecconfigeep);
//Restaurieren des Aufzeichnung-Index
for(eepromindex = 0;eepromindex < eeprom_size_ad();eepromindex++) {
  asm volatile("wdr"); //Die Schleife könnte lange dauern
  //Ob 1 oder 2 ist hier egal
  if (eeprom_read_ad(eepromindex,1) < 0) {
    break;
  }
}
}


void calcweekday(void) {
/*Berechnung basiert auf:
  http://www.cs.uu.nl/wais/html/na-dir/sci-math-faq/dayofweek.html
  Nach der obigen Methode wurden 984 Byte Flash benötigt!!!, funktionieren tat
  sie trotzdem nicht.
  Neue Methode basiert auf eigenen Überlegungen
  Globale zur Verfügung stehenden Zeit Variablen:
 Variablen für die Zeit: 14 Byte
u08 dayofweek = 1, day = 1, month = 1;
u16 year = 2004;
u08 isschaltjahr;
*/
u16 tagdesjahres;
u08 jahrdiff;
u16 tempwochentag;
//Tag des Jahres berechnen
calcschaltjahr();//Dazu muss bekannt sein, ob es sich um ein Schaltjahr handelt
tagdesjahres = 31*(month-1);
if (month > 2) {
   tagdesjahres = tagdesjahres -3 +isschaltjahr;
}
if (month > 4) { tagdesjahres--; }
if (month > 6) { tagdesjahres--; }
if (month > 9) { tagdesjahres--; }
if (month > 11) { tagdesjahres--; }
tagdesjahres += day;
//Wochentag berechnen
jahrdiff = year -2000; //Das Referenzjahr muss durch 400 ohne Rest teilbar sein.
tempwochentag = jahrdiff + (jahrdiff-1) / 4 - (jahrdiff-1) / 100 + (jahrdiff-1)
                / 400 + tagdesjahres;
if (jahrdiff > 0) { //Weil das Referenzjahr auch ein Schaljahr ist
 tempwochentag++;
}
//Normalerweise wäre Samstag=1 (Weil der 1.1.2000 ein Samstag ist), so ist Sa.=6
tempwochentag = (tempwochentag % 7)+5;
if (tempwochentag > 7) { //Überlauf
tempwochentag = tempwochentag -7;
}
dayofweek = tempwochentag;
//Die obige Funktion benötigt c.a. 182 Byte Flash
}


void calcschaltjahr(void) {
/* Die Berechnung erfolgt nach der gregorianischen Regel, da ich mal davon
ausgehe, dass die Uhr keinen Zeitsprung in die Zeit vor 1582n.Chr machen
wird :-)
*/
//Wenn Jahr durch 4 ohne Rest teilbar, aber nicht durch 100
if (((year % 4) == 0)&&((year % 100) != 0)) {
  isschaltjahr = 1;        //Dann Schaltjahr
}else {
  isschaltjahr = 0;        //Dann kein Schaltjahr
}
if ((year % 400) == 0) {   //Wenn Jahr durch 400 Teilbar
  isschaltjahr = 1;        //Dann doch wieder Schaltjahr
}
}


SIGNAL(SIG_OVERFLOW1) {//timer abfrage
/*wird 703 mal pro sec ausgeführt
  140,6 mal pro sec gibt es ein neues Bit
  Ausführung erfolgt also alle 11379 Takte (bei8 MHZ Chip Frequenz)
  der timer1 läuft immer, es sei denn, es werden selber von dem Device Daten
  per IR gesendet
  256² - 11379 = 54157
  bei 4MHZ:
  256² - 7168 = 58368  hat sich as optimaler Wert herausgestellt
*/
if (empmode == 0) { //wenn auf empfang gewartet wird,
if bit_is_clear(PIND,3) {//gehe in Empfangsmodus
empmode = 1;
empdurch = 0;
precalc = 0;
}
}
if (empmode == 1){
if bit_is_clear(PIND,3) {//wenn Eins, dann erhöhen
precalc++;
}
if (empdurch == 5) { //wenn 5 Sampels
//least sicnificant Bit wird zuerst gesendet
empfang = empfang /2;
if (precalc > 2) { //wenn Eins empfangen
empfang = empfang | 0x80;//oberstes Bit setzen
}

precalc = 0;
empdurch = 0;
if (empfang == 0) { //ende der Übertragung
empmode = 0;
}
}//ende wenn 5 Samples
empdurch++;
}//ende wenn Empfangsmode
if (empmode == 2) {
empfang = 0;
}
outp(0xe4,TCNT1H);//Das hohe Bit muss zuerst beschrieben werden
outp(0x00,TCNT1L);//58368
if ((empfang | 0x3c) == 0xbd) { //Überprüfe empfangene Daten auf Richtigkeit
empfangir = empfang;
empfang = 0;
}
}

SIGNAL(SIG_ADC) {
u08 nowconvert;
cconv += ADCW;
convno++;
if (convno == 32) { //Wenn 32 Convertierungen
convno = 0;
nowconvert = inp(ADMUX);      //ermittle, welcher Kanal verwendet wurde
adwerte[nowconvert] = cconv / 32;   //schreibe A/D Wert in Array
nowconvert++;
//Beim ATMEGA16 nötig, da hier alle Bits des ADMUX beschreibbar sind
if (nowconvert == 8) {
  nowconvert = 0;
}
outp(nowconvert,ADMUX);
cconv = 0;
}
sbi(ADCSR,ADSC);              //Setze Bit, um neue Konvertierung zu starten
}

void zumsenden(u16 zeichen) { //Sendet die Daten an den Empfänger
u16 sendebuff;
u08 temp01,temp02;
//Variablen, da die Rechenzeit nicht ausreicht, um alles auf einmal zu berechnen
u08 statusledsfast;
u08 nextled,nextziffer,suboper;

if (empmode == 0) {//es wird gerade nicht empfangen
//sendetechnik b10DDDDDDDDDDDD01
sendebuff = (zeichen*4) | 0x8001;
senddurch = 1;
warte(2500);//damit der Empfänger die letzten Daten verarbeiten kann
outp(1,TCCR0); //startet Timer1
statusledsfast = statusleds;
statusledsfast = statusledsfast << 1;
nextled = 0;
nextziffer = 0;
cli();
while (sendebuff > 0) { //schiebt Bit für Bit nach draußen
if ((sendebuff & 1) == 0) { //wenn Bit null, setze Ausgang
cbi(PORTD,PIND5);
}else { //wenn Bit eins
sbi(PORTD,PIND5);
}
sendebuff = sendebuff /2;
while (senddurch != 0) {
senddurch++;
cbi(PORTD,PIND4);//Takte bis zum sbi:14+1
asm volatile("wdr");
outp(0,TCNT0); //Reset Timer0, 1 Takt

suboper = senddurch %32; //3 Takte
temp01 = ((senddurch / 32) % 4); //6 Takte
/* 1+3+6= 10 Takte wegen des obrigen Codes einsparen
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
*/
if (suboper == 3) { //Anzeigen
temp01 = segment[temp01];
temp02 = nextziffer | nextled;
outp(0,PORTB);
outp(temp01,PORTC);//Den Wert der Ziffer, 5 Takte
outp(temp02,PORTB);//Welche Ziffer, 10+x Tage,c.a. +0...15
}
/* Dec Code ist so schon langsam genug
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
*/
sbi(PORTD,PIND4);//Takte bis zum } :64
if (suboper == 1) { //LED berechnen
nextled = (statusledsfast << temp01)& 0x10;
}
if (suboper == 2) { //Ziffer berechnen
nextziffer =  (0x01<<temp01);
}


while (inp(TCNT0) < 70) {//normalerweise 73, jedoch muss der Zeitverlust durch
asm volatile("nop");    //die Routinen zur Anzeigen Berechnung nachgeholt werden
}
}//Warteschleife
senddurch++;//höhere Transferraten führten zu Fehlern auf Empfängerseite
}//ende Bit für Bit schieben
/*
Die folgende Messung stammt noch aus uhr-0s04.c. Wahrscheinlich ist die Anzahl
der benötigten Takte in dieser Programm Version geringfügig anders!
zwischen cli() und sei() vergehen laut Simulator 381951-381952 Takte
(je nachdem ob man cbi() mitzählt oder nicht).
presec wird alle 15625 Takte weitergezählt
somit wurde 24,44 mal das Weiterzählen vergessen
Da der Timer jedoch selber nicht gestoppt wird, sondern nur das Auslösen des
Interrupts, wird nach sei() in jedem Fall der SIG_OUTPUT_COMPARE2 Interrupt
aufgerufen. Im Mittel ist bei cli() bereits die Hälfte der Zeit bis zu
SIG_OUTPUT_COMPARE2 abgelaufen. Also sind 7812,5 Takte abzuziehen.
Somit hätte presec 23,94 -> ~24 mal erhöht werden müssen
Dies soll jetzt nachgeholt werden. Zu beachten sind noch Überläufe, bei denen
die Sekunden um eins erhöht werden müssen prinzieiell würde auch dieser Code
wiederum Zeit benötigen. Da presec aber eigentlich nur 23,94 mal erhöht werden
müsste, jedoch 24 mal erhöht wird, reduziert sich dadurch sogar die
Ungenauigkeit.
*/
//ab 232 werden die Sekunden weiter gezählt; 256-24 = 232; 256 wäre wieder Null
if (presec > 231*newledspeed) {
  seconds++;
}
presec += 24*newledspeed;//alt:24
sei();
sbi(PORTD,PIND4); //nur zur Sicherheit
cbi(PORTD,PIND5);
}//ende nicht beim Empfangen gestört
}//ende Prozedur

void tastentesten(void) {
//Wertet die Tasten der Uhr aus, und fürt die gewünschten Aktionen aus
u08 nowkeys,temp01;
nowkeys = 0;
temp01 = 0;
while (temp01 < 4) { //spart 18 Byte gegenüber 4 Einzelanweisungen
if (adwerte[temp01*2] < tastschwelle) {
  nowkeys += (1<<temp01);
}
temp01++;
}
if (lastkeys != nowkeys) {
lastkeys = nowkeys;
if (lastkeys > 0) {
if (wactive != 0) { //Wecker aus
  wactive = 0;
  return;
}
if (lastkeys < 3) { //Datum oder Weckzeit anzeigen
  dispmode = 3-lastkeys;
}
}
if (lastkeys == 0) {//Uhrzeit anzeigen
dispmode = 0;
if (wdisabled24 == 1) {
  wdisabled24 = 0;
}
}
if (lastkeys == 0x04) { //IR / RS232 Mode verlassen
commtype = 0;
statusleds = statusleds & 0x3f;//status LED für RS232 und IR aus
//damit der Controller den IR Modus nicht sofort wieder aktiviert
empfangir = 0xff;
}
//Nächste Weckzeit deaktivieren, Part 1, oder Deaktivieren rückgängig machen
if (lastkeys == 0x08) {
wdisabled24 = 1;
}
//Nächste Weckzeit Deaktivieren, Part 2
if ((lastkeys == 0x09) && (wdisabled24 = 1)) {
wdisabled24 = 2;
}

} //Ende Tastenwechsel
}

void testallwecker(void) {
u16 timetonext,wreamingtime;
u08 wnextsettings,wnextweckno,temp01;
wnextsettings = 0;
wnextweckno = 0;
timetonext = 32000;
for(temp01 = 1;temp01<maxweckx;temp01++) {
wreamingtime = getreamingtime(temp01);
if ((wreamingtime >= 0)&&(wreamingtime < timetonext)) {
  timetonext = wreamingtime;
  showwhour = weckdata[0];
  showwminute = weckdata[1];
  wnextsettings = weckdata[3];
  wnextweckno = temp01;
}
}
if (timetonext < 1440) { //Weniger als ein Tag
  wleds = 1;             //LED an
}else{
  wleds = 0;       //LEDs Aus
  showwhour = 255; //Damit das Display nicht irgendwas anzeigt
  wdisabled24 = 0; //Es kann ein Wecker nicht mehr als 24 Std im Voraus deaktiviert werden
  }
if (timetonext < 120) { //Weniger als zwei Stunden
  wleds = 2;            //LED an
}
/*Wenn bis zum Auslösen Null minuten und nicht bereits in der selben Minute
  ausgelöst wurde, dann jetzt Wecker starten*/
if ((timetonext == 0) && (wdisabled == 0)){
  //Wenn Wecker nicht vorher per Taster deaktiviert worden ist
  if (wdisabled24 != 2) {
    wactive = 70; //Piepton-Stärke 70 von 255
  }
  //Deaktivieren des Weckers
  if (wnextsettings == 1) { //Wecker soll nur einmal ausgelöst werden
    wnextweckno *= 4;//Die Variable wird ja nur innerhalb dieser Prozedur verwedet
    temp01 = eeprom_read_byte(&weckzeiten[wnextweckno -1]);
    /*Damit das EEProm nich ständig neu beschrieben wird,
     vorher auslesen, ob überhaupt Änderungen nötig sind
     zu häufiges Schreiben des EEProms Ende   */
    if (temp01 != 0) {
      eeprom_write_byte(&weckzeiten[wnextweckno -1],0);
    } //Ende EEProm geschrieben
    //Test ob EEProm Schreiben erfolgreich, nur dann setze wdisabled
    temp01 = eeprom_read_byte(&weckzeiten[wnextweckno -1]);
    if (temp01 == 0) { //Deaktivierung erfolgreich
      wdisabled = 1;
    }//Ende Deaktivierung erfolgreich
  } else {//Ende Wecker nur einmal auslösen, ansonsten setze wecker disabled
  wdisabled = 1; //Damit der Wecker
  }
  wdisabled24 = 0; //Damit der Wecker nächstes Mal wieder auslöst
}//Ende Wecker aktivieren

}

u16 getreamingtime(u08 weckno) {
/*Rückgabe:
es wird der Wert bis zur nächsten Auslösung des WEckers in Minuten zurückgegeben, wenn:
der Wecker aktiv ist, die Auslösung am aktullem oder nächstem Tag erfolgt.
ansonsten wird 32000 zurückgegeben
Berechnung der Differenz:
Wenn Wecker am gleichem Tag auslöst:
(wh-h)*60-(m-wm)
Wenn Wecker erst am nächstem Tag auslöst:
(60*24)+((wh-h)*60-(m-wm);
für das Ergebnis wird s16 benötigt

weckdata[2] = MoDiMiDoFrSaSo--
*/
u08 nextday; //weckdata[0] = whour; [1] = wminute; [2] = wdaysofweek; [3] = wsettings
s16 reamingtime;
weckno *= 4;
weckdata[0] = eeprom_read_byte((unsigned char *)(weckno -3));
weckdata[1] = eeprom_read_byte((unsigned char *)(weckno -2));
weckdata[2] = eeprom_read_byte((unsigned char *)(weckno -1));
weckdata[3] = eeprom_read_byte((unsigned char *)(weckno*1));
if (weckdata[3] == 0) {
return 3200;
}

reamingtime = (weckdata[0]-hours)*60-(minutes-weckdata[1]);
//Am aktuellem Tag ist der Wecker NICHT oder war bereits Aktiv, sehe nach, ob dies am nächstem Tag der Fall ist
if ((((weckdata[2] << (dayofweek-1))& 0x80)== 0) || (reamingtime < 0)) {
nextday = dayofweek;
if (nextday == 7) {//7 bedeutet So, es folgt also Mo
  nextday = 0; //Nächster Tag ist ein Montag
}
if (((weckdata[2] << nextday)& 0x80) == 0) {//am nächstem Tag auch nicht aktiv, somit
reamingtime = 32000;
}else{//Ja, am nächstem Tag ist der Wecker aktiv
reamingtime += 1440;
}
}//ende, am aktuellem Tag nicht aktiv
return reamingtime;
}

void smallnottimecriticalcode(void) {
//Diese Pozedur darf nicht länger als 15000 Takte sein. Demnach dürfen keine Warteschleifen vorhanden sein
//Sie wird unregelmäßig häufig und in untegelmäßigen Abständen aufgerufen.

//Punkte zwischen der Minuten und Stundenanzeige blinken/leuchten lassen
asm volatile("wdr");
if ((punkteblinken == 1)&&(dispmode == 0)) {
if (presec < 128*newledspeed) {
  sbi(PORTD,7);
}else{
  cbi(PORTD,7);
}
}else{ //wenn nicht punkteblinken oder dispmode = 0
  if (dispmode != 1) { //wenn nich Datum
  sbi(PORTD,7);
  }else { //bei Datum Punkte aus
  cbi(PORTD,7);
  }
}
tastentesten();
//LED Anzeigen hell-dunkel, wird mittels DDRC 0..6 gesteuert
if ((hours > ledshell)&&(hours < ledsdunkel)) { //helle LEDs von 7 bis 22 Uhr
ledhelligkeit = 4;
}else{
ledhelligkeit = 2;
}
//LEDs für Wecker Ein-Ausschalten
//Das presec sorgt fürs Blinken bei Weckerdeaktivierung
if (((presec > 128*newledspeed) || (wdisabled24 != 2))&&(wleds != 0)) {
  if (wleds == 1) { //LEDs für 24 Std an
   statusleds = (statusleds | 0x02) & 0xfe; //LED für 2 Std aus, LED für 24 an
  }
  if (wleds == 2) { //LEDs für 2 Std und 24 Std an
   statusleds = statusleds | 0x03;
  }
}else{
statusleds = statusleds & 0xfc;//Status LEDs aus
}
//Wenn dcferror dann error led dauer an, wenn bereits so lange wie blinkdcferror dann blinken
if ((dcferror == 1)&&((dcferrorminutes < blinkdcferror)||(presec > 128*newledspeed))) {
statusleds = statusleds | 0x10;//fehlerhaftes Signal, LED an
}else{
statusleds = statusleds & 0xef; //Fehler LED aus
}
//Direkte aktualisierung, da setsegments() nicht häufig genut aufgerufen wird, um die LED blinken zu lassen
segment[3] = (segment[3] & 0x7f) | ((statusleds << 3)& 0x80);
}


void savewecktime(u08 weckno) {//speichert eine Weckzeit
//Die Prozedur darf nicht zu häuftig aufgerufen werden, da der interne EEprom
//maximal 100000 Schreibzyklen garantiert
if ((eepromaway == 0)&&(weckno > 0)&&(weckno<maxweckx)) {
wdisabled24 = 0;
weckno *= 4;
eeprom_write_byte(&weckzeiten[weckno -4],whour);
eeprom_write_byte(&weckzeiten[weckno -3],wminute);
eeprom_write_byte(&weckzeiten[weckno -2],wdaysofweek);
eeprom_write_byte(&weckzeiten[weckno -1],wsettings); //wenn weckno für sich stehen würde, gäbe der Compiler eine Warnung aus
eepromaway = 10;
}
}

void getwecktime(u08 weckno) {//liest eine Weckzeit aus dem EEprom
//der AT90S8535 hat 512 Byte EEporm, jede Weckzeit benötigt 4 Byte
//wir wollen 10 Weckzeiten, also 40 Byte werden benötigt
//irgendwo stand mal, das erste Byte könnte sich bei Resets ändern, daher benutze
//ich es sicherhaltshalber lieber nicht
if ((weckno < maxweckx)&&(weckno>0)) {
wselected = weckno;
weckno *=4;
whour = eeprom_read_byte(&weckzeiten[weckno -4]);
wminute = eeprom_read_byte(&weckzeiten[weckno -3]);
wdaysofweek = eeprom_read_byte(&weckzeiten[weckno -2]);
wsettings = eeprom_read_byte(&weckzeiten[weckno -1]);
}
}

SIGNAL(SIG_OUTPUT_COMPARE2) { //Setzen der Uhr, ansteuern der LED Anzeige, ist 256 mal pro Sekunde aufzurufen
u08 temp01;
u08 wacktivenonvolatile; //Spart gegenüber der direkten Verwendung von wactive 16 Byte Flash
//damit die Routine, die IR Empfangs Möglichkeit nicht stört, sollte sie möglichst schnell abzuarbeiten sein
outp(0,PORTB); //so sind die LEDs während der Prozedur aus und erscheinen dunkler/nicht ganz so hell
wacktivenonvolatile = wactive;
//Sorgt dafür, dass der Wecker zunächst nur kurzzeitig klingelt
if ((wacktivenonvolatile*newledspeed >= presec)&&(wacktivenonvolatile != 0)) {
sbi(PORTD,6); //Beeper an
}else {
  cbi(PORTD,6);//Beeper aus
}
presec++;
temp01 = ((presec/newledspeed) % 4);
outp(segment[temp01],PORTC);//Den Wert der Ziffer
if (presec == (256*newledspeed)) { //War in altem Programm überflüssig, da presec automatisch überlief
  presec = 0;
}
if (presec == 0) {
seconds++;
if ((wacktivenonvolatile != 0)&&(wacktivenonvolatile < 251)) {  //Wecker langsam lauter machen
  wacktivenonvolatile += 5;
}
}
//Da die Prozedur seit Version 7 schneller Laufen kann, der DCF77 dies jedoch nicht soll
if ((presec % newledspeed) == 0) {
fastsample = fastsample + (inp(PIND)&0x04);
//alle 8 Durchläufe wird fastsample überprüft
if (((presec/newledspeed) & 0x07) == 0x07) { //wenn unteren 3 Bits = 1
/* fastsample = 128 wenn alle 8 Messungen 1
fastsample = 0 wenn alle 8 Messungen 0
zu beachten ist, dass der inventierte Eingang verwendet wird,
somit 1-> Low Empfangen; 0-> High Empfangen
geändert in Version 5: fastsample < 13
geändert in Version 7: fastsample < 13*newledspeed
*/
if (fastsample < 13) { //mindestens 5 der 8 Messungen müssen High sein, um ein High als empfangen zu erkennen
currentdcfval = 2; //High Empfangen
}else{
currentdcfval = 1; //Low empfangen
}
fastsample = 0; //noch nichts empfangen
}
}//Ende Geschwindigkeitsausgleich
wactive = wacktivenonvolatile;
/*
temp01 = 0 -> Bit3 -> Pin4 -> (statusleds<<1) & 0x10
temp01 = 1 -> Bit2 -> Pin4 -> (statusleds<<2) & 0x10
temp01 = 2 -> Bit1 -> Pin4 -> (statusleds<<3) & 0x10
temp01 = 3 -> Bit0 -> Pin4 -> (statusleds<<4) & 0x10
*/
if ((ledhelligkeit == 4)||((presec % newledspeed) == 0)) {
outp((0x01<<temp01) | ((statusleds <<(temp01+1))& 0x10),PORTB);//Welche Ziffer, + unteren 4 Status LEDs (PinB4)
}
}

SIGNAL(SIG_UART_RECV) {
rs232reinstring[0] = rs232reinstring[1];
rs232reinstring[1] = rs232reinstring[2];
rs232reinstring[2] = rs232reinstring[3];
rs232reinstring[3] = rs232reinstring[4];
rs232reinstring[4] = rs232reinstring[5];
rs232reinstring[5] = inp(UDR);
if (rs232echo == 1) {
  if (bit_is_set(UCSRA,5)){ //Register Name geändert für ATMEGA16, vorher USR
    outp(rs232reinstring[5],UDR);
  }
}
}

void rs232sendstring(void) {
u08 temp01;
asm volatile("wdr");
/*Optimiert Leerzeichen weg, insbesondere beim Zurücksenden der aufgezeichneten
A/D Werte spart dies eine Menge Zeit*/
for (temp01 = 32;temp01 > 0;temp01--){
  if (rs232rausstring[temp01] != 32) { //Wenn kein Leerzeichen,
    break;                             //dann Abbruch
  }
}//Ende Optimierung
rs232rausstring[temp01+1] = 10;          //Zeichen für neue Zeile schreiben
rs232rausstring[temp01+2] = 13;
for (temp01 = 0;temp01<35;temp01++) {
  loop_until_bit_is_set(UCSRA,5);    //wartet, bis der sende Buffer leer ist  //Register Name geändert für ATMEGA16, vorher USR
  outp(rs232rausstring[temp01],UDR);
  if (rs232rausstring[temp01] == 13) { //Wenn Zeichen für neue Zeile,
    rs232rausstring[temp01] = 32; //Letzes Zeichen durch Leerzeichen ersetzen
    break;
  }
  rs232rausstring[temp01] = 32;//Leerzeichen
}
}

//Größe vor wordtostr :5220
//mit wordtostr: 5282 -> worddtstr (u16) kostet 62 Byte speicher
//bei einer Convertierung von einer Zahl mit 2 Stellen werden 14 Byte Flash gespart
//ändern der Variable Nummer von u16 auf u32 kostet 186 Byte
//jedoch werden durch die Verwendung der Routine zur Konvertierung der uptime 368Byte gespart
//somit spart u32 insgesamt immer noch 182 Byte gegenüber u16
void wordtostr(u32 nummer, u08 digits, u08 strposition) {
while (digits > 0) {
digits--;
rs232rausstring[strposition+digits] = nummer%10+48;
nummer = nummer / 10;
}
}

s16 strtoword(void) {
u08 nun,temp01,therewasinput;
u16 temp02;
temp02 = 0;
therewasinput = 0;
if (rs232reinstring[5] == 0xd) {//Wenn letztes Zeichen ein Enter
for (nun  = 0;nun <5;nun++){
temp01 = rs232reinstring[nun];
if ((temp01 > 47)&&(temp01 < 58)) {//ist Zahl
if (temp02 > 3200) { //eingegebene Zahl wird bei weiterer Eingabe zu groß
therewasinput = 0;
break;
}
temp02 = (temp02*10)+(temp01-48);
therewasinput = 1;
}
rs232reinstring[nun] = 1;//löschen, damit nicht noch mal erkannt
}
}//Ende wenn letztes Zeichen ein Enter
if (therewasinput == 0) {
  return -1;
}
return temp02;
}

const char ureboot[] PROGMEM = "Uhr reboot: ";
void initsystem(void) { //Der Code zum Starten des MCU
u08 temp01;
asm volatile("wdr");
outp(0x0f,WDTCR);//Aktiviert den Wachdog mit 1,9sec bis zu einem Reset
outp(0x00,DDRA);//Taster Eingänge, Analog Eingänge
outp(0x55,PORTA);//Aktiviert die Pullups für die Taster
outp(0x10,DDRB);//Display Ausgänge (Steuerung erfolgt mittels Pullups), Programmier Stecker Eingänge
outp(0xff,DDRC);//Display Ausgänge
outp(0xf2,DDRD);//IR, DCF 77, Lautsprecher, RS232
outp(0x80,ACSR);//Deaktiviert den Analog Comparator, spart Strom
outp(244,OCR2);//Compare Wert
outp(0x84,TIMSK);//Aktiviert timer1 overfolw interrupts und timer2 compare interrupt
outp(0x0b,TCCR2);//timer2 Prescaler : 32, automatisches Resetten beim Erreichen des Interrupts, (vorher war es 0x0c: PRescaler:64)
outp(0,TCNT2);//timer2 resetten
outp(0,TCNT1H);  //setzt den Timer1 auf null (reset timer1)
outp(0,TCNT1L);  //setzt den Timer1 auf null (reset timer1)
outp(25,UBRRL);//Baudraten:  103 -> 2400 Baud; 25 -> 9600 Baud                   //Register Name geändert für ATMEGA16, vorher UBRR
outp(0x98,UCSRB);//RS232 Sender aktiviert, RS 232 Empfänger Interupt aktiviert   //Register Name geändert für ATMEGA16, vorher UCR
outp(0xcf,ADCSR);//A/D Converter initialisieren, ADC Frequenz festlegen unt ADC Interrupt aktivieren
//outp(0x50,UDR);//Test Byte Senden

strcpy_P(rs232rausstring,ureboot);
temp01 = inp(MCUCSR); //Register Name geändert für ATMEGA16, vorher MCUSR
if ((temp01 & 0x01) == 0x01) { //Power on Reset
rs232rausstring[13] = 80; //P
}
if ((temp01 & 0x02) == 0x02) { //Externer Reset
rs232rausstring[13] = 69; //E
}
if ((temp01 & 0x04) == 0x04) { //Brown out Reset
rs232rausstring[13] = 66; //B
}
if ((temp01 & 0x08) == 0x08) { //Watchdog Reset
rs232rausstring[13] = 87; //W
}

outp(0,MCUCSR); //Register Name geändert für ATMEGA16, vorher MCUSR
rs232sendstring();
/*Da erst RS232 gesendet wird, und dies ein wenig Zeit dauert, dient dies
 als Delay um sicher zu stellen dass nicht der Programmer vor einer Programmierung
 das Programm kurzzeitig laufen lässt (bei sp12 zu beobachten) */
initsystemeepromactions();
sei();
while (inp(ADCSR) & BV(ADSC));//wartet, bis die erste Konvertierung komplett ist (wait until the first conversion is done)
statusleds = 0;
}

void setsegments(void) {//berechnet die Ziffern
const u08 segcodierung[10] = {0x6f,0x28,0x5d,0x79,0x3a,0x73,0x77,0x29,0x7f,0x7b};
u16 temp01;//zu zeigender Wert
u08 ziffera,zifferb;
u08 segtemp1,segtemp2,segtemp3,segtemp4;
u08 faststatusleds; //weil statusleds selbst volatile und zu viel Speicher für das LDS und STS beim direkten Verwenden verbraucht werden würde
faststatusleds = statusleds;
if (dispmode == 1) { //zeige Datum
  ziffera = day;
  zifferb = month;
  faststatusleds = (faststatusleds & 0xf3 ) | 0x08;
}else
if (dispmode == 2) { //zeige Wecker
  ziffera = showwhour;
  zifferb = showwminute;
  faststatusleds = (faststatusleds & 0xf3 ) | 0x04;
}else { //ansonsten zeige Uhr
  ziffera = hours;
  zifferb = minutes;
  faststatusleds = faststatusleds & 0xf3;
}
//Umrechnen
statusleds = faststatusleds; //Zurückschreiben
segtemp1 = faststatusleds & 0x80;
segtemp2 = (faststatusleds*2) & 0x80;
segtemp3 = (faststatusleds*4) & 0x80;
segtemp4 = (faststatusleds*8) & 0x80;
if (ziffera < 100 ) {
temp01 = segcodierung[ziffera/10];
if ((temp01 == 0x6f)&&(dispmode != 1)){ //vorstehende Null entfernen
  temp01 = 0;
}
segtemp1 |= temp01;
segtemp2 |= segcodierung[ziffera%10];
segtemp3 |= segcodierung[(zifferb/10)%10];
segtemp4 |= segcodierung[zifferb % 10];
} else { //Wenn kein Wecker innerhalb der nächsten 24 Std -> Bindestriche anzeigen
  segtemp1 |= 0x10;
  segtemp2 |= 0x10;
  segtemp3 |= 0x10;
  segtemp4 |= 0x10;
}
segment[0] = segtemp1;
segment[1] = segtemp2;
segment[2] = segtemp3;
segment[3] = segtemp4;
}

void updatetime(void) { //Aktualisiert die Zeit durch den Quarz (ohne DCF77)
eeprom_daysup_update();
if (seconds > 59) {
  minutes++;
  uptimeminutes++;
  commtimeoutminutes++;
  seconds = 0;
  wdisabled = 0;
  if ((dcferror == 1) && (dcferrorminutes != 255)) {
    dcferrorminutes++;
  }
}
if (commtimeoutminutes > commtimeup) { //IR / RS232 Mode verlassen, wenn länger als 15 Minuten aktiv
commtype = 0;
statusleds = statusleds & 0x3f;//status LED für RS232 und IR aus
empfangir = 0xff;//damit der Controller den IR Modus nicht sofort wieder aktiviert
}
if (commtype == 0) {
  commtimeoutminutes = 0;
}
if (minutes > 59) {
  hours++;
  minutes = 0;
}
if (hours > 23) {
  dayofweek++;
  day++;
  hours = 0;
}
if (month == 2) {//Wenn Februar
calcschaltjahr();
if (day >= (isschaltjahr+29)) { //Schaljahre werden berücksichtigt
  month++;
  day = 1;
}
}
if (day == 31) { //Bei Monaten mit 30 Tagen (April, Juni, September, November)
if ((month == 4)||(month == 6)||(month == 9) || (month == 11)) {
  month++;
  day = 1;
}
}
if (day > 31) { //Bei Monaten mit 31 Tagen
  month++;
  day = 1;
}
if (month > 12) {
  year++;
  month = 1;
}
if (dayofweek > 7) {
  dayofweek = 1;
}
if (commtype == 1) { //Wochentag berechnen (ansonsten wird durch DCF77 gesetzt oder einfach weitergezählt)
calcweekday();
}
}

const char linielang[] PROGMEM = "---------------------------------";
const char zeitmaske[] PROGMEM = "Zeit: xx xx:xx Datum: xx.xx.xxxx";

void newscreen(void) {
const char wochentage[] = "MoDiMiDoFrSaSo";
  calcweekday(); //Damit der Wochentag sofort richtig angezeigt wird
  rs232sendstring();
  strcpy_P(rs232rausstring,linielang);
  rs232sendstring();
  strcpy_P(rs232rausstring,zeitmaske);
  wordtostr(hours,2,9);
  wordtostr(minutes,2,12);
  wordtostr(day,2,22);
  wordtostr(month,2,25);
  wordtostr(year,4,28);
  rs232rausstring[6] = wochentage[(dayofweek-1)*2];
  rs232rausstring[7] = wochentage[(dayofweek-1)*2+1];
  rs232sendstring();
  rs232sendstring();
}

const char menu111[] PROGMEM = "Wecker xx xx:xx MoDiMiDoFrSaSo O";
void makewstatus(u08 weckno) {
u08 temp01;
strcpy_P(rs232rausstring,menu111);
wordtostr(weckno,2,7);
wordtostr(whour,2,10);
wordtostr(wminute,2,13);
for (temp01 = 0;temp01<7;temp01++) {       //spart gegenüber 7 Einzelanweistungen 46 Byte
if ((wdaysofweek & (0x80>>temp01)) ==0) {//tage mit -- erstetzen
rs232rausstring[16+temp01*2] = 45;
rs232rausstring[17+temp01*2] = 45;
}
}
if (wsettings == 1) { //Einmalig
rs232rausstring[31] = 69;
}
if (wsettings == 2) { //Multiple/Mehrmalig
rs232rausstring[31] = 77;
}
}

//Menu Variablen müssen leider global sein, sonst wird PROGRMEM ignoriert und das wäre fatal
const char menu500[]  PROGMEM = "2.Config";
const char menu501[]  PROGMEM = "1 Uhrzeit";
const char menu502[]  PROGMEM = "2 Tag";
const char menu503[]  PROGMEM = "3 Monat";
const char menu504[]  PROGMEM = "4 Jahr";
const char menu505[]  PROGMEM = "5 RS232 Echo";
const char menu506[]  PROGMEM = "6 Punkte blinken";
const char menu507[]  PROGMEM = "9 Hauptmenu";
const char menu570[]  PROGMEM = "7 Im EEProm Speichern";
const char menu510[]  PROGMEM = "2.1 Uhrzeit eingeben (hh:mm)";
const char menu520[]  PROGMEM = "2.2 Tag(1..31) eingeben";
const char menu530[]  PROGMEM = "2.3 Monat(1..12) eingeben";
const char menu540[]  PROGMEM = "2.4 Jahr(0..31999) eingeben";
const char menu550[]  PROGMEM = "2.5 RS232 Echo (1:An, 2:Aus)";
const char menu560[]  PROGMEM = "2.6 Punkte blinken (1:An, 2:Aus)";
const char menu10[]   PROGMEM = "Hauptmenu";
const char menu11[]   PROGMEM = "1 Wecker";
const char menu12[]   PROGMEM = "2 Config";
const char menu13[]   PROGMEM = "3 A/D Wandler";
const char menu14[]   PROGMEM = "9 Beenden";
const char menu15[]   PROGMEM = "Uhr Uptime: xxxxxxxxx Minuten";
const char menu16[]   PROGMEM = "Reboots: xxx Laufzeit: xxxxx Tage";
const char menu01[]   PROGMEM = "Well, I suggest you to sleep";
const char menu02[]   PROGMEM = "Bye, have a nice day";
const char menu03[]   PROGMEM = "Bye Bye, and enjoy the day";
const char menu04[]   PROGMEM = "I wish you a nice evening";
const char menu05[]   PROGMEM = "You can sleep, I must work";
const char menu1000[] PROGMEM = "3. A/D Wandler";
const char menu1001[] PROGMEM = "1 Werte erneut einlesen";
const char menu1002[] PROGMEM = "9 Hauptmenu";
const char menu1003[] PROGMEM = "Wandler: x Wert: ";//ursprünglich "Wandler: x Wert: xxxx", xxxx wurde jedoch weggenommen um Flash zu sparen
const char menu1004[] PROGMEM = "2 Neue Aufzeichnung starten";
const char menu1005[] PROGMEM = "3 Aufzeichnung einlesen";
const char menu1006[] PROGMEM = "4 Aufzeichnungs Status";
const char menu1007[] PROGMEM = "5 Aufzeichnen stoppen";
const char menu1010[] PROGMEM = "Neue Aufzeichnung beginnen?";
const char menu1011[] PROGMEM = "1:Ja, Ueberschreiben 9: Nein";
const char menu1012[] PROGMEM = "1: 1. A/D, 2: 2. A/D 3: Beide";
const char menu1013[] PROGMEM = "Aufzeichnen jede (1-255) Minute";
const char menu1014[] PROGMEM = "0: Nicht aufzeichnen:";
const char menu1020[] PROGMEM = "Aufgezeichnete Datensaetze:";
const char menu1021[] PROGMEM = "Aufzeichenbare Datensaetze:";
const char menu1022[] PROGMEM = "Verbleibende Minuten:";
const char menu1023[] PROGMEM = "Aufzeichnung: Eingang";
const char menu1024[] PROGMEM = "Aufzeichnung: Beide Eingaenge";
const char menu1025[] PROGMEM = "Aufz. gestartet: xx:yy Uhr";
const char menu1026[] PROGMEM = "Aufzeichnung gestoppt";
const char menu1027[] PROGMEM = "Aufzeichnung alle xxx Minuten";
const char menu1030[] PROGMEM = "Daten von Sensor x:";
const char menu1031[] PROGMEM = "0-9: Zurueck zum A/D Menu";
const char menu100[]  PROGMEM = "1. Weckzeiten";
const char menu101[]  PROGMEM = "1 Wecker uebersicht";
const char menu102[]  PROGMEM = "2 Wecker bearbeiten";
const char menu103[]  PROGMEM = "9 Hauptmenu";
const char menu110[]  PROGMEM = "1.1 Weckzeiten Uebersicht";
const char menu1200[] PROGMEM = "Wecker (1-10) auswaehlen";
const char menu1210[] PROGMEM = "Wecker xx : Zeit eingeben (hh:mm)";
const char menu1220[] PROGMEM = "1-7: Aktiver Wochentag, 9: Weiter";
const char menu1230[] PROGMEM = "Wecker Aktiv Modus";
const char menu1231[] PROGMEM = "1: Einmalig, 2: Merhmals, 3: Aus";
const char menu1232[] PROGMEM = "9: Abbruch, nicht Speichern";
const char menu1240[] PROGMEM = "Neue Weckzeit gesetzt";
//Strings für IR Menü
const char uhrzeit[]  PROGMEM = "  :     .  .    End Upd A/D Weck";
const char weckerma[] PROGMEM = "W xx: hh:mm   O MoDiMiDoFrSaSo  ";
const char saveweck[] PROGMEM = "Wecker xx: Save?Ja Nein   Zurück";
const char showad[]   PROGMEM = "P1:xxxx P2:xxxx Update    Zurück";

const char* fullmenu[] = {menu01  ,menu02  ,menu03  ,menu04  ,menu05  , //00..04
menu10  ,menu11  ,menu12  ,menu13  ,menu14  ,menu15 ,                   //05..10
menu100 ,menu101 ,menu102 ,menu103 ,                                    //11..14
menu110 ,                                                               //15
menu500 ,menu501 ,menu502 ,menu503 ,menu504 ,menu505 ,menu506 ,menu507, //16..23
menu510 ,menu520 ,menu530 ,menu540 ,menu550 ,menu560 ,                  //24..29
menu1000,menu1001,menu1002,menu1003,                                    //30..33
menu1200,menu1210,menu1220,menu1230,menu1231,menu1232,menu1240,         //34..40
uhrzeit ,weckerma,saveweck,showad,                              //41..44, Für IR
menu16  ,menu570 ,                                                      //45..46
menu1004,menu1005,menu1006,                                             //47..49
menu1010,menu1011,menu1012,menu1013,menu1014,                           //50..54
menu1020,menu1021,menu1022,                                             //55..57
menu1030,menu1031,                                                      //58..59
menu1023,menu1024,menu1025,menu1026,                                    //60..63
menu1007,                                                               //64
menu1027};                                                              //65

void strcpypandsend(u08 startstring, u08 numofstrings) {
u08 temp;
temp = 0;
while (temp < numofstrings){
strcpypshort(startstring+temp);
rs232sendstring();
temp++;
}
}

void strcpypshort (u08 stringno) { //Benötigt 24 Byte, jeder Aufruf spart jedoch 6 Byte gegenüber strcpy_P. Ab 5 Aufrufen, wurde Platz gespart
strcpy_P(rs232rausstring,fullmenu[stringno]);
}

void show232menu(void) {
u08 temp01;
u16 temp02;
s16 readedadvalue;
//Menu anzeigen
if (newmenu == 1){
newmenu = 0;
if (submenu232 == 24) {//Wecker zu ende eingestellt
rs232sendstring();
strcpypandsend(40,1);
getwecktime(wselected);
makewstatus(wselected);
rs232sendstring();
}
if (submenu232 == 23) {//Wecker aktivieren
strcpypandsend(37,3);
}
if (submenu232 == 22) { //Wochentage auswählen
makewstatus(wselected);
rs232sendstring();
strcpypandsend(36,1);
}
if (submenu232 == 21) { //Weckzeit eingeben, Wecker gewählt
strcpypshort(35);
wordtostr(wselected,2,7);
rs232sendstring();
}
if (submenu232 == 20) { //Wecker auswählen
rs232sendstring();
strcpypandsend(34,1);
}

if (submenu232 == 11) { //alle Weckzeiten anzeigen
newscreen();
strcpypandsend(15,1);
rs232sendstring();
//Schleife zum Anzeigen des Weckers
for (temp01=1;temp01<maxweckx;temp01++) {
getwecktime(temp01);
makewstatus(temp01);
rs232sendstring();
}
rs232sendstring();
strcpypandsend(12,3);
}
if (submenu232 == 10) {//Wecker Hauptmenu
newscreen();
strcpypandsend(11,1);
rs232sendstring();
strcpypandsend(12,3);
}
if (submenu232 == 100) {//A/D Werte anzeigen
newscreen();
strcpypandsend(30,1);
rs232sendstring();
for (temp01  = 0;temp01 <8;temp01++){
strcpypshort(33);
rs232rausstring[9] = temp01+48;
wordtostr(adwerte[temp01],4,17);
rs232sendstring();
}
rs232sendstring();
strcpypandsend(31,1);
strcpypandsend(47,3);//aufzeichnen
strcpypandsend(64,1);//stoppen
strcpypandsend(32,1);
}//ende A/D Wandler
if (submenu232 == 110) { //neue Aufzeichnung
strcpypandsend(50,2);
}
if (submenu232 == 111) { //Modus
strcpypandsend(52,1);
}
if (submenu232 == 112) { //Zeit
strcpypandsend(53,2);
}
if (submenu232 == 120) { //Anzeigen der AD Werte, kostet ca.178 Byte Flash
 if (recordermode != 2) { //Mode 1 oder 3 -> Daten von AD 1:
   strcpypshort(58);
   wordtostr(1,1,17);
   rs232sendstring();
   //Jetzt senden
   for(temp02 = 0;temp02 < eeprom_size_ad();temp02++) {
    readedadvalue = eeprom_read_ad(temp02,1);
    if (readedadvalue < 0) {
      break;
    }
    wordtostr(readedadvalue,4,0);
    rs232sendstring();
   }//Ende der Schleife
 }//Ende 1. Kanal
 if (recordermode != 1) { //Mode 2 oder 3 -> Daten von AD 2:
   strcpypshort(58);
   wordtostr(2,1,17);
   rs232sendstring();
   //Jetzt senden
   for(temp02 = 0;temp02 < eeprom_size_ad();temp02++) {
    readedadvalue = eeprom_read_ad(temp02,2);
    if (readedadvalue < 0) {
      break;
    }
    wordtostr(readedadvalue,4,0);
    rs232sendstring();
   }//Ende der Schleife
 }//Ende 2. Kanal
 strcpypandsend(59,1);
}
if (submenu232 == 130) { //Status anzeigen
rs232sendstring();
strcpypshort(55);
wordtostr(eepromindex,3,28);
rs232sendstring(); //Aufgezeichnete Datensätze
strcpypshort(56);
wordtostr(eeprom_size_ad(),3,28);
rs232sendstring(); //Aufzeichenbare Datensäte
if (recorderspeed != 0) { //Wenn nicht gestoppt
strcpypshort(57);
wordtostr(recorderspeed*eeprom_free_ad(),5,22);
rs232sendstring(); //Noch freie Minuten
}else{   //wenn gestoppt, dann sende dies auch
strcpypandsend(63,1);
}
if (recordermode == 3) { //Beide Sensoren
strcpypshort(61);
}else { //nur ein Sensor
strcpypshort(60);
wordtostr(recordermode,1,23);
}
rs232sendstring();
strcpypshort(62); //Zeigt die Uhrzeit, um die die Aufzeichnung gestartet wurde
wordtostr(eeprom_read_byte(&adrecstarttime[0]),2,17);
wordtostr(eeprom_read_byte(&adrecstarttime[1]),2,20);
rs232sendstring();
strcpypshort(65);
wordtostr(eeprom_read_byte(&adrecspeedeep),3,18);
rs232sendstring(); //Wieviele Mintuen zwischen zwei Aufzeichnungen liegen
strcpypandsend(59,1); //Zurück mit beliebiger Taste
}
if (submenu232 == 140) { //aufzeichnung wurde angehalten
  strcpypandsend(63,1);
  strcpypandsend(59,1); //Zurück mit beliebiger Taste
}
if (submenu232 == 50) { //config
  newscreen();
  strcpypandsend(16,1);
  rs232sendstring();
  strcpypandsend(17,6);//1..6
  strcpypandsend(46,1);//7
  strcpypandsend(23,1);//9
}
if ((submenu232 > 50)&&(submenu232 <57)) { //Wenn config-Untermenu ausgewählt wurde
newscreen();
strcpypandsend(submenu232-27,1);
}
if (submenu232 == 1) {//Hauptmenu
newscreen();
strcpypandsend(5,1);
rs232sendstring();
strcpypandsend(6,4);
rs232sendstring();
strcpypshort(10);
/* die verwendete Anzeige ist 9 stellig, das entspricht ungefähr:
1950 Jahre !!!
und der interne Zähler kann noch einiges mehr zählen,
nämlich so ungefähr eine Uptime von 8378 Jahren....
*/
wordtostr(uptimeminutes,9,12);
rs232sendstring();
strcpypshort(45);
wordtostr(rebootstat,3,9);
wordtostr(uptimedays,5,23);
rs232sendstring();
}
if (submenu232 == 0) {//goodbye
temp01 = 0;
if (hours > 4) {
  temp01++;
}
if (hours > 10) {
  temp01++;
}
if (hours > 16) {
  temp01++;
}
if (hours > 21) {
  temp01++;
}
strcpypandsend(temp01,1);
}
}//ende neues Menu
}

void rs232com(void){
s16 eingabe;
u08 temp01,temp02;
/*Menü Struktur
Passwort: 1234
1 Auswahl
1.1 Wecker
1.1.1 Übersicht anzeigen
1.1.2 Wecker setzen
1.1.2.1 Wecker auswählen
1.1.2.1.1 Zeit setzen
1.1.2.1.2 Wochentage setzen
1.1.2.1.3 Wiederholung setzen/aus
1.1.9 Zurück zum Hauptmenu
1.2 Config
1.2.1 Zeit setzen
1.2.2 Tag setzen
1.2.3 Monat setzen
1.2.4 Jahr setzen
1.2.5 Rs232 Echo
1.2.6 Punkte Blinken
1.2.7 Einstellungen Speichern
1.2.9 Zurück zum Hauptmenu
1.3 A/D Ports lesen
1.3.1 A/D Werte erneut einlesen
1.3.2 Neue Aufzeichnung Starten
1.3.3 Aufzeichnung einlesen
1.3.4 Aufzeichnungs Status
1.3.9 Zurück zum Hauptmenu
1.9 RS232 Sitzung beenden
*/
statusleds = statusleds | 0x80;//RS232 aktiv
eingabe = strtoword();
//Menu Berechnen
if (submenu232 == 24) {
submenu232 = 10;
newmenu = 1;
}
if (submenu232 == 23) {//Wecker aktiv Modus auswählen
if (eingabe == 9) { //Abbruch
eingabe = -1;
submenu232 = 10;
newmenu = 1;
}
if (eingabe == 3) {
eingabe = -1;
submenu232 = 24;
newmenu = 1;
savewecktime(wselected);
}
if ((eingabe == 1)||(eingabe == 2)) {
wsettings = eingabe;
eingabe = -1;
submenu232 = 24;
newmenu = 1;
savewecktime(wselected);
}
}
if (submenu232 == 22) {//Wochentage auswählen
if (eingabe == 9) {
eingabe = -1;
submenu232 = 23;
newmenu = 1;
}
if ((eingabe > 0) && (eingabe < 8)){//Wochentag aktivieren
newmenu = 1;
wdaysofweek = wdaysofweek | (0x80>>(eingabe-1));
}
}
if (submenu232 == 20) { //Auswahl des Weckers
if ((eingabe > 0) && (eingabe < maxweckx)) {
getwecktime(eingabe);
eingabe = -1;
submenu232 = 21;
newmenu = 1;
}
}
if (submenu232 == 21) { //Weckzeit einstellen
if (eingabe > -1) {
temp01 = eingabe/100;
temp02 = eingabe % 100;
eingabe = -1;
submenu232 = 22;
newmenu = 1;
if (temp01 < 24) {
  whour = temp01;
}
if (temp02 <60) {
  wminute = temp02;
}
wdaysofweek = 0;
wsettings = 0;
}
}
if ((submenu232 == 10)||(submenu232 == 11)) { // Wecker
if (eingabe == 2) {//Wecker setzen, Part 1
submenu232 = 20;
newmenu = 1;
}
if (eingabe == 9) {//zurück
  submenu232 = 1;
  newmenu = 1;
}
if (eingabe == 1) { //Übersicht
submenu232 = 11;
newmenu = 1;
}
eingabe = -1;
}

  
if (submenu232 == 1) { //1 Auswahl
if (eingabe == 1) {//Wecker
submenu232 = 10;
newmenu = 1;
}
if (eingabe == 2) {//dann Config
submenu232 = 50;
newmenu = 1;
}
if (eingabe == 3) {//dann A/D Wandler
submenu232 = 100;
newmenu = 1;
}
if (eingabe == 9) {//RS232 verlassen
submenu232 = 0;
newmenu = 1;
commtype = 0;
statusleds = statusleds & 0x7f;//status LED für RS232 aus
}
eingabe = -1;
}//ende auswahl
if (submenu232 == 100) {//A/D Wandler Werte
if (eingabe == 1) {//nochmal anzeigen
newmenu = 1;
}
if (eingabe == 2) {//Neue Aufzeichnung Starten
submenu232 = 110;
newmenu = 1;
}
if (eingabe == 3) {//Aufzeichnung einlesen
submenu232 = 120;
newmenu = 1;
}
if (eingabe == 4) {//Aufzeichnungsstatus
submenu232 = 130;
newmenu = 1;
}
if (eingabe == 5) {//Aufzeichnung stoppen
  submenu232 = 140;
  newmenu = 1;
  recorderspeed = 0; //Aufzeichnung anhalten
}
if (eingabe == 9) {//Zurück
newmenu = 1;
submenu232 = 1;
}
eingabe = -1;
}
//A/D Werte aufzeichnen
if (submenu232 == 110) { //neue aufzeichnung teil 1
if (eingabe == 1) { //neue aufzeichnung
recorderspeed = 0; //Damit nicht während der Einstellung fehlerhafte Daten geschrieben werden
submenu232 = 111;
newmenu = 1;
}
if (eingabe == 9) {//Doch lieber nicht, zurück
submenu232 = 100;
newmenu = 1;
}
eingabe = -1;
}
if (submenu232 == 111) { //Aufzeichnungsform auswählen
if ((eingabe > 0) && (eingabe < 4)) {
  recordermode = eingabe;
  submenu232 = 112;
  newmenu = 1;
  eingabe = -1;
}
}
if (submenu232 == 112) { //Aufzeichnungsgeschwindigkeit auswählen
if ((eingabe >= 0) && (eingabe < 256)) {
  eeprom_clear_ad();//Löschen der Speicherzellen
  recorderspeed = eingabe; //Muss vor eeprom_writeconfig_ad() ausgeführt werden
  eeprom_writeconfig_ad();//Schreibt die neuen Aufzeichnungs Infos
  lastcapture_uptimeminutes = uptimeminutes; //Damit die Aufzeichnung zeitlich korrekt startet
  adtempa = 0;
  adtempb = 0;
  captureno = 0;
  submenu232 = 130; //Aufzeichnungs Status ansehen
  newmenu = 1;
  eingabe = -1;
}
}
//Aufzeichnung ausgelesen/Einstellungen angesehen/gestoppt
//-> mit beliebiger Taste zurück
if ((submenu232 == 120)||(submenu232 == 130)||(submenu232 == 140)) {
 if (eingabe >= 0) {
  newmenu = 1;
  submenu232 = 100;
  eingabe = -1;
 }
}
// Config
if (submenu232 == 50) {
if ((eingabe > 0)&&(eingabe <7)) { // Uhrzeit, Tag,Monat,Jahr,RS232 Echo, Punkte blinken
submenu232 += eingabe;
newmenu = 1;
}
if (eingabe == 7) { //Weckzeiten speichern
speichereconfig();
newmenu = 1;
}
if (eingabe == 9) {//Zurück
submenu232 = 1;
newmenu = 1;
}
eingabe = -1;
}//ende config
if (eingabe > -1) { //Untermenu bei Config ausgewählt, jetzt Eingabe der Werte
if (submenu232 == 51) { //Uhrzeit
temp01 = eingabe/100;
temp02 = eingabe % 100;
if (temp01 < 24) {
  hours = temp01;
}
if (temp02 <60) {
  minutes = temp02;
  wdisabled = 0;
}
}
if (eingabe > 0) { //Damit nicht Tag/Monat Null
if (submenu232 == 52) { //Tag
if (eingabe < 32) {
  day = eingabe;
}
}
if (submenu232 == 53) { //Monat
if (eingabe < 13) {
  month = eingabe;
}
}
if (eingabe < 3) {
if (submenu232 == 55) { //RS232 Echo
  rs232echo = 2-eingabe;
}
if (submenu232 == 56) { //Punkte blinken
punkteblinken = 2-eingabe;
}
}
}//ende damit nicht Tag/Monat Null
if (submenu232 == 54) { //Jahr
year = eingabe;
}

if ((submenu232 > 50)&&(submenu232 < 57)) {
submenu232 = 50;
newmenu = 1;
}
eingabe = -1;
}
show232menu();
}

void warte(u16 zeit) { //warte 2857~20ms
warteschleife = 0;
while (warteschleife < zeit){
  warteschleife++;
}
}

void sendirstring(void) {
u08 temp01,sendstringpos,sendcursorpos;
warte(60000);
if ((submenuir < 21) || (submenuir > 23)) { //in diesem Fall ist mehr als ein Tastendruck nur störend
empmode = 0;
empfangir = 0xff;
}
if (empmode == 0) {
outp(0,TCCR1B);
sendstringpos = 0;
sendcursorpos = 0;
if (submenuir == 20) {
  sendstringpos = 2;
}
if (submenuir == 21) {
  sendstringpos = 6;
}
if (submenuir == 22) {
  sendstringpos = 9;
}
if (submenuir == 23) {
  sendstringpos = 14;
}
sendcursorpos = sendstringpos;
if ((submenuir > 23)&&(submenuir < 31)) { //Wochentage
  sendstringpos = (submenuir-24)*2+16;
  sendcursorpos = sendstringpos +64-16;//weil 2.Zeile
}

if (lcdupdatecomplexity == 3) { //Nur obere Zeile senden
zumsenden(136);//return home
for (temp01 = 0;temp01<16;temp01++) {
zumsenden(rs232rausstring[temp01]);
}
}
if (lcdupdatecomplexity == 2) { //Beide Zeilen senden
zumsenden(136);//return home
for (temp01 = 0;temp01<32;temp01++) {
zumsenden(rs232rausstring[temp01]);
if (temp01 == 15) { //zweite Zeile
zumsenden(0xd);
}
}
}
if (lcdupdatecomplexity == 1) { //nur die nötigen zwei Byte senden
zumsenden(rs232rausstring[sendstringpos]);
zumsenden(rs232rausstring[sendstringpos+1]);
}
//Cursor an/aus
if ((submenuir == 31)||(submenuir == 1)) {  //bei submenuir == 31 || 1 -> Cursor aus
zumsenden(129);
}else {
zumsenden (128);
}
//Platziere Cursor, für mehr Übersichtlichkeit
if ((submenuir > 19)&&(submenuir < 31)) {
  zumsenden((256*2)+128+sendcursorpos);
}

outp(1,TCCR1B);//aktiviere ir empfänger wieder
}//Ende empmode == 0
}

void showirmenu(void) {
u08 temp01;
if (submenuir == 1) {//zeige Uhrzeit + Datum und Wochentag
//hh:mm dd.mm.yyyy
strcpypshort(41);
wordtostr(hours,2,0);
wordtostr(minutes,2,3);
wordtostr(day,2,6);
wordtostr(month,2,9);
wordtostr(year,4,12);
}
if ((submenuir > 19)&&(submenuir < 31)) {//Wecker auswahl
//W xx: hh:mm   0 MoDiMiDoFrSaSo
if (submenuir == 20) {
getwecktime(wselected);
}
strcpypshort(42);
wordtostr(wselected,2,2);
wordtostr(whour,2,6);
wordtostr(wminute,2,9);
for (temp01 = 0;temp01<7;temp01++) {       //spart gegenüber 7 Einzelanweistungen 46 Byte
if ((wdaysofweek & (0x80>>temp01)) ==0) {//Tage mit -- erstetzen
rs232rausstring[16+temp01*2] = 45;
rs232rausstring[17+temp01*2] = 45;
}
}
if (wsettings == 1) { //Einmalig
rs232rausstring[14] = 69;
}
if (wsettings == 2) { //Multiple/Mehrmalig
rs232rausstring[14] = 77;
}
}

if (submenuir == 31) { //Weckzeit speichern?
  strcpypshort(43);
wordtostr(wselected,2,7);
}
if (submenuir == 50) { //A/D Wandler anzeigen
strcpypshort(44);
wordtostr(adwerte[1],4,3);
wordtostr(adwerte[3],4,11);
}
sendirstring();
newmenu = 0;
}

void ircom(void) {
/*Menü Struktur
0. Verlassen
1.Zeit anzeigen
20. Wecker anzeigen/auswählen
21 Std stellen
22 Min stellen
23 Mode stellen
24 Mo stellen
25 Di stellen
26 Mi stellen
27 Do stellen
28 Fr stellen
29 Sa stellen
30 So stellen
31 speichern
50 zeige A/D Wandler
*/
u08 empfangir2; //unterschied: die Variable muss gegenüber empfangir nich volatile sein, so dass die Codeoptimierung besser arbeiten kann
empfangir2 = empfangir;
empfangir = 0xff;
statusleds = statusleds | 0x40;//IR aktiv
if (empfangir2 == 0xff) {
  return;
}
//empmode = 2;
empfangir2 = empfangir2 & 0x3c; //extrahiere Tasten
if (submenuir == 0) { //IR Mode betreten
  submenuir = 1;
  newmenu = 1;
  empfangir2 = 0xff;
  lcdupdatecomplexity = 2;
}
if (submenuir == 1) {
if (empfangir2 == 0x20) {//Verlasse IR Modus
commtype = 0;
statusleds = statusleds & 0x3f;//status LED für RS232 und IR aus
submenuir = 0;
}
if (empfangir2 == 0x10) {//Refresh
newmenu = 1;
lcdupdatecomplexity = 3;
}
if (empfangir2 == 0x08) {//Gehe zu Wecker Menu
submenuir = 20;
newmenu = 1;
lcdupdatecomplexity = 2;
}
if (empfangir2 == 0x04) {// A/D Wandler zeigen
submenuir = 50;
newmenu = 1;
lcdupdatecomplexity = 2;
}
empfangir2 = 0xff;
}
if (submenuir == 50) { // A/D Wandler Menu
  if ((empfangir2 == 0x20)||(empfangir2 == 0x10)) { //Update
    newmenu = 1;
    lcdupdatecomplexity = 3;
  }
  if ((empfangir2 == 0x08)||(empfangir2 == 0x04)) { //Zurück
    submenuir = 1;
    newmenu = 1;
    lcdupdatecomplexity = 2;
  }
  empfangir2 = 0xff;
  }
if (submenuir == 20) {//Wenn Weckermode
if (empfangir2 == 0x20) {//gehe zurück
submenuir = 1;
newmenu = 1;
lcdupdatecomplexity = 2;
}
if (empfangir2 == 0x10) {//Wecker stellen
submenuir = 21;
newmenu = 1;
lcdupdatecomplexity = 0;
}
if (empfangir2 == 0x08) {//vorherigen Wecker
wselected--;
if (wselected == 0) {
  wselected = maxweck;
}
newmenu = 1;
lcdupdatecomplexity = 2;
}
if (empfangir2 == 0x04) {//nächster Wecker
wselected++;
if (wselected == maxweckx) {
  wselected = 1;
}
newmenu = 1;
lcdupdatecomplexity = 2;
}
empfangir2 = 0xff;
}
if ((submenuir > 20) &&(submenuir < 31)) {
  if (empfangir2 == 0x20) {
    submenuir--;
    lcdupdatecomplexity = 0;
  }
  if (empfangir2 == 0x10) {
    submenuir++;
    lcdupdatecomplexity = 0;
  }
  if (submenuir == 21) {//Studen stellen
  if (empfangir2 == 0x08) {
    whour--;
    lcdupdatecomplexity = 1;
  }
  if (empfangir2 == 0x04) {
    whour++;
    lcdupdatecomplexity = 1;
  }
  }
  if (submenuir == 22) {//Minuten stellen
    if (empfangir2 == 0x08) {
    wminute--;
    lcdupdatecomplexity = 1;
    }
    if (empfangir2 == 0x04) {
    wminute++;
    lcdupdatecomplexity = 1;
    }
  }
  if (submenuir > 22) {//gehe zum Speicher-menu
  if (empfangir2 == 0x08) {
  submenuir = 31;
  lcdupdatecomplexity = 2;
  }
  }
  if (submenuir == 23) { //aus, einfach, mehrfach wählen
  if (empfangir2 == 0x04) {
    wsettings++;
    lcdupdatecomplexity = 1;
  }
  }
  if (submenuir > 23) { //Wochentage an/abwählen
  if (empfangir2 == 0x04) {
    lcdupdatecomplexity = 1;
    wdaysofweek = wdaysofweek ^ (0x1<<(31-submenuir)); //Toggelt das betreffende Bit
    }
  }
  if (whour == 255) {
    whour = 23;
  }
  if(whour == 24) {
    whour = 0;
  }
  if (wminute == 255) {
    wminute = 59;
  }
  if (wminute == 60) {
    wminute = 0;
  }
  if (wsettings > 2) {
    wsettings = 0;
  }
  newmenu = 1;
  empfangir2 = 0xff;
}
if (submenuir == 31) { //Speichern
  if (empfangir2 == 0x20) { //Ja, speichern
    savewecktime(wselected);
    submenuir = 20;
  }
  if (empfangir2 == 0x10) { //Nein, nicht speichern
    submenuir = 20;
  }
  if ((empfangir2 == 0x08)||(empfangir2 == 0x04)) { //zurück
    submenuir = 21;
  }
  empfangir2 = 0xff;
  newmenu = 1;
  lcdupdatecomplexity = 2;
}
    
//empmode = 0;
}

void analyzedcf77(void) {
const u08 dcffaktor[8] = {1,2,4,8,10,20,40,80};
/*
Globale Variablen, die in dieser Prozedur besonders wichtig sind
u08 volatile currentdcfval;
u08 lastdcfval; //um festzustellen, ob sich der pegel gändert hat
u08 lastuptime,lastdowntime;//zur bestimmung, ob 0,1 oder neue minute
u08 dcfposition = 70;   //70 = nicht syncron
u08 checkbits;
currentdcfval wird 31 mal pro sec aktualisiert
wenn currentval = 0 -> noch keine neuen Daten
wenn currentval = 1 -> low empfangen
wenn currentval = 2 -> high empfangen
-> 2-4 mal high, so wurde eine Null empfangen
-> 5-8 mal high, so wurde eine Eins empfangen
-> 30-64 mal Null empfangen, es fängt eine neue Minute an

warte darauf, dass currentval = 2 wird, während dieser Warteperiode, wird lastdowntime++ gerechnet
*/
//Warte, bis ein Signal empfangen werden kann
lastdowntime = 0;
while (currentdcfval < 2){ //solange keine 1
lastdowntime++;
currentdcfval = 0;
if (lastdowntime > 64) { //es ist ein Fehler aufgetreten, kein DCF Signal
break;
}
while (currentdcfval == 0) {
  smallnottimecriticalcode();
  
}
}
//nun warte, bis die Eins zu ende empfangen wurde
if (lastdowntime < 65) {//wenn nicht durch Abbruch verlassen wurde
lastuptime = 0;
while (currentdcfval != 1){ //solange keine 0
lastuptime++;
currentdcfval = 0;
if (lastuptime > 8) { //es ist ein Fehler aufgetreten, kein DCF Signal
break;
}
while (currentdcfval == 0) {
  smallnottimecriticalcode();
}
}
if ((lastuptime < 9)&&(lastuptime > 1)) { //weder lastdowntime, noch lastuptime zeigen Anzeichen für Fehler
//beginne Signalauswertung
if (lastdowntime > 30) { //neue Minute
//logische Überprüfung und setze Uhr
if ((checkbit == 0)&&(rminutes[0] < 60)&&(rhours[0] < 24)&&(rdayofweek[0] > 0)&&
    (rdayofweek[0] < 8)&&(rmonth[0]>0)&&(rmonth[0]<13)&&(rday[0]<32)){
if (seconds > 30) {
minutes++;
uptimeminutes++;
wdisabled = 0;
}
seconds = 0;
//zuerst wurde nur die zu setzende Variable überprüft, jetzt müssen alle Variablen überprüft werden,
//dürfte die Wahrscheinlichkeit eines Fehlers nochmal reduzieren
if (rminutes[0]==(rminutes[1]+1))  {
if (rhours[0] == rhours[1]) {
if (rdayofweek[0] == rdayofweek[1]){
if (rday[0] == rday[1]) {
if (rmonth[0] == rmonth[1]){
if (ryear[0] == ryear[1]) {
dcferror = 0;
dcferrorminutes = 0;
minutes = rminutes[0];
wdisabled = 0;
hours = rhours[0];
dayofweek = rdayofweek[0];
day = rday[0];
month = rmonth[0];
year = year/100*100+ryear[0];//Da DCF77 Angaben nur zweistellig, Uhr aber 4 Stellige Jahreszahlen hat
}
}
}
}
}
}
// UDR = rminutes[2];
rminutes[1] = rminutes[0];
rhours[1] = rhours[0];
rdayofweek[1] = rdayofweek[0];
rday[1] = rday[0];
rmonth[1] = rmonth[0];
ryear[1] = ryear[0];
}//Ende logische Prüfung bestanden
rminutes[0] = 0;
rhours[0] = 0;
rdayofweek[0] = 0;
rday[0] = 0;
rmonth[0] = 0;
ryear[0] = 0;
dcfposition = 0;
checkbit = 0;
}
//Auswerten der Bits, während einer Minute
if (dcfposition <61) {//wenn dcfposition innerhalb der Toleranzen
dcfposition++;
if (lastuptime > 4) { //Eins empfangen
//mit swich code: 5728 Byte
//mit if code: 5586 Byte  -> 142 Byte gespart
/* Auswertung der Prüfbits
Die getesteten Bits zusammen mit dem Prüfbit ergeben immer eine grade Anzahl von High Bits
dcfposition = 29: 1. Prüfbit, bezieht sich nur auf die Minuten
dcfposition = 36: 2. Prüfbit, bezieht sich nur auf die Stunden
dcfposition = 59: 3. Prüfbit, bezieht sich auf Tage, Wochentage, Monate und Jahre
wenn am Ende des Durchlaufes checkbit gleich null, ist entweder kein oder mehr als ein Fehler aufgetreten
*/
if ((dcfposition > 21) &&(dcfposition < 29)){ //Minuten
rminutes[0] += dcffaktor[dcfposition-22];
}
if ((dcfposition > 21) &&(dcfposition < 30)){ //Checkbit 1
checkbit = 1-checkbit;
}
if ((dcfposition > 29) &&(dcfposition < 36)){ //Stunden
rhours[0] += dcffaktor[dcfposition-30];
}
if ((dcfposition > 29) &&(dcfposition < 37)){ //Checkbit 2
checkbit = 4-checkbit;
}
if ((dcfposition > 36) &&(dcfposition < 43)){ //Tage
rday[0] += dcffaktor[dcfposition-37];
}
if ((dcfposition > 42) &&(dcfposition < 46)){ //Wochentag
rdayofweek[0] += dcffaktor[dcfposition-43];
}
if ((dcfposition > 45) &&(dcfposition < 51)){ //Monat
rmonth[0] += dcffaktor[dcfposition-46];
}
if ((dcfposition > 50) &&(dcfposition < 59)){ //Jahr
ryear[0] += dcffaktor[dcfposition-51];
}
if ((dcfposition > 36) &&(dcfposition < 60)){ //Checkbit 3
checkbit = 16-checkbit;
}
}else{
if (dcfposition == 21) {//Dieses Bit ist normalerweise immer High, wenn dies aufgerufen wird, liegt ein Fehler vor
dcfposition = 70;
}
}
//Statusbit für Fehler setzen
}//ende DCF Position korrekt
}else{
dcfposition = 70;
}  //ende Signale fehlerfrei
}else{//ende downtime fehlerfrei
dcfposition = 70;
}
if (dcfposition > 60) {
dcferror = 1;
}
}

int main( void ){
u08 temp01;
initsystem();
for (;;) {
asm volatile("wdr");
updatetime();
setsegments();
smallnottimecriticalcode();
testallwecker();
adrecorder();
//zumsenden(0xf0);//Sendet ein Test-Zeichen; 72 wäre ein H für Hallo
if (eepromaway > 0) { //Das EEprom kann für die Weckzeiten nur beschrieben
                      //werden, wenn die Variable null ist
  eepromaway--;//dies ist eine Sicherheitsvorkehrung
}

if (commtype == 0) {
analyzedcf77();
statusleds = statusleds | 0x20;//DCF Aktiv  (Optimierbar ???)
  if (strtoword() == RS232code){
  for (temp01 = 0;temp01<36;temp01++) { //sonst könnten sich in dem String noch Reste vom IR Senden befinden
    rs232rausstring[temp01] = 32;
  }
  statusleds = statusleds & 0xdf;//DCF inaktiv
  dcferror = 1;
  commtype = 1;
  submenu232 = 1;
  newmenu = 1;
}
if (empfangir != 0xff){
if (wactive != 0) {
  wactive = 0;
  empfangir = 0xff;
} else { //Starte IR Kommunikation
  statusleds = statusleds & 0xdf;//DCF inaktiv
  dcferror = 1;
  commtype = 2;
  submenuir = 0;
  newmenu = 1;
  getwecktime(1);
}
}
}

if (commtype == 1) {
outp(0,TCCR1B);  //stop timer1, IR Empfang
rs232com();
dcfposition = 70;
dcferror = 1; //Eigentlich überflüssig, da die Variable noch bei Commtype == 0 gesetzt werden würde
} else {
  outp(1,TCCR1B);  //startet timer1, IR Empfang aktiv
}
if (commtype == 2) {
  ircom();
  if (newmenu == 1) {
  showirmenu();
  }
dcfposition = 70;
dcferror = 1; //Eigentlich überflüssig, da die Variable noch bei Commtype == 0 gesetzt werden würde
}

}
}

//End of File
