Forum: Compiler & IDEs Abschätzung Rechenzeitverbrauch einer ISR


von c-ler (Gast)


Lesenswert?

Moinsen,

ich hätte eine Frage, bzw eher die Bitte um eine kurze Abschätzung.

Ich habe einen Timerinterrupt, der alle 100us auftritt.

Dieser schaut ungefähr so aus:
1
ISR (TIMER0_COMPA_vect)
2
{
3
    counter++;
4
    if (counter == BESTIMMTER_WERT)
5
    {
6
        counter = 0;
7
        //tu etwas
8
    }
9
}

D.h. die meißte Zeit hat die ISR nicht viel zu tun, lediglich der Sprung 
und Rücksprung von und zur ISR findet statt sowie das inkrementieren 
einer 8 Bit Variable.

Alle heilige Zeit muss die ISR bisl mehr machen, aber das interessiert 
mich eher weniger.
Es ist eher das Kleinviech, welches hier wahrscheinlich Mist macht.

Kann jemand ungefähr abschätzen, wieviel Takte der Aufruf, der Vergleich 
und das Inkrementieren kosten?
Ich kenne mich leider mit Assembler eher weniger aus. Mir wäre schon 
geholfen wenn mir jemand erläutern könnte wie ich es selbst herausfinden 
kann.

Nur als Beispiel um was es mir geht:
Angenommen die ISR bräuchte 30 Takte (dieser Wert ist nur ein Schuss ins 
Blaue, ich wüsst nicht mal in welchen Größenordnungen ich mich bewege).
Dann hätte ich bei 20 MHz Takt und 100us ISR Intervall einen "Verlust" 
von 1.5%. Damit könnte man wohl leben.
(Wobei bei niedriger Taktfrequenz der "Verlust" sehr schnell ansteigen 
würde).

Kann jemand also ungefähr abschätzen, was diese ISR so braucht?

Danke und viele Grüße,
ein (noch) c-ler :-)

von c-ler (Gast)


Lesenswert?

Und gleich noch ein Entschuldigung hinterher, natürlich das Wichtigste 
vergessen:

ATmega64 @ 20MHz
avr-gcc

von tt2t (Gast)


Lesenswert?

Der Compiler macht aus C erst mal Assembler, das Listing endet mit .lst 
(glaube ich, kann gerade nicht nachsehen, da bei der Arbeit). Dann 
schaust Du ins Datenblatt, wie lange die einzelnen Befehle dauern.

von Bananen Joe (Gast)


Lesenswert?

Oder einfach mit AVR-Stduio simulieren.

von c-ler (Gast)


Lesenswert?

Bananen Joe schrieb:
> Oder einfach mit AVR-Stduio simulieren.

Ich arbeite unter Linux :/

Hier ist mal das Listing der ISR:
1
00000238 <__vector_13>:
2
 238:  1f 92         push  r1
3
 23a:  0f 92         push  r0
4
 23c:  0f b6         in  r0, 0x3f  ; 63
5
 23e:  0f 92         push  r0
6
 240:  0b b6         in  r0, 0x3b  ; 59
7
 242:  0f 92         push  r0
8
 244:  11 24         eor  r1, r1
9
 246:  2f 93         push  r18
10
 248:  3f 93         push  r19
11
 24a:  4f 93         push  r20
12
 24c:  5f 93         push  r21
13
 24e:  6f 93         push  r22
14
 250:  7f 93         push  r23
15
 252:  8f 93         push  r24
16
 254:  9f 93         push  r25
17
 256:  af 93         push  r26
18
 258:  bf 93         push  r27
19
 25a:  ef 93         push  r30
20
 25c:  ff 93         push  r31
21
 25e:  80 91 12 01   lds  r24, 0x0112
22
 262:  8f 5f         subi  r24, 0xFF  ; 255
23
 264:  80 93 12 01   sts  0x0112, r24
24
 268:  80 91 12 01   lds  r24, 0x0112
25
 26c:  89 31         cpi  r24, 0x19  ; 25
