Forum: Compiler & IDEs Fehler im gcc, im Chip oder im Hirn?


von Thomas W. (lodentoni)


Lesenswert?

Hallo,

ich hatte gerade mit einem heftigen Bug zu kämpfen. Das Problem konnte 
ich umgehen, allerdings würde mich die Ursache mal interessieren. Mein 
Problem ist, dass ich eine (globale) Variable nicht beschreiben kann. 
Also, ich kann zwar eine Zuweisung machen, danach steht aber nicht drin 
was drin stehen sollte. Definiere ich, vor der eigentlichen Variable, 
eine weitere Variable ist das Problem weg.

Praxibeispiel;
Ich lese einen LM75 per iic aus und will dessen Temperatur in einem 
globalen struct speichern:
1
struct {
2
  unsigned char ActTemp;
3
} _status;
4
5
void lm75_task(void)
6
{
7
  //iic Zugriff
8
  _status.ActTemp = iic_read;
9
}

In der Variablen iic_read steht die Korrekte Temperatur (dez. 29), aber 
in _status.ActTemp steht nur ein dez. 6. Die iic_read Variable konnte 
ich an jede andere Stelle weitergeben, es blieb immer beim Wert dez. 29, 
nur in dem struct war der Wert nicht speicherbar. Nach drei Tagen und 
vielen Litern instant Kaffee bin ich auf folgende Lösung gestoßen:
1
struct {
2
  unsigned char Foo; //unused, just to waste space
3
  unsigned char ActTemp;
4
} _status;
5
6
void lm75_task(void)
7
{
8
  //iic Zugriff
9
  _status.ActTemp = iic_read;
10
}

Den Member Foo von _status benutze ich nie, aber jetzt funktioniert 
alles wie geplant. Wie kann das sein? Meine erste Annahme war, dass der 
gcc nen Bug hat, schließlich muss man ja die Schuld erstmal von sich 
schieben. Dann habe ich versucht nachzudenken und habe jetzt die Idee, 
dass der AVR ne kaputte Speicherstelle haben könnte. Allerdings tritt 
das Problem bei den Optimierungen Os, O1 und O3 auf. Da es aber wiederum 
statische Variablen sind wird die Optimierungsstufe wohl egal sein.

Mich würde interessieren ob jemand ähnliche Phänomene erlebt hat. 
Vielleicht hat einer eine Idee wie man das Problem anders umgehen kann 
und wie man es beim nächsten mal schneller erkennt.

von Karl H. (kbuchegg)


Lesenswert?

Ich wette 10:1 dass ein "volatile" an der richtigen Stelle das Problem 
gelöst hätte.

von ... (Gast)


Lesenswert?

Interrupts? 'volatile' vergessen?
Warum struct, wenn Du eh nur einen Member hast?
Ansonsten, welche gcc Version? Ist das im Simulator oder auf realer HW? 
Welcher µC?

von Thomas W. (lodentoni)


Lesenswert?

Ich glaube die Wette hättest du verloren. Sowohl das struct als auch den 
ActTemp Member als volatile zu kennzeichnen ändert nichts.

P.S. Ich hab die Quellen bei diesem Problem eingefroren und eine neue 
Version begonnen. Ideen kann ich also gerne ausprobieren.

Edit:
Es ist ein ATMega 644, avr-gcc sagt Version 4.3.3 an. Ich arbeite mit 
Eclipse unter OpenSuse 11.3. Simuliert war das nicht, sondern mit printf 
debuggt. In das struct sollten später weitere Member hinein und in einem 
Interrupt wird das struct nicht angefasst. Zwar wird diese iic_read 
Variable in einem Interrupt geändert, aber wenn ich sie mir abhole sind 
die iic Zugriffe eigentlich längst vorbei. Ich kann ja mal einen SREG 
gesicherten Zugriff ausprobieren.

Edit2: Nein, auch ein atomarer Zugriff führt zum selben Problem.

von (Ein) (Gast)


Lesenswert?

Ich hatte mal einen ähnlichen Fehler. Irgendwo war ein Buffer zu klein. 
Ich wollte eine Zahl als Hex in einen String schreiben (sprintf(), nur 
zum Debuggen). Der war aber nur 2 Bytes groß, also kein Platz mehr für 
'\0'. Vielleicht ists bei dir ja ähnlich...

