Forum: Mikrocontroller und Digitale Elektronik Interrupts sperren / freigeben


von AVR Neuling (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Sauger (Gast)


Lesenswert?

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.

von Daniel V. (danvet)


Lesenswert?

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

von Daniel V. (danvet)


Lesenswert?


von Daniel V. (danvet)


Lesenswert?

> 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.

von Peter II (Gast)


Lesenswert?

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)

von Daniel V. (danvet)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Weingut P. (weinbauer)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

von AVR Neuling (Gast)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

von Marwin (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Stefan (Gast)


Lesenswert?

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.

von Route_66 H. (route_66)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@  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!

von Peter D. (peda)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Route_66 H. (route_66)


Lesenswert?

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
Noch kein Account? Hier anmelden.