Forum: Compiler & IDEs Optimierung Fehlerhaft?


von Stefan R. (stefanrau)


Angehängte Dateien:

Lesenswert?

Hallo ich bin hier fast am verzweifeln, ich habe hier ein Programm, bei 
dem der Compiler einen wichtigen wert wegoptimiert, Wie kann ich das 
Verhindern?
1
C-Code
2
char adcwertermittlung(unsigned char zeit_art) 
3
{    
4
      //Fehler: zeit_art ist immer 0??? da wegoptimiert
5
  
6
    
7
    adcteiler=1;
8
    
9
    ADCSRA |= (1<<ADSC);
10
    while (ADCSRA & (1<<ADSC))  
11
    adc_ergebnis = ADCH;
12
    if (zeit_art==MINUTEN)
13
      {
14
      adcteiler=4;
15
      }
16
    if (zeit_art==STUNDEN)
17
      {
18
      adcteiler=10;
19
      }      
20
    adc_ergebnis=adc_ergebnis/adcteiler;
21
    
22
    if (adc_ergebnis>zeit_art)
23
      {
24
      adc_ergebnis = zeit_art;
25
      }      
26
    return adc_ergebnis;
27
    
28
}

Die Variable zeit_art wird immer wegotimiert, auch
1
C-Code
2
char adcwertermittlung(volatile unsigned char zeit_art)

Hat nichts geholfen.
Wer weiß Rat?

VG Stefan

von Peter (Gast)


Lesenswert?

Der Wert wird wohl in einem Register landen und nicht in einer 
gesonderten Variable.
Das kannst Du dir im Listing File genauer ansehen.

Der Wert an sich sollte bei der Abfrage "if(zeit_art==MINUTEN)"
richtig ausgewertet werden.

von Julian B. (julinho)


Lesenswert?

Poste Doch mal den compilierten Assembler-Code, dann sind wir alle 
schlauer!!!

von Stefan R. (stefanrau)


Angehängte Dateien:

Lesenswert?

Ich habe jetzt den Code noch mal so zusammen genommen, wie ich Ihn 
Ursprünglich hatte.
Ich muss jetzt noch mal korrigieren, der Code Funktioniert beim 
Debuggen, mit und ohne Optimierung, nur wenn ich alles auf den uC 
spiele, wird die letzte Abfrage nicht ausgeführt.
Ich habe jetzt mal alles Angehängt.

VG Stefan

von Stefan R. (stefanrau)


Lesenswert?

Hier noch der passende Assembler Code:
1
00000250 <adcwertermittlung>:
2
 250:  28 2f         mov  r18, r24
3
 252:  81 e0         ldi  r24, 0x01  ; 1
4
 254:  80 93 02 01   sts  0x0102, r24
5
 258:  80 91 7a 00   lds  r24, 0x007A
6
 25c:  80 64         ori  r24, 0x40  ; 64
7
 25e:  80 93 7a 00   sts  0x007A, r24
8
 262:  04 c0         rjmp  .+8        ; 0x26c <adcwertermittlung+0x1c>
9
 264:  80 91 79 00   lds  r24, 0x0079
10
 268:  80 93 0b 01   sts  0x010B, r24
11
 26c:  80 91 7a 00   lds  r24, 0x007A
12
 270:  86 fd         sbrc  r24, 6
13
 272:  f8 cf         rjmp  .-16       ; 0x264 <adcwertermittlung+0x14>
14
 274:  2c 33         cpi  r18, 0x3C  ; 60
15
 276:  11 f4         brne  .+4        ; 0x27c <adcwertermittlung+0x2c>
16
 278:  84 e0         ldi  r24, 0x04  ; 4
17
 27a:  03 c0         rjmp  .+6        ; 0x282 <adcwertermittlung+0x32>
18
 27c:  28 31         cpi  r18, 0x18  ; 24
19
 27e:  19 f4         brne  .+6        ; 0x286 <adcwertermittlung+0x36>
20
 280:  8a e0         ldi  r24, 0x0A  ; 10
21
 282:  80 93 02 01   sts  0x0102, r24
22
 286:  60 91 02 01   lds  r22, 0x0102
23
 28a:  8d ef         ldi  r24, 0xFD  ; 253
24
 28c:  0e 94 83 01   call  0x306  ; 0x306 <__udivmodqi4>
25
 290:  80 93 0b 01   sts  0x010B, r24
26
 294:  28 17         cp  r18, r24
