Datum:
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):
typedef union { uint8_t Index[5]; struct { uint8_t A, B, C, D, E; } Name; } tFlex; typedef struct { int8_t Alpha, Beta; tFlex flex; } tData; tData Data[COUNT_DATA] = { { 7, 1, {{ 23, 42, 1, 2, 3}}}, { 2, 4, {{ 47, 11, 0, 0, 7}}}, {47, 87, {{ 0, 8, 15, 0, 0}}}, { 9, 73, {{ 3, 1, 4, 1, 5}}}, { 5, 10, {{ 1, 1, 2, 3, 5}}}, { 2, 11, {{ 90, 60, 90, 0, 190}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}}, // ...diese "leeren" (man beachte die 1 an Stelle 1) // Zeilen wiederholen sich bis zum Ende des Arrays... } |
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):
tData Data[COUNT_DATA] __attribute__((section(".noinit"))); |
Dann die entscheidenden ersten Zeilen ins ROM:
const tData InitData[7] PROGMEM = { { 7, 1, {{ 23, 42, 1, 2, 3}}}, { 2, 4, {{ 47, 11, 0, 0, 7}}}, {47, 87, {{ 0, 8, 15, 0, 0}}}, { 9, 73, {{ 3, 1, 4, 1, 5}}}, { 5, 10, {{ 1, 1, 2, 3, 5}}}, { 2, 11, {{ 90, 60, 90, 0, 190}}}, { 1, 0, {{ 0, 0, 0, 0, 0}}} } |
Und nun:
void copydata(const tData *source, tData *dest) { dest->Alpha = pgm_read_byte(&source->Alpha); dest->Beta = pgm_read_byte(&source->Beta); uint8_t i; for (i = 0; i < 5; i++) dest->Flex.Index[i] = pgm_read_byte(&source->Flex.Index[i]); } uint8_t i; for (i = 0; i < 6; i++) copydata(&InitData[i], &Data[i]); 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?
Datum:
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.
Datum:
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.
uint8_t i; memcpy_P(&Data[0], &InitData[0], COUNT_INITDATA * sizeof(tData)); 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...
Datum:
Mit neuen Toolchains geht's auch komplett ohne pgm_read_ und PROGMEM, siehe http://gcc.gnu.org/onlinedocs/gcc/Named-Address-Sp...
Datum:
> http://gcc.gnu.org/onlinedocs/gcc/Named-Address-Sp... IMHO ist dort ein Bug in dem Beispiel (var != i)
#include <avr/pgmspace.h> /* From avr-libc */
const int var PROGMEM = 1;
^^^
int read_i (void)
{
return (int) pgm_read_word (&i);
^^
}
|
Datum:
Hmm, das geht aber erst mit neueren Versionen, oder?
Datum:
Jo, oder hast du in den alten Versionen was revolutionäres erwartet?
Datum:
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.