Forum: Compiler & IDEs PROGMEM auslesen


von Darki (Gast)


Lesenswert?

Hi,
ich bin mittlerweile am verzweifeln mit PROGMEM. Ich würde gerne eine 
Menüstruktur auf einem LCD anzeigen. Ich hab schon einiges gesucht und 
probiert, aber irgendwie will das alles nicht so wie ich.
Es geht mir hier nicht drum etwas fertiges zu nutzen von wem anders und 
es nicht zu verstehen, sondern es selbst zu machen und zu verstehen wie 
das funktioniert! Leider bisher ohne erfolg.
Ich habe folgende Struktur:
1
#define MenuPoint0 "Einstellungen"
2
#define MenuPoint0_1 "Uhrzeit"
3
#define MenuPoint0_2 "Dauer"
4
#define MenuPoint0_3 "Weckzeit"
5
#define MenuPoint0_3_1 "Uhrzeit"
6
#define MenuPoint0_3_2 "Fadingzeit"
7
#define MenuPoint0_4 "Testeintrag 0_4"
8
9
typedef struct def_MenuStruktur
10
{
11
  const char *text;
12
  uint8_t next;
13
  uint8_t previous;
14
  uint8_t sub;
15
  uint8_t up;
16
  void (*funktion)(void);
17
} MenuStruktur;
18
19
MenuStruktur PROGMEM Menu[] =
20
{
21
  {MenuPoint0, 7, 10, 1, 255, },    //0
22
  {MenuPoint0_1, 2, 6, 255, 0, },    //1
23
  {MenuPoint0_2, 3, 1, 255, 0, },   //2
24
  {MenuPoint0_3, 6, 2, 4, 0, },    //3
25
  {MenuPoint0_3_1, 5, 5, 255, 3, },  //4
26
  {MenuPoint0_3_2, 4, 4, 255, 3, },  //5
27
  {MenuPoint0_4, 1, 3, 255, 0, },    //6
28
  {MenuPoint1, 10, 0, 8, 255, },    //7
29
  {MenuPoint1_1, 9, 9, 255, 7, },    //8
30
  {MenuPoint1_2, 8, 8, 255, 7, },    //9
31
  {MenuPoint2, 0, 7, 11, 255, },    //10
32
  {MenuPoint2_1, 12, 12, 255, 10, },  //11
33
  {MenuPoint2_2, 11, 11, 255, 10, },  //12
34
};

Diesen struct würde ich jetzt gerne in etwa so auslesen:
1
char Output[4][20];
2
uint8_t MenuItemCount = 0;
3
4
strcpy(Output[0], Menu[MenuItemCount].text);
Also den Wert (hier z.B. ".text") in ein Array kopieren. Das 
funktioniert soweit OHNE PROGMEM. Kommt jetzt aber das PROGMEM dazu, 
geht das nichtmehr, was ja verständlich ist.
Also hab ich das versucht:
1
strcpy_P(Output[0], (char*)pgm_read_word(&Menu[MenuItemCount].text));
Was aber auch nicht den erhofften erfolg brachte. Ich hab noch einiges 
anderes probiert, wie statt (char*) mit (PGM_P) etc... aber alles ohne 
Erfolg. Compiler-Error bekomm ich keine. Wie bekomm ich das also hin?
Danke schonmal im vorraus!

von Sam P. (Gast)


Lesenswert?

const char *text in deiner struct zeigt wieder auf RAM. Deine 
Stringkonstanten musst du separat ins progmem verlegen.

von Karl H. (kbuchegg)


Lesenswert?

