www.mikrocontroller.net

Forum: Compiler & IDEs Funktionsaufruf aus Interruptroutine OK?


Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ganz wohl fühle ich mich ja nicht dabei dort Code aufzurufen, auf den 
ich keinen Einfluss habe.

SIGNAL (SIG_OUTPUT_COMPARE1A)
{
  OCR1A += pgm_read_word(&table[second/UPDATE_RATE]);
}

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist völlig ok.

Zudem rufst du damit nicht wirklich eine Funktion auf. Diese "Funktion" 
ist bloss inline-code.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Thomas (Gast)

>ganz wohl fühle ich mich ja nicht dabei dort Code aufzurufen, auf den
>ich keinen Einfluss habe.


>  OCR1A += pgm_read_word(&table[second/UPDATE_RATE]);

Tststst, ein böse Division. Das sollte man meiden, erst recht in einem 
Interrupt.

MFG
Falk

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder Zweierpotenz als UPDATE_RATE verwenden.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, und danke für die Antworten.

Was ist denn "inline-code"?

Ist das mit der Division wirklich so schlimm? Das Ergebnis ist ja auch 
wieder ein Integer, also ohne Nachkommastelle.

Gut, ich könnte mein Programm natürlich folgendermaßen abändern:

SIGNAL (SIG_OUTPUT_COMPARE1B)
{
  if( --prescaler == 0 ){ 
    prescaler = (uint8_t) DEBOUNCE;
    second++;      // exact one second over
    second_++;   // zus. Variable nötig
    if (second_ == UPDATE_RATE) //zus. IF-Abfrage
    {
      second_ = 0;
      i+=1;
    }
  }  
#if XTAL % DEBOUNCE
  if (prescaler <= XTAL % DEBOUNCE) {
    OCR1B += XTAL / DEBOUNCE +1;   /* um 1 Takt längere Periode um 
              den Rest abzutragen */
  } else {
#endif
    OCR1B += XTAL / DEBOUNCE;   /* kurze Periode */
#if XTAL % DEBOUNCE    
  }
#endif
}



SIGNAL (SIG_OUTPUT_COMPARE1A)
{
  OCR1A += pgm_read_word(&table[i]);  //division entfällt
}

aber ob das so viel effizienter ist...

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Thomas (Gast)

>Was ist denn "inline-code"?

Code, der direkt an Stelle des Funktionsaufrufs eingefügt wird, ohne 
Call & return. Ist schneller.

>Ist das mit der Division wirklich so schlimm?

Jain.

> Das Ergebnis ist ja auch
>wieder ein Integer, also ohne Nachkommastelle.

Ist egal. So eine Division dauert je nach Wortbreite 50..500 Takte.

>aber ob das so viel effizienter ist...

Ist es. Und wie du siehst geht es gut ohne Division.

MFG
Falk

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Was ist denn "inline-code"?

Kurze Funktionen und Makros, die nicht zu einem "klassischen" 
Funktionsaufruf auf Assembler-Ebene führen, sondern deren Code direkt 
dort eingesetzt wird, wo sie in C aufgerufen wurden. Der Overhead für 
den Aufruf ist damit komplett weg.

> Ist das mit der Division wirklich so schlimm?

Das pgm_read_word, um das du dir Sorgen machst, mutiert im Prinzip zu 
gerade mal zwei Assembler-Instruktionen. Die Division dagegen wird zu 
einem richtigen Funktionsaufruf einer mehr oder weniger aufwendigen 
Funktion, da AVRs keine Division in Hardware durchführen können.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus wrote:

> ... Die Division dagegen wird zu
> einem richtigen Funktionsaufruf einer mehr oder weniger aufwendigen
> Funktion, da AVRs keine Division in Hardware durchführen können.

...es sei denn, UPDATE_RATE ist eine zur Compilezeit bekannte und
konstante Zweierpotenz, dann sollte der Compiler das durch eine
Schiebeoperation ersetzen.  Allerdings kann der AVR immer nur ein
Bit mit einem Befehl schieben, ein Schieben um viele Bits ist also
auch Aufwand.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK, das mit dem Inline-Code fand ich interessant und hab mal ein weinig 
experimentiert:

