www.mikrocontroller.net

Forum: Compiler & IDEs Structure und PROGMEM


Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich versuche das speicherintensive Menü vom SRAM in den Flash zu 
bekommen. Jedoch scheitere ich an den vorhandenen structures, die sich 
scheinbar nicht in den Flash verschieben lassen wollen. Zumindest 
menu_entry_t, dass nach der Initialisierung konstant bleibt, könnte man 
ja in den Flash verschieben.

Hat jemand Tips wie ich das bewerkstelligen kann?

typedef struct menu_entry_s {
  uint8_t flags;                            
  void (*select)(uint8_t *value, char *name);  
  char name[MENU_ENTRY_NAMELEN];            
  void *value;                              
} PROGMEM menu_entry_t;

typedef struct menu_s {
  uint8_t top_entry;        
  uint8_t current_entry;    
  uint8_t num_entries;      
  struct menu_s *previous; 
  menu_entry_t entry[];
} menu_t;

menu_t Zeit = {
   .top_entry = 0,
   .current_entry = 0,
   .entry =  {
      {.flags = 0,
       .select = vEditTime,
       .name = "Uhrzeit",
       .value = 0,
      },
      {.flags = 0,
       .select = vEditDate,
       .name = "Datum",
       .value = 0,
      },
   },
   .num_entries = 2,
   .previous = NULL,
};

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: Das obige Beispiel zeigt mit bzw. ohne PROGMEM keine Änderung, 
wenn man die einzelnen Größen mit avr-size anzeigen lässt.

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hat wirklich niemand eine Idee?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gemäß Tutorial würde ich es so versuchen (habe derzeit keinen AVR-GCC 
zum selbst testen):
typedef struct menu_entry_s {
  uint8_t flags;                            
  void (*select)(uint8_t *value, char *name);  
  char name[MENU_ENTRY_NAMELEN];            
  void *value;                              
} /* PROGMEM */ menu_entry_t;       // <==

typedef struct menu_s {
  uint8_t top_entry;        
  uint8_t current_entry;    
  uint8_t num_entries;      
  struct menu_s *previous; 
  menu_entry_t entry[];
} menu_t;

menu_t Zeit = {
   .top_entry = 0,
   .current_entry = 0,
   .entry = PROGMEM {               // <==
      {.flags = 0,
       .select = vEditTime,
       .name = "Uhrzeit",
       .value = 0,
      },
      {.flags = 0,
       .select = vEditDate,
       .name = "Datum",
       .value = 0,
      },
   },
   .num_entries = 2,
   .previous = NULL,
};

(1) "GCC and the PROGMEM Attribute" von abcminiuser
http://www.avrfreaks.net/index.php?name=PNphpBB2&f...

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Vorschlag, hab diese Variante auch schon ausprobiert, 
jedoch kommen dabei einige Fehlermeldungen:

