DS1302 und DS1307 RTC Real-Time Clock

Über den Beitrag

In diesem Beitrag komme ich noch einmal auf das Thema Echtzeituhr (RTC = Real-Time Clock) zurück und stelle den DS1302 und den DS1307 vor. Für die Ansteuerung nutze ich die Bibliothek Rtc by Makuna. Sie ist ähnlich konzipiert wie die RTClib, die ich in meinem Beitrag über den DS3231 benutzt habe. Ich nutze hier eine andere Bibliothek, da die RTClib den DS1302 nicht mit abdeckt.

Das kommt auf euch zu:

Technische Eigenschaften

Der DS1302 und der DS1307 sind Echtzeituhren. Das bedeutet, dass sie im Gegensatz zu einfachen Timern die Zeit und das Datum im Format Jahr/Monat/Tag/Stunde/Minute/Sekunde vorhalten. Dabei sind die Schaltjahre bis zum Jahr 2100 einprogrammiert.

Im Vergleich zum DS3231 besitzen der DS1302 und der DS107 weniger Funktionen. So haben beide beispielsweise keine Alarmfunktion. Dafür ist ihr Stromverbrauch noch einmal geringer als der des DS3231. Im „Timekeeping Modus“, also im reinen Batteriebetrieb liegt er bei unterhalb 0.5 Mikroampere – damit hält eine CR2032 Knopfzelle (theoretisch) Jahrzehnte.

  • Betriebsspannung:
    • DS1302: 2.0 – 5.5 Volt
    • DS1307: 2.0 -3.5 Volt VBAT / 4.5 – 5.5 Volt VCC
  • Stromverbrauch im „Timekeeping Modus“: < 0.5 Mikroampere
  • Kommunikation:
    • DS1302: spezielles 3-Wire Protokoll
    • DS1307: I2C (Adresse: 0x68, nicht einstellbar)
  • Temperaturkompensation: keine
  • Kristall: extern, 32768 Hz, 45 kOhm
    • DS1302: 6 pF
    • DS1307: 12.5 pF
  • RAM:
    • DS1302: 31 Byte
    • DS1307: 56 Byte
  • Trickle Charger: nur DS1302

Das Datenblatt für den DS1302 findet ihr hier, das Datenblatt für den DS1307 gibt es hier.

Pinout

Beide ICs haben acht Pins. Neben GND gibt es einen Pin für die Batteriespannung (VCC1 bzw. VBAT) und einen für die „normale“ Versorgungsspannung (VCC2 bzw VCC). X1 und X2 sind die Pins für den Oszillator.

Der DS1302 kommuniziert über eine spezielle 3-Wire Schnittstelle und nutzt dazu die drei Pins SCLK, I/O (DAT) und CE (RST). Der DS1307 kommuniziert über I2C und benötigt deswegen nur SDA und SCL. Am DS1307 könnt ihr ein 1 Hz, 4 KHz, 8 KHz oder 32kHz Signal an SQW/OUT abgreifen.

Pinout DS1302 und DS1307
Pinout DS1302 und DS1307

DS1302 und DS1307 Module

Module des DS1302 und DS1307 sind in vielen verschiedenen Ausfertigungen erhältlich. Ich habe diese gängigen Vertreter verwendet:

DS1302 und DS1307 Module
DS1302 und DS1307 Module

Der Vorteil an Modulen ist, dass ihr euch nicht um den Oszillator kümmern müsst und dass für gewöhnlich ein Halter für CR2032 Batterien bzw. LIR2032 Akkus vorhanden ist. 

Modul im Eigenbau

Für einige Versuche habe ich mir ein DS1302 Modul selber gebaut. Dazu habe ich den DS1302 IC mit einem Batteriehalter, Pinleisten und einem Oszillator (auf der Rückseite) versehen:

DS1302 Selbstbau – Modul

Dabei habe ich festgestellt, dass die zu diesem Zweck benötigten 6 pF / 32768 Hz Oszillatoren gar nicht so leicht zu bekommen sind. Ich erzähle lieber nicht, was ich dafür ausgegeben habe …

Besonderheiten des DS1307 Moduls (HW-111)

Das hier verwendete DS1307 Modul (HW-111) hat zusätzlich noch einen 32 Kilobit EEPROM (=4 KB), auf dem ihr Daten speichern könnt. Das ist ideal für die Verwendung als Datalogger. Hinzu kommen Pull-Up Widerstände für die I2C Leitungen und Kondensatoren zur Spannungsstabilisierung. Weniger gelungen ist die Ladeschaltung für Akkus – dazu komme ich gleich. 

Ladeschaltung des DS1307 Moduls (HW-111)

Das auf dem DS1307 basierende HW-111 Modul besitzt eine Ladeschaltung für LIR2032 Akkus. Der positive Pol des Batteriehalters ist dazu über eine Diode und einen 200 Ohm Widerstand mit VCC verbunden. Die Ladeschaltung ist nicht abschaltbar und das ist gar nicht gut für CR2032 Batterien. Selbst für LIR2032 Akkus ist die Schaltung nicht wirklich geeignet. Meine Empfehlung: entfernt die Diode (das auffällige rote Bauteil). Im Detail bin ich auf dieses Thema schon in meinem Beitrag über den DS3231 eingegangen.

Vorbereitungen

Installation der Bibliothek „Rtc by Makuna“

Falls ihr einen DS1302 verwendet, dann empfehle ich die Bibliothek „Rtc by Makuna“, die ihr über den Library Manager der Arduino IDE installieren könnt oder ihr ladet sie direkt hier von GitHub herunter. Verwendet ihr einen DS1307, dann könnt ihr ebenso gut die Bibliothek RTClib von Adafruit verwenden, müsstet dann allerdings die Beispiele aus diesem Beitrag anpassen. Beide Bibliotheken sind ähnlich in der Bedienung, aber eben nicht gleich.

Anschluss an den Mikrocontroller

DS1302

Am einfachsten versorgt ihr euren DS1302 über den 5 Volt Ausgang eures Arduinos. Die Pins für die Kommunikation via 3-Wire könnt ihr frei wählen:

DS1302 am Arduino Nano

DS1307

Der Anschluss des DS1307 bedarf eigentlich keiner Erklärung. GND kommt an GND, VCC an 5V und SDA/SCL verbindet ihr mit den I2C Pins eures Mikrocontrollers:

Den SQW Pin könnt ihr unverbunden lassen.

