Hallo, es geht um eine Messung einer Pulslänge. Dafür nehme ich einen Timer der mit Prescaler 1 frei läuft. Ich lese im Interrupt jeweils den Zählerstand und den Overflow Count aus. Soweit ist das ja üblich. Später wird das miteinander verrechnet usw. Mich treibt dafür eine Frage um wofür ich keine Lösung habe. Selbst wenn ich den Timercounter und Overflowcounter direkt nacheinander auslese, kann doch der Overflowcounter noch umspringen. Es könnte folgendes Szenario geben. Ich lese folgendes nacheinander aus. Timercount = 65535 Overflow = 1 Aber eigentlich war es zum Zeitpunkt den Timercount auslesens Timercount = 65535 Overflow = 0 Weil beim auslesen von Overflow dieser mindestens einen Takt weitergelaufen ist steht der Timer beim auslesen vom Overflow vielleicht so da. Timercount = 0 Overflow = 1 Das heißt ich hätte einen Fehler eines gesamten Overflows. 65535 oder 131070. Wie korrigiert ihr das Problem?
Wo ist denn der Vorteil von zwei Zählern? Die Bits könnte man doch auch zusammen als einen Zähler laufen lassen der dann erst viel später überläuft. Komisches Konzept.
Veit D. schrieb: > mit Prescaler 1 frei läuft. Ich lese im Interrupt jeweils den > Zählerstand und den Overflow Count aus. Soweit ist das ja üblich. Später > wird das miteinander verrechnet usw. Nein, das ist nicht üblich. Interrupt wird erst angesprungen (TOV Flag gesetzt) wenn der Zählerstand schon auf Null ist. Also, in der TOV-ISR immer 65536 dazurechnen, (TOV Flag wird in der ISR automatisch zurückgesetzt) und weitermachen.
:
Bearbeitet durch User
Beitrag #6423476 wurde vom Autor gelöscht.
Veit D. schrieb: > Wie korrigiert ihr das Problem? Du kannst Overflow einmal vor und einmal nach Timercount auslesen. Wenn kein Überlauf stattgefunden hat, sind beide gelesenen Werte für Overflow gleich. Wenn sie ungleich sind, kommt es drauf an, ob Timercount als nahe bei 0 oder nahe bei 65535 gelesen wurde.
Beitrag #6423506 wurde von einem Moderator gelöscht.
Hallo Peter, Danke, ich versuche es zu verstehen ...
Beitrag #6423516 wurde von einem Moderator gelöscht.
@ Veit D.
> Ich lese im Interrupt ...
Das Problem scheint mir zu sein, dass der Timer weiterzählt während der
laufende Befehl beendet und der Interrupt behandelt wird. Zwar kann mit
einer Variante von Peters Code noch getestet werden, ob der Überlauf
aufgetreten ist und die Behandlung des Interrupts dauer definitiv 7
Takte lang (ATMega169P_doc8018, S. 16) aber ...
... da nicht klar ist, welchen Befehl der die CPU gerade ausgeführt hat
können zwischen 1 und 4 Zyklen zusätzlich fehlen.
Selbst wenn Du den Befehl noch bestimmst (in dem Du die Rückkehradresse
auf dem Stack auswertest) und die dazugehörige Zyklenzahl bestimmst,
bleibt in vielen Fällen noch eine Unsicherheit zwischen 1 und 4 Zyklen.
(Im Detail hängt das davon ab, von welchem Interrupt Du oben geredet
hast).
Kannst Du nicht vielleicht den Capture Interrupt verwenden?
Theor schrieb: > Kannst Du nicht vielleicht den Capture Interrupt verwenden? Im Capture Interrupt kann man es genau so machen. Nur muß man natürlich nicht nochmal atomar kapseln.
Peter D. schrieb: > Theor schrieb: >> Kannst Du nicht vielleicht den Capture Interrupt verwenden? > > Im Capture Interrupt kann man es genau so machen. Nur muß man natürlich > nicht nochmal atomar kapseln. Ich meine, dass man den Überlauf im Capture-Interrupt gar nicht berücksichtigen darf! Denn entweder ist der Überlauf vor dem Capture aufgetreten, dann wird er in der Capture-ISR schon berücksichtigt. Oder der Überlauf tritt nach dem Capture auf; dann ist er aber für den im Register festgehaltenen Timerwert nicht mehr zu berücksichtigen. Wozu günstigerweise die Overflow-ISR ja sowieso vorerst keine Gelegenheit mehr hat, weil die Capture-ISR schon läuft.
Meine Aufzählung war Unvollständig. Falls der Capure simultan mit dem Überlauf auftritt, müsste der Überlauf auch berücksichtigt werden. Das ist durch die Prioritäten durch den Fall "vor" dem Capture abgedeckt, denn der Überlauf-Interrupt wird dann vor dem Capture abgehandelt.
Hallo, AVR 8-Bit Timer
1 | ISR(TIMER2_OVF_vect) |
2 | {
|
3 | t2_soft += 256; |
4 | }
|
5 | |
6 | u32 get_ticks(void) // read T2 as 32 bit timer |
7 | {
|
8 | u32 val; |
9 | u8 tifr; |
10 | |
11 | cli(); |
12 | val = t2_soft + TCNT2; |
13 | tifr = TIFR; // read interrupt flags |
14 | sei(); |
15 | if( (tifr & 1<<TOV2) && !(val & 0x80) ) // overflow prior reading TCNT2 ? |
16 | val += 256; // then add overflow |
17 | |
18 | return val; |
19 | }
|
1 | if( (tifr & 1<<TOV2) && !(b1111'1111 & b1000'0000) ) val += 256; |
Wenn TCNT2 = 255 ausliest, vergeht doch schon mindestens ein Takt bis man TIFR ausliest. Zu dem Zeitpunkt ist TCNT2 schon bei 0 und löst einen OVF Interrupt aus. Jetzt speichert man in tifr das gesetzte Flag.
1 | TCNT2 = 254: if( (tifr & 1<<TOV2) && !(b1111'1110 & b1000'0000) ) val += 256; >> keine Addition |
2 | |
3 | TCNT2 = 255: if( (tifr & 1<<TOV2) && !(b1111'1111 & b1000'0000) ) val += 256; >> keine Addition |
Bis hierher komme ich mit. Selbst wenn das OVF Flag gesetzt ist, erfolgt keine Addition. Danach komme ich nicht mehr mit. Der nächste TCNT2 Zählerstand wäre 0.
1 | TCNT2 = 0: if( (tifr & 1<<TOV2) && !(b0000'0000 & b1000'0000) ) val += 256; >> Addition |
Hier erfolgt theoretisch eine Addition durch die Überprüfung, wenn das OVF Flag gesetzt ist. Hier gehe ich allerdings davon aus, dass zu dem Zeitpunkt das OVF Flag nicht mehr gesetzt ist. Man liest TCNT2 mit 0 aus, dass heißt die OVF ISR Routine wurde direkt davor oder direkt danach schon ausgeführt und das Flag ist gelöscht. Weil zwischen TCNT2 und TIFR auslesen muss die ISR Routine ausgeführt sein. Wenn das nicht so wäre käme es zu einer doppelten Addition von 256, was falsch wäre. Ich würde jetzt behaupten wollen, dass der Code im Grunde nichts macht im Sinne von er beeinflusst den Zählerstand nicht, weil es wird durch den Vergleich nie 256 addiert. Würde bedeuten man kann das alles weglassen. Gehen wir diese Zeile nochmal durch. val = t2_soft + TCNT2; Wenn man hier TCNT2 mit 255 ausliest, hat OVF noch nicht ausgelöst. t2_soft hat noch den alten Wert, wird mit 255 addiert, alles i.O. Wenn man danach TCNT2 mit 0 ausliest, hat OVF schon ausgelöst. t2_soft hat einen neuen Wert, wird mit 0 addiert, alles i.O. Demzufolge liegt der Trick des korrekten Zählens darin das man in der OVF ISR den vollen Wert addiert und keinen Overflowcounter unterhält. Korrigiert mich wenn mein Gedankengang irgendwo falsch ist.
> Würde bedeuten man kann das alles weglassen.
Und wenn andere Interrupts freigegeben sind?
Hallo, okay, mit der frei aufrufbaren Funktion get_ticks(). Das hatte ich nicht bedacht. Wenn ich das in einer Interrupt Routine mache, bspw. Pin Interrupt, dann muss ich den vollen Aufwand nach meiner Überlegung nicht betreiben. Richtig?
Das habe ich nicht verstanden, wie ist das gemeint?
Veit D. schrieb: > Wenn ich das in einer Interrupt Routine mache, bspw. Pin Interrupt, dann > muss ich den vollen Aufwand nach meiner Überlegung nicht betreiben. > Richtig? Nein. Timer läuft immer, egal ob dein Programm sich in einer ISR befindet oder nicht.- gerade in einer ISR wird der Timer zwar auf 0 gesetzt, aber OVF-ISR kann nicht ausgeführt werden.
:
Bearbeitet durch User
Ein simples Beispiel für den beliebten ATmega328P in Assembler, Timer0 soll XH,XL mit 16-bit zählen, der Timer2 "grätscht dazwischen":
1 | ... |
2 | .org OVF2addr |
3 | reti |
4 | .org OVF0addr |
5 | in tmp2,SREG |
6 | inc XH |
7 | out SREG,tmp2 |
8 | reti |
9 | ... |
10 | ldi XH,0 |
11 | ldi XL,0 |
12 | ldi tmp0,(1<<CS20) |
13 | sts TCCR2B,tmp0 |
14 | ldi tmp0,(1<<TOIE2) |
15 | sts TIMSK2,tmp0 |
16 | ldi tmp0,(1<<CS00) |
17 | out TCCR0B,tmp0 |
18 | ldi tmp0,(1<<TOIE0) |
19 | sts TIMSK0,tmp0 |
20 | sei |
21 | ldi tmp0,253 |
22 | sts TCNT2,tmp0 |
23 | ldi tmp0,255 |
24 | out TCNT0,tmp0 |
25 | nop |
26 | in XL,TCNT0 |
27 | ==> hier steht nun XH,L auf 0000 |
Bei dem nop laufen beide Zähler über, der Timer2-Interrupt wird zuerst bedient, da höhere Priorität. Nach der Rückkehr aus einer ISR wird bei AVR8 aber grundsätzlich der nächste Befehl im Hauptprogramm ausgeführt, bevor wieder in eine ISR gesprungen werden kann. Dieser nächste Befehl ist hier das 'in XL,TCNT0', die Timer0-ISR wird erst danach angesprungen.
Hallo, ich dachte zwar ich hätte mir das heute genug x mal durch den Kopf gehen lassen, scheinbar noch nicht genug. Ich denke morgen nochmal darüber nach. Danke erstmal. Die Kurzantwort wäre gewesen, wenn ich das in einer ISR mache bspw. Pin Interrupt, dann muss ich a) keinen Interrupt sperren b) kann der OVF Interrupt nicht auslösen und damit das Ergebnis auch nicht verfälschen. Wie gesagt, ich denke morgen nochmal darüber nach. :-)
Veit D. schrieb: > Die Kurzantwort wäre gewesen, wenn ich das in einer ISR mache bspw. Pin > Interrupt, dann muss ich > a) keinen Interrupt sperren > b) kann der OVF Interrupt nicht auslösen und damit das Ergebnis auch > nicht verfälschen. Noch einmal: OVF Interrupt kann zwar in einer anderen ISR nicht ausgeführt werden, TCNT kann aber sehr wohl auf Null gehen und dann hast du eine Differenz von 256. Wenn du aber unbedingt mit Prescaler 1 und 8bit Timer arbeiten willst, gibt es auch dafür eine Lösung...
Moin, Ich bin beim 8Bit Timer geblieben wegen dem Code von Peter. Sonst wechselst die Grundlage.
Theor schrieb: > Falls der Capure simultan mit dem Überlauf auftritt, müsste der > Überlauf auch berücksichtigt werden. Das ist durch die Prioritäten durch > den Fall "vor" dem Capture abgedeckt, denn der Überlauf-Interrupt wird > dann vor dem Capture abgehandelt. Welcher AVR soll das denn sein? Bei den ATmega8/16/32 und Nachfolger wird zuerst der Captureinterrupt ausgeführt. Es kann also das Overflowbit gesetzt sein oder auch nicht. D.h. es muß behandelt werden, wie in meinem Beispielcode. Z.B. der Overflow erfolgt und 2 Zyklen später das Capture. Nun muß aber ein 4Zyklen-Befehl noch beendet werden, d.h. danach sind beide Flags gesetzt und Capture wird zuerst ausgeführt. Ohne Auswertung des Overflow fehlen dann 65536 Zyklen.
:
Bearbeitet durch User
Peter D. schrieb: > Theor schrieb: >> Falls der Capure simultan mit dem Überlauf auftritt, müsste der >> Überlauf auch berücksichtigt werden. Das ist durch die Prioritäten durch >> den Fall "vor" dem Capture abgedeckt, denn der Überlauf-Interrupt wird >> dann vor dem Capture abgehandelt. > > Welcher AVR soll das denn sein? > Bei den ATmega8/16/32 und Nachfolger wird zuerst der Captureinterrupt > ausgeführt. [...] Ich interpretiere das Datenblatt des ATMega169 und des ATMega8 anders als Du. In beiden Datenblättern finde ich den Satz: "The lower the address the higher is the priority level." In beiden Datenblättern finde ich die Tabellen, deren (teilweise) Screenshots ich hier anhänge. Dokumentenversionen sind: ATMega169: 8018P–AVR–08/10 ATMega8: 2486AA–AVR–02/2013 (habe ich eben aus dem Internet bei Microchip)
Theor schrieb: > In beiden Datenblättern finde ich den Satz: "The lower the address the > higher is the priority level." Also bei mir ist immer noch 6 < 9
Peter D. schrieb: > Also bei mir ist immer noch 6 < 9 Und davon abgesehen, ist die Priorität auch egal. Wenn OVF-ISR vor CAPT1-ISR aufgerufen würde, wäre das Flag schon gelöscht und der OVF-Zähler schon inkrementiert. Etwas anders sieht es aus, wenn auf anderen µCs verschiedene Interruptprioritäten möglich sind, STM32xxx zum Beispiel.
m.n. schrieb: > Und davon abgesehen, ist die Priorität auch egal. Wenn OVF-ISR vor > CAPT1-ISR aufgerufen würde, wäre das Flag schon gelöscht und der > OVF-Zähler schon inkrementiert. Ist absolut nicht egal! Z.B. das Capture erfolgt 2 Zyklen vor dem Overflow. Der 4Zyklen Befehl wird beendet und der Overflowhandler zuerst ausgeführt, d.h. der Zähler für die obersten 16Bit incrementiert. Nun liest der Capturehandler das Register, was aber vor dem Overflow gelatcht wurde und bastelt daraus den 32Bit Timestamp. Wir haben damit einen Fehler von 65536 Zyklen, also den Supergau. Die Atmel-Leute haben sich also was dabei gedacht, warum sie die Priorität genau so rum festgelegt haben.
:
Bearbeitet durch User
Die Problematik ist nun gerade nicht mein Thema, aber sowie man in der Capture-ISR nachsehen kann, ob OVF schon gesetzt ist, kann man auch in der OVF-ISR nachsehen, ob das CAPT-Flag gesetzt ist und entsprechend reagieren. Dafür hast Du bestimmt eine Minimallösung ;-)
Hallo, bevor das hier driftet. Bei mir gehts nicht um das Capture. Ich werde den Pin Interrupt verwenden, weil am Ende 2 Pulslängen verglichen werden. Bei der Architektur können wir beim bekannten Atmega bleiben. Ich hätte einen ATmega328PB, Mega2560 und ATmega4808/4809 zur Auswahl. Den 2560 und 4809 in Form eines Arduino Boards. Der Einfachheit halber bleiben wir beim ATmega328xx oder Verwandten, die kennt hier denke ich jeder. Ihr bestimmt in- und auswendig. Sonst wird das zu kompliziert. Jetzt denke ich nochmal über den Ablauf nach ... bis später.
Veit D. schrieb: > weil am Ende 2 Pulslängen verglichen > werden. Die Frage ist, wie genau die Messung sein soll. Falls man nacheinander messen kann, kann man den Captureeingang über den ADMUX umschalten.
Ein Beispiel für Verwendung von PCINTx findest Du hier: http://www.mino-elektronik.de/fmeter/fm_software.htm#bsp4 Es sind zwar insgesamt vier Kanäle, die gemessen werden, aber im Prinzip nicht anderes, als wenn per CAPT1-ISR ausgewertet würde.
Peter D. schrieb: > Theor schrieb: >> In beiden Datenblättern finde ich den Satz: "The lower the address the >> higher is the priority level." > > Also bei mir ist immer noch 6 < 9 Du hast Recht, Peter. OFV hat die niedrigere Priorität. Ich habe die Timer-Nummer nicht beachtet. Mein ursprünglicher Gedanke mit dem Capture-Int vom 01.10.2020 02:18 war also, meine ich, richtig. @ Veit Der Punkt ist, dass Du mit dem Capture-Interrupt nicht die Unsicherheit hast, wie lang der Befehl war, der unterbrochen wurde.
Hallo, ich wollte zwar nicht vorgreifen, weil ich erstmal das mit dem OVF Flag, Peters Idee verstehen und verinnerlichen möchte, aber mach es dennoch einmal kurz und knapp. Die zwei zu messenden Pulslängen können im dümmsten Fall zeitlich sehr dicht nacheinander erfolgen. Zeitgleich im Sinne von syncron sind die jedoch definitiv nicht. Ich kann aktuell nicht einschätzen wieviel Zeit der µC zwischen den 4 Messzeitpunkten hat. Ich möchte es erstmal so genau wie möglich machen. Ein Roboterarm bewegt eine Scheibe durch 2 Lichtschranken. Wenn der sich mit vollen Speed bewegt möchte man eigentlich nicht mehr hinschauen. Fürs Auge ist die Bewegung zu schnell. Die Pulslängen sollen später miteinander verglichen werden um Abweichungen festzustellen. Dabei sollte es nicht zu Fehlauslösungen kommen auf Grund von Softwarefehlern beim Timer auslesen. :-) Ich bin jedoch wiederum davon überzeugt, dass ein nacheinander auslösen von zwei Pin Interrupts keine zeitlichen Probleme mit sich bringt. Der Grund für alles ist, dass ich vielleicht die Chance habe mit meinem Hobbywissen auf Arbeit eine kleine Nachrüstung beizusteuern. Zur Zeit versuche ich herauszufinden ob ich mir das überhaupt zu traue. Ich unterscheide zwischen Programmierung zu Hause für mich oder etwas für Arbeit was dann auch wirklich funktionieren muss. Es wäre peinlich am Ende sagen zu müsssen ich bekomme es nicht hin. Die grundlegende Idee kann ich immer noch als reine Idee vorschlagen. Die Lösung käme dann aber nicht von mir. Ich habe jedoch den Ehrgeiz dafür. :-) Erstmal zurück zum Capture Int. Angenommen ich verwende den Capture. Weil dessen Interrupt Prio höher ist kommt es zu weniger Problemen beim auswerten? Ist das der Grund?
Veit D. schrieb: > Die zwei zu messenden Pulslängen können im dümmsten Fall zeitlich sehr > dicht nacheinander erfolgen. Zeitgleich im Sinne von syncron sind die > jedoch definitiv nicht. Ich kann aktuell nicht einschätzen wieviel Zeit > der µC zwischen den 4 Messzeitpunkten hat. Ich möchte es erstmal so > genau wie möglich machen. > ... > Es wäre peinlich am > Ende sagen zu müsssen ich bekomme es nicht hin. Die sichere Lösung wäre, einen µC mit 2 x Capture-Einheiten zu verwenden. Ein Kandidat wäre ein ATmega162, ein anderer ein ATmega64, 128, 324, ..., die mehrere 16 Bit Timer bieten. Noch besser wäre zum Beispiel ein STM32Fxxx, wo einige Timer 4 x Capture-Eingänge haben. Dein Problem ist mit vertretbarem Aufwand gut lösbar.
Veit D. schrieb: > Hallo, > [...] > > Erstmal zurück zum Capture Int. > Angenommen ich verwende den Capture. Weil dessen Interrupt Prio höher > ist kommt es zu weniger Problemen beim auswerten? Ist das der Grund? Nein. Der Grund ist, dass die Genauigkeit der Zeitmessung davon beeinflusst wird, welcher Befehl gerade ausgeführt wurde, als der Interrupt eintrat.
Mist. Ich habe eben einen ellenlangen Text geschrieben, der das erklärt hat. Aber es gab ein Problem und der Text ist weg. Grmbl. Nochmal in kurz: Der Capture-Interrupt speichert den Timerwert (mit einer Unsicherheit von +- 0.5 Zyklen) 3 Zyklen nach dem Eintritt des Ereignisses. Bei anderen Interrupts weiß man nicht, wann er auftratt als gerade ein Befehl ausgeführt wurde. Es kann gerade am Ende des Befehl gewesen sein. Dann wäre die Verzögerung 0. Oder es kann am Anfang des Befehl gewesen sein. Da es aber Befehle mit 1, 2, 3 oder 4 Zyklen Ausführungszeit gibt, Man weiß aber auch nicht welcher Befehl es war. Also ergibt das einen Timerwert mit einer Unsicherheit von -4 Zyklen. Das ist der Unterschied.
Hallo, jetzt habe ich den Vorteil/Unterschied mit Capture verstanden. Vielen vielen Dank. Auf Grund dessen würde ich erstmal sagen wollen, dass die maximal 4 Takte Differenz keine entscheidende Rolle spielen sollten. Ich behalte das jedoch im Hinterkopf, falls die Messungen von Haus aus ziemlich Zappelfrei sein sollten, dann kann ich die Messgenauigkeit mit Capture erhöhen. Bitte nicht als Rückzug von Capture missverstehen. Danke. Jetzt muss ich nochmal zurück zur Basis für mein Verständnis. Das lässt mir keine Ruhe. https://www.mikrocontroller.net/attachment/highlight/16302 AVR 8-Bit Timer
1 | u32 t2_soft; |
2 | |
3 | ISR(TIMER2_OVF_vect) |
4 | {
|
5 | t2_soft += 256; |
6 | }
|
7 | |
8 | u32 get_ticks(void) // read T2 as 32 bit timer |
9 | {
|
10 | u32 val; |
11 | u8 tifr; |
12 | |
13 | cli(); |
14 | val = t2_soft + TCNT2; |
15 | tifr = TIFR; // read interrupt flags |
16 | sei(); |
17 | if( (tifr & 1<<TOV2) && !(val & 0x80) ) // overflow prior reading TCNT2 ? |
18 | val += 256; // then add overflow |
19 | |
20 | return val; |
21 | }
|
Meine Gedanken kreisen um folgende Zustände die laut meiner Meinung nach zufällig auftreten können.
1 | TCNT2 = 254: if( ('x' & 1<<TOV2) && !(b1111'1110 & b1000'0000) ) val += 256; >> keine Addition |
2 | |
3 | TCNT2 = 255: if( ( 'false' ) && !(b1111'1111 & b1000'0000) ) val += 256; >> keine Addition |
4 | |
5 | TCNT2 = 255: if( ( 'true' ) && !(b1111'1111 & b1000'0000) ) val += 256; >> keine Addition |
6 | |
7 | TCNT2 = 0: if( ( 'true' ) && !(b0000'0000 & b1000'0000) ) val += 256; >> Addition erfolgt |
Die letzte Annahme macht mir Sorgen. Ich lege mal meine Gedankengänge offen. Man liest den Zählerstand mit 0 ein. Das OVF Flag ist schon gesetzt. Die OVF ISR kam aber aus irgendwelchen Gründen noch nicht zum Zug. Bis hierher so möglich? Wenn ja mach ich weiter. Jetzt addiert man selbst 256 dazu. Soweit noch okay. Danach wird die OVF ISR ausgeführt und addiert auch nochmal 256 dazu. Dann hätte man laut meiner Meinung nach für die nächste Zählerstandabfrage einmal zu viel 256 addiert. get_ticks macht aus dem Zählerstand 0 richtigerweise 256. Dann addiert die OVF ISR paar Takte später 256 dazu. Damit hat man ein paar Takte später mindestens 512. Wenn man dann get_ticks nochmal aufruft, meinetwegen mit Timer-Zählerstand 100 ohne erneuten Überlauf, müßte der richtige Zählerstand eigentlich 256 + 100 = 356 sein. Man hätte laut meiner Überlegung jedoch falsche 512 + 100 = 612. Hab ich einen Denkfehler oder nicht? Wenn ja an welcher Stelle? Übrigens, wenn ich das sagen darf, gibts wirklich einen kleinen Fehler. Die globale Variable t2_soft muss volatile sein.
Veit D. schrieb: > Hallo, > > jetzt habe ich den Vorteil/Unterschied mit Capture verstanden. Vielen > vielen Dank. Bitte. Gerne. > Bitte nicht als Rückzug von Capture missverstehen. > Danke. Du machst das so, wie Du das für richtig hältst und wie es für Dich passt. Ich gebe nur Infos. > Jetzt muss ich nochmal zurück zur Basis für mein Verständnis. Das lässt > mir keine Ruhe. > [...] > Man liest den Zählerstand mit 0 ein. Das OVF Flag ist schon > gesetzt. Die OVF ISR kam aber aus irgendwelchen Gründen noch nicht zum > Zug. Bis hierher so möglich? Seufz. Wieviel Tassen Kaffee schulde ich Peter eigentlich inzwischen? Aber ja. Soweit möglich. > Wenn ja mach ich weiter. Jetzt addiert man > selbst 256 dazu. Soweit noch okay. Danach wird die OVF ISR ausgeführt > und addiert auch nochmal 256 dazu. Dann hätte man laut meiner Meinung > nach für die nächste Zählerstandabfrage einmal zu viel 256 addiert. > [...] > Hab ich einen Denkfehler oder nicht? Wenn ja an welcher Stelle? > [...] Ja. Du hast übersehen (wenigstens geht's nicht nur mir so, hi hi ha ha hu hu), dass in dem einen Fall die Variable "val" um 256 erhöht wird und dem anderen Fall die Variable "t2_soft". Es wird also nicht zweimal die selbe Variable erhöht.
Veit D. schrieb: > Hallo, > > [...] > Übrigens, wenn ich das sagen darf, gibts wirklich einen kleinen Fehler. > Die globale Variable t2_soft muss volatile sein. Das macht in dem Fall nichts. Denn die Variable wird nur an einer Stelle und nur in einem "Thread" - einer Folge von Anweisungen -, verändert. Volatile ist nötig dann, falls eine Variable in verschiedenen Folgen von Anweisungen geschrieben wird. Also z.B. im Hauptprogramm und in einem Interrupt.
Wird die Lesefunktion jetzt aus einer ISR aufgerufen, oder nicht? Oliver
Hallo, @ Theor: Oh man, Code lesen müßte man können. :-) Zu tief in einen Gedanken und man sieht die Unterschiede nicht mehr. Sind verschiedene Variablen die sich automatisch korrigieren. Puhh. Die Welt ist wieder in Ordnung. Alles macht wieder Sinn. Vielen Dank für den entscheidenden Hinweis. Das mit dem volatile nehme ich mal so hin. Ich selbst mach jede Variable volatile die außerhalb des normalen Programmablaufes verändert werden könnte. Dann kann nichts schief gehen. :-) Oder ist das wieder ein Fall von Handoptimierung für genau diesen Code seitens Peter? @ Oliver: Für diese soeben erfolgte Betrachtung nicht. Wollte nur mit Peters Code Peters Rechentrick verstehen. In meinem späteren Vorhaben verwende ich nach aktuellen Überlegungen 2 Pin Interrupts.
:
Bearbeitet durch User
Veit D. schrieb: > Für diese soeben erfolgte Betrachtung nicht. Wollte nur mit Peters Code > Peters Rechentrick verstehen. In meinem späteren Vorhaben verwende ich > nach aktuellen Überlegungen 2 Pin Interrupts. Also wird TCNT2 (später) doch in der ISR gelesen. Um wirkliche Kontrolle und exakte Resultate zu haben, wäre es besser, die ISRs in Assembler anstatt in C zu schreiben. Beispiel: Capture-Interrupt feuert bei TCNT2 von 254 und Befehlslänge von 2 Cy. Bis dein Programm in der ISR landet, vergehen noch mal 9 Takte. Je nachdem, was du in der Capture-ISR machst, vergehen noch 9-15 Takte bis du TCNT2 auslesen kannst (in C). Das ist ein Overhead von etwa 18-24 Cyclen. In Assembler hast du die 9 Cyclen + 3 PUSH(6Cy) + IN reg, SREG(1Cy) + LDS reg, TCNT2(2Cy) = 18 Cyclen bis zum auslesen. Also ist jeder Zählerstand > 18 (in ASM) schon aktualisiert, du gibst als val (t2_soft+TCNT2-18) und brauchst dich nicht weiter darum zu kümmern. Bei Zählerstand <= 18 wird (t2_soft+256+TCNT2-18) als val zurückgegeben. Dass t2_soft gleich nach Capture-ISR in der OVF-ISR um 256 erhöht wird, interessiert dich nicht weiter - das wird erst beim nächsten Auslesen benötigt. Auf diese Weise hast du einen Fehler von max. 1Cy. P.S. Da die 18Cy konstant sind, brauchst du diese auch nicht unbedingt abzuziehen.
:
Bearbeitet durch User
Hallo, scheitert daran das ich kein Assembler kann. :-) Ich setze das erstmal in C++ um und schau was die Schwankungsbreite vom Zähler macht. Frequenzgenerator kann ich mir mit anderen µC programmieren und paar Messmittel habe ich auch zu Hause. Mal schauen was bei raus kommt ... Danke.
Veit D. schrieb: > scheitert daran das ich kein Assembler kann. :-) Braucht’s dafür auch nicht. Auf die paar Zyklen mehr oder weniger kommt es nicht an. Und Peters „Rechentrick“ ist gar keiner. Der prüft schlicht und einfach mit Test auf das gesetzte MSB, ob TCNT größer oder kleiner TCNT Max/2 ist. Wenn nein, und das OVF-Flag gesetzt ist, dann gabs einen nicht gezählten Oberflow. Oliver
:
Bearbeitet durch User
Hallo, lasse uns bitte nicht über das Wort streiten. Ob nun als Rechentrick oder Auswertetrick betitelt, ich meine, dass sollte egal. sein. Hauptsache ich habe es verstanden. :-)
Veit D. schrieb: > scheitert daran das ich kein Assembler kann. :-) Ist ja auch nicht nötig. Eine einfache Prüfung von TOV2 reicht auch, vorausgesetzt deine beiden PinChange-ISR dauern nicht so lange, dass die beiden ständig nacheinander feuern.
Hallo, letzteres wird herausstellen. :-) Ich ziehe mich erstmal freundlich zurück.
Marc V. schrieb: > Das ist ein Overhead von etwa 18-24 Cyclen. > In Assembler hast du die 9 Cyclen + 3 PUSH(6Cy) + IN reg, SREG(1Cy) > + LDS reg, TCNT2(2Cy) = 18 Cyclen bis zum auslesen. Diese Berechnungen taugen nur dann etwas, wenn nur Interrupts für die Impulsmessung freigegeben sind. Sobald zum Beispiel noch ein anderer Timer, die USART, usw. per Interrupt bedient werden, blockiert deren ISR die genaue Zeitmessung der Impulse. Die Verzögerungen können dann auch in den Bereich von >= 10 µs kommen. Aber vielleicht ist das ja auch völlig ausreichend.
m.n. schrieb: > Diese Berechnungen taugen nur dann etwas, wenn nur Interrupts für die > Impulsmessung freigegeben sind. Sobald zum Beispiel noch ein anderer > Timer, die USART, usw. per Interrupt bedient werden, blockiert deren ISR > die genaue Zeitmessung der Impulse. Nein, Timer2 kommt gleich nach INT0/1 und PCINT. Und eine Impulsmessung bei der alle Interrupts freigegeben sind, hätte auch wenig Sinn. Was ich aber nicht verstehe - wenn es so genau sein soll, warum wird dann nicht Timer1 mit seinen 16bit benutzt? Es sind über 4ms bis zum Überlauf, bei Timer2 sind es nur 16us. Bei Timer1 könnte man bedenkenlos TCNT1 Werte aufzeichnen - wenn der aktuelle Wert kleiner als der vorherige ist, addiert man ganz einfach 65536 dazu...
:
Bearbeitet durch User
Moin, keine Sorge, ich nehme dafür einen 16 Bit Timer. Wie schon mehrfach geschrieben war der 8Bit Timer aus Peters Codebsp. Was für das Verständnis keine Rolle gespielt hat.
Hallo, ich habe es in Form gegossen, erhalte jedoch große Messwertschwankungen. Unterschiede bis zu 6375 Ticks was ca. 0,4ms bzw. 0,08% entspricht. Eine Testfrequenz von 1Hz erzeugt ein Arduino Mega2560 mittels Timerausgang. Der wird von einem Resonator getaktet. Davon wird die positive Signallänge vermessen. Bei 1Hz hat die Pin ISR nur aller 500ms etwas zu tun. Der Timercount wird immer am Anfang an der gleichen Codestelle ermittelt. Danach erfolgt "nur" eine Auswertung wem das Ergebnis zugewiesen wird. Die Serielle wird nur am Ende einer Messung aktiv. Um das auszuschließen habe ich eine Zwischenspeicherung im Array eingebaut. Hat nichts gebracht. Dann habe ich den Timer TCB komplett stillgelegt, hat auch nichts gebracht. Dann habe ich eine generelle Messfreigabe eingebaut, erst wenn die Serielle fertig ist. Hilft auch nichts. Es gibt auch mittendrin größere Abweichungen. Ist der Messfehler normal? Ich hoffe nicht. Wenn nein, Ideen was den Count verfälschen könnte? Verwendet wird ein Arduino Nano Every Board mit Atmega4809.
Hallo, hab noch einen ATmega328PB rausgeholt, einen 16MHz Quarz rangebammelt und lasse Timer 1 einen 1Hz Takt erzeugen. Steckbrettaufbau. Über 100 Werte gesehen beträgt die max. Abweichung nur noch 2522 Ticks (0,15ms).
Hallo, vielleicht buddel ich auf der falschen Baustelle. Also. Jeder Quarz bzw. RC hat doch eine Fehlertoleranz. Quarze meistens +/-30ppm und der Atmega4809 interne RC +/- 1.5% (25°C). 1.5% von 16MHz wären 240kHz. Was ist damit genau gemeint? Ist das der erlaubte Jitter vom Takt oder ist das die erlaubte Taktabweichung vom Sollwert welche ohne spezifierte Jitterangabe "stabil" taktet? Anders gefragt, wenn bspw. der interne RC mit 16.2 statt 16.000 MHz taktet, bleibt der dann weitgehend stabil auf 16.2MHz oder schwankt dieser zwischen 15.8 und 16.2 MHz ständig hin und her? Konstante Temperatur und Spannung vorrausgesetzt.
> vielleicht buddel ich auf der falschen Baustelle
(Arm am Beutel, krank am Herzen
Schleppt' ich meine langen Tage ...)
Da der ATmega4809 ja sicher mit dem internen RC-Oszillator läuft, ist
ein Jitter von 0.15/500 = 300 ppm normal.
(... Und so zog ich Kreis' um Kreise,
Stellte wunderbare Flammen,
Kraut und Knochenwerk zusammen:
Die Beschwörung war vollbracht.)
Veit D. schrieb: > Anders gefragt, wenn bspw. der interne RC mit 16.2 statt 16.000 MHz > taktet, bleibt der dann weitgehend stabil auf 16.2MHz So ist es. Wie er dann typisch mit der Versorgungsspannung und der Temperatur driftet steht im Datenblatt. Veit D. schrieb: > Über 100 > Werte gesehen beträgt die max. Abweichung nur noch 2522 Ticks Das würde ich anders formulieren: "... beträgt die max. Abweichung doch noch 2522 Ticks ..." Mit einem Quarzoszillator betrieben muß die Genauigkeit deutlich besser sein. Du hoppst mit Deinen Gechichten zur sehr durch die Gegend. Wie Deine "messwerte.txt" enstanden ist, ist unklar. Zunächst wird über einen ATmega328 geredet, dann ein ATmega4809 verwendet und zum Schluß ein Arduino-Code für den ATmega2560 gezeigt. Weiter oben hatte ich Dir einen funktionierenden Code gezeigt. Der scheint jedoch nicht angekommen zu sein.
S. Landolt schrieb: > (... Und so zog ich Kreis' um Kreise, > Stellte wunderbare Flammen, > Kraut und Knochenwerk zusammen: > Die Beschwörung war vollbracht.) Und? Haben die Keckse geschmeckt? ;-)
> Und? Haben die Keckse geschmeckt?
Sieht man doch!
> ... und der Atmega4809 interne RC ...
Ich habe eben mal einen ATmega4809 mit seinem internen RC-Oszillator
gegen 10 MHz quarzstabil gemessen, über ca. 40 Werte, und erhalte als
Minimum 0x9898B6, als Maximum 0x98AE0C, also 546 ppm.
(Trinke Mut des reinen Lebens!
Dann verstehst du die Belehrung,
Kommst mit ängstlicher Beschwörung
Nicht zurück an diesen Ort.
Grabe hier nicht mehr vergebens!)
m.n. schrieb: > Wie Deine "messwerte.txt" enstanden ist, ist unklar. Der erste Wert ist die gemessene Pulsdauer in Timerticks, dahinter umgerechnet in ms. > Du hoppst mit Deinen Gechichten zur sehr durch die Gegend. ... > Zunächst wird über einen ATmega328 geredet, dann ein ATmega4809 verwendet > und zum Schluß ein Arduino-Code für den ATmega2560 gezeigt. Das hier ist kompletter Unsinn. Ohne weiteren Kommentar. Ansonsten, ich überlege weiter ...
@ Veit Vermutlich bist Du darauf auch schon gekommen, aber da seit gut 7 Stunden nichts kommt, sage ich es mal sicherheitshalber: Betreibe am besten beide Schaltungen, den Mess-uC und den Generator-uC mit einem Quarz oder Resonator.
Theor schrieb: > Betreibe am besten beide Schaltungen, den Mess-uC und den Generator-uC > mit einem Quarz oder Resonator. Der ATmega2560 hat genügend Timer, um beides zu machen. Damit sind schon mal Taktfehler eleminiert. Die Frequenzen und Meßzeiten wählt man dann leicht unterschiedlich, um worst case Bedingungen zu durchlaufen, d.h. keine gemeinsamen Teiler. Die Testsignale werden von den PWM-Einheiten in HW erzeugt, also ohne Interrupts.
Hallo, ich denke laut S.Landolt ist die Baustelle Takterzeugung die falsche Baustelle? Deswegen bin ich am Code durchgehen und habe vielleicht meine eigene Pin Lib in Verdacht, allerdings ohne ersichtlichen Grund, und überlege die rauszunehmen und alles pure zu programmieren. Was den Eingangspin und dessen Interruptauswertung betrifft. Alles auf dem Mega2560 zu testen ist eine gute Idee, werde alles rüberziehen. Ich bitte um Geduld. Danke.
Gute Nacht, habe den Code auf den Arduino Mega2560 portiert. Läuft. Prescalereinstellung sollte keine Rolle spielen, weil das alles gemeinsame Vielfache sind. Habe erstmal mit 1Hz, 3Hz und 9Hz probiert. Maximale Messabweichung sind 8 Timerticks. Im Mittel weniger. Das sieht schon einmal gut aus. Wenn ich morgen dazu komme teste ich das mit fremden Takt, also zwei verschiedene µC.
Hallo, Zwischenstand ist: Taktgeber ATmega328PB mit 16MHz Quarz. Messung mit Arduino Mega2560. Soweit stabil bis max. 7 Timerticks, bis auf einen Ausreißer mit 27 Timerticks von 500 Messwerten. Jetzt bau ich gerade den Code für den Arduino Nano Every (ATmega4809) um und steck dabei gerade fest. Der misst derzeit völligen Mist.
Veit D. schrieb: > bis auf einen Ausreißer mit 27 > Timerticks von 500 Messwerten. Dann wurde wohl grad ein Interrupthandler abgearbeitet bei der Flanke. Veit D. schrieb: > Der misst derzeit völligen Mist. Sicher, daß der Quarz als Takt ausgewählt wurde. Der RC-Oszillator reicht nur für 2-3 Digits.
Hallo, um die Taktquellenauswahl kümmert sich die Arduino IDE. Das überlasse ich ihr. Da sind standardmäßig 16MHz eingestellt. Die Taktquelle ist der interne RC-Oszillator. Daran kann ich auch nichts ändern. Wenn man einen externen Quarz ranhängen möchte, benötigt die megaAVR0 Serie einen Quarz mit einem Taktausgangspin. Ich komme gerade nicht auf die genaue Bezeichnung. Meine vorhandenen HC49 Typen sind dafür die Falschen. Das derzeitige Messproblem betrifft nicht nur eine Abweichung von paar Ticks, nein, der misst permanent völlig falsche 80.000 Ticks als Abweichung. Ich bin dabei meine PinLib rauszunehmen. Ich erreiche nicht einmal die Anfangswerte mit Abweichungen von "nur" 6375 Ticks. Da muss ein Fehler im Code sein. :-) Ich melde mich wieder.
> Ich komme gerade nicht auf die genaue Bezeichnung.
Nanu? "Quarzoszillator" heißt so ein Teil, schlicht&einfach.
PS: Der Atmega4809 hat nur einen eingebauten Quarzoszillator für einen 32 KiHz-Quarz, zur Verwendung als RTC; wobei dieser allerdings auch für den Haupttakt verwendet werden kann. Ansonsten aber muss, wenn der interne RC-Oszillator zu ungenau ist, ein externer Takt angeschlossen werden.
Hallo, Quarz vs. Quarzoszillator. :-) Gut. > Ich habe eben mal einen ATmega4809 mit seinem internen RC-Oszillator > gegen 10 MHz quarzstabil gemessen, über ca. 40 Werte, und erhalte als > Minimum 0x9898B6, als Maximum 0x98AE0C, also 546 ppm. Diese Abweichung ist derzeit nicht mein Problem. Wenn ich nicht weiterkomme zeige ich den Code.
Hallo, ich habe jetzt aus 285 Messwerten eine maximale Differenz von 3182 Timerticks. Frequenz ist 1Hz und Duty-Cycle ist 1/3. 1/3 deswegen, damit ich prüfen kann ob ich immer die positive Signallänge messe. Die durchschnittliche Pulslänge beträgt 5.361.638 Ticks. Mal 3 ergibt die Periodendauer 16.084.914 Ticks, dass mal 62,5ns = 1.005.307.125ns = 0,99472Hz 3182 Ticks von 16.084.914 Ticks = 3182 / 16,084914 = 197ppm Alles in ppm umgerechnet wären laut meiner Logik 197ppm. Würde bedeuten der interne RC-Oszillator schwankt permanent mit 200ppm. Soweit korrekt? Oder sieht jemand Fehler im Code, in der Auswertelogik? Wenn nicht, liegst allein am internen RC-Oszillator?
Das verstehe ich, wieder einmal, nicht: 3182/5361638 entspricht doch 593 ppm. Was übrigens auch eher dem entspricht, was hier mein ATmega4809 mit seinem internen RC-Oszillator zeigt.
PS: Ich meine, sonst könnte man ja einfach den Tastgrad auf z.B. 1/100 absenken, und, hast du nicht gesehen, wäre der ATmega4809 auf 6 ppm stabil.
Hallo, ja stimmt, ich habe die falschen Werte ins Verhältnis gesetzt. Danke. Edit: Vielleicht fragt sich jemand, warum ich nur noch eine Abweichung von ca. 3200 zu anfänglichen ca. 6400 Ticks messe. Das Einzigste was ich beim Umbau in meiner Lib entdeckt habe, war eine falsch hinterlegte Bitmaske zum reseten für Interruptpins. Dadurch waren im PINnCTRL Register alle "sens on ... edges" aktiviert statt auf 0.
:
Bearbeitet durch User
Guten Morgen, ich wollte mich nochmal bei allen Beteiligten bedanken für die Hilfestellungen und Korrekturen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.