Forum: Mikrocontroller und Digitale Elektronik AVR Texte im SRAM, Überlauf??


von Philipp P. (putzer_philipp)


Lesenswert?

Hallo!

Ich beschäftige mich seit geschlagenen 6 Stunden um das Fehlverhalten 
meines Programms auszumerzen.

Folgendes:
Ich will mit einem 4x20 LCD eine Menüstruktur aufbauen, so mit vier bis 
fünf Tasten zur Navigation (links, rechts, +, - MENÜ). So ein Menü habe 
ich schon öfters gemacht, bisher waren diese auch nicht gerade ewig lang 
und aufwendig.

Nur dieses Mal wird das Ganze etwas länger, wodurch ich auch mehr Texte 
usw. anzeigen und speichern muss.

Meine bisherige Idee war:
Ich schreibe den Menütext, wohin mein Menüpointer (diesen verändere ich 
mit den Tasten PLUS und MINUS) zeigt, mit einem Pfeil vorangestellt ans 
Display. Die restlichen Texte werden einfach umdrei Stellen nach rechts 
versetzt und ohne Pfeil ans LCD geschrieben.

In etwa so:

"-> Menüpunkt 1      "
"   Menüpunkt 2      "
"   Menüpunkt 3      "
"   Menüpunkt 4      "

Naja, eine etwas unelegante Methode, da ich bei einer Änderung immer den 
gesatem Bildschirm neu schreibe. Der Pfeil bewegt sich nur optisch auf 
und nieder, in Wahrheit wird der gesamte Bildschirminhalt neu aufgebaut. 
Würde ich den Pfeil mit den Stringfunktionen z.B. an den Text vorne 
herankopieren würde ich vielleicht 8 Texte weniger brauchen

Nun (endlich) zum Problem

Kurz: Die ganzen Texte scheinen irgendwie nicht im SRAM(?) platz zu 
haben.

Alles in allem habe ich ca. 50 Texte à 20 Zeichen => 1kByte Speicher im 
Flash.

Soweit ich das nun vertsanden habe, lädt das Programm zu Beginn die 
Texte in den SRAM, welcher dann überläuft, und das totale Kaos tritt 
auf.

Kann ich diese nicht auch vom Flash heraus ins LCD schreiben; oder 
zumidest  nur in den RAM laden wenn diese gerade benötigt werden?

Lösche ich mal die halben Texte für das Menü, funkt es perfekt.

Gibt es da irgendwas (programmtechnisches), wie ich mein Problem lösen 
könnte?? Vielleicht ohne den ganzen Menüaufbau über den Haufen zu 
werfen?

Achja, momentan "läuft" das Ganze auf einem ATMEGA16 mit 1kByte RAM


Vielen vielen Dank und schöne Grüße

philipp

von Magnus Müller (Gast)


Lesenswert?

> Kurz: Die ganzen Texte scheinen irgendwie nicht im SRAM(?) platz zu
> haben.
>
> Alles in allem habe ich ca. 50 Texte à 20 Zeichen => 1kByte Speicher im
> Flash.
>
> Achja, momentan "läuft" das Ganze auf einem ATMEGA16 mit 1kByte RAM

1kByte(Texte) + Variablen + Stack = deutlich mehr als 1kByte
-> SRAM reicht nicht aus
-> Stack kollidiert mit Variablen / Texten.


> Soweit ich das nun vertsanden habe, lädt das Programm zu Beginn die
> Texte in den SRAM, welcher dann überläuft, und das totale Kaos tritt
> auf.

Richtig... siehe oben.


> Kann ich diese nicht auch vom Flash heraus ins LCD schreiben; oder
> zumidest  nur in den RAM laden wenn diese gerade benötigt werden?

Ja.


> Gibt es da irgendwas (programmtechnisches), wie ich mein Problem lösen
> könnte?? Vielleicht ohne den ganzen Menüaufbau über den Haufen zu
> werfen?

