Source file: /~heha/Mikrocontroller/Displays/utft/Kalender.zip/ILI9325/Kalender.cpp

// Müllkalender, basierend auf
// UTFT_Demo_320x240
// Mikrocontroller: ATmega328

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <string.h>
#include "UTFT.h"
#include "TouchScreen.h"

// Dieser Datensatz: Frank Philippczyk 2016 + Januar 2017
PROGMEM const char GelbeTonne[]={
/*Januar*/	6,20,0,
/*Februar*/	5,19,0,
/*März*/	4,18,0,
/*April*/	2,15,29,0,
/*Mai*/		13,27,0,
/*Juni*/	10,24,0,
/*Juli*/	8,22,0,
/*August*/	5,19,0,
/*September*/	2,16,30,0,
/*Oktober*/	14,28,0,
/*November*/	11,25,0,
/*Dezember*/	9,23,0};

PROGMEM const char GraueTonne[]={
/*Januar*/	12,26,0,
/*Februar*/	11,25,0,
/*März*/	10,24,0,
/*April*/	7,21,0,
/*Mai*/		6,20,0,
/*Juni*/	2,16,30,0,
/*Juli*/	14,28,0,
/*August*/	11,25,0,
/*September*/	8,22,0,
/*Oktober*/	7,20,0,
/*November*/	4,18,0,
/*Dezember*/	1,15,30,0};

PROGMEM const char BlaueTonne[]={
/*Januar*/	18,0,
/*Februar*/	17,0,
/*März*/	16,0,
/*April*/	13,0,
/*Mai*/		11,0,
/*Juni*/	8,0,
/*Juli*/	6,0,
/*August*/	3,31,0,
/*September*/	28,0,
/*Oktober*/	26,0,
/*November*/	23,0,
/*Dezember*/	21,0};

PROGMEM const char BrauneTonne[]={
/*Januar*/	4,11,18,25,0,
/*Februar*/	3,10,17,24,0,
/*März*/	2,9,16,23,31,0,
/*April*/	6,13,20,27,0,
/*Mai*/		4,11,19,25,0,
/*Juni*/	1,8,15,22,29,0,
/*Juli*/	6,13,20,27,0,
/*August*/	3,10,17,24,31,0,
/*September*/	7,14,21,28,0,
/*Oktober*/	6,12,19,26,0,
/*November*/	3,9,17,23,30,0,
/*Dezember*/	7,14,21,29,0};

struct _time{
 char sec,min,hour;
};

struct _date{
 char day,month,year,jh;
 char maxday() const;	// Letzten Tag des Monats ermitteln
 void morgen();		// Tag um 1 erhöhen
 bool ereignis(const char*p) const;	// Ereignis aus o.a. Tabelle finden
 char dow() const;
};


// Makros um die Kompilierzeit als Startzeit einzusetzen
// (d.h. automatisches Stellen der Uhr beim „Betanken“)
#define YEAR (	__DATE__[9]*10+__DATE__[10]-528)
#define MONTH (	__DATE__[0]=='J'&&__DATE__[1]=='a'?1:\
		__DATE__[0]=='F'?2:\
		__DATE__[0]=='M'&&__DATE__[2]=='r'?3:\
		__DATE__[0]=='A'&&__DATE__[1]=='p'?4:\
		__DATE__[0]=='M'?5:\
		__DATE__[0]=='J'&&__DATE__[2]=='n'?6:\
		__DATE__[0]=='J'?7:\
		__DATE__[0]=='A'?8:\
		__DATE__[0]=='S'?9:\
		__DATE__[0]=='O'?10:\
		__DATE__[0]=='N'?11:\
		__DATE__[0]=='D'?12:0)
#define DAY ((__DATE__[4]>='0'?__DATE__[4]:'0')*10+__DATE__[5]-528)
#define HOUR (	__TIME__[0]*10+__TIME__[1]-528)
#define MIN (	__TIME__[3]*10+__TIME__[4]-528)
#define SEC (	__TIME__[6]*10+__TIME__[7]-528)