26
 26e:  b0 f0         brcs  .+44       ; 0x29c <__vector_13+0x64>
27
 270:  10 92 12 01   sts  0x0112, r1
28
 274:  80 91 13 01   lds  r24, 0x0113
29
 278:  82 30         cpi  r24, 0x02  ; 2
30
 27a:  69 f0         breq  .+26       ; 0x296 <__vector_13+0x5e>
31
 27c:  83 30         cpi  r24, 0x03  ; 3
32
 27e:  71 f4         brne  .+28       ; 0x29c <__vector_13+0x64>
33
 280:  80 91 6f 00   lds  r24, 0x006F
34
 284:  8d 7f         andi  r24, 0xFD  ; 253
35
 286:  80 93 6f 00   sts  0x006F, r24
36
 28a:  e0 91 28 01   lds  r30, 0x0128
37
 28e:  f0 91 29 01   lds  r31, 0x0129
38
 292:  09 95         icall
39
 294:  03 c0         rjmp  .+6        ; 0x29c <__vector_13+0x64>
40
 296:  84 e0         ldi  r24, 0x04  ; 4
41
 298:  80 93 13 01   sts  0x0113, r24
42
 29c:  ff 91         pop  r31
43
 29e:  ef 91         pop  r30
44
 2a0:  bf 91         pop  r27
45
 2a2:  af 91         pop  r26
46
 2a4:  9f 91         pop  r25
47
 2a6:  8f 91         pop  r24
48
 2a8:  7f 91         pop  r23
49
 2aa:  6f 91         pop  r22
50
 2ac:  5f 91         pop  r21
51
 2ae:  4f 91         pop  r20
52
 2b0:  3f 91         pop  r19
53
 2b2:  2f 91         pop  r18
54
 2b4:  0f 90         pop  r0
55
 2b6:  0b be         out  0x3b, r0  ; 59
56
 2b8:  0f 90         pop  r0
57
 2ba:  0f be         out  0x3f, r0  ; 63
58
 2bc:  0f 90         pop  r0
59
 2be:  1f 90         pop  r1
60
 2c0:  18 95         reti

Also wie gesagt, in Sachen asm bin ich eher unbewandert, aber auf den 
ersten Blick schauts so aus als machen die PUSHs und POPs, die ja immer 
notwendig sind, den größten Teil der ISR ausmachen (die laut Datenblatt 
leider 2 Takte brauchen).

Was ab und zu nur ausgeführt wird ist der Teil zwischen 270: und 29c:

Ich muss also damit rechnen, dass meine ISR immer ca. 80 Takte braucht, 
sehe ich das richtig?

von Peter II (Gast)


Lesenswert?

c-ler schrieb:
> schauts so aus als machen die PUSHs und POPs, die ja immer
> notwendig sind, den größten Teil der ISR ausmachen (die laut Datenblatt
> leider 2 Takte brauchen).

eigentlich sind sie nicht notwendig, ist der C quellcode genau das was 
in der ISR steht oder nicht?

von Karl H. (kbuchegg)


Lesenswert?

Am genauesten ist immer noch ein Blick ins Assembler-Listing bzw. im 
Simulator einmal durchsteppen.

Aber so über den Daumen gepeilt:
Da der AVR eine 8 Bit Maschine ist und die 8-Bit Variablen benutzt.

  counter++;

das ist ein Inkrement, den kann die CPU mit einem eigenen Befehl 
abarbeiten. Also 1 Assembler Befehl

if (counter == BESTIMMTER_WERT)

Ein Vergleich. BESTIMMTER_WERT sieht jetzt so aus, als ob das eine 
Konstante ist. Ein derartiger Vergleich ist eine Assembler-Instruktion 
für den Vergleich mit einem nachfolgenden Sprung über den Programmteil 
der vom Vergleich abhängig ist. Also 2 Assembler Instruktionen

counter = 0;

Also den Wert auf 0 setzen. Wieder 1 Assembler Instruktion.

Dazu kommt jetzt noch etwas Overhead für