von ZiZi (Gast)


Lesenswert?

Generierten Code schon mal angeschaut?

von Thomas W. (lodentoni)


Lesenswert?

Sein könnte das, ja. Ich denke dass würde sich aber anders äußern. 
Entweder scheibt die printf dann Müll oder die Programmausführung würde 
nicht mehr erwartet ablaufen, da er verkehrt durch die Gegend springt.

Momentan häng ich grad an der volatile Idee. Was ich gerade darüber lese 
ist dicht das, was ich darüber wusste Beziehungsweise glaubte zu Wissen. 
Zwar kann ich mir nicht vorstellen, dass es wirklich am volatile liegt, 
aber ich muss es trotzdem ich viel mehr anwenden.

von Karl H. (kbuchegg)


Lesenswert?

> Zwar wird diese iic_read
> Variable in einem Interrupt geändert, aber wenn ich sie mir abhole sind
>  die iic Zugriffe eigentlich längst vorbei.

Bestärkt mich eigentlich nur in der Vermutung, dass es ein volatile 
Problem ist.

> Ich glaube die Wette hättest du verloren. Sowohl das struct als auch den
> ActTemp Member als volatile zu kennzeichnen ändert nichts.

Weder noch.
Die Variable, die du von diesem Datentyp anlegst, soll volatile sein.

von Thomas W. (lodentoni)


Lesenswert?

ZiZi schrieb:
> Generierten Code schon mal angeschaut?

Nee, da hörts dann bei mir auf.

Karl Heinz Buchegger schrieb:
>> Zwar wird diese iic_read
>> Variable in einem Interrupt geändert, aber wenn ich sie mir abhole sind
>>  die iic Zugriffe eigentlich längst vorbei.
>
> Bestärkt mich eigentlich nur in der Vermutung, dass es ein volatile
> Problem ist.
>
>> Ich glaube die Wette hättest du verloren. Sowohl das struct als auch den
>> ActTemp Member als volatile zu kennzeichnen ändert nichts.
>
> Weder noch.
> Die Variable, die du von diesem Datentyp anlegst, soll volatile sein.

Okay, ich werde mich mal über volatile genauer belesen und damit 
experimentieren. Ich geb dann Bescheid was dabei rauskommt.

von ... (Gast)


Lesenswert?

Wo kommt Dein avr-gcc her? Aus dem OpenSuse-Repo? Fehlen dem eventuell 
einige Patches. Die Linuxtoolchain von Atmel hat wohl auch noch so ihre 
Probleme. Am besten sind wohl die Versionen von hier:
http://www.wrightflyer.co.uk/avr-gcc/
Hier ist der zugehörige Thread:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=42631

von Thomas W. (lodentoni)


Lesenswert?

Ja, mein avr-gcc kommt aus dem OpenSuse Repo. Deinen Link werde ich auch 
mal ausprobieren. Vorher muss ich aber meine volatile Wissenlücke 
schließen.

Ich habe gerade das Problem, dass wenn ich Member eines volatile struct 
als Zeiger weitergeben möchte bekomme ich eine Warnung die ich nicht so 
recht verstehe:
1
struct {
2
  unsigned char DoScan;
3
  unsigned char WDat[2];
4
  unsigned char WLen;
5
  unsigned char RDat[2];
6
  unsigned char RLen;
7
  unsigned char Error;
8
  iic_handle_t Handle;
9
} volatile _iic;
10
11
void lm75_task(void)
12
{
13
  if (_iic.Handle && iic_finished(&_iic.Handle)) { //lm75.c:62
14
    //read iic results
15
  }
16
}

Bei der iic_finished Funktion bekomme ich jetzt folgende Warnung: 
"../lm75.c:62: warning: passing argument 1 of 'iic_finished' discards 
qualifiers from pointer target type"

Bedeutet das, dass an dieser Stelle das volatile ignoriert wird?

von Michael B. (mb_)


Lesenswert?

Thomas Weinhold schrieb:
> Bei der iic_finished Funktion bekomme ich jetzt folgende Warnung:
> "../lm75.c:62: warning: passing argument 1 of 'iic_finished' discards
> qualifiers from pointer target type"
>
> Bedeutet das, dass an dieser Stelle das volatile ignoriert wird?

Mehr oder weniger: ja.