Genauigkeit / Abweichung von DS1302 und DS1307

Die hier betrachteten RTCs sind erheblich ungenauer als der DS3231. Zum einen haben die externen Oszillatoren eine höhere Toleranz, zum anderen fehlt ihnen die Temperaturkompensation des DS3231.

Ich habe mehrere gekaufte DS1302 und DS1307 Module und zwei selbst verdrahtete DS1302 ICs (DS1302_self_x) gegeneinander antreten lassen. Die Module habe ich nach einer Atomuhr exakt gestellt und dann die Abweichung täglich für eine Woche gemessen. Hier das Ergebnis:

Abweichung von DS1302 und DS1307 über eine Woche

Die Ergebnisse sind sowohl für die fertigen, als auch die selbstgebauten DS1302 Module sehr enttäuschend. Die Abweichungen betragen bis zu einer Minute innerhalb von 7 Tagen. Lediglich eines der Module zeigt eine Abweichung von unter 10 Sekunden in einer Woche.

Die drei getesteten DS1307 Module liefern bessere Ergebnisse. Alle Abweichungen liegen unterhalb von 10 Sekunden pro Woche.

Zum Vergleich: DS3231 Module haben nach meiner Erfahrung eine Abweichung von einigen Sekunden (< 10) im Monat.

Wenn ihr vor einer Kaufentscheidung steht, dann legt euch deshalb lieber einen DS3231 zu. Solltet ihr aber schon Besitzer eines DS1302 oder DS1307 sein, dann seid auf entsprechende Abweichungen gefasst. Ich kann natürlich nicht behaupten, dass meine Ergebnisse repräsentativ sind. Dafür müsste ich noch viel mehr Module aus unterschiedlichen Quellen testen. Aber der Trend ist schon sehr eindeutig.

Die Abweichungen sind zwar modulspezifisch unterschiedlich, scheinen aber für jedes Modul einigermaßen konstant zu sein. Daraus ergibt sich die Möglichkeit, eine automatische Nachstellung zu programmieren. Allerdings habe ich meine Versuche bei ziemlich konstanten Temperaturen durchgeführt. Bei größeren Schwankungen mag das Ergebnis wieder anders aussehen. Wie ihr die Zeit per Google nachstellt, habe ich weiter unten beschrieben.

Grundlegende Funktionen des DS1302 und des DS1307

Die folgenden Sketche demonstrieren, wie ihr die grundlegenden Funktionen des DS1302 und des DS1307 mit der Bibliothek Rtc by Makuna nutzt. Beide Sketche tun im Prinzip dasselbe. Ihr erzeugt zunächst ein RTC-Objekt und initialisiert es. Dann stellt ihr die Zeit mithilfe der Computersystemzeit zum Zeitpunkt des Kompilierens. Dabei werden noch einige Checks durchgeführt. Schließlich wird die Zeit in loop() regelmäßig gelesen und ausgegeben.

Die Sketche basieren auf den Bibliotheksbeispielen DS1302_simple und DS1307_simple, die ich geringfügig modifiziert habe. 

Basissketch DS1302

Hier zunächst der Sketch:

#include <ThreeWire.h>  
#include <RtcDS1302.h>

ThreeWire myWire(6,5,4); // DAT (I/O), CLK (SCLK), RST (CE)
RtcDS1302<ThreeWire> rtc(myWire);

void setup () {
    Serial.begin(9600);
    Serial.print("compiled: ");
    Serial.print(__DATE__);
    Serial.print(" ");
    Serial.println(__TIME__);

    rtc.Begin();

    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    printDateTime(compiled);
    Serial.println();

    if (!rtc.IsDateTimeValid()) {
        // Common Causes:
        //    1) first time you ran and the device wasn't running yet
        //    2) the battery on the device is low or even missing

        Serial.println("RTC lost confidence in the DateTime!");
        rtc.SetDateTime(compiled);
    }

    if (rtc.GetIsWriteProtected()){
        Serial.println("RTC was write protected, enabling writing now");
        rtc.SetIsWriteProtected(false);
    }

    if (!rtc.GetIsRunning()){
        Serial.println("RTC was not actively running, starting now");
        rtc.SetIsRunning(true);
    }
    RtcDateTime now = rtc.GetDateTime();
    if (now < compiled) {
        Serial.println("RTC is older than compile time!  (Updating DateTime)");
        rtc.SetDateTime(compiled);
    }
    else if (now > compiled) {
        Serial.println("RTC is newer than compile time. (this is expected)");
    }
    else if (now == compiled) {
        Serial.println("RTC is the same as compile time! (not expected but all is fine)");
    }
}

void loop () {
    RtcDateTime now = rtc.GetDateTime();

    printDateTime(now);
    Serial.println();

    if (!now.IsValid()) {
        // Common Causes:
        //    1) the battery on the device is low or even missing and the power line was disconnected
        Serial.println("RTC lost confidence in the DateTime!");
    }

    delay(10000); // ten seconds
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt){
    char datestring[25];
    char daysOfTheWeek[7][4] = {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    snprintf_P(datestring, 
        countof(datestring),
        PSTR("%3s, %02u.%02u.%04u %02u:%02u:%02u"),
        daysOfTheWeek[dt.DayOfWeek()],
        dt.Day(),
        dt.Month(),
        dt.Year(),
        dt.Hour(),
        dt.Minute(),
        dt.Second() );
    Serial.print(datestring);
}

 

Und hier die Ausgabe:

Ausgabe von ds1302_basic.ino
Ausgabe von ds1302_basic.ino

Erklärungen zu ds1302_basic

Ich werde nicht jede einzelne Zeile durchsprechen, da vieles selbsterklärend ist.

  • Zunächst bindet ihr die notwendigen Bibliotheksdateien ein. 
  • Mit ThreeWire myWire(6,5,4); erzeugt ihr ein 3-Wire Objekt und legt dabei die zugehörigen Pins fest. Dafür könnt ihr jeden beliebigen I/O Pin eures Mikrocontrollers wählen.
  • RtcDS1302<ThreeWire> rtc(myWire); erzeugt ein RTC Objekt, dem das 3-Wire-Objekt übergeben wird.
  • RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); kreiert ein RtcDateTime Objekt, das den Zeitpunkt der Kompilierung enthält. RtcDateTime Objekte speichern Datum und Zeit intern als Jahr, Monat, Tag, Stunde, Minute, Sekunde. Darauf zugreifen könnt ihr über die Funktionen Year(), Month(), Day(), Hour(), Minute() und Second(). Dabei liefert Year() das Jahr als uint16_t zurück. Alle anderen Funktionen liefern ein uint8_t.
  • IsDateTimeValid() prüft, ob das Datum auf der RTC Sinn ergibt (z.B.: sind Minuten <= 60?).  Die Funktion ist identisch mit GetDateTime().IsValid().
  • SetDateTime() stellt die Zeit, indem ihr der Funktion ein RtcDateTime Objekt übergebt.
  • Der DS1302 hat einen Schreibschutz. Mit GetIsWriteProtected() und SetIsWriteProtected(true/false) erfragt ihr den Status oder setzt ihn.
  • Um den DS1302 zu starten, muss er erst einmal „angeschubst“ werden. Das tut ihr mit SetIsRunning(true). Übergebt ihr false, schaltet er ab. Mit GetIsRunning() fragt ihr den Status ab.
  • GetDateTime() fragt die aktuelle Zeit des DS1302 ab und liefert als Rückgabewert ein RtcDateTime Objekt.
  • Ihr könnt mit RtcDateTime Objekten Rechen- und Vergleichsoperationen wie beispielsweise now > compiled durchführen.
  • Zum Formatieren des Datums wird die Funktion snprintf_P() verwendet. Das ist keine Funktion der RTC Bibliothek. Ich komme gleich dazu.