// Dem Arduino fehlt eine Echtzeituhr.
// Daher wird die Uhr bei Stromausfall wenigstens „angehalten“.
// Da eine Stromausfall-Früherkennung (Puffer-Elko und Signalisierungseingang)
// fehlt, muss die aktuelle Uhrzeit im EEPROM nachgeführt werden.
// Um den EEPROM nicht übermäßig mit Schreibvorgängen zu belasten,
// wird jedes Bit nur stündlich gelöscht und beschrieben,
// macht <9000 Zugriffe pro Jahr
// und damit eine von Atmel garantierte Haltbarkeit von über 10 Jahren.
// Die Minuten werden bitweise (in 60 Bits) gespeichert.
// Sekunden werden nicht gespeichert.
// Beim Restart wird kurzerhand Sekunde 55 angesetzt.
EEMEM byte ee_hdmy[4];	// Stunde,Tag,Monat,Jahr als Bytes
EEMEM byte ee_min[8];	// 60 Bits für „verbrauchte“ Minuten

struct _jetzt{
 _time t;
 _date d;
}jetzt={{SEC,MIN,HOUR},{DAY,MONTH,YEAR,20}};

UTFT dc;
TouchScreen ts;

extern const byte ArialBP19[] PROGMEM;
extern const byte ArialBP24[] PROGMEM;
/*
void debug(const __FlashStringHelper*fmt,...) {
 static UTFT dc2;
 dc2.orient=0;
 dc2.flags=COOKED;
 dc2.clrClip();
// dc2.clrScale();
 dc2.setColor(COLOR_BLACK);
 dc2.setBackColor(COLOR_WHITE);
 dc2.setFont(TimesP18);
 va_list va;
 va_start(va,fmt);
 dc2.vprintf(fmt,va);
 va_end(va);
 dc2.X=0;
 dc2.Y+=dc2.getFontHeight();
 if (dc2.Y>DISP_Y_SIZE-14) dc2.Y=0;
}
*/
int deb1,deb2;

static void ClearMiddle(word bgcolor=COLOR_BLACK) {
 dc.clrOrg();
 dc.setClip(2,19,dc.getDisplayXSize()-3,dc.getDisplayYSize()-19);
 dc.setBackColor(bgcolor);
 dc.clrScr();
 dc.setColor(~bgcolor);
}

const char*stridx_P(const char*s, int idx) {
 if (idx) do s+=strlen_P(s)+1; while(--idx);
 return s;
}

/********************************
 * _date-Objektfunktionen	*
 ********************************/

bool _date::ereignis(const char*p) const{
// monatsweise vorrücken
 for (byte i=month;i>1;i--) p+=strlen_P(p)+1;
// Tag suchen
 return strchr_P(p,day)!=0;
}

char _date::dow() const{
 char m=month, y=year;
 if (m<3) m+=10, y--;
 else m-=2;
 return ((26*m-2)/10+day+y+(y>>2)+(jh>>2)-2*jh+14)%7;
}

char _date::maxday() const{
 if (month==2) return year&3?28:29;	// ohne Jahrhundertregel
 return ((month>>3)^month)&1?31:30;	// ungerade Monate 31 Tage, ab August (8!) gerade Monate
}

void _date::morgen() {
 if (++day>maxday()) {
  day=1; if (++month>12) {
   month=1; ++year;
  }
 }
}

/*************************************
 * Animation des Randes (sekündlich) *
 *************************************/

struct _animate{
 int y,ye;
 word color;	// Für animierten Rand für „morgen“
 void paint();
}animate_morgen;

void _animate::paint() {			// jede Sekunde
 if (y) {
  dc.setColor(color);
  const int w=10;		// Randbreite
  dc.fillRect(16-w,y-w,16,ye+w);	// links
  dc.fillRect(16,y-w,224,y);		// oben
  dc.fillRect(224,y-w,224+w,ye+w);	// rechts
  dc.fillRect(16,ye,224,ye+w);		// unten
  color=~color;
 }
}

/****************************************
 * Müll-Box anzeigen und		*
 * Animationskoordinaten aktualisieren	*
 * (1x täglich zu Mitternacht)		*
 ****************************************/

byte Sorte(const _date&d) {		// jedes Bit des Returnwertes repräsentiert eine Müllsorte
 byte ret=0;
 if (d.ereignis(GelbeTonne)) ret|=1;
 if (d.ereignis(GraueTonne)) ret|=2;
 if (d.ereignis(BlaueTonne)) ret|=4;
 if (d.ereignis(BrauneTonne)) ret|=8;
 return ret;
}

void gotoXdY(int x, int dy) {
 dc.gotoXY(x,dc.whereY()+dy);
}

static PROGMEM const word bgcolors[9]={COLOR_BLACK,		// kennzeichnende Hintergrundfarben
  COLOR_YELLOW,COLOR_GRAY,COLOR_BLUE,RGB(139,69,19),
  COLOR_PURPLE,COLOR_LIME,COLOR_AQUA,COLOR_TEAL};
