Zeitbasis / RTC

Einleitung:

Oftmals sieht man Projekte, wo ein externer RTC-Baustein angeschlossen ist, ohne dessen Vorteil zu nutzen.
Der einzige Vorteil eines externen RTC ist nämlich nur, daß man die Zeitzählung während eines Ausfalls der Hauptstromversorgung über eine kleine Stützbatterie bei geringem Strombedarf fortsetzen kann.

In allen anderen Fällen ist zusätzliche Hardware völlig unnötig und man kann eine präzise Zeitbasis bequem mit dem Hauptquarz des Mikrokontrollers programmieren.
Daraus ergeben sich sogar noch zusätzliche Vorteile, wie eine geringere Temperaturabhängigkeit und höhere Güte, die Quarze im MHz-Bereich gegenüber Quarzen im kHz-Bereich besitzen.
Ich möchte hiermit die Angst vor dem bischen Mathematik nehmen, welches zur Berechnung der Teilerfaktoren benötigt wird.

Lösung:

Speziell für den AVR kommen Quarze im Bereich 1MHz...16MHz zum Einsatz. Diese Zahlen lassen sich jedoch nicht in 16 Bit ausdrücken und somit ist ein direktes Setzen des Compare-Wertes des Timers T1 nicht möglich.
Deshalb unterteilt man die Quarzfrequenz in 2 Faktoren, der 1. bestimmt die Periode des Timers T1 und der 2. den Reloadwert eines Registers, welches im Timerinterrupt runtergezählt wird.

Beispiel:

Als Beispiel wird 11,0592MHz gewählt, welches ein übliches Baudratenquarz ist, d.h. damit können die UART Standardbautraten erzeugt werden.
Der Softwareteiler wird mit 256 gewählt und kann somit nur mit einem einzigen Byte realisiert werden. Für größere Werte muß der Softwareteiler als int (2 Byte) deklariert werden. Bei kleineren Werten als 256 muß man beachten, daß der 2.Faktor immer noch in 2 Byte paßt, um als Comparewert für T1 verwendet zu werden.

Mit 256Hz ergibt sich eine Timerinterruptzeit von 4ms, die auch sehr gut zum Entprellen von Tasten benutzt werden kann. Eine Entprellroutine kann also bequem in den Timerinterrupt mit eingefügt werden.

Beachten muß man nur noch, daß das Nullsetzen des Timers erst einen Zyklus nach dem Comparematch erfolgt. D.h der Comparewert ist der gewünschte Teilerfaktor - 1.

Berechnung:

Die Berechnung des Comparewertes ist also sehr einfach:

OCR1A = 11059200 / 256 - 1 = 43199, Rest 0.

Da haben wir ja noch mal Glück gehabt, es gibt keinen Rest bei der Division und die Sekunde ist exakt 256 * 43200 = 11059200 Zyklen lang.

Nun habe ich die Uhr einen Tag laufen lassen und festgestellt, daß sie 1,5s nach geht.

D.h. die Quarzfrequenz beträgt in Wirklichkeit:

11059200 * (1 - 1,5 / 24 / 60 / 60) = 11059008 Hz.

Also die ganze Rechnung nochmal:

OCR1A = 11059008 / 256 - 1 = 43198, Rest 64.

Nun habe wir einen Rest und es würden uns jede Sekunde 64 Zyklen fehlen. Das geht natürlich nicht.

Deshalb wird jedensmal, wenn der Softwareteiler Null ist und die Sekunde weitergezählt wird, ein anderer Comparewert geladen. Dieser ist dann um den Rest größer. Und beim nächsten Timerinterrupt wird dann wieder der Comparewert geladen, der das Ergebnis der Division war.

Es ergeben sich somit:

255 * (43198 + 1) + 1 * (43198 + 64  + 1) = 11059008 Zyklen, exakt, wie wir es wollten.

Das Programm:

Nachfolgend nun das C-Programm. Da wir ja alle nicht gerne rechnen, lassen wir das einfach den C-Compiler erledigen. D.h. wir brauchen nur noch per Definition für XTAL den entsprechenden Wert eintragen und der Compiler rechnet alle nötigen Konstanten ganz alleine aus.

So ein Compiler ist auch ziemlich faul, der merkt sofort, wenn die Operanden für eine Berechnung alles Konstanten sind. Und ehe er sich damit abquält, extra Code für diese Berechnungen zu erzeugen, rechnet er es lieber selber aus und fügt das Ergebnis direkt in den Code ein.

Der Assembler kann auch 32-Bit Konstanten-Berechnungen ausführen. Allerdings muß man dann die entsprechenden Preprozessoroperationen benutzen. Man könnte auch eine Divisionsroutine aufrufen, aber dann würde ja echter Code erzeugt.

Nachfolgend nun die Beispiele in C und Assembler: