Forum: Compiler & IDEs _delay_ms() innerhalb einer ISR


von Leo (Gast)


Lesenswert?

Hallo,

ich hätte eine Frage zum Thema Interrupts. Wenn ich folgendes Programm 
auf meinem Atmega168 ausführe, leuchtet die Lampe an PB0 für 5 Sekunden 
anstatt 2.5. Mit der CPU Frequenz hat es wohl nichts zu tun, denn wenn 
ich _delay_ms() in der main() Methode verwende funktioniert das Timing 
einwandfrei.

Kann mir das Phänomen nicht wirklich erklären und hoffe daher auf Eure 
Erklärungen.

Grüße!
Leo

#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>


void setupInterrupt(void) {
  PORTD |= (1 << PD2);
  EIMSK |= (1 << INT0);
  EICRA |= (1 << ISC00);
     sei();
}

ISR (INT0_vect) {
  PORTB |= (1 << PB0);
  _delay_ms(2500);
  PORTB &= ~(1 << PB0);
}


int main(void) {

  DDRB |= (1 << PB0);
  setupInterrupt();
  while(1) {
  }

return 0;
}

von dunno.. (Gast)


Lesenswert?

Was generiert denn den interrupt?
Kommt er vielleicht mehrmals? So wie deine isr gebaut ist, siehst du den 
unterschied nicht.

von Frank G. (frank_g53)


Lesenswert?

Normalerweise werden keine Pausen in Interrupt-Routinen eingesetzt.

https://www.mikrocontroller.net/articles/Interrupt

Dort steht:
Im Mittel muss die Interruptroutine kürzer sein als die Periodendauer 
des Ereignisses, andernfalls wird es passieren, dass Interrupts 
"verschluckt" werden.

von Wolfgang (Gast)


Lesenswert?

Leo schrieb:
> Wenn ich folgendes Programm auf meinem Atmega168 ausführe, leuchtet die
> Lampe an PB0 für 5 Sekunden anstatt 2.5.

Wie hast du das festgestellt? Bist du sicher, dass sie nicht zwei mal 
für 2.5s leuchtet? Ein Unterbrechung im Mikrosekundenbereich siehst dú 
nicht ohne sauberen Trigger und ausreichend hohe Abtastrate.

von Leo (Gast)


Lesenswert?

Der Interrupt wird durch einen Taster generiert. Es stimmt schon dass 
solche Taster öfter zweimal schalten wenn sie nur einmal sollten. Das 
müsste aber hier egal sein oder? Wenn ich den Taster bewusst 3 mal 
drücke läuft die Routine ja auch nur 5 Sekunden.

Naja, das Problem liegt ja nicht darin dass der Interrupt verschluckt 
wird, sondern zu lange dauert. Auslösen tut er ja immer.

von Leo (Gast)


Lesenswert?

@ wolfgang: dann müsste der Interrupt immer sofort nach der ISR ein 
zweites mal ausgelöst werden. Und das auch jedes mal nur einmal, nie 
zweimal oder öfter. Ist eher unwahrscheinlich oder?

von Oliver S. (oliverso)


Lesenswert?

Leo schrieb:
> Der Interrupt wird durch einen Taster generiert. Es stimmt schon
> dass
> solche Taster öfter zweimal schalten wenn sie nur einmal sollten. Das
> müsste aber hier egal sein oder?

Ist es aber nicht.


> Wenn ich den Taster bewusst 3 mal
> drücke läuft die Routine ja auch nur 5 Sekunden.

Weil halt neben der schon laufenden ISR das Flag nur einmal gesetzt 
wird. De dritte Tastendruck ändert daran nichts mehr.

Oliver

von Lukey S. (lukey3332)


Lesenswert?

Eventuell musst du Interrupts am Anfang der Interrupt Routine 
Deaktivieren und am Ende wieder Aktivieren. Sonst würde die Routine 
mehrmals rekursiv ausgeführt. Ist jetzt allerdings nur mal ins Blaue 
geraten.

: Bearbeitet durch User
von Leo (Gast)


Lesenswert?

@oilver: ja das klingt plausibel! Wenn ich während die ISR läuft den 
Flag "händisch" überschreiben würde dann sollte es funktionieren oder?

Kann man auf den Flag irgendwie zugreifen, beim Atmega168?

von Hosenmatz (Gast)


Lesenswert?

Leo schrieb:
> Der Interrupt wird durch einen Taster generiert.

Seufz. War ja klar.

Man kann hier im Forum millionenmal erklären, dass man Taster nicht mit 
Interrupts auswertet. Das hat nur zur Folge, das der 
Eine-Million-und-Einte kommt und es versucht.

Ebenso oft kann man hier erklären, dass man delays nicht in Interrupts 
verwendet. Das hilft nix. Es wird wieder einer geboren, der das 
versucht.

von Wolfgang (Gast)


Lesenswert?

Leo schrieb:
> Kann man auf den Flag irgendwie zugreifen, beim Atmega168?

IFR?

von Oliver S. (oliverso)


Lesenswert?

Es wird langsam Zeit für RTFM, auch Datenblatt genannt.

Wenns angelsächsische Probleme bereitet, hilft auch das Tutorial und die 
Artikel hier auf mikrocomtroller.net, zu finden über die Links links 
oben.


Oliver

von Wolfgang (Gast)


Lesenswert?

Leo schrieb:
> @ wolfgang: dann müsste der Interrupt immer sofort nach der ISR ein
> zweites mal ausgelöst werden. Und das auch jedes mal nur einmal, nie
> zweimal oder öfter. Ist eher unwahrscheinlich oder?

Warum soll das unwahrscheinlich sein. Dein Taster wird doch nur beim 
Drücken kurz (einige Millisekunden) rumprellen und nicht die ganzen 
2.5s.

Hast du dir dein Ausgangssignal jetzt mal genau angesehen, ob es (je 
nach CPU Frequenz) mal kurz für irgendetwas im Mikrosekundenbereich 
unterbrochen ist?

von Leo (Gast)


Lesenswert?

Hab die ISR mal folgendermaßen umgeschrieben und es blinkt viermal. => 
Die flag wird gleich während die ISR läuft wieder gesetzt.

Hätte jetzt mit Hilfe von Google und Datenblat folgende Zeile eingefügt:
PCIFR |= (1 << PCIF1);
Es blinkt aber trotzdem viermal.


ISR (INT0_vect) {
  PORTB |= (1 << PB0);
  _delay_ms(1000);
  PORTB &= ~(1 << PB0);
  _delay_ms(1000);
  PORTB |= (1 << PB0);
  _delay_ms(1000);
  PORTB &= ~(1 << PB0);
  _delay_ms(1000);
  PCIFR |= (1 << PCIF1);
}

von Leo (Gast)


Lesenswert?

Mit "EIFR |= (1 << INTF0);" funktioniert`s perfekt und blinkt zweimal.

Vielen Dank Euch Allen und schönen Abend noch!

Leo

von guest (Gast)


Lesenswert?

Mit
1
EICRA |= (1 << ISC00);
brauchst Du dich über mindestens 2 Interrupts nicht wundern, selbst wenn 
Du einen idealen nicht prellenden Taster hättest.

von Leo (Gast)


Lesenswert?

Ja das stimmt, mit "EICRA |= (1 << ISC01);" funktionierts deutlich 
besser

von Roman (Gast)


Lesenswert?

Hallo Leo,

du hast aber nach wie vor das Problem das du die Ausführung deines 
Progammes mit einer Warteschleife in der ISR blockierst.

Versuche mal das Ganze mit einem Timer zu realisieren.

ISR...

IF Timer abgelaufen
  Lade Timer mit Teiler
  starte Timer
END

IRET

Es ist dann egal wie oft der Knopf während der Laufzeit betätigt wird.

Gruß

Roman

von Dieter (Gast)


Lesenswert?

Hey,

Arbeite doch mit Cli() und Sei(), damit der Interrupt sich nicht selber 
unterbricht. Damit solltest du dafür sorgen, dass der Interupt einmal 
ausgeführt wird und die Lampe 2,5s leuchtet

Gruß
Dieter

von Anonymous User (Gast)


Lesenswert?

Tastaturabfrage per Interrupt.
Delays in der ISR.

Die zwei Kardinalfehler in der Microcontroller-Programmierung in einem 
Programm vereint. Wie war noch mal der Spruch? "Jeden Morgen steht ein 
Dummer auf."

von Oliver S. (oliverso)


Lesenswert?

Dieter schrieb:
> Arbeite doch mit Cli() und Sei(), damit der Interrupt sich nicht selber
> unterbricht.

Das macht er auf einem AVR grundsätzlich nicht. Spieleirein mit 
cli()/sei() in einer ISR sind daher sinnlos, und kann unschöne 
Nebenwirkungen haben.

Oliver

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Entprellung lesen und verstehen. Und dann weg mit dem INT0.

von Rolf Magnus (Gast)


Lesenswert?

Dieter schrieb:
> Arbeite doch mit Cli() und Sei(), damit der Interrupt sich nicht selber
> unterbricht.

Das hat aber genau den gegenteiligen Effekt. Es sorgt dafür, dass der 
Interrupt sich selbst unterbricht.

von Prelletariat (Gast)


Lesenswert?

Und dabei ist die Lösung so einfach:
Wir "pollen" die Taste z.B. im 20ms - Raster.

Timer mit ISR aufsetzen, da drin nachschauen ob sich der Eingang 
geändert hat, fallweise ein Flag setzen. Dauert wenige Taktzyklen.

Die Flags schaut man sich z.B. in der main an, und ruft die zugehörige 
Funktion auf, wenn eins davon schreit.

Zusatznutzen : Reagiert werden kann asynchron im Resteittask. Eine 
Reaktion nach 200ms empfindet man als Mensch immer noch als wirklich 
rasant, ist für den µC aber eine halbe Ewigkeit, in der man 
zeitkritischeres tun kann.

Alternative:
Hardware. Siehe vielzitierter Artikel.

Delay() + ISR = epic fail ;-)

von Oliver S. (oliverso)


Lesenswert?

Nu ja, wenn der TO sich jetzt duch das Thema durchbeisst, und versteht, 
was da eigentlich passiert, dann ist das doch ok. Eine bessere 
Trainingseinheit zum Thema ISR auf dem AVR gibt es nicht, danach hat der 
das abgehakt.

Oliver

von Carl D. (jcw2)


Lesenswert?

Dieter schrieb:
> Hey,
>
> Arbeite doch mit Cli() und Sei(), damit der Interrupt sich nicht selber
> unterbricht. Damit solltest du dafür sorgen, dass der Interupt einmal
> ausgeführt wird und die Lampe 2,5s leuchtet
>
> Gruß
> Dieter

Die ISR läuft schon mit gesperrten Interrupts, das ist bei AVR 
grundsätzlich so und wenn anders gewünscht müssen Int's explizit 
freigegeben werden.

Was passiert wirklich:
- Tastendruck löst Int aus
- ISR läuft 2,5 Sekunden mit gesperrten Int's
- Taste prellt ca 2ms, d.h. es steht ein neuer Interruptrequest an der
- sobald die ISR fertig ist, diese gleich nochmal ruft
- wieder 2,5 Sekunden warten, also in Summe 5 Sekunden
- das Prellen der Taste ist innerhalb der ersten 2,5 Sekunden natürlich 
vorbei, d.h.
- es laufen immer exakt 2 ISR in Folge

Was insgesamt zu diesen Programm-Design zu sagen ist, wurde schon 
gesagt.

von Amateur (Gast)


Lesenswert?

Das Problem erinnert mich an das Auto, das einen Unfall hatte und, beim 
Transport zur Werkstatt vom Abschleppwagen, gefallen ist.

Eine Verzögerung, von der verwendeten Länge, in einer Unterbrechung ist 
ein Unding.
Das erzwingt ja praktisch die Unterbrechung der Unterbrechung.
Wird diese dann noch mit Tastern ausgelöst, ist die Kacke erst richtig 
am Dampfen.
Lustig wird es aber auch, weil delay () selber unterbrochen wird und 
werden darf.

Ich habe das Gefühl, dass hier über eine Krücke diskutiert wird, obwohl 
es schon lange Zeit ist, das Bein zu behandeln. Sprich: Ab in die Tonne!

von Leo (Gast)


Lesenswert?

Vielen Dank nochmal für die Tips und Erklärungen!

Für die Skeptiker sei noch erwähnt, dass es hier weniger darum ging eine 
echte Applikation zu entwickeln, sondern eher darum sich mit den 
Grundprinzipien auseinander zusetzen.

Schönen Tag noch!

Leo

von Rolf Magnus (Gast)


Lesenswert?

Dann hoffe ich, dir ist dabei klar geworden, dass zu diesen 
Grundprinzipien auch gehört, dass man keine Delays in ISRs steckt und 
dass man Tasten entprellt.

von Peter D. (peda)


Lesenswert?

Dieter schrieb:
> Arbeite doch mit Cli() und Sei(), damit der Interrupt sich nicht selber
> unterbricht.

Das ist Quatsch mit Soße.
Mir ist keine CPU-Architektur bekannt, die beim Eintritt in den Handler 
nicht automatisch Interrupts gleicher Priorität sperrt.
Eine CPU, die das nicht macht, wäre schlichtweg unbrauchbar, der Stack 
würde rasch überlaufen.

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.