Unixzeit mit XC8 | Datetime Bibliothek

Grundidee

Immer wieder benötigt man auf einem Mikrocontroller eine Uhrzeit und immer wieder steht man vor dem Problem, wie man das am besten umsetzt. Mit der menschliche Schreibweise (Tag, Monat, Jahr, Stunde, …) lässt es sich sehr schlecht rechnen. Zwar ist das eine gute Möglichkeit um lästiges umwandeln zu vermeiden aber es erhöht den Aufwand an vielen Stellen enorm. Nehmen wir an, wir programmieren eine Uhr und erhöhen über einen Timer Interrupt die Zeit immer um eine Sekunde. Wenn wir bei der 59 Sekunde angekommen sind, müssen wir eine Minute aufaddieren und danach eine Stunde, und so weiter. Dazu kommen noch Schaltjahre, Zeitumstellung usw. Wenn der Prozessor das alles in einem Interrupt abarbeiten soll, kann er schnell (je nach Konfiguration) einen Zyclus überspringen. Einen Interrupt sollte man ja schließlich kurz halten.

Ich habe mich also dazu entschlossen, die Unixzeit zu verwenden. Diese rechnet in Sekunden und benötigt somit nur eine Variable die groß genug ist um den gewünschten Bereich abzudecken. In unserem Interrupt inkrementieren wir nur noch die Variable jede Sekunde und haben eine Sekunde unserer Zeitrechnung hinzugefügt. Ebenfalls ist das rechnen mit der Unixzeit einfacher da man nur die beiden Unix-Zeitstempel miteinander addieren oder subtrahieren muss. Jedoch wie bekomme ich dann mein Datum und meine Uhrzeit in lesbarer Schrift auf mein Display? Darum werden wir uns in diesem Beitrag kümmern.

Was ist die Unixzeit genau?

Gerade eben habe ich ja schon die Unixzeit erwähnt und erklärt, dass diese nur mit Sekunden arbeitet. Die Unixzeit beginnt mit der Rechnung am 01.01.1970 um 00:00:00 Uhr. Wenn in meiner Variable für die Zeit nun eine 200 stehen würde, hätten wir die Zeit 01.01.1970 00:03:20. Die Unixzeit kennt aber keine Zeitzonen, keine Sommerzeit und berücksichtigt keine Schaltsekunden. In der Regel wird ein solcher Zeitstempel in einer 32-Bit vorzeichenbehafteten Variable gespeichert (bei mir NICHT, später mehr dazu). Damit ist es auch möglich, eine Zeit vor dem 01.01.1970 anzuzeigen indem der Zeitstempel negativ ist. Mit dieser Speichermethode tritt auch gleich das 2038 Problem auf. Denn am 19.01.2038 neigt sich die Variable dem Ende. Viele Rechner mit Unixzeit verwenden bereits 64-Bit für den Zeitstempel was das Problem ein paar Milliarden Jahre verschiebt. Kleine eingebettete Systeme haben teilweise jedoch nicht die Möglichkeit mit 64-Bit umzugehen. Vielmehr muss man über die Unixzeit nicht unbedingt wissen. Wer jedoch mehr erfahren möchte, kann ja mal auf dem Wikipedia Artikel vorbei schauen.

Die Datetime Bibliothek

In dieser Bibliothek habe ich einige Funktionen untergebracht mit denen man ein menschliches Datum in eine Unixzeit und andersrum umwandeln kann. Die Umwandlung in die Unixzeit ist recht einfach. Jedoch das zurückwandeln ist ein wenig komplizierter. Damit sich nun nicht jeder den Kopf über sowas zerbrechen muss, stelle ich die Bibliothek online und jeder darf sie verwenden!

Defines

UseMESZUseWdayMo bis SoJan bis FebVollmond bis Neumond

UseMESZ

Ist dieses Define auskommentiert, wird beim umrechnen von Unixzeit in Datetime die Sommerzeit nicht berücksichtigt. Sollte die Sommerzeit nicht benötigt werden, dann sollte man dieses Define auskommentieren. Das spart einiges an Platz auf dem Flash und Ram sowie Rechenzeit.

UseWday

