Forum: Mikrocontroller und Digitale Elektronik Compiler Optimierung macht Probleme


von Phil (Gast)


Lesenswert?

Hallo Leute.

Ich habe momentan ein Problem mit meinem Code. Es soll eine LED über 
eine Geradengleichung gedimmt werden. Es funktioniert aber nicht und ich 
weiß nicht wieso. Hab schon nen Debugger rangehangen, aber da ich mich 
damit (noch) nicht so auskenne hilft mir das nicht weiter.

Ich vermute es liegt unter anderem an der Optimierung. Jene kann ich 
aber nicht ausstellen, da sonst der Code zu groß ist. Die Variable 
"zaehler" ist immer "out of scope".

Als Anmerkung sei gesagt, dass "sek" in einer ISR sekündlich hochgezählt 
wird.

Es wäre schön, wenn einer ne Idee hat :-)

Hier mal der Codeausschnitt:
1
//globale Variablen
2
volatile uint8_t sek = 0, abschnitt = 0;
3
uint8_t intervalle_werte[7] PROGMEM = {0, 100, 255, 100, 150, 100, 50}; //gibt die Stützstellen der y-Werte (Helligkeit) an
4
uint16_t intervalle_t[7] PROGMEM = {0, 5, 10, 15, 33, 37, 60};  // gibt die Stützstellen der x-Werte (Zeit) an
5
6
// Zeit lesen
7
void get_time(void) {
8
volatile uint8_t i;
9
  for(i = 0; i < 7; i++) {
10
    if ( sek < intervalle_t[i]) {
11
    abschnitt = i;
12
    break;
13
    }
14
  }
15
}
16
17
// Wert lesen und übergeben
18
uint8_t get_value(void) {
19
20
  get_time();
21
22
volatile int16_t ergebnis;            // ergebnis = ( (zaehler/nenner) / 1000 ) + n
23
volatile uint16_t nenner,zeit;        
24
volatile int32_t zaehler;
25
volatile int16_t n;
26
27
  zeit = sek - intervalle_t[abschnitt-1];
28
  zaehler = (intervalle_werte [abschnitt] - intervalle_werte [abschnitt - 1]) * (int32_t)1000 * zeit;
29
  nenner = (intervalle_t[abschnitt] - intervalle_t[abschnitt - 1]);
30
  n = intervalle_werte[abschnitt - 1] + Verschiebung;
31
  ergebnis= ((zaehler / nenner) / 1000) + n;
32
  OCR1A = pgm_read_word(&pwmtable[result]);
33
34
  return ergebnis;
35
}

von Peter (Gast)


Lesenswert?

>  OCR1A = pgm_read_word(&pwmtable[*result*]);

Woher kommt result ???

Da musst Du schon ein bisschen mehr Code posten um etwas nachvollziehen 
zu können....

von g457 (Gast)


Lesenswert?

> uint16_t intervalle_t[7] PROGMEM [..]

> if ( sek < intervalle_t[i]) {
             ^
das (und sämtliche andere Zugriffe auf den PROGMEM) dürfte so nicht 
funktionieren wie erwartet -> pgm_read_*()

..weiter hab ichs mir noch nicht angesehen..

von (prx) A. K. (prx)


Lesenswert?

Was sollen denn diese ganzen volatiles für lokale Variablen? Ist dir die 
CPU zu schnell?

von Phil (Gast)


Lesenswert?

Peter schrieb:
> Woher kommt result ???

Es muss natürlich Ergebnis heißen.

g457 schrieb:
> das (und sämtliche andere Zugriffe auf den PROGMEM) dürfte so nicht
>
> funktionieren wie erwartet -> pgm_read_*()

Das hab ich gerade gesehen und bin dabei es zu ändern.

A. K. schrieb:
> Was sollen denn diese ganzen volatiles für lokale Variablen

Wenn ich die Variablen nicht volatile hatte wurden sie vom Compiler 
wegveroptimiert. Zumindest waren sie "out of scop" auch innerhalb der 
fkt.

Ich änder gerad den Code nochmal um und wenn ich nicht weiter weiß poste 
ich ihn wieder :-)

