www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik [AVR] Sehr schnelle Interruptroutine mit 16Bit Counter


Autor: Nico (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manch einer mag das jetzt zwar als unsinnig abtun, aber ich habe mir aus 
gegebenem Anlass mal ein paar Gedanken zu einer schnellen aber kleinen 
Interruptroutine gemacht.

Der ISR soll eigentlich nicht mehr machen als bei einem Interrupt einen 
16bit Counter hochzählen. Ausgehend vom avr-gcc generierten Code hab ich 
mich bisher bis zu dem Code vorgearbeitet:
volatile uint16_t Counter;

ISR(INT0_vect, ISR_NAKED) {
    asm volatile(
        "PUSH R16\n"
        "IN   R16, __SREG__\n"
        "PUSH R16\n"
        "LDS  R16, Counter\n"
        "SUBI R16, 0xFF\n"
        "STS  Counter, R16\n"
        "BRCS 0f\n"
        "LDS  R16, Counter+\n"
        "SUBI R16, 0xFF\n"
        "STS  Counter+1, R16\n"
        "0:"
        "POP  R16\n"
        "OUT  __SREG__,R16\n"
        "POP  R16\n"
        "RETI\n"
        :
    );
}

Der avr-gcc code brauch 37 Takte. Meiner normal 21, bei einem overflow 
25 (wenn ich micht nicht verzählt habe). Ausserdem braucht er weniger 
flash.

Hat da noch jemand Verbesserungsvorschläge? Das einzig bessere was mir 
noch einfallen würde wäre den Counter direkt an zwei Register zu binden, 
was ich aber eigentlich gerne vermeiden würde.

Nico

P.S. Ja ich weiss, das man nicht grundlos überoptimieren sollte, ist für 
mich in erster Linie aber auch nur ein Gedankenspiel. Also bitte kein 
sinnloses bashen oder trollen, danke.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"LDS  R16, Counter+1\n"

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die alles entscheidende Frage ist hier: wie oft wird dieser Interrupt 
aufgerufen? Wie hoch ist also der Gewinn an freier CPU-Zeit real?

Ich habe ähnliche Versuche mal bei DCF77-Experimenten mit dem Schieben 
eines uint64_t um 1 Bit gemacht.
Da wwaren es aber mehr als 600 Takte gegen ca. 20. Mehr aus Interesse an 
dem Stück Inline-ASM allerdings. Ich denke, die GCC-Routine hätte es 
genauso getan. ;-)

Gruß aus Berlin
Michael

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mit ADIW...?
volatile uint16_t Counter;

ISR(INT0_vect, ISR_NAKED) {
    asm volatile(
        "PUSH R16\n"
        "IN   R16, __SREG__\n"
        "PUSH R16\n"
        "PUSH R17\n"
        "LDS  R16, Counter\n"
        "LDS  R17, Counter+1\n"
        "ADIW R17:16,1\n"
        "STS  Counter, R16\n"
        "STS  Counter+1, R17\n"
        "0:"
        "POP  R17\n"
        "POP  R16\n"
        "OUT  __SREG__,R16\n"
        "POP  R16\n"
        "RETI\n"
        :
    );
}

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

hatte ich auch kurz überschlagen, sind dann aber 26 Takte ohne RETI 
durch das zusätzliche POP/PUSH.

Gruß aus Berlin
Michael

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der große Irrtum ist, daß man denkt, es reicht nur den einen 
Zählinterrupt zu optimieren.

Der AVR hat keine Interruptprioritäten. Daher ist der Worst-Case, daß 
auch alle anderen Interrupts auftreten können. Wenn Du z.B. einen 
UART-Interrupt hast, der viel machen muß, dann verzögert dieser Dein 
Zählen.

Die maximale Zählfrequenz ergibt sich also nicht aus der Laufzeit des 
Zählinterrupts, sondern aus der Summe der Ausführungszeiten aller 
Interrupts. Und da können schnell mal >500 Zyklen zusammmen kommen.


Peter

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falls noch ein Timer frei sein sollte, so kann man externe Signale in 0 
Takten zählen, indem man den Timer per extern taktet.

Leider muss man den Start/Stop aber per Software machen, AVR bietet 
nicht die Möglichkeit den Start/Stop per I/O-Port zu triggern.

Johann

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nico schrieb:
> Manch einer mag das jetzt zwar als unsinnig abtun, aber ich habe mir aus
> gegebenem Anlass mal ein paar Gedanken zu einer schnellen aber kleinen
> Interruptroutine gemacht.
>
> Der avr-gcc code brauch 37 Takte. Meiner normal 21, bei einem overflow
> 25 (wenn ich micht nicht verzählt habe). Ausserdem braucht er weniger
> flash.

Interessant und durchaus nicht unsinnig wäre auch, das avr-gcc 
beizubringen!
Damit die unnötigen Operationen im ISR-Prolog/-Epilog entfallen :-)

Johann

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
man kann doch mit dem GCC auch globale variablen an Register binden, 
damit sollte sich das ganze noch wesentlich schneller machen, weil man 
nicht erst R16,R17 Retten muss und auch noch die Zählvariable aus dem 
Speicher lesen und Zurückschreiben.

