Hallo zusammen,
Trotz intensiver Suche und diversem "Pröblen" finde ich einfach keine
Lösung für eine (wahrscheinlich) wirklich simple Aufgabe:
Ich möchte mit meinem ATtiny25 einige Displayausgaben machen. Wenn ich
nun meine auszugebenden Texte mit sprintf in einen Buffer schreibe, wird
beim Build der gesamte Inhalt aller Displayausgaben bereits ins RAM
geschrieben, was natürlich nach nur schon wenigen LCD-Zeilen zum
RAM-Überlauf führt.
Nun meine Frage: Gibt es eine Möglichkeit, den Inhalt im RAM nach der
entsprechenden Displayausgabe wieder freizugeben, bzw. mit neuen Daten
zu füllen?
Hier ein Codeauszug zur Verdeutlichung:
1
uint8_tbuffer[16];
2
3
sprintf(buffer,"LCD-AUSGABE 123");
4
DOG_writetext(buffer,0,0);
5
6
sprintf(buffer,"LCD-AUSGABE 456");
7
DOG_writetext(buffer,0,0);
Nach der Displayanzeige "LCD-AUSGABE 123" brauche ich diesen String
nicht mehr, möchte also das RAM freigeben, bzw. einfach beim nächsten
Block mit "LCD-AUSGABE 456" überschreiben.
Mit malloc() und free() war ich nicht erfolgreich, irgendwo las ich was
von Unions, muss aber ehrlich gestehen, dass ich die Implementation mit
meinen Arrays zusammen mit Unions noch nicht so ganz sehe...
Auch "PROGMEM" versuchte ich zu verwenden, leider wird aber mein Array
trotzdem immer schon beim Build mit allen Daten gefüllt.
Die Optimierung in AVR Studio6 "0s" möchte/kann ich nicht ändern (hat
diese überhaupt einen Einfluss?)
Gibt es Ideen von euch Profis?
Vielen Dank für Hilfen aller Art, das Thema beschäftigt mich nun seit
Stunden, die Webrecherchen haben nicht gefruchtet... :-(
Gruss Edi
Simon Mahler schrieb:> sprintf(buffer,"LCD-AUSGABE 456");> DOG_writetext(buffer,0,0);
Schreib dir eine "writetext" Funktion, die Strings aus dem Flash
(PROGMEM) ausgeben kann. Hier fabrizierst du doppelten Overkill, erst
rumkopieren von Flash ins sram im Startup Code, dann vom ram nach
buffer...
Simon Mahler schrieb:> Auch "PROGMEM" versuchte ich zu verwenden, leider wird aber mein Array> trotzdem immer schon beim Build mit allen Daten gefüllt.
Das ist der Weg, den es zu gehen gilt.
(Was du mit 'Mein Array wird gefüllt' meinst, erschliesst sich mir
allerdings nicht wirklich)
Hallo zusammen und merci für die superschnellen Antworten!
Tom M. schrieb:> Schreib dir eine "writetext" Funktion, die Strings aus dem Flash> (PROGMEM) ausgeben kann.
bzw.
Karl Heinz Buchegger schrieb:> Das ist der Weg, den es zu gehen gilt.
Werde ich mal versuchen, merci für den Hinweis! Trotzdem, fürs
allgemeine Verständnis kapiere ich nicht, weshalb der Kompiler nicht
einfach die Werte im buffer überschreibt, sondern alles einzeln
ablegt...
Karl Heinz Buchegger schrieb:> (Was du mit 'Mein Array wird gefüllt' meinst,...
Sorry, mein Fehler, nicht das Array, sondern das RAM wird natürlich
gefüllt...
Was hälst Du davon, die Ausgaben als Konstanten zu definieren und dann
nur einen Pointer zu übergeben? Damit liegen die Werte im Flash und du
brauchst noch nicht mal 16byte (es sei denn du willst auch Texte
zusammnestellen dann bruchst du einmal 16Byte)
Simon Mahler schrieb:> weshalb der Kompiler nicht> einfach die Werte im buffer überschreibt, sondern alles einzeln> ablegt...
Das tut er ja. Allerdings ist der avr-gcc eigentlich ein Compiler für
eine Von-Neumann-Architektur. Mit Harvard tut er sich schwer (getrennte
Speicherbereiche für Flash/ROM und RAM). Deshalb kopiert der Startup
Code beispielsweise erstmal den STring "LCD-AUSGABE 123" vom Flash ins
SRAM, und DAVON legst du mittels sprintf eine weitere Kopie in buffer
ab. Deshalb geht dir das SRAM zu Neige, bevor du gross angefangen hast.
(Warum du hier sprintf() verwendest ist wohl dein Geheimnis. Du könntest
ja ebenso DOG_writetext("blah",0,0); verwenden.)
friedrich schrieb:> Was hälst Du davon, die Ausgaben als Konstanten zu definieren und dann> nur einen Pointer zu übergeben? Damit liegen die Werte im Flash und du> brauchst noch nicht mal 16byte (es sei denn du willst auch Texte> zusammnestellen dann bruchst du einmal 16Byte)
Das wird nicht helfen bzw. reicht nicht! Die Strings sollen
ausschliesslich im Flash bleiben; das muss man dem Compiler aber
beibringen (nochmals das Stichwort PROGMEM) und bei allen Zugriffen auf
solche Daten beachten (siehe pgmspace.h aus der avr-libc).
Wow, vielen Dank für Euer reges Interesse, komme gar nicht nach mit
Lesen, Wahnsinn!
Tom M. schrieb:> (Warum du hier sprintf() verwendest ist wohl dein Geheimnis. Du könntest> ja ebenso DOG_writetext("blah",0,0); verwenden.)
Da hast Du völlig recht. Solange natürlich nur fixer Text drin steht,
trifft dies zu. Jedoch möchte ich auch irgendwann mal Variablen
mitausgeben können...
Kann ich trotzdem den PROGMEM-Ansatz weiter verfolgen?
Tom M. schrieb:> Mit Harvard tut er sich schwer
Mist, moment mal, habe ich mit dem AVR Studio bzw. GCC tatsächlich einen
Kompiler, der eigentlich für von Neumann optimiert ist? Kann ich ihn bei
solchen Anliegen irgendwie anweisen, die Daten nicht doppelt zu
speichern?
Karl Heinz Buchegger schrieb:> void Print_LCD(char *String)> {> char c;>> while (( c = pgm_read_byte(String++)) != '\0')> {> Write_byte_toLCD(c);> }> }
Das werde ich nun mal reinhacken und sehen obs läuft, vielen Dank!
Nochmals die Frage hier: wenn auch mal eine Variable auf dem Display
erscheinen soll, geht diese Variante dann auch irgendwie?
Simon Mahler schrieb:> Da hast Du völlig recht. Solange natürlich nur fixer Text drin steht,> trifft dies zu. Jedoch möchte ich auch irgendwann mal Variablen> mitausgeben können...>> Kann ich trotzdem den PROGMEM-Ansatz weiter verfolgen?
Sicher.
Du kannst sogar sprintf weiter benutzen. sprintf kann auch mit Texten im
Flash umgehen. Im AVR-GCC-Tutorial steht, wie das geht.
Allerdings so viele unterschiedliche Ausgaben hat man ja meistens nicht.
sprintf ist zwar komfortabel, allerdings hat man die halbe Handvoll
Speizlaroutinen auch so sehr schnell zusammengestoppelt.
Karl Heinz Buchegger schrieb:> Simon Mahler schrieb:>>> Da hast Du völlig recht. Solange natürlich nur fixer Text drin steht,>> trifft dies zu. Jedoch möchte ich auch irgendwann mal Variablen>> mitausgeben können...>>>> Kann ich trotzdem den PROGMEM-Ansatz weiter verfolgen?>> Sicher.> Du kannst sogar sprintf weiter benutzen. sprintf kann auch mit Texten im> Flash umgehen. Im AVR-GCC-Tutorial steht, wie das geht.
Hab mich geirrt. Im Tutorial steht nichts davon.
Aber in der eigentlich libc-Doku
http://nongnu.org/avr-libc/user-manual/group__avr__stdio.html#ga6017094d9fd800fa02600d35399f2a2a
es gibt ein sprintf_P
(trotzdem ist das Tutorial an dieser Stelle nicht unnütz)
Ich habe das sprintf_P zusammen mit PROGMEM mal eingesetzt, allerdings
wird bei zwei unterschiedlichen Ausgaben auf dem LCD wiederum mehr RAM
verwendet, als wenn ich nur einmal eine Zeile ausgebe. Zudem explodiert
mir nun der Flash-Speicher(?!).
Der Code sieht folgendermassen aus:
1
voidDOG_writetext(char*string)
2
{
3
uint8_tc;
4
5
while(c=pgm_read_byte(string++)!='\0')
6
{
7
sendbyte(string[c]);
8
_delay_us(30)
9
}
10
}
11
12
13
// Aufruf:
14
15
uint8_tPROGMEMBuffer[16];
16
17
sprintf_P(Buffer,"HALLO WELT 1");
18
DOG_writetext(Buffer,0,0);
19
20
sprintf_P(Buffer,"HALLO WELT 2");
21
DOG_writetext(Buffer,1,0);
Habe ich was falsch verstanden? Ich meinte mich auf AVR-GCC-Tutorial und
die obigen Ideen berufen zu haben, kann aber sein, dass ichs falsch
verstanden habe?
OOps.
ATtiny25
Da musst du etwas sparen. sprintf ist ab da tabu.
> allerdings wird bei zwei unterschiedlichen Ausgaben auf dem LCD> wiederum mehr RAM verwendet, als wenn ich nur einmal eine Zeile ausgebe.
Nicht mit dem was du gezeigt hast.
Der SRAM verbrauch ist konstant und hat nichts damit zu tun, wie oft du
sprintf_P benutzt.
:-(
Aber bisher hats eigentlich gut geklappt, was den Flash anbelangt.
sprintf ging immer gut. Erst seit sprintf_P gibts Probleme...
Und wenn ich sonst halt auf den 85-er gehe?
Karl Heinz Buchegger schrieb:> Nicht mit dem was du gezeigt hast.> Der SRAM verbrauch ist konstant und hat nichts damit zu tun, wie oft du> sprintf_P benutzt.
Nun, ok, in der Angabe "Data Memory Usage" ist natürlich nicht nur das
SRAM drin. Könnte der Grund für die Änderung hier liegen?
Simon Mahler schrieb:> uint8_t PROGMEM Buffer[16];>> sprintf_P(Buffer,"HALLO WELT 1");> DOG_writetext(Buffer,0,0);>> sprintf_P(Buffer,"HALLO WELT 2");> DOG_writetext(Buffer,1,0);
sprintf_P hat nicht das Flash als Ziel, sondern holt nur den
Formatstring aus dem Flash.
Mist, irgendwie fühle ich mich der Sache nicht gewachsen. Nun habe ich
testweise mal alle Arrays entfernt und schreibe einfach direkt meine
Texte ans LCD:
1
DOG_writetext("*BOS08E-Konfig.*",0,0);
2
DOG_writetext("Start: kurz dr.",1,0);
3
DOG_writetext("Konfig: lang dr.",2,0);
Data Memory Usage: 98 Bytes
Lasse ich eine Zeile weg:
1
DOG_writetext("*BOS08E-Konfig.*",0,0);
2
DOG_writetext("Start: kurz dr.",1,0);
Data Memory Usage: 80 Bytes (?!)
Mit dem früher geposteten Ausschnitt mit PROGMEM wars das gleiche
Problem:
1
uint8_tstring1[12]PROGMEM={"test1"};
2
uint8_tstring2[12]PROGMEM={"test2"};
3
4
DOG_writetext(pgm_read_byte(&string1[0]),0,0);
5
DOG_writetext(pgm_read_byte(&string2[1]),1,0);
Data Memory Usage: 70 Bytes
und
1
uint8_tstring1[12]PROGMEM={"test1"};
2
uint8_tstring2[12]PROGMEM={"test2"};
3
4
DOG_writetext(pgm_read_byte(&string1[0]),0,0);
Data Memory Usage: 58 Bytes
Mache ich da einen groben Überlegungsfehler, wenn ich behaupte, dass da
definitiv etwas nicht stimmen kann?
Simon Mahler schrieb:> Mist, irgendwie fühle ich mich der Sache nicht gewachsen. Nun habe ich> testweise mal alle Arrays entfernt und schreibe einfach direkt meine> Texte ans LCD:>>
1
DOG_writetext("*BOS08E-Konfig.*",0,0);
2
>DOG_writetext("Start: kurz dr.",1,0);
3
>DOG_writetext("Konfig: lang dr.",2,0);
Diese Strings liegen aber NICHT im Flash.
Du musst schon aufpassen, was im Flash liegt und was nicht und wie die
Funktion, die du aufrufst das erwartet.
Da DOG_writetext mitlerweile so umgebaut ist, dass es einen Pointer ins
Flash erwartet, muss es an dieser Stelle heißen
1
DOG_writetext(PSTR("Start: kurz dr."),1,0);
Dann verfrachtet der Compiler den Text auch ins Flash und er belegt
KEINEN Platz mehr im SRAM dafür.
Man muss also wissen, welchen Pointer eine FUnktion jeweils erwartet:
einen ins SRAM oder einen ins Flash.
Und damit man da nicht durcheinanderkommt, gibt es die Konvention, dass
derartige Funktionen, die einen Pointer ins Flash erwarten, immer im
Namen mit _p enden.
Die Funktion sollte also eigentlich DOG_writetext_p heißen, wobei dir
als Programmierer das _p sagt: Ich will einen Pointer ins Flash. Das
PSTR Makro sorgt dafür.
1
// Text aus dem Flash heraus ausgeben
2
voidDOG_writetext_p(char*string)
3
{
4
uint8_tc;
5
6
while(c=pgm_read_byte(string++)!='\0')
7
{
8
sendbyte(c);
9
}
10
}
11
12
// Text aus dem SRAM heraus ausgeben
13
voidDOG_writetext(char*string)
14
{
15
uint8_tc;
16
17
while(c=*string++)!='\0')
18
{
19
sendbyte(c);
20
}
21
}
22
23
24
...
25
// Strings aus dem Flash heraus ausgeben
26
27
DOG_writetext_p(PSTR("*BOS08E-Konfig.*"),0,0);
28
DOG_writetext_p(PSTR("Start: kurz dr."),1,0);
29
DOG_writetext_p(PSTR("Konfig: lang dr."),2,0);
30
31
// Strings aus dem SRAM heraus ausgeben
32
DOG_writetext("*BOS08E-Konfig.*",0,0);
33
DOG_writetext("Start: kurz dr.",1,0);
34
DOG_writetext("Konfig: lang dr.",2,0);
35
36
...
PS: Du hast deine Ausgabefunktionen verhunzt. Übernimm sie nochmal
Karl Heinz Buchegger schrieb:> Da DOG_writetext mitlerweile so umgebaut ist, dass es einen Pointer ins> Flash erwartet, muss es an dieser Stelle heißen> DOG_writetext( PSTR("Start: kurz dr."), 1, 0 );
Wow, danke, das hat schon mal ein ganzes Stück geholfen.
Eine kleine Unschönheit in Deiner Ausgaberoutine war noch drin, die ich
unterdessen ebenfalls gefunden habe:
1
while(c=pgm_read_byte(string++)!='\0')
Gab eigenartige Zeichen auf dem Display aus, mein Kompiler scheint dies
zu missinterpretieren...
1
while((c=pgm_read_byte(string++))!='\0')
Mit den zugehörigen Klammern scheint das Problem behoben, kein
zusätzlicher RAM-Verbrauch mehr, zuverlässige Ausgabe.
Vielen vielen Dank Heinz, ich hoffe, dass ich mich irgendwann auf irgend
eine Art bei Dir revanchieren werden kann! Vielen Dank natürlich auch an
alle anderen, die mitberaten haben, Ihr seid ein geniales Team!
Beste Grüsse
Edi