Forum: Mikrocontroller und Digitale Elektronik Unterprogrammaufruf in ISR


von Jan K. (jan_k)


Lesenswert?

Hi Leute,

spricht etwas dagegen, in einer ISR eine weitere Unterfunktion 
aufzurufen? Wirkt sich das Register Pushen auf die Performance aus? Ich 
benutze einen STM32 @ 56Mhz.

Sinn der Übung ist, meine ISR Handler in einer file zu haben, damit ich 
eine Übersicht habe. In den anderen c Dateien, die natürlich thematisch 
getrennt sind befindet sich die eigentliche Funktion zur Abarbeitung der 
ISR. Das hätte auch den Vorteil, dass ich alle (DMA) Puffer, Flags, 
Semaphoren etc, die ich sonst global machen müsste im lokalen Scope 
halten könnte, was ebenfalls der Übersicht dienlich ist.

Danke und schöne Grüße,
Jan

von Arne Maximilian R. (arnemaximilian_r)


Lesenswert?

Moin Jan,

ja es spricht was dagegen. Es bleibt normal nicht bei den Pushen der 
paar Register, sondern die CPU braucht auch ein paar Takte um den Sprung 
auszufuehren (ueber 10 wenn ich mich nicht irre). Auch steht die Aussage 
im Raum, dass man eine ISR ja so kurz wie nur moeglich halten sollte.

Am Ende musst du aber wissen, wie kritisch deine ISR ist. Ich zweifle 
auch daran, dass du wirklich einen Schaden dadurch erleidest, wenn du es 
machst und es gibt auch genug Programme, bei denen unzaehlige 
Funktionsaufrufe in den ISRs sind.

von Umsonst (Gast)


Lesenswert?

Sagen wir mal so: Der kluge Mensch vermeidet das, wie der Teufel das 
Weihwasser.

Die übliche Technik ist, in der ISR ein Flag zu setzen und auf dieses 
Flag in einer Schleife in main-Funktion zu reagieren. So erhälst Du auch 
die von Dir gewünschte organisatorische Aufteilung.

Das hat eine Reihe von Gründen, die zu erläutern hier zu umfangreich 
würde. Dennoch findest Du hier bestimmt eine Menge Threads dazu.

von Arne Maximilian R. (arnemaximilian_r)


Lesenswert?

Umsonst schrieb:
> Sagen wir mal so: Der kluge Mensch vermeidet das, wie der Teufel das
> Weihwasser.
>
> Die übliche Technik ist, in der ISR ein Flag zu setzen und auf dieses
> Flag in einer Schleife in main-Funktion zu reagieren. So erhälst Du auch
> die von Dir gewünschte organisatorische Aufteilung.
>
> Das hat eine Reihe von Gründen, die zu erläutern hier zu umfangreich
> würde. Dennoch findest Du hier bestimmt eine Menge Threads dazu.

Also mal davon abgesehen, dass es unmengen an Gruenden dafuer gibt es 
nicht zu machen (ich gebe dir Recht, damit kann man Threads fluten), ist 
es nicht unbedingt die "uebliche Technik". Gaengige Betriebssysteme fuer 
MCUs bieten sogar fuer die Queue Funktionen spezielle Funktionsaufrufe 
fuer ISRs an, die einen Aufruf ausloesen. Teilweise moechte man auch das 
ISR verhalten haben und lagert den Code daher nicht in die Main aus. Es 
haengt also stark davon ab, welche Art der parallelen Programmierung man 
gewaehlt hat und wie gut man damit umgehen kann.

Btw.: Es gibt auch genuegend Software, die sogar im medizinischen 
Bereich eingesetzt wird, bei der die ISR erst eine Funktion aufruft, die 
dann die naechste Funktion aufruft und erst dann wird das Problem 
geloest. Wenn man das jedoch macht sollte man sich mit dem Timing sicher 
sein.

von Jan K. (jan_k)


Lesenswert?

Umsonst schrieb:
> Sagen wir mal so: Der kluge Mensch vermeidet das, wie der Teufel das
> Weihwasser.
>
> Die übliche Technik ist, in der ISR ein Flag zu setzen und auf dieses
> Flag in einer Schleife in main-Funktion zu reagieren. So erhälst Du auch
> die von Dir gewünschte organisatorische Aufteilung.
>
> Das hat eine Reihe von Gründen, die zu erläutern hier zu umfangreich
> würde. Dennoch findest Du hier bestimmt eine Menge Threads dazu.

