Forum: Compiler & IDEs Initialisierung großer structs mit vielen Nullen


von flex (Gast)


Lesenswert?

Hallo,

bei meinem Projekt kommt es auf jedes Byte an. Beim Optimieren habe ich 
festgestellt, dass die Initialisierung von Variablen eigentlich besser 
laufen könnte.
Ich habe z.B. einen großen struct bzw. Array davon (mit Beispielwerten):
1
typedef union {
2
  uint8_t Index[5];
3
  struct {
4
    uint8_t A, B, C, D, E;
5
  } Name;
6
} tFlex;
7
8
typedef struct {
9
  int8_t Alpha, Beta;
10
  tFlex flex;
11
} tData;
12
13
tData Data[COUNT_DATA] = {
14
  { 7,  1, {{ 23,  42,   1,   2,   3}}},
15
  { 2,  4, {{ 47,  11,   0,   0,   7}}},
16
  {47, 87, {{  0,   8,  15,   0,   0}}},
17
  { 9, 73, {{  3,   1,   4,   1,   5}}},
18
  { 5, 10, {{  1,   1,   2,   3,   5}}},
19
  { 2, 11, {{ 90,  60,  90,   0, 190}}},
20
  { 1,  0, {{  0,   0,   0,   0,   0}}},
21
  { 1,  0, {{  0,   0,   0,   0,   0}}},
22
  { 1,  0, {{  0,   0,   0,   0,   0}}},
23
  { 1,  0, {{  0,   0,   0,   0,   0}}},
24
  { 1,  0, {{  0,   0,   0,   0,   0}}},
25
  { 1,  0, {{  0,   0,   0,   0,   0}}},
26
  { 1,  0, {{  0,   0,   0,   0,   0}}},
27
  { 1,  0, {{  0,   0,   0,   0,   0}}},
28
  { 1,  0, {{  0,   0,   0,   0,   0}}},
29
  { 1,  0, {{  0,   0,   0,   0,   0}}},
30
  { 1,  0, {{  0,   0,   0,   0,   0}}},
31
// ...diese "leeren" (man beachte die 1 an Stelle 1)
32
//   Zeilen wiederholen sich bis zum Ende des Arrays...
33
}

Dieses Array befindet sich im RAM und im EEPROM. Die o.g. Werte sind der 
Default, für den Fall, dass die Werte im EEPROM ungültig sind oder falls 
man einen Reset der Werte auf den Default braucht.

Mein Gedanke war nun folgender: Da im Default nur einige Zeilen Werte 
enthalten, müsste sich doch durch die Art und Weise des Init eine Menge 
Platz sparen lassen. Kann man irgendwie "in einem Rutsch" einen Struct 
aus dem ROM ins RAM kopieren?

Ich habe es so probiert, aber das frisst auch ziemlich viel Speicher:
Erst die Ganze Geschichte als noinit (im Gegensatz zu oben):
1
tData Data[COUNT_DATA] __attribute__((section(".noinit")));

Dann die entscheidenden ersten Zeilen ins ROM:
1
const tData InitData[7] PROGMEM = {
2
  { 7,  1, {{ 23,  42,   1,   2,   3}}},
3
  { 2,  4, {{ 47,  11,   0,   0,   7}}},
4
  {47, 87, {{  0,   8,  15,   0,   0}}},
5
  { 9, 73, {{  3,   1,   4,   1,   5}}},
6
  { 5, 10, {{  1,   1,   2,   3,   5}}},
7
  { 2, 11, {{ 90,  60,  90,   0, 190}}},
8
  { 1,  0, {{  0,   0,   0,   0,   0}}}
9
}

Und nun:
1
void copydata(const tData *source, tData *dest) {
2
  dest->Alpha = pgm_read_byte(&source->Alpha);
3
  dest->Beta = pgm_read_byte(&source->Beta);
4
  uint8_t i;
5
  for (i = 0; i < 5; i++) dest->Flex.Index[i] = pgm_read_byte(&source->Flex.Index[i]);
6
}
7
8
uint8_t i;
9
for (i = 0; i < 6; i++) copydata(&InitData[i], &Data[i]);
10
for (i = 6; i < COUNT_DATA; i++) copydata(&InitData[6], &Data[i]);

Ich empfinde den Zugriff auf jedes Element mit pgm_read_byte als sehr 
umständlich. Geht das nicht irgendwie in einem Rutsch bzw. mit einer 
einzigen Schleife?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Es gibt doch memcpy_P aus avr/pgmspace.h der AVR-LibC. Das spart schon 
mal copydata.

Den Array-Anfang kannst en bloc initialisieren; es sind 6*sizeof(tData) 
Bytes.

von flex (Gast)


Lesenswert?

Danke! Weiß auch nicht, wie mir memcpy_P entgehen konnte. Damit klappt's 
gut und es spart mir jetzt insgesamt im fertigen Programm ca. 250 Bytes, 
was natürlich ein kleiner Jackpot ist.
1
uint8_t i;
2
memcpy_P(&Data[0], &InitData[0], COUNT_INITDATA * sizeof(tData));
3
for (i = COUNT_INITDATA; i < COUNT_DATA; i++) memcpy_P(&Data[i], &InitData[COUNT_INITDATA-1], sizeof(tData));

Vielleicht gibt's ja noch revolutionär andere Ideen zu dem Thema...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mit neuen Toolchains geht's auch komplett ohne pgm_read_ und PROGMEM, 
siehe

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

von Krapao (Gast)


Lesenswert?

> 
http://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html#AVR-Named-Address-Spaces

IMHO ist dort ein Bug in dem Beispiel (var != i)
1
     #include <avr/pgmspace.h> /* From avr-libc */
2
     
3
     const int var PROGMEM = 1;
4
               ^^^     
5
     int read_i (void)
6
     {
7
         return (int) pgm_read_word (&i);
8
                                     ^^
9
     }

von flex (Gast)


Lesenswert?

Hmm, das geht aber erst mit neueren Versionen, oder?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jo, oder hast du in den alten Versionen was revolutionäres erwartet?

von flex (Gast)


Lesenswert?

Hätte ja sein können, dass das auch ein Feature ist, das ich übersehen 
habe. ;)
Aber mal ehrlich, spart das wirklich Platz oder ist das nur eine etwas 
eleganter aussehende Konstruktion? Ich meine, intern kocht der GCC auch 
nur mit Wasser. Und neuere Versionen neigen im Allgemeinen doch eher 
dazu, größeren Code zu erzeugen.

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.