static PROGMEM const word fgcolors[9]={COLOR_YELLOW,		// kontrastreiche Vordergrundfarben
  COLOR_BLACK,COLOR_BLACK,COLOR_WHITE,COLOR_WHITE,
  COLOR_WHITE,COLOR_BLACK,COLOR_WHITE,COLOR_WHITE};

void ShowMuell(char tag) {
 _date d=jetzt.d;
 for (char i=0; i<tag; i++) d.morgen();
 byte sorte=Sorte(d);
 char lsb=0;
 if (sorte) lsb=__builtin_ctz(sorte)+1;	// Bitnummer ermitteln
 char lines=__builtin_popcount(sorte);		// Anzahl auszugebender Textzeilen
 if (!lines) lines=1;
 word bc=pgm_read_word(bgcolors+lsb);
 word fc=pgm_read_word(fgcolors+lsb);
 dc.setColor(bc);
 int y=dc.whereY();
 int ye=y+34+20*lines;				// Jede Textzeile 20 Pixel
 if (tag==1) {
  if (sorte) animate_morgen.y=y, animate_morgen.ye=ye, animate_morgen.color=fc;
  else animate_morgen.y=0;
 }
 dc.fillRoundRect(16,y,224,ye);
 dc.setColor(fc);
 dc.drawRoundRect(16,y,224,ye);
 dc.setBackColor(bc);
 dc.gotoXY(30,y+4);
 static PROGMEM const char hm[]="heute\0morgen\0übermorgen";
 dc.print(reinterpret_cast<const __FlashStringHelper*>(stridx_P(hm,tag)));
 gotoXdY(30,24);
 if (sorte) {
  for (byte m=0x80;m;m>>=1) if (m&sorte) {
   switch (m) {
    case 1: dc.print(F("Gelbe Tonne")); break;
    case 2: dc.print(F("Graue Tonne")); break;
    case 4: dc.print(F("Blaue Tonne")); break;
    case 8: dc.print(F("Braune Tonne")); break;
    case 16: dc.print(F("Schadstoffmobil")); break;
    case 32: dc.print(F("Essensreste")); break;
    case 64: dc.print(F("Pferdeäpfel")); break;
    case 128: dc.print(F("Geburtstag")); 
      gotoXdY(30,dc.getFontHeight()); dc.printf(F("%s"),"Mama?"); break;
   }
   gotoXdY(30,20);
  }
 }else{
  dc.print(F("Kein Abfall"));
  gotoXdY(30,20);
 }
 gotoXdY(16,20);	// Cursorposition für die nächste Box hinterlassen
}

/****************************************
 * Uhrzeit anzeigen / aktualisieren	*
 * (1x pro Sekunde)			*
 ****************************************/
void ShowDate() {
 static PROGMEM const char wt[]=
   "Sonntag\0Montag\0Dienstag\0Mittwoch\0Donnerstag\0Freitag\0Sonnabend";
 static PROGMEM const char mn[]=
   "Januar\0Februar\0März\0April\0Mai\0Juni\0"
   "Juli\0August\0September\0Oktober\0November\0Dezember";
 dc.setBackColor(COLOR_BLACK);
 dc.setColor(COLOR_WHITE);
 const char*p=stridx_P(wt,jetzt.d.dow());	// Wochentag
 char s[20];

 sprintf_P(s,PSTR(" %S "),p);
 dc.gotoXY(120,240);
 dc.print(s);

 p=stridx_P(mn,jetzt.d.month-1);	// Monatsname
 sprintf_P(s,PSTR(" %d. %S %d%02d "),jetzt.d.day,p,jetzt.d.jh,jetzt.d.year);
 dc.gotoXY(120,259);
 dc.print(s);
}

void ShowTime() {
 char s[20];
 sprintf_P(s,PSTR(" %02d:%02d:%02d "),jetzt.t.hour,jetzt.t.min,jetzt.t.sec);
 dc.gotoXY(120,278);
 dc.print(s);
}

// Zeit um 1 Sekunde erhöhen
void AddSec() {
 if (++jetzt.t.sec==60) {
  jetzt.t.sec=0; if (++jetzt.t.min==60) {
   jetzt.t.min=0; if (++jetzt.t.hour==24) {
    jetzt.t.hour=0; jetzt.d.morgen();
   }
  }
 }
}

// Interruptbedienroutine für Timer1 (Uhr) im Sekundentakt
ISR(TIMER1_COMPA_vect) {
 AddSec();
}

