Forum: Mikrocontroller und Digitale Elektronik Array vom PROGMEM aufs Display


von Holger K. (zaldo)


Lesenswert?

Hallo zusammen.

Ich habe mein eigentliches Problem bereits gelöst. Ich möchte aus einem 
Array im PROGMEM einen String auf einem LCD ausgeben, unter Zuhilfenahme 
der HD44780 Library hier aus dem Board.

So funktioniert es:
1
const char mainmenu[] [8] PROGMEM = {"Menue 1","Menue 2","Menue 3"};
2
lcd_string_P(mainmenu[1]);

Da ich aber zu Anfang nicht wusste, dass die Library auch direkt mit dem 
PROGMEM arbeiten kann (lieber zwei Stunden rumprobieren als eine Minute 
lesen) habe ich im ersten Ansatz versucht, dies über den (manuellen) 
Umweg über das RAM zu machen. Es hat nicht funktioniert, und ich würde 
gerne noch verstehen warum nicht?

Also so sah es aus:
1
const char mainmenu[] [8] PROGMEM = {"Menue 1","Menue 2","Menue 3"};
2
char *buffer=NULL;
3
4
// so aus dem Tut übernommen...
5
void read_string (char* buf, size_t i)
6
{
7
    // Kopiere den Inhalt der i-ten Zeichenkette vom Flash ins RAM
8
    strcpy_P (buf, mainmenu[i]);
9
}
10
11
read_string(buffer, 1);
12
lcd_string(buffer);

Nix. Das Display blieb leer. Warum? So müsste es doch eigentlich auch 
gehen. Ich verstehe das auch syntaktisch nicht ganz. Es heißt "Kopiere 
die Zeichenkette ins RAM", also den "literalen" String aus PROGMEM 
mainmenu[] nach RAM buf - zumindest verstehe ich die Beschreibung so. 
Warum muss "buf" (und auch mein buffer) dann als Pointer initialisiert 
werden? Und wenn der - anders als beschrieben - dann nicht die 
Zeichenkette selber sondern nur den Pointer zu der Zeichenkette enthält, 
ist auch einlechtend warum nix auf dem Display kommt.

Wie gesagt, ich möchte nur noch verstehen warum es so nicht funktioniert 
hatte. Habt Nachsicht mit mir, bin noch AVR-GCC Anfänger :-)

Beste Grüße
Holger

: Bearbeitet durch User
von Frickelfritze (Gast)


Lesenswert?

Holger K. schrieb:
> const *buffer=NULL;

Diese Konstante hat keinen Typ (wird zum default-Typ gestempelt),
und eine Konstante kann bekanntlich nicht verändert werden.

Versuchs mal mit
1
char *buffer=NULL;

von Holger K. (zaldo)


Lesenswert?

Oh sorry, das war nur ein Typo beim runterschreiben. Da hätte der 
Compiler ja bei der späteren Zuweisung ja auch gemeckert. Es war 
natürlich
1
char *buffer=NULL;

ich hab das im EP auch gleich geändert...

von W.A. (Gast)


Lesenswert?

Wo bekommst du denn den Speicherplatz her, auf den buffer zeigt?

von Holger K. (zaldo)


Lesenswert?

Aus dem RAM? Oder wie ist die Frage gemeint?

Glaubst Du, dass das stellen rhetorischer Fragen an einen Anfänger 
zielführend ist?

von Ralf G. (ralg)


Lesenswert?

Holger K. schrieb:
> Aus dem RAM? Oder wie ist die Frage gemeint?
>
> Glaubst Du, dass das stellen rhetorischer Fragen an einen Anfänger
> zielführend ist?

Mutig reagiert!

Hier stand noch:
Holger K. schrieb:
> bin noch AVR-GCC Anfänger
           ^^^^^^^

Grundlagen:
http://www.c-howto.de/tutorial-zeiger-speicher.html

von Falk B. (falk)


Lesenswert?

@  Holger Keil (zaldo)

>So funktioniert es:

>const char mainmenu[] [8] PROGMEM = {"Menue 1","Menue 2","Menue 3"};
>lcd_string_P(mainmenu[1]);

Damit ist dein Problem gelöst.

>Umweg über das RAM zu machen. Es hat nicht funktioniert, und ich würde
>gerne noch verstehen warum nicht?

>const char mainmenu[] [8] PROGMEM = {"Menue 1","Menue 2","Menue 3"};
>char *buffer=NULL;

Das ist nur ein Pointer, kein Array. Du brauchst aber ein Array, wo du 
deine Daten reinkopierst.

