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
unsignedcharActTemp;
3
}_status;
4
5
voidlm75_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
unsignedcharFoo;//unused, just to waste space
3
unsignedcharActTemp;
4
}_status;
5
6
voidlm75_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.
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?
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.
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...
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.
> 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.
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.
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:
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?
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.
>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...
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.
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.
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?
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?
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:
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.
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)
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
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
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
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
... 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?
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.