Hallo liebe Geminde,
nach Jahren schreibe ich, zugegeben, mit Halbwissen, mal wieder ein AVR
Programm.
Ich nutze einen ATmega16 der meinen selbstbau Hifiverstärker steuern
soll.
Letztendlich geht es mir aber auch um Übung und Spaß an der Sache,
deswegen habe ich erfolgreich folgende Komponenten integriert:
- Infrarotempfang
- LCD Ansteuerung
- Drehencoderauswertung inkl. zusätzlicher Taster
- LCD Menu zur Einstellung von Eingangskanalnamen, Fernbedienungscodes,
Displayhelligkeit usw.
Alles funktioniert bisher zu meiner vollen Zufriedenheit.
Jetzt spuckt der Compiler mir, im noch nicht fertigen Projekt, folgende
Info aus:
Device: atmega16
Program: 5568 bytes (34.0% Full)
(.text + .data + .bootloader)
Data: 848 bytes (82.8% Full)
(.data + .bss + .noinit)
EEPROM: 30 bytes (5.9% Full)
Program wird der Flash sein, Data der RAM speicher. Im Data bleibt jetzt
nicht mehr viel :(
Wenn ich meine Menusteuerung rausschmeiße, sieht es so aus:
Device: atmega16
Program: 2790 bytes (17.0% Full)
(.text + .data + .bootloader)
Data: 39 bytes (3.8% Full)
(.data + .bss + .noinit)
EEPROM: 30 bytes (5.9% Full)
Das Menu besteht aus 27 Einträgen (inkl. Untermenu) zum Auswählen /
editieren der EEPROM Inhalte.
Wie man so ein Menu aufbaut habe ich mir hier abgelesen.
Die Struktur der Menueinträge sieht so aus:
1
structMenuEntry{
2
charText[20];//Text für Menupunkt
3
MenuFnctFunction;//Funktion für Menupunkt
4
intArgumentToFunction;//Parameter zur Funktionsübergabe
5
uint8_tPrev;//vorheriger Menupunkt
6
uint8_tNext;//nächster Menupunkt
7
uint8_tu_Prev;//vorheriger Untermenupunkt
8
uint8_tu_Next;//nächster Untermenupunkt
9
};
und sind für mein Verständnis im Data Speicher abgelegt. Ich würde die
Geschichte deswegen im Flash ablegen wollen. //sofern das Sinn macht ???
Sofern ich das Verstanden habe, können die Daten aus dem Flash so nicht
direkt gelesen werden.
Hier sind die Strukturvariablen angelegt:
und da wären noch die Fnktionen, die entsprechend dem Menupunkt
ausgerufen werden
1
//Hauptmenu 1
2
voidHandleInput(intarg)
3
{}
4
//usw.
Um meine Fragen noch einmal auf den Punkt zu bringen:
- Liege ich richtig, das ich den Speicherbedearf im data Speicher durch
ablegen der Menustrukturen im Flash reduzieren (und dadurch die
Programmspeicherbelegung erhöhen) kann ?
- Macht das überhaupt Sinn?
Viel Quellcode und flüchtige Variablen kommen nicht mehr dazu.
- Wenn ja, wie muss ich die Deklaration, die Definition und die Zugriffe
auf die Strukurelemente der struct MenuEntry anpassen ?
Ich habe einiges über die Vorgehensweise gelesen, kriege das aber nicht
auf meinen Quellcode umgebaut. Ehrlich gesagt fehlt da das Verständnis.
Gruß
technikus
Hallo,
ich werde die Strings heute abend mal in den Flash verlagern.
>Es kommt aber noch der Stack dazu, und damit wird es garantiert jetzt>schon im SARM eng.
Wie meinst du das ? Übernimmt der Compiler das für mich? Oder was muss
ich tuen ?
Mein Halbwissen sagt mir, dass der Stack sich Programmzeiger bei
Funktionsaufrufen merkt (bitte nicht schlagen ;) ).
Wie äussert sich ein Stack Problem ?
Gruß
Oliver schrieb:> Vermutlich wird es schon eine ganze Menge bringen, wenn du erst einmal> die konstanten Strings ins Flash verlegst.
Das ist leider im AVR-GCC "a pain in the ass".
Das ganze wird total unleserlich, besonders die Initialisierung.
Du hast aber 20 Zeichen reserviert und die Texte scheinen deutlich
kürzer (8 Byte) zu sein.
Dann kann man statt des Textes einen Pointer in das Array setzen.
Das braucht zwar 2 Bytes je Text mehr, aber die Texte belegen nur noch
ihre wirkliche Größe. Bei wenigen langen Texten spart man dann.
Vermutlich ist der ATmega1284-Entwickler ein AVR-GCC Fan gewesen (16kB
SRAM).
Der ist auch pinkompatibel zum ATmega16.
Peter
Peter Dannegger schrieb:> Das ganze wird total unleserlich, besonders die Initialisierung.
Da wolltest du wohl schreiben:
Das ganze wird total unleserlich, bis auf die Initialisierung.
Die Strings in ein PSTR() einzupacken, macht ja nun nicht viel aus. Viel
unleserlicher ist es, die hinterher wieder ais dem Flash
"herauszupulen".
Peter Dannegger schrieb:> Vermutlich ist der ATmega1284-Entwickler ein AVR-GCC Fan gewesen (16kB> SRAM).> Der ist auch pinkompatibel zum ATmega16.
Das wäre auch meine nächste Antwort gewesen ;)
technikus schrieb:> Wie äussert sich ein Stack Problem ?
Sehr unschön. Der Stack überschreibt dir einfach die Variablen im RAM.
Hallo Peter,
da sieht man den Wald vor lauter Bäumen nicht mehr.
Ich habe jetzt 12 Zeichen pro String reserviert. Damit sind "nur" noch
60% vom SRAM gefüllt.
Einen ATmega1284 werde ich wohl nicht brauchen ;)
Zur Not würde es ja auch ein ATmega32 tuen - aber wenn das nicht sein
muss, erspare ich mir gerne den größeren Controller.
Gruß
Oliver schrieb:> technikus schrieb:>> Wie äussert sich ein Stack Problem ?>> Sehr unschön. Der Stack überschreibt dir einfach die Variablen im RAM.
und wie merke ich das ? Nur wenn es zu spät ist?
Hallo,
um den Thread abzuschließen:
Ich habe nun die Menutexte in den Flash abgelegt.
Das Compilerergebnis:
Device: atmega16
Program: 5436 bytes (33.2% Full)
(.text + .data + .bootloader)
Data: 344 bytes (33.6% Full)
(.data + .bss + .noinit)
EEPROM: 30 bytes (5.9% Full)
(.eeprom)
Also noch einmal die hälfte vom SRAM.
Sollte ich nocheinmal mehrere Strings ablegen, dann im Flash. Zugegeben,
das Handling ist doch eher schei§€, doch die Ersparnis im SRAM ist es
wert.
technikus
Oliver schrieb:> Die Strings in ein PSTR() einzupacken, macht ja nun nicht viel aus.
Also ich bin daran gescheitert.
Es ist ja nicht ein einfacher String, sondern eine Struct:
technikus schrieb:> Hier sind die Strukturvariablen angelegt:> struct MenuEntry MainMenu[] = {> { "Input", HandleInput, 0, 26, 7, 0, 1 }, //[0] //Hauptmenu 1> { "Input 1", HandleInput1, 0, 6, 2, 1, 1 }, //[1] //Untermenu 1.1> { "Input 2", HandleInput2, 0, 1, 3, 2, 2 }, //[2]> { "Input 3", HandleInput3, 0, 2, 4, 3, 3 }, //[3]> { "Input 4", HandleInput4, 0, 3, 5, 4, 4 }, //[4]> { "Input 5", HandleInput5, 0, 4, 6, 5, 5 }, //[5]> { "Back", HandleInputBack, 0, 5, 1, 0, 0 }, //[6] //Untermenu 1.6> //usw.
Kannst mir aber gerne zeigen, wie man diese Struct definieren muß, damit
sie im Flash landet.
Peter
Ralf schrieb:> Ist 'HandleInputXX' eine Konstante?
Ja, die Adresse einer Funktion ist konstant und als Initializer
geeignet. Und wenn sie als Initializer für statische Daten im RAM
geeignet ist, dann auch für Daten im ROM (in C, nicht C++) - dem
Compiler ist dieser subtile Unterschied nämlich völlig egal.
Damit wird für jeden Text nur die tatsächlich benötigte Anzahl von
Zeichen (inklusive abschließender Null) reserviert, zuzüglich des
Pointers.
Das ist gerade bei Strings unterschiedlicher Länge sehr ratsam, auch
musst Du Dir nicht an zwei Stellen Gedanken über die maximal zulässige
Stringlänge machen.
Das ganze kannst Du dann noch auf die im Flash gespeicherte Variante
anpassen, auch da kannst Du statt des Strings selbst in der Struktur mit
Pointern arbeiten.
Hier zeigen sich die Nachteile der Harvard-Architektur der AVRs, bei
Controllern mit von-Neumann-Architektur ist das erheblich einfacher.
Rufus Τ. Firefly schrieb:> Damit wird für jeden Text nur die tatsächlich benötigte Anzahl von> Zeichen (inklusive abschließender Null) reserviert, zuzüglich des> Pointers.
Und diese Strings kann man relativ leicht ins Flash packen, ohne sich
mit Horden von pgm_xxx() Funktionen für jeden Strukturzugriff abkämpfen
zu müssen. Jedenfalls in C - PSTR inmitten der Initialisierung ging
zumindest in der damaligen avr-gcc Version nur in C, nicht aber in C++.
lcd_puts_p((pgm_read_word(&MainMenu[actualMenuPoint].Text)));//Menutext aus Flash holen
Alles andere ist so geblieben, da ich nur die Menutexte in den Flash
gelegt habe.
Ich danke euch vielmals für die Unterstützung!
Eine Frage ist aber noch offen: Wenn der SRAM zu voll wird und nicht
mehr genügend Platz für den Stack bleibt, was passiert?
Merke ich das erst bei einem Crash im Programm, und wieviel Platz sollte
ich für den Stack im SRAM lassen?
Klar, das wird programmabhängig sein, es wird aber doch eine
Schmerzgrenze geben.
Gruß
!ACHTUNG! Meine Lösung klappt so nicht wie dargestellt.
Ich habe die ganze Zeit den alten Hexfile geladen ;)
Die Texte aus dem Flash werden nicht im Display ausgegeben.
Wenn ich ein Array der Strings anlege kann ich mit der lcd_puts_p()
Funktion die Einträge auf das Display schreiben.
das glaub ich nicht.
MeinMenuStrArray liegt selber wieder im Flash. Also musst du der
pgm_read_word Funktion die Adresse das Strings mit dem Index
actualMenuPoint im Array MainMenuStrArray geben. Die Adresse! Nicht den
Wert!
Wenn du's nicht mehr durchblickst, dann nimm Abstand vor zu tiefen
Schachtelungen und drösle dir die Dinge in mehrere Anweisungen und
Hilfsvariablen auf (der Compiler schmeisst die eh wieder raus)
Karl Heinz Buchegger schrieb:>> Erkennt jemand den Fehler im Quelltext vom Posting oben ?>> Auf welchen Code beziehst du dich?
Wahrscheinlich auf das hier
lcd_puts_p((pgm_read_word(&MainMenu[actualMenuPoint].Text)));//Menutext aus Flash holen
MainMenu liegt nicht im Flash. Also brauchst du auch kein pgm_read_word
um die Inhalte aus MainMenu auszulesen.
lcd_puts_p( MainMenu[actualMenuPoint].Text );
wieder aufgedröselt
Karl Heinz Buchegger schrieb:> das glaub ich nicht.> MeinMenuStrArray liegt selber wieder im Flash. Also musst du der> pgm_read_word Funktion die Adresse das Strings mit dem Index> actualMenuPoint im Array MainMenuStrArray geben. Die Adresse! Nicht den> Wert!
Zu Spät am Abend ;) Als verzweifelter Laie ist mir im Posting das &
durchgerutscht.
Wenn ich die Adresse an die Funktion Pgm_read_word Funktion
übergebe,passt es.
Gruß
Karl Heinz Buchegger schrieb:> Karl Heinz Buchegger schrieb:>>>> Erkennt jemand den Fehler im Quelltext vom Posting oben ?>>>> Auf welchen Code beziehst du dich?>>> Wahrscheinlich auf das hier> struct MenuEntry MainMenu[] = {> { MainMenu100, HandleInput, 0, 26, 7, 0, 1}, //[0] //Hauptmenu 1>> ....> lcd_puts_p(( pgm_read_word( &MainMenu[actualMenuPoint].Text ) )); //Menutext
aus Flash holen
>> MainMenu liegt nicht im Flash. Also brauchst du auch kein pgm_read_word> um die Inhalte aus MainMenu auszulesen.>> lcd_puts_p( MainMenu[actualMenuPoint].Text );>> wieder aufgedröselt> const char* adrOfStringInFlash = MainMenu[actualMenuPoint].Text;> lcd_puts_p( adrOfStringInFlash );
Genau den Teil meinte ich. Da aber das Strukturelement MainMenu100 im
Flash liegt und Teil von MainMenu (was nicht im Flash liegt) ist, dachte
ich, dass ich mit pgm_read_word auf das im Flash liegende Element
zugreifen muss.
Probiere ich heute Abend.
Danke
>> Da MainMenu nicht als im Flash liegend markiert ist, kann ganz normal> auf das Array zugegriffen und daraus gelesen werden.
technikus schrieb:> Genau den Teil meinte ich. Da aber das Strukturelement MainMenu100 im> Flash liegt und Teil von MainMenu (was nicht im Flash liegt) ist,
das ist an dieser Stelle uninteressant.
Du greifst zuallererst auf MainMenu zu. Und das liegt nun mal nicht im
Flash.
Das du aus dem MainMenu dann einen Pointer bekommst, der auf einen
String zeigt, der dann im Flash liegt ist eine ganz andere Geschichte.
Aber darum kümmert sich ja das lcd_puts_p.
Eine Analogie:
Dein MainMenu ist ein Inhaltsverzeichnis auf dem vermerkt ist, welche
Bücher es gibt. Das Inhaltsverzeichnis liegt im Foyer auf, jeder kann es
ohne weiteres lesen und sich aus diesem Inhaltsverzeichnis herauslesen,
in welchem Regal das Buch zu finden ist. Nur wenn du an die eigentlichen
Bücher heran willst, dann brauchst du einen Wachmann, der den Tresor
aufschliest, damit du an die Bücher rankommst.
Legst du das Inhaltsverzeichnis selber auch in den Tresor, dann brauchst
du auch zum Lesen des Inhaltsverzeichnisses schon den Wachmann zum
aufschliessen.