// Interruptbedienroutine für Pin-Change (Touchscreen)
// Tut nichts als den Prozessor aufzuwecken
EMPTY_INTERRUPT(PCINT0_vect);

void Rahmen() {
  // Clear the screen and draw the frame
 dc.setBackColor(COLOR_SILVER);
 dc.clrScr();
 dc.setColor(COLOR_BLACK);
 dc.setFont(ArialBP19);
 dc.align=2;
 dc.gotoXY(120,320-19);
 dc.print(F("Frank Philippczyk, 2016"));	// Straßenzug, Jahr
}

// Hintergrund malen (jeden Tag oder nach Touch-Ereignis neu)
void Hintergrund() {
 Rahmen();

 dc.gotoXY(120,0);
 dc.print(F("Müllkalender"));

 ClearMiddle(COLOR_WHITE);

 dc.clrOrg();

 static const word colors[6] PROGMEM={0,RGB(255,0,255),COLOR_RED,COLOR_GREEN,COLOR_BLUE,COLOR_YELLOW};

 ClearMiddle();

// Draw some filled, rounded rectangles
 for (int i=1; i<6; i++) {
  dc.setColor(pgm_read_word(colors+i));
  int j=i*20;
  dc.fillRoundRect(190-j,130+j,250-j,190+j);
 }

// Draw some filled circles
 for (int i=1; i<6; i++) {
  dc.setColor(pgm_read_word(colors+i));
  int j=i*20;
  dc.fillCircle(20+j,80+j,30);
 }

// Draw some lines in a pattern
  dc.setColor(COLOR_RED);
 int y=dc.clip.B-dc.clip.T;
 for (int i=0; i<y; i+=5) {
  dc.drawLine(dc.clip.L,dc.clip.T+i,dc.clip.L+(i<<1),dc.clip.B);
 }
 for (int i=y; i>0; i-=5) {
  dc.drawLine(dc.clip.R,dc.clip.T+i,dc.clip.L+(i<<1),dc.clip.T);
 }
 dc.setColor(0,255,255);
 for (int i=y; i>0; i-=5) {
  dc.drawLine(dc.clip.L,dc.clip.T+i,dc.clip.R-(i<<1),dc.clip.T);
 }
 for (int i=0; i<y; i+=5) {
  dc.drawLine(dc.clip.R,dc.clip.T+i,dc.clip.R-(i<<1),dc.clip.B);
 }
}

/****************************************
 * Uhrzeit-Eingabe per Touchscreen	*
 ****************************************/

int buttonidx;
struct _rect{
 int l,t,r,b;	// anklickbare Knöpfe
}buttons[14];

_jetzt Stellen;	// Zu stellende Uhrzeit

void Button(int x, int y, const char*s,
  word tlcolor=RGB(240,240,240), word brcolor=RGB(128,128,128)) {
 int cx=dc.getTextExtent(s);
 int x1=x-(cx>>1)-10;
 int x2=x1+cx+20;
 int y1=y-2;
 int y2=y1+4+19+4;
 dc.setColor(COLOR_SILVER);
 dc.fillRect(x1,y1,x2,y2);
 dc.setColor(tlcolor);
 dc.fillRect(x1-2,y1-2,x1,y2+2);	// Schattenrand: links
 dc.fillRect(x1,y1-2,x2+2,y1);		// oben
 dc.setColor(brcolor);
 dc.fillRect(x2,y1,x2+2,y2+2);		// rechts
 dc.fillRect(x1,y2,x2,y2+2);		// unten
 dc.drawPixel(x1-1,y2+1);		// links unten
 dc.drawPixel(x2+1,y1-1);		// rechts oben
 dc.gotoXY(x,y);
 dc.setColor(COLOR_BLACK);
 dc.setBackColor(COLOR_SILVER);
 dc.print(s);
 if (tlcolor!=brcolor) {
  _rect&r=buttons[buttonidx++];
  r.l=x1-2;
  r.t=y1-2;
  r.r=x2+2;
  r.b=y2+2;
 }
}

void UpDown(int n) {
 char s[20];
 int y=dc.whereY();
 sprintf_P(s,PSTR("%d"),n);
 Button(120,y,s,COLOR_BLUE,COLOR_BLUE);
 Button(40,y,"–");			// Mathematisches Minus!
 Button(200,y,"+");
}

