Forum: Compiler & IDEs PROGMEM initialisieren mit verschiedenen Typen


von Klaus M. (meinzinet)


Lesenswert?

Ich möchte parameter und Strings im Flash Speicher ablegen, die später 
von einer Funktione gelesen und abgearbeitet werden.

Das soll so auschauen

uint8_t _attribute_ ((progmem)) menu_main[] = {hier die 
parameter......}

Nun ist das etwas unübersichtlich wenn ich nur 8bity werte reinschreiben 
kann.
Ich möchte das gerne so eingeben können :

uint8_t _attribute_ ((progmem)) menu_main[] =
{(uint8_t) 120, (int) 13245, (long) -145120, "Bezeichnung", ....}

Ich kann aber auch kein struct mit den bentötigten typen definieren, 
weil nicht immer die selben datentypen benötigt werden.

Im Prinzip schaut es so aus:

typedef struct
{
...
} struct_a;

typedef struct
{
...
} struct_b;

typedef struct
{
...
} struct_c;

uint8_t _attribute_ ((progmem)) menu_main[] =
{
(uint8_t) datentyp1, ((struct_a) {....},
(uint8_t) datentyp2, ((struct_b) {....},
(uint8_t) datentyp1, ((struct_a) {....},
(uint8_t) datentyp3, ((struct_c) {....}
};

Der erste Parameter im Flash soll den nachfolgenden Datentyp 
spezifizieren.
Der nachfolgende Datentyp ist in Größe und Aufbau unterschiedlich. 
Danach folgt wieder ein Parameter der den nächsten Datentypen 
spezifiziert.


Wie kann man soetwas realisieren. Ich meine wie kann ich dem compiler 
mitteilen welchen Datentyp ich jetzt in uint8_t array umgewandelt haben 
möchte.


Schönen Dank für alle hilfreichen Antworten.
Klaus

von Falk B. (falk)


Lesenswert?

@Klaus Me (meinzinet)

>uint8_t attribute ((progmem)) menu_main[] =
>{(uint8_t) 120, (int) 13245, (long) -145120, "Bezeichnung", ....}

So macht man aber auch in "normalem" C keine Structs.

>Ich kann aber auch kein struct mit den bentötigten typen definieren,
>weil nicht immer die selben datentypen benötigt werden.

Doch, kann und muss man. Und dann kann man auch relativ leicht die ganze 
Sache definieren, das Attribut PROGMEM sorgt für die Platzierung im 
Flash.
Was du hier machen willst ist ein über Hackertrick auf Assemblerebene, 
nicht schön und nicht sehr praktikabel.

MFG
Falk

von Klaus M. (meinzinet)


Lesenswert?

Wie man structs macht weiss ich schon.

Ich wollte eigentlich wissen wie ich die Initialisierung des arrays 
schreiben muss , so dass ich zB. ein integer eigeben kann und der 
compiler automatisch 2 Bytes daraus macht.
1
>uint8_t attribute ((progmem)) menu_main[] =
2
>{(uint8_t) 120, (int) 13245, (long) -145120, "Bezeichnung", ....}

was hier in Klammern geschrieben ist, soll den Datentyp darstellen, den 
ich eingeben will. Normalerweise müsste ich ja eine Reihe von Bytes 
eingeben.

von P. S. (Gast)


Lesenswert?

Mir ist immer noch nicht klar, warum du keine structs nehmen willst. 
Vieleicht solltest du nochmal klar formulieren, was du am Ende erreichen 
willst und die voreiligen Halbschluesse weglassen...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Klaus Me wrote:
> Wie man structs macht weiss ich schon.
>
> Ich wollte eigentlich wissen wie ich die Initialisierung des arrays
> schreiben muss , so dass ich zB. ein integer eigeben kann und der
> compiler automatisch 2 Bytes daraus macht.
>
1
>>uint8_t attribute ((progmem)) menu_main[] =
2
>>{(uint8_t) 120, (int) 13245, (long) -145120, "Bezeichnung", ....}
3
>

Du sagst aber doch, daß alles vom Typ uibt8_t sein soll! Also du musst 
dich schon entscheiden, was du willst...

In deinem Falle vielleicht
1
typedef struct
2
{
3
   uint8_t id;
4
   int ival;
5
   long lval;
6
   const char label[]; // ist was anderes als chost char * label !!
7
} item_t ; 
8
9
const item_t items[] PROGMEM = 
10
{
11
    ...
12
};

Johann

von Klaus M. (meinzinet)


Lesenswert?

Hallo Johann

Ich glaub war wohl zu schnell und ungenau definiert was ich machen will.
1
typedef struct
2
{
3
  uint8_t posx;
4
  uint8_t posy;
5
  uint8_t format;
6
  char    bezeichnung[20];
7
} display_text_t;
8
9
typedef struct
10
{
11
  uint8_t posx;
12
  uint8_t posy;
13
  uint8_t format;
14
  long    zahl;
15
} display_long_t;
16
17
typedef struct
18
{
19
  uint8_t posx1;
20
  uint8_t posy1;
21
  uint8_t posx2;
22
  uint8_t posy2;
23
} display_rechteck_t;
24
25
26
display_text_t __attribute__ ((progmem)) pgm_text= {0,0,CENTER_VIEW,"der text soll angezeigt werden"};
27
display_long_t __attribute__ ((progmem)) pgm_long = {0,0,CENTER_VIEW,9960435};
28
display_rechteck_t __attribute__ ((progmem)) pgm_rechteck = {0,0,100,100};
So und die die funktionierende Variante:
1
int main(void)
2
{
3
  ....
4
5
  display_text_P(&pgm_text);    // Funktion zeigt den Text am Display an 
6
  display_long_P(&pgm_long);  // Funktion stellt die Zahl  am Display an 
7
  display_rechteck_P(&pgm_rechteck); // Funktion zeichnet ein Rechteck am Display 
8
}
Nun will ich das aber so machen
1
uint8_t __attribute__ ((progmem))  pgm_display = {??????????}; // Hier müssten die Daten stehen die dann die selbe Anzeige ergeben wie im ersten Beispiel
2
3
void display( const char * pgm_ptr)
4
{
5
 while (1==1)
6
 {
7
  uint8_t display_typ= pgm_read_byte(pgm_ptr++);
8
  switch (display_typ)
9
  {
10
    case 1 : 
11
      display_text_P(pgm_ptr);
12
      pgm_ptr+= sizeof(display_text_t);
13
    break;
14
15
    case 2 :
16
     display_long_P(pgm_ptr);
17
     pgm_ptr+=sizeof(display_long_t);
18
    break;
19
20
    case 3 :
21
     display_rechteck_P(pgm_ptr);
22
     pgm_ptr+=sizeof(display_rechteck_t);
23
    break;
24
25
   case 99 : return; // kein weiteres element zum Anzeigen
26
  }
27
 }  
28
}
29
30
int main(void)
31
{
32
.....
33
34
display(&pgm_display);
35
}
Nun meine Frage : wie kann ich pgm_display die Daten zuweisen, dass auch 
die selbe Anzeige am Display erscheint.

Nun eine Möglichkeit wäre alles in 8bit Zahlen umrechnen und 
einzutragen.
Verständlichweise suche ich nach einer Möglichkeit dies sichtbarer und 
komfortabler zu machen.

von Falk B. (falk)


Lesenswert?

@Nun meine Frage : wie kann ich pgm_display die Daten zuweisen, dass 
auch
>die selbe Anzeige am Display erscheint.

1. Structs sind die saubere und einfache Lösung.

2. Um verschiedene Structs an ein Funktion zu übergeben kann man einen 
Cast bzw. void Pointer nutzen. Nicht schön, aber halbwegs konform. Über 
den Zugriff auf das erste Byte, kann man dann in der Funktion einen 
neuen Cast auf den richtigen Struct machen und komfortabel auf die Daten 
in C Manier zugreifen. Keinerlei notwenigikeit, alles zu Fuss 
zusammenzusetzen.

Richtig sauber wird es erst durch nutzung verketteter Structs, sprich es 
gibt eine Art Header-Struct, welches IMMER gleich ist. Dort sind dann 
die Pointer auf die unterschiedlichgen Nutzdaten drin.

>Nun eine Möglichkeit wäre alles in 8bit Zahlen umrechnen und
>einzutragen.

Völliger Blödsinn. Dann kannst du auch gleich den C-Compiler 
wegschmeissen. Du denkst noch viel zu Bastlermässig.

>Verständlichweise suche ich nach einer Möglichkeit dies sichtbarer und
>komfortabler zu machen.

Siehe oben.

MfG
Falk

P.S. Und nutze doch einfch die fertigen Defines ala PROGMEM, solche 
Sachen wie _atribute_ will ich als normaler Programmierer nicht sehen. 
Das ist was für die Kompilerbauer.
1
display_text_t pgm_text PROGMEM = {0,0,CENTER_VIEW,"der text soll angezeigt werden"};

MFG
Falk

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Klaus Me wrote:
> Hallo Johann
>
> Ich glaub war wohl zu schnell und ungenau definiert was ich machen will.
> [...]
> Zentnerweise C-Code
> [...]

> Nun eine Möglichkeit wäre alles in 8bit Zahlen umrechnen und
> einzutragen.
> Verständlichweise suche ich nach einer Möglichkeit dies sichtbarer und
> komfortabler zu machen.

Eigentlich steht's oben geschrieben

1)  Alle Datensätze sehen gleich aus: Definier dir eine Struktur
2)  Die Datensätze sehen nicht gleich aus
2a) Definier dir jeweils Strukturen, die du in eine Union packst.
    In einer id merkst du, wie das Zeug zu interpretieren ist.
    Nachteil: Es geht Platz verloren, wenn nicht alle Sichten
    gleich groß sind, was insbesondere bei char[20] und vielen
    Einträgen übel aufstößt.
2b) Definier jeweils Strukturen, die nacheinander die Komponenten
    einer großen Bandwurm-Struktur bilden.
    Nachteil: schlechter zu pflegen, die interpretation ist implizit.
    Dafür kleinerer Code. Bei vielen Einträgen ist zu überlegen, den
    Code dafür auf dem Host per Code-Generator erzeugen zu lassen.
