Hallo!
Ich versuche gerade die Zeit zwischen zwei steigenden Flanken zu messen.
Mein uC ist ein ATmega8 der mit 16MHz läuft. Hierfür verwende ich den
INT0. Löst der Interrupt aus, wird ein Timer gestartet. Löst er ein
zweites Mal aus, wird der Timer gestoppt. Danach gebe ich den
Zählerstand/2 auf einem LCD aus. Die beiden Interrupts werden noch zum
Testen softwareseitig ausgelöst. Also setze ich den Pin einmal auf high,
dann low und wieder high. Da habe ich die beiden steigenden Flanken.
Dazwischen hab ich mal unterschiedlich lange delays gemacht. Immer bin
ich 5us über dem richtigen Wert. Auch wenn kein delay dazwischen ist,
zeigt er mir 5us an.
Was dauert hier so lange? Etwa das Starten und Beenden des Timers? Oder
vielleicht der Aufruf der ISR?
Gruß
Pascal
Das Reingehen in die ISR kostet etwas Zeit, genauso wie das Rausgehen.
Je nachdem sind da ein paar CPU-Register zu sichern und wieder
herzustellen.
Deshalb verwendet man für solche Sachen, wenn es auf den Takt genau sein
soll, auch nicht den externen Interrupt sondern den Input Capture
Interrupt (in deinem Fall den vom Timer 1)
Zum einen verleist Du Zeit durch das Starten und Stoppen des Timers.
Zweitens dauert es eine Weile, bis die Interrupt-Service Routine
angesprungen wird.
Es ist besser, den Timer ständig laufen zu lassen und beim Interrupt den
aktuellen Zählerstand zu erfassen und dann die Differenz der beiden
Werte berechnen. Dabei ist es wichtig, das vom Auslösen der
Interrupt-Routine bis zum Erfassen des Zählerstandes immer die gleiche
Zeit verstreicht, sowei man das unter Kontrolle hat.
volatile uint8_t counter1;
volatile uint8_t counter2;
ISR(INT0_vect) {
counter1=counter2;
counter2=TCNT0;
}
int main(void) {
...
while (1) {
uint8_t diff=counter2-counter1;
// diff anzeigen
}
}
In diesem Fall wird die DIfferenz zwischen zwei Impulsen fortlaufend
gemessen. Wenn der Counter zwischen zwei Messungen über läuft, ist das
Ergebnis (diff) trotzdem richtig, sofern die Variablem vom gleichen Typ
sind, wie das Counter Register (also hier 8 bit unsigned).
Vermeide es, in Interrupt-Routinen irgendweile langwierigen
Ein/Ausgaben, Zahlen-zu-String Umwandlungen und Punkt-Rechnungen
(Multiplikation, Division) durchzuführen. Denn das dauert unter
Umständen einige hundert Mikrosekunden. Andere folgende Interrupts
verzögern sich entsprechend.
Solange Du nur eine Messung machst, fällt das noch nicht auf. Aber das
Programm wird sich noch umfangreicher werden, nicht wahr?
Dieses Prinzip funktioniert auch mit dem Input Capture Interrupt von
Timer 1. Dann musst Du 16 Bit Variablen verwenden und das Input Capture
Register in der ISR auslesen. Der Vorteil ist, dass das ICR genau den
Zählerstand vom Zeitpunkt des Signals enthält, auch wenn die ISR etwas
verzögert ausgeführt wird.
So, habe das jetzt mal mit der ICU probiert. Leider funktioniert es noch
nicht ganz. Anscheinend funktioniert die Zuweisung der Differenz nicht.
Weiter unten im Programm gebe ich die Differenz aus. Dort gibt es keine
Probleme mit der Berechnung. Sie ist korrekt, deswegen denke ich, dass
es eher ein Problem der Zuweisung ist. In Zeile 6 tritt das Problem auf.
Am Ende ist period immer noch 0. Was ist hier das Problem?
Pascal S. schrieb:> So, habe das jetzt mal mit der ICU probiert. Leider funktioniert es noch> nicht ganz. Anscheinend funktioniert die Zuweisung der Differenz nicht.> Weiter unten im Programm gebe ich die Differenz aus. Dort gibt es keine> Probleme mit der Berechnung. Sie ist korrekt
Das sagen sie alle.
> deswegen denke ich, dass> es eher ein Problem der Zuweisung ist. In Zeile 6 tritt das Problem auf.
Eher nicht.
Dein Problem liegt an einer Stelle im Code, die du nicht gezeigt hast.
> Am Ende ist period immer noch 0. Was ist hier das Problem?
Woher weißt du in der Hauptschleife, dass eine Messung beendet ist?
Mit welchem Prescaler taktet der Timer?
Okay, ich hätte gleich den ganzen Code posten sollen. Sorry dafür.
Ich generiere nur zwei steigende Flanken. Deswegen weiß ich wann die
Messung fertig ist. Der ICP ist mit PD2 verbunden.
> period &= ICR1 - firstStamp;
Da steht aber ein &= und kein =
Das da 0 rauskommt, ist jetzt nicht wirklich verwunderlich :-)
Und es zeigt wieder mal:
Poste IMMER deinen richtigen Source Code. Halte Abstand von der Technik
extra fürs Forum mal kurz was einzutippen. Manchmal sind es wirklich nur
solche Kleinigkeiten, die im Originalcode vorhanden sind und in der
Forumsversion nicht.
da würde ich sicherheitshalber nach dem letzten Port setzen noch eine
kleine Verzögerung reinbauen, damit mir der Interrupt nicht reinknallt,
während gerade das auslesen von period begonnen hat.
Und siehe da: Es funktioniert! Danke! Ja wahrscheinlich knallt der
Interrupt gerade dann rein, wenn er auf period zugreift. Und da period
ja eine 16 Bit Variable ist, ist der Zugriff nicht atomar. Also geht da
etwas schief. Man sollte bei solchen Zugriffen also Interrupts lieber
deaktiviern. Ist der Grund für den Fehler so richtig beschrieben?
Pascal S. schrieb:> Und siehe da: Es funktioniert! Danke! Ja wahrscheinlich knallt der> Interrupt gerade dann rein, wenn er auf period zugreift.
Nach dem Setzen des Port Pins auf 1 vergeht noch ein bischen Zeit, bis
dann am ICP-Eingang die Veränderung ankommt (paar Takte). Drumm muss man
auch einen NOP einlegen, wenn man auf einen Portpin ausgibt und
denselben Pin gleich danach abfragen will. Ich schätze mal, dass hier so
ein ähnliches Problem vorlag. Wenn sich der Compiler die Adresse von
period irgendwo in einem Register vorgehalten hatte, kann er ja mehr
oder weniger sofort mit dem Auslesen der Variable nach dem Pinsetzen
anfangen und wenn das zu früh war, dann war die ISR noch gar nicht
drann. Kann sich aber nur um ein paar Takte gerissen haben.
Man müsste da jetzt den Assembler Output untersuchen, wenn man es ganz
genau wissen will. Da das aber nur ein Testprogramm ist und dein
endgültiges Pgm mit Sicherheit ganz anders aussieht, kann man das
Problem aber auch zu den Akten legen.
> Und da period> ja eine 16 Bit Variable ist, ist der Zugriff nicht atomar. Also geht da> etwas schief. Man sollte bei solchen Zugriffen also Interrupts lieber> deaktiviern.
das sowieso.
> Und da period> ja eine 16 Bit Variable ist, ist der Zugriff nicht atomar. Also geht da> etwas schief. Man sollte bei solchen Zugriffen also Interrupts lieber> deaktiviern.
So isses...