Hallo zusammen, ich hab mich ein Wenig in die AVR Mikrocontroller mit C und GNU eingearbeitet. Meine ersten Projekte funktionieren ganz gut. Ich hätte jedoch noch ein oder zwei Fragen für die ich keine passenden Antworten finde. Ich kann ja die Interrrupts sperren und wieder frei geben. Mit cli() und sei(). Was ich jetzt noch nicht ganz verstehe ist, wenn ich die Interrupts gesperrt habe und in dieser zeit kommt ein Interrupt, ist der dann weg, oder wird er dann direkt nach Freigabe ausgeführt? Meine zweite Frage bezieht sich auch noch auf das selbe Problem. Wenn ich nun mitten in der Ausführung eines Interrupts bin und in diesem Interrupt eine Funktion aufrufe, die kurz die Interrupts sperrt und wieder frei gibt. Was passiert dann? Das kann man ja dann noch weiter spinnen. Wenn ich in dem oben erwähnten Interrupt bin und diese Funktion gerade noch die Interrupts sperrt und genau dann ein weiterer Interrupt kommt. Was ist dann? Wird der zweite Interrupt dann direkt nach dem ersten ausgeführt, oder geht dieser verloren. Ich weiß, dass ich noch ein Anfänger bin, aber diese Fragen beschäftigen mich nun mal. Danke schon mal.
AVR Neuling schrieb im Beitrag #2753451: > Meine zweite Frage bezieht sich auch noch auf das selbe Problem. Wenn > ich nun mitten in der Ausführung eines Interrupts bin und in diesem > Interrupt eine Funktion aufrufe, die kurz die Interrupts sperrt und > wieder frei gibt. Was passiert dann? das solltest du einfach nicht machen. in einer ISR oder auch in funktionen die von der ISR aufgerufen werden haben SEI und CLI nichts zu suchen. wenn die ISR angespruchen wird, dann sind automtisch alles interrupts gesperrt, wenn du sie jetzt duch ein SEI freigibst dann kann es zu einen stackoverflow kommen.
Moin, AVR Neuling schrieb im Beitrag #2753451: > Was ich jetzt noch nicht ganz verstehe ist, wenn ich die Interrupts > gesperrt habe und in dieser zeit kommt ein Interrupt, ist der dann weg, > oder wird er dann direkt nach Freigabe ausgeführt? Interrupts sind speichernd, werden also nach Freigabe abgeaarbeitet.
Sehr gut Fragen stellst du da. - Wenn Interrupts gesperrt werden, dann heißt das lediglich, dass sie zu diesem Zeipunkt keinen Interrupt auslösen. Die anstehenden Interrupts werden "gesammelt". - Wenn man die interrupts frei gibt, dann werden anstehenden Interrupts ausgelöst (eventuell gibt's da auch Prioritäten) - Wenn du in einer Interruptfunktion die Interrupts sperrst und wieder frei gibst passiert gar nix, da die Interruptfunktion ja gerade noch läuft und eh nicht unterbrochen werden kann/darf (vielleicht gibt es ja auch krasse Systeme, die sogar eine anstehende Interruptroutine unterbrechen?) - Achtung!: Falls die Interruptroutine (oder die Sperrung der Interrupts) zu lange dauert, dann geht ein Interrupt, der mehrmals kommt verloren. Z.B.: Du hast einen Timernterrupt, der alle 2ms kommt. Wenn du jetzt in deinem Hauptprogramm die Interrupts sperrst und dann 3ms rumdödelst, dann fehlt dir ein Timerinterrupt. -Fazit: Interruptroutinen so kurz (zeitlich) wie möglich halten, Interrupt sperren nur wenn unbedingt notwendig und so kurz (zeitlich) wie möglich
> wenn die ISR angespruchen wird, dann sind automtisch alles interrupts > gesperrt, wenn du sie jetzt duch ein SEI freigibst dann kann es zu einen > stackoverflow kommen. Das "sei2 bewirkt diesbezüglich nichts, da die Interrupts hardwaremässig gesperrt sind und ert durch "reti" wieder frei gegeben werden.
Daniel V. schrieb: > Wenn du in einer Interruptfunktion die Interrupts sperrst und wieder > frei gibst passiert gar nix doch. wenn du ihn freigibst wird gleich danach der nächste interrupts aufgerufen. Aus dem Grund sollte man dies nicht machen. (oder nur wenn man genau weiss was man tut)
Peter II schrieb: > Daniel V. schrieb: >> Wenn du in einer Interruptfunktion die Interrupts sperrst und wieder >> frei gibst passiert gar nix > doch. wenn du ihn freigibst wird gleich danach der nächste interrupts > aufgerufen. Aus dem Grund sollte man dies nicht machen. (oder nur wenn > man genau weiss was man tut) Falls eine Interruptroutine läuft und in dieser Zeit ein neuer Interrupt kommt, dann wird dieser sowieso nach Ablauf der Routine aufgerufen. Mit oder ohne "sei". Deswegen verstehe ich deine Antwort nicht.
Daniel V. schrieb: > Peter II schrieb: >> Daniel V. schrieb: >>> Wenn du in einer Interruptfunktion die Interrupts sperrst und wieder >>> frei gibst passiert gar nix >> doch. wenn du ihn freigibst wird gleich danach der nächste interrupts >> aufgerufen. Aus dem Grund sollte man dies nicht machen. (oder nur wenn >> man genau weiss was man tut) > > Falls eine Interruptroutine läuft und in dieser Zeit ein neuer Interrupt > kommt, dann wird dieser sowieso nach Ablauf der Routine aufgerufen. Mit > oder ohne "sei". > Deswegen verstehe ich deine Antwort nicht. bei sei wird es aber schon innerhalb der ISR aufgerufen, damit sind noch die werte der alten ISR auf dem Stack. Wenn man das mehrfach macht, hat man ein stack überlauf.
Daniel V. schrieb: > Falls eine Interruptroutine läuft und in dieser Zeit ein neuer Interrupt > kommt, dann wird dieser sowieso nach Ablauf der Routine aufgerufen. Dank normalerweise gesperrter Interrupts während der Ausführung einer ISR. > Mit oder ohne "sei". Falsch - das genau macht den Unterschied aus. Der Return-from-Interrupt Befehl beeinflusst das Interrupt-Enable-Flag, hat sonst aber keine weiteren Auswirkungen auf die Interrupt-Bearbeitung. Es gibt andere Controller, deren Interrupt Controller gegen Ende der ISR eine Quittung erhält, da können die Verhältnisse anders liegen.
AVR Neuling schrieb im Beitrag #2753451: > Was ich jetzt noch nicht ganz verstehe ist, wenn ich die Interrupts > gesperrt habe und in dieser zeit kommt ein Interrupt, ist der dann weg, Alle Interruptquellen haben ein Pending-Flag. Und solange dieses gesetzt bleibt, wird ein Interrupt ausgelöst, sobald möglich. Man kann dieses Flag aber auch pollen und manuell löschen. Wird z.B. bei SPI, I2C, EEPROM oft gemacht. AVR Neuling schrieb im Beitrag #2753451: > in diesem > Interrupt eine Funktion aufrufe, die kurz die Interrupts sperrt und > wieder frei gibt. Was passiert dann? Vielleicht einen Stackoverflow, also nicht machen! Funktionen, die von Main und Interrupt aufgerufen werden, müssen den Interruptstatus beim Eintritt merken und beim Austritt restaurieren. Dafür gibt es beim AVR-GCC das Macro ATOMIC_BLOCK(ATOMIC_RESTORESTATE). Peter
Daniel V. schrieb: > Das "sei2 bewirkt diesbezüglich nichts, da die Interrupts hardwaremässig > gesperrt sind und ert durch "reti" wieder frei gegeben werden. Im Gegensatz zu anderen Architekturen merkt sich der AVR nicht, daß er in einem Interrupt ist. Mit dem SEI ist für ihn der Interrupt beendet. Ob SEI oder RETI, ist ihm egal. Bei Xmega kann es anders sein, da dieser Interruptprioritäten hat. Dann muß das RETI ja den Interruptlevel restaurieren. Peter
Ja, man kann in einer ISR die Interrupts aktivieren per SEI, kann aber hässliche Folgen haben, wie beschrieben, mit Overflow ... hab da auch schon böse Überraschungen erleben dürfen.
AVR Neuling schrieb im Beitrag #2753451: > Meine zweite Frage bezieht sich auch noch auf das selbe Problem. Wenn > ich nun mitten in der Ausführung eines Interrupts bin und in diesem > Interrupt eine Funktion aufrufe, die kurz die Interrupts sperrt und > wieder frei gibt. Was passiert dann? Wie schon mehrfach geschrieben kann das zu einem Stackoverflow führen. Du kannst natürlich in einer Funktion, die aus einem Interrupt aufgerufen wird, die Interrupts (nochmal)sperren, nur darfst du sie am Ende nicht einfach so wieder freigeben. D.h. du musst vor dem sei() den Status des Global Interrupt Enable Flag abfragen und am Ende entsprechend die Interrupts wieder freigeben oder eben nicht freigeben. Dann kannst du die Funktion sowohl aus einem Interrupt als auch aus der Mainschleife oder einer anderen Funktion heraus aufrufen und der richtige Interruptstatus wird wieder hergestellt. Du kannst aber auch den betreffenden Code in einen Atomic-Block packen. Dann erledigt der Compiler das für dich. mfg.
Super, danke für eure Antworten. Ich denke, dass ich einen Teil verstanden habe. Ich DARF kein sei() in einem Interrupt machen, sonst kann es zu Problemen kommen. Was ich bis jetzt noch nicht kapiert habe. Wenn ich doch ein sei() im Interrupt mache, wird er dann sofort abgebrochen, oder ist dann nur die Gefahr da, dass ein anderer dazwischen haut. Danke an alle.
Thomas Eckmann schrieb: > D.h. du musst vor dem sei() den > Status des Global Interrupt Enable Flag abfragen und am Ende Quark. Vor dem cli() natürlich. mfg.
AVR Neuling schrieb im Beitrag #2753670: > wird er dann sofort abgebrochen, oder ist dann nur die > Gefahr da, dass ein anderer dazwischen haut. es wird sofort unterbrochen wenn ein das ereigniss für einen neuen interupt eintritt oder schon eingetreten ist.
AVR Neuling schrieb im Beitrag #2753670: > Was ich bis jetzt noch nicht kapiert habe. Wenn ich doch ein sei() im > Interrupt mache, wird er dann sofort abgebrochen, oder ist dann nur die > Gefahr da, dass ein anderer dazwischen haut. Wenn schon einer wartet, ist der sofort dran. Die laufende ISR wird allerdings nicht ab- sondern unterbrochen. mfg.
AVR Neuling schrieb im Beitrag #2753670: > Ich denke, dass ich einen Teil verstanden habe. > Ich DARF kein sei() in einem Interrupt machen, sonst kann es zu > Problemen kommen. Leider setzt sich zunehmend das Gequake von den Halbschlauen durch - richtig ist: Ein sei() in Interrupts kann zu Problemen fuehren, also solltest du als Anfaenger die Finger davon lassen. Ansonsten ist auch das eine ganz normale Technik mit der man arbeiten kann, wenn man die noetige Erfahrung hat. Meist kaschiert das Blockieren der Interrupts aber sowieso nur Programmierfehler, sein Nutzen ist also zweifelhaft - rettet aber sicher viele µC-Anwendungen im praktischen Betrieb.
Marwin schrieb: > Meist kaschiert das Blockieren der Interrupts aber > sowieso nur Programmierfehler, sein Nutzen ist also zweifelhaft Nein, es ist unbedingt notwendig. Wie willst Du sonst bei Datensätzen > 1Byte Konsistenz erreichen? Oder bei read-modify-write Zugriffen? Und daher gibt es dafür auch die <atomic.h>. Peter
Ich habe diesen Thread gerade durchgelesen und überwiegend richtige aber auch einige falsche Informationen entdeckt. Da auf einen wichtigen Aspekt gar nicht eingegangen wurde, sehe ich mich mich auch genötigt, meinen Senf dazu zu geben. Fie folgenden Aussagen beziehen sich auf ATmega und ATtiny Controller. Ob sie auch für Xmega gelten, weiß ich nicht. Das globale Interrupt Flag befindet sich im Status Register SREG. Das globale Interrupt Flag ist standardmäßig ausgeschaltet, also sind Interrupts gesperrt. Wer Interrupts nutzen will, muss das Flag also zunächst ein schalten mit sei(). Der Mikrocontroller prüft während der Abarbeitung von Befehlen in gewissen Intervallen, ob ein Interrupt gerade ansteht. Wenn ja, dann springt er NACH der Abarbeitung des aktuellen Befehls in die Interrupt Routine und löscht das globale Interrupt Flag (sperrt Interrupts). Die Interrupt Routine endet (wenn sie in C korrekt deklariert wurde) mit dem Assembler Befehl RETI. Normale Prozeduren enden mit RET. Der Unterschied ist, daß RETI zusätzlich zum Rücksprung in das unterbrochene Programm auch das globale Interrupt Flag wieder einschaltet. Somit ist der AVR nach Beendigung der Interrupt-Routine also bereit, den nächsten Interrupt zu verarbeiten. In der Regel führt man nicht mehrere Interrupt-Routine gleichzeitig oder verschachtelt aus, sondern immer nur nacheinander. Wenn es unbedingt sein muss, kann man dem AVR auch erlauben, die gerade laufende Interrupt-Routine wiederum durch einen weiteren Interrupt zu unterbrechen: Hauptprogram -> Interrupt -> 1. Interrupt Routine beginnt -> 1. Interrupt Routine erlaubt weitere Interrupts mit sei() ---> Nächster Interrupt ---> Interrupt Routine wird durch 2. Interrupt-Routine unterbrochen ---> Die 2. Interrupt Routine endet mit RETI -> Die 1. Interrupt Routine wird fortgesetzt -> Die 2. Interrupt Routine endet mit RETI Hauptprogramm wird fortgesetzt Da hier eine Interrupt Routine durch eine weitere unterbrochen wird, braucht man entsprechend mehr Stack für lokale Variablen und Rücksprungadressen. Halb falsch ist die Aussage, daß der AVR sich Interrupts als Ereignis merkt und dann später abarbeitet. Tatsächlich springt der Prozessor die Interrupt-Routine nur dann an, wenn er einen Befehl ausführt und gleichzeitig das globale Interrupt Flag an ist und gleichzeitig ein Interrupt anliegt. Er reagiert also auf Pegel, nicht auf Flanken! Der Unterschied wird deutlich, wenn ein Interrupt auftritt, während das globale Interrupt-Flag aus ist. cli(); -> Timer Interrupt tritt auf Programm löscht Timer Interrupt Flag sei(); nichts passiert, die Interrupt Routine des Timer wird nicht aufgerufen, weil nach dem Ausführen des sei() Befehls kein Interrupt mehr aktiv ist. Jedoch speichern die meisten I/O Kompontenten den Interrupt Status. Wenn der Timer überläuft und deswegen einen Interrupt auslöst, dann setzt er in "seinem" Interrupt Register ein Bit. Das Bit bleibt solange auf High, bis es vom Prozessor wieder aktiv zurück gesetzt wird. Und das macht der Prozessor genau dann, wenn er die Interrupt-Routine anspringt. Man kann es auch per Software manuell tun. Die Konsequenz daraus ist: Wenn die Timer-Interrupt Routine länger dauert als bis zum nächsten Timer Überlauf, dann wird sie gleich nach dem Ende wieder erneut aufgerufen, denn das Interrupt Signal ist dann schon wieder neu gesetzt. Aber wenn die Interrupt-Routine so langsam ist, dass der Timer zwischenzeitlich zweimal über läuft, dann verpasst dein Programm Interrupts, denn es gibt nirgendwo einen Zähler, der mit schreibt, wie viele Ausführungen der Interrupt Routine noch aus stehen. Es kann immer nur maximal eine Ausführung pro I/O Komponente anstehen.
Hallo! @peda >> Meist kaschiert das Blockieren der Interrupts aber >> sowieso nur Programmierfehler, sein Nutzen ist also zweifelhaft > Nein, es ist unbedingt notwendig. Blödsinn. Daß höher priorisierte INTs niedere unterbrechen können, ist jahrzentelang die Regel gewesen. Es ist Aufgabe des Programmierers, genau wie im main-loop, die Probleme der Datenkonsistenz sicher zu stellen.
@ Stefan (Gast) >Der Mikrocontroller prüft während der Abarbeitung von Befehlen in >gewissen Intervallen, ob ein Interrupt gerade ansteht. Nicht in gewissen Intervallen, sondern am Ende eines jeden Maschinenbefehls. Das heißt praktisch nach fast jedem Takt, weil die meisten Befehlt eben nur einen Takt lang sind. >Halb falsch ist die Aussage, daß der AVR sich Interrupts als Ereignis >merkt und dann später abarbeitet. Doch, eben DAS ist die Kerneigenschaft von Interrupts, auch auf dem AVR. ABER, er (und AFAIK auch alle anderen) können sich nur EIN Ereignis von jedem Typ merken, also Timer, UART, ADC, etc. Siehe Artikel Interrupt. >Tatsächlich springt der Prozessor die >Interrupt-Routine nur dann an, wenn er einen Befehl ausführt und >gleichzeitig das globale Interrupt Flag an ist und gleichzeitig ein >Interrupt anliegt. Nein. Das Interrupt Request Bit. Das kann Ewigkeiten vorher gesetzt worden sein. >Jedoch speichern die meisten I/O Kompontenten den Interrupt Status. ALLE! >Es kann immer nur maximal eine Ausführung pro I/O Komponente anstehen. Genau!
Route 66 schrieb: > Blödsinn. Daß höher priorisierte INTs niedere unterbrechen können, ist > jahrzentelang die Regel gewesen. Von Prioritäten war überhaupt nicht die Rede, sondern vom Blockieren! Bitte erstmal lesen vor dem Schreiben. Interrupts Blockieren ist nötig, um Daten mit Interrupts auszutauschen, die mehrere Lese- oder Schreiboperationen benötigen. Interrupts stellen den CPU-Zustand von vor dem Interrupt wieder her. Das Main kann also nicht erkennen, ob Operationen durch einen Interrupt unterbrochen wurden und damit ein Teil der Daten Müll ist. Peter
Falk Brunner schrieb: >>Jedoch speichern die meisten I/O Kompontenten den Interrupt Status. > > ALLE! Nö, es gibt eine Ausnahme, den externen Interrupt bei low level. Peter
Hallo peda, hast ja Recht! Ich meinte das GENERELLE Blockieren von WEITEREN INTs innerhalb von bereits laufenden ISRs.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.