Naja, die übliche Technik ist, das in der ISR zu bearbeiten/zu triggern, 
was eben dann bearbeitet werden muss. Ich setze ja auch flags bzw 
inkrementiere meine Semaphore, aber ich muss immerhin den DMA Puffer 
resetten, den Counter neu setzen usw. Und mMn spricht auch nix dagegen, 
Kleinigkeiten in der ISR zu berechnen (z.B. ein Werte des Puffers 
addieren und in einen "echten" fifo schreiben), so spart man nämlich 
letztendlich Zeit, weil statt z.B. 10 Werten nur einer in den FIFO 
geschrieben werden muss.

Und ja es gibt ein Haufen Threads hier, aus denen man sich so ziemlich 
jede Meinung zusammenreimen kann. Konkret meine Frage habe ich aber 
nicht gefunden.

Es ist ein komplexes Programm, mit ein paar Flags ist es nicht getan, 
die leicht schlechtere Performance nehme ich für eine bessere Übersicht 
gerne in kauf.

Mich würde nur interessieren, ob irgendwas auftreten kann, an das ich 
nicht gedacht habe.

von Arne Maximilian R. (arnemaximilian_r)


Lesenswert?

Jan K. schrieb:
> Mich würde nur interessieren, ob irgendwas auftreten kann, an das ich
> nicht gedacht habe.

Durch die Zeit, die du mehr in der ISR verbringst kann deine ISR durch 
eine mit hoeherer Prioritaet unterbrochen werden und wenn eine ISR mit 
niedriegerer Prioritaet zweimal ausgeloest werden wuerde wirst du 
nurnoch einmal davon sehen. Das sind so die Kernpunkte.
Ansonsten werden die ISRs aber auch gerne fuer eine Funktion als 
Betriebssystem verwendet und dann wird richtig viel Code in die ISR 
gepackt. Ich komme jetzt leider nicht auf dem Namen. Jedoch gibt es 
Strategien, wie du bei zeitlich diskreten Systemen fast den kompletten 
Code in die ISRs deiner Timer legst und die Main sich nur am Hintern 
kratzt. Von daher wuerde es mich wundern, wenn du dir damit einen 
Schaden einfaengst (solange du die ersten Punkte im Hinterkopf hast).

von Umsonst (Gast)


Lesenswert?

Hm. Naja. Was ist dann "üblich"?
Ich habe z.B. kein Betriebssystem vorausgesetzt. So gesehen, ist bei 
Hinzuziehung oder Absehen von beliebigen Aspekten, jede Aussage 
relativierbar, falsifizierbar oder eben auch zu bestätigen.

Wenn der TO soetwas nicht festlegt und eine Rundumfrage abschiesst:
>Mich würde nur interessieren, ob irgendwas auftreten kann, an das ich
>nicht gedacht habe.
, dann muss er eben damit leben, das er nur eine relative Antwort erhält 
(und der Antworter, dass er widerlegt wird).

Jan K. schrieb:
>Ich setze ja auch flags bzw inkrementiere meine Semaphore, aber ich muss 
>immerhin den DMA Puffer resetten, den Counter neu setzen usw.

Gut dann mach das so. Es gibt vermutlich auch Fälle wo es nicht anders 
geht. Aber eben auch mindestens soviele in denen es auch nur mit einem 
Flag geht, auf das woanders reagiert wird.

>Mich würde nur interessieren, ob irgendwas auftreten kann, an das ich
>nicht gedacht habe.
Sicher. Da kann "irgendwas" auftreten, an das Du nicht gedacht hast.

von Dr. Sommer (Gast)


Lesenswert?