den aktuellen Wert von der Variablen 'counter' im SRAM in ein Register 
laden, ehe die ganzen Manipulationen beginnen und danach wieder 
zurückschreiben.

Zusätzlich noch der Overhead, den man bei einer ISR immer hat.

Die ganzen 8-Bit Dinge in deinem Code brauchen 1 Takt pro Instruktion. 
Der Sprung braucht (aus dem Gedächtnis) länger - 2 Takte. Es gibt keinen 
Befehl, der mehr als 2 Takte braucht.

Ich würde sagen, deine 30 Takte sind da schon großzügig geschätzt. Ich 
hätte mit allem drum und drann irgendwas um die 20 Takte angenommen.

von Karl H. (kbuchegg)


Lesenswert?

>  292:  09 95         icall

Du machst da einen Funktionsaufruf in der ISR. Keine gute Idee. Das 
zwingt den Compiler ausnahmslos alle Register zu sichern, was wiederrum 
Zeit kostet.

von Karl H. (kbuchegg)


Lesenswert?

Der Teil hier
1
       80 91 12 01   lds  r24, 0x0112
2
 262:  8f 5f         subi  r24, 0xFF  ; 255
3
 264:  80 93 12 01   sts  0x0112, r24
4
 268:  80 91 12 01   lds  r24, 0x0112
5
 26c:  89 31         cpi  r24, 0x19  ; 25
6
 26e:  b0 f0         brcs  .+44       ; 0x29c <__vector_13+0x64>
7
 270:  10 92 12 01   sts  0x0112, r1

ist das, was aus deinem
1
    counter++;
2
    if (counter == BESTIMMTER_WERT)
3
    {
4
        counter = 0;
5
        ...
geworden ist. Das heftige Speichern und Laden ist der volatile Variablen 
geschuldet. Würdest du es so schreiben
1
ISR (TIMER0_COMPA_vect)
2
{
3
    uint8_t tmpCounter = counter;
4
5
    tmpCounter ++;
6
    if (tmpCounter == BESTIMMTER_WERT)
7
    {
8
        tmpCounter = 0;
9
        ...
10
    }
11
12
    counter = tmpCounter ;

würde auch das wahrscheinlich wegfallen.

von (prx) A. K. (prx)


Lesenswert?

Und der grössere Teil der Push/Pop-Orgie geht nicht auf den Counter 
selbst, sondern dem nicht gezeigten Rest vom Code zurück. Ein 
Funktionsaufruf bringt mit sich, dass alles gesichert werden muss, was 
dort verwendet werden könnte. Das ist hier vmtl. kaum zu vermeiden, 
erklärt aber das Ausmass.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Du machst da einen Funktionsaufruf in der ISR. Keine gute Idee.

So entstehen Gerüchte. Jene nicht so selten zu findenden, dass in einer 
ISR keinesfalls eine Funktion aufgerufen werden darf. Klar, der Aufwand 
steigt dadurch, aber obs anders wirklich einfacher und eleganter ist?

Abgesehen davon kriegst du einen generischen Timer-basierten Scheduler 
beim besten Willen nicht ohne Funktionsaufruf in der ISR zustande. Der 
ist darin der Kern der Sache.

Also bitte keine solchen pauschalen Aussagen ins Blaue schiessen.

von c-ler (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich würde sagen, deine 30 Takte sind da schon großzügig geschätzt. Ich
> hätte mit allem drum und drann irgendwas um die 20 Takte angenommen.

Das hast du aber schon geschrieben bevor ich mein Listing gepostet habe 
oder? Die ganzen PUSHs und POPs haben ja schon ca. 40 Takte :-)

Ansonsten vielen Dank an dich, sehr interessante Lektüre, ich werde mich 
wohl doch mal mehr mit asm beschäftigen, sehr interessant das ganze...


Und ja, du hast recht, im
1
//tu was
-Teil findest wirklich ein Funktionsaufruf statt.

Auskommentieren des Aufrufs verwandelt das Listing in ein hübsches
1
00000238 <__vector_13>:
2
 238:  1f 92         push  r1
3
 23a:  0f 92         push  r0
4
 23c:  0f b6         in  r0, 0x3f  ; 63
5
 23e:  0f 92         push  r0
6
 240:  11 24         eor  r1, r1
7
 242:  8f 93         push  r24
8
 244:  80 91 12 01   lds  r24, 0x0112
9
 248:  8f 5f         subi  r24, 0xFF  ; 255
10
 24a:  80 93 12 01   sts  0x0112, r24
11
 24e:  80 91 12 01   lds  r24, 0x0112
12
 252:  89 31         cpi  r24, 0x19  ; 25
13
 254:  88 f0         brcs  .+34       ; 0x278 <__vector_13+0x40>
14
 256:  10 92 12 01   sts  0x0112, r1
15
 25a:  80 91 13 01   lds  r24, 0x0113
16
 25e:  82 30         cpi  r24, 0x02  ; 2
17
 260:  41 f0         breq  .+16       ; 0x272 <__vector_13+0x3a>
18
 262:  83 30         cpi  r24, 0x03  ; 3
19
 264:  49 f4         brne  .+18       ; 0x278 <__vector_13+0x40>
20
 266:  80 91 6f 00   lds  r24, 0x006F
21
 26a:  8d 7f         andi  r24, 0xFD  ; 253
22
 26c:  80 93 6f 00   sts  0x006F, r24
23
 270:  03 c0         rjmp  .+6        ; 0x278 <__vector_13+0x40>
24
 272:  84 e0         ldi  r24, 0x04  ; 4
25
 274:  80 93 13 01   sts  0x0113, r24
26
 278:  8f 91         pop  r24
27
 27a:  0f 90         pop  r0
28
 27c:  0f be         out  0x3f, r0  ; 63
29
 27e:  0f 90         pop  r0
30
 280:  1f 90         pop  r1
31
 282:  18 95         reti

Das ist ja ne echt heftige Einsparung!

Werde wohl doch lieber ein Flag setzen und die Funktion danach aufrufen 
:-)

Danke euch nochmal!

von (prx) A. K. (prx)


Lesenswert?

c-ler schrieb:

> Werde wohl doch lieber ein Flag setzen und die Funktion danach aufrufen

Wenn das zum Programm passt, dann ist das sicherlich der effizientere 
Weg. Jetzt musst du nur noch die erwähnte Sache mit dem volatile 
angehen.

von c-ler (Gast)


Lesenswert?

In dieseml Fall genügt ein Flag, ja.

Das zwischenspeichern der volatile Variable "counter" in einer lokalen 
Variablen scheint leider nichts gebracht zu haben.
Wahrscheinlich sind diese 3 Zugriffe (Inkrementieren, Vergleichen, evtl. 
auf 0 setzen) nicht genug um da was zu verschlechtern.

Viele Grüße,

c-ler

von Falk B. (falk)


Lesenswert?

@  c-ler (Gast)

>Das zwischenspeichern der volatile Variable "counter" in einer lokalen
>Variablen scheint leider nichts gebracht zu haben.

Logisch, LESEN muss die CPU immer.

>Wahrscheinlich sind diese 3 Zugriffe (Inkrementieren, Vergleichen, evtl.
>auf 0 setzen) nicht genug um da was zu verschlechtern.

Jo.

Allgemein sollte man beim AVR-GCC keine Funktionsaufrufe in einer ISR 
machen, wenn es schnell gehen soll. Der push/pop Overhead ist enorm. Der 
Rest steht im Artikel Interrupt.

MfG
Falk

von Sam .. (sam1994)


Lesenswert?

Bringt zwar nicht viel: Zähler runter zu zählen und mit 0 zu vergleichen 
spart eine Instruktion. Sollte man sich zumindest angewöhnen.

von Rolf Magnus (Gast)


Lesenswert?

A. K. schrieb:
> Abgesehen davon kriegst du einen generischen Timer-basierten Scheduler
> beim besten Willen nicht ohne Funktionsaufruf in der ISR zustande.

Naja, etwas generisch zu machen, zieht meistens einen Overhead mit sich. 
Aber warum soll ich dazu Funktionen in einer ISR aufrufen?

> Also bitte keine solchen pauschalen Aussagen ins Blaue schiessen.

Im Hinblick darauf, daß der Fragesteller möglichst nicht viel Zeit in 
der ISR verbringen will und daß Funktionsauf, dem er quasi gar keine 
Beachtung geschenkt hat, dann doch indirekt schon für mehr Laufzeit 
verantwortlich ist, als der ganze Rest, finde ich die Aussage schon 
gerechtfertigt.

Falk Brunner schrieb:
> @  c-ler (Gast)
>
>>Das zwischenspeichern der volatile Variable "counter" in einer lokalen
>>Variablen scheint leider nichts gebracht zu haben.
>
> Logisch, LESEN muss die CPU immer.

Naja, aber mit der temporären Variable muß genau einmal gelesen und 
einmal geschrieben werden, unabhängig davon, was da alles noch 
zwischendrin damit angestellt wird.

>Wahrscheinlich sind diese 3 Zugriffe (Inkrementieren, Vergleichen, evtl.
>auf 0 setzen) nicht genug um da was zu verschlechtern.

Inkrementieren besteht aus zwei Zugriffen.

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:

> Aber warum soll ich dazu Funktionen in einer ISR aufrufen?

Beispielsweise wenn die erforderlichen Reaktionszeiten auf bestimmte 
Timer-Events im Millisekundenbereich liegen, aber in der Mainloop Zeug 
läuft, das sich nicht auf elegante Art in Häppchen garantiert unter 1ms 
zerschnippeln lässt.

Man hat dann die Wahl, den Mainloop-Code entsprechend gewaltsam zu 
zerlegen, unter dem Risiko, dass man irgendeinen Pfad übersieht, oder 
die zeitgesteuerten Aktivitäten in der ISR aufzurufen.

Oft wird der Overhead von ein paar µs pro 1ms für gesicherte Reaktion 
nicht weiter ins Gewicht fallen, während eine verpasste Reaktion fatal 
ist.

Dass diese Aktivitäten dann eher preemptime als cooperative laufen und 
man gemeinsame Resourcen in der Mainloop entsprechend beachten muss ist 
klar. Einen Tod muss man sterben.

> Naja, aber mit der temporären Variable muß genau einmal gelesen und
> einmal geschrieben werden, unabhängig davon, was da alles noch
> zwischendrin damit angestellt wird.

Es kommt zwar unterschiedlicher Code raus, es sind aber beides Mal 6 
Befehle in 8 Takten. Wird erst wirklich relevant, wenn man mit dem 
Counter noch mehr macht, als ihn nur modulo N zu inkrementieren.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
> Allgemein sollte man beim AVR-GCC keine Funktionsaufrufe in einer ISR
> machen, wenn es schnell gehen soll. Der push/pop Overhead ist enorm. Der
> Rest steht im Artikel Interrupt.

Das kann man so allgemein nicht sagen. Ist die aufgerufene Funktion 
static, weiß der Compiler genau, ob und welche Register gesichert werden 
müssen. Bei externen Funktionen hast Du natürlich recht.

Gruß,

Frank

von Peter D. (peda)


Lesenswert?

Es werden alle 12 zerstörbaren Register gesichert, das sind allein schon 
48 Zyklen. Ich würde sagen, insgesamt etwa 80 Zyklen.

Allerdings sind das nur Milchmädchenrechnungen, sobald Du mehrere 
Interrupts hast.
Es kann ja sein, daß gerade ein anderer Interrupt mit 500 Zyklen 
angefangen hat und der muß natürlich erst zuende ausgeführt worden sein.
Der längste mögliche Interrupt bestimmt also die maximale 
Interruptlatenz.


Peter

von benwilliam (Gast)


Lesenswert?

@c-ler: blöde frage aber warum kann der Timerinterrupt nicht gleich bis 
100µS * BESTIMMTER_WERT warten? wenn man alle 100µS eh erstmal nur eine 
Zählervariable inkrementiert kann man doch gleich mit dem Timer Compare 
register rumspielen oder?

von Volkmar D. (volkmar)


Lesenswert?

Peter Dannegger schrieb:
> Es kann ja sein, daß gerade ein anderer Interrupt mit 500 Zyklen
> angefangen hat und der muß natürlich erst zuende ausgeführt worden sein.
> Der längste mögliche Interrupt bestimmt also die maximale
> Interruptlatenz.

wenn man aber weiß, das ein anderer Interrupt wichtiger ist, kann man in 
solchen Fällen auch andere Interrupts während der Abarbeitung eines 
solchen längeren Interrupts zulassen und somit die Latenz verbessern.

von (prx) A. K. (prx)


Lesenswert?

benwilliam schrieb:

> @c-ler: blöde frage aber warum kann der Timerinterrupt nicht gleich bis
> 100µS * BESTIMMTER_WERT warten? wenn man alle 100µS eh erstmal nur eine
> Zählervariable inkrementiert kann man doch gleich mit dem Timer Compare
> register rumspielen oder?

Bei einer einzelnen Wartezeit ja. Oft ist es freilich so, dass man 
diverse zeitgesteuerte Abläufe hat, Uhrzeit inklusive, bei der man nicht 
jeden einzelnen mit einem Hardware-Timer versehen will. Sondern mit 
einem zentralen Timer-Tick arbeitet, von dem man alle nicht auf die 
Mikrosekunde genauen Zeiten ableitet.

Zwar kann man einen solchen Timer-Tick auch adaptiv programmieren, also 
statt in Software tote Ticks abzuzählen den Timer ad hoc 
reprogrammieren, aber da siegt gern die Faulheit. In Software ist es 
einfacher. Und wenn das nicht grad dank sehr kurzer Tick-Intervalle 
exzessiv Background-Zeit frisst, dann spielt der Unterschied oft keine 
Rolle.

von Oliver (Gast)


Lesenswert?

A. K. schrieb:
> Zwar kann man einen solchen Timer-Tick auch adaptiv programmieren, also
> statt in Software tote Ticks abzuzählen den Timer ad hoc
> reprogrammieren, aber da siegt gern die Faulheit.

und die Berechnung der nächsten Zykluszeit sowie die Auswertung, welche 
Aktion beim aktuellen Tick fällig ist, gibt es auch nicht umsonst.

Oliver

von c-ler (Gast)


Lesenswert?

benwilliam schrieb:
> @c-ler: blöde frage aber warum kann der Timerinterrupt nicht gleich bis
> 100µS * BESTIMMTER_WERT warten? wenn man alle 100µS eh erstmal nur eine
> Zählervariable inkrementiert kann man doch gleich mit dem Timer Compare
> register rumspielen oder?

Bei meiner ersten Version hab ichs so gemacht, ja.
Und dazu hab ich auch einen 16 Bit Timer verwendet.

Das ersschien mir aber als Verschwendung, mit dem kann man doch viel 
schönere Sachen machen.
Durch Ändern des Compare Wertes kann man natürlich die Zeitbasis der 
Software ändern.

Ich wollte also einen 8 Bit Timer verwenden. Dieser ist ja erstmal 
weniger flexibel was die Einstellung der Zeitbasis betrifft. Und nur mit 
dem Compare Register kommt man da auch nicht weit. Ohne Änderung des 
Prescalers kriegt man kaum einen größeren Bereich an möglichen Zeitbasen 
abgedeckt.

Deshalb verwende ich jetzt eben einen festen Prescaler (8) und einen 
festen Comparewert (250) um die Kleinstmögliche Zeitbasis (100us) zu 
generieren. Durch Ändern von BESTIMMTER_WERT kann man Vielfache bis 
100us * 255 davon ableiten.
Ich halte das für die flexibelste Lösung und auch für die einfachste. 
Außerdem wird nur ein 8 Bit Timer benötigt, und an dessen Einstellungen 
muss auch nicht herumgespielt werden.
Eine Funktion die zB eine Stoppuhr für einen bestimmten Codeabschnitt 
implementiert tut sich auch leichter (vor allem wenn diese 
Festkommaarithmetik und Divisionsoptimierung durch Shiften von 
Zweierpotenzen benutzt).

Und diese Vorteile überwieden imho den Nachteil, dass der 
"Steuer"interrupt möglicherweise öfters ausgeführt wird.
Bei 30 Takten (mittlerweile gezählt anstatt geraten :-)) sind das 1.5% 
Overhead, imho akzeptabel.

@ PeDa:
Danke für den Hinweiß mit der größtmöglichen Interruptlatenz.
In meinem Fall weniger tragisch, da der Timer ja trotzdem weiterläuft.
Solang also ein anderer IR nicht über 100us frisst geht mir nichts 
verloren :-)

@ Allgemeinheit: Zwar bisl Offtopic:
In welchen Größenordnungen bewegt sich den üblicherweise der Overhead, 
denn man bei Betriebssystem, Schedulern oder 
was-auch-immer-für-Tasksteuerung in Kauf nimmt?

Grüße,
der c-ler

von Peter D. (peda)


Lesenswert?

Volkmar Dierkes schrieb:
> wenn man aber weiß, das ein anderer Interrupt wichtiger ist, kann man in
> solchen Fällen auch andere Interrupts während der Abarbeitung eines
> solchen längeren Interrupts zulassen und somit die Latenz verbessern.

Leider sind aber die langen Interrupts ausgerechnet immer solche, die 
ihr Flag nicht beim Eintritt löschen, z.B. UART-, SPI- oder 
I2C-Protokoll-Parser.
Das ISR_NOBLOCK beim AVR-GCC würde dann sofort den Stack fluten, geht 
also nicht.

Also hilft nur, alles Lange ins Main auslagern. Den Luxus langer 
Interrupts kann man sich nur bei MCs mit konfigurierbaren 
Interruptleveln leisten.