27
 296:  10 f4         brcc  .+4        ; 0x29c <adcwertermittlung+0x4c>
28
 298:  20 93 0b 01   sts  0x010B, r18
29
 29c:  80 91 0b 01   lds  r24, 0x010B
30
 2a0:  08 95         ret

und dazu noch mal den C-Code
1
C-Code
2
char adcwertermittlung(unsigned char zeit_art) 
3
{    
4
  
5
    adcteiler=1;
6
    
7
    ADCSRA |= (1<<ADSC);
8
    while (ADCSRA & (1<<ADSC))  
9
    adc_ergebnis = ADCH;
10
    adc_ergebnis = 253;
11
    if (zeit_art==MINUTEN)adcteiler=4;
12
      
13
    if (zeit_art==STUNDEN)adcteiler=10;      
14
    
15
    adc_ergebnis=adc_ergebnis/adcteiler;
16
    
17
    if (adc_ergebnis>zeit_art)adc_ergebnis = zeit_art;// Fehler: Befehl nach Klammer wird nicht ausgeführt. 
18
    return adc_ergebnis;
19
    
20
}

von Jörg G. (joergderxte)


Lesenswert?

Fehlt da nicht ein Semikolon?
1
while (ADCSRA & (1<<ADSC))  
2
    adc_ergebnis = ADCH;
"Solange ADSC gesetzt ist, lese ADCH"??

von Peter II (Gast)


Lesenswert?

Hallo,
was dein code ein wenig schwer zu lesen machst ist, das du alle 
variabeln oben deklariert hast.

adc_ergebnis wird nur in adcwertermittlung verwendet, warum dann nicht 
dort deklarieren?

von Helfer (Gast)


Lesenswert?

1
>  294:  cp  r18, r24      Vergleiche adc_ergebnis mit zeit_art
2
>  296:  brcc  .+4         Springe zu #29c wenn adc_ergebnis <= zeit_art
3
>  298:  sts  0x010B, r18  adc_ergebnis = zeit_art;
4
>  29c:  lds  r24, 0x010B  return adc_ergebnis;

von Peter D. (peda)


Lesenswert?

Stefan Rau schrieb:
> Hier noch der passende Assembler Code:

Sieht doch alles korrekt compiliert aus.

Nur warum zwingst Du den Compiler, alles mit globalen Variablen zu 
rechnen?
Das erzeugt doch nur unnützten Code (die ganzen LDS/STS).


Peter

von willibald (Gast)


Lesenswert?

Stefan Rau schrieb:
> if (adc_ergebnis>zeit_art)adc_ergebnis = zeit_art;
> // Fehler: Befehl nach Klammer wird nicht ausgeführt.

Der Befehl wird dann übersprungen, wenn adc_ergebnis kleiner oder gleich 
zeit_art ist. Welche Werte haben die Variablen denn an der Stelle?

Stefan Rau schrieb:
> nur wenn ich alles auf den uC
> spiele, wird die letzte Abfrage nicht ausgeführt.

Woran genau erkennst du das?

Und was wird nicht ausgeführt, die ganze if-Abfrage oder die vom if 
abhängige Zuweisung? Deine Beschreibungen sind hier nicht eindeutig.

von Stefan R. (stefanrau)


Lesenswert?

@Jörg G
Du hast Recht. habe es korrigiert.

@PeterII
Da es nicht funtioniert hat habe ich angefangen alles zu ändern und habe 
auch die Variablen rausgeholt.

@Helfer
zum Glück mal jemand der Assembler erklärt.

@Peter Dannecker
Siehe PeterII

Die Frage aber bleibt, warum funktioniert das nicht auf den uC?
Habe den uC auch noch mal extra gelöscht. Ändert sich aber nichts.
Auch noch mal das Hex File gelöscht, Pfade geprüft, ändert sich auch 
nichts

Hier noch mal den C Code
1
C-Code
2
#define STUNDEN 24
3
#define MINUTEN 60
4
5
char adcwertermittlung(unsigned char zeit_art) 
6
{    
7
      unsigned char adc_ergebnis;
8
    unsigned char adcteiler=1;
9
    
10
    ADCSRA |= (1<<ADSC);
11
    while (ADCSRA & (1<<ADSC));  
12
    adc_ergebnis = ADCH;
13
  
14
    if (zeit_art==MINUTEN)adcteiler=4;
15
      
16
    if (zeit_art==STUNDEN)adcteiler=10;      
17
    
18
    adc_ergebnis=adc_ergebnis/adcteiler;
19
    
20
    if (adc_ergebnis>zeit_art)adc_ergebnis = zeit_art;// Fehler: Befehl nach Klammer wird nicht ausgeführt. Da wegoptimiert.      
21
    return adc_ergebnis;
22
}
und der passende ASMCode
1
AVR-Assembler-Code
2
00000250 <adcwertermittlung>:
3
 250:  28 2f         mov  r18, r24
