Manchmal muss man verhindern, dass sich main-Code und ISR in die Quere
kommen, beispielsweise wenn man mit int16_t arbeitet, da die zur
Behandlung immer mindestens zwei Anweisungen brauchen.
Es wird dann immer die Vorgehensweise mittels cli und sei bzw. die
Methoden aus util/atomic.h empfohlen, also
1
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
2
// Kritische Sektion, die in einem Zug abgearbeitet werden soll
3
}
Meine Frage wäre jetzt, ob man dadurch nicht evtl. wichtige Updates
verpasst? Bei einem Timer ist es vielleicht noch OK, weil der dann eben
später noch mal auslöst, aber was ist mit TWI/SPI/UART oder externen
PIN-Interrupts, die verpasst man dann evtl. doch, wenn man ab und zu die
Interrupts ausschaltet, oder habe ich da einen Denkfehler?
>Bei einem Timer ist es vielleicht noch OK, weil der dann eben>später noch mal auslöst, aber was ist mit TWI/SPI/UART oder externen>PIN-Interrupts, die verpasst man dann evtl. doch, wenn man ab und zu die>Interrupts ausschaltet, oder habe ich da einen Denkfehler?
Das kommt darauf an wie lang deine ATOMIC_BLOCK sind.
Wenn du da Unsinn machst wie LCD Ausgaben oder printf()
Dann kannst du auch beim Timer Probleme bekommen.
Halt den Kram kurz der im ATOMIC_BLOCK steht und du hast auch
keine Probleme mit andere Peripherie.
McLovin schrieb:> Manchmal muss man verhindern, dass sich main-Code und ISR in die Quere> kommen, beispielsweise wenn man mit int16_t arbeitet, da die zur> Behandlung immer mindestens zwei Anweisungen brauchen.>> Es wird dann immer die Vorgehensweise mittels cli und sei bzw. die> Methoden aus util/atomic.h empfohlen, also>>
1
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
2
>// Kritische Sektion, die in einem Zug abgearbeitet werden soll
3
>}
>> Meine Frage wäre jetzt, ob man dadurch nicht evtl. wichtige Updates> verpasst? Bei einem Timer ist es vielleicht noch OK, weil der dann eben> später noch mal auslöst, aber was ist mit TWI/SPI/UART oder externen> PIN-Interrupts, die verpasst man dann evtl. doch, wenn man ab und zu die> Interrupts ausschaltet, oder habe ich da einen Denkfehler?
Verpasst wird zumindest der 1. Interrupt pro Quelle nicht, da das
Interrupt-Flag gesetzt wird und somit die ISR sofort nach dem Ende
deines Atomic Blocks angesprungen wird. Er wird natürlich dann etwas
verzögert ausgeführt und weitere Interrupts werden natürlich auch nicht
erfasst.
Einen Tod musst du sterben, entweder sofortige Reaktion auf Interrupts
oder Unterbrechungsfreien Code.
gruß cyblord
OK, also wenn ich nur eine Variable vergleiche und auf 0/1 setze sind
das zusammen mit abschließendem sei() ca. 8 Taktzyklen, ich denke mal
das ist schnell genug.
Das mit dem 1. Interrupt ist schon mal gut zu wissen, das Datenblatt vom
ATmega8 ist zu dem ganzen Thema relativ knapp gehalten. Das heißt,
selbst wenn in den 8 Taktzyklen ein Timer überläuft, wird die Timer-ISR
direkt nach dem Aufruf von sei() ausgeführt?
McLovin schrieb:> Manchmal muss man verhindern, dass sich main-Code und ISR in die Quere> kommen, beispielsweise wenn man mit int16_t arbeitet, da die zur> Behandlung immer mindestens zwei Anweisungen brauchen.>> Es wird dann immer die Vorgehensweise mittels cli und sei bzw. die> Methoden aus util/atomic.h empfohlen, also>>
1
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
2
>// Kritische Sektion, die in einem Zug abgearbeitet werden soll
3
>}
>> Meine Frage wäre jetzt, ob man dadurch nicht evtl. wichtige Updates> verpasst?
Das ist einerseits möglich, andererseits leider aber auch unvermeidbar.
Man kann die Gefahr nur minimieren, indem man an allen Fronten die
Phasen exclusiver Codeausführung minimiert. Dazu gehören neben den
expliziten cli..sei-Konstrukten insbesondere auch die ISRs, denn die
implizieren ein cli..sei.
> Bei einem Timer ist es vielleicht noch OK, weil der dann eben> später noch mal auslöst, aber was ist mit TWI/SPI/UART oder externen> PIN-Interrupts, die verpasst man dann evtl. doch
Du hast das nicht ganz verstanden. Wirklich verpasst wird nur dann
etwas, wenn eine Sperre lange genug anhält, um ZWEI Ereignisse einer
Kategorie zu verpassen. Nur ein Ereignis während der Sperre ist ein
kleineres Problem. Das wird nicht verpaßt, sondern nur verspätet (nach
Ende der Sperre) bearbeitet.
>OK, also wenn ich nur eine Variable vergleiche und auf 0/1 setze sind>das zusammen mit abschließendem sei() ca. 8 Taktzyklen, ich denke mal>das ist schnell genug.
Das geht schneller. Kopiere im ATOMIC_BLOCK einfach die gewünschte
Variable in eine temporäre Variable. Vergleichen und irgendwas auf
0/1 setzen und sonstiger Klimbim dann ausserhalb des ATOMIC_BLOCK.
Aus dem handbuch des ATMega16:
"There are basically two types of interrupts. The first type is
triggered by an event that sets the interrupt flag. For these
interrupts, the Program Counter is vectored to the actual Interrupt
Vector in order to execute the interrupt handling routine, and hardware
clears the corresponding interrupt flag. Interrupt flags can also be
cleared by writing a logic one to the flag bit position(s) to be
cleared. If an interrupt condition occurs while the
corresponding interrupt enable bit is cleared, the interrupt flag will
be set and remembered until the interrupt is enabled, or the flag is
cleared by software. Similarly, if one or more interrupt conditions
occur while the Global Interrupt Enable bit is cleared, the
corresponding interrupt flag(s) will be set and remembered until the
global interrupt enable bit is set, and will then be executed by order
of priority.
The second type of interrupts will trigger as long as the interrupt
condition is present. These interrupts do not necessarily have interrupt
flags. If the interrupt condition disappears before the interrupt is
enabled, the interrupt will not be triggered."
Das heißt die erste Gruppe an Interrupts verpaßt du nicht, auch wenn du
atomare operationen verwendest, da der Chip sich das Flag merkt bis es
abgearbeitet wird. Du mußt aber dafür sorgen, daß die Verzögerungen bei
zeitkritischen Ereignissen nicht zu groß werden (beispielsweise
Stichwort: Pufferüberlauf).
Die zweite Gruppe von Ereignissen könntest Du theoretisch verpassen. Das
wäre allerdings ein generelles Problem und nicht an die Thematik
"atomic" gebunden. Nicht nur Atomics deaktivieren kurzfristig
Interrupts. Sie tun dies wie Du sagst per entsprechender
Prozessorbefehle im Code. Auch ISR tun dies, aber automatisch in
Hardware. Solange eine ISR läuft können keine weiteren Interrupts
ausgelöst werden, es sei denn man implementiert nested Interrupts. Aber
auch da gibt es eine kurze Blockadezeit.
Wenn das kritisch ist mußt Du dein Konzept anpassen, sei es in Software
oder in Hardware, so daß kurze "black-outs" der Interrupts tollerierbar
sind oder anderwertig gespeichert und überbrückt werden. Alternativ kann
man auch alle konkurrierenden Interrupts maskieren. Aber will man das?
McLovin schrieb:> Das heißt,> selbst wenn in den 8 Taktzyklen ein Timer überläuft, wird die Timer-ISR> direkt nach dem Aufruf von sei() ausgeführt?
Ja, es sei denn es ist noch ein anderer Interrupt mit höherer Priorität
aktiviert und ausgelöst worden und wartet auf seine Ausfhrung. Dann
kommt der Timer erst danach. Die Abarbeitung der Interruptflags erfolgt
nicht chronologisch, sondern nach Prioritäten. Die Prioritäten
entsprechen beim AVR der Reihenfolge der Vektoren. Reset hat also die
höchste Priorität. Was darunter steht hat eine niedrigere Priorität.
Theoretisch könnte so ein Interrupt also lange bis ewig warten.
Ein Grund mehr ISRs genau wie atomics immer möglichst kurz halten. Je
länger die Routinen sind, um so höher ist die Wahrscheinlichkeit, daß
sich solche Ereignisse zumindest vorrübergehend Verketten können, was zu
schwer bis gar nicht nachvollziehbaren Verzögerungen der Ausführung
führen kann.