Mahlzeit!
Bei den Cortex-M sind die Interrupts nach einem Reset eingeschaltet. Ob
sich ISR gegenseitig unterbrechen können, wird per Priorität geregelt.
Natürlich kann man wie immer jede Interrupt-Quelle einzeln einschalten.
Vielleicht möchte man per Watchdog einen Reset auslösen und vorher
alle Interrupts ausschalten - dafür braucht man eine irq_disable()
Funktion.
Aber gibt es eine vernünftige Verwendung für irq_enable() in C?
(Den Maschinenbefehl cpsie i braucht man natürlich, damit man sowas wie
einen ATOMIC_BLOCK bauen kann).
Apropos BLOCK, meiner sieht so aus ;)
Dafür ist doch das atomic-Paar viel schöner. Ja, es macht exakt das
gleiche.
Meine Frage ist aufgekommen, weil "man" oft am Anfang den aktuellen
Zustand rettet und den Ende wieder herstellt. Also die Interrupts u.U.
nicht wieder einschaltet. Den Fall darf es doch niemals geben?
Bauform B. schrieb:> Aber gibt es eine vernünftige Verwendung für irq_enable() in C?
Ob du nun in C programmierst oder eine andere Programmiersprache wählst,
ist für sowas eigentlich schnurz.
Früher bei 8 Bit Maschinen war es relativ gebräuchlich, daß man
generelle Schalter für Interupts hatte und auch benutzte. Aber
heutzutage und insbesondere bei 32 Bit Maschinen sind die ehemaligen
Gründe für so etwas weitgehend weggefallen. Bei den ARM's vor der
Cortex-Generation war so etwas sogar weitestgehend verpönt und so
einiges eben unmöglich, wenn dort Usercode auch im Usermodus lief, wo
einige Privilegien wegfallen, die im Supervisormodus möglich sind. So
etwas zwang die Programmierer, sich über ihr Geschreibsel vorher mehr
Gedanken zu machen, was heutzutage im Zeitalter von copy&paste als zu
schwer empfunden wird.
Fazit: ich sehen bei den Cortexen für das Benutzen genereller
Interruptsperren in den allermeisten Fällen keinen Grund. Allenfalls bei
einigen Stellen innerhalb eines RTOS-Kernels oder ähnlichen Dingen mag
es da noch notwendig sein. Und für gewöhnliche Firmware sollte man so
etwas erst gar nicht verwenden wollen.
W.S.
W.S. schrieb:> ich sehen bei den Cortexen für das Benutzen genereller Interruptsperren> in den allermeisten Fällen keinen Grund. Allenfalls bei einigen> Stellen innerhalb eines RTOS-Kernels oder ähnlichen Dingen mag es> da noch notwendig sein.
Das ist ein gutes Stichwort, danke. Stellenweise braucht man die Sperre,
keine Frage. Ich war nur nicht auf die Idee gekommen, dass man an
solchen Stellen C benutzen will.
> Und für gewöhnliche Firmware sollte man so etwas erst gar nicht> verwenden wollen.
Sag' ich doch.
Bauform B. schrieb:> Apropos BLOCK, meiner sieht so aus ;)
Verfahrenstechnisch geschickter finde ich die Variante der avrlibc, weil
sich da die Disable/Enables in einem Statement verbergen.
(prx) A. K. schrieb:> Verfahrenstechnisch geschickter finde ich die Variante der avrlibc, weil> sich da die Disable/Enables in einem Statement verbergen.
Ja, ich schau auch ganz neidisch in die Richtung. Meine Schreibweise
soll eben so ähnlich aussehen, aber ohne so ein Macro-Monster auskommen
;)
Bauform B. schrieb:> Ich war nur nicht auf die Idee gekommen, dass man an> solchen Stellen C benutzen will.
Ich auch nicht. Aber es gibt hier ja Leute, die außer C nix können.
W.S.
Bauform B. schrieb:> Aber gibt es eine vernünftige Verwendung für irq_enable() in C?
Manchmal will man erst die komplette Peripherie initialisieren, bevor
die erste ISR aufgerufen wird, z.B. um in einem Debug-Build stderr
bereitzustellen.
W.S. schrieb:> Allenfalls bei> einigen Stellen innerhalb eines RTOS-Kernels oder ähnlichen Dingen mag> es da noch notwendig sein.(prx) A. K. schrieb:> Noch schöner ist freilich oft die Variante über BASEPRI: unsigned> saved = BASEPRI;> BASEPRI_MAX = <level>;> ...> BASEPRI = saved;> weil da nicht alle Interrupts abgeschaltet werden, sondern bloss die> relevanten. Höhere kommen durch, was deren Latenz zugute kommt.
Korrekt, genau so wird es bei einigen RTOS, gemacht :-). Wenn ich im
RTOS irgendwas update, was auch durch eine ISR verändert werden kann,
muss ich Interrupts sperren. Interrupts mit höherer Priorität können
aber immer noch ausgeführt werden (heißen z.B. bei embOS Zero Latency
Interrupts). Wir merken uns aber nicht das BASEPRI sondern schreiben fix
0x00 und 0x80 in das BASEPRI. Ob wir an der Stelle Interrupts wieder
freigeben dürfen (BASEPRI = 0x00) merken wir uns über einen Task
spezifischen Counter.
Ein
OS_INT_IncDI();
OS_INT_IncDI();
OS_INT_DecRI();
würde also Interrupts nicht wieder freigeben.
Ich hoffe das war nicht zu sehr ausgeschweift ;-).
Til S. schrieb:> Interrupts mit höherer Priorität können> aber immer noch ausgeführt werden (heißen z.B. bei embOS Zero Latency> Interrupts). Wir merken uns aber nicht das BASEPRI sondern schreiben fix> 0x00 und 0x80 in das BASEPRI.
Also gibt es praktisch auch hier ein globales "alles oder nichts
Enable", aber die Zero Latency Interrupts laufen immer? Das wäre ja noch
überschaubar.
> Ob wir an der Stelle Interrupts wieder freigeben dürfen> (BASEPRI = 0x00) merken wir uns über einen Task spezifischen Counter.> Ein> OS_INT_IncDI();> OS_INT_IncDI();> OS_INT_DecRI();> würde also Interrupts nicht wieder freigeben.
Dass man das wirklich braucht... Na gut, ein richtiges RTOS mit allen
Tricks spielt doch in einer anderen Liga als das typische AVR-Programm.
Ich suche eben einen Mittelweg.
> Ich hoffe das war nicht zu sehr ausgeschweift ;-).
Keineswegs, vielen Dank.
vvBauform B. schrieb:> Also gibt es praktisch auch hier ein globales "alles oder nichts> Enable", aber die Zero Latency Interrupts laufen immer? Das wäre ja noch> überschaubar.
Im Prinzip ja, hier ist es genauer beschrieben, bei Interesse:
https://www.segger.com/doc/UM01001_embOS.html#InterruptsBauform B. schrieb:> Dass man das wirklich braucht... Na gut, ein richtiges RTOS mit allen> Tricks spielt doch in einer anderen Liga als das typische AVR-Programm.> Ich suche eben einen Mittelweg.
Das brauche ich, wenn ich Interrupts in foo() sperre und eine
Sub-Routine bar() aufrufe, die auch Interrupts sperrt und wieder frei
gibt. Die Interrupts sollen aber erst wieder am Ende von foo()
freigegeben werden.
void bar(void) {
OS_INT_IncDI();
OS_INT_DecRI(); // hier ints noch nicht wieder einschalten
}
void foo(void) {
OS_INT_IncDI();
bar();
OS_INT_DecRI(); // sondern erst hier
}
Til S. schrieb:> Das brauche ich, wenn ich Interrupts in foo() sperre und eine> Sub-Routine bar() aufrufe, die auch Interrupts sperrt und wieder frei> gibt. Die Interrupts sollen aber erst wieder am Ende von foo()> freigegeben werden.
Also ein genereller Freigabezähler. Naja, das könnte man benutzen, aber
einfacher wäre es, wenn man den Zustand erfaßt, bevor man ihn ändert.
Und dann die etwaige Rückänderung nur dann macht, wenn man sie selbst
zuvor getan hat.
Oder noch besser, wenn man es generell vermeidet, an Interruptsperren
herumzuzerren. Das geht mit ein bissel Nachdenken bei allen
Programmteilen außer evtl. bei irgendwelchen Innereien eines RTOS, wo
ich mir nicht sicher bin, ob man es dort tatsächlich braucht.
W.S.
Bauform B. schrieb:> Das ist ein gutes Stichwort, danke. Stellenweise braucht man die Sperre,> keine Frage. Ich war nur nicht auf die Idee gekommen, dass man an> solchen Stellen C benutzen will.
Ich wäre kaum auf die Idee gekommen, auf so kleinen µC überhaupt C
benutzen zu wollen...
...wenn mich nicht die Umstände dazu zwingen würden, insbesondere die
Absenz weiterer Asm-fester Programmierer in der Firma...
c-hater schrieb:> Ich wäre kaum auf die Idee gekommen, auf so kleinen µC überhaupt C> benutzen zu wollen...>> ...wenn mich nicht die Umstände dazu zwingen würden, insbesondere die> Absenz weiterer Asm-fester Programmierer in der Firma...
Tja, ich hatte mal einen jungen Kollegen, der in belehrendem Ton mir
verkündete "ich sehe keinen Grund, C nicht für einen PIC10xx zu
verwenden". Die Ansichten haben sich mittlerweile gewandelt. Man liest
auch keine Manuals mehr oder Leute hier im Forum brüsten sich mit "ich
programmiere nicht auf Registerebene".
Bei sowas kommt mir immer der Vorschlag in den Sinn, einfach ein
geeignetes Ingenieurbüro zu beauftragen.
W.S.