Hallo, was ich vorhabe: der Abstand zweier H/L-Flanken soll möglichst genau gemessen werden mit einem ATmega8. Ich hab mir das so zusammengelesen und frage ob es so korrekt ist: 1) ich verwende ICP1 (PinB.0) und schließe dort das zu messende Signal an. 2) ich lasse den Timer1 laufen, so schnell er kann (den passenden mode muss ich noch raussuchen) und erlaube interrupts allgemein so wie den input capture und den overflow-interupt 3) wenn Overflow auslöst, zähle ich eine ov-variable hoch, damit ich auch Signallängen messen kann, die länger als 65535 Takte sind. 4) wenn der Capture-Interrupt auslöst, lese ich den Zählerstand bei Auslösung und setze danach den Zähler und die ov-variable auf 0 Ist das so richtig? Und was passiert, wenn das Signal innerhalb der Overflow-ISR auftritt? verliere ich das dann? Oder bekomme ich einen verzögerten Wert?
int schrieb: > Und was passiert, wenn das Signal innerhalb der > Overflow-ISR auftritt? verliere ich das dann? Oder bekomme ich einen > verzögerten Wert? Weder noch, denn die ICP Hardware läuft ja ohne Programmunterstützung in Hardware ab. Für mich klingt deine Vorgehensweise völlig richtig.
danke - ok also weil Hardware, werde ich keinen verzögerten Wert bekommen - das sit schonmal gut. Aber wird die Capture-ISR auch ausgelöst werden, direkt nachdem die Overflow-ISR gelaufen ist (also falls der capture während der Overflow-isr passiert)? Oder muss ich per Programm nachschauen, ob das Capture-Interrupt-Flag gesetzt ist?
Hey, der Atmega8 hat keine Verschachtelten Interrupts. Erst wird der eine ausgeführt und nach dem Rücksprung der nächste, falls anstehend - siehe: https://www.mikrocontroller.net/articles/Interrupt#Verschachtelte_Interrupts
int schrieb: > 4) wenn der Capture-Interrupt auslöst, lese ich den Zählerstand bei > Auslösung und setze danach den Zähler und die ov-variable auf 0 Damit geht die Interruptlatenz mit ein, laß den Timer durchlaufen und bilde die Differenz. Das Auslesen eines Timers mit Bereichserweiterung per Interrupt ist etwas tricky: Beitrag "AVR Timer mit 32 Bit"
ist ja wirklich etwas tricky.. aber danke für's Beispiel, ich glaube bei mir wird es nicht ganz so kompliziert, insbesondere muss ich mir ja das overflow-bit nicht merken, wie in Deinem Beispiel, oder? Also abgewandelte Vorgehensweise: 1) und 2) wie oben 3) wenn capture auslöst, lese ich die Variable "periode" (s.u.) und verwende sie als Messwert (außer beim ersten Mal). Dann lese + merke ich mir den Timer-Wert, ziehe vorigen Timer-Wert davon ab und speichere das Ergebnis in eine Variable "periode" 4) wenn Overflow auslöst, addiere ich 65536 zur Variable "periode" Müsste doch klappen, oder?
4) wenn Overflow auslöst, addiere ich 65536 zur Variable "periode" shifte lieber, wenn der Compiler es nicht eh optimiert :D
Um das Problem mit dem fast gleichzeitig auftretendem Überlauf beim Timer und ICP event kommt man auch bei nur einer Periode nicht herum. Man muss also schon in der ISR zum ICP daraus achten ob das overflow interupt Flag gesetzt ist, und dann nach dem ICP wert unterscheiden ob der Overflow kurz vor ICP oder kurz danach kam. Nur wenn der Overflow kurz davor kam muss man den noch berücksichtigen.
Lurchi schrieb: > Man muss also schon in der ISR zum ICP daraus achten ob das overflow > interupt Flag gesetzt ist Muss man das wirklich? Nehmen wir mal 3 Fälle an: a) Overflow kommt einen Takt vor dem Signal b) Overflow kommt mit dem Signal (gleichzeitig) c) Overflow kommt einen Takt nach dem Signal bei a) ist alles gut - es wird zuerst 65535 addiert und der gelesene Wert ist relativ klein. bei c) habe ich in den capture-registern noch den Wert vor dem Overflow, relativ groß nahe am Überlauf - stimmt auch bei b) kommt es darauf an, welcher Interrupt zuerst drankommt b1) ICP zuerst = wie c), oder? b2) OVL zuerst = wie a), oder? Ich fürchte ich hab was übersehen..
int schrieb: > bei a) ist alles gut - es wird zuerst 65535 addiert und der gelesene > Wert ist relativ klein. Nö, nicht jeder Befehl ist einen Takt lang, z.B. RET = 4. Ist nach Befehlsende ICP und OVF gesetzt, hat ICP Vorrang.
Peter D. schrieb: > Das Auslesen eines Timers mit Bereichserweiterung per Interrupt ist > etwas tricky: > Beitrag "AVR Timer mit 32 Bit" Ich habe jetzt Dein Beispiel gedanklich durchgearbeitet und auch ganz verstanden. Es ist aber anders als mein Anwendungsfall: Erstens will ich den Timerwert in der ISR lesen, zusätzliche Interrupts sind in dem Moment also ohnehin nicht möglich. UNd zweitens will ich nicht direkt den Timer lesen, sondern das ICR (Input Capture Register), das den Timer-wert zum Zeitpunkt der Auslösung beinhaltet (unabhängig von der ZEit für den Einsprung in den Interrupt.. ganz schöne Kopfnuss.. :-)
int schrieb: > Ich habe jetzt Dein Beispiel gedanklich durchgearbeitet und auch ganz > verstanden. Nein, das hast du nicht. > Es ist aber anders als mein Anwendungsfall Ja, aber das Problem besteht analog für deinen Anwendungsfall. Es sei denn, es ist gewährleistet, das zwei zu messende Flanken niemals weiter als den Timer-Zählumfang -1 auseinander liegen. Dann (und nur dann!) kann man auf das Gehampele mit dem Overflow komplett verzichten.
c-hater schrieb: > Ja, aber das Problem besteht analog für deinen Anwendungsfall. Es sei > denn, es ist gewährleistet, das zwei zu messende Flanken *niemals* > weiter als den Timer-Zählumfang -1 auseinander liegen. Mist, da fehlt ein Wort. Es muß heissen: "den halben Timer-Zähl..."
int schrieb: > Peter D. schrieb: >> Beitrag "AVR Timer mit 32 Bit" > > Ich habe jetzt Dein Beispiel gedanklich durchgearbeitet und auch ganz > verstanden. Es ist aber anders als mein Anwendungsfall: Erstens will ich > den Timerwert in der ISR lesen, zusätzliche Interrupts sind in dem > Moment also ohnehin nicht möglich. Falsch. Interrupts sind sehr wohl möglich. Im Sinne daß die Hardware die Bedingungen für einen Interrupt weiterhin prüft und im Fall des Falles das entsprechende Flag setzt. Was stimmt: die zugehörige ISR wird nicht sofort angesprungen. Diese Aktion wird so lange verzögert bis du aus der gerade abgearbeiteten ISR zurückkehrst. > UNd zweitens will ich nicht direkt > den Timer lesen, sondern das ICR (Input Capture Register), das den > Timer-wert zum Zeitpunkt der Auslösung beinhaltet (unabhängig von der > ZEit für den Einsprung in den Interrupt.. Das hast du nicht richtig überlegt. Natürlich liest du das ICR und nicht den Timer direkt. Aber es geht um die Variable, die die Überläufe zählt. Wenn der Überlauf und das Capture-Event gleichzeitig[1] auftreten, dann kann es passieren daß du einen Überlauf verpaßt. Dein Meßwert wäre dann um 65536 zu klein. Und deswegen mußt du in der Capture-ISR zusätzlich noch nach dem Überlauf-Interruptflag schauen und entscheiden ob dieser Fall vorliegt oder nicht. [1] wie PeDa schon richtig sagte, muß das nicht exakt gleichzeitig sein. Ein AVR kann immer nur dann in eine ISR springen, wenn er gerade eine Instruktion fertig abgearbeitet hat. Instruktionen die länger als einen Takt brauchen erzeugen also ein Zeitfenster. Alle Interrupts die innerhalb dieses Zeitfensters passieren, sind für den µC zeitlich nicht mehr unterscheidbar.
ok ich sehe es ein. Ich werde also meine 2 ISR so aufbauen: t1overflow: overflow-zähler erhöhen t1Capture: ?ov-Flag gesetzt ? wenn ja - ist der gelesene ICR - Wert klein (höchstes Bit =0) ? wenn ja - overflow-Zähler erhöhen, overflow flag löschen Periodendauer = icr_neu - icr_alt plus (overflow-zähler * 10000hex) icr_alt = icr_neu overflow-zähler = 0 Bin gespannt :-)
Der Ansatz ist vollkommen richtig. Wirklich exakte Messungen kann man nur mit freilaufendem Timer machen. Anzahl der Timer1 Takte zwischen 2 Input Captures mir 32 Bit Zähler messen. Bei 16 MHz Timer1 Takt reicht es also für ca. 268 Sekunden Intervalle. Wenn es in C sein darf kann es z. B. so aussehen: wobei _BV(x) ist nichts anderes ist als altes Atmel define ein für (1<<x) static int ovfl_cnt = 0; // timer overflow interrupt vector ISR(TIMER1_OVF_vect) { ovfl_cnt++; } // input capture interrupt vector ISR(TIMER1_CAPT_vect) { static unsigned int last_icr = 0; // Capture is an external asynchronous event. Overflow and Capture may occur at the // same time, but we are already entering this ISR. Check for missed overflow first. if (ICR1H < 16 && (TIFR1 & _BV(TOV1))) { ovfl_cnt++; // do what ISR(TIMER1_OVF_vect) should have done ;) TIFR1 = _BV(TOV1); // clear overflow flag, because already handled here } // calculate total tick difference using overflows and last value unsigned long t = ((unsigned long) ovfl_cnt << 16) + ICR1 - last_icr; last_icr = ICR1; ovfl_cnt = 0; // ISR code .... }
Adam P. schrieb: > der Atmega8 hat keine Verschachtelten Interrupts. Das ist so nicht ganz richtig. Der AVR ist durchaus in der Lage, eine ISR zu unterbrechen, eine zweite zu bearbeiten und dann wieder in die erste ISR zurückzukehren. Dazu ist es lediglich notwendig, in der unterbrechbaren ISR globale Interupts wieder zu erlauben durch löschen des I-Bits, welches normalerweise beim Eintritt in jede ISR gesetzt wird. Siehe dazu auch AppNote AVR1200. In avr-gcc und avrlibc kann man das durch das Attribut 'ISR_NOBLOCK' für eine ISR ermöglichen. Ob es sinnvoll ist, ist dabei eine andere Frage. ISR_NOBLOCK bietet sich für untergeordnete ISR an, die durch hochpriorisierte ISR unterbrochen werden müssen, sowas wie Soft PWM, software-serielle ISR oder USB Komms.
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.