Basissketch DS1307

Der Basissketch zum DS1307 ist ähnlich:

#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS1307.h>
RtcDS1307<TwoWire> rtc(Wire);


void setup () 
{
    Serial.begin(9600);

    Serial.print("compiled: ");
    Serial.print(__DATE__);
    Serial.println(__TIME__);

    rtc.Begin();

    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    printDateTime(compiled);
    Serial.println();

    if (!rtc.IsDateTimeValid()){
        if (rtc.LastError() != 0){
            Serial.print("RTC communications error = ");
            Serial.println(rtc.LastError());
        }
        else{
            Serial.println("RTC lost confidence in the DateTime!");
            rtc.SetDateTime(compiled);
        }
    }

    if (!rtc.GetIsRunning()) {
        Serial.println("RTC was not actively running, starting now");
        rtc.SetIsRunning(true);
    }

    RtcDateTime now = rtc.GetDateTime();
    if (now < compiled) {
        Serial.println("RTC is older than compile time!  (Updating DateTime)");
        rtc.SetDateTime(compiled);
    }
    else if (now > compiled) {
        Serial.println("RTC is newer than compile time. (this is expected)");
    }
    else if (now == compiled) {
        Serial.println("RTC is the same as compile time! (not expected but all is fine)");
    }

    rtc.SetSquareWavePin(DS1307SquareWaveOut_Low); 
}

void loop () 
{
    if (!rtc.IsDateTimeValid()) {
        if (rtc.LastError() != 0){
            // we have a communications error
            // see https://www.arduino.cc/en/Reference/WireEndTransmission for 
            // what the number means
            Serial.print("RTC communications error = ");
            Serial.println(rtc.LastError());
        }
        else{
            // Common Causes:
            //    1) the battery on the device is low or even missing and the power line was disconnected
            Serial.println("RTC lost confidence in the DateTime!");
        }
    }

    RtcDateTime now = rtc.GetDateTime();

    printDateTime(now);
    Serial.println();

    delay(1000); // ten seconds
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt){
    char datestring[25];
    char daysOfTheWeek[7][4] = {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    snprintf_P(datestring, 
            countof(datestring),
            PSTR("%3s, %02u.%02u.%04u %02u:%02u:%02u"),
            daysOfTheWeek[dt.DayOfWeek()],
            dt.Day(),
            dt.Month(),
            dt.Year(),
            dt.Hour(),
            dt.Minute(),
            dt.Second() );
    Serial.print(datestring);
}

 

Erklärungen zu ds1307_basis

Es gibt viele Übereinstimmungen mit der Steuerung des DS1302. Damit ihr aber nicht zu viel hin- und herscrollen müsst, gehe ich noch einmal auf alle Funktionen ein:

  • Zunächst bindet ihr die notwendigen Bibliotheksdateien ein.
  • Der DS1307 nutzt I2C. Deswegen übergebt ihr bei der Erzeugung eures RTC Objektes das Wire Objekt: RtcDS1307<TwoWire> rtc(Wire);.
  • RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); kreiert ein RtcDateTime Objekt, das den Zeitpunkt der Kompilierung enthält. RtcDateTime Objekte speichern Datum und Zeit intern als Jahr, Monat, Tag, Stunde, Minute, Sekunde. Darauf zugreifen könnt ihr über die Funktionen Year(), Month(), Day(), Hour(), Minute() und Second(). Dabei liefert Year() das Jahr als uint16_t zurück. Alle anderen Funktionen liefern ein uint8_t.
  • IsDateTimeValid() macht einen Check, ob das Datum auf der RTC Sinn ergibt (z.B.: sind Minuten <= 60?).  Die Funktion ist identisch mit GetDateTime().IsValid().
  • SetDateTime() stellt die Zeit, indem ihr der Funktion ein RtcDateTime Objekt übergebt.
  • Um den DS1307 ans Laufen zu bringen, muss er erst einmal „angeschubst“ werden. Das tut ihr mit SetIsRunning(true). Übergebt ihr false, schaltet er ab. Mit GetIsRunning() fragt ihr den Status ab.
  • SetSquareWavePin() legt die Ausgabe des SQW Pins fest. Als Parameter übergebt ihr DS1307SquareWaveOut_x, mit x = High, Low, 1Hz, 4kHz, 8kHz oder 32kHz.
  • GetDateTime() fragt die aktuelle Zeit des DS1307 ab und liefert sie als RtcDateTime Objekt.
  • Ihr könnt mit RtcDateTime Objekten Rechen- und Vergleichsoperationen wie beispielsweise now > compiled durchführen.
  • Wenn LastError() etwas anderes als 0 zurückgibt, liegt ein Fehler in der I2C Verbindung vor. Bei dem Fehlercode handelt es sich um den Rückgabewert von Wire.endTransmission(), der an LastError() durchgereicht wird. Für Details zu den Fehlercodes schaut hier.
  • Zum Formatieren des Datums wird die Funktion snprintf_P() verwendet. Das ist keine Funktion der RTC Bibliothek. Ich komme gleich dazu.

Formatierung von Zeit und Datum

Über das Formatieren von Ausgaben könnte man einen ganzen Beitrag schreiben. Die Funktionen printf() und sprintf() sind vielen wahrscheinlich geläufig. Zusätzlich gibt es noch eine Reihe verwandter Funktionen. Eine sehr kompakte Zusammenfassung findet ihr hier. Die Funktion snprintf_P() findet seltener Anwendung. Sie basiert auf der Funktion snprintf(), die z. B. hier erklärt ist.

So wird snprintf_P() angewendet:

snprintf_P(s, sizeof(s), PSTR("String mit Variablen"), Variable_1, Variable_2, usw.)

Dabei ist „s“ das Character Array, in dem das Ergebnis gespeichert wird. Die Größe wird über sizeof(s) berechnet. Darauf folgt der Ausgabestring („Format String“) mit dem Text und den Platzhaltern und dann schließlich die Variablen, die die Platzhalter in der Ausgabe ersetzen. Das PSTR() Makro macht den Unterschied zu snprintf() aus. Es bewirkt, dass der umschlossene String nicht aus dem SRAM, sondern dem Flash gelesen wird. Das ist vergleichbar mit dem bekannteren F() Makro. In kurz: snprintf_P() spart gegenüber snprintf() wertvollen SRAM.

Dass ihr hier F() nicht nutzen könnt, liegt an dem Datentyp. Das im Detail zu erläutern, würde zu weit führen. 

Noch nicht erklärt habe ich die Funktion DayOfWeek(). Sie liefert berechnet den Wochentag als Zahl (0 = Sonntag, 1 = Montag, 2 = Dienstag, usw.).

Die Zeit einstellen

Zeit über seriellen Monitor stellen

Wenn ihr den Zeitpunkt der Kompilierung zum Einstellen der RTC-Zeit nutzt, liegt ihr von vornherein schon ein paar Sekunden gegenüber der realen Zeit zurück. Das könnte man ausgleichen, indem man ein paar Sekunden auf compile aufschlägt: compile += 10; o. ä. Eine bessere Methode ist, die Uhrzeit über den seriellen Monitor zu stellen. Das könnt ihr auch jederzeit im laufenden Betrieb wiederholen.

Einstellen des DS1302

Der folgende Sketch stellt den DS1302 zunächst grob nach dem Zeitpunkt der Kompilierung. Die darauf basierende Zeit wird im Sekundentakt ausgegeben. Dann stellt ihr die Zeit exakt nach, indem ihr die aktuelle Zeit im seriellen Monitor im Format „Stunde:Minute:Sekunde“ eingebt und sendet.

#include <ThreeWire.h>  
#include <RtcDS1302.h>

ThreeWire myWire(6,5,4); // DAT (I/O), CLK (SCLK), RST (CE)
RtcDS1302<ThreeWire> rtc(myWire);

void setup () {
    Serial.begin(9600);

    rtc.Begin();
    if(!rtc.GetIsRunning()) {
        Serial.println("RTC was not actively running, starting now");
        rtc.SetIsRunning(true);
    }
    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    Serial.println("Compile Time:");
    printDateTime(compiled);
    Serial.println();
    rtc.SetDateTime(compiled);
 
    Serial.println("Enter the new time as hh:mm:ss");
}

void loop () {
    RtcDateTime now = rtc.GetDateTime();
    static int currentSecond = 61; // currentSecond to be different from first nextSecond
    int nextSecond = now.Second();
  
    if(nextSecond != currentSecond){
        printDateTime(now);
        Serial.println();
        currentSecond = nextSecond;
    }

    if(Serial.available()){
        uint8_t newHour = Serial.parseInt();
        uint8_t newMinute = Serial.parseInt();
        uint8_t newSecond = Serial.parseInt();
        rtc.SetDateTime(RtcDateTime(now.Year(),now.Month(),now.Day(),newHour, newMinute, newSecond));
        while(Serial.available()){
            Serial.read();
        }
    }
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt){
    char datestring[25];
    char daysOfTheWeek[7][4] = {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    snprintf_P(datestring, 
        countof(datestring),
        PSTR("%3s, %02u.%02u.%04u %02u:%02u:%02u"),
        daysOfTheWeek[dt.DayOfWeek()],
        dt.Day(),
        dt.Month(),
        dt.Year(),
        dt.Hour(),
        dt.Minute(),
        dt.Second() );
    Serial.print(datestring);
}

 

Um zu gewährleisten, dass die übergebene Zeit auch sofort übernommen wird, darf in loop() kein delay() verwendet werden. Um trotzdem die Zeit im Sekundentakt auszugeben, fragt der Sketch permanent die Zeit ab, „merkt“ sich die Sekunde und gibt nur dann die Zeit aus, wenn die abgefragte von der gemerkten Sekunde abweicht.

Einstellen des DS1307

Und hier noch einmal dasselbe für den DS1307:

#include <Wire.h>  
#include <RtcDS1307.h>

RtcDS1307<TwoWire> rtc(Wire);

void setup () {
    Serial.begin(9600);

    rtc.Begin();
    if(!rtc.GetIsRunning()) {
        Serial.println("RTC was not actively running, starting now");
        rtc.SetIsRunning(true);
    }
    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    Serial.println("Compile Time:");
    printDateTime(compiled);
    Serial.println();
    rtc.SetDateTime(compiled);
 
    Serial.println("Enter the new time as hh:mm:ss");
}

void loop () {
    RtcDateTime now = rtc.GetDateTime();
    static int currentSecond = 61; // currentSecond to be different from first nextSecond
    int nextSecond = now.Second();
  
    if(nextSecond != currentSecond){
        printDateTime(now);
        Serial.println();
        currentSecond = nextSecond;
    }

    if(Serial.available()){
        uint8_t newHour = Serial.parseInt();
        uint8_t newMinute = Serial.parseInt();
        uint8_t newSecond = Serial.parseInt();
        rtc.SetDateTime(RtcDateTime(now.Year(),now.Month(),now.Day(),newHour, newMinute, newSecond));
        while(Serial.available()){
            Serial.read();
        }
    }
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt){
    char datestring[25];
    char daysOfTheWeek[7][4] = {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    snprintf_P(datestring, 
        countof(datestring),
        PSTR("%3s, %02u.%02u.%04u %02u:%02u:%02u"),
        daysOfTheWeek[dt.DayOfWeek()],
        dt.Day(),
        dt.Month(),
        dt.Year(),
        dt.Hour(),
        dt.Minute(),
        dt.Second() );
    Serial.print(datestring);
}

 

Ausgabe der Einstellsketche

So sah das Ausgabe aus:

Ausgabe von ds1302_set_time_via_serial_monitor bzw. ds1307_set_time_via_serial_monitor
Ausgabe von ds1302_set_time_via_serial_monitor bzw. ds1307_set_time_via_serial_monitor

Um den Effekt besser sichtbar zu machen, habe ich die Zeit in diesem Fall ein paar Stunden vorgestellt. 

Zeit über Google stellen

Sollte euer Mikrocontroller Internetzugang haben, könnt ihr die RTC (die Real-Time Clock) bequem automatisch nachstellen. Dazu sendet ihr Google einen Get-Request und bekommt daraufhin einen Haufen Daten zurück. Darunter ist eine Zeile, die das aktuelle Datum und die Uhrzeit enthält:

Date: Sun, 15 Jan 2023 10:30:45 GMT

Um diese Information zum Stellen eurer RTC zu nutzen, sind ein paar Hürden zu nehmen:

  • Integration eures Mikrocontrollers ins Heimnetz.
  • Kreieren und Senden des Get-Requests.
  • Herausfiltern der „Date-Zeile“ aus der Antwort.
  • Herausfiltern der Stunden, Minuten und Sekunden aus der „Date-Zeile“.

Ich habe einen entsprechenden Sketch für ein ESP32-Board und den DS1302 geschrieben:  

#include <WebServer.h>
#include <ThreeWire.h>  
#include <RtcDS1302.h>

ThreeWire myWire(18, 17, 16); // DAT (I/O), CLK (SCLK), RST (CE)
RtcDS1302<ThreeWire> rtc(myWire);
 
const char* ssid     = "Your Wifi Name";
const char* password = "Your Wifi Password";
const char* host = "www.google.de";
const unsigned long rtcAdjustPeriod = 12000;
 
void setup() {
    Serial.begin(115200);
    delay(1000);
    initRTC();
    initWifi();   
}
 
void loop() {
    static unsigned long lastTimeAdjust = 0;
    RtcDateTime now = rtc.GetDateTime();
    printDateTime(now);
    Serial.println();
    if( ((millis()-lastTimeAdjust)>rtcAdjustPeriod) || (millis()<lastTimeAdjust) ){  // millis() will overflow after ~50 days!
        uint8_t googleTime[3] = {0}; // hour, minute, second as array
        getGoogleTime(googleTime);
        RtcDateTime adjusted = RtcDateTime(now.Year(),now.Month(),now.Day(),googleTime[0],googleTime[1],googleTime[2]);
        adjusted += 3600; // German time = GMT + 1 hour
        rtc.SetDateTime(adjusted);
        lastTimeAdjust = millis();     
    }
    delay(5000);
}

void initRTC(){
    rtc.Begin();
    if(!rtc.GetIsRunning()) {
        Serial.println("RTC was not actively running, starting now");
        rtc.SetIsRunning(true);
    }
    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    Serial.println("Compile Time:");
    printDateTime(compiled);
    Serial.println();
    rtc.SetDateTime(compiled);
}

void initWifi(){
    Serial.print("Connecting to ");
    Serial.println(ssid);
 
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
 
  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println();
}

void getGoogleTime(uint8_t *nowTime){
    String timeString = "";
    timeString.reserve(40);
    Serial.print("connecting to ");
    Serial.println(host);
  
    WiFiClient client;
    const int httpPort = 80;
    if (!client.connect(host, httpPort)) {
        Serial.println("connection failed");
        return;
    }

    // build the get request
    String getRequest = "GET: / HTTP/1.1\r\n Host: ";
    getRequest += host;
    getRequest += "\r\n Connection: close\r\n\r\n";
    
    // Send the get request to google
    client.print(getRequest);
    // start waiting for the response             
    unsigned long lasttime = millis();
    while (!client.available() && millis() - lasttime < 1000) {
        delay(1);
    }   // wait max 1s for data

    //evaluate response
    while(client.available()) {
        timeString = client.readStringUntil('\r');
        if (timeString.indexOf("Date:")>=1) { // scan for the line containing the date/time
            int lastColon = timeString.lastIndexOf(":"); // to find out position of hh:mm:ss
            String subTimeString = timeString.substring(lastColon-6,lastColon-3);
            nowTime[0] = (uint8_t)atoi(subTimeString.c_str()); // extract the hours
            subTimeString = timeString.substring(lastColon-2,lastColon);
            nowTime[1] = (uint8_t)atoi(subTimeString.c_str()); // extract the minutes
            subTimeString = timeString.substring(lastColon+1,lastColon+3);
            nowTime[2] = (uint8_t)atoi(subTimeString.c_str()); // extract the seconds
            return;  // return because we have found what we needed
        }
    }
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt){
    char datestring[25];
    char daysOfTheWeek[7][4] = {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    snprintf_P(datestring, 
        countof(datestring),
        PSTR("%3s, %02u.%02u.%04u %02u:%02u:%02u"),
        daysOfTheWeek[dt.DayOfWeek()],
        dt.Day(),
        dt.Month(),
        dt.Year(),
        dt.Hour(),
        dt.Minute(),
        dt.Second() );
    Serial.print(datestring);
}

 

Und so sieht die Ausgabe dazu aus:

Ausgabe von DS1302_set_time_google.ino
Ausgabe von DS1302_set_time_google.ino

Ein paar Erklärungen zum Sketch

Der Sketch DS1302_set_time_google.ino ist ziemlich gehaltvoll. Für alles, was das Thema WLAN und Get-Request angeht, verweise ich auf meinen Beitrag WLAN mit ESP8266 und ESP32. Nur so viel dazu: Ihr müsst natürlich „Your Wifi Name“ und „Your Wifi Password“ durch die Zugangsdaten eures WLANs ersetzen.

Zum Rest nur ein paar wenige Anmerkungen:

  • rtcAdjustPeriod ist die Periode, in der die Uhrzeit nachgestellt wird. Die 12 Sekunden habe ich nur gewählt, um den Sketch zu prüfen. In realen Anwendungen wäre eine Periode von ein paar Stunden oder einem Tag angemessen.
  • Bei langfristig laufenden Anwendungen, in denen ihr millis() verwendet, müsst ihr bedenken, dass dieser Zähler nach 232 Mikrosekunden (= ~50 Tage) überlauft. Das heißt millis() beginnt wieder bei null. Das erklärt die Bedingung millis()<lastTimeAdjust in Zeile 25.
  • Der Abruf der Google-Zeit erfolgt durch getGoogleTime(). Die Daten, die Google sendet, werden zeilenweise gelesen und nach der Sequenz „Date:“ durchsucht.
  • Ist die richtige Zeile gefunden, werden daraus die Stunden, Minuten und Sekunden extrahiert und in einem Array (nowTime bzw. googleTime) gespeichert. Dabei dient der letzte Doppelpunkt („colon“) als Referenzpunkt für Ermittlung der Position der Stunden, Minuten und Sekunden innerhalb der Zeile. 
  • Ein exzessiver Gebrauch von Strings und Stringfunktionen ist nicht gut, wie ich in meinem letzten Beitrag begründet habe. Allerdings ist das beim schnellen, SRAM-reichen ESP32 nicht so dramatisch. Außerdem werden die Strings in einer Funktion und nicht in loop() verwendet.

Zeit mit einem DCF77 nachstellen

Eine weitere Möglichkeit wäre, die Zeit mit einem DCF77 Funkuhrempfänger nachzujustieren. Über den DCF77 habe ich hier berichtet. Allerdings stellt sich die Frage, wozu man dann überhaupt noch zusätzlich eine RTC braucht. In einem solchen Fall würde ich nur mit dem DCF77 arbeiten oder ihn in Kombination mit einer Software-RTC verwenden, das auf millis() basiert. Die RTClib von Adafruit enthält eine solche Software-RTC. Schaut euch dazu den Beispielsketch softrtc.ino an. 

Rechnen mit RtcDateTime Objekten

In einigen Sketchen haben wir schon Vergleichsoperatoren auf RtcDateTime Objekte angewendet (z.B. now > compiled). Ihr könnt aber auch Rechenoperationen mit ihnen vornehmen. Das, und wie ihr die Unixzeit aus den RtcDateTime Objekten ermittelt, zeige ich in diesem Sketch:

#include <ThreeWire.h>  
#include <RtcDS1302.h>

ThreeWire myWire(6,5,4); // DAT (I/O), CLK (SCLK), RST (CE)
RtcDS1302<ThreeWire> rtc(myWire);

void setup () {
    Serial.begin(9600);
    rtc.Begin();
    RtcDateTime now = rtc.GetDateTime();
    Serial.println("Current date and time: ");
    printDateTime(now);
    Serial.println("\n\r");

    Serial.print("Unixtime: ");
    Serial.println(now.Unix32Time());
    Serial.print("Seconds since 1.1.2000: ");
    Serial.println(now.TotalSeconds());
    Serial.println();

    Serial.println("Date/Time in 1000000 seconds: ");
    now += 1000000;
    printDateTime(now);
    Serial.println("\n\r");

    Serial.print("Time to new year: ");
    now = rtc.GetDateTime();
    RtcDateTime newYear = RtcDateTime(now.Year()+1, 0, 0, 0, 0, 0);
    uint32_t timeToNewYear = newYear.TotalSeconds() - now.TotalSeconds();
    Serial.print(timeToNewYear);
    Serial.println(" seconds, or:");
    uint8_t secondsToNewYear = timeToNewYear%60;
    timeToNewYear /= 60;
    uint8_t minutesToNewYear = timeToNewYear%60;
    timeToNewYear /= 60;
    uint8_t hoursToNewYear = timeToNewYear%24;
    uint16_t daysToNewYear = timeToNewYear/24;
    Serial.print("Days: "); Serial.println(daysToNewYear);
    Serial.print("Hours: "); Serial.println(hoursToNewYear);
    Serial.print("Minutes: "); Serial.println(minutesToNewYear);
    Serial.print("Seconds: "); Serial.println(secondsToNewYear); 
}

void loop () {}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt){
    char datestring[25];
    char daysOfTheWeek[7][4] = {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    snprintf_P(datestring, 
        countof(datestring),
        PSTR("%3s, %02u.%02u.%04u %02u:%02u:%02u"),
        daysOfTheWeek[dt.DayOfWeek()],
        dt.Day(),
        dt.Month(),
        dt.Year(),
        dt.Hour(),
        dt.Minute(),
        dt.Second() );
    Serial.print(datestring);
}

 

Ich denke, der Sketch ist einigermaßen selbsterklärend und spare mir deswegen die Erläuterungen. Auch sollte es euch nicht schwerfallen, den Sketch bei Bedarf auf den DS1307 umzuschreiben.

Hier noch die Ausgabe:

Ausgabe von ds1302_calculations.ino
Ausgabe von ds1302_calculations.ino

Den RAM des DS1302 und DS1307 nutzen

Der DS1302 hat einen RAM von 31 Byte, der RAM des DS1307 beträgt „stolze“ 56 Byte. Die Möglichkeiten, die sich aus einem solchen, geradezu lächerlich kleinem Speicher ergeben, sind natürlich recht begrenzt. Mehr eine Art elektronischer Post-it, der sich allerdings bei Stromunterbrechung in Luft auflöst. Aber unterschlagen wollte ich ihn trotzdem nicht.

DS1302 – Beispiel für RAM Nutzung

In meinem Beispiel für den DS1302 zeige ich, wie ihr im RAM ein formatiertes Datum speichert und auslest. Ihr könntet den Sketch modifizieren und dort zum Beispiel festhalten, wann ihr das letzte Mal die Zeit aktualisiert habt o.ä.

#include <ThreeWire.h>  
#include <RtcDS1302.h>

ThreeWire myWire(6,5,4); // IO, SCLK, CE
RtcDS1302<ThreeWire> rtc(myWire);

#define countof(a) (sizeof(a) / sizeof(a[0]))
RtcDateTime now;

void setup () {
    Serial.begin(9600);
    rtc.Begin();
    char data[] = "No entry yet";
    uint8_t count = sizeof(data);
    uint8_t written = rtc.SetMemory((const uint8_t*)data, count); // this includes a null terminator for the string
    if (written != count) {
        Serial.print("something didn't match, count = ");
        Serial.print(count, DEC);
        Serial.print(", written = ");
        Serial.print(written, DEC);
        Serial.println();
    }
    Serial.println("Enter \"w\" to write current time to the RAM");
    Serial.println("Enter \"r\" to read stored time from the RAM");  
}

void loop () {
    char datestring[20];
    if((millis()%5000) == 0){
        now = rtc.GetDateTime();
        makeDateTimeString(now, datestring, sizeof(datestring));
        Serial.println(datestring);
    }
    if(Serial.available()){
        char command = Serial.read();
        now = rtc.GetDateTime();
        switch(command){
            case 'w':
                Serial.print("Write to RAM: ");
                makeDateTimeString(now, datestring, sizeof(datestring));
                Serial.println(datestring);
                rtc.SetMemory((const uint8_t*)datestring, sizeof(datestring));
                break;
            case 'r':
                rtc.GetMemory((uint8_t*)datestring, sizeof(datestring));
                Serial.print("Read from RAM: ");
                Serial.println(datestring);
                break;
            default:
                Serial.println("No valid command");
        }
    }
}

void makeDateTimeString(const RtcDateTime& dt, char* charArray, uint8_t size){
    snprintf_P(charArray, 
        size,
        PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
        dt.Month(),
        dt.Day(),
        dt.Year(),
        dt.Hour(),
        dt.Minute(),
        dt.Second() );
}

 

Und so sieht die Ausgabe aus:

Ausgabe von ds1302_ram.ino (identisch mit der Ausgabe von ds1307_ram.ino)
Ausgabe von ds1302_ram.ino (identisch mit der Ausgabe von ds1307_ram.ino)

Erklärungen zu ds1302_ram.ino

Die Funktion, um etwas in den RAM zu schreiben, lautet:

SetMemory((const uint8_t*)data, sizeof(data));

Das heißt, ihr übergebt der Funktion typischerweise ein Array. Die Startadresse ist null. Die Funktion liefert die Zahl der geschriebenen Bytes zurück.

Alternativ könnt ihr einzelne Bytes an eine Adresse eurer Wahl schreiben:

SetMemory(address, value);

Die Parameter address und value sind vom Datentyp byte (uint8_t).

Ihr lest den Inhalt des RAM mit der Funktion:

GetMemory((uint8_t*)data, sizeof(data));

Alternativ lest ihr einzelne Bytes mit:

GetMemory(address);

Dabei ist address vom Datenyp byte (uint8_t).

DS1307 – Beispiel für RAM Nutzung

Der Sketch für den DS1307 macht dasselbe wie der vorherige Sketch für den DS1302. Der Unterschied ist, dass ihr hier auch bei der Übergabe von Arrays eine Startadresse wählen könnt:

#include <Wire.h>  
#include <RtcDS1307.h>

RtcDS1307<TwoWire> rtc(Wire);

#define countof(a) (sizeof(a) / sizeof(a[0]))
RtcDateTime now;

void setup () {
    Serial.begin(9600);
    rtc.Begin();
    char data[] = "No entry yet";
    uint8_t count = sizeof(data);
    uint8_t written = rtc.SetMemory(0,(const uint8_t*)data, count); // this includes a null terminator for the string
    if (written != count) {
        Serial.print("something didn't match, count = ");
        Serial.print(count, DEC);
        Serial.print(", written = ");
        Serial.print(written, DEC);
        Serial.println();
    }
    Serial.println("Enter \"w\" to write current time to the RAM");
    Serial.println("Enter \"r\" to read stored time from the RAM");  
}

void loop () {
    char datestring[20];
    if((millis()%5000) == 0){
        now = rtc.GetDateTime();
        makeDateTimeString(now, datestring, sizeof(datestring));
        Serial.println(datestring);
    }
    if(Serial.available()){
        char command = Serial.read();
        now = rtc.GetDateTime();
        switch(command){
            case 'w':
                Serial.print("Write to RAM: ");
                makeDateTimeString(now, datestring, sizeof(datestring));
                Serial.println(datestring);
                rtc.SetMemory(0,(const uint8_t*)datestring, sizeof(datestring));
                break;
            case 'r':
                rtc.GetMemory(0,(uint8_t*)datestring, sizeof(datestring));
                Serial.print("Read from RAM: ");
                Serial.println(datestring);
                break;
            default:
                Serial.println("No valid command");
        }
    }
}

void makeDateTimeString(const RtcDateTime& dt, char* charArray, uint8_t size){
    snprintf_P(charArray, 
        size,
        PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
        dt.Month(),
        dt.Day(),
        dt.Year(),
        dt.Hour(),
        dt.Minute(),
        dt.Second() );
}

 

Erklärungen zu ds1307_ram.ino

Die Funktion, um etwas in den RAM zu Schreiben, lautet:

SetMemory(address, (const uint8_t*)data, sizeof(data));

Dabei ist address vom Datentyp byte (uint8_t). Die Funktion liefert die Zahl der geschriebenen Bytes zurück.

Alternativ könnt ihr einzelne Bytes an eine bestimmte Adresse schreiben:

SetMemory(address, value);

Die Parameter address und value sind vom Datentyp byte (uint8_t).

Ihr lest aus dem RAM mit der Funktion:

GetMemory(address, (uint8_t*)data, sizeof(data));

Alternativ lest ihr einzelne Bytes mit:

GetMemory(address);

Dabei ist address vom Datenyp byte (uint8_t).

Der DS1302 als Trickle Charger

Der DS1302 kann als Trickle Charger eingesetzt werden, d.h. er sorgt dafür, dass ein Akku oder ein Kondensator seinen vollständigen Ladezustand aufrechterhält (Erhaltungsladung). Als Akkulader, also um entleerte Akkus zu laden, ist die Trickle Charger Funktion nicht geeignet, da die Ströme zu gering sind.

Ihr könnt zwei Parameter für den Ladestromkreis einstellen:

  1. Die Anzahl der Dioden (ein oder zwei).
    • Pro Diode wird die Ladespannung um ca. 0.7 V verringert.
  2. Den Widerstand (2, 4 oder 8 kΩ).

Die Spannungsversorgung erfolgt über VCC2, der Ausgang für den Ladestrom ist VCC1. Schematisch sieht das so aus:

DS1302 Trickle Charge Settings
DS1302 Trickle Charge Einstellungen

Praktisch könnte eine Schaltung (hier mit zu ladendem Kondensator) so aussehen:

Der maximale Strom im ungeladenen Zustand beträgt:

    \[ I_{max}\;\text{[A]}=\frac{V_{CC2}-0.7\cdot{Anzahl\_Dioden}}{R_{set}}\;\;\;\;\; \text{mit:}\;{R_{set}}=2, 4, 8\; \text{k}\Omega \]

Wenn ihr also beispielsweise eine Versorgungsspannung von 5 Volt wählt, eine Diode und den kleinsten Widerstand wählt, dann ist der maximale Strom:

    \[ I_{max}=\frac{5-0.7}{2000} = 0.00215 \;\text{[A]} = 2.15 \;\text{[mA]} \]

Mit steigendem Ladezustand wächst VCC1 und der Ladestrom sinkt entsprechend. Bevor ihr den Trickle Charger bei Akkus einsetzt, informiert euch darüber, ob euer Akku dafür geeignet ist! Im Allgemeinen wird dringend davon abgeraten, Trickle Charger für Lithium-Akkus einzusetzen!

Die Programmierung der Trickle Charge Funktion ist einfach. Der Sketch ist selbsterklärend:

#include <ThreeWire.h>  
#include <RtcDS1302.h>

ThreeWire myWire(6,5,4); // DAT (I/O), CLK (SCLK), RST (CE)
RtcDS1302<ThreeWire> rtc(myWire);

void setup () {
    rtc.Begin();
    /* DS1302TcrResistor:
    DS1302TcrResistor_2KOhm
    DS1302TcrResistor_4KOhm
    DS1302TcrResistor_8KOhm

    DS1302TcrDiodes: 
    DS1302TcrDiodes_One  
    DS1302TcrDiodes_Two  

    DS1302TcrStatus:
    DS1302TcrStatus_Enabled
    DS1302TcrStatus_Disabled
    
    Usage: 
    SetTrickleChargeSettings(DS1302TcrResistor | DS1302TcrDiodes | DS1302TcrStatus)
    */
    
    rtc.SetTrickleChargeSettings(DS1302TcrResistor_2KOhm | DS1302TcrDiodes_One |  DS1302TcrStatus_Enabled);
}

void loop () {

 

Danksagung

Das Beitragsbild (Big Ben) habe ich Peggy und Marco Lachmann-Anke auf Pixabay zu verdanken.

8 thoughts on “DS1302 und DS1307 RTC Real-Time Clock

  1. Hallo Wolfgang,
    Du schreibst im Artikel dass sich der RAM-Inhalt bei Stromausfall in Luft auflöst. Tatsächlich ist der RAM aber auch Batterie gestützt. „31 x 8 Battery-Backed General-Purpose RAM“
    Gerade das macht diesen RAM ja aus. Anders als bei einem Flash oder EEPROM lassen sich hier wichtige Betriebszustände ablegen und beliebig oft aktualisieren. In einem Projekt gibt es meist nicht viele Werte welche ein so kurzes Speicherintervall erfordern. Da lässt sich mit diesen wenigen Bytes schon einiges anfangen.
    LG Oliver

  2. Hallo Wolfgang,
    wie läßt sich bei der Datumsangabe das Jahr auf die letzten beiden Stellen reduzieren?
    Also statt 2024 nur 24!

    Gruß, Ian

    1. Hallo Ian,

      so z.B.:

      snprintf_P(datestring, 
              countof(datestring),
              PSTR("%3s, %02u.%02u.%02u %02u:%02u:%02u"),
              daysOfTheWeek[dt.DayOfWeek()],
              dt.Day(),
              dt.Month(),
              (dt.Year())-2000,
              dt.Hour(),
              dt.Minute(),
              dt.Second() );
      

      VG, Wolfgang

  3. „Die Ergebnisse sind sowohl für die fertigen, als auch die selbstgebauten DS1302 Module sehr enttäuschend. “

    Die Genauigkeit hängt an der Qualität des verwendeten Quarzes. Auch muss er genau zur spezifizierten Lastkapazität des DS1307 von 12pF passen. Leider ist kein „Ziehkondensator“ für einen Feinabgleich vorgesehen.
    Man kann sich aber behelfen, wenn man die Gangabweichung misst und per Software alle paar Tage eine Korrektur vornimmt. Den gemessenen Gangfehler sowie Datum und Korrektur des letzten Abgleichs kann man ja im EEPROM ablegen.
    Dann muss man keinen teuren RTC kaufen.

    1. Hi, vielen Dank. Die richtigen Kapazitäten habe ich schon gewählt. Und dabei habe ich gelernt, dass Frequenzstabilität nichts mit der absoluten Abweichung zu tun hat. Ein DS3231 ist mit seiner Temperaturkompensation einfach besser. Sicherlich würde man das auch irgendwie mit den einfachen RTCs hinbekommen, aber dann kann man auch gleich besser zum DS3231 greifen. Und so viel teurer sind die Dinger auch nicht.
      VG, Wolfgang

  4. Hallo Wolfgang

    Vielen Dank für dein tollen Artikel. Für mich als 66 jährigen Rentner sehr gut nachzuvollziehen.
    Ich habe die selbe Erfahrung gemacht das die diversen RTC Module zum Teil starke Abweichungen
    der Ganggenauigkeit haben. Eine Stunde pro Woche als schlechtestes Ergebnis. Zufällig fand ich ein Artikel bei RNT über das NTP Zeitnormal. Ich bin eigentlich dagegen mit meine „Uhren“ ins Internet zu gehen. Auch hier kam ich zufällig darauf das wir doch ein NTP Server in der FritzBox haben. Es gibt da auch eine Bibliothek und Sketch bei RTN dazu. Bei mir hat das sofort funktionier und den RTC geht es nach und nach an den Kragen. Ob andere Internet Router auch NTP Server haben weiß ich leider nicht.
    RNT Random Nerd Tutorials

    Nochmals vielen Dank für dein perfekten Artikel.
    Veit

  5. Habe letzte Woche zwei Basteleien mit DS3231 abgeschlossen (‚Hollow Clock‘ und ‚BTTF Clock‘ auf Thingiverse), da kam mir dieser Artikel gerade recht zum Thema Stromverbrauch und Genauigkeit.
    Als ich vor seeeehr vielen Jahren in der Lehre war, da galt ein Quartz Kristall als das genaueste aller Möglichkeiten in der Elektronik, gar teils in der Physik. Die Atomuhr der Elektroniker sozusagen.
    Nun, der YT-Kanal von „Elektronik“ hat mich da mal wunderschön erden können.
    Ich denke, dieser Link passt so wunderbar in die Betrachtung des Themas Genauigkeit:
    https://www.youtube.com/watch?v=T1RucobZRac

    Und wieder mal Danke an Wolle für den Unterricht.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert