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;
}
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.
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.
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.
@ 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?
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
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.
@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?
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.
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
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?
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);
}
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
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
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."
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
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.
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 ;-)
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
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.
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!
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
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.
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.