www.mikrocontroller.net

Forum: Compiler & IDEs Bug in WinAVR ? Ich dreh bald durch !


Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich programmiere mir gerade ein Leistungsmessgerät mit einem mega48 das
mir auf einer 3 stelligen 7-Segment Anzeige die Wirkleistungsaufnahme
eines Geräts anzeigt.
Leider habe ich ein absolut unerklärliches Problem mit der Anzeige:

Zu Beginn werden alle Segmente eingeschaltet (als Displaytest), dann
werden die Werte angezeigt. Nur bei mir tut sich nichts. Die Anzeige
und ADCs laufen per Interrupt. Alle Segmente bleiben an, obwohl ich
DDRAM[] auf 0 setze. Lasse ich das for weg gehen alle Anzeigen wie
gewünscht aus, dasselbe passiert es vor der Endlosschleife mache.
Im Interrupt wird DDRAM[0-3] der Reihe nach an die Anzeige ausgegeben.

Ist das ein Bug in WinAVR oder sollte ich nicht 10 Stunden ohne Pause
durchprogrammieren ?

  DDRAM[0]=255;
  DDRAM[1]=255;
  DDRAM[2]=255;

  for(;;)
  {
    DDRAM[0]=0;  //pgm_read_byte(&CGROM[peff/1000%10]);
    DDRAM[1]=0;  //pgm_read_byte(&CGROM[peff/100%10]);
    DDRAM[2]=0;  //pgm_read_byte(&CGROM[peff/10%10]);
  }

PS: Ich habe auch definitiv kein Strichpunkt hinter dem for(;;) falls
das jemand vermutet.

Autor: Jens (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist das ein Leistungsmesser für das 230V-Netz?

Autor: Stefan Seegel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Deklarier mal dein DDRAM-Array als volatile. Vielleicht gehts dann...

Stefan

Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, der funktioniert auch ganz gut, da die ganzen drecksteile die es bei
Pollin, C usw. gibt mir absolut falsche Ergebnisse liefern:
60W Halogenlampentrafo: 1-2W
Mit 50W Halogenlampe: 60W
Die Schaltung ist extrem einfach, ohne Netztrennung misst aber ziemlich
genau, da ich die Signale direkt mit dem mega48 ohne irgendwelche Opamps
dazwischen mit 11kHz abtaste.

Hier mal das Assembler Listung von obigem Code. Der Inhalt der for
Schleife wird komplett ignoriert:
  82 0024 8FEF          ldi r24,lo8(-1)
  83 0026 84B9          out 36-0x20,r24
  84                 .LM9:
  85 0028 8AB9          out 42-0x20,r24
  86                 .LM10:
  87 002a 8093 7E00     sts 126,r24
  88                 .LM11:
  89                 /* #APP */
  90 002e 7894          sei
  91                 .LM12:
  92                 /* #NOAPP */
  93 0030 8093 0000     sts DDRAM,r24
  94                 .LM13:
  95 0034 8093 0000     sts DDRAM+1,r24
  96                 .LM14:
  97 0038 8093 0000     sts DDRAM+2,r24
  98                 .L2:
  99                 .LM15:
 100 003c FFCF          rjmp .L2

Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Deklarier mal dein DDRAM-Array als volatile. Vielleicht gehts dann...

Ja, es geht.
Aber müsste es eigentlich nicht auch ohne volatile gehen ? Ich dachte
das bräuchte man nur für Interrupts ?

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

maybe wurde es wegoptimiert.

Gruß,
Dirk

Autor: Stefan Seegel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Ich dachte das bräuchte man nur für Interrupts ?"

Du benutzt doch auch Interrupts?

"Die Anzeige und ADCs laufen per Interrupt."

Stefan

Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also wird das volatile nicht nur gebraucht, wenn der Wert im Interrupt
verändert werden kann, sondern auch wenn er nur ausgelesen wird ?

Autor: Fritz Ganter (fritzg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genau.

Weil die Variable sonst ev. nur im Register steht und nie auf eine
RAM-Stelle geschrieben wird, Wozu auch, solange noch genug Register
frei sind.

Für den Compiler wird ja der Interrupt nirgends aufgerufen, der weiss
nix von Hardware Interrupts.

Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wozu auch, solange noch genug Register frei sind.

Das frage ich mich oft, wenn ich mit Pointern arbeite, und der Compiler
bei einer for Schleife jedesmal den Pointer aus dem RAM liest und wieder
reinschreibt, obwohl dieser kein volatile ist.

Der Compiler scheint genau das wegzuoptimiern was ich brauche, und
unnötige Sachen lässt er stehen...

Autor: Stefan Seegel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und noch eins: Wenn du 16 bit (oder größer) Variablen hast und in der
Main-Schleife UND in einem Interrupt(s) darauf zugreifst muss man noch
mehr aufpassen. Wenn du z.B. in der Mainfunktion die Variable liest,
kann's passieren dass grad nachdem das erste Byte der Variablen
gelesen wurde der Interrupt aufgerufen wird und die Variable verändert
wird. Dann hast du als Ergebniss Müll. Man muss sicherstellen dass das
nicht passiert. Unter Multitaskingbetriebssystemen (Windows, Linux,
RTOS etc etc) würde man das "Thread-Synchronisierung" oder "Critical
Section" nennen. Am besten mal danach googeln, ist für's prinzipielle
Verständniss gut.
In diesem kongreten Fall würde ich bevor ich in der Mainfunktion auf so
eine Variable zugreife die Interrupts abschalten, danach wieder ein.

Stefan

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#define ENTER_CRITICAL do { sreg_copy = SREG; cli(); } while(0)
#define LEAVE_CRITICAL do { SREG = sreg_copy; } while(0)

volatile unsigned char sreg_copy = 0;
volatile int x = 0;

void foo(void)
{
    /* code ... */
    ENTER_CRITICAL;
    if ( x > 32000)
        printf("bar");
    LEAVE_CRITICAL;
}

So in etwa dürfte das aussehen, was Stefan beschreibt.

Autor: Stefan Seegel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sieht interessant aus :-)
Wozu ist denn das while(0) ?

Stefan

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wozu ist denn das while(0) ?

Wenn da jetzt stünde:

  if (x)
      ENTER_CRITICAL;
  else
      foo();

Was würde der Präprozessor daraus machen ohne das do...while?

Autor: peter dannegger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das "while(0)" dient nur dazu, damit der Compiler meckert, wenn
dahintger kein ";" kommt.

Einige Leute halten das für sicherer gegen Schrehbfeiler.

Ist wie wenn manche Leute schreiben "if(0 == i)" statt "if(i ==
0)".


