www.mikrocontroller.net

Forum: GCC Abschätzung Rechenzeitverbrauch einer ISR


Important announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: c-ler (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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:
ISR (TIMER0_COMPA_vect)
{
    counter++;
    if (counter == BESTIMMTER_WERT)
    {
        counter = 0;
        //tu etwas
    }
}

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 :-)

Autor: c-ler (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Und gleich noch ein Entschuldigung hinterher, natürlich das Wichtigste 
vergessen:

ATmega64 @ 20MHz
avr-gcc

Autor: tt2t (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: Bananen Joe (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Oder einfach mit AVR-Stduio simulieren.

Autor: c-ler (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Bananen Joe schrieb:
> Oder einfach mit AVR-Stduio simulieren.

Ich arbeite unter Linux :/

Hier ist mal das Listing der ISR:
00000238 <__vector_13>:
 238:  1f 92         push  r1
 23a:  0f 92         push  r0
 23c:  0f b6         in  r0, 0x3f  ; 63
 23e:  0f 92         push  r0
 240:  0b b6         in  r0, 0x3b  ; 59
 242:  0f 92         push  r0
 244:  11 24         eor  r1, r1
 246:  2f 93         push  r18
 248:  3f 93         push  r19
 24a:  4f 93         push  r20
 24c:  5f 93         push  r21
 24e:  6f 93         push  r22
 250:  7f 93         push  r23
 252:  8f 93         push  r24
 254:  9f 93         push  r25
 256:  af 93         push  r26
 258:  bf 93         push  r27
 25a:  ef 93         push  r30
 25c:  ff 93         push  r31
 25e:  80 91 12 01   lds  r24, 0x0112
 262:  8f 5f         subi  r24, 0xFF  ; 255
 264:  80 93 12 01   sts  0x0112, r24
 268:  80 91 12 01   lds  r24, 0x0112
 26c:  89 31         cpi  r24, 0x19  ; 25
 26e:  b0 f0         brcs  .+44       ; 0x29c <__vector_13+0x64>
 270:  10 92 12 01   sts  0x0112, r1
 274:  80 91 13 01   lds  r24, 0x0113
 278:  82 30         cpi  r24, 0x02  ; 2
 27a:  69 f0         breq  .+26       ; 0x296 <__vector_13+0x5e>
 27c:  83 30         cpi  r24, 0x03  ; 3
 27e:  71 f4         brne  .+28       ; 0x29c <__vector_13+0x64>
 280:  80 91 6f 00   lds  r24, 0x006F
 284:  8d 7f         andi  r24, 0xFD  ; 253
 286:  80 93 6f 00   sts  0x006F, r24
 28a:  e0 91 28 01   lds  r30, 0x0128
 28e:  f0 91 29 01   lds  r31, 0x0129
 292:  09 95         icall
 294:  03 c0         rjmp  .+6        ; 0x29c <__vector_13+0x64>
 296:  84 e0         ldi  r24, 0x04  ; 4
 298:  80 93 13 01   sts  0x0113, r24
 29c:  ff 91         pop  r31
 29e:  ef 91         pop  r30
 2a0:  bf 91         pop  r27
 2a2:  af 91         pop  r26
 2a4:  9f 91         pop  r25
 2a6:  8f 91         pop  r24
 2a8:  7f 91         pop  r23
 2aa:  6f 91         pop  r22
 2ac:  5f 91         pop  r21
 2ae:  4f 91         pop  r20
 2b0:  3f 91         pop  r19
 2b2:  2f 91         pop  r18
 2b4:  0f 90         pop  r0
 2b6:  0b be         out  0x3b, r0  ; 59
 2b8:  0f 90         pop  r0
 2ba:  0f be         out  0x3f, r0  ; 63
 2bc:  0f 90         pop  r0
 2be:  1f 90         pop  r1
 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?

Autor: Peter II (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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?

Autor: Karl Heinz Buchegger (kbuchegg) (Moderator)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: Karl Heinz Buchegger (kbuchegg) (Moderator)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: Karl Heinz Buchegger (kbuchegg) (Moderator)
Datum:

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

ist das, was aus deinem
    counter++;
    if (counter == BESTIMMTER_WERT)
    {
        counter = 0;
        ...
geworden ist. Das heftige Speichern und Laden ist der volatile Variablen 
geschuldet. Würdest du es so schreiben
ISR (TIMER0_COMPA_vect)
{
    uint8_t tmpCounter = counter;

    tmpCounter ++;
    if (tmpCounter == BESTIMMTER_WERT)
    {
        tmpCounter = 0;
        ...
    }

    counter = tmpCounter ;

würde auch das wahrscheinlich wegfallen.

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: c-ler (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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
//tu was
-Teil findest wirklich ein Funktionsaufruf statt.

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

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: c-ler (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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

Autor: Sam .. (sam1994)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: Frank M. (ukw) Benutzerseite
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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

Autor: benwilliam (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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?

Autor: Volkmar Dierkes (volkmar)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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

Autor: c-ler (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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.

Autor: Volkmar Dierkes (volkmar)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel




Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder GIF-Format hochladen.
Siehe Bildformate
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net