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.
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.
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.
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.
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: