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?
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?
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.