/* * dcf77.c – DCF77-Empfaenger fuer ATmega32A, 16 MHz Quarz * * Signal : Port A, Bit 0 (PA0), interner Pull-Up aktiv * Timer : Timer1, CTC-Mode, 10 ms Takt (Prescaler 8, OCR1A = 19999) * UART : 9600 Baud (UBRR = 103) * Compiler : avr-gcc, -mmcu=atmega32 -DF_CPU=16000000UL * * Programmstruktur (identisch zur MicroPython-Vorlage): * ISR(TIMER1_COMPA_vect) – nur Signal lesen + Flag setzen, kein GC-Risiko * dcf_update() – Flankenauswertung + Autodetect, aus main() aufgerufen * dcf_decode() – DCF77-BCD-Dekodierung + Paritaetspruefung * main() – Hauptschleife, Bit-Puffer, Ausgabe * * DCF77-Bitstruktur (Bit 0 zuerst, 59 Bits pro Minute): * Bit 0 Startbit, immer 0 * Bits 1-14 Wetterdaten / reserviert * Bit 15 Rufbit * Bit 16 Reserviert * Bit 17 MESZ aktiv (1 = Sommerzeit) * Bit 18 MEZ aktiv (1 = Winterzeit) * Bit 19 Schaltsekunde angekuendigt * Bit 20 Startbit Zeitinfo, immer 1 * Bits 21-27 Minute (BCD, LSB zuerst) * Bit 28 Paritaet Minute (gerade Paritaet ueber Bits 21-28) * Bits 29-34 Stunde (BCD, LSB zuerst) * Bit 35 Paritaet Stunde (gerade Paritaet ueber Bits 29-35) * Bits 36-41 Tag (BCD, LSB zuerst) * Bits 42-44 Wochentag (1=Mo .. 7=So) * Bits 45-49 Monat (BCD, LSB zuerst) * Bits 50-57 Jahr (BCD, LSB zuerst, zweistellig) * Bit 58 Paritaet Datum (gerade Paritaet ueber Bits 36-58) */ #include #include #include #include #include #include /* ------------------------------------------------------------------------- * Hardwarekonstanten * F_CPU wird ausschliesslich per -DF_CPU=16000000UL auf der Kommandozeile * gesetzt, um Redefinitions-Konflikte mit avr-libc-Headern zu vermeiden. * ---------------------------------------------------------------------- */ #define DCF_PORT PINA /* Einlesen ueber PIN-Register */ #define DCF_DDR DDRA #define DCF_PORTOUT PORTA #define DCF_PIN_BIT PA0 /* Signal an PA0 */ /* Timer1 CTC: 10 ms bei 16 MHz, Prescaler 8 * OCR1A = (F_CPU / Prescaler / Taktrate) - 1 = (16e6 / 8 / 100) - 1 = 19999 */ #define TIMER1_PRESCALER_BITS (1<> 8); UBRRL = (uint8_t)(UART_UBRR); UCSRB = (1<> DCF_PIN_BIT) & 0x01; dcf.isr_flag = true; } /* ------------------------------------------------------------------------- * Hilfsfunktion: Prueft ob Breite im DCF77-Pulsfenster liegt * ---------------------------------------------------------------------- */ static bool is_valid_pulse(uint16_t width) { return (width >= FALSE_TIME_LO && width <= TRUE_TIME_HI); } /* ------------------------------------------------------------------------- * Autodetect: Flankenbreite sammeln, nach AUTODETECT_SAMPLES auswerten * ---------------------------------------------------------------------- */ static void autodetect_update(uint16_t delta, bool rising) { if (rising) { dcf.rising_cnt++; if (is_valid_pulse(delta)) dcf.rising_score++; } else { dcf.falling_cnt++; if (is_valid_pulse(delta)) dcf.falling_score++; } if ((dcf.rising_cnt + dcf.falling_cnt) < AUTODETECT_SAMPLES * 2) return; /* noch nicht genug Daten */ dcf.inverted = (dcf.rising_score > dcf.falling_score); dcf.autodetect = false; printf("Autodetect: rising=%u falling=%u -> inverted=%u\n", dcf.rising_score, dcf.falling_score, (uint8_t)dcf.inverted); } /* ------------------------------------------------------------------------- * dcf_update() – Flankenauswertung, regelmaessig aus main() aufrufen. * Gibt true zurueck wenn ein neuer Puls in dcf.pulse bereitsteht. * ---------------------------------------------------------------------- */ bool dcf_update(void) { /* ISR-Flag und Uebergabewerte atomar lesen und Flag loeschen */ cli(); bool flag = dcf.isr_flag; uint16_t now = dcf.isr_time; uint8_t raw = dcf.isr_signal; dcf.isr_flag = false; sei(); if (!flag) return false; bool dcf_signal = dcf.autodetect ? (bool)raw : ((bool)raw ^ dcf.inverted); if (!dcf.initialized) { dcf.last_edge_time = now; dcf.old_signal = dcf_signal; dcf.initialized = true; return false; } /* Nur bei Flanke auswerten */ if (dcf.old_signal == dcf_signal) return false; uint16_t delta = now - dcf.last_edge_time; dcf.delta_time = delta; /* Fallende Flanke (logisch: High -> Low) */ if (dcf.old_signal && !dcf_signal) { if (dcf.autodetect) { autodetect_update(delta, false); } else { if (delta >= TRUE_TIME_LO && delta <= TRUE_TIME_HI) dcf.pulse = PULSE_TRUE; else if (delta >= FALSE_TIME_LO && delta <= FALSE_TIME_HI) dcf.pulse = PULSE_FALSE; else dcf.pulse = PULSE_ERROR; } } /* Steigende Flanke (logisch: Low -> High) */ else if (!dcf.old_signal && dcf_signal) { if (dcf.autodetect) { autodetect_update(delta, true); } else { if (delta >= PAUSE_TIME_LO && delta <= PAUSE_TIME_HI) dcf.pulse = PULSE_MIN_BEGIN; else if (delta >= TRUE_TIME_HI) ; /* normale Sekunden-Pause (~800 ms): kein Fehler */ else dcf.pulse = PULSE_ERROR; } } dcf.old_signal = dcf_signal; dcf.last_edge_time = now; return (dcf.pulse != PULSE_NONE); } /* ------------------------------------------------------------------------- * BCD-Dekodierung (LSB zuerst) * ---------------------------------------------------------------------- */ static uint8_t bcd_decode(const int8_t *bits, uint8_t start, uint8_t length) { static const uint8_t weights[] = { 1, 2, 4, 8, 10, 20, 40, 80 }; uint8_t val = 0; for (uint8_t i = 0; i < length; i++) if (bits[start + i] == 1) val += weights[i]; return val; } /* ------------------------------------------------------------------------- * Gerade Paritaetspruefung ueber bits[start..end] (end inklusive) * ---------------------------------------------------------------------- */ static bool parity_even(const int8_t *bits, uint8_t start, uint8_t end) { uint8_t ones = 0; for (uint8_t i = start; i <= end; i++) if (bits[i] == 1) ones++; return (ones % 2 == 0); } /* ------------------------------------------------------------------------- * dcf_decode() – DCF77-Bitpuffer auswerten. * Gibt true bei Erfolg zurueck, Ergebnis in *result. * ---------------------------------------------------------------------- */ bool dcf_decode(const int8_t *bits, uint8_t count, dcf_time_t *result) { if (count < 59) { printf("DECODE ERROR: nur %u Bits empfangen (59 erwartet)\n", count); return false; } /* Empfangsfehler (-1) im relevanten Bereich pruefen */ for (uint8_t i = 20; i < 59; i++) { if (bits[i] == -1) { printf("DECODE ERROR: fehlerhafte Bits im Zeitbereich\n"); return false; } } /* Startbits pruefen */ if (bits[0] != 0) { printf("DECODE ERROR: Bit 0 ist nicht 0 (Startbit)\n"); return false; } if (bits[20] != 1) { printf("DECODE ERROR: Bit 20 ist nicht 1 (Zeitstartbit)\n"); return false; } /* Paritaeten pruefen */ if (!parity_even(bits, 21, 28)) { printf("DECODE ERROR: Paritaetsfehler Minute (Bits 21-28)\n"); return false; } if (!parity_even(bits, 29, 35)) { printf("DECODE ERROR: Paritaetsfehler Stunde (Bits 29-35)\n"); return false; } if (!parity_even(bits, 36, 58)) { printf("DECODE ERROR: Paritaetsfehler Datum (Bits 36-58)\n"); return false; } /* Zeitwerte dekodieren */ result->minute = bcd_decode(bits, 21, 7); result->hour = bcd_decode(bits, 29, 6); result->day = bcd_decode(bits, 36, 6); result->weekday = bcd_decode(bits, 42, 3); result->month = bcd_decode(bits, 45, 5); result->year = bcd_decode(bits, 50, 8); result->mesz = (bits[17] == 1); const char *tz_str = result->mesz ? "MESZ" : "MEZ "; const char *wd_str = (result->weekday >= 1 && result->weekday <= 7) ? WEEKDAY_NAMES[result->weekday] : "??"; printf(">>> Zeit : %02u:%02u %s\n", result->hour, result->minute, tz_str); printf(">>> Datum : %s %02u.%02u.20%02u\n", wd_str, result->day, result->month, result->year); return true; } /* ------------------------------------------------------------------------- * Initialisierung des DCF77-Zustands * ---------------------------------------------------------------------- */ static void dcf_init(void) { memset(&dcf, 0, sizeof(dcf)); dcf.autodetect = true; /* Automatische Erkennung aktiv */ /* dcf.inverted = false; bereits durch memset */ /* dcf.inverted = true; fuer bekannt invertierte Module */ } /* ------------------------------------------------------------------------- * Hardwareinitialisierung * ---------------------------------------------------------------------- */ static void hw_init(void) { /* PA0 als Eingang, Pull-Up aktivieren */ DCF_DDR &= ~(1<