Hilfe bei Zeitmessung mit Interrupt

OP #4415909
Lesenswert?

Hallo zusammen,
ich komme mit meiner Software zur Zeitmessung einfach nicht weiter...

Der Plan:
Eine Lichtschranke wird periodisch unterbrochen, die Zeit zwischen N 
Unterbrechungen soll genauer als 1ms bestimmt werden.

Die Umsetzung:
Hardware: Arduino Uno.

Jede Unterbrechung der Lichtschranke löst einen Timer1-Capture-Interrupt 
aus. Der 1. Interrupt enabled den T1-Overflow-Interrupt, da die Zeiten 
zwischen den Impulsen einige Sekunden lang sein können. Der letzte 
T1-Capture-Interrupt disabled den T1-Overflow und den 
T1-Capture-Interrupt. Aus den beiden Captures und den Overflows wird die 
Zeit berechnet.

Hilfskonstruktion:
Mit Timer2 wird ein periodisches Signal erzeugt, welches auf den 
Capture-Eingang gegeben wird..
--> Funktioniert, habe ich mit dem Oszi nachgeprüft.

Das Problem:
a) Ich bekomme keine T1-Overflow-Interrupts, auch die Captures sind 
meist Null.
b) Nach einem Durchlauf bleibt das Programm hängen!

Bin für jeden zielführenden Tipp dankbar!

Gruß
  Hermann
Angehängte Dateien:
Gast #4417951
Lesenswert?

Hermann G. schrieb:
> Hallo nochmal,
> hab ich in meiner Frage etwas vergessen zu erwähnen, oder hat wirklich
> niemand einen Hinweis für mich???
>
> Viele Grüße
>   Hermann

Variablen, welche in einer Interrupt-Routine verändert und an anderer 
Stelle gelesen werden, sollten besser volatile deklariert werden.

Wäre mal so der erste Versuch.
Gast #4417955
Lesenswert?

Ich glaube man kann es komplizierter nicht machen!

Male Dir mal ein Zeitdiagramm auf.

Dann ist eine fallende Flanke am INTx für den Start eines Timers, z.B. 
Timer1 zuständig, den man per Software beliebig auf 32 Bit oder 64 Bit 
erweitern kann.

Die nächste steigende Flanke am INTx stopp den Timer und man setzt ein 
Flag "Messung beendet" auf true.

Im der Mainloop wird dann das Ergebnis ausgewertet und verrechnet.

In der Interrupt Serviceroutine wird natürlich auch der INTx um 
konfiguriert  - Flanke high-low und umgekehrt - man achtet auf das 
Zeitverhalten der Routine.
#4417956
Lesenswert?

Hallo!

Ich habe mir den Code nicht angesehen, hoffe aber trotzdem, dass dir 
dieser Hinweis hilft: Konzentriere deinen Blick auf's Wesentliche, 
reduziere den Code, und lass dich nicht ablenken vom Drumherum.

Aktuell ist dein Problem, dass ein Timer-Overflow keinen Interrupt 
auslöst.

Lösche (oder kommentiere) allen Code, der damit nichts zu tun hat. Das 
alles lenkt jetzt nur ab. Wenn das dann hinhaut, kannst du den Programm 
Schritt um Schritt ergänzen. Prüfe nach jedem Schritt, dass wirklich 
noch alles tut.

Ich frage mich, warum dieses Problem jetzt erst auftaucht, wo dein 
Programm schon quasi fertig zu sein scheint. Müsste dieses Problem nicht 
schon von Beginn an existieren? Das kommt mir ein bisschen vor, als 
würde man, während man am Dachgeschoss arbeitet draufkommen, dass die 
Wasserleitung im Keller schon seit Beginn an leckt.
Gast #4418080
Lesenswert?

Karl M. schrieb:
> Die nächste steigende Flanke am INTx stopp den Timer und man setzt ein
> Flag "Messung beendet" auf true.

Warum den Timer stoppen? Es reicht doch, per Capture auf der Flanke den 
Zählerstand in ein Register zu übernehmen. In der Capture ISR kann man 
dann in Ruhe das Register lesen und in eine Variable übernehmen. Der 
Wert ist gleichzeitig das Ende der abgelaufenen Taktperiode und der 
Anfang der nächsten. Die Differenz zweier solcher Capture Werte, 
erweitert um volle Timer-Durchläufe, die einer Overflow ISR mitgezählt 
werden können, liefert die Zeitdifferenz zwischen den Flanken. Die 
Zeitauflösung entspricht der Taktfrequenz des Timers, kann also locker 
im µs-Bereich liegen.
Gast #4418129
Lesenswert?

Karl M. schrieb:
> Wolfgang schrieb:
>> Die
>> Zeitauflösung entspricht der Taktfrequenz des Timers, kann also locker
>> im µs-Bereich liegen.
>
> Der Plan:
> Eine Lichtschranke wird periodisch unterbrochen, die Zeit zwischen N
> Unterbrechungen soll genauer als 1ms bestimmt werden.

Ist eine µs-Auflösung denn nicht genauer als eine ms?
Gast #4418130
Lesenswert?

Karl M. schrieb:
> Eine Lichtschranke wird periodisch unterbrochen, die Zeit zwischen N
> Unterbrechungen soll genauer als 1ms bestimmt werden.

Was willst du damit sagen?

