mikrocontroller.net

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


Autor: Marcel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
struct segmap 
{
   volatile uint8_t *port;
   uint8_t bitmask;
};

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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Marcel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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   |
----------|----------|----------|

struct segmap 
{
   volatile uint8_t *port;
   uint8_t bitmask;
};


inline float pgm_read_struct(struct segmap *addr)
{  

    struct segmap i[9];  // Array einer Spalte aus dem Flash

  
  return i[9];
} 


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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Marcel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?
  for( y = 0; y < AnzahlZeilen; ++y )
     mach_was_mit( Array[y][ Spalte_die_ich_haben_will ] );

Oder eben, wenn das Array im Flash liegt
  for( y = 0; y < AnzahlZeilen; ++y )
     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
  for( y = 0; y < AnzahlZeilen; ++y ) {
    struct segmap tmp;
    memcpy_P( &tmp, &Array[y][ Spalte_die_ich_haben_will ], sizeof( tmp ) );

    mach_was_mit( tmp );
  }

Autor: Marcel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Marcel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
const struct segmap digits[8][9] = 
{
  { /* digit 1 */
    { &LCDDR12, 0x40 },  /* segment 1A */
    { &LCDDR12, 0x20 },  /* 1B */
    { &LCDDR7, 0x20 },  /* 1C */
  { &LCDDR2, 0x40 },  /* 1D */
    { &LCDDR7, 0x80 },  /* 1E */
  { &LCDDR12, 0x80 },  /* 1F */
  { &LCDDR7, 0x40 },  /* 1G */
  { &LCDDR2, 0x20 },  /* 1DP */
  { &LCDDR2, 0x80 },  /* 1AN */
  },

  { /* digit 2 */
    { &LCDDR12, 0x08 },  /* segment 2A */
    { &LCDDR12, 0x04 },  /* 2B */
    { &LCDDR7, 0x04 },  /* 2C */
  { &LCDDR2, 0x08 },  /* 2D */
    { &LCDDR7, 0x10 },  /* 2E */
  { &LCDDR12, 0x10 },  /* 2F */
  { &LCDDR7, 0x08 },  /* 2G */
  { &LCDDR2, 0x04 },  /* 2DP */
  { &LCDDR2, 0x10 },  /* 2AN */
  },
...

Und hier ist die Funktion die eine Zahl/Buchstaben "schreibt":
PS: Der
 LCD_character_table[] 
 gibt nur den Code Zurück welche Segmente angeschaltet bzw. 
ausgeschaltet werden müssen:
const unsigned int LCD_character_table[54]  = { // Character definitions table.

  0x00,    // '' (Not defined)
  0x00,    // '+' (Not defined)
  0x00,    // ',' (Not defined)
  0x00,    // '-' (Not defined)
  0x00,    // '.' (Not defined)
  0x00,    // '/' (Not defined)
  0b00111111,    // '0'
  0b00000110,    // '1'
  0b01011011,    // '2'
  0b01001111,    // '3'
  0b01100110,    // '4'
  0b01101101,    // '5'
  0b01111101,    // '6'
  0b00000111,    // '7'
  0b01111111,    // '8'
  0b01101111,    // '9'
....
void LCD_out(uint8_t digit, uint8_t character)
{
  
  //Konvertiere character in den LCD-Code
  
  uint8_t lcdcode= LCD_character_table[character-42];

  //Routine, um LCD-Code in die Register zu schieben
  uint8_t i,mask;
  
  //Zeigt auf das Register-Array einer Zahl
   volatile struct segmap *sm;
   
  
  for (mask = 1,i = 0 ; i < 8; mask <<= 1,i++) 
  {
    
  sm = & (digits[digit][i]);


    if (lcdcode & mask) 
  {
      /* enable this segment */
      *(sm->port) |= sm->bitmask;

    } 
  
  else 
  {
      /* disable this segment */
      *(sm->port) &= ~sm->bitmask;
    }

  }

}

tak Marcel

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcel schrieb:

> Im SRAM sieht das wie folgt aus: :)

Na, passt doch wunderbar
const struct segmap digits[8][9] PROGMEM =
{

....
};

void LCD_out(uint8_t digit, uint8_t character)
{
  //Konvertiere character in den LCD-Code 
  uint8_t lcdcode = LCD_character_table[character-42];
 
  //Routine, um LCD-Code in die Register zu schieben
  uint8_t i,mask;
 
  //Zeigt auf das Register-Array einer Zahl
  struct segmap sm;
 
  for (mask = 1,i = 0 ; i < 8; mask <<= 1,i++)
  {
    memcpy_P( sm, & (digits[digit][i]), sizeof( struct segmap ) );

    if (lcdcode & mask)
    {
      sm->port |= sm->bitmask;
    }
 
    else
    {
      sm->port &= ~(sm->bitmask);
    }
  }
}

Autor: Marcel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Marcel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Laszlo H. (mobius)
Datum:

Bewertung
0 lesenswert
nicht 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.

struct segmap 
{
   uint8_t port;
   uint8_t bitmask;
};

const struct segmap digits[8][9] PROGMEM =
{
  { /* digit 1 */
    { (uint8_t)_SFR_MEM_ADDR(LCDDR12), 0x40 },  /* segment 1A */
    { (uint8_t)_SFR_MEM_ADDR(LCDDR12), 0x20 },  /* 1B */
 // Und so weiter und so fort ;)
};

// Routine von Karl heinz Buchegger
void LCD_out(uint8_t digit, uint8_t character)
{
    // hier ist alles gleich

    if (lcdcode & mask)
    {
      // hier casten
      (*(uint8_t*)((uint16_t)sm->port)) |= sm->bitmask;
    } 
    else
    {
      // und auch hier
      (*(uint8_t*)((uint16_t)sm->port)) &= ~(sm->bitmask);
    }
  }
}


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

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.