4
 252:  80 91 7a 00   lds  r24, 0x007A
5
 256:  80 64         ori  r24, 0x40  ; 64
6
 258:  80 93 7a 00   sts  0x007A, r24
7
 25c:  80 91 7a 00   lds  r24, 0x007A
8
 260:  86 fd         sbrc  r24, 6
9
 262:  fc cf         rjmp  .-8        ; 0x25c <adcwertermittlung+0xc>
10
 264:  80 91 79 00   lds  r24, 0x0079
11
 268:  2c 33         cpi  r18, 0x3C  ; 60
12
 26a:  21 f0         breq  .+8        ; 0x274 <adcwertermittlung+0x24>
13
 26c:  28 31         cpi  r18, 0x18  ; 24
14
 26e:  21 f0         breq  .+8        ; 0x278 <adcwertermittlung+0x28>
15
 270:  61 e0         ldi  r22, 0x01  ; 1
16
 272:  03 c0         rjmp  .+6        ; 0x27a <adcwertermittlung+0x2a>
17
 274:  64 e0         ldi  r22, 0x04  ; 4
18
 276:  01 c0         rjmp  .+2        ; 0x27a <adcwertermittlung+0x2a>
19
 278:  6a e0         ldi  r22, 0x0A  ; 10
20
 27a:  0e 94 75 01   call  0x2ea  ; 0x2ea <__udivmodqi4>
21
 27e:  28 17         cp  r18, r24
22
 280:  08 f4         brcc  .+2        ; 0x284 <adcwertermittlung+0x34>
23
 282:  82 2f         mov  r24, r18
24
 284:  08 95         ret

von Stefan R. (stefanrau)


Lesenswert?

Der die Variable ADCergebnis nimmt nach dem Auslesen des ADC einen Wert 
zwischen 0 und 255 an, in dem Beispiel hat er den Wert 253.

Die Rückgabe gebe ich auf einem Display aus.
Die Funktion wird mit
adc_reuckwert=(int)adcwertermittlung(MINUTEN); in Main aufgerufen.

Wenn ich nun an dem Poti drehen, der an dem ADC sitzt, den bewegt sich 
der Wert zwischen 0 und 74, sollte aber zwischen 0 und 60 sein. Da ja in 
der Abfrage steht:
Zeit Art ist MINUTEN also 60

if (adc_ergebnis>zeit_art)adc_ergebnis = zeit_art;

Wenn ich den Code im Simulator ausführe, dann wird bei 253 60 
zurückgegeben.
Nach dem Laden auf den uC wird 74 ausgegeben.

von Karl H. (kbuchegg)


Lesenswert?

>   ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADIE);

Dein Programm schmiert ab, sobald der ADC fertig ist.
Man gibt NIEMALS einen Interrupt frei, für den man keine ISR hat.

von Helfer (Gast)


Lesenswert?

> Die Frage aber bleibt, warum funktioniert das nicht auf den uC?

Wie prüfst du das? Vielleicht hast du beim Testen auf dem µC andere 
Eingangsbedingungen als im Simulator. Im Simulator macht der ADC 
"nix", d.h. du gibst ihm in der Simulation Daten vor. Auf dem µC ist das 
anders.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#ADC_.28Analog_Digital_Converter.29

"In der praktischen Anwendung wird man zum Programmstart den ADC erst 
einmal grundlegend konfigurieren und dann auf verschiedenen Kanälen 
messen. Diese beiden Dinge sollte man meist trennen, denn das 
Einschalten des ADC und vor allem der Referenzspannung dauert ein paar 
Dutzend Mikrosekunden. Ausserdem ist das erste Ergebnis nach dem 
Einschalten ungültig und muss verworfen werden."

Hast du das beachtet?

von Karl H. (kbuchegg)


Lesenswert?

Jetzt gehst du mal als erstes her und machst hier, hinter diese Ausgabe

    lcd_setcursor(1,1);
    lcd_string(adc_string);

noch ein Leerzeichen drann, nicht dass dich die Ausgabe narrt.

Und dann: Du hast ein LCD. Das kannst du benutzen um dir 
Zwischenergebnisse ausgeben zu lassen.