3)  Schreib ein Assembler-Modul, wo du die Daten per .byte, .word etc
    reinhackst. Dann meckert kein Compiler. Alternativ in ein globales
    asm("") schreiben.

von Klaus W. (mfgkw)


Lesenswert?

Die Version 2a von Johann L. ausformuliert sieht etwa so aus:
1
typedef struct
2
{
3
  uint8_t posx;
4
  uint8_t posy;
5
  uint8_t format;
6
  char    bezeichnung[20];
7
} display_text_t;
8
9
typedef struct
10
{
11
  uint8_t posx;
12
  uint8_t posy;
13
  uint8_t format;
14
  long    zahl;
15
} display_long_t;
16
17
typedef struct
18
{
19
  uint8_t posx1;
20
  uint8_t posy1;
21
  uint8_t posx2;
22
  uint8_t posy2;
23
} display_rechteck_t;
24
25
26
typedef union
27
{
28
  display_text_t      u_text;
29
  display_long_t      u_long;
30
  display_rechteck_t  u_rechteck;
31
} display_irgendwas_t;
32
33
typedef enum
34
{
35
  eDisplayTypeText,
36
  eDisplayTypeLong,
37
  eDisplayTypeRechteck,
38
} eDisplayType;
39
40
struct { uint8_t eDisplayType, display_irgendwas_t display } pgm_display[] =
41
  {
42
    { eDisplayTypeText,     { .u_text     = { 12, 13, 2, "erster text" } } },
43
    { eDisplayTypeLong,     { .u_long     = { 13, 14, 1, 1234 } } },
44
    { eDisplayTypeRechteck, { .u_rechteck = { 15, 16, 18, 19 } } },
45
    { eDisplayTypeText,     { .u_text     = { 12, 13, 2, "zweiter text" } } },
46
  };