>// so aus dem Tut übernommen...

Tut? tut tut? Baysprache? Hipstersyndrom?

Meine Herrn, man kann es mit den Abks. echt übertreiben!

>void read_string (char* buf, size_t i)
>{
>    // Kopiere den Inhalt der i-ten Zeichenkette vom Flash ins RAM
>    strcpy_P (buf, mainmenu[i]);
>}

>read_string(buffer, 1);

Damit landen die Daten im RAM ab Adresse NULL. Das willst du sicher 
nicht.

>lcd_string(buffer);

Eher so.

1
const char mainmenu[] [8] PROGMEM = {"Menue 1","Menue 2","Menue 3"};
2
char buffer[8];
3
4
strcpy_P (buf, mainmenu[i], 8);
5
lcd_string(buffer);

von Arduinoquäler (Gast)


Lesenswert?

Falk B. schrieb:
> Eher so.

Ahhhh .... Falk hat uns gerettet. Wenn wir ihn nicht hätten ....
... keiner hätte es sonst gewusst.

von Falk B. (falk)


Lesenswert?

OK, eher so

strcpy_P (buf, mainmenu[i]);

(ich war gedanklich bei memcpy, dort braucht man die Anzahl der Bytes, 
nicht aber bei 0-terminierten Strings)

von Holger K. (zaldo)


Lesenswert?

Falk B. schrieb:

Danke, wenigstens mal ein konstruktiver Beitrag!

> Damit ist dein Problem gelöst.

Ich weiß, aber aus Fehlern lernt man nur, wenn man auch versteht wo und 
warum sie waren.

>>char *buffer=NULL;
>
> Das ist nur ein Pointer, kein Array. Du brauchst aber ein Array, wo du
> deine Daten reinkopierst.

Ich dachte, weil die Rückgabe von strcpy_P doch ein Pointer ist.

"The strcpy_P() function returns a pointer to the destination string 
dest."

Ist die Rückgabe demnach also ein Array von Pointern?

>>// so aus dem Tut übernommen...
>
> Tut? tut tut? Baysprache? Hipstersyndrom?
>
> Meine Herrn, man kann es mit den Abks. echt übertreiben!

Die Abk. von Abk. ist Abk.
TUTorial war gemeint

Und ich glaube da war das größte Mißverständnis bei mir. Ich bin davon 
ausgegangen, dass er den String aus mit [i] referenzierten Element des 
Arrays (im Flash) direkt als Rückgabe übergibt.

Er kopiert also nur das komplette Array vom Flash ins RAM, und liefert 
ein Array mit den Startadressen der jeweiligen Elemente im RAM zurück?

> const char mainmenu[] [8] PROGMEM = {"Menue 1","Menue 2","Menue 3"};
> char buffer[8];
>
> strcpy_P (buf, mainmenu[i]);
> lcd_string(buffer);

Wo bekommt buffer in dem Beispiel denn seinen Inhalt her?

von Holger K. (zaldo)


Lesenswert?

Arduinoquäler schrieb:
> ... keiner hätte es sonst gewusst.

Zumindest hat es keiner sonst geschrieben. Gelegenheit (mehr als nur 
Gegenfragen oder Links zu posten) wäre ja gewesen.

von Falk B. (falk)


Lesenswert?

@ Holger Keil (zaldo)

>>>char *buffer=NULL;
>>
>> Das ist nur ein Pointer, kein Array. Du brauchst aber ein Array, wo du
>> deine Daten reinkopierst.

>Ich dachte, weil die Rückgabe von strcpy_P doch ein Pointer ist.

Der interessiert nicht.

>"The strcpy_P() function returns a pointer to the destination string
>dest."

Den kein Mensch braucht, dieser Rückgabewert ist rein akademisch, denn 
eben dieser Pointer wird ja als Argument übergeben.

>Ist die Rückgabe demnach also ein Array von Pointern?

Nein.

>TUTorial war gemeint

Das weiß ich, trotzdem geht es mir auf den Keks!

>Und ich glaube da war das größte Mißverständnis bei mir. Ich bin davon
>ausgegangen, dass er den String aus mit [i] referenzierten Element des
>Arrays (im Flash) direkt als Rückgabe übergibt.

Nein.

>Er kopiert also nur das komplette Array vom Flash ins RAM,

Nein, er kopiert nur einen String des Array, in deinem Beispiel 
mainmenu[i].

