Forum: Compiler & IDEs Structure und PROGMEM


von Hans (Gast)


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?
1
typedef struct menu_entry_s {
2
  uint8_t flags;                            
3
  void (*select)(uint8_t *value, char *name);  
4
  char name[MENU_ENTRY_NAMELEN];            
5
  void *value;                              
6
} PROGMEM menu_entry_t;
7
8
typedef struct menu_s {
9
  uint8_t top_entry;        
10
  uint8_t current_entry;    
11
  uint8_t num_entries;      
12
  struct menu_s *previous; 
13
  menu_entry_t entry[];
14
} menu_t;
15
16
menu_t Zeit = {
17
   .top_entry = 0,
18
   .current_entry = 0,
19
   .entry =  {
20
      {.flags = 0,
21
       .select = vEditTime,
22
       .name = "Uhrzeit",
23
       .value = 0,
24
      },
25
      {.flags = 0,
26
       .select = vEditDate,
27
       .name = "Datum",
28
       .value = 0,
29
      },
30
   },
31
   .num_entries = 2,
32
   .previous = NULL,
33
};

von Hans (Gast)


Lesenswert?

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

von Hans (Gast)


Lesenswert?

Hat wirklich niemand eine Idee?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Gemäß Tutorial würde ich es so versuchen (habe derzeit keinen AVR-GCC 
zum selbst testen):
1
typedef struct menu_entry_s {
2
  uint8_t flags;                            
3
  void (*select)(uint8_t *value, char *name);  
4
  char name[MENU_ENTRY_NAMELEN];            
5
  void *value;                              
6
} /* PROGMEM */ menu_entry_t;       // <==
7
8
typedef struct menu_s {
9
  uint8_t top_entry;        
10
  uint8_t current_entry;    
11
  uint8_t num_entries;      
12
  struct menu_s *previous; 
13
  menu_entry_t entry[];
14
} menu_t;
15
16
menu_t Zeit = {
17
   .top_entry = 0,
18
   .current_entry = 0,
19
   .entry = PROGMEM {               // <==
20
      {.flags = 0,
21
       .select = vEditTime,
22
       .name = "Uhrzeit",
23
       .value = 0,
24
      },
25
      {.flags = 0,
26
       .select = vEditDate,
27
       .name = "Datum",
28
       .value = 0,
29
      },
30
   },
31
   .num_entries = 2,
32
   .previous = NULL,
33
};

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

von Hans (Gast)


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

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ich komme nur durch Aufteilung der verschachtelten Struktur um das 
Problem herum:
1
#include <avr/io.h>
2
#include <avr/pgmspace.h>
3
4
typedef unsigned char uint8_t;
5
6
#define MENU_ENTRY_NAMELEN 42
7
#define NULL ((void *)0)
8
#define vEditTime ((void *)1234)
9
#define vEditDate ((void *)5678)
10
11
typedef struct menu_entry_s {
12
  uint8_t flags;
13
  void (*select)(uint8_t *value, char *name);
14
  char name[MENU_ENTRY_NAMELEN];
15
  void *value;
16
} PROGMEM menu_entry_t;
17
18
typedef struct menu_s {
19
  uint8_t top_entry;
20
  uint8_t current_entry;
21
  uint8_t num_entries;
22
  struct menu_s *previous;
23
  menu_entry_t *entry;             // <===
24
} menu_t;
25
26
menu_entry_t progmem_entry[] = {   // <===
27
      {.flags = 0,
28
       .select = vEditTime,
29
       .name = "Uhrzeit",
30
       .value = 0,
31
      },
32
      {.flags = 0,
33
       .select = vEditDate,
34
       .name = "Datum",
35
       .value = 0,
36
      },
37
   };
38
39
menu_t Zeit = {
40
   .top_entry = 0,
41
   .current_entry = 0,
42
   .entry = progmem_entry,         // <===
43
   .num_entries = 2,
44
   .previous = NULL,
45
};
46
47
int main(void)
48
{
49
  return 0;
50
}

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