Arne Maximilian R. schrieb:
> ja es spricht was dagegen. Es bleibt normal nicht bei den Pushen der
> paar Register, sondern die CPU braucht auch ein paar Takte um den Sprung
> auszufuehren (ueber 10 wenn ich mich nicht irre).
Quatsch, beim Cortex-M4 (STM32F4) sind es 1-4 Takte je nach 
Momentanzustand der Pipeline. Dank Link-Register sind Funktionsaufrufe 
da sehr effizient. Wenn in der aufrufenden Funktion ( = die ISR) keine 
Register gesichert werden müssen (z.B. weil sie nur den Aufruf enthält) 
erfolgen auch keine PUSH'es. Wenn man Optimierungen (insb. LTO) 
einschaltet und die ISR nur einen Funktionsauruf enthält, wird die 
aufgerufene Funktion vom Linker in die ISR kopiert und der "Aufwand" 
entfällt komplett.
Da der Cortex außerdem Interrupt-Prioritäten hat kann man wichtigeren 
Interrupts höhere Prioritäten geben und somit das "Blockieren" des Cores 
durch langwierige ISR's vermeiden. Somit ist es eigentlich ziemlich 
unnötig, die Verarbeitung in die main() umzulagern oder gar auf 
Funktionsaufrufe zu verzichten.
Operationen wie in FIFO's schreiben/lesen, Pins togglen etc müssen ja 
wohl so oder so ausgeführt werden, und ob das jetzt in der ISR oder in 
der main() passiert ist doch wurst, da kann man es auch in der ISR 
machen wenn man sich dadurch die Fummelei mit dem Flag oder gar einer 
Event Queue spart.
Was man nur vermeiden sollte ist in der ISR auf externe Ereignisse zu 
warten (= den Core lahmzulegen) wie abgeschlossene serielle Transfers 
z.B. über UART.

von Arne Maximilian R. (arnemaximilian_r)


Lesenswert?

Dr. Sommer schrieb:
> Quatsch, beim Cortex-M4 (STM32F4) sind es 1-4 Takte je nach
> Momentanzustand der Pipeline. Dank Link-Register sind Funktionsaufrufe
> da sehr effizient.

Ok, da habe ich irgendwas verdreht. Danke noch einmal fuer die 
Aufklaerung.

Dr. Sommer schrieb:
> Da der Cortex außerdem Interrupt-Prioritäten hat kann man wichtigeren
> Interrupts höhere Prioritäten geben und somit das "Blockieren" des Cores
> durch langwierige ISR's vermeiden.

Das wurde hingegen schon beschrieben. ;)

War da nicht der generelle Tenor:
Kurze und haeufige ISRs hohe Prioritaeten und je laenger die ISRs werden 
niedrigere Prioritaeten.

von Ulrich (Gast)


Lesenswert?

Eine Funktionsaufruf erzeugt ggf. etwas Overhead, insbesondere werden 
oft mehr Register benötigt. Ein wirkliches Ausschlusskriterium ist das 
aber nicht. Je nach Konstellation kann der Compiler viel wegoptimieren, 
insbesondere wenn die Funktion nur das eine mal in der SIR benutzt wird 
- allerdings geht das gerade dann schlecht, wenn die Funktion in einem 
anderen File (.c) steht.

Ob man eine oder mehrere Funktionen aufruft macht dann keinen großen 
Unterschied mehr. Der größte Punkt ist das man einmal die zusätzlichen 
Register braucht. Je nach µC braucht das relativ viel Zeit die zu Retten 
- Beim ARM sollte das aber nicht so schlimm sein wie etwa beim AVR 
(relativ viele kleine Register).

Bei Funktionsaufrufen muss man ggf. noch aufpassen ob die Funktionen 
reentrant sind, also mehrmals gleichzeitig laufen dürfen.

Was gemacht werden muss, kann man auch in der ISR erledigen. Die 
Verlagerung ins Hauptprogramm verursacht nur zusätzlichen Overhead. Nur 
warten sollte man in der ISR eher nicht.

von Peter D. (peda)


Lesenswert?

Die Forderung, sämtliche Interrupts kurz zu halten, ist nur bei MCs ohne 
Prioritäten akut, z.B. AVR.

Schon der 8051 hatte 2 oder 4 Prioritäten, da ist das dann deutlich 
entspannter.

Wichtig ist aber, daß die aufgerufenen Funktionen reentrant sein müssen.

von prio (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Die Forderung, sämtliche Interrupts kurz zu halten, ist nur bei MCs ohne
> Prioritäten akut, z.B. AVR.

Natürlich hat auch der AVR Interruptprioritäten.
Aber die haben rein gar nichts damit zu tun, ob ein Interrupthandler 
kurz oder lang sein soll. Auf keiner CPU.

von Route_66 (Gast)


Lesenswert?

prio schrieb:
> Natürlich hat auch der AVR Interruptprioritäten.

Hallo!
Peter meinte: sich gegenseitig unterbrechbare Interrupts.

von Falk B. (falk)


Lesenswert?

Aka verschachtelte Interrupts, neudeutsch nested interrupts.

http://www.mikrocontroller.net/articles/Interrupt#Verschachtelte_Interrupts

von Peter D. (peda)


Lesenswert?

prio schrieb:
> Natürlich hat auch der AVR Interruptprioritäten.
> Aber die haben rein gar nichts damit zu tun, ob ein Interrupthandler
> kurz oder lang sein soll. Auf keiner CPU.

Beim AVR nicht, aber beim 8051 schon.
Z.B. der AT89C51CC03 hat 4 Interrupt Priority Register, damit kann man 
jeder Interruptquelle eine von 4 Prioritäten zuweisen:

"Each interrupt source can also be individually programmed to one of 
four priority levels by setting or clearing a bit in the Interrupt 
Priority registers. A low-priority interrupt can be interrupted by a 
high priority interrupt but not by another low-priority interrupt. A 
high-priority interrupt cannot be interrupted by any other interrupt 
source.
If two interrupt requests of different priority levels are received 
simultaneously, the request of the higher priority level is serviced. If 
interrupt requests of the same priority level are received 
simultaneously, an internal polling sequence determines which request is 
serviced. Thus within each priority level there is a second priority 
structure determined by the polling sequence, see Table 109."

Und dann kann man auch lange Interrupts haben, weil die von denen mit 
höherer Priorität unterbrochen werden können.
Man muß also nicht sämtliche Interrupts kurz halten.

von Davis (Gast)


Lesenswert?

1
// Initialisierungen
2
3
while (1)
4
  {
5
   __WFI();
6
  };

Auszug aus einer main() eines STM32F103C8. Alle anderen Routinen (USB, 
LCD, SD-Card, etc.) sind ISRs oder werden von diesen aufgerufen.

von Peter D. (peda)


Lesenswert?

Davis schrieb:
> Alle anderen Routinen (USB,
> LCD, SD-Card, etc.) sind ISRs oder werden von diesen aufgerufen.

Ja, sowas sieht man von Anfängern häufig.
Es gibt aber keinen Grund, auf den klaren und eindeutigen Ablauf einer 
Mainloop zu verzichten.

Das Problem bei Interrupts ist die völlig zufällige Abfolge.

Z.B. Du willst einer Temperatur einer Meßstelle auf dem LCD ausgeben.
Du gibts also die Nummer der Meßstelle aus. Nun willst Du den Wert 
ausgeben, aber genau dazwischen haut Dir die ADC-Routine und gibt schon 
den nächsten Wert aus. Und schon hast Du eine falsche Anzeige auf dem 
LCD.

von Dr. Sommer (Gast)


Lesenswert?

Ulrich schrieb:
> Eine Funktionsaufruf erzeugt ggf. etwas Overhead, insbesondere werden
> oft mehr Register benötigt. Ein wirkliches Ausschlusskriterium ist das
> aber nicht.
Aber nur wenn sie auch tatsächlich benötigt werden, wenn da nur ein 
Funktionsaufruf steht passiert da gar nix mit Registern.

Ulrich schrieb:
> - allerdings geht das gerade dann schlecht, wenn die Funktion in einem
> anderen File (.c) steht.
Das geht ganz wunderbar, wenn man einen neuen GCC nimmt (>= 4.7.3) und 
mit -flto kompiliert & linkt.

Ulrich schrieb:
> Der größte Punkt ist das man einmal die zusätzlichen
> Register braucht.
Was für Register braucht man für einen Funktionsaufruf???

Peter Dannegger schrieb:
> Es gibt aber keinen Grund, auf den klaren und eindeutigen Ablauf einer
> Mainloop zu verzichten.
Doch, die Fummelei Flags zu setzen und darauf zu warten. Und natürlich 
wenn man mehrere gleichzeitige Abläufe hat, und nicht nur ADC->LCD; dann 
bräuchte man schon "richtiges" Multithreading...

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.