Bug in WinAVR ? Ich dreh bald durch !

Gast #238211
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.
Gast #238214
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
Gast #238215
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 ?
Gast #238220
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...
Gast #238221
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
Persönliche Seite #238222
Lesenswert?

1
#define ENTER_CRITICAL do { sreg_copy = SREG; cli(); } while(0)
2
#define LEAVE_CRITICAL do { SREG = sreg_copy; } while(0)
3

4
volatile unsigned char sreg_copy = 0;
5
volatile int x = 0;
6

7
void foo(void)
8
{
9
    /* code ... */
10
    ENTER_CRITICAL;
11
    if ( x > 32000)
12
        printf("bar");
13
    LEAVE_CRITICAL;
14
}

So in etwa dürfte das aussehen, was Stefan beschreibt.
Gast #238225
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
Gast #238226
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).
Gast #238228
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
Gast #238230
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 :-))
Gast #238231
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
Gast #238234
Lesenswert?

Eine etwas verspätete Reaktion auf OldBugs "SREG-Rettung" ;)

@OldBug

Dein SREG-sichern ist aber auch ... sagen wir mal problematisch
1
#define ENTER_CRITICAL do { sreg_copy = SREG; cli(); } while(0) 
2
define LEAVE_CRITICAL do { SREG = sreg_copy; } while(0) 
3
   
4
volatile unsigned char sreg_copy = 0; 
5
volatile int x = 0; 
6
   
7
void foo(void) 
8
{ 
9
    /* code ... */ 
10
    ENTER_CRITICAL; 
11
      ...
12
    LEAVE_CRITICAL; 
13
}
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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren