DS3231 – die Atomuhr des Arduino

Eine Funkuhr läuft wie jede Uhr nicht wirklich präzise. Deswegen gleicht sie sich jede Stunde wieder über das DCF77-Signal mit der Atomuhr ab. Beim Computer nicht anders (wobei die eine RTC mit drin haben), hier geschieht das ganze per Netzwerk über das NTP-Protokoll.
Ein Arduino mag in der Regel mit 16MHz schwingen, doch da gibt es Abweichungen durch Temperatur und Verarbeitung des Quarzes. Doch wie realisiert man am Besten eine Uhr, ohne das ganze DCF-Signal zu dekodieren?

Ein delay von 1000 Millisekunden steht für 16 Millionen Takte (bei 16MHz) nichts tun. Wenn man eine Variable namens Sekunde inkrementiert, dann nimmt das laut Datenblatt des ATmega einen Takt in Anspruch (INC). Will man dann noch einen Pin verändern, braucht das einen halben Takt (SBI/CBI). Bedeutet Jede Sekunde braucht die Sekunde 1,5 Takte mehr. Allerdings ist die Arduino etwas überladener, um es dem Benutzer einfacher zu machen. Des Weiteren sind noch unterschiedliche Befehle dazwischen, die auch noch einige Takte rauben (RJMP, RETI, …). Interrupts verlängern das ganze noch einmal. Bedeutet im Endeffekt, egal, wie genau der Quarz läuft und der delay auch noch so genau eine Sekunde sein mag:
a) Kann man während des delays keine anderen Dinge mehr machen (z.B. Zeit durch Tasten stellen)
b) Wird die Sekunde warten durch nachfolgende Operationen stark ausgedehnt
c) Timer-Interrupts verlassen sich auf den Quarz, der beschriebene Ungenauigkeiten hat
d) Ein ausgelöster Interrupt kann einen anderen am Auslösen hindern

Viel Geschwafel, um eines zu sagen: ohne hohen Aufwand und low-level Coding wird man kaum eine genaue Uhr bauen können. Uhren habe ich schon mehrere gebaut, also muss es ja doch einen Weg geben. Das Stichwort: Real Time Clock, kurz RTC, auf deutsch schlicht Echtzeituhr. Das kleine Wunderding übernimmt Datum und Uhrzeit ohne groß Nachzudenken.

In der Vergangenheit nutzte ich ein DS1307 Modul, musste aber schnell feststellen, dass das fast genau so gut funktioniert, als wenn ich die Methode mit dem delay nutze. Bereits nach einem Tag gab es Abweichungen von mehreren Sekunden. Die Alternative: DS3231. Das Teil solle hoch präzise sein. In China kosten 10 Stück ca. $9. Tut nicht weh, also gekauft. Aus der Post geholt, Zeit eingestellt, eingebaut und nach mehreren Wochen immer noch keine einzige Sekunde daneben.

Die ganze Magie:
Das Modul besitzt einen Temperatursensor, der eventuelle Abweichungen des Quarzes durch Wärmeeinfluss kompensiert. Netter Nebeneffekt: die Raumtemperatur kann auch ausgelesen werden. RTCs sind mit Batterien (standardmäßig CR2032 oder als Akku LIR2032) ausgestattet, die sicherstellen, dass die Zeit nicht verloren geht, wenn der Strom am Hauptgerät ausfällt. Auch um Schaltjahre muss man sich bis 2100 keine Gedanken machen, allerdings um die Sommerzeit, aber darum hat sich ja zum Glück schon Olaf gekümmert.
Angesprochen wird das Modul über I2C, 8 verschiedene Adressen sind mittels Jumpern (Lötbrücken) möglich.

Mittels der Library kann das Modul abgefragt werden, ohne dass man sich einen Kopf drum machen muss. Man muss das Modul nicht einmal initialisieren. Lösung 1: im Loop immer wieder ein time_t struct anlegen, die Zeit von der RTC auslesen und dann die Werte verarbeiten. Geht, aber geht auch einfacher! Der Arduino ist unsere Funkuhr, die RTC unsere Atomuhr. Um die beiden regelmäßig zu synchronisieren, bietet die Time-Library eine Funktion namens setSyncProvider(). Diese rufen wir einfach mit dem Parameter RTC.get() auf, schon fragt der Arduino regelmäßig und selbstständig die Zeit ab und synchronisiert seine interne Zeit damit. Im nachfolgenden Programm kann man dann einfach hour(), minute() und second() nutzen, um die Zeit abzufragen, now() gibt die UNIX-Timestamp an (allerdings nur in CET, wenn auch die restliche Zeit in der Zeitzone definiert ist). year(), month() und day() spucken das Datum aus. Mittels RTC.temperature() wird die Temperatur in °C ermittelt, allerdings mit Faktor 4 ausgegeben. RTC.temperature()/4.0 gibt dann in ¼° Schritten die Temperatur aus, gibt man nur /4 ein, so erhält man einen gerundeten int-Wert, also in 1° Schritten. Die Abweichungen liegen bei ±3.0° laut Datenblatt.

Vorher muss man allerdings noch die Zeit stellen. Dafür gibt es in der Library unter examples den Sketch SetTime. Hier wird zum Zeitpunkt des Kompilierens die Zeit des Computers mit in die HEX-Datei eingebaut und geflasht. Da der Prozess des Aufspielens immer etwas dauert (kompilieren, hochladen), habe ich bei der Funktion getTime(…) tm.Second = Sec+5; eingestellt. Das kompensiert die Verzögerung. Sollte es nicht ganz passen, so einfach die Zahl etwas modifizieren.

*Info: In diesem Beitrag verweisen orangefarbende Links auf Affiliates.
Dieser Eintrag wurde veröffentlicht in Allgemein von Luca Zimmermann. Permanenter Link des Eintrags.

Über Luca Zimmermann

Programmiere seit ich 12 bin und habe einen Hang zur Elektronik seit ich denken kann. Studiere aktuell angewandte Informatik an der Hochschule Hannover. Hier moderiere ich das Forum, warte den Server, bin China-Zwischenhändler und kümmer mich um noch so einiges. Beantworte alle Fragen, sofern möglich, gerne auch im Forum. Fast 24/7 erreichbar.