Peter

von (prx) A. K. (prx)


Lesenswert?

c-ler schrieb:

> In welchen Größenordnungen bewegt sich den üblicherweise der Overhead,
> denn man bei Betriebssystem, Schedulern oder
> was-auch-immer-für-Tasksteuerung in Kauf nimmt?

Ein "üblicherweise" gibt es nicht. Das hängt vom Betriebsystem (oder 
RTOS, Realtime-Kernel, Scheduler), der eingesetzten Hardware und der 
Länge des Timer-Ticks ab.

Ich habe mal einen 16MHz AVR mit einem Realtime-Kernel mit von mir recht 
kurz definierten 100µs Ticks gefahren, weil der damit auch für manche 
1-Wire Delays taugte. Da gingen zwar 10% für den Tick drauf, das störte 
aber nicht weiter.

von Volkmar D. (volkmar)


Lesenswert?

Peter Dannegger schrieb:
> Volkmar Dierkes schrieb:
>> wenn man aber weiß, das ein anderer Interrupt wichtiger ist, kann man in
>> solchen Fällen auch andere Interrupts während der Abarbeitung eines
>> solchen längeren Interrupts zulassen und somit die Latenz verbessern.
>
> Leider sind aber die langen Interrupts ausgerechnet immer solche, die
> ihr Flag nicht beim Eintritt löschen, z.B. UART-, SPI- oder
> I2C-Protokoll-Parser.
> Das ISR_NOBLOCK beim AVR-GCC würde dann sofort den Stack fluten, geht
> also nicht.

In diesem Fall würde ich auch nicht die ISR_NOBLOCK Option verwenden, 
sondern es von Hand durchführen. Also erst den Interrupt der langen ISR 
sperren und dann die Interrupts wieder freigeben. Somit wird die ISR 
nicht mehrfach aufgerufen. Am Ende der ISR muß dann der zugehörige 
Interrupt wieder erlaubt werden. Aber es ist nur ein letzter Notnagel, 
wenn die HW in dieser Hinsicht eingeschränkt ist.

Volkmar

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.