1
MenuStruktur PROGMEM Menu[] =
2
{
3
  {MenuPoint0, 7, 10, 1, 255, },    //0
4
5
  ...


Der jeweilige Menüpunkt liegt zwar im Flash.
Aber der Menüpunkt enthält einen Zeiger auf den Text. Und dieser Text 
liegt nicht im Flash. Daher ist strcpy_P hier die falsche Funktion.

von Oliver (Gast)


Lesenswert?

strcpy_P erwartet einen Pointer ins Flash als Parameter, da musst du 
nicht noch selber mit pgm_read rumwursteln.

Oliver

von Hmm (Gast)


Lesenswert?

Aber mein Guter!
Du kannst doch nicht einfach irgendeine Funktion darein klatschen nur 
weil "pgm" im Namen vorkommt und dann annehmen, dass das dann genauso 
wie im RAM funktionieren wird.
Lies Dir mal genau durch was pgm_read_word macht und falls dann 
notwendig stelle die nächste Frage.

von Hans (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Und dieser Text
> liegt nicht im Flash.

Verstehe ich nicht.

#define MenuPoint0 "Einstellungen"

ist doch keine Variable. Der String sollte doch dann im Flash liegen, 
oder nicht?

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:
> Karl Heinz Buchegger schrieb:
>> Und dieser Text
>> liegt nicht im Flash.
>
> Verstehe ich nicht.
>
> #define MenuPoint0 "Einstellungen"
>
> ist doch keine Variable. Der String sollte doch dann im Flash liegen,
> oder nicht?

Warum sollte der im Flash liegen?
Solange du nichts anderes anordnest, kommt der String ins SRAM

Und hier
1
typedef struct def_MenuStruktur
2
{
3
  const char *text;

vereinbarst du nur, dass in einer Menüstruktur ein Pointer steht. Der 
Pointer selber ist natürlich dann im Flash. Aber wo er hinzeigt nicht.



Du baust das hier
1
    Menu
2
    +-------------------+
3
    | text: o-----------------------------> "Einstellungen"
4
    | next: 7           |
5
    | previous: 10      |
6
    | .....             |
7
    |                   |
8
    +-------------------+
9
    | text: o-----------------------> "Uhrzeit"
10
    | next: 2           |
11
    | previous: 6       |
12
    | ......            |
13
    |                   | 
14
    +-------------------+
15
    | text: o--------------

der linke Teil (das Array) liegt durch das PROGMEM im Flash. Soweit 
richtig. Aber die Pointer führen ins SRAM. Du hast nicht dafür gesorgt, 
dass auch die Texte im Flash liegen.

von Hmm (Gast)


Lesenswert?

>Verstehe ich nicht.

>#define MenuPoint0 "Einstellungen"

>ist doch keine Variable. Der String sollte doch dann im Flash liegen,
>oder nicht?

Was hat das damit zu tun? Wie das Literal aussieht hat doch keine 
Auswirkung darauf wohin es gespeichert wird.
Dafür ist die Deklaration und Definition zuständig.

In deinem Strukt steht,

...
   const char *
...

Ergo liegt zwar der Zeiger im PGM-Speicher aber nicht das worauf der 
Zeiger zeigt.

von Hans (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Warum sollte der im Flash liegen?
> Solange du nichts anderes anordnest, kommt der String ins SRAM

#define MenuPoint0 "Einstellungen"

der Define ist doch nur ein Makro. Zur Compile-Zeit wird doch einfach 
(in diesem Fall) "Einstellung" durch MenuPoint0 ersetzt.

Also hier: würde dann stehen

MenuStruktur PROGMEM Menu[] =
{
  {"Einstellung", 7, 10, 1, 255, },    //0


....

jetzt bin ich aber wirklich verwirrt. Ich dachte immer die 
Makro-Expansion ist ein Einsetzen für einen Alias.

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:
> Karl Heinz Buchegger schrieb:
>> Warum sollte der im Flash liegen?
>> Solange du nichts anderes anordnest, kommt der String ins SRAM
>
> #define MenuPoint0 "Einstellungen"
>
> der Define ist doch nur ein Makro.

Der #define ist völlig uninteressant.


Du hast ein STring-Literal. Und solange du nichts dagegen unternimmst, 
landet das im SRAM.

Du hättest genauso schreiben können

> MenuStruktur PROGMEM Menu[] =
> {
>   {"Einstellung", 7, 10, 1, 255, },    //0


genau das.
Das #define ändert daran nichts. Das ist nur syntaktischer Zucker, so 
dass du den eigentlichen Test schreibweisenmässig im Code an einer 
anderen Stelle hast. Aber es ist und bleibt ein Stringliteral.

von Karl H. (kbuchegg)


Lesenswert?

> Ich dachte immer die Makro-Expansion ist ein Einsetzen für einen Alias.

Oho.
C-Buch

ein Makro ist nichts anderes als eine Textersetzung. Nicht mehr und 
nicht weniger.
Im Grunde macht ein Makro nichts anderes als es auch dein Editor macht, 
wenn du dir mittels Suchen&Ersetzen einen Text durch einen anderen Text 
ersetzen lässt.

Ok, die Details sind ein wenig komplexer (aber nicht viel), aber im 
Grunde ist das genau das, was ein Makro macht: es ersetzt einen Text 
durch einen anderen Text.
Und zwar bevor der eigentliche C-Compiler den Quellcode zu Gesicht 
bekommt!

von Hans (Gast)


Lesenswert?

@Karl-Heinz

Ich bin blöd. Alles klar

von Karl H. (kbuchegg)


Lesenswert?

> Also hier: würde dann stehen
>
> MenuStruktur PROGMEM Menu[] =
> {
>   {"Einstellung", 7, 10, 1, 255, },    //0

Ja, und?

Deine Strukturdefinition sagt
1
typedef struct def_MenuStruktur
2
{
3
  const char *text;
4
  uint8_t next;
5
...

text ist nur ein Pointer. Dort steht die Adresse drinnen, an der der 
Text zu finden ist. Und der (der Pointer) steht im Flash, weil auch 
die komplette Struktur im Flash steht.
Aber das sagt nichts darüber aus, dass auch der Text im Flash zu finden 
sein wird.

von Hans (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Deine Strukturdefinition sagt
> typedef struct def_MenuStruktur
> {
>   const char *text;
>   uint8_t next;
> ...
>
> text ist nur ein Pointer. Dort steht die Adresse drinnen, an der der
> Text zu finden ist. Und der (der Pointer) steht im Flash, weil auch
> die komplette Struktur im Flash steht.
> Aber das sagt nichts darüber aus, dass auch der Text im Flash zu finden
> sein wird.

Deshalb habe ich ja gerade geschrieben. Das ich blöd bin. Ich habe 
darauf nicht geachtet. Ich will mich jetzt nicht mehr weiter aus dem 
Fenster lehnen. Aber ich vermute, dass der TO den Struct in PROGMEN 
packen muss. Egal. Ich halte mich hier jetzt zurück.

von Hmm (Gast)


Lesenswert?

>Aber ich vermute, dass der TO den Struct in PROGMEN
>packen muss. Egal.

Ja, Hans. Das muss der TO tun. Oder auch nicht. Jedenfalls hat er.
Ich reiche mal Karl-Heinz ein Taschentuch.

von Darki (Gast)


Lesenswert?

Danke, der Hinweis mit den Strings die im SRAM statt im Flash liegen hat 
geholfen!
Habs jetzt so gemacht:
1
const char PROGMEM MenuPoint0[]  = "Einstellungen";
2
const char PROGMEM MenuPoint0_1[]  = "Uhrzeit";
3
const char PROGMEM MenuPoint0_2[] = "Dauer";
4
const char PROGMEM MenuPoint0_3[] = "Weckzeit";
5
const char PROGMEM MenuPoint0_3_1[] = "Uhrzeit";
6
const char PROGMEM MenuPoint0_3_2[] = "Fadingzeit";
7
const char PROGMEM MenuPoint0_4[] = "Testeintrag 0_4";
8
9
const char PROGMEM MenuPoint1[] = "Fading";
10
const char PROGMEM MenuPoint1_1[] = "RGB";
11
const char PROGMEM MenuPoint1_2[] = "WW";
12
13
const char PROGMEM MenuPoint2[] = "Sleep Light";
14
const char PROGMEM MenuPoint2_1[] = "Zeit";
15
const char PROGMEM MenuPoint2_2[] = "Farbe";

Und dann aufgerufen mit:
1
strcpy_P(Output[0], (char*)pgm_read_word(&Menu[MenuItemCount].text));
Funktioniert genauso wie ichs mir vorgestellt hab!

@Oliver: Mit "strcpy_P(Output[0], Menu[MenuItemCount].text);" geht es 
nicht, muss also wohl doch pgm_read_xxx davor.


Aufjedenfall danke an alle!

von Darki (Gast)


Lesenswert?

Ein Stolperstein gibts jetzt noch...

wie rufe ich die Funkion ("void (*funktion)(void);") auf?

Hab einfach mal zum testen folgendes versucht:
1
void time(void)
2
{
3
  PORTB = (1<<PB0);
4
}
5
6
MenuStruktur PROGMEM Menu[] =
7
{
8
...
9
  {MenuPoint0_1, 2, 6, 255, 0, time},    //1
10
...
11
};
12
13
14
if (BtnEnS)    //Button Enter Short
15
{
16
  pgm_read_word(&Menu[MenuItemCount].funktion);
17
}
Was aber natürlich nicht zum erfolg geführt hat, währe zu einfach 
gewesen ;)
Ideen?

von Karl H. (kbuchegg)


Lesenswert?

Darki schrieb:

> wie rufe ich die Funkion ("void (*funktion)(void);") auf?

So wie immer.
Du brauchst entweder den Namen der Funktion oder (was im Grunde 
eigentlich gleichwertig ist) deren Startadresse und ein () verursacht 
dann den Aufruf der Funktion.

(Mal sehen ob du 2 und 2 zusammenzählen kannst)

   time();
   |  |||
   |  |++
   |  | |
   +--+ |
    |   |
   Dieser 'Ausdruck' ist der Name einer Funktion. Und wie bei Arrays
   steht der Name einer Funktion für deren Startadresse.
        |
        |
      Und dieses () besagt, dass die Funktion aufgerufen werden soll,
      deren Startadresse mit dem vorhergehenden Ausdruck festgelegt
      wurde.


:-) Der Schlüssel liegt darin, dass man () als eine 'Operation' 
auffassen kann, die auf die Startadresse einer Funktion angewendet 
werden kann und zum Aufruf der Funktion führt.

Ob du nun die Startadresse der Funktion durch Nennung des Namens 
spezifizierst, oder ob diese Adresse aus einer Variablen kommt, .....

Nur muss im Falle, dass die Startadresse aus einer Variablen kommt, 
diese Variable dann natürlich auch vom Typ her ein Funktionspointer 
sein. Und an dieser Stelle kommt dann auch mein Rat für jeden ins Spiel, 
der mit Funktionspointern hantiert: Du sparst dir eine Menge 
Schreibaufwand, wenn du dir erst mal einen typedef für den Datentyp des 
Funktionspointers machst, anstatt dauernd ständig immer wieder die 
ausführliche Deklaration/Definition eines Funktionspointers zu 
schreiben. Denn aus pgm_read_word kommt ja erst mal nicht dieser 
Datentyp raus. Numerisch ist das zwar schon richtig, aber der Datentyp 
ist noch falsch ....

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Darki schrieb:
> Ein Stolperstein gibts jetzt noch...
>
> pgm_read_word(&Menu[MenuItemCount].funktion);
 
1
if (BtnEnS)    //Button Enter Short
2
{
3
    ((void(*)(void)) pgm_read_word(&Menu[MenuItemCount].funktion))();
4
}
 
Gleichwertig und lesbarer geht's auch mit einem Typedef und Cast auf den 
entsprechenden Prototypen.

Oder du verwendest __flash mitd er aktuellen Compiler-Version:
1
if (BtnEnS)    //Button Enter Short
2
{
3
    Menu[MenuItemCount].funktion();
4
}

von Darki (Gast)


Lesenswert?

@Karl Heinz:
Hab mal ein wenig damit gespielt:
1
void (*FuncPtr)(void);
2
3
FuncPtr = pgm_read_word(&Menu[MenuItemCount].funktion);
4
FuncPtr();
Das wollte der Compiler aber nicht.
Also der Versuch war die Adresse von .funktion in meinen FuncPtr zu 
übergeben. Ging aber schief... vllt liegt auch an der Uhrzeit ;)


@Johann:
Danke, das mit ((void(*)(void)) funktioniert.
Hab mal nach "__flash" gegoogelt, aber da kam nix bei rum. Hast du einen 
Link?

von Karl H. (kbuchegg)


Lesenswert?

Darki schrieb:
> @Karl Heinz:
> Hab mal ein wenig damit gespielt:
>
1
> void (*FuncPtr)(void);
2
> 
3
> FuncPtr = pgm_read_word(&Menu[MenuItemCount].funktion);
4
> FuncPtr();
5
>
> Das wollte der Compiler aber nicht.

Genauer.

WAS wollte der Compiler nicht?
Der wirft ja nicht einfach eine Fehlermeldung aus "Ich will nicht"

> Also der Versuch war die Adresse von .funktion in meinen FuncPtr zu
> übergeben. Ging aber schief... vllt liegt auch an der Uhrzeit ;)

Du musst anfangen zu realisieren, dass bei einer Zuweisung nicht nur 
Zahlenwerte im Spiel sind sondern auch Datentypen. Der Ausdruck rechts 
vom = ergibt einen Datentyp. Die Zuweisung erfolgt an eine Variable, die 
auch einen Datentyp hat.

  FuncPtr = pgm_read_word( ...

Welcher Datentyp steht links vom = ?
  Da steht ein Funktionspointer
Welchen Datentyp ergibt der Ausdruck rechts vom = ?
  Da kommt ein word raus (also ein uint16_t)

Sind die beiden Datentypen zuweisungskompatibel?
  Nein, sind sie nicht. Die Datentypen passen nicht zusammen. Ein
  uint16_t ist erst mal kein Pointer. Selbst wenn der Zahlenwert stimmt.

Wenn wir wissen, dass der Zahlenwert korrekt ist, können wir den
Compiler darin überstimmen, dass die Datentypen nicht zusammenpassen?
  Ja, natürlich. Man kann den Compiler immer überstimmen. Mit einem
  Cast

1
typedef void (*FuncPtr)(void);
2
3
FuncPtr Function;
4
5
Function = (FuncPtr) pgm_read_word(&Menu[MenuItemCount].funktion);
6
Function();

oder auch in einem Schritt ohne die Zwischenvariable.
1
typedef void (*FuncPtr)(void);
2
3
  ((FuncPtr) pgm_read_word(&Menu[MenuItemCount].funktion))();


> @Johann:
> Danke, das mit ((void(*)(void)) funktioniert.
Genau das wollte ich eigentlich vermeiden, dass du einfach nur 
abschreibst ohne zu wissen, was du da eigentlich warum tust.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Darki schrieb:

> Hab mal nach "__flash" gegoogelt, aber da kam nix bei rum.
> Hast du einen Link?

http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Named-Address-Spaces.html

von Darki (Gast)


Lesenswert?

Erstmal danke nochmal für die hilfe!

Mein Problem ist immer der Pointer kram. Ich versteh zwar im grunde was 
da passiert, aber ich steh immer total auf Kriegsfuß damit. Die 
Grundlagen sind ja schön und gut, aber die Umsetzung in einem Konkreten 
fall ist schwierig.
Daher zauder ich auch noch mit einem C Buch. Ich bin kein blutiger 
Anfänger, aber es fehlt hier und da noch an Grundlagen. Oder an 
Übersicht was grade passiert.
Hab schon paarmal versucht ein C Buch anzufangen zu lesen, aber schnell 
langweil ich mich weil der Anfang bekannt ist und in späteren Kapiteln 
fehlt dann Wissen von davor.
Es fehlt mir hier wohl an Ausdauer...

Danke aufjedenfall nochmals!

PS: Und wenn du ein Tip für nen C-Buch hast, was nicht ewig in Schleifen 
etc rumeiert und auf µC bezogen ist, immer her damit!

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.