Forum: Compiler & IDEs Struct-Array im Flash oder doch lieber aufsplitten?


von Marcel (Gast)


Lesenswert?

Hallo zusammen,

ich beschäftige mich gerade mit dem Thema Konstanten im Flash-Speicher 
zu versenken.

Hierbei habe ich folgenden Array-Aufbau:

      0        1   2   3   4... 7
0   struct  ...

1   struct  ...

2   struct ...
.
.
.
8

Der Aufbau der Struktur (struct) ist folgender:
1
struct segmap 
2
{
3
   volatile uint8_t *port;
4
   uint8_t bitmask;
5
};

Ich benötige immer eine bestimmte Spalte des Arrays.

Sprich ich habe hier nur ein Word und ein Byte. Für mich stellt sich 
jetzt die Frage, ob ich mich hinsetzen soll, um Hilfsfunktionen zu 
programmieren oder lieber aus dem einen Array einfach 2 Arrays mache, 
die dann einen Word bzw. einen Byte Inhalt haben.

Die Struktur-Variante ist doch rechenlastiger oder, da zusätzlich noch 
Hilfsfunktionen ausgeführt werden müssen.
Für die beiden Arrays habe ich dann leider aber auch nicht mehr die 
bequemen und auch schnellen Zugriffe.

Wie soll ich es eurer Meinung nach machen?

Vielen Dank für eure Hilfe

Marcel

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Strukturvariante.

Sollte besser lesbar sein und damit weniger bugträchtig und besser zu 
warten.

Die "Hilfsfunktionen" sind einfache Offsets, d.h. Additionen fester 
Größen. Ich bezweifele, dass ein selbstgebauter Code mit zwei Arrays 
weniger Overhead hätte.

von Marcel (Gast)


Lesenswert?

Stefan B. schrieb:
> Die "Hilfsfunktionen" sind einfache Offsets, d.h. Additionen fester
> Größen. Ich bezweifele, dass ein selbstgebauter Code mit zwei Arrays
> weniger Overhead hätte.

O.K., also werd ich mich an die StrukturVariante dranmachen

----------|----------|----------|
          |          |          |
     PORTADRESSE     | BITMASK  |
   1Byte  |   1Byte  |  1Byte   |
----------|----------|----------|
          |          |          |
     PORTADRESSE     | BITMASK  |
   1Byte  |   1Byte  |  1Byte   |
----------|----------|----------|

1
struct segmap 
2
{
3
   volatile uint8_t *port;
4
   uint8_t bitmask;
5
};
6
7
8
inline float pgm_read_struct(struct segmap *addr)
9
{  
10
11
    struct segmap i[9];  // Array einer Spalte aus dem Flash
12
13
  
14
  return i[9];
15
}

Doch wie genau addiere ich den  Pointer? Ich möchte ja eine Spalte des 
Arrays auslesen
Vielen Dank für eure Hilfe Marcel

von Karl H. (kbuchegg)


Lesenswert?

Marcel schrieb:

> O.K., also werd ich mich an die StrukturVariante dranmachen

Strktur ist immer gut.
Dinge die zusammengehören sollen auch zusammen bleiben.

> Doch wie genau addiere ich den  Pointer?

Was meinst du mit addieren?
Wieso willst du Pointer addieren?

