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