Vorsicht! mainmenu[i] ist ein Pointer auf den Arrayeintrag so wie 
mainmenu (ohne Index) ein Pointer auf das Gesamtarray ist. Nur wenn alle 
Indizes angegeben werden, wie z.B. mainmenu[1][i] ist das der INHALT des 
Arrays an dieser Stelle. Nicht wundern, das ist nun mal C.

> und liefert
>ein Array mit den Startadressen der jeweiligen Elemente im RAM zurück?

Nein, die Funktion liefert einen einfachen Pointer als Rückgabewert, 
siehe oben.

>> const char mainmenu[] [8] PROGMEM = {"Menue 1","Menue 2","Menue 3"};
>> char buffer[8];
>
>> strcpy_P (buf, mainmenu[i]);
>> lcd_string(buffer);

>Wo bekommt buffer in dem Beispiel denn seinen Inhalt her?

Na wie wohl? Das macht die Funktion!!! Die könnte in etwa so aussehen.

1
PGM strcpy_P(PGM dest, void *src) {
2
  char c;
3
  PGM tmp=dest;
4
5
  while( (c=pgm_read_byte(src++)) ) {
6
    *dest = c;
7
     dest++;
8
  }
9
  return tmp;
10
}

von Holger K. (zaldo)


Lesenswert?

Falk B. schrieb:

>Nicht wundern, das ist nun mal C

Ja hab schon bemerkt, wenn man sich vorher nur ein wenig mit VB und PHP 
befasst hat, kommt man schon das eine oder andere mal ins grübeln :-)

> Na wie wohl? Das macht die Funktion!!! Die könnte in etwa so aussehen.

Ok, dann aber
1
 strcpy_P (buffer, mainmenu[i]);
und nicht
1
 strcpy_P (buf, mainmenu[i]);

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Holger K. schrieb:
> Ok, dann aber strcpy_P (buffer, mainmenu[i]);
> und nicht
>  strcpy_P (buf, mainmenu[i]);

 Er zeigt dir wo deine Fehler sind und wie das Ganze aufzubauen ist
 und du findest es nötig, ihn auf einen Schreibfehler aufmerksam zu
 machen ?

von Holger K. (zaldo)


Lesenswert?

Nu lass mal die Kirche im Dorf Marc. Das war nicht als Belehrung zu 
verstehen, ich wollte nur sicher gehen dass ich es richtig verstanden 
habe. Und wenn es nur ein Schreibfehler ist finde ich es auch 
grundsätzlich nicht verwerflich ihn zu korrigieren. Irgendjemand stößt 
vielleicht ein anderer Anfänger mit demselben Problem über die Suche auf 
den Thread und findet was was er aus gutem Grund nicht nachvollziehen 
kann.

von Falk B. (falk)


Lesenswert?

Ja, es war ein Tepfuhler ;-)

von Holger K. (zaldo)


Lesenswert?

Okay, Danke. Das hat mir sehr weitergeholfen.

von Stringanwender (Gast)


Lesenswert?

hmmmmmmm ..... ich hätte da gerne noch mal ein Problem ...

Kann mir bitte jemand sagen warum das nicht funktioniert:
1
strcpy_P (buffer, (PGM_P)"TestString");

Die Zeile übersetzt sich fehlerfrei .... aber ....
Man kann den konstanten String casten wie man will, der
GCC reserviert dafür immer RAM.

Anders herum gefragt: wie bekommt man (Konstanten-)Strings
ins Flash ohne Variablen zu deklarieren?

von Rainer B. (katastrophenheinz)


Lesenswert?

wie wär's mit PSTR ?
1
#include <avr/pgmspace.h>
2
strcpy_P (buffer, PSTR("TestString"))

von Stringanwender (Gast)


Lesenswert?

Rainer B. schrieb:
> wie wär's mit PSTR ?

Ja danke, das funktioniert.

Wie soll da einer draufkommen .... Ich hab es mir vorher im
Header-File angeschaut und bin nicht drübergestolpert.

von Falk B. (falk)


Lesenswert?

@ Stringanwender (Gast)

>Wie soll da einer draufkommen

RTFM. Die Doku der libc ist dein Freund und Helfer.

von Stringanwender (Gast)


Lesenswert?

Falk B. schrieb:
> RTFM. Die Doku der libc ist dein Freund und Helfer.

Ja, ich weiss. Ich hätte eigentlich nicht fragen sollen
dürfen müssen können. Meine Frage war etwa so blöd wie:

"Wie setze ich beim ATMega8, Port B ein Bit auf 1"

'Tschuldigung dass ich meine Frage nicht selbst als
"blöde Frage" deklariert habe.

Stringanwender schrieb:
> Ich hab es mir vorher im Header-File angeschaut

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.