mir ist schon oft in der Verwendung mit der "Time" Library aufgefallen das da irgendwo schon mal nen Sekündchen verlorengeht bei nicht allzulanger Laufzeit oder so 40 Minuten.. Jetzt wollte ich eine Millisekunden-Stopuhr bauen und irgendwie stimmte alles hinten und vorne nicht.
Man kann jetzt natürlich bei nem 328P Timer2 für seine eigenen Millisekunden nutzen.. wieso so umständlich wenn schon ein Timer läuft? Meine Idee wäre jetzt Timer0 von ausserhalb kalibrierbar im Core umzuschreiben. Kennt jemand schon so ein Projekt oder ein Workaround?
Also schnell mal nen Versuchsaufbau mit nem Mega2560 Board, ds3231 und GPS PPS gestartet.
Die Messung lief über getriggerte Timer.
Dabei musste ich feststellen das der Quarz auf dem Board gerademal 15,958.000 Mhz schwingt und der Arduino in einer echten Sekunde wechselnd zwischen 997ms und 999ms zählt. Beim großen C nachgeschaut kann ein Blindlinks gekaufter 16.000 Quarz auch mal Tolerance:30%,30ppm im Datenblatt stehen haben, also im endeffekt ist es garnicht so selten.
Andersum wenn ich eben mal in F_CPU in der Boards.txt den gemessenen Wert Eingebe dann laufen 1060ms in einer echten sekunde.. also völlig falsche Precisionsrechnung weil keine 6 Nullen am Ende sind.
Die Standard Arduinos haben gar keinen Quarz, sondern einen Keramik-Resonator. Der Quarz ist für den USB/TTL Wandler. Es gibt aber auch Arduino-kompatible Boards mit Quarzen für den eigentlichen Prozessor.
Wenn es auf längere Zeit genau werden soll nimmt man eine RTC. Für sehr kurze Zeiten ist das aber eher nichts.
Serenifly:
Wenn es auf längere Zeit genau werden soll nimmt man eine RTC. Für sehr kurze Zeiten ist das aber eher nichts.
Die Fernost-Nanos, die ich mal hatte, dürften alle aus einer Charge gestammt haben, und die liefen schon nach einer Minute nicht mehr synchron. Und zwar deutlich.
Die haben auch Schaltregler drauf. Wenn das endlich mal die Standard Arduinos hätten gäbe es nicht ganz so viele Threads wegen problematischer Stromversorgung.
Der Arduino UNO oder MEGA2560 haben keinen Quarz sonedern einen Resonator. Dieser ist weniger prezise (max Fehler zwischen 0,1% und 0,5%) Das ist um den Faktor 50 -100 schlechter als ein Quarz.
Der Quarz ist wie gesagt für den USB/TTL Wandler. Der sitzt auch neben dem 16U2 Prozessor. Nicht am Atmega328. An dem siehst die etwa mittig auch den Resonator.
Achso okay, war jetzt nur ein Begriffverständnis.. der unterschied ist mir schon klar.
Trotzdem kann ich auch mit dem Resonator solang die Abweichung Linear ist und dieser sich in 20ppm hält einen Timer2 aufsetzen der genaue millis zählt.
Serenifly:
Der Quarz ist wie gesagt für den USB/TTL Wandler. Der sitzt auch neben dem 16U2 Prozessor. Nicht am Atmega328. An dem siehst die etwa mittig auch den Resonator.
Ahja, ich habe den mega2560 noch nie benutzt, nur jetzt erst richtig wegen den 3 Timern zur Zeitmessung.. ist mir auch gerade aufgefallen.. Kann ich allerdings selbst tauschen wie es aussieht, das ist nen SMD auf dem Platz vom HC-49/US-SMD.
Meine restlichen sind mit Quarz ,hab ich jetzt noch garnicht ausprobiert.
Abgleich mit Trimmkondensator - falls das auch beim Keramikresonator funktioniert
RTC - dürfte für Stoppuhr eher ungeeignet sein
Uhrenquarz (32kHz) am Timer 2 - noch mehr Änderungen
Kalibrieren - erfordert Eingriff in die Standard-Bibliothek
Kalibrierbare Uhr mit Timer 1 oder 2 - nach Strickmuster der Standard-Uhr
Abgleich mit Trimmkondensator - falls das auch beim Keramikresonator funktioniert
Tja gute Frage, habe welche für Uhrenquarze da.
RTC - dürfte für Stoppuhr eher ungeeignet sein
Einige liefern ein Langzeitstabilisiertes 32,768 TCX0 Signal.
Uhrenquarz (32kHz) am Timer 2 - noch mehr Änderungen.
War auch meine Idee, nur mit dem RTC Signal.
Kalibrieren - erfordert Eingriff in die Standard-Bibliothek
Kalibrierbare Uhr mit Timer 1 oder 2 - nach Strickmuster der Standard-Uhr
Man erfindet Quasi die hälfte von Timer0 neu.
Ich habe beim Arduino Micro den 16er Quarz gemessen und der ist schon auf die letzten 4 Stellen genau.. ich selbst benutze ja nur die genauen auf selbst geätzten Boards.. das Keramikresoantor Problem ist mir halt nur aufgefallen für andere die das Projekt nachbauen wollen und nen Board kaufen müssen. Es gibt ja auch zb den Nano mit nem normalen Quarz.. aber das ist dann das teure Original.
Kalibrierbare Uhr mit Timer 1 oder 2 - nach Strickmuster der Standard-Uhr
Uhren für Timer 1+2 kann ich dir geben.
Habe ich mal als Ersatz für millis() gebaut. Wenn Timer0 andere Aufgabe erledigen soll.
Auch kann die originale Zeitabhandlung keine Ereignisse feuern.
Schuppeste:
Kalibrierbare Uhr mit Timer 1 oder 2 - nach Strickmuster der Standard-Uhr
Man erfindet Quasi die hälfte von Timer0 neu.
Das ist nicht viel, und neu erfinden muß man da garnichts. Es reicht, wenn man beim Abschreiben die Konstanten FRACT_INC und MILLIS_INC durch Variablen ersetzt, die z.B. aus dem EEPROM geladen werden können.
MILLIS_INC gibt an, wieviele volle Millisekunden pro Timer-Überlauf vergangen sind.
FRACT_INC gibt an, wieviele Mikrosekunden zusätzlich vergangen sind. Wenn sich dieser Anteil auf über 1ms summiert hat, wird die aktuelle Zeit nochmal um 1ms erhöht.
Diese beiden Werten müssen bei der Kalibrierung bestimmt werden, die muß man natürlich neu erfinden
Alternativ kann man T1 (16 Bit) so hintrimmen, daß er genau nach 1ms zurückgesetzt wird, und dann einfach diese Überläufe als Millisekunden mitzählen. Damit wird aber (beim 328) der einzige 16 Bit Timer belegt, was zu Problemen bei Bibliotheken führen kann, die nur mit diesem Timer funktionieren. Bei den 8 Bit Timern (T0,T1) kommt man nicht exakt auf 1ms, dann muß obiges Schema verwendet werden. Man könnte mal nachschauen, ob sowas in der Timer1Lib bereits implementiert ist...
Gerade sehe ich, daß combie sowas schon programmiert hat, dann reduziert sich der Aufwand auf das Hinzufügen der Kalibrierung
DrDiettrich:
Das ist nicht viel, und neu erfinden muß man da garnichts. Es reicht, wenn man beim Abschreiben die Konstanten FRACT_INC und MILLIS_INC durch Variablen ersetzt, die z.B. aus dem EEPROM geladen werden können.
MILLIS_INC gibt an, wieviele volle Millisekunden pro Timer-Überlauf vergangen sind.
FRACT_INC gibt an, wieviele Mikrosekunden zusätzlich vergangen sind. Wenn sich dieser Anteil auf über 1ms summiert hat, wird die aktuelle Zeit nochmal um 1ms erhöht.
Diese beiden Werten müssen bei der Kalibrierung bestimmt werden, die muß man natürlich neu erfinden
Ja an den Fract Variablen war ich auch schon dran, das wäre natürlich eine Idee.. bzw es nützt ja nichts diese jedesmal beim boot neu zu berechenn solang man das programm nicht wieder neu hochlädt oder Fuses verändert. Im Core wird das ja anscheinend trotzdem so gelöst.. da könnte man dann wie vorgeschlagen schon ansetzen.
Für eine Berechnung der Faktoren braucht man ein exaktes Referenzsignal, z.B. von der RTC. Die ist aber nicht immer vorhanden. Dann kann man die Werte einmal ermitteln und dauerhaft im EEPROM ablegen, und sie dort beim Booten auslesen.
Im Core wird einfach die Frequenz genommen, die für das Board vordefiniert ist (16MHz), und daraus die Faktoren bestimmt. Damit läuft die Uhr eben so genau wie der Oszillator.
Ich habe mir eine Timer Programmierung mit einer Schaltung von 30ns Sekunden langzeitsynchroniertem PPS Impulses eines GPS und 32,xxxKHZ eines 2ppm RTC Signals aufgebaut.
Die RTC Taktet ab und zu mal in 32.767, also nur einen daneben.. das kann man glaube ich vernachlässigen wenn man Stunden in ms messen will.. da werden einige Lösungen ohne viel Investition weiter daneben liegen.
combie:
Die Kalibrierung müsste man da noch rein kleben.
Jepp Danke, hab ich mir mal angeschaut.
Ich habe das ganze jetzt anders gelöst, falls mal jemand über das gleiche Problem stolpert...
Grundproblem:
Fast alle Timerlibraries benutzen "Millis", da ist leider nix mehr zu machen da diese Funktion und "Micros" für viele Timing Geschichten verwendet werden aber auf Langzeit ungenau sind.
Der einzige 8bit Timer der einen eigenen Eingang hat, ist T0.. denn Timer2 sein Clocksource liegt ja an den Pins des Quarzes der Arduinos und ist somit blockiert.
Also habe ich einen Fork von der Arduino IDE erstellt, hier kann man Timer0 und Timer2 für das Core im Boardmenü auswählen. Wenn ich Zeit finde kommt noch ein Update der Millis() Funktion, so das diese extern vom Timer0 Triggereingang T0 mit einem DS3231,TCX0 32khz gespeist werden kann.