|
|
AVR - Die genaue Sekunde / RTC
[Bearbeiten] EinleitungOftmals sieht man Projekte, bei denen ein externer RTC-Baustein (engl. Real Time Clock, Echtzeituhr; z. B. PCF8583 mit I2C-Anschluss Datenblatt (PDF)) angeschlossen ist, ohne dessen Vorteil wirklich zu nutzen. Die wichtigsten Vorteile einer externen RTC sind
In allen anderen Fällen ist die zusätzliche Hardware nicht unbedingt nö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 bisschen Mathematik nehmen, welche zur Berechnung der Teilerfaktoren benötigt wird. [Bearbeiten] LösungSpeziell für den AVR kommen Quarze im Bereich 1MHz bis 16MHz zum Einsatz, d.h. in einer Sekunde werden 1.000.000 bis 16.000.000 Zyklen durchlaufen (Zyklen pro Sekunde = Frequenz). Möchte man eine Sekunde messen, kann man somit die entsprechende Anzahl Zyklen von Null an hochzählen oder von dem entsprechenden Wert bis auf 0 runterzählen. Für das Zählen bietet sich im µC der Compare-Modus eines Timers an. Die obigen Zahlen lassen sich jedoch nicht in einer 16 Bit Variablen ausdrücken da sie zu gross sind und somit ist ein direktes Setzen des Compare-Wertes des Timers T1 im AVR nicht möglich. Deshalb unterteilt man die Quarzfrequenz in zwei Faktoren; der erste (Softwareteiler) bestimmt die Periode des Timers T1 und der zweite den Reloadwert eines Registers, welches im Timerinterrupt runtergezählt wird. [Bearbeiten] BeispielIm Beispiel AVR wird ein 11,0592-MHz-Quarz verwendet, was einem üblichen Baudratenquarz entspricht, d.h. damit können die UART-Standardbaudraten erzeugt werden. Der Softwareteiler wird mit 256 gewählt, d.h. 256 Timerinterrupts pro Sekunde, und kann somit mit nur einem einzigen Byte realisiert werden. Für größere Werte muß der Softwareteiler als int (2 Byte) deklariert werden. Bei kleineren Werten als 256 muss man beachten, dass der zweite Faktor immer noch in 2 Byte passt, um als Comparewert für den 16-Bit-Timer 1 verwendet werden zu können. Die Timerroutine wird in einer Sekunde Softwareteiler-Mal aufgerufen. Mit diesen 256 Hz ergibt sich eine Timerinterruptzeit von ca. 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 muss man nur noch, dass das Nullsetzen des Timers erst einen Zyklus nach dem Comparematch erfolgt. [Bearbeiten] BerechnungDie Berechnung des Comparewertes ist sehr einfach:
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. Dazu wird eine Uhr programmiert und mit dem theoretischen Wert laufen gelassen. Nun habe ich die Uhr einen Tag laufen lassen und festgestellt, dass sie 1,5 s nach geht (Dazu kann man beispielsweise die Tagesschau nutzen, welche jeden Tag um 20:00 (+einer konstanten Zeitverschiebung durch das Übertragungsmedium SAT,DVB-T, ...) beginnt und sogar eine Uhr dabei einblendet). D.h. die Quarzfrequenz beträgt in Wirklichkeit:
Ausführliche Rechnung:
Also die ganze Rechnung nochmal:
Nun haben wir einen Rest und es würden uns jede Sekunde 64 Zyklen fehlen. Das geht natürlich nicht. Deshalb wird jedesmal, 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:
Exakt so, wie wir es wollten. [Bearbeiten] Das ProgrammNachfolgend 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 Präprozessoroperationen benutzen. Man könnte auch eine Divisionsroutine aufrufen, aber dann würde ja echter Code erzeugt. [Bearbeiten] Beispiel in CWichtig: Ab Ver.1.4.0 haben sich Namen/Funktionen geändert: "There are currently two different styles present for naming the vectors. One form uses names starting with SIG_, followed by a relatively verbose but arbitrarily chosen name describing the interrupt vector. This has been the only available style in avr-libc up to version 1.2.x. Starting with avr-libc version 1.4.0, a second style of interrupt vector names has been added, where a short phrase for the vector description is followed by _vect. The short phrase matches the vector name as described in the datasheet of the respective device (and in Atmel's XML files), with spaces replaced by an underscore and other non-alphanumeric characters dropped. Using the suffix _vect is intented to improve portability to other C compilers available for the AVR that use a similar naming convention...." Quelle: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
[Bearbeiten] Beispiel in Assembler
[Bearbeiten] Verbesserte Version mit durchlaufendem HardwarezählerHier sind trotzdem kleine Verbesserungsvorschläge. Wird im Timer die Option "Clear On Compare Match" verwendet, so verliert man den Overflow Interrupt. Oder möchte man nebenher noch eine Zeit mit dem Capture Interrupt messen, so benötigt man einen durchlaufenden Timer. Um dies zu erreichen, wird OCR1A nicht fest eingestellt, sondern bei jedem Aufruf um den gleichen Wert erhöht. In Peters Code wurde der Rest auf einmal abgearbeitet. Damit erspart man sich bei jedem Interrupt einen Vergleich und die Verarbeitungszeit verkürzt sich in seinem Code. Der Unterschied zwischen kurzem und langem Interrupt ist hier der Rest. Bei den geänderten Codeschnipseln wird der Rest gleichmäßiger abgearbeitet. Der Unterschied zwischen kurzem und langem Interrupt beträgt 1 Takt. ungetesteter Code für den durchlaufenden Timer:
[Bearbeiten] Durchlaufender Hardwarezähler und fortlaufende AdditionGänzlich ohne Hin- und Hertogglen eines Bits kommt man aus, wenn irgendein Timer mit einer Überlauf-Interruptfrequenz > 1Hz arbeitet. Dann kann die ISR eine 32-bit-Konstante auf einen 32-bit-Akkumulator addieren; bei Überlauf ist eine Sekunde vergangen. Zugegeben, es gibt "lange" und "kurze" Sekunden, aber der Fehler summiert sich nicht, und der Code ist sehr einfach.
Es gibt ein Programm gettick(), das einen Wert holt. Dieser Wert wird von Zeit zu Zeit von der Hardware (Beispiel AVR: der Timer) oder dem Betriebssystem (Beispiel Linux: gettick ruft getmsec, getmsec ruft gettimeofday) Dieser aufgerufene Wert sollte pro Sekunde um TICKPERSEC erhöht werden, was aber mit einer gewissen Ungenauigkeit geschieht. Die Laufzeitkorrektur wird dadurch ausgeführt, dass der von gettick() zurückgegebene Wert mit einem Faktor TFAK multipliziert wird. Der Faktor sollte so gewählt werden, dass:
Immer wenn tsdaytim_calibrated() erkennt, dass eine Sekunde vergangen, wird last_value um die Zahl onesecond erhöht. Der Zahlenwert onsecond wird mit TICKPERSEC*TFAK initalisiert und kann angezeigt oder neu eingegeben werden (Die Bedienung der seriellen Schnittstelle hierfür ist in diesem Beispiel nicht enthalten). Wenn festgestellt wird, dass die Uhr pro Zeit (P) um (E) zu schnell geht, dann kann der Wert onsecond korrigiert werden. c = d * (P+E)/P Beispiel Pro 7 Tage 120 Sekunden zu viel: 7 Tage = 7*86400= 604800 sekunden c = 992000000 * (604800+120)/604800=991803175 Dann wird die Uhr genauer gehen.
[Bearbeiten] Echtzeituhr mit Uhrenquarz[Bearbeiten] RTC mit wenig StromverbrauchDas vorgestellte Verfahren ist leider nicht direkt anwendbar, wenn man eine RTC mit einem 32,768 KHz Uhrenquarz realisieren möchte. Dieser Weg ist dann notwendig, wenn der Controller batteriebetrieben sehr lange laufen soll und durch Verwendung des Sleep Mode Strom gespart wird. [Bearbeiten] Begrenzung der AuflösungWo liegt das Problem? Das obige Verfahren nutzt recht hochfrequente Quarze mit 1 MHz und mehr. Wenn man die Frequenz eines Quarzes von 1 MHz = 1.000.000 Hz mit einer Auflösung von 1 Hz angibt, entspricht das einem maximalen Fehler von 1/1.000.000 oder 1ppm (engl. parts per million, millionstel Teil). Das ist sehr wenig. Eine RTC mit einem Fehler von 1ppm hat eine Gangabweichung von 86,4ms pro Tag, oder 2,5s pro Monat. Das ist ein sehr guter Wert. Wenn ich nun aber die Frequenz 32768 Hz mit 1 Hz Auflösung angebe, ist das schlimmstenfalls ein Fehler von 1/32768 = 30,5ppm, was einer Abweichung von 2,5s pro Tag und 75s pro Monat entspricht! [Bearbeiten] Uhrentakt genau messen und digital korrigierenWie kann man das Problem lösen? Die AVRs können im Sleep Mode den Uhrenquarz nur am Timer 2 betreiben, welcher ein 8 Bit Timer ist. D.H. ein Überlauf passiert alle 256 Takte, sprich 7,8125ms (=128 Hz). Man kann auch einen Prescaler verwenden, welcher das Problem aber nicht löst. Wie bereits festgestellt, müssen wir die Frequenz des 32768Hz Uhrenquarzes genauer messen und darstellen. Wir wollen hier annehmen, dass wir die Frequenz auf 0,001Hz = 1mHz (Millihertz) auflösen, ein guter Frequenzzähler kann das problemlos und ist auch so genau. Wir können beispielsweise feststellen, dass ein Quarz mit 32768,423Hz schwingt, das entspricht einem Fehler von
Dabei ist zu beachten, daß diese Messung nicht direkt am Quarz erfolgen darf, auch nicht mit kapazitätsarmen 10:1 Tastköpfen! Denn so ein Quarz wird von kleinsten Kapazitäten im Bereich von weniger als ein pF messbar verstimmt. Darum muss man in so einem Fall den Uhrentakt auf ein anderes IO-Pin ausgeben und dort messen. Beim MSP430 ist das einfach, man kann ACLK auf ein Pin direkt ausgeben. Der AVR kann das leider nicht. Hier muss man zu einem Trick greifen. Man benutzt die Output Compare Funktion, um mit Timer 2 einen 128 Hz Takt zu erzeugen. Dieser ist fest mit dem Uhrentakt verbunden und kann anstellte dessen gemessen werden, ohne den Quarz zu verstimmmen. Die Messung ergibt in diesem Fall eine Frequenz von 128,00165 Hz. OK, jetzt haben wir den Takt gemessen, wir wissen, daß pro Sekunde 128 Timerüberläufe passieren (Prescaler =1). Wir wissen, dass unser Quarz pro Sekunde um 0,423 Takte zu schnell ist. Wir können aber keine halben Takte mehr oder weniger zählen? Doch! Mit Festkommaarithmetik! Denn nach 1000s ist unser Quarz um 423 Takte zu weit gelaufen und muss um diese Anzahl zurückgestellt werden. Also könnte man im ersten Ansatz nach 1000 Sekunden den Timer 2 um diesen Betrag korrigieren. Das Problem dabei ist nur, dass der Zähler nur 8 Bit breit ist. Ein Addieren oder Subtrahieren von 423 würde mehrfache Überläufe verursachen, welche programmtechnisch nur schwer zu handhaben wären. Also muss eine etwas bessere Methode her. [Bearbeiten] Bresenham für RTCsWenn man sich die Idee der Festkommaarithmetik mal eine Weile durch den Kopf gehen lässt, kommt man vielleicht auf folgende Idee. Der Frequenzfehler beträgt in unserem Beispiel 0,423 Hz, also Takte pro Sekunde. Oder aber 423 Tausendstel Takte pro Sekunde. Nach drei Sekunden sind es schon 1,269 Takte oder 1269 Tausendstel Takte. Moment! Einen Takt können wir korrigieren, indem wir den Zähler um 1 Takt zurück setzen. Den Restfehler von 269 Tausendstel merken wir uns und akkumulieren weiter. Wenn dann wieder die 1000er Marke überschritten wird machen wir das Gleiche. Analog dazu natürlich auch bei negativem Vorzeichen, sprich wenn der Quarz zu langsam schwingt wird er um einen Takt vorgestellt. É Voilà! Dieses Verfahren ist sehr ähnlich zum Bresenham-Algorithmus zum Zeichnen von Linien auf Computermonitoren. [Bearbeiten] BeispielprogrammDas folgende Beispiel stellt eine RTC mit sehr niedrigem Stromverbrauch zur Verfügung. Laut Datenblatt beträgt die Stromaufnahme nur ca. 6µA bei 3V Versorgungsspannung, besser als einige kommerzielle RTCs! Möglich macht das die Nutzung des Power Save Sleep Modes. Die Frequenz kann auf 10 − 5 Hz genau abgeglichen werden, was in diesem Fall bei einer Messfrequenz von 128Hz einer Auflösung von 0,078ppm entspricht. Das bedeutet eine Abweichung von 6,7ms pro Tag, 0,2s pro Monat oder 2,46s pro Jahr! Die Abweichung des Quarzes wird in einer vorzeichenbehafteten 16 Bit Variable in der Einheit 10 − 3 Hz gespeichert. D.h. es kann ein Frequenzfehler von +/- 32,768 Hz korrigiert werden, das sind 1000ppm! Normale Quarze haben Abweichungen von max. 100ppm, meist viel weniger. Jede Sekunde wird der Frequenzfehler neu berechnet und in den darauffolgenden 128 Interrupts ggf. mehrfach korrigiert. Wichtig ist dabei, dass die Korrektur wirklich ganz am Anfang der ISR steht, weil hier das Timing wirklich kritisch ist. Denn innerhalb eines Uhrenquarztaktes von ~30µs muss der Timer 2 neu beschrieben werden. Bei 1 MHz RC-Oszillatortakt sind das nur 30 Takte, wovon 6 zum Aufwachen und 4 zum Anspringen der ISR benötigt werden. Dazu kommen noch das Sichern einiger Register am Anfang der ISR, was man im C nicht direkt sieht. Sicherheitshalber sollte man auch einen höheren Takt einstellen, z. B. 2 MHz, dann ist das Timing wesentlich entspannter.
[Bearbeiten] Weitere VerbesserungenDer Algorithmus ermöglicht eine sehr hochauflösende Kalibrierung der RTC. Allerdings heisst das leider noch lange nicht, daß die RTC dann auch wirklich so genau ist. Denn diese Kalibrierung gilt nur für eine exakte Temperatur! Auch ein Quarz hat eine Temperaturabhängigkeit der Schwingfrequenz. Wenn man nun eine sehr genaue RTC bauen möchte, welche über einen grossen Temperaturbereich genau läuft, muss man periodisch die Temperatur messen und in die Kalibrierung einbeziehen. Einen typischen Temperaturverlauf des Frequenzfehlers zeigt das folgende Bild.
Weiterhin kann man natürlich die Kalibrierung ohne erneutes Kompilieren des Quelltextets vornehmen, indem man den Kalibrierwert per UART oder I2C an den AVR sendet und im EEPROM speichert. Kernpunkt der Kalibrierung ist natürlich die Messung der realen Quarzfrequenz. Die oben beschriebene Methode per Tagesschau und 1 Tag warten ist sehr zeitaufwändig und hat eine begrenzte Genauigkeit von ca. 1/2 Sekunde, das entspricht 5,8ppm. Hier muss zwangsläufig ein guter Frequenzzähler her. Da dieser nicht jedem Hobbybastler zur Verfügung steht, muss man sich in Lehrwerkstätten, Universitäten oder Firmen im Elektronikbereich umsehen und nachfragen, ob man einen Frequenzzähler vor Ort kurze Zeit nutzen kann. [Bearbeiten] Links |