Ich halte es für Unsinn, da man nicht jeden Schreibfehler abfangen
kann, warum sollte man es dann für einige tun ?


Peter

Autor: ---- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wozu ist denn das while(0) ?
Das ist im Endeffekt nur dazu da, um sich etwaige geschweifte
Klammern bedenkenlos sparen zu können (in denen dieser Code
irgendwann mal stehen könnte).

Wenn man sich immer sauber an alle Klammerungen hält, dann sollte
dieser Kunstgriff nicht notwendig sein. Richtig?

----, (QuadDash).

Autor: Daniel Braun (khani)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

wenn man wirklich nur das "do" und das "while(0)" weglässt, macht
der Präprozessor nichts Schlimmes, zumindest in diesem Beispiel. Die
geschweiften Klammern ((curly) braces) sollte man aber dranlassen.


MfG, Daniel.

Autor: peter dannegger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was Rolf meint, läßt sich bereits durch Einklammern vermeiden.

Macros sollte man generell immer einklammern, sonst kann es z.B.
passieren, daß float-Konstanten nicht zur Compilezeit berechnet werden
sondern die fp-lib mit einbinden.


Peter

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Was Rolf meint, läßt sich bereits durch Einklammern vermeiden.

Nein.

Autor: FPGA-User (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Benedikt

Du schreibst, die Drecksteile von ... liefern falsche Ergebnisse...
60W Halogenlampentrafo: 1-2W
Mit 50W Halogenlampe: 60W

Meinst Du damit, dass eine 50W Halogenlampe immer 50,0 W haben
sollte (bei exakt 12V?)
Der Halogentrafo wird doch auch nicht immer exakt 12 V liefern
oder hab ich da was nicht verstanden (als Digitalmensch :-))