"genauer als 1ms" kriegt man doch fast noch mit der Handstopuhr zu 
fassen. Zusammen mit einem µC ist das eher keine ernste Anforderung, 
wenn die Lichtschranke nicht zu sehr unter Schwierigkeiten mit 
Umweltstörungen zu kämpfen hat.
#4418140
Lesenswert?

Hermann G. schrieb:
> Bin für jeden zielführenden Tipp dankbar!
1
volatile unsigned long nOverflow = 0;
2
volatile unsigned long nTime = 0;
3
volatile unsigned char bReady = 0;
4

5
ISR(TIMER1_CAPT_vect)
6
{
7
  static unsigned long last = 0;
8
  unsigned long icr = ICR1 + nOverFlow;
9
  nTime = icr -last;
10
  last = icr;
11
  bReady = 1;
12
}
13

14
ISR(TIMER1_OVF_vect)
15
{
16
  nOverFlow += 65536;
17
}
18

19
int main(void)
20
{
21
.
22
.
23
.
24

25
 if(bReady)
26
 {
27
  bReady = 0;
28
  /* Tu, was zu tun ist*/
29
 }
30
}

Kein INTx-Blödsinn, kein Timer-Interrupt-Geschalte-Unsinn.
Gast #4418394
Lesenswert?

Das hat zwar noch nichts mit der groben Funktion zu tun, aber noch ein 
Hinweiss, wenn es zuverlässig werden soll:

Bei der Nutzung von Überläufen zur erweiterung der Auflösung auf mehr 
als 16 Bit muss man etwas aufpassen, wenn ICP event und Timer Überlauf 
zusammenfallen. Da passt dann die Zählung der Überläufe ggf. nicht zum 
ICP1 Stand. Meistens geht es gut aber etwa eine von 10000 Messungen wird 
deutlich (1 Überlauf) daneben liegen, wenn man nicht aufpasst. Das 
Problem lässt sich aber lösen (siehe z.B. hier: 
http://rn-wissen.de/wiki/index.php?title=Timer/Counter_%28Avr%29#Input_Capture).

Für einfach nur besser als 1 ms Auflösung könnte man ggf. einfach den 
Timer langsam genug laufen lassen, dass man mit der 16 Bit Auflösung des 
Timers auskommt.

Der geziegte Code ist einfach recht lang / unübersichltich. Das hat 
findet sich nicht unbedingt einer zur Fehlersuche. Je weiter der Code 
auf das wesentliche reduziert ist, desto eher wird man Hilfe finden.
Gast #4418457
Lesenswert?

Danke, Lurchi,


> ... wenn ICP event und Timer Überlauf zusammenfallen.
Das Risiko hatte ich bisher noch nicht im Blickfeld.
Gut, dass Du mich jetzt schon drauf gebracht hast, bevor wegen eines 
extrem seltenen Fehlers lange rätseln muss.

Ciao
Wolfgang Horn
#4418477
Lesenswert?

Hier mal die lesbare Version des Bugfixes. Doch Vorsicht! Die Abfrage 
auf <128 ist nicht ganz korrekt! Den im Extremfall kann ein anderer, 
hochpriorer Interrupt die Ausführung sowohl von ICP1 als auch TOV1 für 
mehr als 128 Timertakte verhindern (auch vor dem Timerüberlauf!), vor 
allem bei hohen Timerfrequenzen (kleiner Vorteiler)! Dann stimmt der 
Vergleich NICHT! Mit der Abfrage auf ICP1<32000 hat man nahezu die halbe 
Timerperiode Zeit, so einen Fehler auszugleichen. Länger darf der TOV1 
Interrupt nicht blockiert werden. Wenn doch, knallt es und man mißt 
Fahrkarten.
Hier ist auch eine schöne Anwendung von Short Circuit Evaluation in C. 
Wenn der 1. Vergleich (TIFR1 & (1<<TOV1)) false ist, was er zu 99,99% 
immer sein wird, wird der 2. "aufwändige" Vergleich nicht ausgeführt.
1
ISR(TIMER1_CAPT_vect) {
2
  static unsigned long last = 0;
3
  unsigned long icr;
4

5
  // check race condition of timer overflow
6
  if ((TIFR1 & (1<<TOV1)) && (ICR1 < 32000)) {
7
    nOverFlow++;
8
    TIFR1 = (1<<TOV1); // clear flag, increment already done
9
  }
10

11
  icr = ICR1 + nOverFlow;
12
  nTime = icr - last;
13
  last = icr;
14
  bReady = 1;
15
}
#4419026
Lesenswert?

Ich dachte immer, dass es im Arduino-System, bereits einen Timer gibt.
Den braucht man doch nur bei jeder Unterbrechung auslesen, und 
anschließend feststellen, wann das letzte Mal was los war.
Das geht natürlich erst ab dem n+1-ten Mal.
Natürlich sollte der Hintergrund nicht total zugemüllt, mit 
irgendwelchen Unterbrechungen, sein.
OP #4420422
Lesenswert?

So, es geht.

Vielen Dank an alle, die konstruktive Vorschläge gepostet haben. Ich 
habe die Interrupt-Struktur überarbeitet und so weit wie möglich 
vereinfacht und etliche der Hinweise berücksichtigt.

@Falk: Danke für den Tipp, den Overflow-Interrupt ggf. im 
Capture-Interupt abzufangen. ("Race"). Das hat dem Prohramm den letzten 
Schliff gegeben.

Grüße an alle Mitleser
  Hermann

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren