Forum: Mikrocontroller und Digitale Elektronik Problem mit AVR/ext. Interrupt/Zeitmessung


von Mari (Gast)


Lesenswert?

Hallo!

Ich möchte gerne ein Signal analysieren, indem ich die Zeit zwischen 
zwei steigenden Flanken messe.
Der verwendete Controller ist ein ATMega1284P.
Am Pin INT2 (externes Interrupt) schließe ich einen Signalgenerator an, 
der mir ein 100Hz-Rechtecksignal erzeugt. Timer1 (16 Bit) läuft mit 
Prescaler 8, der Systemtakt ist 20MHz. Wenn eine steigende Flanke 
erkannt wurde, wird der Zählerwert ausgegeben und der Zähler wieder auf 
0 zurückgesetzt. Nach 1/100s sollte der Zähler theoretisch bei 25.000 
stehen. Die Ausgabe (per UART an PC gesendet) sieht aber anders aus:
1
...
2
15685;
3
18843;
4
14795;
5
18833;
6
15253;
7
18763;
8
15328;
9
18816;
10
...

Das ist der verwendete Code:
1
...
2
//Timer1
3
  TCCR1B = (1<<CS11);  //prescaler 1/8
4
  TIMSK1 = (1<<TOIE1);//Timer1 Overflow Interrupt enable
5
6
7
8
  //externes Interrupt an Pin INT2
9
  EIMSK = (1<<INT2);
10
  EICRA = (1<<ISC21) | (1<<ISC20);  // Interrupt bei steigender Flanke
11
sei();
12
...
13
14
15
16
//Externes Interrupt
17
ISR(INT2_vect)
18
{
19
20
  uint16_t zeit = TCNT1;
21
  printf("%u;\r\n",zeit);
22
23
  TCNT1 = 0;
24
25
}
26
27
28
//Timer Overflow Interrupt
29
ISR(TIMER1_OVF_vect)
30
{
31
  printf("Timer 1 Overflow\r\n");
32
33
}


Wie man an der Ausgabe sieht wird kein Overflow ausgelöst- daran kann es 
also schon einmal nicht liegen. Hat jemand eine andere Idee, wo der 
Fehler liegt?

von Karl H. (kbuchegg)


Lesenswert?

Und die printf brauchen keine Zeit?

Wenn du schon in einer ISR eine Ausgabe machst, dann bitte so
1
ISR(INT2_vect)
2
{
3
4
  uint16_t zeit = TCNT1;
5
  TCNT1 = 0;
6
7
  printf("%u;\r\n",zeit);
8
}
damit der Zähler schon wieder weiterzählen kann, solange der printf 
läuft.


Aber:
Das ganze ist insofern unsinnig, als du ja den Zähler nicht auf 0 
zurücksetzen brauchst.
Man kann ja auch mit einer Uhr mit Sekundenzeiger stoppen, ohne dauernd 
die Uhr zurückzusetzen. Einfach die Startzeit von der Endzeit abziehen 
und die Differenz ist die verstrichene Zeit.
Hier beim Timer geht das nicht anders
1
uint16_t startZeit;
2
3
ISR(INT2_vect)
4
{
5
  uint16_t zeit = TCNT1;
6
7
  printf("%u;\r\n", zeit - startZeit);
8
  startZeit = zeit;
9
}

Und wenn man jetzt noch weiß, dass der µC genau für den Fall:
wenn Flanke an einem Pin auftritt, dann sichere SOFORT den Zählerstand 
des Timers,
eine extra Hardware/Behandlung eingebaut hat, dann hat man da auch noch 
die Interrupt-Latenz draussen. Dieses Spezialding nennt sich "Input 
Capture" und ist das MIttel der Wahl, wenn es darum geht Pulse mit 
höchstmöglicher Genauigkeit auszumessen. IN jedem Thread hier im Forum, 
in dem es in irgendeiner Forum um Frequenzmessung geht, landet man 
letzten Endes unweigerlich beim Input Capture. Du bist ja schlieslich 
nicht der Erste, der Pules ausmessen / Frequenzen messen will.


Das man in einer ISR keine langsamen Ausgaben macht, brauch ich dir 
hoffentlich nicht erzählen. Ich schreibe das jetzige Vorgehen jetzt 
einfach mal den ersten Tests und Versuchen zu. Das du dir natürlich 
genau dadurch erst mal fehlerhafte Ergebnisse eingehandelt hast, steht 
auf einem anderen Blatt.

von Oliver (Gast)


Lesenswert?

Hast du mal nachgerechnet, wie lange printf für die Asugabe braucht?

Oliver

von Mari (Gast)


Lesenswert?

OK, vielen Dank! Ich habe jetzt auf Input Capture umgestellt und alles 
funktioniert einwandfrei :-)

von Marc 8. (marc87)


Lesenswert?

Mich würde der genaue Code mal interessieren Mari. Bin gerade auch dabei 
solch ein Messverfahren zu erstellen mit dem Atmega 1280.

von Karl H. (kbuchegg)


Lesenswert?

Ganz ehrlich.
In der Zeit, in der du hier die Frage formuliert und geschrieben hast, 
hättest du dir wahlweise
* einen Beitrga suchen können, in dem der Input Capture vorkommt (gute 
Stichworte zum Suchen wurden ja schon genannt)
* aus dem Datenblatt das Register und das Bit rausgesucht, das zu setzen 
ist um den Input Capture einzuschalten und bei einem Capture einen 
Interrupt Aufruf auszulösen.

Ja, es ist so einfach:
* Timer auf Input Capture konfigurieren
* Zugehörigen Interrupt einschalten
* Timer durch Setzen des Vorteilers einschalten

Die zum Interrupt gehörende ISR schreiben, in der man die Differenzh zum 
vorhergehenden ISR Aufruf bestimmt (so wie oben) und damit irgendwas 
macht (zb ausgeben. Vorzugsweise nicht in der ISR ausgeben, aber für 
erste Tests und moderate Pulslängen wäre sogar das akzeptabel). Alles in 
allem ca. 10 Zeilen Code die den Input Capture betreffen. Und da hab ich 
die Leerzeilen auch schon mit eingerechnet.

: Bearbeitet durch User
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
Noch kein Account? Hier anmelden.