Autor: jo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter Dannegger

> Der große Irrtum ist, daß man denkt, es reicht nur den einen
> Zählinterrupt zu optimieren.

"Ja ich weiss, das man nicht grundlos überoptimieren sollte, ist für
mich in erster Linie aber auch nur ein Gedankenspiel. Also bitte kein
sinnloses bashen oder trollen, danke."

Es sind immer die gleichen, die rumklugscheissern muessen...

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jo schrieb:
> "Ja ich weiss, das man nicht grundlos überoptimieren sollte

Nö.
Man sollt nur vorher überlegen, ob das, was man macht, überhaupt einen 
Effekt hat.

Es ist mir doch vollkommen schnurz, ob Du trotz Deiner super-duper 
Optimierung Counts verlierst. Solange ich Deine Geräte nicht kaufen muß 
und mich damit rumärgern.


> Also bitte kein
> sinnloses bashen oder trollen, danke."

Nichts zu danken, wer so bescheuert reagiert, ist selber der Troll.
Schreib nächstes mal besser gleich dazu: "Ich verbitte mir jeden 
ernsthaften Kommentar".


> Es sind immer die gleichen, die rumklugscheissern muessen...

Bleib nur weiter bei Deinem beschränkten Horizont. Sofort rumzumotzen, 
wenn einer auf Probleme hinweist, wird Dich weit bringen und zu einem 
guten Programmierer machen.


Peter

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter schrieb:
> man kann doch mit dem GCC auch globale variablen an Register binden,
> damit sollte sich das ganze noch wesentlich schneller machen, weil man
> nicht erst R16,R17 Retten muss und auch noch die Zählvariable aus dem
> Speicher lesen und Zurückschreiben.

Da würde ich aber nicht R16/R17 verwenden; die sind viel zu "wertvoll". 
Ausserdem verliert man u.U ABI-konformität und kann viele Funktionen aus 
der avr-libc bzw. libgcc nicht mehr verwenden!

Bei dem Lösungsansatz würde die Wahl also zB auf R2/R3 fallen. SUBI ist 
dort nicht mehr anwendbar, aber es gehen ja auch INC/BRNE-Sequenzen:
   inc R2
   ...
   brne 0f
   ...
   inc R3
   ...
0:

Johann

Autor: jo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Bleib nur weiter bei Deinem beschränkten Horizont.

Fragt sich, wer hier beschraenkt ist, wenn er nicht mal 2 Namen 
ausseinander halten kann...

> Sofort rumzumotzen, wenn einer auf Probleme hinweist, wird Dich weit
> bringen und zu einem guten Programmierer machen.

Deine Kollegen koennen einem nur Leid tun.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
> Bei dem Lösungsansatz würde die Wahl also zB auf R2/R3 fallen. SUBI ist
> dort nicht mehr anwendbar, aber es gehen ja auch INC/BRNE-Sequenzen:

Nachtrag:

Allerdings muss dann jedes Objekt mit -ffixed-2 -ffixed-3 compiliert 
werden (bzw. die entsprechende globale Register-Deklaration includet 
werden) -- auch dann, wenn es den Zähler überhaupt nicht verwendet.

Johann

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Johann L.
ja mir ist klar das das keine schöne universelle Lösung ist. Aber wenn 
es wirklich sehr schnell gehen muss ist es immerhin eine Alternative.

Da ich erst mit ASM angefangen habe und dann mal ein paar versuche mit 
dem gcc gemacht habe ärgere ich mich auch immer über den grossen 
Overhead den der Compiler erzeugt.
Dafür schreibt sich C wesentlich schneller als ASM.

Autor: Nico (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter Danneger

Erstmal, ich bin nicht Jo. Die Anmerkung im OP hatte ich nur aus 
Erfahrungen hier im Forum dazugeschrieben.
Das mit den Interruptlatenzen ist natürlich beim AVR immer ein Problem, 
aber wie gesagt, es geht nur um ein Gedankenspiel, wie man das ganze 
möglichst kompatibel und schnell hinbekommen kann.

@Johann L.
Ich bin mir jetzt nicht ganz sicher ... aber ist SUBI R16,0xFF nicht als 
opcode das selbe wie INC R16? Meine das mal gelesen zu haben, kann es 
aber grad nicht nachschauen.

Nico

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nico schrieb:

> Ich bin mir jetzt nicht ganz sicher ... aber ist SUBI R16,0xFF nicht als
> opcode das selbe wie INC R16?

Ist es nicht.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter schrieb:

> Da ich erst mit ASM angefangen habe und dann mal ein paar versuche mit
> dem gcc gemacht habe ärgere ich mich auch immer über den grossen
> Overhead den der Compiler erzeugt.

Dieser Overhead ist in über 98% aller Fälle irrelevant und hat lediglich 
"psychologische" Auswirkungen :-)

Johann

Autor: Karl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@jo:
Man merkt, dass du wohl noch neu im Forum bist. Sonst würdest du  Peter 
Dannegger wohl kaum als Troll bezeichnen. Damit hast du dich selbst 
disqualifiziert.

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
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
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 bestätigst du, die Nutzungsbedingungen anzuerkennen.