// Zeichnet alles oder nur das Clipping-Rechteck
void SetTime_Redraw(byte was=0xFF) {
 if (was&0x80) {
  Rahmen();		// Alles löschen
  dc.gotoXY(120,0);
  dc.print(F("Datum/Uhrzeit stellen"));
  dc.setColor(RGB(200,200,200));
  dc.fillRect(2,19,238,301);
 }
 dc.setFont(ArialBP24);
 dc.align=2;
 dc.gotoXY(120,30);	// oben anfangen
 buttonidx=0;
 if (was&0x01) UpDown(Stellen.d.jh*100+Stellen.d.year); gotoXdY(120,38);
 if (was&0x02) UpDown(Stellen.d.month); gotoXdY(120,38);
 if (was&0x04) UpDown(Stellen.d.day); gotoXdY(120,38);
 if (was&0x08) UpDown(Stellen.t.hour); gotoXdY(120,38);
 if (was&0x10) UpDown(Stellen.t.min); gotoXdY(120,38);
 if (was&0x20) UpDown(Stellen.t.sec); gotoXdY(120,38);
 if (was&0x80) {
  Button(40,260,"OK");
  Button(160,260,"Abbrechen");
 }
}

struct _point{
 int x,y;
}touchpoint;

// Kalibrierdaten für Touchscreen
// Index 0: A/D-Ergebnis für linke Kante
// Index 1: A/D-Ergebnis für obere Kante
// Index 2: A/D-Ergebnis für rechte Kante
// Index 3: A/D-Ergebnis für untere Kante
const word calib[4]={4000,8000,65000,65000};

word wwtrafo(word x,word a,word e,word A, word E) {
 return (unsigned long)(x-a)*(E-A)/(e-a)+A;
}
 
void Warte() {
 dc.clrOrg();
 dc.clrClip();
 for (;;) {
  if (ts.isTouching()) break;
  _delay_ms(100);
  sleep_cpu();
 }
 dc.setColor(COLOR_YELLOW);
 int l=239,t=319,r=0,b=0;
 while (ts.isTouching()) {
  int x=touchpoint.x=wwtrafo(ts.readTouch(TOUCH_X),calib[0],calib[2],0,DISP_X_SIZE-1);
  int y=touchpoint.y=wwtrafo(ts.readTouch(TOUCH_Y),calib[1],calib[3],0,DISP_Y_SIZE-1);
  dc.setColor(255,240,0);
  dc.fillCircle(x,y,20);
  dc.setColor(COLOR_BLUE);
  dc.drawCircle(x,y,20);
  dc.drawCircle(x,y,21);
  dc.drawCircle(x,y,22);
  if (l>x-22) l=x-22;
  if (t>y-22) t=y-22;
  if (r<x+22) r=x+22;
  if (b<y+22) b=y+22;
  _delay_ms(200);	// zeige Berührpunkt zur Kontrolle
 }
 dc.setClip(l,t,r,b);
 SetTime_Redraw();	// Nur den vermatschten Bereich neu zeichnen
 dc.clrClip();
}

int HitTest() {
 for (int i=0; i<14; i++) {
  _rect&r=buttons[i];
  if (r.l<=touchpoint.x && touchpoint.x<r.r
  &&  r.t<=touchpoint.y && touchpoint.y<r.b) return i;	// gefunden
 }
 return -1;		// daneben!
}

void SetTime() {
 Stellen=jetzt;
 dc.clrClip();
 SetTime_Redraw();
 for(;;) {
  Warte();
  int ht;
  switch (ht=HitTest()) {
   case 0: Stellen.d.year--; if (Stellen.d.year<0) Stellen.d.year=99, Stellen.d.jh--; break;
   case 1: Stellen.d.year++; if (Stellen.d.year>99) Stellen.d.year=0, Stellen.d.jh++; break;
   case 2: Stellen.d.month--; if (Stellen.d.month<1) Stellen.d.month=12; break;
   case 3: Stellen.d.month++; if (Stellen.d.month>12) Stellen.d.month=1; break;
   case 4: Stellen.d.day--; if (Stellen.d.day<1) Stellen.d.day=Stellen.d.maxday(); break;
   case 5: Stellen.d.day++; if (Stellen.d.day>Stellen.d.maxday()) Stellen.d.day=1; break;
   case 6: Stellen.t.hour--; if (Stellen.t.hour<0) Stellen.t.hour=23; break;
   case 7: Stellen.t.hour++; if (Stellen.t.hour>23) Stellen.t.hour=0; break;
   case 8: Stellen.t.min--; if (Stellen.t.min<0) Stellen.t.min=59; break;
   case 9: Stellen.t.min++; if (Stellen.t.min>59) Stellen.t.min=0; break;
   case 10: Stellen.t.sec--; if (Stellen.t.sec<0) Stellen.t.sec=59; break;
   case 11: Stellen.t.sec++; if (Stellen.t.sec>59) Stellen.t.sec=0; break;
   case 12: jetzt=Stellen; /*nobreak*/	// Daten übernehmen und Uhr starten
   case 13: return;			// Daten verwerfen
   default: goto noredraw;
  }
  SetTime_Redraw(1<<(ht>>1));	// Nur eine Knopfzeile neu malen
noredraw:;
 }
}