#define swp(type,a,b) {type tmp; tmp=a; a=b; a=tmp;}
...
int x=2;
int y=1;
swp(int,x,y);
swp(int,x,y);
{
  int x=3;
  swp(int,x,y)
}
{
  int x=3;
  float y =2.7;
  swp(int,x,y)
}


sehe ich das richtig, dass man überall einfach Codefragmente mit 
geschweiften Klammeren umgeben kann, um temporäre Variablen direkt 
wieder frei zu geben?
Ist das auch sinnvoll?
Variablen außerhalb mit gleichem Namen sind dann innen nicht sichtbar?
Wieso gibt der beim letzterem swp noch nicht einmal eine Warnung aus?
Was passiert in diesem Fall?

Gruß,
Thomas

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thomas wrote:

> sehe ich das richtig, dass man überall einfach Codefragmente mit
> geschweiften Klammeren umgeben kann, um temporäre Variablen direkt
> wieder frei zu geben?

Das siehst du falsch.  Erstens legt der GCC die Stackframes immer für
die gesamte Lebensdauer der Funktion an, es wird also für
geschachtelte Blöcke weder innerhalb der Funktion neuer Platz
alloziert noch welcher freigegeben.

Das betrifft aber praktisch nur große Variablen oder aber den Fall
ausgeschalteter Optimierung.  Kleine Variablen (bzw. daraus berechnete
Teilausdrücke) werden bei eingeschalteter Optimierung in Registern
abgelegt, und der Compiler weiß genau, wie lange er ein Register für
einen bestimmten Wert benötigt und wann es wieder für eine andere
Aufgabe zur Verfügung steht.

> Ist das auch sinnvoll?

Kommt drauf an.  Es kann die Übersichtlichkeit bei sehr langen
Funktionen erhöhen.  Alternativ kannst du aber auch aus dem
geschachtelten Block gleich eine weitere Funktion (mit dem Scope
"static") machen.  Wenn sie im gesamten Programmlauf nur einmal
benötigt wird, entsteht daraus kein Funktionsaufruf, sondern es ist
immer einfacher (mindestens durch das Einsparen von CALL und RET),
dass der Compiler sie dann gleich inline in den Aufrufer hinein
compiliert.

> Variablen außerhalb mit gleichem Namen sind dann innen nicht
> sichtbar?

Ja.

> Wieso gibt der beim letzterem swp noch nicht einmal eine Warnung
> aus?

Was hättest du denn erwartet?  float und int sind gegenseitig
zuweisungkompatibel.

> Was passiert in diesem Fall?

Bei int -> float wird eine Gleitkommazahl erzeugt, die so nahe wie
möglich der int-Zahl entspricht.  (Naja, so ungefähr, den Gesetzestext
dazu müsste ich erst suchen.)  Bei float -> int wird der ganzzahlige
Anteil der Gleitkommazahl benutzt.

Code generieren deine swp()s bei eingeschalteter Optimierung gar
keinen, da sie allesamt nichts wirklich ändern.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>aber ob das so viel effizienter ist...

>Ist es. Und wie du siehst geht es gut ohne Division.

Gut, aber außer dass diese Lösung noch zusätzlich zwei dauerhafte 
Variablen (der AVR hat nur 32 Register??) und eine if-Abfrage braucht 
habe ich damit vor allem ein Problem:

Der Code wird absolut unleserlich.
Die Funktion der ersten Lösung hingegen ist selbsterklärend und 
erschließt sich auf dem ersten Blick.

Ich werde das jetzt wohl mit einer Zweierpotenz machen, wie bereits 
vorgeschlagen. Wie oft jetzt genau die damit erzeugte Frequenz angepasst 
wird ist eigentlich nicht so wichtig. Das wird was von 4 bis 32 Sekunden 
werden. Damit dürfte das so auch die schnellste Lösung sein, wenn der 
Compiler das wirklich optimiert.

Gruß,
Thomas

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wow, danke für die ausführliche Antwort, Jörg!