von Thomas W. (lodentoni)


Lesenswert?

Okay, dann erstmal vielen Dank euch allen. Ich werd mal schauen, was ich 
aus den ganzen anregungen umgesetzt bekomme.

von Peter (Gast)


Lesenswert?

>Zwar wird diese iic_read Variable in einem Interrupt geändert, aber wenn
>ich sie mir abhole sind die iic Zugriffe eigentlich längst vorbei.

Dann solltest Du iic_read als volatile deklarieren, so müsste es 
gehen...

von Thomas W. (lodentoni)


Lesenswert?

Nun, das scheint nicht zu funktionieren. Ein Zeiger auf iic_read wird an 
die iic funktionen übergeben. Genau bei den Funktionsaufrufen kommt vom 
compiler die Warnung, dass die qualifiers nicht mit übergeben werden.

Die volatiles funktionieren also auch nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas Weinhold schrieb:
> Die volatiles funktionieren also auch nicht.

Wenn man sie nicht richtig benutzt, funktionieren sie auch nicht.

Zeig doch mal den kompletten Code.  Die Deklarationen der iic_*-
Funktionen würden bereits genügen.  Wenn deren Parameter nicht
volatile markiert sind, dann kann der Compiler sie natürlich auch
nicht so behandeln.

Stellt sich die Frage, wofür du den ganzen Parameterkram überhaupt
brauchst.  Hast du von diesen Strukturen denn mehrere?  Ansonsten
ist sowas ein guter Kandidat für eine globale Variable.

von Lutz (Gast)


Lesenswert?

Wie ist eigentlich die Variable iic_read deklariert? Du erwartest laut 
Deklaration der struct ein unsigned char und schreibst, daß iic_read 
einen Wert von dez. 29 oder dez. 6 hat? Vielleicht irgendwo ein 
casting-Fehler?

von Randy N. (huskynet)


Lesenswert?

Thomas Weinhold schrieb:
> Sowohl das struct als auch den
> ActTemp Member als volatile zu kennzeichnen ändert nichts.

Wenn iic_read diejenige Variable ist, die in der ISR beschrieben wird, 
muss diese Variable als volatile gekennzeichnet werden, da sich diese 
Variable für den Compiler unerwartet ändern kann.

Die Struct oder deren Member brauchen hingegen nicht volatile sein, es 
sei denn, du greifst auf diese auch in irgendeiner ISR (oder der 
Vollständigkeit halber: in einem parallel laufendem Thread, wenn du so 
etwas hast) zu.

Hast du das schonmal probiert?

von Thomas W. (lodentoni)


Lesenswert?

Hallo,
nach einer Woche Pause bin ich wieder am arbeiten. Den kompletten Code 
posten könnte etwas unübersichtlich werden. Hier sind mal einige 
wichtige zeilen:

iic.h:
1
typedef unsigned char iic_handle_t;
2
3
iic_handle_t iic_access(unsigned char *WLen,
4
            unsigned char volatile *WDat,
5
            unsigned char *RLen,
6
            unsigned char volatile *RDat,
7
            unsigned char volatile *Error);
8
9
char iic_finished (iic_handle_t *);

lm75.c:
1
#include <iic.h>
2
//!used to handle the iic bus
3
struct {
4
  unsigned char DoScan;
5
  unsigned char volatile WDat[2];
6
  unsigned char WLen;
7
  unsigned char volatile RDat[2];
8
  unsigned char RLen;
9
  unsigned char volatile Error;
10
  iic_handle_t Handle;
11
} _iic;
12
13
//!status vars
14
 struct {
15
  //unsigned char FakeVar;
16
  signed char   ActTemp;
17
  unsigned char HalfDegrees;
18
} _status;
19
20
//!Running periodically called functions.
21
void lm75_task(void)
22
{
23
  //!read continuously the temperature
24
  if (_iic.Handle && iic_finished(&_iic.Handle)) {
25
    if (!_iic.Error) {
26
      uac_printf("lm75 %i\n",_iic.RDat[0]);
27
      _handle_new_temp();
28
    }
29
    else uac_printf("lm75 read error %u\n",_iic.Error);
30
  }
31
  else if (!_iic.Handle) {
32
    _iic.RLen    = 2;
33
    _iic.WLen    = 1;
34
    _iic.WDat[0] = IIC_ADR +1; //read
35
    if (_iic.DoScan) {
36
      _iic.Handle  = iic_access(&_iic.WLen,_iic.WDat,&_iic.RLen,_iic.RDat,&_iic.Error);
37
      uac_printf("start scan\n");
38
      _iic.DoScan=0;
39
    }
40
  }
41
}
42
43
void _drv_lm75_uac_cmd (int argc, char *argv[])
44
{
45
  uac_printf("actual  temperature: %i.%c'C\n",_status.ActTemp,(_status.HalfDegrees & 0x01)? '5':'0');
46
  _iic.DoScan=1;
47
  uac_printf("iic handle: %u\n",_iic.Handle);
48
}
49
50
void _handle_new_temp (void) {
51
  //unsigned char sreg=SREG;
52
  //cli();
53
  _status.ActTemp = _iic.RDat[0];
54
  _status.HalfDegrees = (_iic.RDat[1] & 0x80) ? 1:0;
55
  //SREG = sreg;
56
}

Die Task Funktion ist etwas schlecht kommentiert. Das liegt daran das es 
Testcode ist. Im Prinzip wird die _iic.DoScan Variable über einen 
Konsolenbefehl gesetzt ( _drv_lm75_uac_cmd-Funktion) und ein Buszugriff 
wird gestartet. Dann wird gepollt bis der Zugriff zu Ende ist und die 
gelesene Temperatur wird gespeichert.

Auch die volatiles in der iic_access()-Funktion für den Zugriff bringen 
nichts. Wenn ich aber im struct-_status die FakeVar Variable - die 
nirgends verwendet wird - aktiviere läuft alles wie geplant. Oder habe 
ich die volatiles falsch eingesetzt?

P.S. Die Speicherstellen der Zeiger, die an die iic_access()-Funktion 
übergeben werden, werden in ISRs geändert.

von Thomas W. (lodentoni)


Lesenswert?

Nachtrag:
Ich hab jetzt alle Variablen, die in irgendeiner ISR genutzt werden als 
volatile deklariert. Immer noch das selbe Problem.
Ich habe auch fest gestellt, dass man FakeVar im _status struct garnicht 
benötigt. Wenn man HalfDegrees vor ActTemp deklariert funktioniert das 
Programm auch, nur kann ich dann nicht mehr richtig auf HalfDegrees 
zugreifen.

Kann mir jemand sagen ob die volatiles deklaration programmiertechnisch 
richtig sind? (Also, typ volatile *name)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas Weinhold schrieb:
> Kann mir jemand sagen ob die volatiles deklaration programmiertechnisch
> richtig sind? (Also, typ volatile *name)

Nein, die sind falsch.  Die deklarieren den in die Funktion
übergebenen Zeiger als volatile, was er gar nicht ist, denn dessen
Wert ändert sich später nicht unerwartet.  Du willst aber stattdessen
das Objekt, auf das der Zeiger zeigt, als volatile markieren.  Dazu
musst du das vorn dran schreiben
1
volatile <type> *<name>;

von Stefan E. (sternst)


Lesenswert?

Jörg Wunsch schrieb:
> Die deklarieren den in die Funktion übergebenen Zeiger als volatile

Na na na Jörg, dazu müssten sie rechts vom * stehen.

Jörg Wunsch schrieb:
> Dazu musst du das vorn dran schreiben
1
 volatile <type> *<name>;

Nö, seine Schreibweise ist äquivalent dazu.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:

> Nö, seine Schreibweise ist äquivalent dazu.

Ja, stimmt, verwechselt.

von Thomas W. (lodentoni)


Lesenswert?


von Kolja W. (-jkw-)


Lesenswert?

Ich hatte mal einen sehr vergleichbaren Fehler und bei mir lag es an dem 
erwähnten sprintf(char_array_mit_nur_drei_felder,"%i",vier_tausend);

Dann ballert dir sprintf schön in deine variablen.
Kann man wunderbar schützen in dem man eine weitere Var definiert, dann 
wird mit etwas glück die überschrieben und man selbst hat den Wert save 
;)
Aber ne Lösung ist das natürlich nicht!

Gruß JKW

von Thomas W. (lodentoni)


Lesenswert?

