/*************************************************** * DCF77 Decoder Uhr: * Anzeige von Zeit, Datum, Wochentag, * Sommer-/Winterzeit-Kuerzel, Quarz-Gangreserve * Ausgabe auf LCD und RS232 * MCU: ATMEL AVR ATtiny4313 * DCF77 input code low active = PD6 * DCF77 output code ASCII 0/1 = PD1,RS232,9k6,8N1 * Quarzfrequenz 4,096 MHz (3,686 MHz) * CPU Takt 4,096 MHz (3,686 MHz) * * LCD 2x16, Vierbitmodus * PortB LCD Ausgabe: R/W=GND * PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 * n/a LED ena rs D7 D6 D5 D4 * Adapter auf LCD-Port: * PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 * D7 D6 D5 D4 LED n/a ena rs * * ATtiny4313 memory use summary [bytes]: * Segment Begin End Code Data Used Size Use% * [.cseg] 0x000000 0x000810 2028 36 2064 4096 50.4% * [.dseg] 0x000060 0x00007b 0 27 27 256 10.5% * [.eseg] 0x000000 0x000000 0 0 0 256 0.0% * Assembly complete, 0 errors. 0 warnings * Rev.: 02042018 ***************************************************/ #include #include #include /* * Definitionen: * Im SRAM reservierte Bytes */ static volatile uint8_t precnt; // Vorteiler 1:100 fuer Gangreserve static volatile uint8_t sekunde; // Sekundenpufferbyte binaer static volatile uint8_t minute; // Minutenpufferbyte binaer static volatile uint8_t stunde; // Stundenpufferbyte binaer static volatile uint8_t tag; // Tag-Pufferbyte binaer (1-31) static volatile uint8_t monat; // Monat-Pufferbyte binaer (1-12) static volatile uint8_t jahr; // Jahr-Pufferbyte binaer (0-99) static volatile uint8_t wtag; // Wochentag-Pufferbyte binaer (1-7) static volatile uint8_t block; // Stoeraustastung /* DCF77-Tabelle, eingehende Bits werden in * Byte 5, Bit 7 eingeschoben und alle weiteren * Bits um eine Position weitergerueckt, * Byte/Bit: * 0/6-7: 0bxx000000 Zeitzone (6=1 Som,7=1 Wint) * 1/2-5: 0b00xxxx00 Minute Einer * 1/6-7: 0bxx000000 Minute Zehner LSB * 2/0: 0b0000000x Minute Zehner MSB * 2/1: 0b000000x0 Pruefbit 1 (Minuten) * 2/2-5: 0b00xxxx00 Stunde Einer * 2/6-7: 0bxx000000 Stunde Zehner * 3/0: 0b0000000x Pruefbit 2 (Stunden) * 3/1-4: 0b000xxxx0 Kalendertag Einer * 3/5-6: 0b0xx00000 Kalendertag Zehner * 3/7: 0bx0000000 Wochentag LSB * 4/0-1: 0b000000xx Wochentag MSB * 4/2-5: 0b00xxxx00 Monat Einer * 4/6: 0b0x000000 Monat Zehner * 4/7: 0bx0000000 Jahr Einer LSB * 5/0-2: 0b00000xxx Jahr Einer MSB * 5/3-6: 0b0xxxx000 Jahr Zehner * 5/7: 0bx0000000 Pruefbit 3 (Datum) */ static volatile uint8_t dcftab[6]; /* DCF-Puffer 1, aktuelle, decodierte Uhrzeit * Byte 0: 0b0000000x Zeitzone * Byte 1: 0b0000xxxx Minute Einer * Byte 2: 0b00000xxx Minute Zehner * Byte 3: 0b0000xxxx Stunde Einer * Byte 4: 0b000000xx Stunde Zehner * Byte 5: 0b0000xxxx Kalendertag Einer * Byte 6: 0b000000xx Kalendertag Zehner * Byte 7: 0b00000xxx Wochentag * Byte 8: 0b0000xxxx Monat Einer * Byte 9: 0b0000000x Monat Zehner * Byte 10: 0b0000xxxx Jahr Einer * Byte 11: 0b0000xxxx Jahr Zehner */ static volatile uint8_t dcbuf1[12]; /* * Konstanten-Definitionen: */ #define DATEN PORTB #define DATEN_DDR DDRB #define LICHT 6 // 0b0x000000 Portbit LCD-Beleuchtung #define ENA 5 #define RS 4 #define ZEIT0 512 // Zeitschleifen Ladewerte #define ZEIT1 65535U // #define MCUfreq 4096000 // Quarzfrequenz 4,096 MHz (resp. 3686000) // // (Fuses:0xFD,0xDF,0xFF) #define COMP 40959 // Timer compare bei 0,01 sec {MCUfreq/(40960-1)} // // (STK500=36859; bei 4 MHz=39999) #define BDRT 27 // (24) Baudratenteiler fuer 9600 Bd festlegen /* * Registerdefinitionen (als globale Variablen): */ static volatile uint8_t dcfsec; // DCF77 Sekundenmarkenzaehler (0-59) static volatile uint8_t dcfcnt; // DCF77-Sampling, PD6 low {x*(1/100) sec} static volatile uint8_t flags; // Verschiedene Flags // Bit 0: aktueller DCF77-Zustand - high oder low // Bit 1: vorheriger DCF77-Zustand // Bit 2: Uhr wurde mind. einmal synchronisiert // Bit 3: Start neue Minute; TimerISR set, main reset // Bit 4: Datum gueltig // Bit 5-7: nicht belegt static volatile uint8_t flags1; // Flagregister fuer Gangreserve // Bit 0: Sperrbit bei Synchronisation // Bit 1: Ausgabefreigabe bei Minutenbeginn static volatile uint8_t inttmp; // Temporaerregister fuer Interrupt-Routinen static volatile uint8_t r14_cnt; // Zaehler fuer Stoeraustastung (r14 in ASM) /* * Tabellen Wochentag: */ static const char mont[] = "Mo "; static const char dien[] = "Di "; static const char mitt[] = "Mi "; static const char donn[] = "Do "; static const char frei[] = "Fr "; static const char sams[] = "Sa "; static const char sonn[] = "So "; /* * Tabellen Sommerzeit/Winterzeit-Kuerzel: */ static const char somzt[] = "SZ "; static const char winte[] = "WZ "; /* Vorwaertsdeklarationen */ static void regclr(void); static void portinit(void); static void timerinit(void); static void rs232init(void); static void lcdinit(void); static void lcd_clear(void); static void char2lcd(uint8_t ch); static void comd2lcd(uint8_t cmd); static void enable(void); static void senden(uint8_t data); static void wait1(void); static void wait2(void); static void wait3(void); static void num(uint8_t val); static void nums(uint8_t val); static void numx(uint8_t val); static uint8_t bcdbin(uint8_t tens, uint8_t ones); static void locate1(void); static void locate1a(void); static void locate1b(void); static void locate1c(void); static void locate1d(void); static void locate2(void); static void locate2a(void); static void locate2b(void); static void locate2c(void); static uint8_t mdcprc(void); static void mtsync(void); static void nosync(void); static void wochentag_display(uint8_t wday); static void szwz(void); static void pmread_lcd_rs232(const char *str); /*--------------------------------------------------- * Hauptprogramm, DCF77 auswerten, Uhrzeit setzen *---------------------------------------------------*/ int main(void) { cli(); // Interrupts sperren /* Stackpointer wird automatisch von C-Runtime gesetzt */ /* SRAM mit Nullen beschreiben (C-Runtime erledigt .bss-Init) */ regclr(); // Registerreset portinit(); // Init I/O-Ports rs232init(); // Init serielle Schnittstelle lcdinit(); // LCD-Init uint8_t temp = 'A'; // LCD-Test Buchstaben char2lcd(temp); wait3(); num(temp); // LCD-Test Ziffern wait3(); lcd_clear(); // LCD-Anzeige loeschen wait3(); timerinit(); // Timerinitialisierung sei(); // generelle Interruptfreigabe /* Hauptschleife */ for (;;) { /* ma1060/main: */ if (flags & (1 << 3)) { // Beginn einer neuen Minute? cli(); // Interrupts sperren flags &= ~(1 << 3); // Minuten-Flag loeschen sei(); // Interrupts wieder aktivieren uint8_t err = mdcprc(); // DCF77-Auswertung if (err == 0) { // DCF77 ok? mtsync(); // Uhr synchronisieren } } /* ma1070: */ flags |= (1 << 2); // Synchronisationsversuch-Flag setzen flags &= ~(1 << 4); // Datum ungueltig if (flags1 & (1 << 1)) { // Gangreserve-Bit in ISR gesetzt? nosync(); // Gangreserve aufrufen } } return 0; // never reached } /* * regclr - Registerreset */ static void regclr(void) { dcfsec = 0; // DCF77-Sekundenmarkenzaehler loeschen dcfcnt = 0; // DCF77-Samplingzaehler loeschen flags = 0; // Flagregister resetten flags1 = 0; // Gangreserve flags1register resetten } /* * portinit - Init I/O-Ports */ static void portinit(void) { DDRB = 0xFF; // PortB Ausgang LCD // PB4=RS, PB5=Enable, PB6=Hintergrundlicht PORTB = 0xFF; // PB0-PB3=Daten DDRD = 0b00111010; // PD0=RXD; PD1=TXD; PD6=DCF77input (low act.) PORTD = 0xFF; // Pullups auch fuer RXD } /* * timerinit - Timer1 CTC Initialisierung */ static void timerinit(void) { // CTC=clear timer on compare match // loescht Zaehlregisterinhalt bei Uebereinstimmung mit dem Vorladewert OCR1AH = (uint8_t)(COMP >> 8); OCR1AL = (uint8_t)(COMP & 0xFF); TCCR1A = 0x00; TCCR1B = (1 << WGM12) | (1 << CS10); // CTC-Modus, kein Vorteiler TIMSK = (1 << OCIE1A); // Freigabe CTC-Interrupt } /* * rs232init - Init serielle Schnittstelle */ static void rs232init(void) { UBRRL = BDRT; UCSRB = (1 << RXEN) | (1 << TXEN); // 0x18 } /* * senden - Zeichen ueber UART senden */ static void senden(uint8_t data) { while (!(UCSRA & (1 << UDRE))) ; UDR = data; } /*------------------------------------------------- * DCF77-Auswertung, Kontrolle der Pruefbits 1-3, * Ablegen der Zeitinformation in Puffer dcbuf1 * Ausgang: return 0 - alles ok, >0 - Fehler *-------------------------------------------------*/ static uint8_t mdcprc(void) { uint8_t errors = 0; // Fehlerzaehler loeschen uint8_t pulse_count = dcfsec; // DCF77-Markenzaehler kopieren dcfsec = 0; // DCF77-Markenzaehler loeschen if (pulse_count != 59) { // wurden 59 Impulse empfangen? errors++; // sonst Fehlerzaehler erhoehen return errors; // Auswertung vorzeitig beenden } /* * Kontrolle des Impulszaehlers und der Paritybits * fuer Minuten, Stunden und Datum */ uint8_t parity; uint8_t r23; uint8_t i; /* Paritaet Minuten (6 Bits aus Byte 1, dann 2 Bits aus Byte 2) */ parity = 0; r23 = dcftab[1]; for (i = 0; i < 6; i++) { // 6 Minuten-Bits auswerten if (r23 & 0x80) parity++; // relevantes Bit (MSB nach rol) r23 <<= 1; } r23 = dcftab[2]; // 2 weitere Bits (Bit 0 und Bit 1 via ror) if (r23 & 0x01) parity++; r23 >>= 1; if (r23 & 0x01) parity++; r23 >>= 1; if (parity & 0x01) { // war das Ergebnis geradzahlig? errors++; // sonst Fehlerzaehler erhoehen } /* Paritaet Stunden (6 Bits aus Byte 2, dann 1 Bit aus Byte 3) */ parity = 0; // r23 ist bereits um 2 nach rechts geschoben (Bits 2-7 verbleiben) r23 = dcftab[2] >> 2; for (i = 0; i < 6; i++) { // 6 Stunden-Bits auswerten if (r23 & 0x01) parity++; r23 >>= 1; } r23 = dcftab[3]; if (r23 & 0x01) parity++; // Pruefbit 2 r23 >>= 1; if (parity & 0x01) { errors++; } /* Paritaet Datum (7 Bits aus Byte 3, 8 aus Byte 4, 8 aus Byte 5) */ parity = 0; for (i = 0; i < 7; i++) { // 7 Datum-Bits auswerten if (r23 & 0x01) parity++; r23 >>= 1; } r23 = dcftab[4]; for (i = 0; i < 8; i++) { // 8 Datum/Wochentag-Bits auswerten if (r23 & 0x01) parity++; r23 >>= 1; } r23 = dcftab[5]; for (i = 0; i < 8; i++) { // 8 Datum/Jahreszahl-Bits auswerten if (r23 & 0x01) parity++; r23 >>= 1; } if (parity & 0x01) { // war das Ergebnis geradzahlig? errors++; } if (errors != 0) { // Paritaetsfehler aufgetreten? return errors; // DCF-Puffer0 loeschen (Abbruch) } /* * Ablegen der Zeitinformation in Puffer dcbuf1 */ uint8_t *z = dcbuf1; // Zeitzone: Byte 0, Bit 6 -> Bit 0 r23 = dcftab[0]; r23 = (r23 >> 6) & 0x01; *z++ = r23; // Zeitzone speichern // Minuten Einer: Byte 1, Bits 2-5 -> Bits 0-3 r23 = dcftab[1]; r23 = (r23 >> 2) & 0x0F; *z++ = r23; // Minuten Einer speichern // Minuten Zehner: Byte 1 Bits 6-7 + Byte 2 Bit 0 -> Bits 0-2 { uint8_t b1 = dcftab[1]; uint8_t b2 = dcftab[2]; uint8_t tens = ((b1 >> 6) & 0x03) | ((b2 & 0x01) << 2); *z++ = tens & 0x07; // Minuten Zehner speichern } // Stunden Einer: Byte 2, Bits 2-5 -> Bits 0-3 r23 = dcftab[2]; r23 = (r23 >> 2) & 0x0F; *z++ = r23; // Stunden Einer speichern // Stunden Zehner: Byte 2, Bits 6-7 -> Bits 0-1 r23 = dcftab[2]; r23 = (r23 >> 6) & 0x03; *z++ = r23; // Stunden Zehner speichern // Kalendertag Einer: Byte 3, Bits 1-4 -> Bits 0-3 r23 = dcftab[3]; r23 = (r23 >> 1) & 0x0F; *z++ = r23; // Kalendertag Einer speichern // Kalendertag Zehner: Byte 3, Bits 5-6 -> Bits 0-1 r23 = dcftab[3]; r23 = (r23 >> 5) & 0x03; *z++ = r23; // Kalendertag Zehner speichern // Wochentag: Byte 3 Bit 7 (LSB) + Byte 4 Bits 0-1 (MSB) { uint8_t b3 = dcftab[3]; uint8_t b4 = dcftab[4]; uint8_t wday = ((b3 >> 7) & 0x01) | ((b4 & 0x03) << 1); *z++ = wday & 0x07; // Wochentag speichern } // Monat Einer: Byte 4, Bits 2-5 -> Bits 0-3 r23 = dcftab[4]; r23 = (r23 >> 2) & 0x0F; *z++ = r23; // Monat Einer speichern // Monat Zehner: Byte 4, Bit 6 -> Bit 0 r23 = dcftab[4]; r23 = (r23 >> 6) & 0x01; *z++ = r23; // Monat Zehner speichern // Jahr Einer: Byte 4 Bit 7 (LSB) + Byte 5 Bits 0-2 (MSB) { uint8_t b4 = dcftab[4]; uint8_t b5 = dcftab[5]; uint8_t yr_ones = ((b4 >> 7) & 0x01) | ((b5 & 0x07) << 1); *z++ = yr_ones & 0x0F; // Jahr Einer speichern } // Jahr Zehner: Byte 5, Bits 3-6 -> Bits 0-3 r23 = dcftab[5]; r23 = (r23 >> 3) & 0x0F; *z++ = r23; // Jahr Zehner speichern return 0; } /*------------------------------------------------------ * Zeitinformationen in Uhrenpuffer kopieren und Uhrzeit * bei Synchronisation auf LCD und RS232 ausgeben *------------------------------------------------------*/ static void mtsync(void) { uint8_t r0, r1; senden('<'); // Vorzeichen nur fuer RS232-Ausgabe /* Stunden anzeigen */ cli(); r0 = dcbuf1[3]; // Stunden Einer holen r1 = dcbuf1[4]; // Stunden Zehner holen sei(); locate2a(); char2lcd(r1 + 0x30); senden(r1 + 0x30); char2lcd(r0 + 0x30); senden(r0 + 0x30); uint8_t std_bin = bcdbin(r1, r0); // Format von BCD => binaer stunde = std_bin; // Kopie fuer Gangreserve-Puffer char2lcd(':'); senden(':'); /* Minuten anzeigen */ cli(); r0 = dcbuf1[1]; // Minuten Einer holen r1 = dcbuf1[2]; // Minuten Zehner holen sei(); char2lcd(r1 + 0x30); senden(r1 + 0x30); char2lcd(r0 + 0x30); senden(r0 + 0x30); uint8_t min_bin = bcdbin(r1, r0); minute = min_bin; // Kopie fuer Gangreserve-Puffer char2lcd(':'); senden('-'); /* Tag anzeigen */ cli(); r0 = dcbuf1[5]; // Tag Einer holen r1 = dcbuf1[6]; // Tag Zehner holen sei(); locate1a(); char2lcd(r1 + 0x30); senden(r1 + 0x30); char2lcd(r0 + 0x30); senden(r0 + 0x30); uint8_t tag_bin = bcdbin(r1, r0); tag = tag_bin; // Kopie fuer Gangreserve-Puffer char2lcd('.'); senden('.'); /* Monat anzeigen */ cli(); r0 = dcbuf1[8]; // Monat Einer holen r1 = dcbuf1[9]; // Monat Zehner holen sei(); char2lcd(r1 + 0x30); senden(r1 + 0x30); char2lcd(r0 + 0x30); senden(r0 + 0x30); char2lcd('.'); senden('.'); uint8_t mon_bin = bcdbin(r1, r0); monat = mon_bin; // Kopie fuer Gangreserve-Puffer /* Jahr Tausender/Hunderter ergaenzen - nicht im DCF-Code */ num(0x14); nums(0x14); // auch auf RS232 ausgeben /* Jahr Einer/Zehner anzeigen */ cli(); r0 = dcbuf1[10]; // Jahr Einer holen r1 = dcbuf1[11]; // Jahr Zehner holen sei(); locate1c(); char2lcd(r1 + 0x30); senden(r1 + 0x30); char2lcd(r0 + 0x30); senden(r0 + 0x30); uint8_t jahr_bin = bcdbin(r1, r0); jahr = jahr_bin; // Kopie fuer Gangreserve-Puffer senden(0x20); // Space /* Wochentag und Sommer-/Winterzeit anzeigen */ wochentag_display(dcbuf1[7]); wtag = dcbuf1[7]; // Kopie fuer Gangreserve-Puffer szwz(); senden(0x0A); // Zeilenvorschub senden(0x0D); // Wagenruecklauf /* Synchronisation abschliessen */ cli(); sekunde = 0; // Sekundenpuffer loeschen precnt = 0; // Vorteiler loeschen flags |= 0b00010100; // Synchron- und Datum-Bit in Flags flags1 = 0x01; // Ausgabesperre Gangreserve bei Sync sei(); } /* * wochentag_display - Wochentag auf LCD (Zeile 1, Spalte 15) und RS232 ausgeben */ static void wochentag_display(uint8_t wday) { const char *str; switch (wday) { case 1: str = mont; break; case 2: str = dien; break; case 3: str = mitt; break; case 4: str = donn; break; case 5: str = frei; break; case 6: str = sams; break; case 7: str = sonn; break; default: return; } locate1d(); pmread_lcd_rs232(str); } /* * szwz - Sommer-/Winterzeit-Kuerzel anzeigen */ static void szwz(void) { uint8_t tz = dcbuf1[0]; const char *str; if (tz == 0x00) { str = winte; // Winterzeit } else if (tz == 0x01) { str = somzt; // Sommerzeit } else { return; } locate2c(); pmread_lcd_rs232(str); } /* * pmread_lcd_rs232 - String auf LCD und RS232 ausgeben (nullterminiert) */ static void pmread_lcd_rs232(const char *str) { while (*str) { char2lcd(*str); senden(*str); str++; } } /* * nosync - Gangreserve bei Fehlen des DCF77-Signals */ static void nosync(void) { if (flags1 & (1 << 0)) { // Gangreservesperrbit bei Synchronisation /* nosync1: Gangreserve-Ausgabe wieder vorbereiten */ flags1 = 0; return; } /* Ausgabe ueber Gangreserve */ locate2a(); numx(stunde); // nur Stunden und Minuten anzeigen char2lcd(':'); senden(':'); numx(minute); char2lcd(':'); senden(':'); // RS232 serielle Ausgabe senden(0x0A); // naechste Zeile (Zeilenvorschub) senden(0x0D); // Anfang Zeile (Wagenruecklauf) /* Indikator "keine Synchronisation" - Uhr laeuft auf Gangreserve */ locate1a(); num(tag); char2lcd('.'); num(monat); char2lcd('.'); num(0x14); locate1c(); num(jahr); wochentag_display(wtag); flags1 = 0; } /*------------------------------------------------------------ * Interrupt-Routine Timer1 Compare A, liest DCF77-Pegel * alle 10 ms ein, zaehlt die Uhrzeit automatisch hoch *------------------------------------------------------------*/ ISR(TIMER1_COMPA_vect) { uint8_t r25 = PIND; // Portbit PD6 DCF77 einlesen uint8_t blk = block; // Pegel von Sperregister holen /* Stoeraustastung: Bit6 in r25 auf high setzen, * wenn vorher in "block" gesetzt */ r25 |= blk; if (blk & (1 << 6)) { /* t010: Eingang 500ms (260ms) auf high halten */ r14_cnt++; if (r14_cnt >= 50) { // 500ms (260ms) erreicht? /* t020: Zaehler ruecksetzen */ r14_cnt = 0; block = 0x00; } } /* t030: DCF77-Pegel holen und in Flags speichern */ if (r25 & (1 << 6)) { flags |= (1 << 0); } else { flags &= ~(1 << 0); } /* t040: DCF77-Sampling */ dcfcnt++; // DCF77-Sampling-Zaehler erhoehen if (!(flags & (1 << 0))) { /* DCF77 ist Low */ if (flags & (1 << 1)) { /* vorher war High -> fallende Flanke */ if (dcfcnt >= 150) { // Zaehler >= 150 (1,5s)? flags |= (1 << 3); // Minutenanfang in Flags setzen } /* t050: */ dcfcnt = 0; // DCF77-Sampling-Zaehler loeschen } } else { /* DCF77 ist High */ if (!(flags & (1 << 1))) { /* vorher war Low -> steigende Flanke */ dcfsec++; // Sekunde hochzaehlen block = 0x40; // Bit6 high setzen in "block" // r25 wird beim naechsten Interrupt // nach l/h-Wechsel 500(260) ms gesperrt /* t080: DCF-Sampling-Zaehler < 14 (140ms)? -> Bit = 0, sonst 1 */ uint8_t new_bit = (dcfcnt >= 14) ? 1 : 0; /* 6 Pufferbytes bearbeiten: neues Bit als MSB von Byte 5 einschieben, * alle Bits um eine Position nach rechts ruecken */ uint8_t carry = new_bit; for (int8_t i = 5; i >= 0; i--) { uint8_t old_carry = dcftab[i] & 0x01; dcftab[i] = (dcftab[i] >> 1) | (carry << 7); carry = old_carry; } /* DCF77 Code Nullen/Einsen nur auf RS232 ausgeben */ senden(new_bit + 0x30); /* Sekundenanzeige DCF synchron auf LCD */ locate2b(); num(dcfsec); } } /* t100: aktuellen DCF77-Status als vorherigen speichern */ if (flags & (1 << 0)) { flags |= (1 << 1); } else { flags &= ~(1 << 1); } /*- - - - - - - - - fuer Gangreserve - - - - - - - - -*/ uint8_t pre = precnt; if (pre == 0) { /* Vorteiler=0 -> nichts tun (Gangreserve inaktiv bis Sync) */ /* In Original-ASM: springt trotzdem zu t110 */ } /* t110: */ pre = precnt; pre++; precnt = pre; if (pre < 100) { // Endwert erreicht? return; // nein -> Ende } precnt = 0; // ja -> zuruecksetzen /* t120: Sekunden hochzaehlen */ uint8_t sek = sekunde; sek++; sekunde = sek; if (sek < 60) return; // Endwert erreicht? nein -> Ende sekunde = 0; // ja -> zuruecksetzen /* Minuten hochzaehlen */ uint8_t min = minute; min++; minute = min; flags1 = 0x02; // Gangreserve-Ausgabe vorbereiten if (min < 60) return; minute = 0; /* Stunden hochzaehlen */ uint8_t std = stunde; std++; stunde = std; if (std < 24) return; stunde = 0; /* Tag hochzaehlen */ uint8_t t = tag; t++; tag = t; /* Wochentag hochzaehlen */ uint8_t w = wtag; w++; wtag = w; if (w >= 7) { wtag = 0; } /* Monatslängen pruefen */ uint8_t m = monat; uint8_t days_in_month; switch (m) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: days_in_month = 31; break; case 4: case 6: case 9: case 11: days_in_month = 30; break; case 2: if ((jahr & 0x03) == 0) { days_in_month = 29; // Schaltjahr } else { days_in_month = 28; } break; default: days_in_month = 31; break; } if (t <= days_in_month) return; /* t400: Monatswechsel */ tag = 1; // Tag=1 setzen uint8_t mon = monat; mon++; monat = mon; if (mon <= 12) return; // Endwert ueberschritten? monat = 1; // sonst Monat=1 setzen uint8_t j = jahr; j++; jahr = j; // Jahr erhoehen } /*- - LCD-Initialisierung - LCD-Ausgabe-Routinen - -*/ static void lcd_reset(void); static void lcd_achtbit(void); static void lcd_acht_vier(void); static void lcd_vier_zwei(void); static void lcd_on_off(void); static void lcd_entrymode(void); static void lcdinit(void) { wait3(); // LCD Power On Self Reset abwarten lcd_reset(); // LCD-Bus-Reset lcd_achtbit(); // LCD-Init: acht Bit lcd_acht_vier(); // LCD-Init: acht zu vier Bit lcd_vier_zwei(); // LCD-Init: vier Bit, zwei Zeilen lcd_on_off(); // LCD-Init: Displaypuffer aus lcd_clear(); // LCD-Init: Anzeige loeschen lcd_entrymode(); // LCD-Init: Entry Mode Set } static void lcd_reset(void) { // LCD-Bus Reset DATEN = 0x00; // RS=0; Reset des LCD-Ausgabeports enable(); wait2(); } static void lcd_achtbit(void) { // Acht-Bit-Init // PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 // n/a LED ena rs D7 D6 D5 D4 DATEN &= ~(1 << RS); // RS=0 DATEN = 0x03; // dreimal hex 0x03 enable(); wait3(); DATEN &= ~(1 << RS); // RS=0 DATEN = 0x03; enable(); wait3(); DATEN &= ~(1 << RS); // RS=0 DATEN = 0x03; enable(); wait3(); } static void lcd_acht_vier(void) { // Acht-zu-Vier-Bit-Modus-Wechsel DATEN &= ~(1 << RS); // RS=0 DATEN = 0x02; // Vier-Bit-Modus-Wechsel-Befehl enable(); wait2(); } static void lcd_vier_zwei(void) { // Vier-Bit-Modus, zwei Zeilen DATEN &= ~(1 << RS); // RS=0 comd2lcd(0x28); // Vier-Bit-Modus, zweizeilig wait2(); } static void lcd_on_off(void) { // On-off-Control: Displaypuffer ein-, ausschalten DATEN &= ~(1 << RS); // RS=0 comd2lcd(0x08); // Displaypuffer aus, Blinken aus, kein Cursor wait2(); } static void lcd_clear(void) { // Anzeige loeschen DATEN &= ~(1 << RS); // RS=0 comd2lcd(0x01); // Anzeige wird im Space-Modus geloescht wait3(); // dauert laenger... Pause laenger } static void lcd_entrymode(void) { // Entry/Shift Mode Set DATEN &= ~(1 << RS); // RS=0 comd2lcd(0x06); // Cursor von rechts nach links, kein Schieben wait2(); DATEN &= ~(1 << RS); // RS=0 comd2lcd(0x10); // kein Schieben, Cursorbewegung nach rechts wait2(); DATEN &= ~(1 << RS); // RS=0 comd2lcd(0x0C); // Anzeige wieder einschalten, Cursor aus, Blinken aus wait2(); DATEN &= ~(1 << RS); // RS=0 comd2lcd(0x01); // Anzeige nochmals loeschen wait2(); } /* * char2lcd - gibt Charakter ueber LCD aus */ static void char2lcd(uint8_t ch) { DATEN &= ~(1 << LICHT); // LCD-Hintergrundbeleuchtung aus uint8_t high_nibble = (ch >> 4) & 0x0F; high_nibble |= (1 << RS); // RS=high (Bit 4 im Portbyte) DATEN = high_nibble; // Ausgabe D4-D7-Daten-Nibble auf Port D0-D3 enable(); uint8_t low_nibble = ch & 0x0F; low_nibble |= (1 << RS); // RS=high DATEN = low_nibble; // Ausgabe D0-D3-Daten-Nibble auf D0-D3 enable(); DATEN |= (1 << LICHT); // Hintergrundbeleuchtung ein wait1(); // Verarbeitungszeit LCD abwarten } /* * comd2lcd - sendet Steuerzeichen an LCD */ static void comd2lcd(uint8_t cmd) { uint8_t high_nibble = (cmd >> 4) & 0x0F; // RS=0 (kein OR mit RS-Bit) DATEN = high_nibble; // Ausgabe D4-D7-Daten-Nibble auf Port D0-D3 enable(); uint8_t low_nibble = cmd & 0x0F; // RS=0 DATEN = low_nibble; // Ausgabe D0-D3-Daten-Nibble auf D0-D3 enable(); wait1(); // Verarbeitungszeit LCD abwarten } /* * enable - Enableimpuls-Generierung */ static void enable(void) { asm volatile("nop"); DATEN |= (1 << ENA); // Portbit "Enable" auf high asm volatile("nop"); // Zeitverzoegerung mindestens asm volatile("nop"); // 1000 ns laut Datenblatt LCD asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); DATEN &= ~(1 << ENA); // Portbit "Enable" auf low zuruecksetzen asm volatile("nop"); } /*************** Verzoegerungsschleifen fuer LCD ************/ /* * wait1 - Verzoegerungsschleife1: Konstante 512: ca. 257,5 us */ static void wait1(void) { volatile uint16_t cnt = ZEIT0; while (cnt--) ; } /* * wait2 - Verzoegerungsschleife2: Konstante 65535: ca. 32 ms */ static void wait2(void) { volatile uint16_t cnt = ZEIT1; while (cnt--) ; } /* * wait3 - Verzoegerungsschleife3: ca. 128 ms */ static void wait3(void) { wait2(); wait2(); wait2(); wait2(); asm volatile("nop"); } /*- - - - - - Zahlenumwandlung(1) binaer=>dezimal - - - - - -*/ /* * num - wandelt 8 Bit binaer nach ASCII dezimal und gibt auf LCD aus * Format: 2 Ziffern mit fuehrender Null */ static void num(uint8_t val) { uint8_t tens = 0x30; uint8_t v = val; while (v >= 10) { // Berechnung Zehner v -= 10; tens++; } char2lcd(tens); // Ausgabe Zehnerstelle auf LCD char2lcd(0x30 + v); // Ausgabe Einerstelle auf LCD } /* * nums - wandelt Byte nach ASCII dezimal (nur RS232-Ausgabe) * Format: 2 Ziffern mit fuehrender Null */ static void nums(uint8_t val) { uint8_t tens = 0x30; uint8_t v = val; while (v >= 10) { // Berechnung Zehner v -= 10; tens++; } senden(tens); // Ausgabe Zehnerstelle auf RS232 senden(0x30 + v); // Ausgabe Einerstelle auf RS232 } /* * numx - wandelt Byte nach ASCII dezimal (LCD- und RS232-Ausgabe) * Format: 2 Ziffern mit fuehrender Null */ static void numx(uint8_t val) { uint8_t tens = 0x30; uint8_t v = val; while (v >= 10) { // Berechnung Zehner v -= 10; tens++; } char2lcd(tens); // Ausgabe Zehnerstelle auf LCD senden(tens); // Ausgabe Zehnerstelle auf RS232 char2lcd(0x30 + v); // Ausgabe Einerstelle auf LCD senden(0x30 + v); // Ausgabe Einerstelle auf RS232 } /*- - - - - - - Zahlenumwandlung(2) BCD=>binaer - - - - - -*/ /* * bcdbin - BCD (Zehner, Einer) nach binaer umwandeln * Eingabe: tens (Zehner), ones (Einer) * Ausgabe: binaerer Wert */ static uint8_t bcdbin(uint8_t tens, uint8_t ones) { uint8_t result = 0; while (tens > 0) { result += 10; tens--; } result += ones; return result; } /*- - - - - - LCD Positionierungen (2x16) - - - - - -*/ static void locate1(void) // Anfang Zeile 1 { comd2lcd(0x80); } static void locate1a(void) // Zeile 1, Spalte 4 { comd2lcd(0x83); } static void locate1b(void) // Zeile 1, Spalte 6 { comd2lcd(0x85); } static void locate1c(void) // Zeile 1, Spalte 8 { comd2lcd(0x8B); } static void locate1d(void) // Zeile 1, Spalte 15 { comd2lcd(0x8E); } static void locate2(void) // Anfang Zeile 2 { comd2lcd(0xC0); } static void locate2a(void) // Zeile 2, Spalte 4 { comd2lcd(0xC3); } static void locate2b(void) // Zeile 2, Spalte 10 { comd2lcd(0xC9); } static void locate2c(void) // Zeile 2, Spalte 15 { comd2lcd(0xCE); } /*** Fin ***/