// Hauptprogramm
int main() {
 dc.InitLCD(0);
 ts.Init();
 TCCR1A=0x00;	// CTC-Modus mit TOP=OCR1A
 TCCR1B=0x0D;	// Vorteiler 1024
 OCR1A =15625-1;// Interruptfrequenz 1 Hz (exakt!)
 TIMSK1=0x02;	// OCIE1A
 SMCR  =0x01;	// nur CPU anhalten
 PRR   =0xF7;	// Alles anhalten außer Timer1 (sollte Icc=8mA generieren)
 PCMSK0=0x02;
 PCICR =0x01;	// Pegelwechselinterrupt für Touchscreen aktivieren
 sei();

// 1. Die Start-Uhrzeit kann wegen eines Fehlers in avr-gcc (C-Compiler)
// nicht im EEPROM vorgegeben werden (siehe avr-gcc-bug.cpp)
// 2. Beim Arduino ist die EESAVE-Fuse mit 1 gesetzt und (via USB) unveränderlich.
// Angeblich!
// Daher ist beim Firmware-Update der EEPROM-Speicher mit 0xFF gefüllt.
 byte hdmy[4];
 eeprom_read_block(hdmy,ee_hdmy,4);
 if (hdmy[0]<24
 && byte(hdmy[1]-1)<31
 && byte(hdmy[2]-1)<12
 && hdmy[3]<100) {	// gültiges Datum im EEPROM?
  memcpy(&jetzt.t.hour,hdmy,4);	// Dann nimm dieses!
  jetzt.t.min=0;
  for (char i=8; --i>=0;) {			// Suche höchstwertiges 0-Bit in ee_min
   byte b=~eeprom_read_byte(ee_min+i);
   if (b) {
    jetzt.t.min=(i<<3)+16-__builtin_clz(b);	// Dies bestimmt die letzte Minute vor dem Stromausfall (Bit 59 wird nie gesetzt)
    break;
   }
  }
  jetzt.t.sec=55;	// gegen Ende
 }
 
 byte tag=0;
 byte minute=jetzt.t.min;
 for(;;) {
  if (minute!=jetzt.t.min) {
   eeprom_update_block(&jetzt.t.hour,&ee_hdmy,4);
   if (jetzt.t.min==0) {
    for (char i=0; i<8; i++) {
     while(EECR&0x02);
     EEAR=word(ee_min+i);
     cli();
     EECR=0x14;	// EEPROM-Byte nur löschen (1,8 ms), nicht programmieren
     EECR=0x16;
     sei();
    }
   }else{
    while(EECR&0x02);
    EEAR=word(ee_min+(minute>>3));
    EEDR=~(1<<(minute&7));	// Wenn Minute 0 vergangen, brenne 0b11111110, bei Minute 1 brenne 0b11111101 usw.
    cli();
    EECR=0x24;	// EEPROM-Byte nur programmieren (1,8 ms), nicht löschen. Daher bleiben Null-Bits stehen.
    EECR=0x26;
    sei();
   }
   EECR=0;
   minute=jetzt.t.min;
  }
  if (tag!=jetzt.d.day) {
   tag=jetzt.d.day;
   Hintergrund();
   dc.setFont(ArialBP24);
   dc.align=0;
   dc.gotoXY(16,30);	// oben anfangen
   ShowMuell(0);	// heute (oben)
   ShowMuell(1);	// morgen (darunter)
   ShowMuell(2);
   dc.setFont(ArialBP19);	// kleiner Font
   dc.align=2;			// zentrieren
   ShowDate();
  }
  if (ts.isTouching()) {
   SetTime();
   tag=0;
  }else{
   dc.setColor(COLOR_WHITE);
   ShowTime();
   animate_morgen.paint();
  }
  sleep_cpu();	// auf dem Arduino-Board verlöschen 2 LEDs
 }
}
Detected encoding: UTF-80