Kolja W. schrieb:
> Ich hatte mal einen sehr vergleichbaren Fehler und bei mir lag es an dem
> erwähnten sprintf(char_array_mit_nur_drei_felder,"%i",vier_tausend);
>
> Dann ballert dir sprintf schön in deine variablen.
> Kann man wunderbar schützen in dem man eine weitere Var definiert, dann
> wird mit etwas glück die überschrieben und man selbst hat den Wert save
> ;)
> Aber ne Lösung ist das natürlich nicht!
>
> Gruß JKW

Hallo, gute Idee. Ich habe aber nur eine printf, die greift nur lesend 
auf die variablen zu. Zwar ist die selbst geschrieben, aber die habe ich 
schon eine ganze Weile in Benutzung. Deren Fehler dürften größten Teils 
raus sein.

MfG

von Thomas W. (lodentoni)


Lesenswert?

... schrieb:
> Wo kommt Dein avr-gcc her? Aus dem OpenSuse-Repo? Fehlen dem eventuell
> einige Patches. Die Linuxtoolchain von Atmel hat wohl auch noch so ihre
> Probleme. Am besten sind wohl die Versionen von hier:
> http://www.wrightflyer.co.uk/avr-gcc/
> Hier ist der zugehörige Thread:
> http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=42631

Aktueller Status:
Also die .dep Pakete, die ...Gast vorgeschlagen hat, bekomme ich auf 
meinem OpenSuse einfach nicht zum laufen (auch nicht mit alien).

... schrieb:
> Wo kommt Dein avr-gcc her? Aus dem OpenSuse-Repo? Fehlen dem eventuell
> einige Patches. Die Linuxtoolchain von Atmel hat wohl auch noch so ihre
> Probleme. Am besten sind wohl die Versionen von hier:
> http://www.wrightflyer.co.uk/avr-gcc/
> Hier ist der zugehörige Thread:
> http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=42631

Ich habe aber eine aktuelle Toolchain von Atmel geladen und installiert. 
Auch habe ich mein altes WinXP Notebook ausgegraben und AVR Studio5 
installiert (ich weiß nicht genau ob es da Unterschiede in der 
C-Toolchain gibt). Alles beides hat nichts an dem Problem geändert.

Nach diesen drei Versuchen glaube ich nicht dass es an der Toolchain 
liegt. Bleibt noch die Möglichkeit der falschen Programmierung und die 
des kaputten Chips. Um letztes auszuschließen muss ich warten bis ich 
mal wieder Bauteile benötige, wegen einem IC kann ich nicht bestellen.

Zur Programmierung habe ich mal eine Frage: Wenn ich eine Funktion f() 
lokal in einer datei a.c deklariert und definiert habe, wieso bekomme 
ich vom gcc einen Fehler angezeigt wenn ich eine andere Funktion f() 
unter gleichem Namen in einer Datei b.c deklariere und definiere. Ist es 
normalerweise nicht so, dass es für jede übersetzte *.o keine 
Zusammenhänge gibt, es sei denn man benutzt extern?

von ... (Gast)


Lesenswert?

Thomas Weinhold schrieb:
>> http://www.wrightflyer.co.uk/avr-gcc/
>> Hier ist der zugehörige Thread:
>> http://www.avrfreaks.net/index.php?name=PNphpBB2&f...
>
> Ich habe aber eine aktuelle Toolchain von Atmel geladen und installiert.

Ja, die gabs damals halt noch nicht. Sollte OK sein, außer man 
compiliert für atmega324a. Die im AS5 sollte dieselbe sein.

Thomas Weinhold schrieb:
> Zur Programmierung habe ich mal eine Frage: Wenn ich eine Funktion f()
> lokal in einer datei a.c deklariert und definiert habe, wieso bekomme
> ich vom gcc einen Fehler angezeigt wenn ich eine andere Funktion f()
> unter gleichem Namen in einer Datei b.c deklariere und definiere. Ist es
> normalerweise nicht so, dass es für jede übersetzte *.o keine
> Zusammenhänge gibt, es sei denn man benutzt extern?

Funktionen sind automatisch extern. Du mußt explizit 'static' 
davorschreiben, wenn Du die Sichtbarkeit auf das C-File begrenzen 
willst.

von Thomas W. (lodentoni)


Lesenswert?

Ahh, ok, danke, das ist mir bisher entgangen.

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.