Ja. Das Thema wurde, wenn ich mich nicht irre, schon desöfteren hier im 
Forum behandelt...

von Hegy (Gast)


Lesenswert?

Vielleichts hilft's:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Vereinfachung_f.C3.BCr_Zeichenketten_.28Strings.29_im_Flash

Vereinfachung für Zeichenketten (Strings) im Flash

Zeichenketten können innerhalb des Quellcodes als "Flash-Konstanten" 
ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies 
erspart die getrennte Deklaration mit PROGMEM-Attribut.

[...]

von Philipp P. (putzer_philipp)


Lesenswert?

Hi!

Aha, das klingt schon mal sehr gut!

Nur leider habe ich noch ein (verständniss-) Problem.

Ich habe nun alle Texte folgendermaßen deklariert:
unsigned char texte_ueberschrift_0 [] PROGMEM = "       Setup        ";

Weiter unten im Programm verwende ich den Text folgendermaßen:
lcd_writetext( PSTR(texte_ueberschrift_0) );

Die Funktion lcd_writetext sieht folgendermaßen aus:

void lcd_writetext ( char *text)
{ uint8_t i = 0;
  while (text[i]!=0)
  { lcd_writechar(text[i]);
    i++;
  }
}

Beim Compilieren erhalte ich die Fehlermeldung "invalid initializer" 
bzw. die Warnung "passing arg 1 of `lcd_writetext' discards qualifiers 
from pointer target type"

Mein Frage nun, muss ich bei der Deklaration PROGMEM hinzuschreiben oder 
nicht?

Das PSTR(xxx) liefert mir ja einen Pointer auf den Text im Flash zurück, 
oder irre ich mich da? Meine Lcd-Routine erwartet ja auch einen Pointer, 
demnach müsste das ja passen.

Lasse ich das PSTR weg, zeigt mir das LCD völlig zusammenhanglose Texte 
an.


Kann mir jemand sagen was ich da falsch mache??

Vielen Dank
Philipp

von Hegy (Gast)


Lesenswert?

> Das PSTR(xxx) liefert mir ja einen Pointer auf den Text im Flash zurück,
> oder irre ich mich da?
1
#define PSTR (s)   ((const PROGMEM char *)(s))
Used to declare a static pointer to a string in program space.

Mehr dazu findest du hier:
http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html

oder zu allen Funktionen/Deklarationen
http://www.nongnu.org/avr-libc/user-manual/

inkl. FAQ, Beispiele, Doku.....

von Hegy (Gast)


Lesenswert?

Achso, vielleicht hilft auch das GCC-Forum und Suche.
klikk den mal vielleicht:

http://www.mikrocontroller.net/search?query=PSTR&forums%5B%5D=2

von Karl H. (kbuchegg)


Lesenswert?

Philipp Putzer wrote:
> Hi!
>
> Aha, das klingt schon mal sehr gut!
>
> Nur leider habe ich noch ein (verständniss-) Problem.
>
> Ich habe nun alle Texte folgendermaßen deklariert:
> unsigned char texte_ueberschrift_0 [] PROGMEM = "       Setup        ";
>
> Weiter unten im Programm verwende ich den Text folgendermaßen:
> lcd_writetext( PSTR(texte_ueberschrift_0) );
>

Wenn der Text mittels
 unsigned char texte_ueberschrift_0 [] PROGMEM = "       Setup        ";

sowieso schon im Flash liegt, dann brauchst du das PSTR
nicht mehr. Das PSTR ist nur ein Makro, dass dir erlaubt
Texte 'on the fly' in den Flash zu verlagern. Also
in Fällen wie

   lcd_writetext( PSTR( "Dies ist ein Text" ) );

Normalerweise würde "Dies ist ein Text" im SRAM landen und das
PSTR sorgt dafür, dass er ins Flash kommt.