Ist dieses Define gesetzt, wird automatisch bei der Umrechnung von Unixzeit in Datetime auch der Wochentag des Datums bestimmt. Auch hier gilt: Wird das nicht gebraucht, bitte auskommentieren um Flash, Ram und Rechenzeit zu sparen.

Mo bis So

Gibt den Tag an (1-7).

Jan bis Feb

Gibt den Monat an (1-12).

Vollmond bis Neumond

Gibt die Mondphase an (0-3).

Struct’s, typedef’s, sonstiges

Struct'stypedef'sSonstiges

struct

dt – Enthält Datum, Uhrzeit, Zeitzone, Wochentag und Sommerzeitindikator

sec – Sekunden

min – Minuten

hour – Stunden

MESZ – 0 wenn Normalzeit ist und 1 wenn Sommerzeit ist (bei ausgeklammertem UseMESZ ist es immer 0)

day – Tag im Monat

month – Monat im Jahre

year – Jahr (1970 – 2106)

zone – Zeitzone

typedef

Datetime – eine kurzschreibweise für „struct dt“

unixzeit – Kurzschreibweise für eine 32-Bit vorzeichenUNbehafteten Datentypen. Dient der besseren lesbarkeit.

Sonstiges

const uint16_t dmon[12] – gibt die Anzahl der Tage an, die im Monat x vergangen sind (normales Jahr)

Funktionen

unixzeit()datetime()wday()Mondphase()

unixzeit()

Diese Funktion wandelt eine Datetime Struktur in die Unixzeit um.
Achtung: Die Datetime muss am 01.01.1970 beginnen. Ansonsten gibt es fehlerhafte Werte.

Parameter

Datetime* zeit – Adresse der Datetime Variable die Umgewandelt werden soll.

Rückgabewert

unixtime – Unixzeit

datetime()

Diese Funktion wandelt eine Unixzeit in eine Datetime Struktur um. Dabei wird die Datetime in die übergebene Datetime Adresse geschrieben. Je nachdem ob UseMESZ bzw. UseWday auskommentiert sind wird die Sommerzeit berücksichtigt bzw. der Wochentag berechnet.

Parameter

Datetime* t – Adresse der Datetime Variable in die die umgewandelte Unixzeit geschrieben werden soll.

unixtime unix_zeit – Die Unixzeit die Umgewandelt werden soll.

int8_t zone – Die Zeitzone die berücksichtigt werden soll.

wday()

Errechnet den Wochentag anhand der übergebenen Datetime. Angegeben werden muss Tag, Monat und Jahr (nur 1900-2099 möglich).

Parameter

Datetime* zeit – Adresse der Datetime Variable für die der Wochentag berechnet werden soll.

Rückgabewert

uint8_t – Der errechnete Wochentag (Montag = 1, Dienstag = 2, …)

Mondphase()

Berechnet näherungsweise die Mondphase die zu dem angegebenen Zeitpunkt herrscht. Dieser Wert ist aber wirklich nur eine Annäherung und weicht nach eigenen Tests 1-2 Tage ab. In der Mitte des Jahres meist 2 Tage.

Parameter

Datetime* zeit – Adresse der Datetime Variable für die Mondphase berechnet werden soll.

Rückgabewert

uint8_t – Die Mondphase (Vollmond = 0, Abnehmender Mond = 1, Zunehmender Mond = 2, Neumond = 3)

Einsetzen der Funktionen

Um die Funktionen einzusetzen benötigt ihr Variabeln mit den Werten mit denen ihr arbeiten wollt.

Beispiel: globale Variable: unixtime time, Datetime mtime

Solltet ihr Beispielsweise die Variable time über einen Interrupt inkrementieren, solltet ihr diese zunächst auf volatile setzen. In dem restlichen Programm könnt ihr dann mit datetime() die Zeit ausrechnen lassen um diese auf einem Display auszugeben.

Dazu schreibt ihr folgendes:

datetime(&mtime, time, 0);

