Hi alle,
in vielen meiner Projekte zähle ich Millisekunden um Zeiten zu messen.
Allerdings nur ungefähr, weil mir noch nicht eingefallen ist, wie ich
das elegant genauer hinbekommen soll. So mache ich es zur Zeit:
1
voidinitSystemTimer(void)
2
{
3
cli();
4
TCCR0A=(1<<WGM01);// CTC Mode
5
#if F_CPU>16000000
6
TCCR0B=(1<<CS02);// Prescaler 256
7
OCR0A=(F_CPU/256+500)/1000;
8
#elif F_CPU>2000000
9
TCCR0B=(1<<CS01)+(1<<CS00);// Prescaler 64
10
OCR0A=(F_CPU/64+500)/1000;
11
#else
12
TCCR0B=(1<<CS01);// Prescaler 8
13
OCR0A=(F_CPU/8+500)/1000;
14
#endif
15
TIMSK0|=(1<<OCIE0A);// Interrupt on compare match
16
sei();
17
}
18
19
ISR(TIMER0_COMPA_vect)
20
{
21
timerCounter++;
22
}
Wenn die Quartz-Frequenz nicht glatt durch 8.000, 64.000 oder 256.000
teilbar ist, bekomme ich aber nur ungefähr Millisekunden. Ich suche nach
einem generischen Lösungsansatz, der mit allen gängigen Quarzen
funktioniert und nicht allzu viel CPU Zeit in der ISR verbraucht.
Kennt jemand dazu eine elegante Lösung?
Stefan U. schrieb:> Kennt jemand dazu eine elegante Lösung?
Xmega nehmen und ggf. 2 16Bit-Timer über das Event-System kaskadieren.
Über die Timer-Periode kannst Du die gewünschten Teiler sehr fein
einstellen.
@ Stefan Us (stefanus)
> void initSystemTimer(void)> {> cli();
Wozu? Nach dem Reset sind die Interrupts sicher aus.
>einem generischen Lösungsansatz, der mit allen gängigen Quarzen>funktioniert und nicht allzu viel CPU Zeit in der ISR verbraucht.AVR - Die genaue Sekunde / RTC
Warum nimmst Du nicht einfach einen der 16Bit-Timer?
Z.B. der ATmega328PB hat 3 16Bit-Timer.
Exakt wird das aber auch nicht, da ja die Quarze nicht exakt 16MHz
haben, sondern z.B. 16,000001MHz.
Ist aber auch kein großes Problem, man kann die Quarzfreqeunz messen und
dann im Interrupt mit gebrochenen Zahlen eine Korrekturrechnung
durchführen. Dann sind z.B. 1000 Interrupts a 1ms auf 1Hz genau.
Du solltest also zuerst mal spezifizieren, wie genau Du es haben
möchtest.
Stefan U. schrieb:> Wenn die Quartz-Frequenz nicht glatt durch 8.000, 64.000 oder 256.000> teilbar ist, bekomme ich aber nur ungefähr Millisekunden.
Eines vorweg: mit jeder Frequenz, die nicht glatt durch 1ms teilbar ist,
wirst du nur "ungefähr Milliskeunden" bekommen...
Ich würde analog zur DDFS diesen Weg gehen:
1. irgendein hinreichend schneller Timer-Interrupt. "Hinreichend" ist im
einfachsten Fall einer, der schneller als 1ms ist und so oft aufgerufen
wird, dass der resultierende Jitter nicht mehr stört.
Im etwas komplizierteren Fall kann das aber auch ein Interrupt mit z.B.
7,65ms sein. Nur wird dann die ms-Uhr immer mal einige ms
"überspringen"...
2. z.B. ein 8 oder 16 Bit Akkumulator, bei dessen Überlauf das
Millisekunden-Register um 1ms hochgezählt wird.
Und wenn jetzt z.B. der Timerinterrupt alle 780us aufgerufen wird, dann
addiere ich addiere ich pro Interrupt 780us/1000us * 65536 = 51118 auf
den 16-Bit Akkumulator drauf. Und wie gesagt: wenn der überläuft ist
wieder eine ms vergangen...
Diese Vorgehensweise ist aber in dem von Falk verlinkten Artikel auch
mit drin... ;-)
HI, Stefan,
> in vielen meiner Projekte zähle ich Millisekunden um Zeiten zu messen.> Allerdings nur ungefähr, weil mir noch nicht eingefallen ist, wie ich> das elegant genauer hinbekommen soll.
Sobald das Target per JTAG geflasht werden kann, kann dessen
Quarzfrequenz gemessen - und per JTAG in das EEPROM geschrieben werden.
Ciao
Wolfgang Horn
U. C. schrieb:> Wie wäre es mit einer RTC mit 1ms SQ Ausgang...?> Oder einem UhrenQuarz direkt am ATMega(wenn möglich)?
Das ist doch viel zu einfach! 6, setzen!
Peter D. schrieb:> Und der wäre?>> Der bekannte PCF8563 kann ja nur 0,98ms (1/1024Hz).DS3231 hat 8.192KHz, 4.096KHz, 1.024KHz und 1Hz output.
1KHz kann meines Wissens keiner.
Ich merke gerade, dass ich ein wichtiges Detail in meiner Frage
vergessen habe:
Es ist mir nicht wichtig, dass die Intervalle alle exakt 1ms lang
dauern, aber im Durchschnitt sollte man auf 1ms kommen, damit sich die
Fehler nicht aufaddieren.
Eine Uhr soll es nicht werden, dazu hätte ich einfach Quarz + Prescaler
so kombiniert, dass ich auf glatte Sekunden komme.
Der Artikel
https://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC
hat einen schönen Ansatz, wie man auf genaue Sekunden kommen kann.
Allerdings habe ich hier den hässlichen Seiteneffekt, dass jede Sekunde
ein 1ms Intervall stark verzerrt wird (eben um die Abweichung
auszugleichen). Außerdem geht das nur, wenn die Abweichung pro Sekunde
<1ms sind, was ich nur mit einem 16bit Counter machen könnte. Ich suche
aber nach einer Lösung, die mit 8bit Counter geht, weil ich sie auch auf
den ganz kleinen ATtinys anwenden möchte.
Lieber wäre mir, wenn jedes einzelne Intervall nur so viel verzerrt
(nennt man das Jitter?) wird, wie unbedingt nötig. In etwa so, wie der
NTP Daemon unter Linux die Uhr "gerade zieht". Der macht auch keine
Zeitsprünge, sondern korrigiert die Uhr in ganz kleinen Intervallen, so
dass es nicht auffällt.
Nicht vergessen: Es geht gar nicht um eine Uhr. Es geht mir nur darum,
dass nicht jede millisekunde etwas länger dauert, als sie soll und sich
so die Fehler aufaddieren.
Ich stelle mir vor, dass ich in der ISR ab und zu einen Korrekturwert in
das Zählregister lade. Aber da fürchte ich ein Problem: Die Berechnung,
wann das Intervall einen Takt mehr oder weniger lang sein soll, dauert
womöglich schon länger, als dieser Takt. Bzw. sie reduziert die
verbleibende Rechenleistung so sehr, dass das Zählen von Millisekudnden
sinnlos wird.
Ich habe keinen Plan, wie ich das anstellen kann.
Zum Glück war das in meinen bisherigen Anwendungen immer Scheißegal. Die
ungefähren Millisekunden waren gut genug. Aber ich hätte es gerne einen
Lösungsansatz parat, für den Fall dass es mal genauer sein soll.
> 1KHz kann meines Wissens keiner.
Nicht? ich dachte jeder PC hat sowohl unter Linux als auch unter Windows
einen 1kHz System Zähler. Genau den will ich nachbilden.
Stefan U. schrieb:> Allerdings nur ungefähr, weil mir noch nicht eingefallen ist, wie ich> das elegant genauer hinbekommen soll.
Genau (im Mittel) wirst du es nur hinbekommen, wenn du ggf. etwas Jitter
in Kauf nimmst. Der Bresenham-Algorithmus aus der Computergraphik
erlaubt jede Geradensteigung. Entsprechend läßt sich auch aus jedem
Prozessortakt ein (mittlerer) 1ms-Takt generieren.
@Stefan Us (stefanus)
><1ms sind, was ich nur mit einem 16bit Counter machen könnte. Ich suche>aber nach einer Lösung, die mit 8bit Counter geht, weil ich sie auch auf>den ganz kleinen ATtinys anwenden möchte.
Die ist auch im Artikel, nämlich die 32 kHz Variante.
>Ich habe keinen Plan, wie ich das anstellen kann.
Und scheinbar auch keine Augen im Kopf um zu lesen. Viel Geschrei um
nix, das Problem wurde schon vor Urzeiten gelöst.
@Stefan Us (stefanus)
>Nicht? ich dachte jeder PC hat sowohl unter Linux als auch unter Windows>einen 1kHz System Zähler. Genau den will ich nachbilden.
Ja und? Nimm einen passenen Quarz und gut!
Ich habe das mit der 32kHz Variante gelesen, und es gibt auch einige
andere Quarze (z.B. 8Mhz und 16Mhz) wo die Rechnung glatt auf geht.
Schön wäre es, auch eine Lösung für andere (beliebige) Taktfrequenzen
parat zu haben.
Aber wir müssen daraus keine Wissenschaft machen. Wenn das nicht "mal
eben so" geht, dann geht es eben nicht. Das würde ich akzeptieren.
@Stefan Us (stefanus)
>Ich habe das mit der 32kHz Variante gelesen,
Aber nicht verstanden?
>und es gibt auch einige>andere Quarze (z.B. 8Mhz und 16Mhz) wo die Rechnung glatt auf geht.
Nö. Wenn man mit einem BINÄREN Zähler auf ein DEZIMALE Frequenz kommen
will, muss der Quarz logischerweise ein Frequenz haben, die das PRODUKT
einer Binärzahl und Dezimalzahl ist. Das war mal in der Schule dran,
Teilbarkeit, kleinstes gemeinsames Vielfaches etc.
Für 1ms und einen 8 Bit Zähler braucht man mindestens N*1 kHz, wenn der
Zähler den CTC-Modus beherrscht (alle "neueren" AVRs). Bei alten ATmega8
& Co, die keinen CTC mit dem 8 Bit Zähler können, braucht man 1kHz * 256
* Vorteiler.
>Schön wäre es, auch eine Lösung für andere (beliebige) Taktfrequenzen>parat zu haben.
Jaja, die beliebige Beliebigkeit. Anything goes . . .
>Aber wir müssen daraus keine Wissenschaft machen.
Warum? Es gibt eine Lösung, man muss sie nur an die 1kHz anpassen.
Kneifst du davor?
>>Ich habe das mit der 32kHz Variante gelesen,>Aber nicht verstanden?
Ich habe das verstanden. Nützt mir aber nichts, wenn der Systemtakt zum
Beispiel 20Mhz ist.
>> es gibt auch einige>>andere Quarze (z.B. 8Mhz und 16Mhz) wo die Rechnung glatt auf geht.> Nö.
Doch!
8MHz mit Prescaler 64 ergibt 125kHz.
125kHz geteilt durch 125 ergibt 1kHz. Und da wollte ich hin.
> Das war mal in der Schule dran
Hmmm, das sieht man.
>> Schön wäre es, auch eine Lösung für andere (beliebige)>> Taktfrequenzen parat zu haben.> Jaja, die beliebige Beliebigkeit. Anything goes . .> Es gibt eine Lösung, man muss sie nur an die 1kHz anpassen.> Kneifst du davor?
Nein ich kneife nicht.
Ich wollte nur wissen, ob es für mein Problem einen einfachen
Lösungsansatz gibt, der für beliebige Quarze geeignet ist. Denn wenn es
den gibt, möchte ich ihn gerne kennen. Denn ich möchte praktische
Lösungen kennen lernen.
Ich wäre ein schlechter Programmierer, wenn ich nicht ab und zu mal
andere nach Lösungsansätzen fragen würde. Wenn ich nur auf die Sachen
vertrauen würde, die ich mir selbst ausgedacht habe.
Letzte Woche hat unsere Tochter vorgeführt, wie man eine
durchgeschüttelte Dose Cola ohne Sauerei schnell öffnet. Ich habe
hingegen in den vergangenen 40 Jahren immer gewartet, bis sich der Druck
von selbst abgebaut hat. Ich hätte ein bequemeres Leben gehabt, wenn ich
diese simple Lösung schon eher gekannt hätte.
Und eben solche einfachen und nützlichen Lösung möchte ich kennen
lernen, bevor ich dumm sterbe. Deswegen frage ich andere.
Und wenn es keine Lösung in meinem Sinne gibt, dann ist das auch Ok.
Hatte ich bereits geschrieben.
> Bei alten ATmega8 & Co, die keinen CTC mit dem 8 Bit Zähler> können, braucht man 1kHz x 256 x Vorteiler.
Auch falsch. Da kann man im Overflow Interrupt den Zähler manipulieren.
Betrifft übrigens IMHO nur den ATmega8, ATtiny22 und ATtiny26.
Lothar M. schrieb:> Eines vorweg: mit jeder Frequenz, die nicht glatt durch 1ms teilbar ist,> wirst du nur "ungefähr Milliskeunden" bekommen...
Das ist wohl wahr. Die böse Mathetik kennt da kein Pardon.
> Ich würde analog zur DDFS diesen Weg gehen:
[suboptimale Lösungen]
Wenn's keine ganzzahligen Lösungen unterhalb einer Millisekunde gibt,
dann benutzt man einfach den guten alten Bresenham zur Fehlerverteilung,
das ist doch trivial. Damit hat man dann neben dem sowieso
unvermeidlichen Jitter wenigstens eine langfristig exakte Frequenz.
Blöd ist allerdings, wenn der Bresenham-Zähler dazu sehr breit werden
muss. Das kostet dann erheblich Performance. Zum Glück ist das bei allen
üblichen Quarzfrequenzen nicht der Fall, i.d.R. ist die Sache also mit
einem 8Bit-Zähler abzuhandeln (was für eine optmierte Routine zur
Verwaltung dieses Zählers auf einem AVR8 genau 10 Takte Laufzeit
erfordert), nur in einigen wenigen Fällen muss man einen 16Bit-Zähler
benutzen, was allerdings auf einem AVR8 schon erheblich mehr reinhaut.
In diesen Fällen muss man dann genau überlegen, wie genau der ms-Takt
tatsächlich sein muss, also ob sich der Aufwand zu seiner (wenigsten
langfristig) exakten Gewinnung tatsächlich lohnt. Kommt man hier zu dem
Entschluss: nein, dann ist der fixed point "Phasenakku" angesagt, den du
vorgeschlagen hast. Aber eben nur dann...
Ich finde beim Stichwort Bresenham nur Beschreibungen, wie man damit
Linien zeichnet. Die Parallele zu meinem Counter sehe ich, allerdings
ist die dahinter steckende Mathematik für mich zu hoch. Das kann ich
nicht in Code umsetzen.
10 Takte schreibst du, na das ist ja in dem Rahmen, den ich erhofft
hatte.
Ich glaube ich muss in der ISR den Counter um +1, -1 oder 0 anpassen.
Und der Bresenham Algorithmus sagt mir den konkreten Wert pro Interrupt.
Richtig?
Kann ich Irgendwo von einer Implementierung abgucken? Wie gesagt
verstehe ich die Mathematische Formel nicht, ich kann nicht einmal diese
ganzen Symbole lesen.
@ Stefan Us (stefanus)
>Ich finde beim Stichwort Bresenham nur Beschreibungen, wie man damit>Linien zeichnet. Die Parallele zu meinem Counter sehe ich, allerdings>ist die dahinter steckende Mathematik für mich zu hoch. Das kann ich>nicht in Code umsetzen.
Ist doch schon fertig im Artikel, sogar in der Überschrift! Schon wieder
übersehen?
https://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC#Bresenham_f.C3.BCr_RTCs
Stefan U. schrieb:> Kennt jemand dazu eine elegante Lösung?
Elegant ist meistens das, wo am wenigsten dran rumgenörgelt wird.
Mit den üblichen Quarzfrequenzen geht es ohne Korrekturen mit einem
8-Bit-Timer bis 18,432MHz. 20MHz ist zu schnell. Kann aber 0,5 ms
erzeugen. Und darauf kann man dann ja aufbauen.
Stefan U. schrieb:> #if F_CPU>16000000> TCCR0 = (1<<CS02); // Prescaler 256> TCNT0 = 256-(F_CPU/256+500)/1000;> #elif F_CPU>2000000> TCCR0 = (1<<CS01)+(1<<CS00); // Prescaler 64> TCNT0 = 256-(F_CPU/64+500)/1000;> #else> TCCR0 = (1<<CS01); // Prescaler 8> TCNT0 = 256-(F_CPU/8+500)/1000;
Du mußt es andersrum machen:
Mit diesem Makro kannst du Timer0 auf Atmega48...328 automatisch
konfigurieren. Die exakte Millisekunde bekommst du mit allen
"Normalquarzen" bis 16MHz, die durch 1000 teilbar sind sowie mit
18,432MHz Baudratenquarz.
Ich habe das Beispiel massiv gekürzt. Deswegen so rudimentär in der
Funktion. Kann auch sein, daß irgendwo die eine oder andere Klammer
fehlt. Aber ich denke, es ist klar, wie ich das meine.
Auf diese Art und Weise konfiguriere ich alle Timer-Funktionen bei allen
Timern, der von mir verwendeten AVRs. Der erzeugte Code ist dabei immer
exakt der gleiche wie bei "händischer" Eingabe.
Meistens interessiert mich nur ein regelmässiges Interval. Das muß
selten eine Zehnerpotenz von 1s sein. Wenn ich es brauche, nehme ich
einen passenden Quarz. Normalerweise nehme ich sowieso 18,432MHz. Damit
passen nicht nur Baudraten, sondern auch Millisekunden.
Ist ein bißchen Fleißarbeit mit 97% Copy & Paste. Ausgedruckt könnte man
mit diesem Makromachwerk ein Zimmer tapezieren. Wer das Scheisse findet,
kann das gerne tun. Es ist mir egal.
Aber wenn ich ein Programm mit einem CTC-Timer von einem Atmega48 mit
8MHz auf einen Attiny4313 mit 18,432MHz oder auf einen Attiny13 mit
9,6MHz portiere, rufe ich in allen drei Fällen den Timer so auf:
Stefan U. schrieb:> Allerdings habe ich hier den hässlichen Seiteneffekt, dass jede Sekunde> ein 1ms Intervall stark verzerrt wird (eben um die Abweichung> auszugleichen).
Unsinn, du sollst die Lösung, mit der Artikel 1s macht, auf deine 1ms
anwenden, also mal 1 mehr (1.001ms) mal knapp zuwenig (0.998ms) auf den
erwarteten nächsten Timerwert draufaddieren damit im zeitlichen Mittel
1ms rauskommt.
Ja, der Algorithmus ähnelt dem Bresenham, aber der ist 2d,
eindimensional heisst er wohl DDFS.
ALLGEMEINGÜLTIG dürfte es schwierig sein, eine Formel anzugeben,
mit der du für jeden Quarztakt, bei jedem Tiny und Mega, mit
jedem Timer eine Lösung findest.
Wer sich da auf "Die genaue Sekunde" beruft, vergisst, dass da schon
einige Einschränkungen vorgegeben sind.
Der grundsätzliche Algorithmus ist schon OK. Ein bis 2 konstante
Speicherplätze für die Korrekturrechnung lassen sich auch meist
bereitstellen. Aber:
Vorteiler und Comparewert für das CTC-Verhalten eines beliebigen Timers
eines beliebigen µCs (Tinyxx, Megaxx) bei beliebiger Quarz-Frequenz
müssen ja schon mal auf die vorhandenen und praktisch nutzbaren Werte
eingeengt sein.
- Der Comparewert darf nicht zu klein sein: entweder ist er in der IRS
vielleicht schon abgelaufen, oder seine Änderung bringt viel zu großen
Jitter...
Du wirst also für jeden µC-Typ + Quarzfrequenz einige Berechnungen
vorher anstellen müssen, um die passenden Parameter als
Konstanten für den gewählten Timer an das Programm zu übergeben.
@ Jakob (Gast)
>ALLGEMEINGÜLTIG dürfte es schwierig sein, eine Formel anzugeben,>mit der du für jeden Quarztakt, bei jedem Tiny und Mega, mit>jedem Timer eine Lösung findest.
Nö. Das kringt man mit ein, zwei Formeln locker hin, man braucht keiner
Monster-Makros.
>Wer sich da auf "Die genaue Sekunde" beruft, vergisst, dass da schon>einige Einschränkungen vorgegeben sind.
Welche denn?
>Du wirst also für jeden µC-Typ + Quarzfrequenz einige Berechnungen>vorher anstellen müssen, um die passenden Parameter als>Konstanten für den gewählten Timer an das Programm zu übergeben.
Auch das kann man automatisch per Prepräzessor klaren. Der Beweis folgt
im Laufe des Tages.
Falk B. schrieb:> Nö. Das kringt man mit ein, zwei Formeln locker hin
Oh die würde ich gern sehen...oder bist du jetzt so kleinlich und sagst:
"...er hat ja nicht 'passende Lösung' gesagt..."?
Falk B. schrieb:> Auch das kann man automatisch per Prepräzessor klaren.
Ja, aber ist das nicht auch eine Berechnung? ;) Ich mach das beim UART
auch immer via Prepräzessor um zu sehen ob die Abweichung nicht zu groß
ist.
@ Michael Köhler (sylaina)
>> Nö. Das kringt man mit ein, zwei Formeln locker hin>Oh die würde ich gern sehen...oder bist du jetzt so kleinlich und sagst:>"...er hat ja nicht 'passende Lösung' gesagt..."?
Abwarten.
>> Auch das kann man automatisch per Prepräzessor klaren.>Ja, aber ist das nicht auch eine Berechnung? ;)
Ja und? Ist das mittlerweile verboten?
OMG!
Falk B. schrieb:> Ja und? Ist das mittlerweile verboten?
Nein, das ist natürlich nicht verboten. Es klang nur so, fand ich jetzt,
als ob das der Präprozessor ohne Berechnung machen würde.
@Michael
Ja du hast Recht, dass ich nicht aufmerksam gelesen habe. Ich habe mir
das nochmal angeschaut. Mal sehen, wie das bei mir passt. Ich wähle mal
willkürlich Werte, bei denen es vermutlich schwierig wird.
- Systemtakt 7,3728Mhz (UART-freundlich)
- Geteilt durch Prescaler 64 ergibt 115200 Hz
- Geteilt durch 8bit Counter 116 ergibt 993,1034482759 Interrupte pro
Sekunde
Mir ist sonnenklar, wir ich jede Sekunde einen Korrekturwert dazu
addiere, um auf exakte Sekunden zu kommen. Aber dann hat die Sekunde
weniger als 1000 Interrupte, also auch weniger als 1000 Intervalle. Das
ist weit von meinem Ziel entfernt.
Ich muss also wohl bei jedem einzelnen Interrupt einen Korrekturwert
berechnen und anwenden. Der Fehler pro Interrupt ist 0,0068965517ms.
Diese Werte addiere ich solange auf, bis über 0,0086806ms Abweichung
erreicht habe. Das entspricht einem Counter-Takt. In diesem Moment
ändere ich das Compare Register, so dass der Zähler ausnahmsweise einen
Takt weniger zählt.
Wenn wir uns die Zahlen anschauen, wird offensichtlich, dass ich dazu
eine 32 Bit Berechnung brauche. Ansonsten wird es wieder ungenauer, als
der Quarz selbst ist. Ich bezweifle, dass ich ohne großartige
Schwierigkeiten 32 Bit Berechnungen in einer ISR machen kann, die jede
ms aufgerufen wird.
Immerhin soll dieser Counter nicht die ganze Leistung des µC
verschlucken. Auch nicht die halbe Leistung.
Mir ist da noch ein Haken bewusst geworden: Wenn ich Interrupt für
länger als 1ms sperre, wird mein Timer ungenau. Im Fall des Atmega 8 (wo
ich keinen CTC verwende) wird er sogar schon ungenau, wenn ich
Interrupte für wenige Takte (ca. 60) sperre.
Das lässt sich aber kaum vermeiden, besonders bei meiner Soft-Serial
implementierung, die ganz borniert mit _delay_us() arbeitet.
Also ist mein Millisekunden Timer ohnehin ungenau und keinesfalls
geeignet, genaue Sekunden zu messen. Was mich zurück zur Frage bringt:
Was will ich eigentlich erreichen?
Ich wollte einen möglichst genauen Millisekunden Zähler, mit dem ich
Zeiten messen kann. Am Besten so genau, dass ich damit auch eine Uhr
realisieren könnte. Diese Idee kollidiert aber mit anderem primitivem
Code, der Interrupt sperrt. Außerdem kann ich Uhren einfach passende
Quarze wählen, oder einen RTC.
Kurz gesagt, mein gestecktes Ziel war wohl unrealistisch. Vielleicht
machbar, aber es würde in Overkill ausarten.
Ich denke, Jakob hat es auf den Punkt gebracht.
Ich bin mit meinem aktuellen Code, der nur ungefähre Millisekunden
zählt, nun doch zufrieden. Aus folgenden Gründen:
- Wenn ich genaue Millisekunden brauche, kann ich einen passenden Quarz
nehmen.
- In den allermeisten Fällen muss es aber gar nicht genau sein. Das war
nur so eine möchtegern Anforderung in meinem perfektionisten Hirn.
- Ein sparsamer Algorithmus, der in allen Fällen auf allen AVR's mit
Hilfe von berechnetem Jitter dafür sorgt, dass 10000 ungefähre
Millisekunden Interrupte exakt eine Sekunde ergeben ist übermäßig
aufwändig.
Ich wollte eine einfache allgemein gültige Lösung. Die scheint es nicht
zu geben.
Danke für eure Antworten, ihr habt mir geholfen, meine Entscheidung zu
treffen.
Stefan U. schrieb:> Mir ist sonnenklar, wir ich jede Sekunde einen Korrekturwert dazu> addiere, um auf exakte Sekunden zu kommen
Du hast immer noch nicht verstanden, daß nicht jede Sekunde der Fehler
korrigiert wird, sondern bei jedem 1ms Interrupt, in dem manche bis 116
und manche bis 115 laufen.
Jede Sekunde wird er nur bei 'Die genaue Sekunde' korrigiert.
@ Stefan Us (stefanus)
>- Systemtakt 7,3728Mhz (UART-freundlich)>- Geteilt durch Prescaler 64 ergibt 115200 Hz>- Geteilt durch 8bit Counter 116 ergibt 993,1034482759 Interrupte pro>Sekunde>Mir ist sonnenklar, wir ich jede Sekunde einen Korrekturwert dazu>addiere, um auf exakte Sekunden zu kommen.
Falsch! Die Korrektur erfolgt bei JEDEM Interrupt.
> Aber dann hat die Sekunde>weniger als 1000 Interrupte,
Nö, es sind 1000, wenn man es richtig macht.
>Ich muss also wohl bei jedem einzelnen Interrupt einen Korrekturwert>berechnen und anwenden.
Genau.
> Der Fehler pro Interrupt ist 0,0068965517ms.>Diese Werte addiere ich solange auf, bis über 0,0086806ms Abweichung>erreicht habe. Das entspricht einem Counter-Takt.
Richtig.
> In diesem Moment>ändere ich das Compare Register, so dass der Zähler ausnahmsweise einen>Takt weniger zählt.
Yupp!
>Wenn wir uns die Zahlen anschauen, wird offensichtlich, dass ich dazu>eine 32 Bit Berechnung brauche.
Nicht zwangsläufig.
> Ansonsten wird es wieder ungenauer, als>der Quarz selbst ist.
Nein.
> Ich bezweifle, dass ich ohne großartige>Schwierigkeiten 32 Bit Berechnungen in einer ISR machen kann, die jede>ms aufgerufen wird.
Ach herje, der arme, kleine AVR. Kann nicht mal ne 32 Bit Addition
ausführen, ohne stehen zu bleiben.
>Immerhin soll dieser Counter nicht die ganze Leistung des µC>verschlucken. Auch nicht die halbe Leistung.
Tut er nicht, auch nicht bei 32 Bit, die man aber möglicherweise auch
nicht braucht.
>Mir ist da noch ein Haken bewusst geworden: Wenn ich Interrupt für>länger als 1ms sperre, wird mein Timer ungenau.
Das macht man ja auch nicht!
> Im Fall des Atmega 8 (wo>ich keinen CTC verwende) wird er sogar schon ungenau, wenn ich>Interrupte für wenige Takte (ca. 60) sperre.
Dann machst du grundlegend was falsch.
>Das lässt sich aber kaum vermeiden, besonders bei meiner Soft-Serial>implementierung, die ganz borniert mit _delay_us() arbeitet.
Ist halt so.
>geeignet, genaue Sekunden zu messen. Was mich zurück zur Frage bringt:>Was will ich eigentlich erreichen?
Eine gute Frage.
>Ich wollte einen möglichst genauen Millisekunden Zähler, mit dem ich>Zeiten messen kann. Am Besten so genau, dass ich damit auch eine Uhr>realisieren könnte.
Ja und? Das wird jeden Tag tausendfach gemacht. Nur du macht ein Ding
der Unmöglichkeit draus. Typisch deutsch!
> Diese Idee kollidiert aber mit anderem primitivem>Code, der Interrupt sperrt. Außerdem kann ich Uhren einfach passende>Quarze wählen, oder einen RTC.
EBEN!
Stefan U. schrieb:> Der Fehler pro Interrupt ist 0,0068965517ms.
Du lügst dir mit diesen dahergerechneten Zahlenkolonnen selber was in
die Tasche, denn dein Quarz ist niemals so genau und stabil.
Stefan U. schrieb:> Der Fehler pro Interrupt ist 0,0068965517ms. Diese Werte addiere ich> solange auf, bis über 0,0086806ms Abweichung erreicht habe.
Machst du das mit Float-Variablen? Die sind auch nicht so arg genau, das
ist dir hoffentlich bewusst...
Lothar M. schrieb:> Machst du das mit Float-Variablen? Die sind auch nicht so arg genau, das> ist dir hoffentlich bewusst...
Und nur um vorzugreifen: Doubles sind auf dem AVR auch nicht genauer als
Floats...um nicht zu sagen dass der Compiler (oder wars der Linker) aus
Doubles im Quellcode eh Floats macht da der AVR nix anderes kann
diesbezüglich. ;)
Michael K. schrieb:> da der AVR nix anderes kann diesbezüglich. ;)
Der AVR kann nur Integers (8 und 16 Bit), alles andere macht der
Compiler.
Allerdings wurde bei der Implementierung des Compilers vor mehr als
15 Jahren die Entscheidung getroffen, sich auf 32-Bit-Gleitkommazahlen
zu beschränken, vermutlich aus Performancegründen ("double" ist ja
der Wertebereich, auf den bei Gleitkomma die default promotion greift).
Eigentlich ein bisschen schade, 48 Bit wäre besser gewesen - ein guter
Kompromiss zwischen Genauigkeit und Geschwindigkeit, gab's schon bei
Turbo-Pascal auf dem Z80. Hätte sogar die Minimalvoraussetzungen für
C99-Kompatibilität erfüllt. ;-)
Jörg W. schrieb:> Der AVR kann nur Integers (8 und 16 Bit), alles andere macht der> Compiler.
Na wenn wir so kleinlich werden wollen: Intergers kann der auch nicht,
er kann nur Bits.
However, ändert nichts daran, dass auf einem AVR kein Unterschied
zwischen Double und Float besteht. Beide werden als Float behandelt ;)
Lothar M. schrieb:> Du lügst dir mit diesen dahergerechneten Zahlenkolonnen selber was in> die Tasche, denn dein Quarz ist niemals so genau und stabil.
Das ist garnicht der Punkt. Der Punkt ist einfach, dass man die
Fehlerakkumulation vermeiden muss, um wenigstens so genau sein zu
können, wie der Muttertakt es ist.
Capisce?
Aber genau deswegen ist der Ansatz mit floats jeglicher Art schon von
vornherein ziemlicher Unsinn, insofern hast du natürlich vollkommen
Recht.
@ Stefan Us (stefanus)
Übrigens: In dem Thread "The secret of WS2812B" findest du in dem
Funktionsdemo eine Bresenham-Anwendung für einen schon recht
komplizierten Fall, der sogar einen 24Bit-Bresenham-Zähler erfordert.
21 Takte. Und das bei dem Luxus, den verschissenen Zähler nicht in
Registern zu halten, sondern im im RAM. Wenn ich den in Registern halten
würde, was problemlos möglich gewesen wäre, hatte das Teil 9 Takte
gekostet, denn 12 Takte von den 21 gehen für das Laden aus dem RAM und
das Rücksichern in den RAM drauf.
Michael K. schrieb:>> Der AVR kann nur Integers (8 und 16 Bit), alles andere macht der>> Compiler.>> Na wenn wir so kleinlich werden wollen: Intergers kann der auch nicht,> er kann nur Bits.
Er kann sie native als Integer addieren, subtrahieren und teilweise
multiplizieren.
c-hater schrieb:> Das ist garnicht der Punkt. Der Punkt ist einfach, dass man die> Fehlerakkumulation vermeiden muss, um wenigstens so genau sein zu> können, wie der Muttertakt es ist.>> Capisce?
Ich hatte dazu schon das Wort DDFS ins Rennen geworfen. Damit kann mit
simplen Integeroperationen leicht den Summationsfehler bis zur
Quarzgenauigkeit reduzieren.
Macht euch keine Sorgen um Fließkomma Zahlen. Nur weil ich 0,xxxxx
Millisekunden geschrieben habe, heißt das noch lange nicht, dass ich
Fließkommazahlen im C Programm verwenden wollte. In der Tat habe ich auf
Mikrocontrollern in 20 Jahren noch nie Fließkomma Zahlen verwendet.
Naja, einmal vielleicht. Aber ein Blick in das Assembler-Listing brachte
mich dazu, das ganz schnell anders zu lösen und nie wieder zu versuchen.
Lothar M. schrieb:> Ich hatte dazu schon das Wort DDFS ins Rennen geworfen. Damit kann mit> simplen Integeroperationen leicht den Summationsfehler bis zur> Quarzgenauigkeit reduzieren.
Was zum Teufel soll "DDFS" sein? Nichtmal Google kennt den Begriff
(jedenfalls nicht für diesen Zshg. auf den ersten zwei Seiten Hits).
Vermutlich handelt es sich also um einen selbstgestrickten Kunstbegriff
eines Re-Inventors, der im Kern meint: Bresenham. Nur dass der
Wiedererfinder den entweder garnicht kannte oder auch nur nicht
begriffen hat, dass er da seit 60 Jahren bekanntes Terrain erneut
erforscht...
@ c-hater (Gast)
>Was zum Teufel soll "DDFS" sein?
Sowas wie DSDS ;-)
DDFS: Direct Digital Frequency synthesizer.
> Nichtmal Google kennt den Begriff>(jedenfalls nicht für diesen Zshg. auf den ersten zwei Seiten Hits).http://www.lothar-miller.de/s9y/categories/31-DDFS
Der 9. Treffer bei einer Googlesuche . . .
@ Falk Brunner (falk)
Jakob (Gast) schrieb
>>Du wirst also für jeden µC-Typ + Quarzfrequenz einige Berechnungen>>vorher anstellen müssen, um die passenden Parameter als>>Konstanten für den gewählten Timer an das Programm zu übergeben.
Falk Brunner (falk) schrieb:
>Auch das kann man automatisch per Prepräzessor klaren. Der Beweis folgt>im Laufe des Tages.
Keine 2 Stunden mehr, ich werde langsam neugierig... :-)
Ansonsten:
Meine Güte, "der Bresenham" wird ja verfochten, als hätten den hier
manche selbst erfunden. Die sind wohl sehr stolz darauf, ihn kapiert
zu haben...
Der Bresenham hat doch nur das in einen Computer gehackt, was sich
Aloisius Lilius schon vor 500 Jahren ausgedacht hat, um den "Timer"
KALENDER mit minimalen, gleichmäßig verteilten Korrekturschritten
auf den "Quarz" ERDUMLAUF zu synchronisieren...
Michael K. schrieb:> However, ändert nichts daran, dass auf einem AVR kein Unterschied> zwischen Double und Float besteht.
Unfug, auch wenn er immer gerne wiederholt wird!
@ m.n. (Gast)
>> However, ändert nichts daran, dass auf einem AVR kein Unterschied>> zwischen Double und Float besteht.
Genauer, beim Compiler avr gcc. Andere (kommerzielle) Compiler ala IAR
machen das ggf. anders.
>Unfug, auch wenn er immer gerne wiederholt wird!
Wie meinen?
@Jakob (Gast)
>>Auch das kann man automatisch per Prepräzessor klaren. Der Beweis folgt>>im Laufe des Tages.>Keine 2 Stunden mehr, ich werde langsam neugierig... :-)
Ich und hab im Moment keine Lust. Man muss halt das Konzept aus dem
Artikel anpassen. ;-)
@ Falk Brunner (falk)
Na super! Und ich dachte, jetzt kommt mal was, an dem
ich was lernen kann!
Mit den "vorherigen Berechnungen" hatte ich auch an den
Präprozessor gedacht - was mir aber selbst für nur mega,
oder tiny bei beliebigem Quarz SEHR aufwändig erschien.
Wird wohl auch nicht mit 2 Zeilen zu erledigen sein, wenn man
bedenkt, dass manche Timer nur wenige Prescaler-Faktoren und
mal 8, mal 16 Bit haben - bei tiny261 auch noch 10 Bit...
OK, man müsste sich für den eigentlichen (Hardware-)Ablauf auf
den kleinsten gemeinsamen Nenner (falls es den gibt) festlegen.
Dazu für jeden µC die Register-Namen, samt Position der
Mode-, Prescaler-, F_In-Bits etc. vorhalten.
Und - und - und...
Damit ist aber auch die anderweitige Nutzung dieser Timer
blockiert. - Wie vermittle ich das dem Programmierer?
2 Zeilen?
Jakob schrieb:> Wird wohl auch nicht mit 2 Zeilen zu erledigen sein, wenn man bedenkt,> dass manche Timer nur wenige Prescaler-Faktoren und mal 8, mal 16 Bit> haben
Ich würde sagen, deine Denkrichtung ist verkehrt herum.
Die Idee hier ist: du sagst dem Makro, welche Frequenz der Interrupt hat
und das Makro berechnet dann den Summationswert für die DDS/DDFS.
Du brauchst keinen 1ms-Interrupt, um eine Sekunde zu "erzeugen".
Es kann auch ein unregelmäßiger IRQ sein, solange gewährleistet ist,
dass der Timer zwischen zwei IRQs keinen Überlauf macht. In der ISR
subtrahierst du die Anzahl der Zyklen oder Ticks, die seit dem letzten
IRQ vergangen sind, in eine z.B. 24-bit-Variable, wenn diese 0
unterschreitet (geht sehr einfach mit dem Negativ-Flag), ist die Sekunde
vorbei. Dann addierst Du die genaue Zahl der Ticks pro Sekunde, damit
die nächste Sekunde um die bereits vergangene Zeit korrigiert wird.
Das ganze dauert nur ein paar Zyklen (3 loads, 3 subc und ein
conditional branch). Wenn die Sekunde vorbei ist, nochmal 3 loads und 3
adc.
@ Jakob (Gast)
>Na super! Und ich dachte, jetzt kommt mal was, an dem>ich was lernen kann!
Das kannst du jetzt schon. Der Abschnitt mit dem 32 KHz Uhrenquarz ist
von mir.
https://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC#Echtzeituhr_mit_Uhrenquarz>Dazu für jeden µC die Register-Namen, samt Position der>Mode-, Prescaler-, F_In-Bits etc. vorhalten.>Und - und - und...
Man kriegt das schon hin, je nach Aufwand auch vollautomatisch.
>Damit ist aber auch die anderweitige Nutzung dieser Timer>blockiert. - Wie vermittle ich das dem Programmierer?
Mein Gott, niemand will ein neues Super-Duper Sonstwas Framework
erschaffen, bei dem jeder Hirntote nur dreimal clicken muss, um die Next
Generation Killer App zu "erschaffen".
Ach ja. Nur selber denken macht geistig fett. Dazu gehört auch, sich mal
an ein kniffliges Problem ranzusetzen und es SELBER hinzukriegen! Los
gehts!
@ eProfi (Gast)
Der TO möchte einen SO GUT WIE MÖGLICH genauen 1 ms Takt.
Wahrscheinlich so, dass 1000 davon ungefähr eine Sekunde
dauern.
Für alle AVR tiny und mega und jeden Quarz.
Setze doch einfach deine klugen Gedanken in etwas dafür
Brauchbares um - und schon bist du der Held!
Blabla hatten wir hier schon mehrfach...
> Der TO möchte einen SO GUT WIE MÖGLICH genauen 1 ms Takt.
Der TO Stefanus glaubt, einen solchen für eine möglichst exakte
Zeitmessung zu brauchen.
Das genaue Vorgehen hängt von den Wünschen (z.B. der maximal messbare
Zeit und der Auflösung) ab:
Bis zu 2^31 / 20000000 = 107,37 Sekunden (bei 20 MHz) reicht es, statt
der 24-bit-Zählvariable eine mit 32 Bits zu verwenden.
(^31, weil eine signed Variable den Algorithmus vereinfacht).
Dann ist die Zeitmessung sehr einfach: Bei Beginn den Zählerstand
merken, bei Ende die gemerkte Zeit vom aktuellen Zählerstand
subtrahieren.
Wenn man es sehr genau haben will, rechnet man noch die beiden
Timer-Stände an den beiden Ereignissen mit dazu.
Nun hat man die Ticks zwischen den beiden Ereignissen.
Mit einer Division durch (Ticks pro Einheit) kommt man auf die
gewünschte Einheit, z.B. ms oder s.
Ein anderer Lösungsansatz ist ein leicht erweiterter Algorithmus mit
zwei kaskadierten Zähler. Damit kann man beliebig lange Zeiten
bestimmen.
Der eine Zähler zählt Ticks, der andere z.B. ms oder s, je nach
gewünschter Auflösung.
> Für alle AVR tiny und mega und jeden Quarz.
Mir ist kein AVR bekannt, der nicht einen Timer und die Befehle adc, sbc
und bmi hätte.
Ein Beispiel:
Fxtal = 1,234 MHz, freilaufender 8bit-Timer, d.h. IRQs mit 4822,xx Hz.
Wir subtrahieren in der ISR 256 und vergleichen auf Null.
int16_t ticks; uintxx_t millis;
ticks -= 256; if (ticks < 0) {ticks += 1234; millis ++;}
Das geht in Asm besonders elegant, da nur ein dec auf das High-Byte von
ticks nötig ist und danach das Ergebnis des Compares schon intrinsic im
Sign-Bit und im Carry-Bit steckt.
dec high(ticks)
bnc done ;oder bpl
addw ticks, #1234
inc millis
done:
reti
Aufpassen muss man, wenn das "1234" kleiner wird als "256", die ISR also
seltener aufgerufen wird als milli hochgezählt, dann heißt es:
ticks -= 256; while (ticks < 0) {ticks += xxx; millis++;}
Das ist ja der Clou an einem Synthesizer: man kann aus einer beliebigen
Frequenz eine (nahezu) beliebige andere erzeugen.
Das gilt für Hardware- als auch für Software-Synthesizer.
Korrektur: bei ersten Algorithmus kann man doch 32 Bits nutzen, da das
Sign-Bit nicht verwendet wird.
Der zweite Algo ist der Bresenham, wobei die Fallunterscheidung if /
while beim Linien-Bresenham mit der Geradensteigung <1 / >1
gleichwertig ist. Beim Klassiker löst man die Fallunterscheidung mit dem
Vertauschen von x und y, das dürfte hier nicht funktionieren.
Ich weiß, die Theorie ist nicht ganz trivial, ich habe es damals (1986?)
auch nicht auf Anhieb verstanden, sondern mehrere Wochen / Anläufe
gebraucht.
Auch bitte ich zu entschuldigen, wenn ich die AVR-Syntax nicht mehr ganz
korrekt wiedergebe, ihre Verwendung liegt auch schon wieder mehrere
Jahre zurück. Klar sind Befehle wie "inc millis" als Makros für 16 / 24
/ 32 Bit zu verstehen etc.
Interessant, das immer noch darüber diskutiert wird, was ich vermutlich
haben wollte und was nicht.
1) Ich brauche einen ungefähren Millisekunden Counter. Der Code soll auf
möglichst allen ATtiny und ATmega µC laufen und sich auf die Nutzung
eines 8bit Counters beschränken.
2) Ich habe längst genug Antworten erhalten.
3) Wenn es ohne großen Aufwand machbar ist, dann möchte ich den Timer so
gestalten, dass 1000 ungefähre 1ms Intervalle zusammen exakt eine
Sekunde ergeben (unter der Annahme, das der Quarz seine Sollfrequenz
exakt einhält).
Punkt 1 war schon fertig, sozusagen die Ausgangslage.
Punkt 2 ist erledigt, wirklich.
Punkt 3 ist nicht möglich. Der Aufwand wird im Verhältnis zum Nutzen
unverhältnismäßig groß und damit hat sich das Thema schon erledigt. Denn
ich habe eine simple Alternative: Passenden Quarz wählen.
Der Papst Gregor XIII. hat im 16. Jahrhundert etwas eingeführt, was auch
hier funktioniert.
Das Schaltjahr.
OK. Wir brauchen eine Schaltmillisekunde. Aber das funktioniert genauso.
Mit drei 16bit korrekturwerten (meistens kommt man mit 8bit aus),
bekommt man nahezu jeden Quarz in den Griff. Es ist kein Float oder
32bit Variable notwendig. Und komplizierte Berechnungen werden zur
Laufzeit auch nicht gemacht. Die benötigte CPU Power liegt bei wenigen
Takten. Der Jitter liegt dann bei maximal 1 ms.
Im ersten Wert speichert man die Anzahl der Timer-Takte die eine
Taktkorrektur benötigt (1 Takt Differenz).
Der zweite Wert ist durch den ersten Wert teilbar. Er speichert die
Anzahl der Takte die NICHT korrigiert werden, obwohl der erste Wert
zutrifft.
Der dritte (da sind wir schon im ppm Bereich) ist durch den zweiten Wert
teilbar und sagt das trotzdem korrigiert wird.
Die Korrektur wird nur in eine Richtung (wie beim Schaltjahr) gemacht.
Entweder -1 wenn der Timer zu langsam läuft oder +1 wenn er zu schnell
ist.
Beispiel: Schaljahre mal anders:
Ein Jahr hat 365,2425 Tage. Der Kalender aber nur 365 Tage. Wann müssen
wir korrigieren? Auf jeden Fall ist der Kalender zu langsam. Wir fügen
also einen Tag hinzu, wenn wir eine Anpassung vornehmen.
1. Korrektur: 1 Tag / 0,2425 Tage = 4 (wir runden ab)
- Wir korrigieren 1/4 = 0,25. Dann bleiben 364,9925. Das war zu viel.
Wir müssen einmal aussetzen. Dafür ist der 2. Wert da.
2. Korrektur: 1 Tag / -0,0075 Tage = -133
- durch 4 teilbar wird daraus -132 . Bei 132 Takten setzen wir einmal
aus.
Wir speichern 132 als 2. Wert. Wir korrigieren 1 / (-132) =
-0,0075757575(Periode). Wir haben dann einen Wert von 365,0000757575
(Periode). Das ist noch zu viel. Naja, es sind 0,2 ppm Abweichung.
3. Korrektur: 1 Tag / 0,00007575757575 Tage = 13200 (genau!). Der dritte
Wert bekommt 13200. Damit ist die genaue Millisekunde perfekt Langzeit
stabil (Rechnerisch jedenfalls).
Damit haben wir die drei Werte:
4
132
13200
Beim Gregorianischen Kalender wird 4, 100 und 400 genommen.
Jetzt machen wird das mit dem OCR0A-Wert:
20 MHz Quarz
OCR0A 78,125 also 78-1 sprich 77 (256 Teiler)
Wert 1: 8 --> Volltreffer; Wert 2 und 3 werden nicht benötigt.
Alle 8 "Millisekunden" den Wert auf 76 absenken.
14,318 MHz Quarz
OCR0A 55,9296875 sind 56-1 sprich 55 (256 Teiler)
Wert 1: 14
Wert 2: 896 --> Volltreffer
Da wir zu schnell sind:
Alle 14 "Millisekunden" den Wert auf 56 erhöhen.
Alle 896 "Millisekunden" NICHT erhöhen.
Anderer Teiler:
OCR0A 223,71875 sind 224-1 sprich 223 (64 Teiler)
Wert 1: 3
Wert 2: 18
Wert 3: 288 --> Volltreffer
Auch hier sind wir zu schnell:
Alle 3 schritte den Wert auf 224 erhöhen
Alle 18 schritte NICHT erhöhen
Alle 288 schritte wieder auf 224 erhöhen
14,7456 MHz Quarz (krummere Werte habe ich nicht gefunden)
OCR0A 57,6 sind 58-1 sprich 57 (256 Teiler)
Wert 1: 2
Wert 2: 10 --> Treffer
Zu schnell also langsamer werden bei der Korrektur.
Anderer Teiler:
OCR0A 230,4 sind 230
Wert 1: 2
Wert 2: 8
Wert 3: 40
Hier sind wir zu langsam, also schneller werden.
2,097152 MHz Quarz
OCRA0A 8,192 sind 8-1 also 7 (256 Teiler)
Wert 1: 5
Wert 2: 125 --> Treffer
Ist das die Lösung?
Leute, jetzt seid ihr soweit vom Thema abgekommen, euch kann man gar
nicht mehr zurück holen.
Lasst es gut sein, das Thema wurde ausreichend beleuchtet.