Danke schonmal. Das mit dem progmem hab ich nicht gesehen... Wenn man 
das ganze Woche mit C# rummacht vergisst man sowas ganz schnelle -.-

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Es funktioniert aber nicht und ich weiß nicht wieso.
Du hast einen Wissensvorsprung, denn immerhin weißt du schon mal,
WAS nicht funktioniert... :-o

von ... .. (docean) Benutzerseite


Lesenswert?

du weißt aber das du die Helligkeit und Storm/Spannung nicht linear 
zusammenhängen?

siehe auch http://www.mikrocontroller.net/articles/LED-Fading

von Εrnst B. (ernst)


Lesenswert?

Hier ein Code-Schnippsel, der was ähnliches macht:
1
// ggfs unterschiedliche calibration-arrays für R,G,B-LEDs...
2
// oder nur eines für alles...
3
prog_uint8_t led_calibration[]= {
4
0,0,
5
50,10,
6
100,30,
7
150,80,
8
200,140,
9
0xFF,0xFF
10
};
11
12
uint8_t get_calibration(prog_uint8_t data[], uint8_t value) {
13
  uint8_t low_pos;
14
  uint8_t low_val;
15
  uint8_t high_pos;
16
  uint8_t high_val;
17
  while (pgm_read_byte(&data[2])<value) {
18
    data+=2;
19
  } 
20
  low_pos=pgm_read_byte(&data[0]);
21
  low_val=pgm_read_byte(&data[1]);
22
  high_pos=pgm_read_byte(&data[2]);
23
  high_val=pgm_read_byte(&data[3]);
24
  high_val-=low_val;
25
  high_pos-=low_pos;
26
  value-=low_pos;
27
  return low_val+value*high_val/high_pos;
28
}

von Phil (Gast)


Lesenswert?

... ... schrieb:
> du weißt aber das du die Helligkeit und Storm/Spannung nicht linear
>
> zusammenhängen?

deswegen wird doch der Wert zum Schluss durch ein Lookuptable übergeben!

Ich habe den Code wieder aktualisiert und er funktioniert jetzt mit der 
Einschränkung, dass ich nicht weiß, warum die Variable "zaehler" ständig 
"not valid" im AVR Studio angezeigt wird. Liegt es daran, dass es eine 
32bit Variable ist? Habe ich falsch gecastet?

Hier nochmal der aktualisierte Code mit den behobenen Fehlern:
1
//globale Variablen
2
volatile uint8_t sek = 0, abschnitt = 0;
3
volatile int16_t Verschiebung = 0;
4
5
uint8_t intervalle_werte[7] PROGMEM = {0, 100, 255, 100, 150, 100, 50}; //gibt die Stützstellen der y-Werte (Helligkeit) an
6
uint16_t intervalle_t[7] PROGMEM = {0, 5, 10, 15, 33, 37, 60};  // gibt die Stützstellen der x-Werte (Zeit) an
7
8
// Zeit lesen
9
void get_time(void) {
10
volatile uint8_t i;
11
  for(i = 0; i < 7; i++) {
12
    if ( sek < intervalle_t[i]) {
13
    abschnitt = i;
14
    break;
15
    }
16
  }
17
}
18
19
// Wert lesen und übergeben
20
uint8_t get_value(void) {
21
22
  get_time();
23
24
volatile int16_t ergebnis;            // ergebnis = ( (zaehler/nenner) / 1000 ) + n
25
volatile uint16_t nenner;        
26
volatile int32_t zaehler;
27
volatile int16_t n, zeit;
28
29
  zeit = sek - pgm_read_word (&intervalle_t[abschnitt-1]);
30
  zaehler = (pgm_read_byte(&intervalle_werte [abschnitt]) - pgm_read_byte (&intervalle_werte [abschnitt - 1])) * (int32_t)1000 * zeit;
31
  nenner = (pgm_read_word (&intervalle_t[abschnitt]) - pgm_read_word (&intervalle_t[abschnitt - 1]));
32
  n = pgm_read_byte (&intervalle_werte[abschnitt - 1]) + Verschiebung;
33
  ergebnis= ((zaehler / nenner) / 1000) + n;
34
  OCR1A = pgm_read_word(&pwmtable[result]);
35
36
  return ergebnis;
37
}