> Die Funktion lcd_writetext sieht folgendermaßen aus:
>
> void lcd_writetext ( char *text)
> { uint8_t i = 0;
>   while (text[i]!=0)

Nein.
Schau noch mal ins Tutorial.
Wenn text ein Pointer in den Flash ist (und das muss er bei
dir jetzt sein), dann geht das nicht so, sondern mit der
Funktion pgm_read_byte. Im Tutorial ist doch eine Funktion
die Text aus dem Flash holt und die einzelnen Zeichen auf dem
UART ausgibt. Das 'auf dem UART ausgeben' ersetzt du durch
'auf dem LCD ausgeben' und du hast deine Funktion.

> Kann mir jemand sagen was ich da falsch mache??

Studiere das Tutorial noch mal.

von Karl H. (kbuchegg)


Lesenswert?

> In etwa so:
>
> "-> Menüpunkt 1      "
> "   Menüpunkt 2      "
> "   Menüpunkt 3      "
> "   Menüpunkt 4      "
>
> Naja, eine etwas unelegante Methode, da ich bei einer Änderung immer den
> gesatem Bildschirm neu schreibe. Der Pfeil bewegt sich nur optisch auf
> und nieder, in Wahrheit wird der gesamte Bildschirminhalt neu aufgebaut.
> Würde ich den Pfeil mit den Stringfunktionen z.B. an den Text vorne
> herankopieren würde ich vielleicht 8 Texte weniger brauchen

Sag blos, du hast jeden Text 2-mal: Einmal mit Pfeil und einmal
ohne Pfeil.

Wozu musst du den Bildschirm immer neu aufbauen?
Du kannst den Cursor positionieren.

Beim 'Cursor down'

 * Cursor in die Zeile wo der Pfeil jetzt ist, in Spalte 0
 * 3 Leerezeichen ausgeben
 * Cursor in die Zeile wo der Pfeil hin soll, in Spalte 0
 * "-> " ausgeben

fertig. Ausser im Fall das gescrollt werden muss, muss der Schirm
keineswegs neu aufgebaut werden.

Und wenn du die Menütexte beim ausgeben gleich ab Spalte 3 ausgibst,
dann kannst du dir auch die 3 Leerzeichen, mit denen jeder Menütext
anfängt sparen und sparst so gleich mal 50 * 3 = 150 Bytes ein.


von Philipp P. (putzer_philipp)


Lesenswert?

Ich glaubs ja fast nicht! Das ging ja schnell!!!

Jetzt verstehe ich auch den Unterschied zwischen PROGMEM und PSTR, super 
danke!

Nun funkt es tadellos!!

@ Karl heinz Buchegger

Ja, das war meist nur eine Notlösung; Aber bei so einem Haufen Texte 
werde ich es sicher anderst machen, wäre ja schade um den verbrauchten 
Speicher.


Also nochmal vielen Dank an alle!!

Schöne Grüße
Philipp

von Michael U. (Gast)


Lesenswert?

Hallo,

ich gebe zu, daß ich bei den Text-Displays auch nicht mit dem Cursor 
rumwerke.
Meine Variante: für jede Displayzeile ist passend Ram reseviert.
Vorgabetexte werden beim ersten "Bild-"Aufbau vom Flash ins Ram kopiert.
Innerhalb des Menüs/der Funktion weredn nur veränderte Zeichen in Ram 
geschrieben und dann die komplette Zeile ans Display geschickt.

Allerdings in Assembler, deshalb hier kein Beispiel. ;)

Eigentlich habe ich so nur eine "Copy"-Routine, die bekommt den 
Textzeiger und die Zeilennummer, die löscht die gewünschte Zeile im Ram 
und kopiert dann den Text aus dem Flash bis zum 0-Byte ins Ram.

Für das Display gibt es außer Init so nur "Write Line" mit der 
Zeilenummer, um die Zeile ins Display zu schaffen.

Bisher hat die unnötige CPU-Last noch nicht wirklich gestört und so ist 
es für kleinere Sachen schön übersichtlich.

Gruß aus Berlin
Michael

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
Noch kein Account? Hier anmelden.