>Was hättest du denn erwartet?  float und int sind gegenseitig
>zuweisungkompatibel.

Eine Warnung hätte ich schon erwartet, hätte man ja per Compileroption 
abschaltbar machen können.
Ich dachte eigentlich immer, dass man für sowas casten müsste...

>Bei float -> int wird der ganzzahlige Anteil der Gleitkommazahl benutzt.

Also ohne zu runden, gut zu wissen.

>Code generieren deine swp()s bei eingeschalteter Optimierung gar
>keinen, da sie allesamt nichts wirklich ändern.

Interessant, habe ich noch gar nicht drüber nachgedacht. Du meinst also, 
der Inhalt der Speicherbereiche wird gar nicht vertauscht, sondern nur 
die Zuordnung?

Gruß,
Thomas

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Thomas (Gast)

>Gut, aber außer dass diese Lösung noch zusätzlich zwei dauerhafte
>Variablen (der AVR hat nur 32 Register??)

und noch einges an SRAM . .

AVR-Tutorial: Speicher

> und eine if-Abfrage braucht
>habe ich damit vor allem ein Problem:

>Der Code wird absolut unleserlich.

Bitte?

>Die Funktion der ersten Lösung hingegen ist selbsterklärend und
>erschließt sich auf dem ersten Blick.

Du hast komische Logik.

>Ich werde das jetzt wohl mit einer Zweierpotenz machen, wie bereits
>vorgeschlagen. Wie oft jetzt genau die damit erzeugte Frequenz angepasst
>wird ist eigentlich nicht so wichtig. Das wird was von 4 bis 32 Sekunden
>werden. Damit dürfte das so auch die schnellste Lösung sein, wenn der
>Compiler das wirklich optimiert.

Hoffen wir das mal.

MFG
Falk

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thomas wrote:

>>Code generieren deine swp()s bei eingeschalteter Optimierung gar
>>keinen, da sie allesamt nichts wirklich ändern.

> Interessant, habe ich noch gar nicht drüber nachgedacht. Du meinst also,
> der Inhalt der Speicherbereiche wird gar nicht vertauscht, sondern nur
> die Zuordnung?

Das wäre eine Möglichkeit.  Aber so, wie du das da oben aufgeschrieben
hast, wird der Compiler sämtlichen Code der entsprechenden C-Zeilen
wegwerfen, da die Berechnung der Ergebnisse nutzlos ist, denn sie
werden am Ende gar nicht verwendet.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>denn sie werden am Ende gar nicht verwendet.

ach, sooo meinst du das...
klar, das war ja auch nur ein Experiment für den inline-Code ohne jede 
Relevanz.
Ich wollte nur mal sehen, ob der Compiler das auch so schluckt.
Dann habe ich mich gefragt, was passiert wenn man das zweimal aufruft, 
also zwei mal eine tmp Instanz erzeugt wird, und so weiter....

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Du hast komische Logik.

finde ich gar nicht, in der ersten Lösung sind alle nötigen 
Informationen zum Verstehen der Funktionalität in einer einzigen Zeile 
zusammengefasst, und zwar nur da wo sie auch benötigt werden.
Durch die klingenden Namen wird auch sofort klar, was da passiert.

bei der zweiten Lösung muss man sich erstmal durch den Code wühlen und 
alle Variablen zusammentragen.
Außerdem taucht da gezwungenermaßen Code an Stellen auf, wo sich dessen 
Funktion noch gar nicht einordnen lässt. Sowas liest sich immer schwer, 
weil man sich nicht mehr aufs Wesentliche konzentriert.

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hängt auch ein bischen von der Qualität des Compilers ab. Gut 
optimierende Exemplare suchen den Code daraufhin ab, in welchen Teilen 
welche Variablen verwendet werden und wie oft. Daraus wird dann die 
Optimierung der Registerbelegung abgeleitet. Folglich ist es bei solchen 
Compilern nicht allzu wichtig, ihnen das Leben durch eigene Klarstellung 
der Lebensdauer zu erleichtern.

Autor: Gisbert (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gehört der GCC dazu ?

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja.

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.