Hallo, ich bin bei meinem C-Programm (GCC) für den ATMega32 auf ein Problem gestoßen. Es ist immer an der gleichen Stelle hängen geblieben. Das Problem ist sogar mit dem AVRStudio Simulator reproduzierbar. Das ganze liegt wohl an der Stack Größe. Zumindest kam im Simulator einmal (!) eine Meldung "Stack overflow"(Sonst lief das Programm einfach nicht weiter). Das kann sehr gut sein, da mein SRAM ziemlich voll ist und das ganz auch nach 2 verschachtelten Funktionsaufrufen passiert. Nun habe ich dazu 2 Fragen: - Wie kann ich mir die stack größe/stack verbrauch anzeigen lassen? Im AVRStudio Simulator gibt es zwar einen Stack Monitor, der ist aber disabled. - Welche generellen Ansätze gibt es, um den SRAM Verbrauch zu reduzieren? Gibt es vielleicht irgendwo eine Art Checkliste? Ich hoffe Ihr könnt mir helfen. Ich krieg langsam Panik, dass mein Programm nicht auf den MCU passt. Flash reicht locker, aber SRAM ist echt eng. Gruß Andreas
"Welche generellen Ansätze gibt es, um den SRAM Verbrauch zu reduzieren?" Intensives Nachdenken. Genauer wird's leider nicht, nicht ohne ein Mindestmass an Information darüber, was du in das RAM alles reinsteckst.
Generelle Regeln gibts nur wenige. -immer den kleinsten möglichen Datentyp benutzen (oft reicht ein Bit statt einem char) -auf globale Variablen soweit wie möglich verzichten, statt dessen lokale Variable verwenden (overlay) -Verschachtelungstiefe beachten/begrenzen -in einer ISR wenn möglich keine weiteren Ints zulassen -evtl. Sicherungsstrategie des Compilers für ISRs beobachten, manche Compiler gehen da mit dem Holzhammer durch, dann per Asemblerprogrammierung zu Fuss die Sicherung selbst durchführen, fehlerträchtig! -Konstanten in den flash auslagern -nicht alle Annehmlichkeiten des Compilers nutzen (stdio.h/printf() z.B. kostet reichlich flash und auch einiges an RAM -wenn möglich auf float verzichten -schlechter Ausweg: eeprom-Bereich benutzen, langsam und begrenzte Schreibzyklenzahl, also für z.B. loop-Variablen denkbar ungeeignet :-), gibt aber durchaus Anwendungsfälle dafür, z.B. device-Adresse wird nur bei Programmstart eingelesen. Sowas kann man durchaus in den eeprom auslagern -allerletzter Notnagel: den I/O-Bereich benutzen, wenn die zugehörige Hardware nicht benutzt wird, einige Adressen können als RAM missbraucht werden. Das Wichtigste: der eigene Programmierstil. Dazu gehört, sich ab und zu das erzeugte Assemblerprogramm kritisch zu betrachten. Noch was vergessen? Sicher.
Ist ja alles schon gesagt, trotzdem hier noch mal die wichtigsten: - strukturiertes Programmieren (mit Plan) - kleinstmöglicher Typ - lokale Variablen, wenn irgend möglich - Konstanten in Flash Allgemein sollte man mit Variablen sparsam sein, z.B. wenn a nicht mehr gebraucht wird, schreibt man statt: c = a + b; besser: a += b; spart sich also c komplett ein. Besonders in Schleifen und Bedingungen fällt es dem Compiler schwer, selber festzustellen, ob er ne Variable noch für später aufheben muß oder nicht. Gleichzeitig sind weniger Variablen auch besser lesbar. Peter
Zur Analyse fällt mir noch ein: SRAM mit einem charakteristischen Pattern vorinitialisieren (klassisch: die 32-Bit-Worte 0xDEADBEEF oder 0xBAADCAFE). Wenn man sich dann einen Memory-Dump ansieht (im Simulator oder Debugger), erkennt man leicht auf einen Blick die noch nicht benutzten Bereiche. Das kann man auch im Programm automatisieren: Man definiert eine "Grenzschicht" am oberen RAM-Ende, die man mit einem Pattern füllt, und prüft im Programm gelegentlich, ob das Pattern noch unmodifiziert drinsteht. Wenn nicht --> Stack overflow exception auslösen.
Hi, eine Anmerkung zum Beispiel von Peter: > c = a + b; > > besser: > > a += b; Die ehutigen Compiler, zu denen auch der GCC gehört, optimieren so etwas ohnehin in "Grund und Boden" ;-) - zumindest insofern die Optimierung nicht abgeschaltet ist! Genaue Auskunft, wieviele lokale Variablen (die bekanntlich auf dem Stack landen) tatsächlich (!) angelegt werden, läßt sich i.d.R. am einfachsten dadurch bestimmen, das man nur bis zum Assemblerquelltext compiliert und sich die entsprechende Funktion danach anschaut - Assemblerkenntnisse vorausgesetzt. Oder ebend Debugger: Stichwort Stackpointer. Das allerdings ist nur ein Snapshot der gerade aktuellen Situation und muß nicht unbedingt den Worst case darstellen! @Andreas: wenn Du einen Verdacht hast, wo ungefähr der Überlauf stattfindet, ist nur der Debugger zu empfehlen, aber auch dies setzt u.U. recht detaillierte Kentnisse, was wo passiert, voraus. Die "sicherste", allerdings auch hirnschmalzintensivste Variante ist die oben propagierte Methode des kritischen Reviews! Schönen Tag noch, Thomas
Hallo, danke für die Antworten. Im Prinzip sind mir die Mechanismen einigermaßen klar. Irgendwie fehlen mir aber noch Methoden um den Speicherverbrauch zu analysieren, d.h. Methoden um die Speicherfresser zu finden. Ich habe mit bisher zunächst immer an der Ausgabe vom Compiler orientiert: AVR Memory Usage: ----------------- Device: atmega32 Program: 210 bytes (0.6% Full) (.text + .data + .bootloader) Data: 20 bytes (1.0% Full) (.data + .bss + .noinit) Gibt "Data" überhaupt den SRAM verbrauch an? Diese Information ist aber bei hohem Stack Verbrauch nutzlos, oder? Zumindest war bei meinem urprünglichen Problem noch ca. 18% frei. Es wäre sicherlich hilfreich, den Stack überwachen zu können, d.h. wenn ich im Simulator eine Funktion aufrufe soll irgendwo angezeigt werden, wieviel auf dem Stack gesichert wurde. @Qwerty Das hört sich interessant an. Wie initialisiere ich denn unter C das SRAM und wie schaue ich mir im Simulator den Memory dump an? Wenn ich mit im AvrStudio unter View->Memory1 "Data" anschaue steht da fast nix drin. Das bleibt auch bei jedem Programm gleich.
Ich habe es noch nie auf dem AVR bzw. mit dem avr-gcc realisiert, aber üblicherweise gibt es eine Möglichkeit dem Compiler eine (Assembler-)Funktion "unterzujubeln", die noch vor dem C-Startup-Code abläuft. Vielleicht hilft der Abschnitt über "Memory Sections" in der avr-libc-Doku an der Stelle weiter, oder einer der AVR-Profis hier im Forum kann das genauer ausführen. Diese Funktion beschreibt dann einfach alle SRAM-Adressen mit dem gewünschten Pattern. Daher ist es wichtig, dass sie vor dem C-Startup läuft, damit sie dessen Initialisierungen nicht kaputtmacht.
Kann es sein, dass du irgendwas rekursiv machst?
die data-Angabe dürften eher Konstanten sein. Es gibt noch einen Ausweg: den ATMEGA644, ist hübsch kompatibel zum 32er, hat aber doppelt RAM und ROM. Aber es besteht die Gefahr, dass das Problem nur vertuscht wird und später wieder auftritt. Ohne Deinen Code können wir keine Aussage treffen. Ein Stackfresser sind rekursive Programmaufrufe, z.B. ein Unterprogramm, welches sich selbst (mit geänderten Parametern) wieder aufruft. Sowas passiert auch mal unbeabsichtigt.
Hi! benutzt du viele printf()s ? Wenn ja dann schau dir mal in der avrlib die entsprechenden Funktionen an die den text im Progmem speichern... Sonst fressen viele (große) Printf()s richtig ram ;) Bye, Simon
Rekursive Funktionsaufrufe habe ich keine. Das mit dem ATMega644 spielt auch keine Rolle, da das Programm später eh auf nem ATMega128 läuft. Allerdings habe ich ja schon die ersten 2k SRAM "verbraten" und wenn ich nun gleich mit dem ATMega128 weitermache sind auch die irgendwann voll. Und auch wenn mein Programm recht umfangreich sein wird, rechtfertigt das auf keinen Fall einen ATMega256. Mein Problem ist immernoch, den Verbauch vom SRAM überhaupt zu sehen!!
Ich kenne (leider?) das AvrStudio nicht, daher kann ich dir nicht sagen, wie man dort im Simulator das SRAM ansehen kann. Das müsste sich aber finden lassen (Doku?). Eventuell gibt es auch eine Memory-Fill-Funktion (sollte eigentlich jeder Simulator und Debugger können), mit der du im Simulator das SRAM vorbelegen kannst. Dann kannst du das von mir beschriebene Verfahren auch ohne Code-Änderung nachspielen: Code in Simulator laden (nicht automatisch bis main() laufen lassen, falls AvrStudio das normalerweise macht!), SRAM mit Pattern füllen, Programm bis main() laufen lassen --> jetzt ist das Pattern im unteren SRAM durch die C-Init-Routine überschrieben worden. Nach ein paar Funktionsaufrufen sollte dann auch "von oben" das Pattern teilweise verschwinden (dort, wo der Stack hingewachsen ist).
Es gibt im AVR-Studio eine Ansicht "Memory". Da kann man sich das EEPROM, SRAM und vielleicht sogar den FLASH-Inhalt ansehen.
Ja, aber das geht nicht. Wenn ich bei Memory mir "Data" anschaue steht da immer das gleich drin und es ändert sich auch nix.
Anbei ne Testroutine für den AVR-GCC, funktioniert aber nur, wenn man kein malloc benutzt (fopen benutzt malloc !). Einmal am Anfang aufrufen, um das Pattern zu füllen und dann später, um zu testen, bis wo das Pattern noch steht. Ich habe 0x77 genommen, da das im RAM-Dump am besten auffällt. Peter
Was kann helfen, es ist wenn du ein Stackdump machst, so du kanst sehen wie war die Stack vor du hast die letzte Funktion angerufen. Vielleicht, du kannst auch ein teil diese Porgamm mit dem PC debuggen (für x86 kompiliert)
Danke Peter, das ist schonmal eine Hilfe. Aber wieso kann ich im simulator den Speicherverbrauch nicht sehen? Oder reicht es, wenn ich den verbleibenden Stack als indikator für den Speicherverbrauch nehme? @Ale Wie kann ich einen Stackdump während des simulierens machen?
OK, ich bin jetzt an einem anderen Rechner. Hier ist ein neueres SP vom AVRStudio drauf. Hier kann ich mir den Speicher von meinem Programm während der Simulation anzeigen lassen. Leider habe ich etwas erschreckendes festgestellt. Das Programm bleibt trotz genügend freiem Speicher immernoch hängen. Ich habe mein Programm mit dem ATMega128 simuliert. Gestern hat es noch funktioniert, aber jetzt bleibt es auch an der selben Stelle hängen und zwar bei einem "sprintf" Befehl... Kann es sein, dass hier ein bug vorliegt?
"jetzt bleibt es auch an der selben Stelle hängen und zwar bei einem "sprintf" Befehl..." Ui, das klingt nach nem Speicherzuweisungsproblem. Entweder Du hast zu wenig Speicher oder gar keinen reserviert, oder der Pointer zeigt in den Wald, auf den Du schreibst. Besonders bei Zahlenausgabe muß man immer den Worst-Case (maximale Zeichenzahl) einkalkulieren. Peter
Die Anweisung sieht wie folgt aus: char sTemp[20]=""; double dZahl; dZahl = 3.1e8; sprintf(sTemp,"%.6e", dZahl); Auf jeden Fall habe ich mal folgendes getan. Ich hab die alte WinAvr Installation umbenannt und die neuste installiert. Dann habe ich alles neu kompiliert. Danach funktioniert es. Nun habe ich die alte wieder umbenannt und nochmal neu kompiliert. Danach ging es wieder nicht ... Die Funktion stack_free() zeigt mir in beiden fällen vor der Anweisung einen Wert von 24 an. Ist zwar nicht viel, aber bei der neueren Version tut es ja auch....
Dann gibt es noch Murphies Gesetz: Der Fehler liegt nie da, wo man ihn vermutet bzw. wo er sich bemerkbar macht. Also irgendwo viel früher wird die Bombe gelegt. Außerdem sind 1% frei wirklich nicht sehr viel. Peter
Ich hab jetzt mal ein wenig mit meinem code gespielt, aber wirklich weniger SRAM-Verbrauch habe ich dadurch auch nicht. So viele globale Variablen habe ich nicht. Ein paar 8-Bit Variablen. Strings habe ich zwar ne Menge, die bleiben aber im flash. Irgendwie finde ich die Speicherfresser nicht ...
Hallo, verwendet man Srings, wie zum Beispiel: sprintf(s,"Hallo"); dann frisst der AVR GCC Compiler 6 Bytes vom RAM! Um diese RAM Verschwendung zu verhindern, schreibt man das folgendermassen um: { uint8_t i; char s[32]; char f[32]; i=0; f[i]='H';i++; f[i]='a';i++; f[i]='l';i++; f[i]='l';i++; f[i]='0';i++; f[i]=0;i++; // Null terminieren nicht vergessen! sprintf(s,f); } Macht man innerhalb einer Funktion zusätzlich geschweifte Klammern, dann sind die lokalen Variablen nur innerhalb gültig und auf dem Stack vorhanden. Ausserhalb ist dieser Stack Bereich dann wieder frei! Weil ich meist ein Display habe und auf den RS232 Schnittstellen Text Protokolle fahre, brauche ich viele Strings. Ich muss dann alle Srings so programmieren, damit ich genug Speicher habe. Ich hoffe, dass dies hilft. Grüsse
> Ich hoffe, dass dies hilft.
Wohl kaum, wer wartet 13 Jahre lang auf eine Antwort?
Abgesehen davon ist dein Lösungvorschlag gaga. Die vernünftige Lösung
wurde bereits oben genannt: z.B. sprintf_P() in Kombination mit PSTR().
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.