von Marcel (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Was meinst du mit addieren?
> Wieso willst du Pointer addieren?

Das war schlecht von mir ausgedrückt -Entschuldige. Ich meinte eher das 
Inkrementieren der Pointer.
1
addr++
beispielsweise

Ich habe mir das AVR-GCC Tutorial angeschaut. Hier ist jedoch nur eine 
Float-Porting aus dem Flash und leider kein Struktur-Beispiel.
Darum habe ich gerade Probleme wie ich die Struktur denn auf den RAM 
portieren soll.

Nochmal zusammengefasst:
Ich habe ein zweidimensionales Array, wovon ich eine bestimmte Spalte 
benötige. Die Werte des Arrays sind von einer Struktur namens "segmap" 
(s.o.)

Vielen Dank
Marcel

von Karl H. (kbuchegg)


Lesenswert?

Marcel schrieb:

> Ich habe ein zweidimensionales Array, wovon ich eine bestimmte Spalte
> benötige.

Inwiefern benötigst du die? Wozu brauchst du sie?
Brauchst du tatsächlich alle Elemente einer Spalte als eigene 
'Datenstruktur' oder genügt es eine Funktion zu haben, die einfach alle 
Elemente einer Spalte einzeln abarbeitet?

> Die Werte des Arrays sind von einer Struktur namens "segmap"
> (s.o.)

Ja, Warum kannst du nicht einfach zugreifen?
1
  for( y = 0; y < AnzahlZeilen; ++y )
2
     mach_was_mit( Array[y][ Spalte_die_ich_haben_will ] );

Oder eben, wenn das Array im Flash liegt
1
  for( y = 0; y < AnzahlZeilen; ++y )
2
     mach_was_mit_dem_FlashPointer_von( &Array[y][ Spalte_die_ich_haben_will ] );

Edit:
oder wenn du eine Funktion hast, die ein einzelnes Element bearbeiten 
kann aber davon ausgeht, dass dieses im SRAM liegt
1
  for( y = 0; y < AnzahlZeilen; ++y ) {
2
    struct segmap tmp;
3
    memcpy_P( &tmp, &Array[y][ Spalte_die_ich_haben_will ], sizeof( tmp ) );
4
5
    mach_was_mit( tmp );
6
  }

von Marcel (Gast)


Lesenswert?

Zur Frage: Warum brauche ich diese.
Ich habe ein LCD-Glas mit 8darstellbaren Zahlen ( jeweils 7Segment 
Anzeige).

Ich nutze den Mega169 Dieser ist in der Lage das LCD Glas anzusteuern.
Alles läuft sehr gut wenn ich diesen Array im Ram habe. Doch benötige 
ich jetzt nochmals Platz für ein 300 Byte großes Array (UART Empfang). 
Dazu muss ich das LCD-Array ins Flash packen.

Zur Frage: Brauchst du alle Elemente einer Datenstruktur als 
geschlossen?

Nein das nicht. Mir reicht es auch Element für Element zu lesen und dann 
wieder zu verarbeiten...  Die Struktur beinhaltet einen Pointer welcher 
auf das IO Register zeigt und einen Wert 00010000 z.B. Welcher dann sagt 
dass an der Stelle des Registers Digit 1 Segment A angesprochen werden 
kann.

Ich werde einfach nicht schlau wie ich ein Element des Arrays aus dem 
Flash hole. Wie schaffe ich das mit Read _word und Byte?

Merci beaucoup Marcel

von Karl H. (kbuchegg)


Lesenswert?

Marcel schrieb:

> Ich werde einfach nicht schlau wie ich ein Element des Arrays aus dem
> Flash hole. Wie schaffe ich das mit Read _word und Byte?

Du brauchst eigentlich einen pgm_read_block.
Die gibt es aber so nicht. Macht aber nichts. Denn eine Struktur ist ja 
auch nichts anderes als eine Abfolge von Bytes. Wenn man also in einer 
Schleife einfach sizeof( Struktur ) Bytes vom Flash in bereitsgestellten 
SRAM Speicher kopiert, dann hat man eine komplette Struktur vom Flash in 
den SRAM kopiert. An dieser Stelle sieht man die Dinger pragmatisch: Es 
ist völlig egal, ob das was du kopieren willst eine Struktur ist oder 
irgendwas anderes. Es hat eine Länge in Bytes und diese Anzahl Bytes 
werden vom Flash ins SRAM umkopiert.

Die Lösung 3, von einem Post weiter oben, macht genau das. Sie benutzt 
memcopy_P um die Umkopieraktion durchzuführen.

von Karl H. (kbuchegg)


Lesenswert?

Zeig doch mal, wie deine Funkion aussehen würde, wenn alles im SRAM ist. 
Dann ist es leichter über die Dinge zu reden, als wenn das alles immer 
nur hypothetisch abgehandelt wird.

von Marcel (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Zeig doch mal, wie deine Funkion aussehen würde, wenn alles im SRAM ist.
> Dann ist es leichter über die Dinge zu reden, als wenn das alles immer
> nur hypothetisch abgehandelt wird.

So nun bin ich wieder zu Hause.

Im SRAM sieht das wie folgt aus: :)

Hier ist das 2D Array, wovon ich jeweils die Daten eines Digits brauche
1
const struct segmap digits[8][9] = 
2
{
3
  { /* digit 1 */
4
    { &LCDDR12, 0x40 },  /* segment 1A */
5
    { &LCDDR12, 0x20 },  /* 1B */
6
    { &LCDDR7, 0x20 },  /* 1C */
7
  { &LCDDR2, 0x40 },  /* 1D */
8
    { &LCDDR7, 0x80 },  /* 1E */
9
  { &LCDDR12, 0x80 },  /* 1F */
10
  { &LCDDR7, 0x40 },  /* 1G */
11
  { &LCDDR2, 0x20 },  /* 1DP */
12
  { &LCDDR2, 0x80 },  /* 1AN */
13
  },
14
15
  { /* digit 2 */
16
    { &LCDDR12, 0x08 },  /* segment 2A */
17
    { &LCDDR12, 0x04 },  /* 2B */
18
    { &LCDDR7, 0x04 },  /* 2C */
19
  { &LCDDR2, 0x08 },  /* 2D */
20
    { &LCDDR7, 0x10 },  /* 2E */
21
  { &LCDDR12, 0x10 },  /* 2F */
22
  { &LCDDR7, 0x08 },  /* 2G */
23
  { &LCDDR2, 0x04 },  /* 2DP */
24
  { &LCDDR2, 0x10 },  /* 2AN */
25
  },
26
...

Und hier ist die Funktion die eine Zahl/Buchstaben "schreibt":
PS: Der
1
 LCD_character_table[]
 gibt nur den Code Zurück welche Segmente angeschaltet bzw. 
ausgeschaltet werden müssen:
1
const unsigned int LCD_character_table[54]  = { // Character definitions table.
2
3
  0x00,    // '' (Not defined)
4
  0x00,    // '+' (Not defined)
5
  0x00,    // ',' (Not defined)
6
  0x00,    // '-' (Not defined)
7
  0x00,    // '.' (Not defined)
8
  0x00,    // '/' (Not defined)
9
  0b00111111,    // '0'
10
  0b00000110,    // '1'
11
  0b01011011,    // '2'
12
  0b01001111,    // '3'
13
  0b01100110,    // '4'
14
  0b01101101,    // '5'
15
  0b01111101,    // '6'
16
  0b00000111,    // '7'
17
  0b01111111,    // '8'
18
  0b01101111,    // '9'
19
....
1
void LCD_out(uint8_t digit, uint8_t character)
2
{
3
  
4
  //Konvertiere character in den LCD-Code
5
  
6
  uint8_t lcdcode= LCD_character_table[character-42];
7
8
  //Routine, um LCD-Code in die Register zu schieben
9
  uint8_t i,mask;
10
  
11
  //Zeigt auf das Register-Array einer Zahl
12
   volatile struct segmap *sm;
13
   
14
  
15
  for (mask = 1,i = 0 ; i < 8; mask <<= 1,i++) 
16
  {
17
    
18
  sm = & (digits[digit][i]);
19
20
21
    if (lcdcode & mask) 
22
  {
23
      /* enable this segment */
24
      *(sm->port) |= sm->bitmask;
25
26
    } 
27
  
28
  else 
29
  {
30
      /* disable this segment */
31
      *(sm->port) &= ~sm->bitmask;
32
    }
33
34
  }
35
36
}

tak Marcel

von Karl H. (kbuchegg)


Lesenswert?

Marcel schrieb:

> Im SRAM sieht das wie folgt aus: :)

Na, passt doch wunderbar
1
const struct segmap digits[8][9] PROGMEM =
2
{
3
4
....
5
};
6
7
void LCD_out(uint8_t digit, uint8_t character)
8
{
9
  //Konvertiere character in den LCD-Code 
10
  uint8_t lcdcode = LCD_character_table[character-42];
11
 
12
  //Routine, um LCD-Code in die Register zu schieben
13
  uint8_t i,mask;
14
 
15
  //Zeigt auf das Register-Array einer Zahl
16
  struct segmap sm;
17
 
18
  for (mask = 1,i = 0 ; i < 8; mask <<= 1,i++)
19
  {
20
    memcpy_P( sm, & (digits[digit][i]), sizeof( struct segmap ) );
21
22
    if (lcdcode & mask)
23
    {
24
      sm->port |= sm->bitmask;
25
    }
26
 
27
    else
28
    {
29
      sm->port &= ~(sm->bitmask);
30
    }
31
  }
32
}

von Marcel (Gast)


Lesenswert?

Vielen Dank Karl heinz! :)

Beim rüberfliegen über deine Lsg ist mir noch nen  klitzekleiner Fehler 
aufegafallen :-P

Karl heinz Buchegger schrieb:
> if (lcdcode & mask)
>     {
>       sm->port |= sm->bitmask;
>     }
>
>     else
>     {
>       sm->port &= ~(sm->bitmask);

sm ist ja jetzt kein Pointer mehr ;)

Das war ja echt einfacher als ich gedacht hatte ^^

Du hast mir echt die Augen geöffnet und die "Angst" vom Flash-Speicher 
genommen...

Grüße Marcel

von Karl H. (kbuchegg)


Lesenswert?

Marcel schrieb:
> Vielen Dank Karl heinz! :)
>
> Beim rüberfliegen über deine Lsg ist mir noch nen  klitzekleiner Fehler
> aufegafallen :-P
>
> Karl heinz Buchegger schrieb:
>> if (lcdcode & mask)
>>     {
>>       sm->port |= sm->bitmask;
>>     }
>>
>>     else
>>     {
>>       sm->port &= ~(sm->bitmask);
>
> sm ist ja jetzt kein Pointer mehr ;)