Mit „&mtime“ übergebt ihr die Adresse der Variable mtime. time wird als kopie übergeben und die Zeitzone ist hier nicht relevant. Wer nicht weiß, warum das &-Zeichen dort eingefügt wird, sollte sich mit Pointern in C auseinandersetzen. Kurz gesagt: Mit einem &-Zeichen übergibt man die Adresse einer Variable. So verbraucht man den Platz im Arbeitsspeicher nicht doppelt wenn es nicht notwendig ist.

Um nun aus der Datetime wieder eine Unixzeit zu verwandeln, wenden Sie unixzeit() auf mtime an.

z.B. so:

time = unixzeit(&mtime);

Wichtig: In der mtime MUSS eine gültige Zeit stehen. Es darf dort keine Minute 60 geben oder ähnliches. Ebenfalls darf Tag und Monat nicht kleiner als 1 sein und Jahr nicht kleiner als 1970.

Anmerkung: Es wäre auch möglich gewesen, die Funktion so zu schreiben, dass sie die Adresse der Unixzeit entgegen nimmt. Jedoch kostet dies mehr Flashspeicher als man an RAM sparen würde. Daher habe ich mich für diese Variante entschieden.

Die einzelnen Daten aus der mtime-Variable kann man nun wie folgt abrufen:

a = mtime.sec; //Sekunde auslesen

Man schreibt also den Namen der Variable dann einen Punkt und den Wert den man wissen möchte.

Anmerkung: Erhält man die Adresse der Variable schreibt man den Variablennamen mit einem Stern davor um den Wert an der Adresse zu erfahren. Bei einem struct schmeißt der XC8 Compiler jedoch gerne Errors. Da hilft es dann, den Variablennamen in Klammern zu setzen:

a = (*mtime).sec; //Sekunde auslesen

Zusätzliche Informationen

Ich erwähnte bereits, dass die Unixzeit normalerweise in einer vorzeichenbehafteten Variable gespeichert wird. Ich persönlich empfand eine Zeit vor 1970 für einen Mikrocontroller relativ uninteressant und habe somit einen vorzeichenunbehafteten Datentypen gewählt. Das machte mir die Umrechnung in die Datetime auch ein wenig einfacher. Die Auswirkung ist dadurch also klar, man kann keinen Zeitraum vor dem 01.01.1970 angeben dafür aber einen längeren Zeitraum in der Zukunft abdecken. Am 06.02.2106 um 06:28:15 MEZ (Zone 0) ist jedoch Schluss!

Einige Fragen sich vielleicht, warum bei der wday() ein Zeitraum von 1900 bis 2099 möglich ist und nicht von 1970 bis 2106. Das liegt an der Rechenmethode die dort angewendet wird. Diese rechnet mit dem Jahrhundert und dem Jahr im Jahrhundert. Ich habe dabei zwei Jahrhunderte berücksichtigt: das 20te Jahrhundert (1900 bis 1999) und das 21te Jahrhundert (2000 bis 2099). Wer möchte, kann jedoch noch weitere Jahrhunderte Hinzufügen. Die Merkziffern für NJh erhaltet ihr bei Wikipedia.

Lizenzbestimmungen

Wie alles in der Welt unterliegt dieser Quellcode einem Copyright. Da ich diese Informationen aber gerne teilen möchte lege ich keinen großen Wert darauf hier strenge Regeln anzuwenden. Aber bitte beachtet folgende Regeln:

  • Ihr dürft den Quellcode frei verwenden, verändern (bis auf den ersten Kommentarblock) und verbreiten. Auch eure veränderte Version dürft ihr verbreiten und euch gerne im Copyright verewigen.
  • In jeder Datei befindet sich am Anfang ein Auskommentierter Block. Dieser darf nicht entfernt werden.
  • Eine Kompilierte Datei die diese Bibliothek verwendet darf nur verbreitet werden, wenn deutlich erkennbar, das Copyright von mir angegeben ist.

Beispiel:

Ihr präsentiert auf eurer Homepage euer Projekt, wollt den Quellcode aber nicht offenlegen und bietet nur das Hexfile zum Download an. Dann möchte ich, dass ihr mich bzw. meine Homepage in eurem Projekt erwähnt.

Ich denke, dass ist human und damit kann man durchaus leben 😉

Downloads

Datetime – Version 1.0

Schreib einen Kommentar