Hallo Ich habe ein merkwürdiges Problem: Ich habe neben vielem anderen ein uint8_t Array. Das Initialisisere ich mit 0en und schreibe dann in einzelne Felder eine 1 Jetzt nutze ich das Array linear und prüfe, ob eine 1 oder 0 steht. Wenn 0, dann zähle ich den Feldzeiger weiter hoch, bis eine 1 gefunden ist. Das funktioniert auch eine Weile, aber irgendwann zwischendrin wird das ganze Array wie von Zauberhand gefüllt (mit 55) Befremdlich für mich daran ist, das es mit einem ca. 2 Jahre alten WinAVR (Version habe ich jetzt nicht mehr im Kopf) compiliert keine Probleme gibt, nur mit dem, was ich gerade frisch eingespielt habe. Am makefile habe ich nichts gerüttelt. Kann irgendwer vermuten, was dafür die Ursache sein kann?
Könnte das irgend ein Adressierfehler sein? Wird vielleicht igend ein uninitalisierter Pointer benutzt? Solche Effekte können auch vom benutzten Compiler abhängen, je nachdem, was der jeweilige Code für Garbage hinterläßt. Tipp zum fangen des Übeltäters: Wenn der Prozessor wenigstens einen Hardware-Breakpoint unterstützt, kannst du feststellen, 'wer' die 55 in dein Array geschrieben hat. Solche Probleme zu finden, kann jedoch beliebig schwer sein...
>Könnte das irgend ein Adressierfehler sein? Wird vielleicht igend ein >uninitalisierter Pointer benutzt? Pointer nutze ich nur wenig. Primär, um Strings aus dem EEPROM zu laden und für Textausgaben auf das LCD. >Solche Effekte können auch vom benutzten Compiler abhängen, je nachdem, >was der jeweilige Code für Garbage hinterläßt. Das verstehe ich nicht, was Du damit meinst. >Tipp zum fangen des Übeltäters: Wenn der Prozessor wenigstens einen >Hardware-Breakpoint unterstützt, kannst du feststellen, 'wer' die 55 in >dein Array geschrieben hat. Ich muß leider gestehen, daß ich vom "richtigen" Debuggen keine Ahnung habe. Ich mach das nach Try and Error und mit vielen Ausgaben auf meinem LCD. >Solche Probleme zu finden, kann jedoch beliebig schwer sein... Ja, hat mich gestern schon alleine einen Tag gekostet herauszufinden, daß ich da wohl nicht ganz alleine Schuld d'ran bin, sondern es mit dem Compiler zusammenhängen muß. Habe verzweifelt in meinem Code erst mal suchen müssen, an welcher Stelle überhaupt die Ursache liegt. Jetzt habe ich noch mal avr-gcc 3.4.6 (2006) installiert und siehe: nach dem compilieren läuft es einwandfrei. Nur der Code, den der aktuelle 4.1.2 produzietr macht den Fehler. Jetzt bin ich natürlich am Grübeln, wo da meine Schuld liegt...
>Kann irgendwer vermuten, was dafür die Ursache sein kann?
Vermutlich wieder irgendein Geraffel im Compiler. Wäre nicht das erste
mal, dass bei der "Weiterentwicklung" von avrgcc eher 3 Schritte
rückwärts gegangen wurde.
Wenn zwei Compilerversionen unterschiedliche Ergebnisse bringen kann es natürlich am Compiler liegen. Nur sind ausserhalb spezieller Foren 99% aller Postings zum Thema Compilerfehler letztlich Anwenderfehler. Und so kann hier auch daran liegen, dass die neue Version korrekten aber anderen Code erzeugt, der einen vorher vorborgen gebliebenen Programmfehler aufdeckt. Überschriebener Speicher ist auch häufig ein Stack-Problem. Nur steht dann selten 55 drin. Alles weitere ist jetzt mangels Information reines Kaffeesatzlesen.
> Vermutlich wieder irgendein Geraffel im Compiler.
Wie oft bist du schon über Compilerfehler gestolpert? Also welche die
nachweislich am Compiler lagen, nicht bloss von der Art "es muss der
Compiler sein, denn der Code ist zigmal von mir kontrolliert 100%
korrekt".
Ron wrote: >>Könnte das irgend ein Adressierfehler sein? Wird vielleicht igend ein >>uninitalisierter Pointer benutzt? > > Pointer nutze ich nur wenig. Primär, um Strings aus dem EEPROM zu laden > und für Textausgaben auf das LCD. Das ist auch nicht unbedingt nötig: Der Compiler ist clever genug, sowas im Hintergrund zu machen, um effizienteren Code zu generieren. > >>Solche Effekte können auch vom benutzten Compiler abhängen, je nachdem, >>was der jeweilige Code für Garbage hinterläßt. > > Das verstehe ich nicht, was Du damit meinst. Garbage nennt man den Datenmüll, der im Speicher von einer früheren Nutzung zurückbleibt. Angenommen, du rufst zuerst eine Funktion f1() auf, und anschließend als nächsten Befehl die Funktion f2(). Dann passiert folgendes: Nach dem Aufruf von f1() (per call-Maschinenbefehl) reserviert der Prolog von f1() so viel Speicher auf dem Stack, wie f1() für seine lokalen Variablen braucht. Dann macht f1() irgendwas und schreibt Daten in seine lokalen Variablen. Bevor f1() (mit dem ret-Maschinenbefehl) an den Befehl nach dem call f1() zurückkehrt, läuft der Epilog von f1() ab. Er ist u.a. dafür verantwortlich, daß der für f1() reservierte Stackbereich wieder freigegeben wird. In aller Regel überschreibt der Epilog diese Daten jedoch nicht, sondern läßt einfach den Garbage stehen. Nun wird f2() aufgerufen, und dessen Prolog reserviert ab derselben Adresse, ab der der von f1() vorher reserviert hatte, den lokalen Speicher für f2(). Da in diesem Bereich aber noch der Garbage von f1() steht, hat ein Lesezugriff auf eine lokale uninitialisierte Variable in f2() den Effekt, daß auf Garbage von f1() zugegriffen wird. Das heißt: Wenn man vergißt, irgendeine Variable zu initialisieren und dort zufällig immer was 'gutartiges' drinsteht - also die Benutzung von Datenmüll zufällig keine gravierenden Folgen hat, wird man den Fehler häufig garnicht entdecken. Wenn man nun aber das Programm mit einer anderen Compilerversion übersetzt, kann es passieren, daß z.B. das Layout der Stackframes von f1() und/oder f2() sich ändert und dadurch plötzlich 'bösartiger' Datenmüll in der Variablen steht und das Programm dann mehr oder weniger schnell abschmiert - meist weniger -, oder völligen Unsinn berechnet. Der Quelltext hat sich dabei absolut nicht geändert... Solche Überraschungen kann man übrigens auch erleben, wenn man ein PC-Programm von einer Windows-Version auf eine andere bringt. Dazu muß man es nochnichteinmal neu compilieren... Prolog und Epilog einer Funktion werden vom Compiler automatisch generiert. Sie bestehen meist nur aus wenigen Maschinenbefehlen. > >>Tipp zum fangen des Übeltäters: Wenn der Prozessor wenigstens einen >>Hardware-Breakpoint unterstützt, kannst du feststellen, 'wer' die 55 in >>dein Array geschrieben hat. > > Ich muß leider gestehen, daß ich vom "richtigen" Debuggen keine Ahnung > habe. Ich mach das nach Try and Error und mit vielen Ausgaben auf meinem > LCD. Das ist keine sehr effiziente Methode. Befasse dich mit dem Debugger. Das spart viel, viel Zeit und außerdem lernt man dabei viel schneller, als bei der Testausgabemethode, was eigentlich im Rechner abläuft. > Jetzt bin ich natürlich am Grübeln, wo da meine Schuld liegt... Grübel hilft nichts. Du solltest dir aber auf jeden Fall deinen Code sehr genau daraufhin ansehen, ob irgendwo der Inhalt von Variablen benutzt wird, ohne daß sie vorher initialisiert wurden. Eine gute Idee ist es auch, den höchsten Warnungslevel des Compilers zu aktivieren - viele Compiler geben dann Warnungen bei der Benutzung uninitialisierter Variablen aus.
Hallo, sorry das ich mich hier als Neuling reinklinke: Uhu Uhuhu: "Eine gute Idee ist es auch, den höchsten Warnungslevel des Compilers zu aktivieren" Kann man das beim AVR Studio irgendwo einstellen? Habe nämlich noch Platz auf meiner To-Do Liste ;=) Danke und lasst euch nicht stören, Jan
Uninitialisierte Variablen in C? AFAIK sind die doch bei avr-gcc immer initialisiert - meine so was mal gelesen zu haben. Ich will jetzt keinen Eid d'auf schwören, denke aber trotzdem stets einen Wert reinzuschreiben, bevor ich das erste mal lese. JTAG steht auch bei mir auf der Wunschliste. Aber bisher ging es auch ohne und mich stört daran, daß man dann mehr Leitungen vom µC auf einen Stecker legen muß, als für ISP nötig (zugegeben: kein gutes Argument)
Ron wrote: > Uninitialisierte Variablen in C? AFAIK sind die doch bei avr-gcc immer > initialisiert - meine so was mal gelesen zu haben. Nein, es Standard bei C, das nicht zu tun - aber du kannst es ja ganz einfach ausprobieren; auch ohne Debugger. In C++ kann man sowas im Konstruktor einer Klasse explizit manchen, aber die primitiven Datentypen werden auch dort nicht automatisch initialisiert. > Ich will jetzt keinen Eid d'auf schwören, denke aber trotzdem stets einen > Wert reinzuschreiben, bevor ich das erste mal lese. Wir sind hier nicht vor Gericht und Prozessoren sind unbestechlich und rechthaberisch... @ Jan Mayer: > Kann man das beim AVR Studio irgendwo einstellen? Habe nämlich noch > Platz auf meiner To-Do Liste ;=) Das weiß ich leider nicht - ich habe zwar schonmal ein Programm geschrieben, aber nicht mit gcc ;-) Sieh dir einfach mal die Optionsliste des Compilers durch, dort wirst du sicherlich fündig...
> Uhu Uhuhu: "Eine gute Idee ist es auch, den höchsten Warnungslevel des > Compilers zu aktivieren" > > Kann man das beim AVR Studio irgendwo einstellen? Habe nämlich noch > Platz auf meiner To-Do Liste ;=) Gibt's noch mehr Warnungen als mit -Wall?
>> AFAIK sind die doch bei avr-gcc immer initialisiert - meine so was mal >> gelesen zu haben. > > Nein, es Standard bei C, das nicht zu tun - aber du kannst es ja ganz > einfach ausprobieren; auch ohne Debugger. Kommt auf die Variable an. Wenn sie global oder static ist, dann schreibt die C-Norm eine implizite Initialisierung mit 0 vor. Für andere Variablen ist es nicht vorgeschrieben, aber auch nicht verboten. GCC tut es da aus Effizienzgründen nicht, warnt aber bei entsprechender Warnungs-Einstellung, wenn man eine Variable ausliest, bevor sie das erste Mal geschrieben wurde. > JTAG steht auch bei mir auf der Wunschliste. Aber bisher ging es auch > ohne und mich stört daran, daß man dann mehr Leitungen vom µC auf einen > Stecker legen muß, als für ISP nötig (zugegeben: kein gutes Argument) Da wäre DebugWire interessant. Da geschieht die ganze Programmierung und das Debugging komplett über einen einzelnen Pin, der sogar schon auf dem ISP-Stecker vorhanden ist, nämlich den RESET-Pin. > Gibt's noch mehr Warnungen als mit -Wall? Ja, eine Menge. Ich arbeite fast immer mit -ansi -pedantic -Wall -Wextra als Minimum.
Ron wrote: > JTAG steht auch bei mir auf der Wunschliste. Aber bisher ging es auch > ohne und mich stört daran, daß man dann mehr Leitungen vom µC auf einen > Stecker legen muß, als für ISP nötig (zugegeben: kein gutes Argument) Gar kein Argument. ;-) JTAG braucht unbedingt: Masse, TCK, TDI, TDO und TMS. ISP braucht unbedingt: Masse, /RESET, SCK, MISO, MISO. Sind gleich viel. :) Beide brauchen außerdem noch VTarget zwingend, wenn man mit den originalen Atmel-Geräten (AVRISP, JTAG ICE) arbeiten will, wobei die Spannung benutzt wird, um den Levelshiftern die entsprechende Information mitzugeben und um festzustellen, ob das Target überhaupt eingeschaltet ist. Bei JTAG würde /RESET (nTRST) nur optional benötigt, um eine Applikation über JTAG neu flashen zu können, die das JTD-Bit setzt. Wenn man das nicht hat/braucht, kann man diese Leitung weglassen.
Jörg Wunsch wrote: > Ron wrote: > >> JTAG steht auch bei mir auf der Wunschliste. Aber bisher ging es auch >> ohne und mich stört daran, daß man dann mehr Leitungen vom µC auf einen >> Stecker legen muß, als für ISP nötig (zugegeben: kein gutes Argument) > > Gar kein Argument. ;-) JTAG braucht unbedingt: Masse, TCK, TDI, TDO > und TMS. ISP braucht unbedingt: Masse, /RESET, SCK, MISO, MISO. Sind > gleich viel. :) Beide brauchen außerdem noch VTarget zwingend, wenn > man mit den originalen Atmel-Geräten (AVRISP, JTAG ICE) arbeiten will, > wobei die Spannung benutzt wird, um den Levelshiftern die entsprechende > Information mitzugeben und um festzustellen, ob das Target überhaupt > eingeschaltet ist. Bei JTAG würde /RESET (nTRST) nur optional benötigt, > um eine Applikation über JTAG neu flashen zu können, die das JTD-Bit > setzt. Wenn man das nicht hat/braucht, kann man diese Leitung > weglassen. Ui, das klingt interessant. Mit dem nTRST kann man ja dann sogar die JTAG Pins anderweitig belegen. Gibts auch eine offizielle Pfostenstecker-Belegung für JTAG von Atmel? An meinem mkII sind vorne nur 10pol Wannenstecker dran - etwas dick eventuell :|
Simon Küppers wrote: > Ui, das klingt interessant. Mit dem nTRST kann man ja dann sogar die > JTAG Pins anderweitig belegen. Allerdings kannst du natürlich nicht mehr (sinnvoll) debuggen damit. Sowie die Applikation das JTD setzt, ist das JTAG-Interface tot. Kann man eigentlich nur nehmen, um via JTAG eine Firmware reinzubringen, die dann kein JTD mehr setzt und diese debuggen. > Gibts auch eine offizielle Pfostenstecker-Belegung für JTAG von Atmel? > An meinem mkII sind vorne nur 10pol Wannenstecker dran - etwas dick > eventuell :| Die 10polige Belegung ist die offizielle Atmel-Variante. Sie beinhaltet außer den genannten 7 Leitungen noch nSRST (kompatibel mit JTAG, aber von Atmel nicht genutzt, wenn ich das recht verstanden haben), ein zweites GND sowie eine Versorgungsleitung, aus der man wohl theoretisch die JTAG-Hardware versorgen könnte. Vielleicht skalierst du das ja auf irgeneinen mechanisch kleineren Stecker runter und nimmst einen Adapter.
> Gar kein Argument. ;-) JTAG braucht unbedingt: Masse, TCK, TDI, TDO > und TMS. ISP braucht unbedingt: Masse, /RESET, SCK, MISO, MISO. Sind > gleich viel. :) Ja, aber eben andere. So daß man zwei Stecker haben muß... Aber wie gesagt: ist ja auf meiner Wunschliste und irgendwann werde ich mir den hier wohl mal nachbauen: http://aquaticus.info/jtag Bis dahin habe ich noch mal dem compiler alle Warnmeldungen aktiviert und bekomme bis auf eine nicht mehr ausgespuckt. Na mal sehen, wann ich dem mystischen Array auf die Schliche komme und bis dahin tut's die alte WinAVR Version. >Kommt auf die Variable an. Wenn sie global oder static ist, dann >schreibt die C-Norm eine implizite Initialisierung mit 0 vor. Für andere >Variablen ist es nicht vorgeschrieben, aber auch nicht verboten. Genau das war's was ich mal gelesen hatte.
> Ja, aber eben andere. So daß man zwei Stecker haben muß... Nö, wenn du JTAG hast, brauchst du kein ISP. Programmieren via JTAG geht sowieso drastisch schneller als via ISP. > irgendwann werde ich mir den > hier wohl mal nachbauen: http://aquaticus.info/jtag Lohnt nicht mehr wirklich. Du bekommst keinen aktuellen AVR mehr debuggt damit. Kauf dir entweder einen AVR Dragon oder sieh' zu, dass du noch so'n Promo-Bundle mit STK500 und JTAG ICE mkII bekommen kannst. Letzteres wäre die Wahl für CPUs mit mehr als 32 KiB ROM.
> Lohnt nicht mehr wirklich. Du bekommst keinen aktuellen AVR mehr > debuggt damit. Kommt auf den Preis an. Selbstgebaut kostet so ein Teil fast nichts. Und obzwar man Bauteile der neueren Generation damit nicht debuggen kann: Man kann ein Projekt anfangs mit passenden Controllern beginnen, ein Mega32 ist ja kein Exot. Wenn man das soweit hat, dass man Funktionen oder Speicherkapazitäten benötigt, die damit nicht mehr handhabbar sind, dann braucht man meist keinen Debugger mehr.
JTAG ICE mkII ist mir dann doch zu abgedreht teuer und mit dem Drachen kann ich meine 644er nicht beackern. Da dann doch lieber die LowCost DIY Lösung. >Nö, wenn du JTAG hast, brauchst du kein ISP. Stimmt, hatte ich übersehen. Macht die Sache natürlich noch interessanter und schiebt sie auf meiner Wunschliste weiter nach oben. Also mal so einen "schrägen" Quarz kaufen.
Wie schon erwähnt, den JTAGICE mkII gabs vor kurzem zusammen mit einem STK500 für 150€ (Da habe ich auch mal zugeschlagen, da ich den mkII für mein AVR32-Board brauchte ;))
Ron wrote: >>Kommt auf die Variable an. Wenn sie global oder static ist, dann >>schreibt die C-Norm eine implizite Initialisierung mit 0 vor. Für andere >>Variablen ist es nicht vorgeschrieben, aber auch nicht verboten. > > Genau das war's was ich mal gelesen hatte. Statische Variable sind in dem Zusammenhang nicht das Problem. Das 'Leben' im Rechner spielt sich im wesentlichen auf dem Stack ab und dort wird eben üblicherweise nichts automatisch initialisiert.
Ron wrote: > JTAG ICE mkII ist mir dann doch zu abgedreht teuer Daher ja der Hinweis auf die Sonderangebote. > und mit dem Drachen > kann ich meine 644er nicht beackern. Da dann doch lieber die LowCost DIY > Lösung. Und was macht die mit dem 644er? Die kann ihn nichtmal flashen, was der Drachen noch könnte. Mit dem Drachen könntest du sogar den 644er debuggen, wenn du dich auf 32 KiB beschränkst.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.