von Karl H. (kbuchegg)


Lesenswert?

Phil schrieb:

> Ich habe den Code wieder aktualisiert und er funktioniert jetzt mit der
> Einschränkung, dass ich nicht weiß, warum die Variable "zaehler" ständig
> "not valid" im AVR Studio angezeigt wird. Liegt es daran, dass es eine
> 32bit Variable ist? Habe ich falsch gecastet?

Es liegt wahrscheinlich daran, dass du optimierten Code debugst und der 
Optimizer einen Weg gefunden hat, wie er die Variable wegeliminiert.

Wenn du den Optmizer eingeschaltet hast, kann, darf und wird der 
Optimizer den Code umstellen und Variablen entfernen.

von Phil (Gast)


Lesenswert?

Und warum sollte man beim Debuggen die Optimierung abschalten?

Ich versteh schon, dass der hier und da ein wenig optimiert. Aber ich 
versteh nicht wie man vorgehen soll wenn der Code wächst und wächst und 
man einfach nicht mehr die Optimierung abschalten kann, weil ebn sonst 
der uc voll ist und sich gar nicht mehr flashen lässt...

Komisch finde ich ja, dass obwohl die Variable volatil ist, der Compiler 
sie wohl TROTZDEM wegoptimiert.

von Karl H. (kbuchegg)


Lesenswert?

Phil schrieb:
> Und warum sollte man beim Debuggen die Optimierung abschalten?
>
> Ich versteh schon, dass der hier und da ein wenig optimiert.

Nicht hie und da!
Der Optimizer rührt da anständig um!

> Aber ich
> versteh nicht wie man vorgehen soll wenn der Code wächst und wächst und
> man einfach nicht mehr die Optimierung abschalten kann, weil ebn sonst
> der uc voll ist und sich gar nicht mehr flashen lässt...

ev. hilft es, nur Teile des Codes zu optimieren.

> Komisch finde ich ja, dass obwohl die Variable volatil ist, der Compiler
> sie wohl TROTZDEM wegoptimiert.

Da es sich hier um eine Funktionslokale Variable handelt kann der 
Optimizer den kompletten Lebenszyklus der Variablen überblicken.

Ich rate jetzt mal ein wenig.
Wenn der Optimizer den Code so umstellt
1
  nenner = (pgm_read_word (&intervalle_t[abschnitt]) - pgm_read_word (&intervalle_t[abschnitt - 1]));
2
  n = pgm_read_byte (&intervalle_werte[abschnitt - 1]) + Verschiebung;
3
  zaehler = (pgm_read_byte(&intervalle_werte [abschnitt]) - pgm_read_byte (&intervalle_werte [abschnitt - 1])) * (int32_t)1000 * zeit;
4
  ergebnis= ((zaehler / nenner) / 1000) + n;
ändert sich am Ergebnis nichts.
Aber: Dadurch, dass er das Berechnen von 'zaehler' nach hinten 
verschoben hat, hat er das Ergebnis nach der Berechnung bereits in einem 
Register vorliegen. Und da es dort bereits liegt, braucht er es in der 
nachfolgenden Division nicht erneut laden -> kürzerer Code. Damit ist 
aber auch klar, da die Variable sonst nirgends mehr benutzt wird, dass 
niemand die Variable braucht. Insbesondere ist das 'volatile' an dieser 
Stelle sinnlos, da ausserhalb der Funktion sowieso niemand auf diese 
Variable zugreifen kann und diese Variable (als funktionslokale 
Variable) auch sonst nicht erreichbar ist (zb über einen Pointer), da 
keine Funktion aufgerufen wird, der die Adresse dieser Variablen 
übergeben wird.