von Rolf Magnus (Gast)


Lesenswert?

Stefan Rau schrieb:
> itoa(adc_reuckwert,adc_string,8);

Hat es einen speziellen Grund, daß du dir den Wert in Basis 8 ausgeben 
läßt? 74 oktal sind übrigens genau 60 dezimal.

von Stefan R. (stefanrau)


Lesenswert?

@Heinz @Helfer

den Interrupt habe ich rausgenommen. Und eine Abfrage zum verwerfen des 
ersten wertes duchgeführt.

ADMUX |= (1<<REFS0)|(1<<ADLAR);
  ADCSRA |= (1 << ADEN) | (1 << ADSC);
  ADCSRA |= (1<<ADSC);
  while (ADCSRA & (1<<ADSC));
  result = ADCH;


Ich habe auch die Variable ADC Ergebnis in ein int geändert, da ich 
neuerdings werte von über 255 bekomme.

@Heinz die zweite
Wie Wo und wie soll ich ein Leerzeichen einfügen?

@Rolf
ja, ich habe die Diaplayansteuereung aus dem TUT genommen, ich wollte da 
jetzt auch nichts ändern, daher gebe ich alles als char aus.

@all
Ich habe jetzt noch ein paar Diplayausgaben integriert und bei der 
If-Abfrage die 60 hart kodiert.

und erhalte als Wert 334 67 67 hier der Code dazu
Anmerkung dazu: 334/4 sind 83 ??? kann der uC nicht rechnen? ;-)
1
C-Code
2
3
/*
4
Diese Routine liest den ADC Wert aus und gibt je nach aufgerufener Einheit den Stunden oder Sekundenwert zurück. 
5
Aufgerufener Wert 24 dibt den ADC Wert so aufgelöst, dass ein Wert zwischen 0-24 zurückgegeben wird.
6
bei 60 das gleiche.
7
Bei anderen Werten ist der Wert die Obergrenze des zurückgegebenen Wertes.
8
*/
9
char adcwertermittlung(unsigned char zeit_art) 
10
{    
11
      unsigned int adc_ergebnis;
12
    unsigned char adcteiler=1;
13
    char tempchar[8];
14
    
15
    ADCSRA |= (1<<ADSC);
16
    while (ADCSRA & (1<<ADSC));  
17
    adc_ergebnis = ADCH;
18
    //Diplay Ausgabe 1
19
    lcd_clear();
20
    itoa((int)adc_ergebnis,tempchar,8);
21
    lcd_string(tempchar);
22
    _delay_ms(700);
23
    
24
    if (zeit_art==MINUTEN)adcteiler=4;
25
      
26
    if (zeit_art==STUNDEN)adcteiler=10;      
27
    
28
    adc_ergebnis=adc_ergebnis/adcteiler;
29
    //Diplay Ausgabe 2
30
    lcd_clear();
31
    itoa((int)adc_ergebnis,tempchar,8);
32
    lcd_string(tempchar);
33
    _delay_ms(500);
34
    
35
    
36
    if (adc_ergebnis>60)adc_ergebnis = 60;// Fehler: Befehl nach Klammer wird nicht ausgeführt. 
37
    //Diplay Ausgabe 3
38
    lcd_clear();
39
    itoa((int)adc_ergebnis,tempchar,8);
40
    lcd_string(tempchar);
41
    _delay_ms(500);
42
    
43
    return adc_ergebnis;
44
    
45
}

von Karl H. (kbuchegg)


Lesenswert?

Stefan Rau schrieb:

> und erhalte als Wert 334 67 67 hier der Code dazu
> Anmerkung dazu: 334/4 sind 83 ??? kann der uC nicht rechnen? ;-)

Doch er kann rechnen.
Aber du kannst nicht in unterschiedlichen Zahlensystemen denken.

Rolf hats gesehen
Beitrag "Re: Optimierung Fehlerhaft?"



    itoa((int)adc_ergebnis,tempchar,8);

Immer noch!
Die 8 als 3. Parameter sind nicht das, was du glaubst das es ist!
Doku zu itoa lesen!

von Stefan R. (stefanrau)


Lesenswert?

Hallo Karl Heinz.

Danke für den Wink mit dem Baumstamm.... ich habe den Beitrag von Rolf 
nicht verstanden.Ich dachte der 3. Parameter wäre die Länge...
Jetzt kommt meine 60 so wie es soll.


Danke an alle.

VG Stefan

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.