von Oliver (Gast)


Lesenswert?

menu_entry_t bekommst du so ins Flash:
1
typedef struct
2
{
3
  uint8_t flags;                            
4
  void (*select)(uint8_t *value, char *name);  
5
  char name[10];            
6
  void *value;                              
7
} menu_entry_s;
8
9
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:
1
#include <avr/io.h>
2
#include <avr/pgmspace.h>
3
4
void selectfunc(uint8_t *value, char *name);
5
6
typedef struct
7
{
8
  uint8_t flags;                            
9
  void (*select)(uint8_t *value, char *name);  
10
  char name[10];            
11
  void *value;                              
12
} menu_entry_s;
13
14
menu_entry_s PROGMEM menu_entry_t = {0xff, selectfunc, "hallo", 0};
15
16
void selectfunc(uint8_t *value, char *name)
17
{
18
}

Oliver

von Hans (Gast)


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.
1
#include <avr/io.h>
2
#include <avr/pgmspace.h>
3
4
typedef unsigned char uint8_t;
5
6
#define MENU_ENTRY_NAMELEN 42
7
#define NULL ((void *)0)
8
#define vEditTime ((void *)1234)
9
#define vEditDate ((void *)5678)
10
11
typedef struct menu_entry_s {
12
  uint8_t flags;
13
  void (*select)(uint8_t *value, char *name);
14
  char name[MENU_ENTRY_NAMELEN];
15
  void *value;
16
} PROGMEM menu_entry_t;
17
18
typedef struct menu_s {
19
  uint8_t top_entry;
20
  uint8_t current_entry;
21
  uint8_t num_entries;
22
  struct menu_s *previous;
23
  menu_entry_t *entry;             
24
} menu_t;
25
26
27
menu_entry_t zeitsub[]= {
28
  {.flags = 3,                       //<== Dieser Wert soll ausgelesen werden
29
   .select = vEditTime,
30
   .name = "Uhrzeit",
31
   .value = 0,
32
  },
33
  {.flags = 4,
34
   .select = vEditDate,
35
   .name = "Datum",
36
   .value = 0,
37
  },
38
};
39
40
menu_t Zeit = {
41
   .top_entry = 0,
42
   .current_entry = 0,
43
   .entry = (menu_entry_t*)&zeitsub,
44
   .num_entries = 2,
45
   .previous = NULL,
46
};
47
48
typedef struct {
49
    menu_t *menu; 
50
} menu_context_t;
51
52
menu_context_t context = {
53
   .menu = NULL
54
};
55
56
57
int main(void)
58
{
59
     uint8_t flag=0;
60
  
61
     context.menu = &Zeit;
62
63
     flag = pgm_read_byte(&((menu_entry_t*)pgm_read_word(&context.menu->entry))[0].flags);
64
65
  return 0;
66
}

von Oliver (Gast)


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:
1
typedef struct menu_entry_s {
2
  uint8_t flags;
3
  void (*select)(uint8_t *value, char *name);
4
  char name[MENU_ENTRY_NAMELEN];
5
  void *value;
6
} menu_entry_t;
7
8
menu_entry_t PROGMEM zeitsub[]= {
9
  {.flags = 3,                
10
   .select = vEditTime,
11
   .name = "Uhrzeit",
12
   .value = 0,
13
  },
14
  {.flags = 4,
15
   .select = vEditDate,
16
   .name = "Datum",
17
   .value = 0,
18
  },
19
};

Oliver

von Hans (Gast)


Lesenswert?

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

von Oliver (Gast)


Lesenswert?

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

von Oliver (Gast)


Lesenswert?

Nachtrag 2:

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

Oliver

von Hans (Gast)


Lesenswert?

Vielen Dank für die schnelle Hilfe!

Hans

von Hans (Gast)


Lesenswert?

So funktioniert's korrekt:

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

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.