LOL
Du hast recht.
Ich hab nur den ersten * entfernt, aber das ist ja klarerweise noch 
nicht alles, bzw. genau falsch. Sorry

     *(sm.port) |= sm.bitmask;


> Das war ja echt einfacher als ich gedacht hatte ^^

Der Trick besteht im Grunde darin, dass du dir alles aus dem Flash erst 
mal ins SRAM kopieren musst, damit du damit arbeiten kannst. Sobald es 
dann erst mal im SRAM ist, geht alles wie gewohnt.

von Marcel (Gast)


Lesenswert?

So habs jetzt eben compiliert und läuft :)

Für die Forumsdurchsucher, die ähnliche Probleme mit dem Flash haben, 
hab ich noch nen kleinen Fehler in Karl heinz Code gefunden:

void * memcpy_P  (  void *  dest, PGM_VOID_P  src, size_t  n)

Sprich das

Karl heinz Buchegger schrieb:
> memcpy_P( sm, & (digits[digit][i]), sizeof( struct segmap ) );

muss umgeändert werden in

> memcpy_P( &sm, & (digits[digit][i]), sizeof( struct segmap ) );

(fürs Verständnis aber unerheblich!)

Vielen Dank nochmal Karl heinz, Du hast mir echt weitergeholfen! :)

Grüße Marcel

von Laszlo H. (mobius)


Lesenswert?

Man kann auch noch 1 Byte / Eintrag sparen, wenn man statt dem pointer 
(2 Byte) die Register-Adresse abspeichert (LCDDR0-18 liegen im speicher 
<= 0xFE). Im Flash abgelegt werden kann die Adresse mit dem Macro 
_SFR_MEM_ADDR(sfr). Beim Auslesen muss anschließend der uint8-Wert nur 
noch in einen Zeiger typecastet werden.

1
struct segmap 
2
{
3
   uint8_t port;
4
   uint8_t bitmask;
5
};
6
7
const struct segmap digits[8][9] PROGMEM =
8
{
9
  { /* digit 1 */
10
    { (uint8_t)_SFR_MEM_ADDR(LCDDR12), 0x40 },  /* segment 1A */
11
    { (uint8_t)_SFR_MEM_ADDR(LCDDR12), 0x20 },  /* 1B */
12
 // Und so weiter und so fort ;)
13
};
14
15
// Routine von Karl heinz Buchegger
16
void LCD_out(uint8_t digit, uint8_t character)
17
{
18
    // hier ist alles gleich
19
20
    if (lcdcode & mask)
21
    {
22
      // hier casten
23
      (*(uint8_t*)((uint16_t)sm->port)) |= sm->bitmask;
24
    } 
25
    else
26
    {
27
      // und auch hier
28
      (*(uint8_t*)((uint16_t)sm->port)) &= ~(sm->bitmask);
29
    }
30
  }
31
}


Ohne den Typecast nach uint16_t würde der Compiler berechtigterweise die 
die Warnung "cast to pointer from integer of different size" (d.h. man 
versucht aus einer Variablen mit mehr/weniger als 16bit einen Zeiger zu 
convertieren) ausspucken. Anschließend castet man (fast analog zu den 
Definitionen von _MMIO_BYTE/_MMIO_WORD in avr/sfr_defs.h) die Variable 
in einen Zeiger und greift auf dessen Inhalt zu.

Ich weiß, ist ein wenig Bit-Hunting mäßig und der Code wird auch 
unleserlicher, aber immer gut im Hinterkopf zu behalten, wenn einem mal 
der Flash-Speicher ausgeht (und es nur um 10-20 Byte net reinpassen 
will).
gruß
Mobius

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.