Quelltext des Programms
Blockschaltbild
Details
/**
* Arduino DCF77-Decodierung und Anzeige mit RTC
*
* S. Taborek
* 14.10.2019 - 25.10.2019
* 10.12.2019 . 12.12.2019
*
* D=Deutschland C=Langwellensender F=Nähe Frankfurt 77,5KHz
*
* Programmierport am MAC: /dev/cu.usbserial-14340
*/
// Wireing:
// LCD Pin 1: Masse
// LCD Pin 2: +5V
// LCD Pin 3: + Kontrast
// LCD Pin 4: RS
// LCD Pin 5: RW
// LCD Pin 6: Enable
// LCD Pin ..:
// LCD Pin 14: D4
// LCD Pin 15: D5
// LCD Pin 16: D6
// LCD Pin 17: D7
// LCD Pin 18: + Hintergrundlicht 3..5V
// LCD Pin 19: - "
#define DCFPIN_Rise 2 // DCF Sekundenpuls
#define DCFPIN_Fall 3
#define BlinkPIN 13 // Led Sek-Takt
#define SommerPIN 8 // Led Sommerzeit
#define Plus1SecPIN 9 // Led Schaltsekunde ankündigen
/**
* DCF Telegrammstruktur
*/
/*.......................................................*/
// sonstige 15;
const byte ResAnt = 15; // Reserve-Antenne
const byte ZonPre = 16; // Ankündigung Zonenzeit
const byte ZoneTime1 = 17; // Zonenzeit 1=Sommerzeit
const byte ZoneTime2 = 18;
const byte InsertSec = 19; // Ankündigung Schaltsekunde
const byte StartBit = 20; // Start-Bit immer 1
const byte mi_Bit1 = 21; // Minute Wert 1
//const byte mi_Bit2 = 22; // Minute 2
//const byte mi_Bit3 = 23; // Minute 4
//const byte mi_Bit4 = 24; // Minute 8
//const byte mi_Bit5 = 25; // Minute 10
//const byte mi_Bit6 = 26; // Minute 20
const byte mi_Bit7 = 27; // Minute 40
const byte ParityBit_mi = 28; // Minute Prüfbit
const byte hh_Bit1 = 29; // Stunde Wert 1
//const byte hh_Bit2 = 30; // Stunde 2
//const byte hh_Bit3 = 31; // Stunde 4
//const byte hh_Bit4 = 32; // Stunde 8
//const byte hh_Bit5 = 33; // Stunde 10
const byte hh_Bit6 = 34; // Stunde 20
const byte ParityBit_hh = 35; // Stunde Prüfbit
const byte dd_Bit1 = 36; // Kalendertag 1
//const byte dd_Bit2 = 37; // Kalendertag 2
//const byte dd_Bit3 = 38; // Kalendertag 4
//const byte dd_Bit4 = 39; // Kalendertag 8
//const byte dd_Bit5 = 40; // Kalendertag 10
const byte dd_Bit6 = 41; // Kalendertag 20
const byte wd_Bit1 = 42; // Wochentag 1
const byte wd_Bit2 = 43; // Wochentag 2
const byte wd_Bit3 = 44; // Wochentag 4
const byte mm_Bit1 = 45; // Monat 1
//const byte mm_Bit2 = 46; // Monat 2
//const byte mm_Bit3 = 47; // Monat 4
//const byte mm_Bit4 = 48; // Monat 8
const byte mm_Bit5 = 49; // Monat 10
const byte yy_Bit1 = 50; // Jahr 1
//const byte yy_Bit2 = 51; // Jahr 2
//const byte yy_Bit3 = 52; // Jahr 4
//const byte yy_Bit4 = 53; // Jahr 8
//const byte yy_Bit5 = 54; // Jahr 10
//const byte yy_Bit6 = 55; // Jahr 20
//const byte yy_Bit7 = 56; // Jahr 40
const byte yy_Bit8 = 57; // Jahr 80
const byte ParityBit_dt = 58; // Prüfbit Kalenderdaten
const byte Synchron = 59; //
/*.......................................................*/
/**
* Variablen für DCF
*/
byte rxBuffer[62];
volatile byte rxPtr; // Zeiger auf Puffer-Position
volatile unsigned char DCF_Bit;
volatile unsigned long t1;
volatile unsigned long t2;
volatile long diff1;
volatile long diff2;
volatile bool Telegramm_Ende = false;
volatile bool Telegramm_Decode = false;
volatile bool Timer_DCF_synchron;
volatile unsigned long errPulsCnt;
/**
* Variablen für die Zeit-Information
*/
struct // DCF
{
uint8_t sec;
uint8_t mi;
uint8_t hh;
uint8_t dd;
uint8_t mm;
uint8_t yy;
uint8_t wd;
long ok_cnt;
} DCF;
struct // DCFprev
{
uint8_t sec;
uint8_t mi;
uint8_t hh;
uint8_t dd;
uint8_t mm;
uint8_t yy;
uint8_t wd;
} DCFprev;
struct // Zeit
{
uint8_t sec;
uint8_t mi;
uint8_t hh;
uint8_t dd;
uint8_t mm;
uint8_t yy;
uint8_t wd;
} aktuell;
struct // Uhr
{
uint8_t sec;
uint8_t mi;
uint8_t hh;
uint8_t sec_prev;
} iUhr;
struct // RTC 1307
{
uint8_t sec;
uint8_t mi;
uint8_t hh;
uint8_t dd;
uint8_t mm;
uint8_t yy;
uint8_t wd;
bool period; // pm am
bool syncronisiert;
bool used;
String wdStr = "MoDiMiDoFrSbSo";
} RTC;
volatile uint8_t mm_p, wd_p, dd_p, hh_p, mi_p, dd_prev_sync;
volatile uint16_t yy_p;
String s1;
String s2;
String s3;
const byte time_zl = 1;
const byte time_sp = 0;
const byte date_zl = 0;
const byte date_sp = 0;
#include <Wire.h>
#include <MsTimer2.h>
#include "RTCDS1307.h"
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 6, 5, 4, 7);
RTCDS1307 rtc(0x68); //
/**
* Initialisieren der DCF77 Interrupt-Routinen
*/
/*---------------------------------------------------------*/
void DCF_Init()
/*---------------------------------------------------------*/
{
rxPtr = 0;
Telegramm_Ende = false;
Timer_DCF_synchron = false;
//
pinMode(DCFPIN_Rise, INPUT);
pinMode(DCFPIN_Fall, INPUT);
// beide ISR-Pins sind parallel geschaltet:
attachInterrupt(digitalPinToInterrupt(DCFPIN_Rise), ISR_RisingEdge, RISING);
attachInterrupt(digitalPinToInterrupt(DCFPIN_Fall), ISR_FallingEdge, FALLING);
}
/*---------------------------------------------------------*/
void RTC_ReadTime()
/*---------------------------------------------------------*/
{
rtc.getTime(RTC.hh, RTC.mi, RTC.sec, RTC.period);
rtc.getDate(RTC.yy, RTC.mm, RTC.dd, RTC.wd);
RTC.wd = RTC.wd - 1; // Angleichung an DCF
iUhr.sec = RTC.sec;
aktuell.hh = RTC.hh; aktuell.mi = RTC.mi;
aktuell.yy = RTC.yy; aktuell.mm = RTC.mm;
aktuell.dd = RTC.dd; aktuell.wd = RTC.wd;
}
/*---------------------------------------------------------*/
void RTC_WriteTime()
/*---------------------------------------------------------*/
{
RTC.yy = aktuell.yy; RTC.mm = aktuell.mm; RTC.dd = aktuell.dd;
RTC.hh = aktuell.hh; RTC.mi = aktuell.mi; RTC.sec = 0;
rtc.setTime(RTC.hh, RTC.mi, RTC.sec);
rtc.setDate(RTC.yy, RTC.mm, RTC.dd);
}
/*---------------------------------------------------------*/
void Timer_Setup()
/*---------------------------------------------------------*/
{
MsTimer2::stop();
MsTimer2::set(1000, Timer_secTakt); // Sekundentakt
MsTimer2::start();
iUhr.sec = 0;
Timer_DCF_synchron = true;
}
/*---------------------------------------------------------*/
void Timer_secTakt()
/*---------------------------------------------------------*/
{
iUhr.sec++;
if (iUhr.sec > 59){iUhr.sec = 0;}
}
/*---------------------------------------------------------*/
void RTC_Synchronisieren()
/*---------------------------------------------------------*/
{
if (aktuell.dd != dd_prev_sync) // täglich synchronisieren
{
RTC.syncronisiert = false;
}
//
if (RTC.syncronisiert == false)
{
if (DCF.ok_cnt > 2)
{
DCF.ok_cnt = 0;
RTC_WriteTime();
RTC.syncronisiert = true;
dd_prev_sync = aktuell.dd;
}
}
}
/*---------------------------------------------------------*/
bool TelegrammVerifizieren()
/*---------------------------------------------------------*/
{
// Verifizieren der Daten:
// Vergleich mit Information der vorhergehenden Minute:
// DCF_ok_cnt wird hochgezählt oder bei Fehler auf 0 gesetzt.
// RTC stellen nur, wenn > 2 aufenanderfolgende Telegramme
// korrekt empfangen worden sind.
if ((DCF.yy < 19) || (DCF.yy > 31)) {return false;}
if ((DCF.hh > 23) || (DCF.mi > 59)) {return false;}
if ((DCF.dd < 1) || (DCF.dd > 31)) {return false;}
if ((DCF.mm < 1) || (DCF.mm > 12)) {return false;}
if ((DCF.mm == 2) && (DCF.dd > 29)) {return false;}
//
// folgende Tests ergeben erst dann true, wenn auch die
// vorangegangene Minute korrekt empfangen worden war.
if (DCF.mi > 0) { if (DCF.mi != mi_p +1) {return false;};}
if (DCF.mi == 0) { if (mi_p != 59) {return false;};}
if (DCF.hh > 0) {if (DCF.hh != hh_p) {return false;};}
if (DCF.mi > 0) { if (DCF.dd != dd_p) {return false;};}
if (DCF.dd < 29) { if (DCF.mm != mm_p) {return false;};}
return true;
}
/* Unterprogramm zum Umwandeln BCD nach Dezimal */
/*---------------------------------------------------------*/
int bcdToDez(byte vonPos, byte bisPos, byte pariCtrl)
/*---------------------------------------------------------*/
/* von und bis bezieht sich auf den dcf_rx_buffer */
{
const byte decodeVal[8] = {1, 2, 4, 8, 10, 20, 40, 80};
int wert = 0;
int j = 0;
byte dcfbit;
volatile byte xorBit;
//
if (bitRead(pariCtrl, 1)) {xorBit = 0;}
for (int i = vonPos; i <= bisPos; i++)
{
dcfbit = rxBuffer[i];
wert = wert + dcfbit * decodeVal[j];
if (dcfbit == 1) {xorBit = xorBit ^ 1;}
j++;
}
// Paritätsbit auswerten:
// if (bitRead(pariCtrl, 2)) {if (rxBuffer[bisPos+1] != xorBit) {wert = 255;};}
return wert;
}
/*---------------------------------------------------------*/
bool TelegrammDecodieren(void)
/*---------------------------------------------------------*/
/*
* Pufferinhalt überprüüfen und dekodieren
* Zeit-Variablen setzen
* Bit 1..14 Wetterdienst
* Bit 15=1 Reserveantenne sendet
* Bit 16=1 Ankündigung Sommerzeit (1h vorher)
* Bit 17=1 18=0 es ist Sommerzeit
* Bit 19=1 Ankündigung für 1 zusätzliche Sekunde:
// Pos: 21 29 36 42 45 50
// mi hh dd wd mm yy
// 1: 1001100:1 001010:0 011001 011 00001 10011000:1 = 14:19 Date: 26.10.19
// 1: 0000010:1 001010:0 011001 011 00001 10011000:1 = 14:20 Date: 26.10.19
{
// errPulse sind in diesem Programm solche, die kürzer als
// 75ms Low waren
// sie entstehen nur bei gestörtem Empfang –
// siehe ISR_RisingEdge()
if (errPulsCnt == 0)
{
// BCD-Zeitinformationen aus dem Puffer lesen:
// Paritätsctrl: 3 : xorBit = 0 und Paritätsbit auswerten
// 2 : xorBit x und Paritätsbit auswerten
// 1 : xorBit = 0 und nicht auswerten
// 0 : -
DCF.mi = bcdToDez(mi_Bit1, mi_Bit7, 3); // PariCtrl
DCF.hh = bcdToDez(hh_Bit1, hh_Bit6, 3);
//
DCF.dd = bcdToDez(dd_Bit1, dd_Bit6, 1);
DCF.wd = bcdToDez(wd_Bit1, wd_Bit3, 0);
DCF.mm = bcdToDez(mm_Bit1, mm_Bit5, 0);
DCF.yy = bcdToDez(yy_Bit1, yy_Bit8, 2);
//..................................
if (TelegrammVerifizieren() == true)
{
// DCF-Zeit übernehmen
aktuell.mi = DCF.mi; aktuell.hh = DCF.hh;
aktuell.wd = DCF.wd; aktuell.yy = DCF.yy;
aktuell.mm = DCF.mm; aktuell.dd = DCF.dd;
//
DCFprev.mi = DCF.mi; DCFprev.hh = DCF.hh;
DCFprev.dd = DCF.dd; DCFprev.mm = DCF.mm;
// yy wird hier nicht übergeben
//
DCF.ok_cnt = DCF.ok_cnt + 1;
RTC.used = false;
RTC_Synchronisieren(); // bedingt
if (rxBuffer[ZoneTime1] == true)
{digitalWrite(SommerPIN, LOW); }
else {digitalWrite(SommerPIN, HIGH); }
if (rxBuffer[ZoneTime2] == true)
{digitalWrite(Plus1SecPIN, LOW); }
else {digitalWrite(Plus1SecPIN, HIGH); }
}
else
{RTC.used = true;}
}
else
{RTC.used = true;}
errPulsCnt = 0; // rücksetzen für neue Minute
// Vergleichsvariable zuweisen auch wenn Empfang gestört ist
// darf nicht in einen obigen else-Zweig erfolgen
yy_p = DCF.yy; mm_p = DCF.mm; dd_p = DCF.dd;
hh_p = DCF.hh; mi_p = DCF.mi;
//...................
if (RTC.used == true)
{
Timer_DCF_synchron = false;
DCF.ok_cnt = 0;
if (RTC.syncronisiert) {s1 =" RTC";} // Kennzeichnung
else {s1 =" RT-";}
}
else {s1 =" DCF";}
noInterrupts();
lcd.setCursor(11, date_zl);
lcd.print(s1);
interrupts();
//
}
/*---------------------------------------------------------*/
void Zeit_Anzeige()
/*---------------------------------------------------------*/
{
byte i;
s1 = "";
// Uhrzeit anzeigen
if (aktuell.hh < 10) { s1 = "0"; }
s1 = s1 + String(aktuell.hh) + ":";
//
if (aktuell.mi < 10) { s1 = s1 + "0";}
s1 = s1 + String(aktuell.mi) + ":00";
//
// Datum anzeigen
s2 = "";
if (aktuell.dd < 10) { s2 = s2 + "0"; }
s2 = s2 + String(aktuell.dd) + ".";
if (aktuell.mm < 10) { s2 = s2 + "0"; }
s2 = s2 + String(aktuell.mm) + ".";
word yyyy = aktuell.yy + 2000;
s2 = s2 + String(yyyy) + " ";
//
// Wochentag anzeigen
s3 = "";
if ((aktuell.wd < 8) && (aktuell.wd > 0))
{
i = 2 * (aktuell.wd - 1);
s3 = RTC.wdStr.substring(i, i+2); // Wochentag
}
//
noInterrupts();
lcd.setCursor(time_sp, time_zl);
lcd.print(s1);
lcd.setCursor(13, time_zl);
lcd.print(s3);
//
lcd.setCursor(date_sp, date_zl);
lcd.print(s2);
interrupts();
}
/**
* Das aktuelle Bit in den Puffer schreiben
* Der Pointer wird hochgezählt.
*/
/*---------------------------------------------------------*/
void RxBufferWrite(byte dcfBit)
/*---------------------------------------------------------*/
{
DCF_Bit = dcfBit;
//
if (rxPtr > 58) {Telegramm_Ende = true;}
if (Telegramm_Ende == true) // 1. Bit startet neue Minute
{
// die 0. Sekunde kommt in der Regel mit rxPtr=59
// der Pointer wird erst hier korrigiert d.h. rxPtr=0
rxPtr = 0; // Sekunde 00
Telegramm_Ende = false;
}
rxBuffer[rxPtr] = dcfBit; // Eintrag in den Telegrammpuffer
rxPtr++;
}
/*
* Interrupthandler für DCF_Pin bei steigender Flanke
* Beide ISR-Pins sind parallel angeschlossen
* Jede Sekunde beginnt mit fallender Flanke.
* Ermitteln, ob empfangenes Bit 1 oder 0 ist.
* Erkennung der 59. Sekunde
*/
/*---------------------------------------------------------*/
void ISR_RisingEdge()
/*---------------------------------------------------------*/
/* Das DCF_Pin erhält das Signal wie unten dargestellt. */
/* diff = t1 - t2 */
/* 1 2 3 4 */
/* _____ ________ ______ ________ */
/* | | | | | | | */
/* | | | | | | | */
/* |__| |____| |__| |__ */
/* ^ ^ ^ ^ ^ ^ ^ */
/* Pegel 0 1 0 1 0 */
/* t2 t1 t2 t1 t2 t1 t2 */
/* Diff 900 200 800 100 900 */
/* */
/* Absenkung liegt exakt am Beginn einer Sekunde */
/* Start einer Minute beginnt mit Bit=0 (Low-Pegel) */
{
t1 = millis(); // Zeitmessung
diff1 = t1 - t2;
/* wenn die Dauer des Pulses < 140ms ist, dann Low */
if (diff1 > 75)
{
if (diff1 < 140) {RxBufferWrite(0);}
else
{if (diff1 < 250) {RxBufferWrite(1);}}
}
else {errPulsCnt++;}
digitalWrite(BlinkPIN, LOW);
}
/*---------------------------------------------------------*/
void ISR_FallingEdge()
/*---------------------------------------------------------*/
/* Interrupt bei jedem fallende Pegel */
/* Das DCF_Pin erhält das Signal wie oben dargestellt. */
{
t2 = millis(); // Zeitmessung
diff2 = t2 - t1;
/* wenn die Dauer des Pulses > 1600ms ist, dann codiert er das Ende der 59. Sekunde*/
if (diff2 > 1200) {Telegramm_Ende = true; Telegramm_Decode = true;} // siehe loop
digitalWrite(BlinkPIN, HIGH);
}
/*=========================================================*/
void setup(void) /*=========================================================*/
{
Wire.begin();
pinMode(BlinkPIN, OUTPUT);
pinMode(SommerPIN, OUTPUT);
pinMode(Plus1SecPIN, OUTPUT);
lcd.begin(16,2);
//
lcd.setCursor(0, 0); // sp zl
lcd.print("Initializing RT");
lcd.setCursor(0, 1);
lcd.print("Position: ");
aktuell.mi = aktuell.hh = 0;
aktuell.dd = aktuell.mm = aktuell.yy = aktuell.wd = 0;
delay(2300);
//
Timer_Setup();
iUhr.sec = 0;
iUhr.sec_prev = 0;
iUhr.mi = 0;
iUhr.hh = 0;
//
rtc.begin();
rtc.setMode(0);
delay(10);
RTC_ReadTime();
RTC.used = true;
RTC.syncronisiert = false;
//
Zeit_Anzeige(); //
//
DCF_Init();
dd_prev_sync = 0;
errPulsCnt = 0;
noInterrupts();
delay(3300);
interrupts();
lcd.setCursor(0, 1);
lcd.print(" ");
//
}
/*=========================================================*/
void loop(void)
/*=========================================================*/
{
if (iUhr.sec != iUhr.sec_prev)
{
iUhr.sec_prev = iUhr.sec;
if (iUhr.sec == 0) // Start einer neuen Timer-Minute
{
if (RTC.used) {RTC_ReadTime();}
Zeit_Anzeige();
}
else
{
// Sekunden-Anzeige:
if (iUhr.sec < 10) {s1 = "0";} else {s1 = "";}
s1 = s1 + String(iUhr.sec) + " " + String(DCF_Bit) + " ";
noInterrupts();
lcd.setCursor(6, time_zl);
lcd.print(s1);
interrupts();
}
}
//
if (Telegramm_Decode == true) // Start neuer DCF-Minute
{
Telegramm_Decode = false;
TelegrammDecodieren();
//
if (Timer_DCF_synchron == false)
{delay(80); Timer_Setup();} // setzt auch sec = 0;
scnt = 0;
}
}
ArduinoSketch