Wie schon erwähnt hat dies den Nachteil, daß alle Einträge gleich groß
sind, nämlich so groß wie das grösste Element der union.
Dementsprechend muss man beim Auslesen in der Schleife seinen Zeiger
NICHT um sizeof(display_long_t) oder sizeof(display_rechteck_t)
weiter setzen, sondern immer um sizeof(display_irgendwas_t), egal
welchen Typ man ausliest - wenn man es unbedingt über ein char*
hinmurksen will.

(Probiert mit gcc 4.3.2; ältere mögen vielleicht die Initialisierung
der Art .u_text=... nicht)

von Klaus M. (meinzinet)


Lesenswert?

Hallo Klaus

vielen dank für den Code vorschlag.
Werde es so ähnlic machen, nur eben so dass alle unions gleich groß 
sind.

struct { uint8_t eDisplayType, display_irgendwas_t display } 
pgm_display[] =
  {
    { eDisplayTypeText,     { .u_text     = { 12, 13, 2, "erster text" } 
} },
    { eDisplayTypeLong,     { .u_long     = { 13, 14, 1, 1234 } } },
    { eDisplayTypeRechteck, { .u_rechteck = { 15, 16, 18, 19 } } },
    { eDisplayTypeText,     { .u_text     = { 12, 13, 2, "zweiter text" 
} } },
  };

Das mit dem Initialisieren hat mir noch gefeht. Danke

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Klaus Wachtler wrote:
> (Probiert mit gcc 4.3.2; ältere mögen vielleicht die Initialisierung
> der Art .u_text=... nicht)

Das hat nix mit der gcc-Version zu tun, sondern mit dem verwendeten 
C-Standard.

Übliche Praxis ist, eine Id in die Union aufzunehmen, die allen 
Komponenten gemein ist und durch sie überlagert wird. Dadurch kann auf 
die Id zugegriffen werden, ohne eine bestimmte Sicht anzusprechend (die 
man ja noch garnicht weiß zu dem Zeitpunkt)
1
enum
2
{
3
   ITEMID_TEXT,
4
   ITEMID_LONG,
5
   ITEMID_RECT,
6
7
   ITEM_NUM_IDS
8
};
9
10
typedef struct
11
{
12
   // common
13
   uint8_t id;
14
   // item specific
15
   uint8_t posx;
16
   uint8_t posy;
17
   uint8_t format;
18
   char    label[20]; // Evtl ist hier ein char* besser (Platzverbrauch!)
19
} display_text_t;
20
21
...
22
23
typedef union
24
{
25
   uint8_t id;
26
   display_long_t    asLong;
27
   display_text_t    asText;
28
} display_item_t;
29
30
...
31
32
const display_item_t items[] PROGMEM = 
33
{
34
    [0] = { .id = ITEMIT_TEXT, .posx = ... },
35
    [1] = { ...
36
};
37
38
void foo (display_item_t * item)
39
{
40
    if (ITEMID_TEXT == item->id)
41
    {
42
        // Aha, ein Text
43
        // .id ist .text.id
44
        printf (item->asText.label);
45
    }
46
}

Johann

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.