Forum: Compiler & IDEs Warum gibt es irq_enable() für Cortex-M?


von Bauform B. (bauformb)


Lesenswert?

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 ;)
1
atomic_begin ();
2
{
3
   unteilbar_teil1;
4
   unteilbar_teil2;
5
}
6
atomic_end();

von Kevin M. (arduinolover)


Lesenswert?

Natürlich wenn du etwas tust was nicht unterbrochen werden soll

Irq_disable()

Do something

Irq_enable()

von Bauform B. (bauformb)


Lesenswert?

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?

von omnna (Gast)


Lesenswert?

Hätte man es weggelassen würde der nächste User fragen, weshalb es so 
was nicht gibt. Ohmann .. dir ist langweilig heute am Muttertag, nicht?

von W.S. (Gast)


Lesenswert?

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.

von Bauform B. (bauformb)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Kevin M. schrieb:
> Natürlich wenn du etwas tust was nicht unterbrochen werden soll

Noch schöner ist freilich oft die Variante über BASEPRI:
1
   unsigned saved = BASEPRI;
2
   BASEPRI_MAX = <level>;
3
   ...
4
   BASEPRI = saved;
weil da nicht alle Interrupts abgeschaltet werden, sondern bloss die 
relevanten. Höhere kommen durch, was deren Latenz zugute kommt.

von (prx) A. K. (prx)


Lesenswert?

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.
1
     ATOMIC_BLOCK(<Variante>)
2
     {
3
       ...
4
     }

von Bauform B. (bauformb)


Lesenswert?

(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 
;)

von W.S. (Gast)


Lesenswert?

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.

von Walter T. (nicolas)


Lesenswert?

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.

von Til S. (Firma: SEGGER) (til_s)


Lesenswert?

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

von Bauform B. (bauformb)


Lesenswert?

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.

von Til S. (Firma: SEGGER) (til_s)


Lesenswert?

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#Interrupts

Bauform 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

}

von W.S. (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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

von W.S. (Gast)


Lesenswert?

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.

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.