Autor: JoachimSchaefer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier noch was zu deinem Problem, das du zwar gelößt hast
aber ich poste es doch mal.

für den Compiler ist im Winavr Makefile default auf Optimierung s
geschalten

siehe makefile.
   # Optimization level, can be [0, 1, 2, 3, s].
   # 0 = turn off optimization. s = optimize for size.
   # (Note: 3 is not always the best optimization level. See avr-
      libc AQ.)
   OPT = s


was ihm nun nach bestimmten regeln erlaubt Code zu optimieren.
Eine Variable die 2 Mal im Code beschrieben wird und nirgends
ausgelesen wird ist nun aus Sicht des Compiler blödsinn daher entfernt
er hier beide stellen!
Beim optimieren geht der Compiler den normalen codelauf durch.
Eine Interrupt Routine wird aber nirgens direkt Aufgerufen also
berücksichtigt er diese nicht (und ohne volatile würde er sie wohl auch
komplett wegoptimieren !)
Du kannst dies eben wie von den vorrednern beschrieben umgehen indem du
die variable als volatile deklarierst.
Du kannst natürlich auch die optimierung abschalten dann gehts auch.
(Ist aber die schlechtere Lösung !)

(testSource)
int dummy;
  dummy=255;
  for (;;)
  {
      dummy=0;
  }

(Optimierung auf s = optimize for size.)
  96                 .L2:
  10:main.c        ****   int dummy;
  11:main.c        ****   dummy=255;
  12:main.c        ****
  13:main.c        ****   for (;;)
  97                 .LM2:
  98 0008 FFCF          rjmp .L2

(Optimierung auf 0 = turn off optimization.)

10:main.c        ****   int dummy;
  11:main.c        ****   dummy=255;
  96                 .LM2:
  97 0008 8FEF          ldi r24,lo8(255)
  98 000a 90E0          ldi r25,hi8(255)
  99 000c 8D8B          std Y+21,r24
 100 000e 9E8B          std Y+22,r25
 101                 .L2:
  12:main.c        ****
  13:main.c        ****   for (;;)
  14:main.c        ****   {
  15:main.c        ****       dummy=0;
 103                 .LM3:
 104 0010 1D8A          std Y+21,__zero_reg__
 105 0012 1E8A          std Y+22,__zero_reg__
 107                 .LM4:
 108 0014 FDCF          rjmp .L2

Autor: Andreas Schwarz (andreas) (Admin) Benutzerseite Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oldbug: warum das SREG sichern?

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

Bewertung
0 lesenswert
nicht lesenswert
Ich bin nicht Patrick :), aber:

damit kann man zu einem beliebigen Zeitpunkt die Interrupts
abschalten und danach den vorherigen Zustand des I-Flags
wiederherstellen.

Wenn man den Code mit Sicherheit nicht innerhalb einer ISR
benutzen will, kann man natürlich gleich cli()/sei() schreiben.

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine etwas verspätete Reaktion auf OldBugs "SREG-Rettung" ;)

@OldBug

Dein SREG-sichern ist aber auch ... sagen wir mal problematisch
#define ENTER_CRITICAL do { sreg_copy = SREG; cli(); } while(0) 
define LEAVE_CRITICAL do { SREG = sreg_copy; } while(0) 
   
volatile unsigned char sreg_copy = 0; 
volatile int x = 0; 
   
void foo(void) 
{ 
    /* code ... */ 
    ENTER_CRITICAL; 
      ...
    LEAVE_CRITICAL; 
}
Nehmen wir an interrupts sind enabled und foo wird aufgerufen...
Aktuelle Codezeile "sreg_copy = SREG;" ist in bearbeitung...
  INTERRUPT*
  ==>der Befehl wird zu ende geführt,
     interrupts werden disabled
     und dann nach SIGNAL_xyz verzweigt.
       SIGNAL_xyz ruft nun ebenfalls foo auf
       -> "sreg_copy = SREG;";
          sreg_copy enthält jetzt sreg mit
          Interrupts disabled ...
Nach der Rückkehr aus SIGNAL_xyz enhält Dein globales sreg_copy den
falschen Wert. Mache sowas als lokale variablen auf dem stack, da
kenn kein Interrupt dran rumdrehen. ;-)

Gruß  Werner

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.