Du unterschätzt gewaltig, was der Optimizer mit deinem Code alles machen 
kann und wieviel Gehirnschmalz da mitlerweile drinnen steckt.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Phil schrieb:
> Komisch finde ich ja, dass obwohl die Variable volatil ist, der Compiler
> sie wohl TROTZDEM wegoptimiert.
Wenn dich das wundert wundert mich nix ;P
volatile heißt doch nur das sich die Variable außerhalb des 
Sichtbereiches des Compilers ändern kann.
Wenn du die Variable aber nie liest (und diese Funktionslokal ist) kann 
er diesen Ausdruck:
1
ergebnis= ((zaehler / nenner) / 1000) + n;
2
OCR1A = pgm_read_word(&pwmtable[result]);
3
return ergebnis;
Wunderbar optimieren zu:
1
OCR1A = pgm_read_word(&pwmtable[result]);
2
return ((zaehler / nenner) / 1000) + n;
(Wobei wir immer noch nicht wissen woher eigentlich result kommt...)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Phil schrieb:
> versteh nicht wie man vorgehen soll wenn der Code wächst und wächst und
> man einfach nicht mehr die Optimierung abschalten kann, weil ebn sonst
> der uc voll ist und sich gar nicht mehr flashen lässt...
Dein "Problem" ist denke ich einfach das du einen riesen Haufen (manche 
nenen es auch Codewust) an Abhängigkeiten zusammenklatscht, gewürzt mit 
ein paar Annahmen und Try&Error Programmierung.

Normalerweise versucht man Funktionen so zu gestalten das diese 
möglichst eigenständig arbeiten und auch getestet werden können. Wenn 
ich dann weiß dass die Funktion korrekt funktioniert kann ich diese in 
anderen Programteilen dann nutzen und muss nicht das gesamte Programm 
debuggen.

von Phil (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> ev. hilft es, nur Teile des Codes zu optimieren.

optimieren.. ich hoffe du meinst compilieren bzw. debuggen. Oder kann 
man tatsächlich die Optimierungsstufe für ganze Codeabschnitte 
einstellen? Wär ja cool. Glaub aber kaum, dass das geht.. Wegen der 
"Ganzheitlichkeit" :-)

Also ich habe jetzt den Code in ein eigenes Projekt ausgelagert und 
siehe da. Es funktioniert auch OHNE volatile und alle Variablen 
EINSCHLIESSLICH "zaehler" haben einen resonablen Wert.

Die große Frage für mich lautet jetzt.

Kann ich nun ruhigen Gewissens den Code in meinem eigentlichen Projekt 
so lassen, oder muss ich "Angst" haben, dass mir der Optimierer wieder 
nen fetten Strich durch die Rechnung macht. Nicht optimiert und 
ausgelagert läuft er.

Läubi .. schrieb:
> Dein "Problem" ist denke ich einfach das du einen riesen Haufen (manche
>
> nenen es auch Codewust) an Abhängigkeiten zusammenklatscht, gewürzt mit
>
> ein paar Annahmen und Try&Error Programmierung.

Kurzn: Anfänger. Japp. Das bin ich :-)

von Klaus (Gast)


Lesenswert?

Der Optimizer macht dir niemals ein Strich durch die Rechnung (mal 
abgesehen, von globalen Variablen und Interrupts, aber das tut hier nix 
zur Sache).


Dass der Optimizer Variablen rausschmeißt, ändert nichts an der 
funktionalität des Codes. Allerdings kannst du sie dann im debugger 
nicht mehr anzeigen lassen, wie du festgestellt hast. Das ist aber kein 
Hinweis darauf, dass der Code nicht funktioniert.

Also:
- Wenn du bei einer Variable  "Not in Scope" oder "Location not Valid" 
angezeigt bekommst, is das kein Grund zur Sorge. Das hat alles seine 
Richtigkeit.
-  Wenn du aber zum Debuggen unbedingt den Inhalt einer Variabelen sehen 
möchtest, kannst du sie (temporär!) als Volatile deklarieren. In den 
meisten Fällen wird sie dann nicht wegoptimiert. Durch das Volatile hast 
du dir nur die Möglichkeit geschaffen, die Variable im Debugger 
anzuzeigen, aber keine Funktionalität des Codes geändert.

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.