Forum: Mikrocontroller und Digitale Elektronik Array-Speicher nach LCD-Ausgabe wieder freigeben


von Simon M. (edimahler)


Lesenswert?

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_t buffer[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

von Tom M. (Gast)


Lesenswert?

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...

von Karl H. (kbuchegg)


Lesenswert?

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)

von Simon M. (edimahler)


Lesenswert?

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...

von friedrich (Gast)


Lesenswert?

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)

von Tom M. (Gast)


Lesenswert?

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.)

von Tom M. (Gast)


Lesenswert?

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).

von Tom M. (tomm) Benutzerseite


Lesenswert?

Tom M. schrieb:
> getrennte Speicherbereiche für Flash/ROM und RA

Korrektur: getrennte Adressbereiche/getrennter Adressraum

von Sepp (Gast)


Lesenswert?

Idee von mir:
1
uint8_t PROGMEM Buffer[] = "LCD-AUSGABE 123";
2
3
Print_LCD(uint8_t *String)
4
{
5
  while (String)
6
    {
7
    Write_byte_toLCD(pgm_read_byte(String++));//Diese funktion noch implementieren!
8
    }
9
}
10
11
Print_LCD(Buffer);

von Tom M. (tomm) Benutzerseite


Lesenswert?

Sepp schrieb:

> while (String)

Fallstrick! Genau das (naja fast, *String wäre passender) funzt eben 
nicht.

von Karl H. (kbuchegg)


Lesenswert?

:-)
1
void Print_LCD(char *String)
2
{
3
  char c;
4
5
  while (( c = pgm_read_byte(String++)) != '\0')
6
  {
7
    Write_byte_toLCD(c);
8
  }
9
}

von Simon M. (edimahler)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Simon M. (edimahler)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Sicher.
> Du kannst sogar sprintf weiter benutzen.

Super, auch dies probiere ich gleich einmal aus, hab vielen Dank!

von Karl H. (kbuchegg)


Lesenswert?

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)

von Simon M. (edimahler)


Lesenswert?

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
void DOG_writetext(char *string)
2
{
3
  uint8_t c;
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_t PROGMEM Buffer[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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Simon M. (edimahler)


Lesenswert?

:-(

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?

von Karl H. (kbuchegg)


Lesenswert?

Simon Mahler schrieb:

> Nun, ok, in der Angabe "Data Memory Usage" ist natürlich nicht nur das
> SRAM drin.

Doch. Ist es.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Simon M. (edimahler)


Lesenswert?

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_t string1[12] PROGMEM  = {"test1"};
2
uint8_t string2[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_t string1[12] PROGMEM  = {"test1"};
2
uint8_t string2[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?

von Karl H. (kbuchegg)


Lesenswert?

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
void DOG_writetext_p(char *string)
3
{
4
  uint8_t c;
5
6
  while (c = pgm_read_byte(string++) != '\0')
7
  {
8
    sendbyte( c );
9
  }
10
}
11
12
// Text aus dem SRAM heraus ausgeben
13
void DOG_writetext(char *string)
14
{
15
  uint8_t c;
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

von Simon M. (edimahler)


Lesenswert?

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

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.