Hallo zusammen, nun habe ich mein recht umfrangreiches Programm endlich fast fertig, wird der Speicher knapp. AVR-GCC sagt: Device: atmega328p Program: 32410 bytes (98.9% Full) (.text + .data + .bootloader) Data: 1905 bytes (93.0% Full) (.data + .bss + .noinit) Optimieren kann ich den Code sicher noch, wird aber sehr schwierig. Ich habe schon - soweit es mir möglich war - alle Register gezogen. Mit PROGMEM habe ich experimentiert, das macht unter Data einiges frei, allerdings brauche ich dafür extra Programmlogik für die wenig Platz ist. Und am Verhalten konnte ich auch keine Veränderungen feststellen. Das Seltsame: Flashe ich den Controller mit dem AVR-ISP II neu, startet der Microcontroller einwandfrei und arbeitet das Programm ab. Nach einem Reset ist er (meistens!) allerdings wie tot oder startet "seltsam" (z.B. mit Verzögerung). Das Programm gibt direkt am Anfang von main() einen Text auf einem LCD aus. Das nicht reproduzierbare Verhalten tritt schon auf, bevor der Controller überhaupt diese Nachricht anzeigt. Man sieht dann lediglich einen schwarzen Balken auf dem LCD. Warum funktioniert das Programm dann aber nach dem Flaschen ohne Probleme? Über Tipps würde ich mich sehr freuen, ich würde nur ungern auf einen anderen Controller (ATMega644) umsteigen ... Platinenlayout is schon fertig und es ist kaum noch Platz für einen größeren Controller ... :-/ Viele Grüße Sebastian
:
Bearbeitet durch User
Du musst zu allererst auch bedenken, dass die RAM-Angabe sich rein auf globale Variablen bezieht. Du brauchst noch RAM für lokale Variablen, Stack, ... Wenn voll, dann voll und dann passieren komische Dinge.
Es könnte am RAM liegen. Du hast etwas über 100 Bytes frei, reicht das für lokale Variablen und Stack? Sollte der Mega644 nicht pinkompatibel sein?
Bislang wurden für das Fehlverhalten nur Symptome am Display genannt. Kannst du auch an anderen Komponenten Fehlfunktionen erkennen? Es kann auch ein Timingproblem bei der Initialisierung des Displays sein, so daß es mal unter bestimmten Vorbedingungen funktioniert und unter anderen Bedingungen nicht mehr. Hast du eine oder mehrer Diagnose-LED oder einen seriellen Anschluß als Kontrollmöglichkeit? So Allgemein beschrieben ist eine zuverlässige Diagnose unmöglich! Da hilft nur ein systematischer Test, Komponente für Komponente. Ein "es funktioniert nicht" ist keine brauchbare Fehlerbeschreibung.
:
Bearbeitet durch User
Es gibt hier irgendwo einen Codeschnipsel, mit dem man im laufenden Betrieb den Minimalwert des noch freien RAMs bestimmen kann. Wenn da 0 rauskommt, braucht man eigentlich gar nicht weitersuchen.
1 | int freeRam (void) { |
2 | extern int __heap_start, *__brkval; |
3 | int v=(int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); |
4 | return v; |
5 | }
|
Hallo zusammen, herzlichen Dank für Euer Feedback!!! Ich werde mit Euren Tipps heute Abend auf Fehlersuche/Optimierung gehen. Ich werde den Speicherverbrauch messen und mal konsequent alles auf PROGMEM umstellen. Das mit der LCD-Initialisierung könnte auch stimmen. Denn da drüber scheint der Controller nicht zu kommen. Wobei es auch sein könnte, dass er gar nicht bis dahin kommt, denn das ist eigentlich das erste, was das Programm macht. Vielleicht ist der Speicher so voll, dass das eine Auswirkung auf das Timing hat. Mir ist es nur ein Rätsel, warum alles nach einem Reset durch den AVR-ISP (augenscheinlich) einwandfrei funktioniert. Incl. Timer, PWM, EEPROM-Funktionen, usw. ... Also am Stack oder lokalen Variablen kann das eigentlich nicht liegen. Zum Debuggen habe ich ein Oszi, da sollte sich evtl. was machen lassen. Ich kann den Code auch über Preprozesser-Direktiven auf eine serielle Ausgabe umkonfigurieren (damit läuft die Firmware auf einem Arduino), allerdings sind da einige Funktionen deaktiviert und der Speicherverbrauch ist deutlich niedriger ... Echt komisch, dieses nichtdeterministische Verhalten. Wirklich wohl ist mir bei der Sache nicht! Vielleicht sollte ich das Ding als Zufallszahlengenerator einsetzen. ;-) Viele Grüße Sebastian
Kommentier irgendwas optionales auf (z.B. die Display-Ausgabe). Wenn das Programm dann stabil läft, hast du warscheinlich einen Stack-Überlauf (also zu viel RAM belegt).
Das dein Programm direkt nach dem Flashen scheinbar ordnungsgemäß funktioniert deutet eher nicht auf einen Stackoverflow hin. Klingt eher nach nicht initialisierem Speicher oder sowas.
Sebastian M. schrieb: > Flashe ich den Controller mit dem AVR-ISP II neu, startet > der Microcontroller einwandfrei und arbeitet das Programm ab. Nach einem > Reset ist er (meistens!) allerdings wie tot oder startet "seltsam" (z.B. > mit Verzögerung). Unterschiede im Startverhalten sind meist Initialisierungsfehler (Reihenfolge, Zeitabläufe).
Sebastian M. schrieb: > Optimieren kann ich den Code sicher noch, wird aber sehr schwierig. Aus Erfahrung kann ich Dir sagen, dass man da noch jede Menge rausholen kann. Das fängt schon bei den Compiler-Optionen an (z.B. flto). Wahrscheinlich magst Du Dein Programm hier nicht posten, aber ich bin mir sicher, dass man 20-50% immer noch rausholen kann, wenn da mal ein anderer auf den Code schaut. Ich tippe nämlich auch auf einen Stacküberlauf - oder auf einen Überschreiber im RAM.
:
Bearbeitet durch Moderator
Sebastian M. schrieb: > Ich habe schon - soweit es mir möglich war - alle Register gezogen. Benutzt du hauptsächlich int statt char? Viele globale Variable? Lokale Variable werden nur dann angelegt, wenn sie auch beötigt werden. Danach ist der Speicher wieder frei. Und eine ganze dumme Frage: Ist die Optimierung eingeschaltet? Sebastian M. schrieb: > Das Programm gibt direkt am Anfang von main() einen Text auf einem LCD > aus. Sebastian M. schrieb: > Das Programm gibt direkt am Anfang von main() einen Text auf einem LCD > aus. Bei der Länge von Texten sollte man sich auch einschränken. Meistens sind einzelne Zeichen oder wenigstens Abkürzungen genauso aussagekräftig.
Die Routinen, die für den Zugriff auf PROGMEM benötigt werden sind nicht besonders umfangreich. Das hast Du bereits nach ein paar Zeilen Text wieder reingeholt. Auch wenn Du darauf achtest, dass DEINE Routinen schonend mit dem Stack umgehen, muss das nicht heißen, das es fertige, eingeblendete Fremdroutinen, auch tun. Wie von anderen gesagt: Die Speicherberechnung erfasst nur den "offensichtlichen" Speicher. Nicht den in den Routinen temporär reservierten, und nicht den Stack. Besonders lecker ist eine Rekursion. Viele Programmierer sichern, nach dem Einsprung in ihre Routinen, alle Register, die sie plattmachen. Egal ob Du sie nutzt oder nicht. Eine 5-fache Verschachtelung von Unterroutinen mit jeweils 5 gesicherten Registern ergibt ohne den program counter 25 Byte die keiner kennt. Ups! Diese Programmierroutine ist aber nicht unbedingt als Fehler anzusehen, sondern eher als Reflex.
@ Sebastian M. (cyberseb) >Das Seltsame: Flashe ich den Controller mit dem AVR-ISP II neu, startet >der Microcontroller einwandfrei und arbeitet das Programm ab. Nach einem >Reset ist er (meistens!) allerdings wie tot oder startet "seltsam" (z.B. >mit Verzögerung). >Das Programm gibt direkt am Anfang von main() einen Text auf einem LCD >aus. >Das nicht reproduzierbare Verhalten tritt schon auf, bevor der >Controller überhaupt diese Nachricht anzeigt. Man sieht dann lediglich >einen schwarzen Balken auf dem LCD. Das ist kein Speicherproblem sondern ein Initialisierungsproblem, dann zu diesem Zeitpunkt sind ja noch keine großen Funktionen aufgerufen worden. Wahrscheinlich fehlt ein Delay am Anfang, um das LCD korrekt zu initialisieren. Vielleicht fehlt auch nur das Einschalten das Brown Out Detektors bzw. einer großen Startup Zeit (AVR Fuses), damit die CPU erst bei ausreichend Spannung losläuft. Das kann vor allem im Zusammenhang mit dem LCD ein Problem sein. Der Controller kann auch mit 1,8V korrekt arbeiten und rennt los, das LCD kann das nicht.
Hallo zusammen, danke für Eure Tipps! Jetzt geht es weiter ... Also, ich habe das Problem auf einem zweiten Gerät reproduziert. An ein (einfaches) Initialisierungsproblem glaube ich nicht - es hängt definitiv mit dem Speicher zusammen. Ob es geht, oder nicht, ist von diesen zwei Codezeilen abhängig. Und die sind mitten im Code, weit, weit weg von der Initialisierung: screen_locate(1, 2); screen_print("(Speed restored)"); Kommentiere ich die aus, läuft alles. Sind sie drin, funktioniert das Programm direkt nach dem Flashen, aber nicht mehr nach einer Stromunterbrechung. Die LCD-Initialisierung funktioniert sonst ohne jegliche Probleme. Ich könnte mir nur vorstellen, dass es evtl. aufgrund einer anderen Speicherinitialisierung (ausgelöst vom AVR-ISP) "irgendwie" doch funktionieren kann. Und mit dem o.g. Code startet das Programm auch manchmal (selten), aber verzögert (mit schwarzen Balken). Tja, ich werde wohl experimentieren und weiter optimieren müssen. Diverse von Euch genannte Tipps hatte ich schon umgesetzt (z.B. CHAR statt INT), ebenso sich widerholende Aufrufe in Schleifen/Funktionen gepackt (hat alles ein paar Byte gebracht). Jetzt werde ich das LCD mal "abschalten" und schauen, ob das Programm soweit läuft. Ebenso werde ich mal testhalber Codeblöcke deaktivieren. Den Code möchte ich nicht zeigen, da es eine Firmware für ein DIY-Kit werden soll. Nicht um richtig Geld zu machen, sondern um Geld in die Hobby-Kasse zurückfließeb zu lassen. Darum gibt es auch eine freie Arduino-Version für die DIY-Community. Wen es interessiert, es geht um einen frequengeregelten Sinus-Inverter zur Geschwindigkeitsregelung von Plattenspielern: http://mate-labs.de/magicquartz/ Viele Grüße Sebastian
Jupp - nehme ich einen Programmteil raus (direkt mit einem "return;" am Anfang einer größeren Funktion), funktioniert es einwandfrei. Es liegt also am Speicherverbrauch! Das Verhalten mit dem Reset durch den AVR-ISP macht mich aber ganz kirre ... Gruß, Sebastian
Mach mal eine Ausgabe des aktuell freien Speichers (auf das Display) rein. Dann siehst du gleich deine Fortschritte.
Sebastian M. schrieb: > screen_print("(Speed restored)"); Hast du mehrere Code-Zeilen dieser Art? Speicher kannst du dann sparen indem du den String im Flash lässt und dir eine screen_print-Variante schreibst, die auch einen String aus dem Flash schreiben kann, z.B.:
1 | //fuer den Zugriff auf den Flashspeicher
|
2 | #include <avr/pgmspace.h> |
3 | ...
|
4 | screen_print_p(PSTR("(Speed restored)"); |
5 | ...
|
6 | //funktion zum Schreiben von Strings aus dem Flash
|
7 | void screen_print_p(const char* stringFromFlash) { |
8 | register char zeichen; |
9 | while( zeichen = pgm_read_byte(stringFromFlash++) ) { |
10 | //lcd_put_char sei die Funktion, die generell nur ein Zeichen schreibt.
|
11 | screen_print_char(zeichen); |
12 | }
|
13 | }
|
14 | ...
|
Auf ähnliche Weise kann man auch das EEPROM nutzen wenn man es sonst nicht braucht, der RAM aber knapp wird.
:
Bearbeitet durch User
Hallo zusammen, @Gerhard: danke, das freut mich!!! @Tom: Um die 450-500 Bytes sind jetzt frei im Betrieb nach der von sylaina vorgeschlagenen Änderung. @sylaina: Gerade gemacht, danke! Ich bekomme jetzt: Program: 32468 bytes (99.1% Full) (.text + .data + .bootloader) Data: 1517 bytes (74.1% Full) (.data + .bss + .noinit) Das hat wohl einiges gebracht. Aber - trotz der ganzen Änderungen am Programmcode - ändert das am Verhalten absolut nichts! Sobald ich das hier einbaue ... screen_locate(1, 2); screen_print("(Speed restored)"); ... startet das Ding nicht mehr. Ich habe schon versucht die Klammern zu ersetzen (man weiß ja nie?) und ein Delay vor die LCD-Init-Routine gepackt, bringt alles nichts. Das muss irgendwie mit dem Speicher zusammenhängen. Mir wird wohl nichts anderes übrig bleiben, den Code kleiner zu bekommen. Vielleicht sollte ich das EEPROM-Wearleveling (wird von Zeit zu Zeit um ein Byte weitergeschoben) raushauen, das wird keinen stören! ;-) Viele Grüße Sebastian
Hmmm, mit screen_locate(1, 2); screen_print_p(PSTR("(Speed restored)")); funktioniert das Ding wiederum. Krank! Gruß, Sebastian
@ Sebastian M. (cyberseb) >Aber - trotz der ganzen Änderungen am Programmcode - ändert das am >Verhalten absolut nichts! Sobald ich das hier einbaue ... > screen_locate(1, 2); > screen_print("(Speed restored)"); >... startet das Ding nicht mehr. Tja, es ist immer noch ein Initialisierungsproblem ;-) > Ich habe schon versucht die Klammern zu >ersetzen (man weiß ja nie?) und ein Delay vor die LCD-Init-Routine >gepackt, bringt alles nichts. >Das muss irgendwie mit dem Speicher zusammenhängen. Nö. Du hast doch gerade das GEGENTEIL bewiesen!! > Mir wird wohl nichts >anderes übrig bleiben, den Code kleiner zu bekommen. Nö. Einfach mal nachdenken und eine systematische Fehlersuche betreiben.
Sebastian M. schrieb: > Das Seltsame: Flashe ich den Controller mit dem AVR-ISP II neu, startet > der Microcontroller einwandfrei und arbeitet das Programm ab. Nach einem > Reset ist er (meistens!) allerdings wie tot oder startet "seltsam" (z.B. > mit Verzögerung). Und wenn die Versorgung fur 1-2 Minuten abgeschaltet wird ? Nach RESET werden nur die I/O Register auf Initial Values gesetzt - aber RAM bleibt unverandert. Ich glaube, dass irgendeine Routine (deine oder Library) falsch annimmt, dass irgendeine RAM-Adresse auf 0 ist. Nach flashen oder Stromabschalten stimmt das auch, aber nach einem RESET nicht. P.S. Check mal: ' screen_print("(Speed restored)"); ' (Assembler listing)
:
Bearbeitet durch User
@ Marc Vesely (Firma: Vescomp) (logarithmus) > Ich glaube, dass irgendeine Routine (deine oder Library) falsch > annimmt, dass irgendeine RAM-Adresse auf 0 ist. Nicht unbedingt 0, ggf. aber ein SINNVOLLER Wert. > Nach flashen oder Stromabschalten stimmt das auch, Nein. Nach einem Power Up Oder Flashen ist der SRAM ebenso undefiniert. Er kann aber besonders beim Flashen durch das vorherige Programm auf einen zufällig brauchbaren Wert gesetzt worden sein. > Check mal: ' screen_print("(Speed restored)"); ' Ja. > (Assembler listing) Nein. Der Fehler liegt mit an Sicherheit grenzender Wahrscheinlichkeit NICHT im Compiler oder Assembler sondern beim Entwickler der Funktionen. Eine Sichtung des C-Quelltextes reicht vollkommen aus. Und man sollte die Compilerwarnungen ernst nehmen. Sowas wie "variable x may be uninitialized" ist sowas. Im Zweiflesfall setzt man ALLE lokalen Variabelen bei der Definition in der Funktion erstmal auf 0.
1 | int x = 0; |
Hallo zusammen, oha - jetzt wird es interessant! Ich will den Fehler undbedingt finden, aus Interesse. Leider habe ich den problematischen Code nicht gesichert (ich Depp), aber der sollte sich aus dem backup vom Vortag wieder herstellen lassen. Viel hatte ich nicht geändert seit dem. Also an unitialisierten Variablen liegt es nicht. Es gibt auch keine Compiler-Warnungen. Und der o.g. Codeschnipsel ist m.E. auch nicht direkt schuld an dem Problem, da der bei dem von mir beschriebenen Fehlverhalten gar nicht ausgeführt wird (auch nicht zwischen dem Neustart). Der ist "tief" im Code vergraben und muss explizit vom User über eine Funktion aufgerufen werden. Ich glaube eher, dass das Problem auftritt, wenn die Programmgröße einen gewissen Wert überschreitet. Vielleicht liegt es doch am Compiler. Ich glaube ich verwende eine ältere Version von 2012 oder 2013, weil mit der aktuellen mein Eclipse Probleme hatte, den ATMega328 korrekt zu erkennen. Heute Abend gehts weiter - aber viel Hoffnung, das zu finden, habe ich nicht ... :-/ Gruß, Sebastian
tolles projekt. Ich hatte seinerzeit eine dds (32.5kHz) aufgesetzt, die 60hz Sinus erzeugt und das über Mosfettreiber / Halbbrücken mit einem Printtrafo auf 110Volt hochtransformiert. Spielt (im "Schuhkarton") ganz toll, war ruckzuck fertig und spielte etwas Geld in die Hobbykasse zurück. man kann die Sache aber auch aufblasen und natürlich auch n Display anschliessen :) Axelr. DG1RTO
@Sebastian M. (cyberseb) (Gast) >Ich will den Fehler undbedingt finden, aus Interesse. Gut! >Leider habe ich >den problematischen Code nicht gesichert (ich Depp), Schlecht! >Also an unitialisierten Variablen liegt es nicht. Es gibt auch keine >Compiler-Warnungen. Sind die alle eingeschaltet? >Und der o.g. Codeschnipsel ist m.E. auch nicht direkt schuld an dem >Problem, da der bei dem von mir beschriebenen Fehlverhalten gar nicht >ausgeführt wird (auch nicht zwischen dem Neustart). Dannhast du uns aber schön veralbert! >Ich glaube eher, dass das Problem auftritt, wenn die Programmgröße einen >gewissen Wert überschreitet. Welche theoretische Überlegung würde denn DAFÜR sprechen? Du macht hier genau das Gegenteil einer systematischen Fehlersuche. Du stellt wilde Theorien ohne Begründung auf. :-( >Vielleicht liegt es doch am Compiler. Möglich, aber unwahrscheinlich. >Heute Abend gehts weiter - aber viel Hoffnung, das zu finden, habe ich >nicht ... :-/ Mit DER Einstellung sicher nicht.
Falk B. schrieb: >> Check mal: ' screen_print("(Speed restored)"); ' > > Ja. > >> (Assembler listing) > > Nein. Der Fehler liegt mit an Sicherheit grenzender Wahrscheinlichkeit > NICHT im Compiler oder Assembler sondern beim Entwickler der Funktionen. Genau das habe ich auch behauptet. 'screen' wird (auch wenn er erst später aufgerufen wird) am Anfang initialisiert. Wie das alles genau gemacht wird und was 'screen_print' für sich reserviert und danach erwartet, sieht man nur aus Assembler listing. Sebastian M. (cyberseb) schrieb: > Ich glaube eher, dass das Problem auftritt, wenn die Programmgröße einen > gewissen Wert überschreitet. Na dann mache mal eine Dummy-Funktion, die genauso lang ist wie 'screen', werfe 'screen' raus und... Wetten, dass es keine Probleme mehr gibt ?
:
Bearbeitet durch User
@Falk: >>Also an unitialisierten Variablen liegt es nicht. Es gibt auch keine >>Compiler-Warnungen. > > Sind die alle eingeschaltet? Standard-Einstellungen. Ich schaue mal, ob sich da noch etwas machen lässt! Guter Tipp - danke! >>Und der o.g. Codeschnipsel ist m.E. auch nicht direkt schuld an dem >>Problem, da der bei dem von mir beschriebenen Fehlverhalten gar nicht >>ausgeführt wird (auch nicht zwischen dem Neustart). > > Dannhast du uns aber schön veralbert! Warum denn? Ich hatte oben doch klar geschrieben, dass dieser Code weit weg von der Initialisierung ist. Sorry, falls ich da nicht präzise genug war - als Hilfesuchender möchte ich Euch auf keinen Fall veralbern! >>Ich glaube eher, dass das Problem auftritt, wenn die Programmgröße einen >>gewissen Wert überschreitet. > > Welche theoretische Überlegung würde denn DAFÜR sprechen? > Du macht hier genau das Gegenteil einer systematischen Fehlersuche. Du > stellt wilde Theorien ohne Begründung auf. :-( Halten wir fest, die Fakten: - Der Code hat scheinbar Auswirkungen auf das Programmverhalten, obwohl er gar nicht ausgeführt wird. - Es macht einen Unterschied, ob der Controller vom AVR-ISP geflasht und resettet wird oder er per Stromunterbrechung neu startet. Daraus folgen meine Überlegungen: Der o.g. C-Code macht das Programm größer. Teile des Binärcodes landen an anderen Stellen im Flash. Allerdings sollte das keinen Einfluss auf das Initialisieren von irgendwelchen Variablen (die ich ja eh alle initialisiere) oder auf das Timing in der LCD-Initialisierung haben. Das könnte vielleicht wirklich ein Compiler-Bug sein. Andererseits glaube ich das aber nicht (unwahrscheinlich) und das Verhalten im Zusammenhang mit dem AVR-ISP spricht auch dagegen. M.E. ist es kein einfaches Initialisierungsproblem des LCDs - die Routine hat immer einwandfrei funktioniert. Es muss irgend ein Mist im RAM liegen (Werte von Variablen), die es verhindern, dass das Programm über die LCD-Initialisierung kommt. Wie würdest Du denn dann "systematisch" vorgehen? Ich werde heute Abend noch ein paar von Euren Vorschlägen durchgehen. @DG1RTO: Danke! Ja, das Ding ist wirklich aufgeblasen, da hast Du Recht. :-) Gruß, Sebastian
@ Sebastian M. (cyberseb) (Gast)
>Wie würdest Du denn dann "systematisch" vorgehen?
Hab ich das nicht schon mehrfach getan?
Zuerst mal die Brown Out Fuse setzen, ebenso die längste Wartezeit für
den Oszillator (beim 328er glaub ich 64ms).
Damit ist die Spannungsversorgung für das LCD! schon mal OK, wenn das
Programm losläuft.
Dann kann man mal den Großteil des Programms (im main) auskommentieren
und nur die IO-Initialisierung und die fragliche Funktion stehen lassen
und testen.
ICh vermute, daß dann das Problem immer noch da ist.
So wird das nichts! Du hast dich entschieden zu glauben, daß es an der Codegröße liegt und willst das nun beweisen. Wenn der Fehler woanders liegt, kann dieses Vorgehen nur scheitern. Die Code-Größe an sich hat definitiv keine Auswirkung auf das Problem. Allenfalls wenn der Programmspeicher nicht ausreichen würde, aber dann kommst du gar nicht erst soweit den Flashvorgang abzuschließen. Allenfalls der Rambedarf könnte Einfluß haben, aber der hängt nicht mit der Codegröße zusammen! Außerdem ist der Rambedarf in der Regel nicht davon abhängig wie resettet wurde. Allenfalls wenn du eine Routine hättest die den Resetgrund auswertet, könnte das Verhalten diesbezüglich unterschiedlich sein. Ansonsten ist das Verhalten des Codes/der Software immer die gleiche. Solange keine Eingaben in den Bootvorgang eingebaut werden, ist das Startverhalten dann immer identisch. Dann bleibt nur ein Unterschiedliches Verhalten der Hardware. Gehe es systematisch durch. Nehme alles raus bis auf die Displayroutinen und füttere die mit Dummydaten zur Ausgabe. Bleibt das Problem? Suche eine alternative Ausgabemöglichkeit, Serieller Port, LED etc Bevor wir mit On-Chip-debugging bzw JTAG loslegen würde ich im Program an den entscheiden Punkten eine Einfache Ausgabefunktion zur Kontrolle einfügen. Dann kannst du sehen wie weit das Programm kommt und ob die Reihenfolge und Inhalte der Ausgaben vom Erwarteten Verhalten abweichen. Sebastian M. schrieb: > Das Seltsame: Flashe ich den Controller mit dem AVR-ISP II neu, startet > der Microcontroller einwandfrei und arbeitet das Programm ab. Nach einem > Reset ist er (meistens!) allerdings wie tot oder startet "seltsam" (z.B. > mit Verzögerung). GANZ ENTSCHEIDENDE FRAGE Was exakt verstehst du in diesem Falle unter einem Reset? Strom aus, Strom an? Strom aus, Pause, Strom an? Benutzung eines Resettasters am Reset-Pin? Oder oder oder... Falls du einen Resettaster nutzt, wie ganau sie die komplette Beschaltung des Pins exakt und absolut vollständig bei dir aus? Welches Display verwendest du?
:
Bearbeitet durch User
Hallo Falk & Carsten (und alle), danke weiterhin für Eure Hilfe - ich hoffe, dass bei der Sache wenigstens neue Erkenntnisse für das Forum herauskommen. (Ja, vermutlich sitzt das Problem vor dem Computer, aber ein Ideal-Standard-Fehler ist das glaube ich auch nicht ...) Ihr habt Recht, meine Vorgehensweise bisher war sicher nicht systematisch genug. Vermutlich weil ich mit meinem Latein bei der Sache einfach am Ende bin. Ich konnte einen älteren Code wiederherstellen, der das Problem hat. An dem Display-Aufruf (Codeschnipsel oben) liegt es hier nicht, es muss also etwas Anderes sein. Viele der Dinge, die Ihr genannt habt, waren schon immer OK: - Kein Watchdog - Verwende Brown-Out-Detection - Wartezeit auf Maximum Und hier hast Du Dich geirrt, Falk: Kommentiere ich einen Codeblock in der main() aus (der niemals zur Auführung kommt und was das Programm kleiner macht), funktioniert es! Weiß der Kuckuck, warum. Ich habe nun meine Tools (Eclipse AVR Plugin) und den Compiler geupdated. WOW, das hat noch einiges an der Programmgröße eingespart, mit dem Ergebnis, dass es mit allen Update-Kombinationen funktioniert. Eclipse Alt + Compiler Alt: Fehler vorhanden Eclipse Neu + Compiler Alt: OK Eclipse Alt + Compiler Neu: OK Eclipse Neu + Compiler Neu: OK ("Eclipse" meint hier das AVR-Plugin) Die Ergebnisse hängen an. Was ich so gesehen habe, verwendet das Eclipse AVR Plugin z.B. die Optionen -ffunction-sections -fdata-sections. Aber das ist nicht alles; auch wenn ich diese deaktiviere, ist das Programm kleiner. Dafür hat der neue Compiler doch noch uninitialisierte Variablen gefunden. Ich werde nun auf die Alt/Alt-Kombi zurückwechseln und weitertesten. Viele Grüße, Sebastian
-Wall -Wextra nicht vergessen. Der findet bestimmt noch ein paar Code-Probleme.
Sebastian M. schrieb: > Das Verhalten mit dem Reset durch den AVR-ISP macht mich aber ganz kirre > ... Dir ist schon bewußt daß der AVRISP einen häßlichen Bug hat der dafür sorgt daß eine Sekunde nach dem Einschalten nochmal grundlos ein Reset gegeben wird? Vielleicht erklärt das das seltsame Verhalten das Du beim Einschalten beobachtest. Stöpsel den mal ab und probier es ohne.
Tom, danke! -Wextra hat tatsächlich noch ein paar unbenutzte an Funktionen übergebene Parameter gefunden. Das dürfte den Code kleiner machen, aber das ursprüngliche Problem sollte das auch nicht lösen ... :-( Bernd, ach, das ist ein Bug? (Man gewöhnt sich dran :-D) Leider startet der Mega ohne den ISP auch nicht ... Also die uninitalisierten Variablen sind unkritisch. Einmal handelte es sich tatsächlich um ungenutzten Code (lustig, was für einen Mist man bei vielen kleinen Änderungen so produziert), das andere ist das Auslesen des ADCW-Registers in der ADC_Init()-Routine. Der Compiler weiß nicht, dass das nötig ist.
Ich glaube, Bernd meinte, du sollst mal händisch nach einer Sekunde einen Reset machen und gucken, ob es dann "anspringt".
Sebastian M. schrieb: > Tom, danke! -Wextra hat tatsächlich noch ein paar unbenutzte an > Funktionen übergebene Parameter gefunden. Das dürfte den Code kleiner > machen, aber das ursprüngliche Problem sollte das auch nicht lösen ... > :-( Willst du nicht hören oder ist das ignore ? Unbenutzte Parameter sind NICHT dein Problem, sondern Variablen die BENUTZT, aber vorher nicht initialisiert sind. Sogar die Reihenfolge der Variablendeklarationen kann dein Problem zum Verschwinden bringen. Von mir aus kannst du weiter stur annehmen, dass Compiler daran schuld ist und dein Program keine Initialisierungfehler enthält. Ich bin raus.
Marc, ich habe keine uninitialisierten Variablen! Sorry, falls ich das nicht explizit gesagt habe. Natürlich versuche ich Eure Ratschläge und Tipps zu berücksichtigen und mich zu melden, aber ich kann auch nur Schritt für Schritt vorgehen ... Jedenfalls danke für Deine Hilfe und Zeit!!! Gruß, Sebastian
Nachtrag: Das mit "unitialisiert" von mir oben ist tatsächlich falsch. --------------- ../main.c: In function 'ADC_Init': ../main.c:815:15: warning: variable 'result' set but not used [-Wunused-but-set-variable] unsigned int result = 0; --------------- Kommt von: --------------- void ADC_Init(void) { unsigned int result = 0; ... result = ADCW; } --------------- Das hat nichts mit "uninitialisiert" zu tun, sondern dass die Variable nicht mehr verwendet wird, der GCC aber nicht weiß, dass ich ADCW auslesen muss. Sorry, war mein Fehler. Ich will Euer Feedback absolut nicht ignorieren, sondern schätze die Zeit, die Ihr aufbringt, SEHR. Gruß, Sebastian
:
Bearbeitet durch User
Spätestens -Wextra aktiviert -Wuninitialized Wenn da nix kommt (ich sehe oben in den angehängten Ausgaben nix), ist es scheinbar ok? Oder verwendest du irgendwo __attribute__((section(".noinit"))) oder sowas?
Carsten R. schrieb: > GANZ ENTSCHEIDENDE FRAGE > Was exakt verstehst du in diesem Falle unter einem Reset? Eine Antwort darauf wäre hilfreich. Ich habe das nicht ohne Grund fett geschrieben.
Sebastian M. schrieb: > ich habe keine uninitialisierten Variablen! Sorry, falls ich das nicht > explizit gesagt habe. Natürlich versuche ich Eure Ratschläge und Tipps > zu berücksichtigen und mich zu melden, aber ich kann auch nur Schritt > für Schritt vorgehen ... Ok, dann vergiss FLASH und konzentriere dich auf RAM. Was ich mit Reihenfolge der Deklaration meinte, ist folgendes:
1 | int ArrPtr; |
2 | int DummyArr[20]; |
3 | int SehrWichtigeVar1, SehrWichtigeVar2; |
kann sehr lange funktionieren, muss aber nicht.
1 | DummyArr[ArrPtr] = 12345; |
ist OK, wenn ArrPtr < 20 ist, was aber wenn ArrPtr > 20 ist ? Deine SehrWichtigeVar1 hat plotzlich einen Wert von 12345. Wenn du aber so deklarierst:
1 | int SehrWichtigeVar1, SehrWichtigeVar2; |
2 | int ArrPtr; |
3 | int DummyArr[20]; |
4 | int UnwichtigeVar1, UnwichtigeVar2; |
kann das sehr lange gut gehen und unentdeckt bleiben. So etwas wiederum:
1 | int SehrWichtigeVar1, SehrWichtigeVar2; |
2 | int DummyArr[20]; |
3 | int ArrPtr; |
4 | int UnwichtigeVar; |
kann richtiges Chaos verursachen - muss aber nicht. Marc V. schrieb: > Check mal: ' screen_print("(Speed restored)"); ' > (Assembler listing) Wie funktioniert diese Routine (wird RAM benutzt, wenn ja, wieviel RAM, woher, etc.) Nur als Beispiel - ich kenne dein Programm nicht. EDIT: Soviel ich weiß, überprüft C die Arraygrenzen nicht, deswegen...
:
Bearbeitet durch User
@Tom: -Wuninitialized sagt, dass alles OK ist. @Carsten: "Strom aus, Pause, Strom an". Ich habe auch schon längere Pausen (mehrere Minuten) probiert, erfolglos. Der Reset-Pin hängt über einen 10kOhm-Widerstand an VCC. Sollte eigentlich passen. Als nächstes werde ich mal mit dem Oszi schauen, ob das Programm im Fehlerfall überhaupt irgendetwas macht. Könnte mit meinem alten Hameg 205 schwierig werden. @Marc: Danke für Deine anschauliche Erklärung! Ich kann nicht 100%ig ausschließen, dass ich in irgend einem Array einen derartigen Fehler habe. Werde ich prüfen. Die Funktion screen_print() ist nichts Besonderes, sondern nur ein Wrapper, damit ich die Ausgabe auf die serielle Schnittstelle für die Arduino-Version umleiten kann:
1 | void screen_print(const char *s) { |
2 | #ifdef LCDDISPLAY
|
3 | lcd_text((u8*) s); |
4 | #endif
|
5 | #ifdef SERIAL
|
6 | uart_puts(s); |
7 | #endif
|
8 | }
|
9 | |
10 | ...
|
11 | |
12 | void lcd_text(u8 *t) { |
13 | while (*t) { |
14 | lcd_data(*t); |
15 | t++; |
16 | }
|
17 | }
|
Das ASM-Listing kann ich auch noch liefern, aber bis Sonntag ist erstmal Schluss (geht nicht, bin bei der Family eingespannt ...). Viele Grüße Sebastian
Marc V. schrieb: > Was ich mit Reihenfolge der Deklaration meinte, ist folgendes: > int ArrPtr; > int DummyArr[20]; > int SehrWichtigeVar1, SehrWichtigeVar2; kann sehr lange funktionieren, > muss aber nicht. > DummyArr[ArrPtr] = 12345; > ist OK, wenn ArrPtr < 20 ist, was aber wenn ArrPtr > 20 ist ? > Deine SehrWichtigeVar1 hat plotzlich einen Wert von 12345. Frage interessehalber: Wenn DummyArr nur 20 Felder lang ist, sollte der Compiler doch meckern wenn man versucht auf Feld 20 oder größer zuzugreifen, oder? Also zumindest bei mir sagt dann der Compiler (ist "nur" ein Warning): Array subscript is above array bounds. OK, ich hab auc -Werror an und entferne auch alle Warnings.
@Sebastian M. (cyberseb) >Als nächstes werde ich mal mit dem Oszi schauen, ob das Programm im >Fehlerfall überhaupt irgendetwas macht. Könnte mit meinem alten Hameg >205 schwierig werden. Dazu braucht man kein Oszi. Eine LED kann man immer blinken lassen, auch so langsam, das man als Mensch was sieht. Z.B. nach jedem größeren Funktionsblock die LED toggeln. >Das ASM-Listing kann ich auch noch liefern, Das interessiert keinen. Poste VOLLSTÄNDIGEN Code als Anhang. Siehe Netiquette.
M. K. schrieb: > Frage interessehalber: Wenn DummyArr nur 20 Felder lang ist, sollte der > Compiler doch meckern wenn man versucht auf Feld 20 oder größer > zuzugreifen, oder? Das kann der Compiler nur in den alleroffensichtlichsten Fällen feststellen, wenn überhaupt. Der Compiler wird jedoch NICHT versuchen das Programm solange zu simulieren bis er alle möglichen Werte von allen Variablen zu irgendeinem späteren Zeitpunkt kennt, das ist nicht praktikabel oder stellenweise gar nicht möglich. Nur offensichtliche Sachen die er in kürzester Zeit vollständig beweisen kann werden angemeckert. Es gibt jedoch Compiler die können (auf expliziten Wunsch) Laufzeit-Checks in den Code einbauen wenn Array-Grenzen, Stack-Grenzen oder andere Grenzen überschritten werden und dann an Ort und Stelle unverzüglich eine aussagekräftige Laufzeitfehlermeldung werfen bevor es im halbzerstörten Zustand noch 2 Minuten weiterläuft und dann erst crasht. Aber sowas geht natürlich auf die Performance und auf die Codegröße. Und ich wüßte jetzt auch nicht ob der gcc das überhaupt kann oder irgendein ein anderer C-Compiler, ich kenn das von verschiedenen Pascal Compilern, sowohl historischen als auch aktuellen, da war das schon immer ein bemerkenswertes optionales Zusatz-Feature das es von den meisten anderen Compilersprachen unterschied.
Falk B. schrieb: >>Das ASM-Listing kann ich auch noch liefern, > > Das interessiert keinen. Poste VOLLSTÄNDIGEN Code als Anhang. Siehe > Netiquette. Warum so ausschliesslich ? Mich würde das (vielleicht) interessieren. Vielleicht auch nicht... Oder doch ? Posten geht heutzutage ziemlich einfach, man muß es nicht selbst zum Forum tragen, also ja, kannst posten. Oder auch nicht, wie du willst...
Bernd K. schrieb: > Und ich wüßte jetzt auch nicht ob der gcc das überhaupt kann oder > irgendein ein anderer C-Compiler, ich kenn das von verschiedenen Pascal > Compilern, sowohl historischen als auch aktuellen, da war das schon > immer ein bemerkenswertes optionales Zusatz-Feature das es von den > meisten anderen Compilersprachen unterschied. Und natürlich die Klarheit, Übersichtlichkeit etc. Auch nach Monaten (oder Jahren) weiss man noch, was da läuft, im Gegensatz zu manch anderen Sprachen (wer hat was von C gesagt ?)
Sebastian M. schrieb: > @Carsten: "Strom aus, Pause, Strom an". Ich habe auch schon längere > Pausen (mehrere Minuten) probiert, erfolglos. Das ist kein Reset sondern ein Neustart. Der Reset des Programmerst zupft einmal an der Resetleitung, während die Spannung stabil bleibt. Viele ICs erwarten vorab eine Pause nachdem de Spannung stabil ist, bevor initialisiert werden kann. Du merkst an dieser Stelle den Unterschied zwischen dem Reset durch den Programmer und deinem "reset"? Noch einmal: Welches Display wird verwendet?
Carsten R. schrieb: > bevor initialisiert werden kann. Du merkst an dieser Stelle den > Unterschied zwischen dem Reset durch den Programmer und deinem "reset"? Kläre mich auf, bitte.
Carsten R. schrieb: > Sebastian M. schrieb: >> @Carsten: "Strom aus, Pause, Strom an". Ich habe auch schon längere >> Pausen (mehrere Minuten) probiert, erfolglos. > > Der Reset des Programmerst > zupft einmal an der Resetleitung, während die Spannung stabil bleibt
Marc V. schrieb: > Kläre mich auf, bitte. Hat er schon gemacht. Etwas genauer: Bei deinem Reset mit Strom aus, Pause, Strom an sind bis zum Programmstart möglicher Weise noch nicht alle Baugruppen (UART, ADC und Co) im µC mit ausreichend Spannung versorgt, auch das LCD ist möglicherweise noch nicht bereit wenn der µC die ersten Befehle an das LCD schickt. Wenn der ISP den µC resetet wird im Prinzip nur der Befehlszeiger wieder an den Start gesetzt, aber alle Komponenten sind da schon startklar.
M. K. schrieb: > Frage interessehalber: Wenn DummyArr nur 20 Felder lang ist, sollte der > Compiler doch meckern wenn man versucht auf Feld 20 oder größer > zuzugreifen, oder? Nein, der Compiler kann das im Allgemeinen nicht, bzw nur unter bestimmten Voraussetzungen, z.B. wenn du beim Kompilieren den Zugriff mit einem konstanten Index im Code stehen hast. Es muß zum Zeitpunkt des kompilierens schon erkennbar sein, daß ein Zugriff außerhalb der Grenzen erfolgt, bzw. erfolgen könnte. Bei einem variablen Index müßte sowohl der Compiler entsprechend intelligent, als auch aus dem Code heraus der Wertebereich der Variablen exakt vorhersehbar sein. Das ist nicht immer grundsätzlich gegeben. Bei einer einfachen for-Schleife wäre zum Beispiel machbar. Stattdessen kann man den Zugriff an sich in eine Funktion einbetten die zur Laufzeit prüft ob der Zugriff korrekt erfolgt. Das kostet natürlich Ressourcen, erfordert aber keine so speziellen Vorbedingungen.
Hallo zusammen, @Falk: Klar kann man das auch mit einer LED machen. Aber hier müsste ich ggf. den Code anpassen, was ich vermeiden möchte. Das mit dem Oszi sollte schon klappen, es ist ja ein digitales Speicheroszi und für eine One-Shot-Messung sollte es taugen. Nur um zu sehen, dass das Programm irgendetwas tut. Mir ist es klar, dass es "pikant" ist, um Euren Rat zu fragen, ohne den Quellcode zeigen zu wollen. Wenn ich gewusst hätte, dass wir derart tief einsteigen, hätte ich vermutlich den Thread gar nicht erst gestartet. Ich war halt der Hoffnung, dass das vielleicht auf dieses Phenomen schonmal gestoßen ist. Ich bin jedoch guter Dinge, dass noch etwas Interessantes für das Forum herauskommt. @Carsten: Das Display ist das "DISPLAYTECH 126C" von Reichelt (http://www.reichelt.de/LCD-162C-LED/3/index.html?&ACTION=3&LA=446&ARTICLE=31653&artnr=LCD+162C+LED&SEARCH=displaytech+162c). An das Display wird nur geschrieben, nichts wird gelesen. Also dass mein Programm auf irgendeine Antwort vom LCD wartet ist damit ausgeschlossen. Auch ein Delay vor dem Initialisieren des Displays brachte nichts. Abgesehen vom Display macht mein Programm im Fehlerfall sonst auch nichts. Normalerweise müssten die PWM-Timer (Sinus-Erzeugung) anspringen. Tun sie aber nicht. Das Programm startet m.E. gar nicht. Mit dem Neustart hast Du Recht, es ist kein Reset. Um es noch mal klar dazurstellen, das ist der Ablauf: 1. Schaltung läuft bereits (mit oder ohne Fehler, je nachdem, was vorher passiert ist) 2. Flashen mit AVR-ISP, incl. Reset durch den AVR-ISP 3. Programm startet korrekt => Kein Fehler 4. Schaltung ausschalten 5. Schaltung einschalten (Neustart) => Fehler, Programm startet nicht Dabei können 2. und 3. bzw. 4. und 5. beliebig wiederholt werden; das Verhalten ist reproduzierbar auf einem 2. Gerät. Der Grundaufbau des Programms in main() ist dabei so: 1. LCD initialisieren 2. Willkommensnachricht anzeigen 3. Timer konfigurieren und starten (Sinus-Erzeugung mit PWM, 1/10 Millisekunden-Zeitmessung) 4. ADC initialisieren (optischer Sensor, Strommessung) 5. Betriebsparameter aus dem "virtuellen" EEPROM (für Wear-Leveling geshiftet) auslesen 6. Sinus-Amplitde hochfahren 7. Hauptschleife für "Hauptmenü", in der ggf. der Benutzer Funktionen mit umfangreicherer Programmlogik startet, sowie "nicht echtzeitkritische" Routineaufgaben (Stromüberwachung) Über "1" kommt das Programm im Fehlerfall wohl nicht hinaus, sonst würde ich einen leeren Bildschirm sehen und keinen schwarzen Balken. Selbst wenn ich irgendwo Mist mit Arrays verzapft habe (z.B. über deren Grenzen hinausschreibe), kann das vor "7" gar nicht passieren (ich kann aber noch nicht ausschließen, dass ich bereits ab "3" einen Fehler drin habe). Und bei meinem oben beschrieben Ablauf wurden niemals Funktionen durch den User (also mich) aufgerufen. Lösche ich dagegen Programmteile aus der Hauptschleife (6) heraus (die ich während der Tests nicht benötige oder gar aufrufe), startet das Programm jedoch einfwandfrei! Ich werde an dem Code morgen weiterdebuggen, und will dabei wie folgt vorgehen: 1. Mit dem Oszi die LCD-Datenleitungen überprüfen, ob irgendetwas im Fehlerfall gesendet wird. 2. (Quick&Dirty): Schauen, ob das Umsortieren der globalen Variablendeklarationen einen Einfluss auf das Verhalten hat. 4. Schauen, ob ich die Schaltung resetten kann, z.B. über den AVR-ISP oder einen Schalter (um den Neustart zu umgehen). 3. Alle Array-Operationen prüfen, ggf. noch eine explizite Routine in z.B. die For-Schleifen einbauen, die die Grenzen überwacht und einen Fehler über das Display meldet. Könnte tricky werden, weil ich hier den Code verändere ... Viele Grüße Sebastian
Schaltung manuell resetten -> gemeint ist, den Reset-Pin im laufenden Betrieb kurz manuell auf Masse zu legen
Und bedenke bitte, dass du mit dem Code oben nur den aktuell freien RAM rausbekommst. In einem Unterprogramm kann viel weniger zur Verfügung stehen. Besser ist der Minimum-Code von hier: http://rn-wissen.de/wiki/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc#Dynamischer_RAM-Verbrauch
@Sebastian M. (cyberseb) (Gast) >ggf. den Code anpassen, was ich vermeiden möchte. Das mit dem Oszi >sollte schon klappen, es ist ja ein digitales Speicheroszi und für eine >One-Shot-Messung sollte es taugen. Warum jammmerst du dann rum? "Als nächstes werde ich mal mit dem Oszi schauen, ob das Programm im Fehlerfall überhaupt irgendetwas macht. Könnte mit meinem alten Hameg 205 schwierig werden." >anspringen. Tun sie aber nicht. Das Programm startet m.E. gar nicht. Merkwürdig. >1. Schaltung läuft bereits (mit oder ohne Fehler, je nachdem, was vorher >passiert ist) >2. Flashen mit AVR-ISP, incl. Reset durch den AVR-ISP >3. Programm startet korrekt => Kein Fehler >4. Schaltung ausschalten >5. Schaltung einschalten (Neustart) => Fehler, Programm startet nicht Das ist schon mal eine wichtige Aussage. Klingt nach wie vor nach einem Initialisierungsproblem. Vielleicht hängt ein wichtiger Eingangspin in der Luft, weil ein Pull-Up Widerstand fehlt. >Der Grundaufbau des Programms in main() ist dabei so: Spar dir die Lyrik, zeig den Quelltext sowie den Schaltplan! >Über "1" kommt das Programm im Fehlerfall wohl nicht hinaus, sonst würde >ich einen leeren Bildschirm sehen und keinen schwarzen Balken. Also ist schon mal was Grundlegendes falsch. Möglicherweise eine IO-Initialisierung. Für ein bisschen LCD-Ansteuerung reichen ein paar Bytes RAM meistens aus, wenn man nicht mit printf() & Co arbeitet. >Lösche ich dagegen Programmteile aus der Hauptschleife (6) heraus (die >ich während der Tests nicht benötige oder gar aufrufe), startet das >Programm jedoch einfwandfrei! Hmmm. >1. Mit dem Oszi die LCD-Datenleitungen überprüfen, ob irgendetwas im >Fehlerfall gesendet wird. Sinnvoll. >2. (Quick&Dirty): Schauen, ob das Umsortieren der globalen >Variablendeklarationen einen Einfluss auf das Verhalten hat. Wenig sinnvoll. >4. Schauen, ob ich die Schaltung resetten kann, z.B. über den AVR-ISP >oder einen Schalter (um den Neustart zu umgehen). ? >3. Alle Array-Operationen prüfen, ggf. noch eine explizite Routine in >z.B. die For-Schleifen einbauen, die die Grenzen überwacht und einen >Fehler über das Display meldet. Könnte tricky werden, weil ich hier den >Code verändere ... Wenig sinnvoll. Wenn der Fehler reproduzierbar ist, muss man die Stelle finden, wo das Programm GENAU hängen bleibt!
Wenn du nicht magst, reicht eventuell auch schon der komplette Code zumindest bis einschließlich des LCD-Inits. Vielleicht "initialisiert" du ja manche Register mit &= oder |= oder so. Setzt also gewisse Anfangszustände voraus.
Hallo Tom & Falk, danke für Eure Hilfe! Vielleicht mache ich wirklich Mist mit dem Initialisieren des LCDs. Ich muss das morgen mal in Ruhe durchgehen und testen. Ich werde berichten - und mich (vermutlich) schämen ... :-/ Wobei mir immer noch nicht wirklich klar ist, wie ein ungenutzter Code (weiter unten in main, der auch nichts an den Ports macht) Einfluss auf die LCD-Initialisierung haben kann, je nachdem, ob er auskommentiert ist, oder nicht. Und die Rolle des AVR-ISPs ... Ich hoffe, das wird noch interessant. Schaltplan hängt an! (Der AVR-ISP könnte da ja Einfluss darauf haben, wenn sich das LCD mit dem AVR-ISP Leitungen teilen würde. Tut er aber nicht ...) Gruß, Sebastian
@Tom: Am freien RAM sollte es nicht liegen. Ich habe den mit der o.g. Routine intervallmäßig per Timer in verschiedenen Programmteilen auf dem LCD ausgegeben. Das waren bei dem alten (fehlerhaften) Code um die 500 Byte frei. Tiefe Rekursionen habe ich ebenfalls nicht. Ich werde das aber noch weiter im Auge behalten, erstmal schau ich mir die LCD-Initialisierung an ... Gruß, Sebastian
Neben VCC oder AVCC? xD Fällt dir was auf?
Tom schrieb: > Neben VCC oder AVCC? xD Fällt dir was auf? Es müsste nur noch jemand einen passenden Schluss erklären können warum dann das Programm läuft wie gewünscht, wenn der nicht benutzte Code auskommentiert wird. Für mich ist das irgendwie nicht wirklich schlüssig.
Hallo Tom, bei mir hängen VCC, AVCC und AREF an +5V, die über 100n gegen Masse gehen. Nach https://www.mikrocontroller.net/articles/Datei:Mega8_Tutorial.png sollte AREF jedoch über einen Kondensator auf Masse gehen. Ich dachte, ich verwende die +5V als Referenzspannung, darum hängt der an +5V. Scheinbar ist das doch komplizierter, als ich dachte. Ich werde wohl den Thread hier durcharbeiten müssen: Beitrag "AREF = AVCC ????" ... Gruß, Sebastian
Mit "versauter" Versorgung ist alles möglich. Da reicht es schon, wenn eine Flash-Zelle falsch gelesen wird, weil die Spannung gerade einen Einbruch hat. Die Zelle ist in der einen Code-Variante vielleicht in Verwendung und bei der anderen nicht. Löte mal SMD-Kerkos direkt an die Pins des Käfers dran. Zwischen VCC und GND sowie zwischen AVCC und GND.
Wie du AREF beschaltest, hängt davon ab, wie du es benutzen willst! Wenn interne Referenz -> Nur Kerko nach Masse. Wenn eine externe Spannung als Referenz, hängst du eben diese dran (und Kerko nach Masse). Was du machen willst, das weißt nur du. Deinen Code kennen wir ja nicht. Ultrawichtig ist aber, dass du jeweils VCC und AVCC je einen Kerko spendierst!! Und zwar direkt am Chip, möglichst wenig Abstand.
M. K. schrieb: > Es müsste nur noch jemand einen passenden Schluss erklären können warum > dann das Programm läuft wie gewünscht, wenn der nicht benutzte Code > auskommentiert wird. Für mich ist das irgendwie nicht wirklich > schlüssig. Geht mir genau so. Ich habe tatsächlich auskommentierte "defines" in meiner LCD.h gefunden:
1 | #define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
|
2 | ...
|
3 | #define LCD_D4 SBIT( PORTD, 0 )
|
4 | #define LCD_DDR_D4 SBIT( DDRD, 0 )
|
5 | #define LCD_D5 SBIT( PORTD, 1 )
|
6 | #define LCD_DDR_D5 SBIT( DDRD, 1 )
|
7 | #define LCD_D6 SBIT( PORTD, 2 )
|
8 | #define LCD_DDR_D6 SBIT( DDRD, 2 )
|
9 | #define LCD_D7 SBIT( PORTD, 3 )
|
10 | #define LCD_DDR_D7 SBIT( DDRD, 3 )
|
11 | #define LCD_RS SBIT( PORTB, 0 )
|
12 | #define LCD_DDR_RS SBIT( DDRB, 0 )
|
13 | #define LCD_E0 SBIT( PORTB, 1 )
|
14 | #define LCD_DDR_E0 SBIT( DDRB, 1 )
|
und dann in LCD.c wird das dann direkt verwendet:
1 | void lcd_init(void) { |
2 | |
3 | LCD_DDR_D4 = 1; |
4 | LCD_DDR_D5 = 1; |
5 | ...
|
Zwar werden die Ports direkts in meiner main.c als Ausgänge gesetzt (nicht aber sämtliche o.g. platzhalter wie LCD_DDR_D4). Mir ist nicht klar, warum das überhaupt funktioniert. WAS setzt er auf "1", z.B. bei [c]LCD_DDR_D4 = 1;[c] ? Irgendwas anderes im RAM? Das könnte evtl. das beschriebene Problem auslösen. Dann dürfte das LCD m.E. aber überhaupt nicht funktionieren und es wundert mich auch, warum der Compiler nicht meckert (auch nicht nach einem "make clean"). Woher nimmt der also diese Definitionen? Ich muss das morgen mal an meiner Schaltung ausprobieren. Wow, was da alles für Probleme zum Vorschein kommen. ICH DANKE EUCH!!! @Tom: Danke für den Hinweis auf den extra Kondensator! Das werde ich noch ändern. Auch die Erklärungen hier: https://www.mikrocontroller.net/articles/AVR-Tutorial:_ADC#Referenzspannung_AREF sind nützlich - z.B. sollte ich für die gewollte 5V Referenzspannung einfach die interne nehmen. Gruß, Sebastian
Sebastian M. (cyberseb) schrieb: > Scheinbar ist das doch komplizierter, als ich dachte. Ich werde wohl den > Thread hier durcharbeiten müssen: > Beitrag "AREF = AVCC ????" ... Bevor du dich in sinnlosen Versuchen verlierst... Stellen wir mal fest, was überhaupt passiert: A) Nach flashen läuft alles. B) Nach Reset oder Stromabschalten aber nicht mehr. C) In der Zwischenzeit macht er keine Probleme. Richtig so ? Wenn ja, dann überlege mal was sich zwischen Flashen und Reset verändert hat ? Flash kann sich nicht verändern und I/O Register werden auch nach Reset auf Initialwerte gesetzt. Bleibt nur - RICHTIG - RAM. Beim LCD-Init wird nichts gelesen, stimmt das ? Da es nach Flashen richtig startet, aber nach Reset nicht mehr, sind nur die Variablen interessant, die beim Initialisieren gebraucht werden. Sind wir uns soweit einig ? Wenn ja, dann setze alle Variablen, die beim Init gebraucht werden ganz vorne (oder ganz hinten, egal) und probiere es mal so. Anbei ein Excel mit Werten nach ISP-Flashen und flashen mit Boot- loader. Was interessant ist - bestimmte Bereiche im RAM behalten immer die gleichen Werte - sind zwar von uC zu uC verschieden, aber ab Adresse 269 z.B. behalten die immer den gleichen Wert. P.S. Es sind RAM-Adressen, nicht physikalische Adressen.
:
Bearbeitet durch User
Hallo zusammen, vergesst das, was ich über die Defines geschrieben habe. Die sind natürlich nicht auskommentiert, sondern werden mit einem "#" eingeleitet. Ouch! Marc, Deine Annahmen sind richtig. Danke für das interessante Dokument! Marc V. schrieb: > Wenn ja, dann setze alle Variablen, die beim Init gebraucht werden > ganz vorne (oder ganz hinten, egal) und probiere es mal so. > > Anbei ein Excel mit Werten nach ISP-Flashen und flashen mit Boot- > loader. Was interessant ist - bestimmte Bereiche im RAM behalten > immer die gleichen Werte - sind zwar von uC zu uC verschieden, aber > ab Adresse 269 z.B. behalten die immer den gleichen Wert. Ich habe aber noch Probleme, das zu verstehen: Was meinst Du mit "setzen"? Also den Variablen einen Wert zuweisen? Das mache ich doch. Sollten die entsprechenden Speicherstellen bei einem erneuten Start (egal ob mit oder ohne ISP) nicht automatisch dazu führen, dass diese mit den gewünschten Variablenwerten befüllt werden? Oder meinst Du mit "setzen" die Position im Code? Was meinst Du mit "hinten"? D.h., ich sollte über die Position im Code erzwingen, dass die Variblen im RAM vor Adresse 269 landen? Heißt das, dass alles nach einem ISP-Flash nach dieser Position im RAM erhalten bleibt? Was passiert dann aber mit dem Code, mit dem ich die Variablen setze? Wird der ignoriert? Gruß, Sebastian
Du solltest deine Register beim Start ordentlich initialisieren. Keine Bits gezielt löschen/setzen, sondern einfach einen definierten Wert reinschreiben. Du weißt ja nicht, was vorher drin war.
Sebastian M. (cyberseb) schrieb: > Oder meinst Du mit "setzen" die Position im Code? Was meinst Du mit Ja, die Position war gemeint. Compiler wird wahrscheinlich die Adressen im RAM entsprechend der Reihenfolge der Deklarationen zuweisen. Falls du Arrays hast, deklariere ein paar Dummy-Variablen dahinter, deklariere Pointer als volatile und integer etc. > "hinten"? D.h., ich sollte über die Position im Code erzwingen, dass die > Variblen im RAM vor Adresse 269 landen? Nicht unbedingt, ich wollte nur sagen, dass die RAM-Inhalte bei bestimmten Speicherzellen bzw. Adressen nicht ganz zufällig sind, nach flashen einen bestimmten Wert haben, aber nach Ausschalten und Reset wiederum einen ganz anderen. Ich arbeite selten mit C, bin mir also nicht sicher wie der Compiler das genau macht, aber ich könnte mir vorstellen, dass beim include schon RAM reserviert wird, falls du - wenn auch später - irgendwelche Routinen aus diese library benutzst. Wie gesagt, C ist nicht meine Stärke, wird es auch nie werden, ist also nur eine Vermutung. Auf jeden Fall sollte der Compiler das merken, ist also unwahrscheinlich, aber Arraygrenzen (Laufzeit) prüft er ganz bestimmt nicht.
:
Bearbeitet durch User
Tom: Wird morgen gemacht. Bei einigen Register setzt ich tatsächlich nur die Bits, die ich benötigte. Auf solche möglichen Seiteneffekte (wenn man einzelne Bits nicht explizit setzt) wäre ich nicht gekommen. Marc: Danke, jetzt ist es mir klar, was Du gemeint hast. Ich werde als nächstes den Code durchgehen, vermutlich laufe ich irgendwo über eine Array-Grenze hinweg. Das könnte solche Effekte, wie Du sie beschrieben hast, zum Fehlerfall führen lassen. Herzlichen Dank! Ich werde Euch auf dem Laufenden halten. Gruß, Sebastian
Ich vermisse im Schaltplan einige Verbindungspunkte. Z.b. bei C3 und C5. Sind die wirklich angeschlossen, oder halt nur auf dem Board vorhanden?
Das müsste alles richtig verbunden sein. Ich glaube eher, da sind zu viele "überflüssige" grüne Punkte drin, weil ich mir beim Erstellen des Plans auch nicht ganz sicher war, wie KiCad das verarbeitet. Beim Erstellen des PCBs bzw. beim "Check" wären die aber aufgeflogen. Ja, den Plan muss ich auch noch schöner machen, bevor das Ding mal fertig wird ... :-) Danke für den Hinweis! Gruß, Sebastian
Bau mal ne Verzögerungsschleife ein von ein paar dutzend Millisekunden, ganz am Anfang bevor Du anfängst das Display zu initialisieren. Beim Aus- und Einschalten vorher den ISP-Stecker abziehen sonst spuckt Dir der AVRISP möglicherweise in die Suppe, dem wird nämlich schwindelig wenn das Gerät eingeschaltet wird während er dransteckt und er übergibt sich dann über die Reset-Leitung.
Marc V. schrieb: > Ich arbeite selten mit C, bin mir also nicht sicher wie der Compiler > das genau macht, aber Warum ergehst Du Dich dann in wilden Spekulationen und Behauptungen darüber?
So, nun bin ich wieder zuhause. Wegen dem "Strom aus, Pause, Strom an" vs reset über Resetpin hatte ich vergessen zu sagen. Pack gleich in in die Startroutine zuallererst, also so das wirklich absolut nichts anderes vorher ausgeführt wird und werden kann, ein Delay bzw. eine Warteschleife die den Start um ein paar Hundert Millisekunden verzögert, so daß sichergestellt ist daß die Spannung sich stabilisiert hat und zusätzlich noch etwas mehr Zeit verstrichen ist für die von den Chips geforderte Wartezeit !nach! anliegen einer stabilen Spannung. Normalerweise braucht das nicht so lange sein, aber wir hkennen die Qualität deiner Spannungsversorgung nicht. Nachtrag: Bernd war schneller mit dem Absenden. Ich würde glatt ein Bier auf die Warteschleife verwetten. :D
:
Bearbeitet durch User
Im Datenblatt des Displays steht: "Wait for more than 15ms after VDD rises to 4.5V" Hast du das berücksichtigt?
Hallo zusammen, danke für den Hinweis - ich hatte weiter oben aber schon geschrieben, dass ich das auch schon probiert hatte: Sebastian M. (cyberseb) schrieb: > An das Display wird nur geschrieben, nichts wird gelesen. Also dass mein > Programm auf irgendeine Antwort vom LCD wartet ist damit ausgeschlossen. > Auch ein Delay vor dem Initialisieren des Displays brachte nichts. Naja, ich kann noch mal mit anderen Werten experimentieren. Mein Buchgefühl sagt mir aber, dass es nicht am LCD liegt. Da wird ja nur geschrieben und nicht gewartet. D.h, der AVR würde "drüberumpeln" und mit dem Programm weitermachen, z.B. die PWM aktivieren. Macht er aber nicht. Carsten, das mit dem Delay schaue ich morgen an. Aber ich glaube nicht, dass es daran liegt. Dafür ist das ganze Verhalten zu zufällig, und das gefällt mir absolut gar nicht ... Vor allem, dass sich das Vorhandensein von ungenutztem Programmcode irgendwie auswirkt! :-/ So wild finde ich Bernds Spekulationen gar nicht. Ich kann mir gut vorstellen, dass ich über eine Arraygrenze hinausschreibe, und die von Bernd beschrieben Effekte eine Auswirkung auf den Fehler haben. Fehlt der unbenutzte Code, werden vielleicht die Variablendeklarationen vom Compiler anders sortiert und der Fehler fällt nicht auf. Inzwischen habe ich alle for-Schleifen und Array-Operationen überflogen, sieht (leider!) gut aus. Relevant sind sicher nur die, die auch während des Tests liefen. Das sind eigentlich nur die für das Abfahren der Sinus-Tabelle und die zum Anpassung der Spannung bzw. Soft-Start (durch Hochskalieren der Werte). Die erste ist etwas komplizierter, die zweite ist wohl OK. Ich werde das morgen in Ruhe durchgehen und debuggen. Seit den Updates (Compiler, Eclipse-AVR-Plugin) ist der Fehler ja verschwunden und bleibt das hoffentlich auch. Vielleicht war das wirklich ein Bug in der Toolchain (glaube ich aber nicht). Ich will den Fehler aber trotzdem finden (alte Toolchain kann ich auch noch verwenden). Viele Grüße, Sebastian
Ich meinte natürlich "Marcs Spekulationen", sorry!
Bernd K. schrieb: > Marc V. schrieb: >> Ich arbeite selten mit C, bin mir also nicht sicher wie der Compiler >> das genau macht, aber > > Warum ergehst Du Dich dann in wilden Spekulationen und Behauptungen > darüber? Erstens, es sind keine wilden Behauptungen und Spekulationen, sondern logisches denken - was bei dir ja scheinbar fehlt. Zweitens, weil es bestimmte Regeln gibt, die ein Compiler ganz einfach befolgen muss, egal ob Pascal oder C. Drittens, du als attestiertes Genie weisst natürlich bis ins kleinste Detail wie der Compiler das macht ? Schweig still, wenn du nichts gescheites zu sagen hast, ausser zu meckern.
:
Bearbeitet durch User
Marc V. schrieb: > Flash kann sich nicht verändern und I/O Register werden auch nach > Reset auf Initialwerte gesetzt. > Bleibt nur - RICHTIG - RAM. Oder interne Register der Peripherie. Möglicherweise gibt es beim AVR auch Register die sich bei POR anders verhalten als bei einem manuellen Reset über den Reset-Pin. Ich arbeite zwar mit PICs, aber da ist das so. Das Datenblatt unterscheidet bei Initialwerten zwischen POR-BOR und manuellem, WDT oder Softreset. Und bei einigen Registern macht das Unterschiede. Vielleicht gibts sowas ja auch beim AVR.
Bad U. schrieb: > Oder interne Register der Peripherie. Möglicherweise gibt es beim AVR > auch Register die sich bei POR anders verhalten als bei einem manuellen > Reset über den Reset-Pin. Ich arbeite zwar mit PICs, aber da ist das so. Nö. Beim AVR macht es keinen Unterschied, es gibt 4 Arten von Reset, bei allen ist das Verhalten nach dem Reset genau dasselbe. Funktionert sogar ohne Clock.
:
Bearbeitet durch User
Ok. Dann kann man das schonmal als Fehlerursache ausschließen :) Ich wollte das auch nur mal in den Raum werfen, weil ich da auch schon drüber gestolpert war.
Hallo zusammen, also, ich habe weiter debuggt und das sind meine Ergebnisse: - Der Controller startet im Fehlerfall gar nicht (und "rumpelt" auch nicht über die LCD-Initialisierung drüber), denn die PWM wird nicht aktiviert. - Schleifen und Array-Zugriffe sind OK, ich habe die Grenzen zur Laufzeit überwacht (Fehler wären auf dem LCD ausgegeben worden). - Verschiedene Delays vor der Display-Initialisierung haben keinen Einfluss. Und ich habe interessante neue Erkenntnisse, die wohl belegen dürften, dass dies kein Standardfehler ist! Dazu habe ich mein Programm wie folgt abgeändert:
1 | int main(void) { |
2 | |
3 | lcd_init(); // Befehl 1 |
4 | screen_print("Hello World"); // Befehl 2 |
5 | _delay_ms(10000); // Befehl 3 |
6 | |
7 | screen_print_twoLines(("Begrüßungs"), 1, ("Text"), 2); // Befehl 4 |
8 | |
9 | // Timer und alles andere initialisieren
|
10 | // Programm-Hauptschleife
|
Das Delay dient dazu, dass ich einen Reset bzw. Neustart des Programms machen kann, ohne dass der Code darunter zur Ausführung kommt. Außerdem wird der Code darunter nicht wegoptimiert und damit kleiner. Auch hier lässt sich der Fehler wie oben beschrieben reproduzieren! Das Programm startet korrekt nach dem Flashen, nicht aber nach einem Neustart (Strom aus, Strom an). Bei dem Test wurde niemals Befehl 4 ausgeführt, weil ich den Neustart immer während Befehl 3 ausgeführt habe. Also dass mein Code nach Befehl 3 irgendetwas im RAM ändert, kann ich damit ausschließen, da er nie zur Ausführung kommt. Interessant: Kommentiere ich jedoch Befehl 4 aus, tritt der Fehler nicht auf. Ich kann mir absolut keinen Reim daraus machen, wie nicht ausgeführter Code einen Einfluss auf das Verhalten haben kann! Doch ein Compiler-Bug? :-( Gruß, Sebastian
:
Bearbeitet durch User
Tom, mir wird wohl nichts anderes übrigbleiben, wenn ich den Fehler finden werde. Ich hab mit dem Simulator noch nie etwas gemacht, aber ich werde mich einarbeiten. Ach ja, um Fehler bei der Stromversorgung (Ripple durch Schaltregler, usw.) auszuschließen, habe ich folgendes Gemessen: DC: 4,98V (Multimeter) Ripple: ca. +/- 30mV (Soweit ich das mit dem Oszi beurteilen kann) Sollte also passen ... Meh! :-( Gruß, Sebastian
Ne, die Ripple, die den uC durcheinanderbringen, kannst du im Zweifelsfall gar nicht messen. Wie sieht es denn an der Kerkofront aus? Hast du da mal welche drangelötet (VCC-GND, AVCC-GND, AREF-GND)? Die sind wichtig. Also, angenommen, ich wäre dein Kunde. Dann würde ich da welche sehen wollen!
Das war gerade mein nächster Versuch ... Brachte leider auch nichts. Allerdings sind das keine Keramikkondensatoren, sondern Wima MKS-02, 100nF. Müssen das unbedingt Keramikkondensatoren sein? Die kommen aber auf jeden Falls ins finale Schaltungsdesign. Als nächstes würde ich die Schaltung auf dem Breadboard nachbauen bzw. eine Minimalbeschaltung, um zu schauen, ob das Programm dann startet ... Oder hey, ich probiere einen Arduino. Zumindest die PWM sollte ich messen können! :-) Gruß, Sebastian
Sebastian M. schrieb: > Also dass mein Code nach Befehl 3 irgendetwas im RAM ändert, kann ich > damit ausschließen, da er nie zur Ausführung kommt. Dem Compiler steht es durchaus frei Teile von 4 vor 3 zu machen, wenn das das Gesamtergebnis aus der Sicht des Compilers nicht beeinflusst. Es ist also unzulässig anzunehmen, dass nichts von 4 ausgeführt wurde.
Woher weißt Du das die vierte Zeile nie zur Ausführung kommt? Wenn du es daraus schließt, daß der Text nicht erscheint, sollte man prüfen ob der Fehler nicht in der Routine selbst liegen könnte. Diesen Schluß würde ich daraus ziehen. Sebastian M. schrieb: > An ein > (einfaches) Initialisierungsproblem glaube ich nicht - es hängt > definitiv mit dem Speicher zusammen. > > Ob es geht, oder nicht, ist von diesen zwei Codezeilen abhängig. Und die > sind mitten im Code, weit, weit weg von der Initialisierung: > > screen_locate(1, 2); > screen_print("(Speed restored)"); Das ist ein Trugschluß. Initialisierungsproblem bedeutet nicht, daß das Problem zwangsläufig während der Initialisierung auftritt, sondern nur daß sie nicht korrekt durchgeführt wird. Das kann sofort zu Problemen führen, muß aber nicht. Initialisierung ist kein Selbstzweck, sondern dient der Vorbereitung, damit nachfolgende Operationen korrekt auf einer sauber definierten Basis durchgeführt werden. War die Initialisierung fehlerhaft, so kann der Fehler später bei den Funktionen auftreten die sich auf die Initialisierung stützen. Es muß auch nicht unbedingt diese Hardware sein, es kann natürlich auch eine nicht definierte Speicherstelle sein, welche je nach Startbedingung eventuell etwas anders aussieht. Mit screen_print_p funktioniert es aber. Worin besteht der Unterschied der beiden Funktionen? Verwendet screen_print_p auch lcd_text oder lcd_data? Wie sieht lcd_data aus? Sebastian M. schrieb: > Den Code möchte ich nicht zeigen, Dann können wir dir auch nicht helfen! Was nützen uns lustige Methodennamen und Codefitzelchen, wenn wir nicht wissen dürfen was sie bedeuten? Wir können keinen Code debuggen den wir nicht sehen dürfen.
Arduino Duemilanove (China-Clone) mit Quarz (kein Keramik-Resonator), gleicher AVR-Typ: Gleiches Verhalten (fast). Ich habe zwar kein LCD dran, aber die PWM startet brav nach einem Flash (über den AVR-ISP). Nach einem Reset über den Reset-Button startet sie nicht. Nehme ich dem Arduino kurz seine 5V (USB-Stecker ziehen), so: - startet das Programm, wenn der AVR-ISP angeschlossen ist - startet das Programm nicht, wenn der AVR-ISP nicht angeschlossen ist. Im ersten fall könnte das mit dem zusätzlichen Reset durch den AVR-ISP zusammenhängen (der resettet auch nochmal, wenn die Schaltung wieder Strom kriegt). Wildes Rumdrücken auf dem Reset-Taster hilft auch nichts. :-) Seeeehr seltsam ... Gruß, Sebastian
Sebastian M. schrieb: > Wildes Rumdrücken auf dem Reset-Taster hilft auch nichts. Was macht der Reset-Taster? RESET auf GND ziehen? Mehr macht der ISP auch nicht.
blablablubb schrieb: > Dem Compiler steht es durchaus frei Teile von 4 vor 3 zu machen, wenn > das das Gesamtergebnis aus der Sicht des Compilers nicht beeinflusst. > Es ist also unzulässig anzunehmen, dass nichts von 4 ausgeführt wurde. Interessante Überlegungen - könnte durchaus wohl sein ... Carsten R. schrieb: > Woher weißt Du das die vierte Zeile nie zur Ausführung kommt? Wenn du es > daraus schließt, daß der Text nicht erscheint, Weil ich den Controller innerhalb des 10-Sekunden-Delays neu starte ... Carsten R. schrieb: > Mit screen_print_p funktioniert es aber. Worin besteht der Unterschied > der beiden Funktionen? Verwendet screen_print_p auch lcd_text oder > lcd_data? Wie sieht lcd_data aus? Das verwendet den Program-Space (PROGMEM). Kommt aber in der fehlerhaften Version zuletzt gar nicht mehr zum Einsatz. Carsten R. schrieb: > Dann können wir dir auch nicht helfen! Was nützen uns lustige > Methodennamen und Codefitzelchen, wenn wir nicht wissen dürfen was sie > bedeuten? Wir können keinen Code debuggen den wir nicht sehen dürfen. Klar ... Aber um Deine Überlegungen mit der Initialisierung weiterführen zu können, habe ich den relevanten Code angehängt - ich hoffe, der ist ausreichend. Ich würde mich freuen, wenn Du drüberschauen könntest. Viele Grüße Sebastian
Das heißt, das Erste, das man messen könnte, wäre ein High-Pegel auf D4 und D5, gefolgt von einem kurzen Impuls auf E0?
Hast du dir das Quarz Signal angesehen, vielleicht schwingt der nicht sauber?
Dieser Code compiliert mit Sicherheit nicht. Bitte Code posten, der das Problem aufzeigt und funktioniert. Aber ansonsten: Warum dieser Quatsch mit der Bitstruktur? Einfach Bitoperatoren, die der Compiler vernünftig optimieren kann, wäre wohl zu einfach gewesen. Das include guard #endif zu #ifndef lcd_drv_h steht mitten im Header. Die Hälfte des Headers ist also nicht geschützt. const wegcasten ist keine gute Idee. Das kann zu undefiniertem Verhalten führen. Mache ein const u8* bei lcd_text.
Der Code setzt übrigens auch die Register nicht auf definierte Werte, sondern popelt nur bitmäßig daran herum. Das ist kein sauberer Init.
Deine Nibble-Funktion löscht auch Bits vor dem Setzen kurz. Wenn D4 z.B. high war, wird es, wenn es nach dem Aufruf wieder high sein soll, kurz auf low getoggelt. Eigentlich kein Problem, aber total unschön.
blablablubb & Tom, ihr seid super! Fragt mich nicht, aus welchen Codeschnipsen ich die LCD-Routinen vor Jahren zusammengebastelt habe ... Die hatten sich bisher bewährt, jahrelang funktioniert und ich habe sie nie hinterfragt. Die Routinen fliegen raus und werden von Grund auf neu geschrieben. Tom schrieb: > Das heißt, das Erste, das man messen könnte, wäre ein High-Pegel auf D4 > und D5, gefolgt von einem kurzen Impuls auf E0? Die sind schon von Beginn auf High und bleiben dann dort (im Fehlerfall) Fritz G. schrieb: > Hast du dir das Quarz Signal angesehen, vielleicht schwingt der nicht > sauber? Sollte passen, nachdem der Fehler auf einem Arduino auch auftritt ... Gruß, Sebastian
Du könntest dir auch die ersten Zeilen des Assemblercodes angucken bzw. mit dem Ergebnis des neuen avr-gcc vergleichen.
Also, die LCD-Rotinen sind ausgetauscht. Ich habe im Prinzip die hier verwendet: https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung#Die_LCD_Routinen und entsprechend angepasst und erweitert. Das Problem besteht weiterhin. Ich glaube auch langsam, dass wir an einem Punkt angekommen sind, an dem Ihr mir ohne den Quellcode nicht helfen könnt. Wenn ich die Ursache nicht finde, kann ich nur darauf hoffen, dass der neure speicherplatzoptimierte Code (mit dem neueren GCC kompiliert) einwandfrei läuft. Bis jetzt tut er es ... Ansonsten habe ich noch die Variablen, die in den Interrupt-Routinen verwendet werden, überprüft. Da waren tatsächlich zwei noch nicht auf volatile gesetzt. Das brachte aber auch nichts. Tom, das ist eine gute Idee. Ich schaue mal, ob ich den kompletten Build-Prozess einmal mit dem alten und einmal mit dem neuen Compiler GLEICH (d.h., ohne andere Compiler-Optionen, was über Eclipse gar nicht so einfach geht) durchziehen kann. Wenn ich auf etwas stoße, lasse ich es Euch wissen. Auf jeden Fall: HERZLICH DANK - ich hätte nicht damit gerechnet, dass Ihr so hilfsbereit bei einem closed-source Projekt seid. Vielleicht kann ich der Community ja doch noch etwas zurückgeben, falls ich die Ursache finde. Viele Grüße Sebastian
Ja, oder du nimmst nur den alten Compiler und entfernst dann die "nicht genutzten" Programmteile, sodass die Initialisierung klappt (wie du es mal beschrieben hast) und nimmst dann dieses Kompilat für den Vergleich. Das Entscheidende müsste ja gleich an Anfang stehen...
Tom, Du bist genial! Danke! :-) So hab ichs gemacht. Und tatsächlich gibt es am Programmanfang einen Unterschied, je nachdem ob weiter unten im Code etwas auskommentiert ist (was wie gesagt nie ausgeführt wird). Und zwar macht er im fehlerbehafteten Code ein "push r0" bevor es losgeht. Im Diff-Screenshot links hellblau markiert. Ich verstehe das noch nicht - ich habe ehrlich gesagt noch nie Assembler programmiert. Ich bleibe aber dran und werde mal schauen, was die neueren Compiler mit dem Code anstellen. Gruß, Sebastian
Der neue Compiler macht dagegen einen viel kürzeren ASM-Code, bevor es überhaupt losgeht ...
Sebastian M. schrieb: > Carsten R. schrieb: >> Woher weißt Du das die vierte Zeile nie zur Ausführung kommt? Wenn du es >> daraus schließt, daß der Text nicht erscheint, > > Weil ich den Controller innerhalb des 10-Sekunden-Delays neu starte ... Ich habe eine begrenzte Ahnung was du machst. Es wäre hilfreich wenn du das was du beschreibst noch einmal kontrollierst ob es für jemanden nachvollziehbar ist, der nicht sieht was du tust. Du nimmst zuviel als gegeben an. Entweder er kommt in beiden Fällen zum Delay und du startest vor Zeile 4 manuell neu. Das bedeutet es Funktioniert in beiden Fällen, Reset durch Programmer und Manuelle Stromunterbrechung. Dann kannst du mangels unterschied aber nicht diesen Schluß ziehen. Sebastian M. schrieb: > Auch hier lässt sich der Fehler wie oben beschrieben reproduzieren! Oder das Teil kommt tatsächlich ins Schleudern und die Ausgabe durch Zeile 2 erfolgt nicht. Aber dann startest du auch nicht innerhalb des 10 Sekunden Delays sondern nur irgendwann nach 10 Sekunden ohne zu wissen wo genau die CPU sich befindet. Sebastian M. schrieb: > Carsten R. schrieb: >> Mit screen_print_p funktioniert es aber. Worin besteht der Unterschied >> der beiden Funktionen? Verwendet screen_print_p auch lcd_text oder >> lcd_data? Wie sieht lcd_data aus? > > Das verwendet den Program-Space (PROGMEM). Kommt aber in der > fehlerhaften Version zuletzt gar nicht mehr zum Einsatz. Das ist nett, beantwortet aber leider meine Frage(n) nicht. Ich habe lcd_text oder lcd_data im Verdacht, beziehungsweise etwas das bei denen in der Nahrungskette liegt, die wir leider nicht nachverfolgen können. Mittlerweile wird es aber anstrengend sich die Bröckchen zusammenzustückeln weil man ständig im Dunkeln tappt.
Vermutlich ist auch der Code interessant, der VOR main() abläuft. Tatsächlich gibt es auch hier Unterschiede: in <__do_copy_data> macht er etwas anderes. Nur wildes Raten, da ich wie gesagt noch nie ASM programmiert habe: In dem auskommentierten Text (der eh nie ausgeführt wird), sind keine Strings (korrekt gesagt char-Arrays) oder ähnliches, nur if-Vergleiche, Zahlenoperationen und ein paar Funktionsaufrufe. Sollten mit "data" in "<__do_copy_data>" z.B. Strings gemeint sein, so würde es für mich Sinn ergeben, wenn die irgendwie an der Stelle in den RAM geladen werden. Der alte Compiler könnte einen Bug haben, der hier irgendetwas falsch macht. Tatsächlich habe ich das gefunden, keine Ahnung, ob das damit zusammenhängt: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=18145 Sebastian
Sebastian M. schrieb: > ergeben, wenn die irgendwie an der Stelle in den RAM geladen werden. Der > alte Compiler könnte einen Bug haben, der hier irgendetwas falsch macht. LOL. Wie ist der Wert für deine High Fuse ? BOOTSZ1 und BOOTSZ0 auf 1 setzen.
:
Bearbeitet durch User
Carsten, sorry, falls ich mich nicht klar ausgedrückt habe. Ich bin auch schon ganz kirre und müde. Nochmal ganz einfach beschrieben: [c]int main(void) { lcd_init(); // Befehl 1 screen_print("Hello World"); // Befehl 2 _delay_ms(10000); // Befehl 3 screen_print_twoLines(("Begrüßungs"), 1, ("Text"), 2); // Befehl 4 // Timer und alles andere initialisieren // Programm-Hauptschleife [c] Nach einem Compilieren und einem Flashen mit dem AVR-ISP (und dem damit einhergehenden Reset) läuft dieser Code zunächst einwandfrei. Auch über Befehl 4 hinweg. Schalte ich die Stromversorgung des AVRs nun aber ab und wieder ein, funktioniert er nicht mehr. Der AVR kommt dabei nicht nichtmal über bzw. zu Befehl 1. (Das mit dem Delay war wirklich Quatsch. Das sollte unterbinden, dass sich der RAM-Inhalt durch den späteren Code ändert. Schalte ich den AVR aber ab, sollte der RAM-Inhalt ja ohnehin schwinden.) Kommentiere ich "Befehl 4" aus und flashe neu, startet der AVR immer. Auch nach einer Stromunterbrechung. In dem Fall bleibt der ASM-Code für main() gleich (also Befehl 4 auskommentiert vs. nicht, hängt an), im Gegensatz dazu, wenn ich weiter unten etwas im Code auskommentiere (was die vorherigen Posts von mir zeigen). Gruß, Sebastian
Ein falscher Resetvektor würde das alles in der Tat gut erklären.
Marc V. schrieb: > LOL. > Wie ist der Wert für deine High Fuse ? > > BOOTSZ1 und BOOTSZ0 auf 1 setzen. Warum LOL? (Klär mich auf!) Die High Fuse ist auf "DE", BOOTSZ1=1 und BOOTSZ0=1. Eclipse sagt: "Boot Flash size=256 words start address $3F00". Sollte passen, oder? Gruß, Sebastian
Sebastian M. schrieb: > Marc V. schrieb: >> LOL. >> Wie ist der Wert für deine High Fuse ? >> >> BOOTSZ1 und BOOTSZ0 auf 1 setzen. > > Warum LOL? (Klär mich auf!) > Sorry, war nicht böse gemeint und war für mich selbst gedacht. > Die High Fuse ist auf "DE", BOOTSZ1=1 und BOOTSZ0=1. Eclipse sagt: "Boot > Flash size=256 words start address $3F00". > Sollte passen, oder? Ja. Zu deinen Routinen: Da werden Daten aus Flash ins RAM kopiert und zwar kopiert die Routine links (alte ?) 1462Bytes von der Flashadresse 31004 nach RAMadresse 0x100 (da fangen Variablen an). 31004 + 1462 = 32466 Die Routine rechts kopiert genauso viele Bytes aber von der Flashadresse 29674 nach RAMadresse 0x100. 29674 + 1462 = 31136 Deswegen dachte ich, dass die Boot Flash Size größer ist. Aber auf jeden Fall bleiben dir somit nur 586 Bytes für Stack und andere Variablen. Ob das genug ist ? Oder andere Variablen die du später deklarierst, überschreiben das.
OH GOTT, BOOTRST war auf 1. Setze ich das Bit auf 0, funktioniert es. Ich glaube, dass es das war. Und mir ist das nicht aufgefallen weil avrdude immer ein Problem mit dem Auswerten des zurückgelesenen Flashs hat (siehe unten) und anschließend nicht mehr die Fuses setzt ... Aber warum hat sich das nicht früher bemerkbar gemacht? Marc, herzlichen Dank für Deine Erklärungen - ich werde das morgen versuchen zu verstehen und nachzuvollziehen. Im Moment ist mein Hirn ziemlich am Ende. Die 586 Byte sollten reichen, habe wenig Rekursion drin und den (neuen) Code inzwischen schon stark optimieren können. Viele Grüße, Sebastian
1 | Reading | ################################################## | 100% 9.32s |
2 | |
3 | avrdude: verifying ... |
4 | avrdude: 32466 bytes of flash verified |
5 | avrdude: reading input file "" |
6 | avrdude: error opening : No such file or directory |
7 | avrdude: input file auto detected as invalid format |
8 | avrdude: can't open input file : No such file or directory |
9 | avrdude: read from file '' failed |
Der falsche Resetvektor springt halt da hin, wo der Bootloader wäre. Wenn dein Programm kleiner ist, ist da bis zum Ende FF. Dann ist das kein Problem... Wenn dein Programm größer ist, steht da Code. Und dann hängt sich der Chip auf, stürzt ab, oder läuft mittendrin uninitialisiert an.
Sebastian M. schrieb: > Nach einem > Reset ist er (meistens!) allerdings wie tot oder startet "seltsam" (z.B. > mit Verzögerung). Freut mich, daß das Rätsel gelöst wurde. Mir ist nur nicht klar wie das damit zusammenpasst. Oder wurde da zwischendurch eine neue Version geflasht und nicht einfach nur neu gestartet? Für die Zukunft solltest Du dir angewöhnen systematisch alle Detailänderungen zu überprüfen. Wenn etwas plötzlich nicht mehr funktioniert hilft eine allgemeine Beschreibung nicht. Erstelle eine präzise Schritt für Schritt Anleitung, so daß jeder sie nachvollziehen kann ohne Dinge hellseherisch anzunehmen. Das ist nicht nur für uns, sondern in erster Linie für Dich. Der Teufel steckt ja bekanntlich im Detail. Dies ist da mal wieder ein Beispiel dafür.
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.