menu.c:92: error: syntax error before "__attribute__"
menu.c:94: error: field name not in record or union initializer
menu.c:94: error: (near initialization for `Zeit')
menu.c:94: warning: missing braces around initializer
menu.c:94: warning: (near initialization for `Zeit.entry')
menu.c:94: warning: initialization makes integer from pointer without a 
cast
menu.c:94: error: initializer element is not computable at load time
menu.c:94: error: (near initialization for `Zeit.entry[0].flags')
menu.c:95: warning: excess elements in struct initializer
menu.c:95: warning: (near initialization for `Zeit')
menu.c:95: error: unknown field `name' specified in initializer
menu.c:95: warning: excess elements in struct initializer
menu.c:95: warning: (near initialization for `Zeit')
menu.c:96: error: unknown field `value' specified in initializer
menu.c:96: warning: excess elements in struct initializer
menu.c:96: warning: (near initialization for `Zeit')
menu.c:98: error: syntax error before '{' token

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich komme nur durch Aufteilung der verschachtelten Struktur um das 
Problem herum:
#include <avr/io.h>
#include <avr/pgmspace.h>

typedef unsigned char uint8_t;

#define MENU_ENTRY_NAMELEN 42
#define NULL ((void *)0)
#define vEditTime ((void *)1234)
#define vEditDate ((void *)5678)

typedef struct menu_entry_s {
  uint8_t flags;
  void (*select)(uint8_t *value, char *name);
  char name[MENU_ENTRY_NAMELEN];
  void *value;
} PROGMEM menu_entry_t;

typedef struct menu_s {
  uint8_t top_entry;
  uint8_t current_entry;
  uint8_t num_entries;
  struct menu_s *previous;
  menu_entry_t *entry;             // <===
} menu_t;

menu_entry_t progmem_entry[] = {   // <===
      {.flags = 0,
       .select = vEditTime,
       .name = "Uhrzeit",
       .value = 0,
      },
      {.flags = 0,
       .select = vEditDate,
       .name = "Datum",
       .value = 0,
      },
   };

menu_t Zeit = {
   .top_entry = 0,
   .current_entry = 0,
   .entry = progmem_entry,         // <===
   .num_entries = 2,
   .previous = NULL,
};

int main(void)
{
  return 0;
}


Ich erkläre mir das so, dass im Ausgangsfall die im SRAM abgelegte 
Struktur gleichzeitig einen Teil haben soll, der im FLASH liegt - das 
ist ein Widerspruch in sich.

Was geht und hier gemacht wurde: die Struktur 1 liegt im FLASH und der 
Zeiger auf diese Struktur wird in die Struktur 2 im Flash eingebunden. 
Man "opfert" einen Zeiger (2 Bytes) pro Struktur 2

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
menu_entry_t bekommst du so ins Flash:
typedef struct
{
  uint8_t flags;                            
  void (*select)(uint8_t *value, char *name);  
  char name[10];            
  void *value;                              
} menu_entry_s;

menu_entry_s PROGMEM menu_entry_t;

Allerdings kommt dann noch die berechtigte Warnung:
../Main.c:12: warning: only initialized variables can be placed into 
program memory area


Also noch initialisieren:
#include <avr/io.h>
#include <avr/pgmspace.h>

void selectfunc(uint8_t *value, char *name);

typedef struct
{
  uint8_t flags;                            
  void (*select)(uint8_t *value, char *name);  
  char name[10];            
  void *value;                              
} menu_entry_s;

menu_entry_s PROGMEM menu_entry_t = {0xff, selectfunc, "hallo", 0};

void selectfunc(uint8_t *value, char *name)
{
}

Oliver

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Hilfe. Ich hab nun die obige Variante von Stefan mit der 
Trennung der beiden structs vorgenommen. Irgendwas mach ich aber noch 
immer beim Auslesen falsch, da ich weder 3 noch 4 angezeigt bekomme.
#include <avr/io.h>
#include <avr/pgmspace.h>

typedef unsigned char uint8_t;

#define MENU_ENTRY_NAMELEN 42
#define NULL ((void *)0)
#define vEditTime ((void *)1234)
#define vEditDate ((void *)5678)

typedef struct menu_entry_s {
  uint8_t flags;
  void (*select)(uint8_t *value, char *name);
  char name[MENU_ENTRY_NAMELEN];
  void *value;
} PROGMEM menu_entry_t;

typedef struct menu_s {
  uint8_t top_entry;
  uint8_t current_entry;
  uint8_t num_entries;
  struct menu_s *previous;
  menu_entry_t *entry;             
} menu_t;


menu_entry_t zeitsub[]= {
  {.flags = 3,                       //<== Dieser Wert soll ausgelesen werden
   .select = vEditTime,
   .name = "Uhrzeit",
   .value = 0,
  },
  {.flags = 4,
   .select = vEditDate,
   .name = "Datum",
   .value = 0,
  },
};

menu_t Zeit = {
   .top_entry = 0,
   .current_entry = 0,
   .entry = (menu_entry_t*)&zeitsub,
   .num_entries = 2,
   .previous = NULL,
};

typedef struct {
    menu_t *menu; 
} menu_context_t;

menu_context_t context = {
   .menu = NULL
};


int main(void)
{
     uint8_t flag=0;
  
     context.menu = &Zeit;

     flag = pgm_read_byte(&((menu_entry_t*)pgm_read_word(&context.menu->entry))[0].flags);

  return 0;
}



Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Irgendwas mach ich aber noch
>immer beim Auslesen falsch, da ich weder 3 noch 4 angezeigt bekomme.

Das PROGMEM gehört in die Variablendeklaration, nicht in die 
Typdefinition.

Also:
typedef struct menu_entry_s {
  uint8_t flags;
  void (*select)(uint8_t *value, char *name);
  char name[MENU_ENTRY_NAMELEN];
  void *value;
} menu_entry_t;

menu_entry_t PROGMEM zeitsub[]= {
  {.flags = 3,                
   .select = vEditTime,
   .name = "Uhrzeit",
   .value = 0,
  },
  {.flags = 4,
   .select = vEditDate,
   .name = "Datum",
   .value = 0,
  },
};

Oliver

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke, aber diese Änderung bewirkt bei mir leider gar nichts. Nach wie 
vor bekomme ich für die Variable flag einen falschen Wert

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: Nur zeitsub liegt im Flash. Mehr nicht. Daher:
flag = pgm_read_byte(&((context.menu->entry)[0].flags));
Oliver

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag 2:

PROGMEM geht doch in der Typdefinition. Na ja, hatte ich noch nie so 
probiert.

Oliver

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die schnelle Hilfe!

Hans

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So funktioniert's korrekt:

flag = pgm_read_byte(&(*(context.menu->entry))[0].flags);

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.