struct array in Flash setzen

OP #1812284
Lesenswert?

Hi

Ich habe derzeit eine struct die bisher 4 Byte groß ist, sich aber 
später verdoppeln bis vervierfachen wird. Außerdem wird diese so 30 mal 
benötigt.
16 * 30 = 480 Byte -> für den Mega8 verkraftbar, aber da diese alle 
Konstant sind, wäre es doch viel besser sie in den Flash zu verlagern. 
Da habe ich aber ein Problem: Ich habe eine Methode die die Struct 
initialisiert und die Werte setzt, aber wie mache ich das direkt, ich 
kann ja nicht die werte erst auf dem µC schreiben.

Das ist die Struct:
1
typedef struct 
2
{
3
    uint16_t time;
4
    uint16_t bonus;
5
} Game;

Muss ich das auslesen über Pointer machen, oder gibts da ein Trick wie 
man  direkt Structs aus dem Flash lesen kann?
OP #1812469
Lesenswert?

Danke, das funktioniert. Kannst du mir auch sagen wie ich da noch ein 
Array reinpacke und initialisiere? Denn wenn ich einfach
1
uint16_t* MeinArray;
will ich später ja die einzelnen Einträge wieder herauslesen:
1
Game GetGame(uint8_t modi)
2
{
3
    Game temp;
4
    temp.time = pgm_read_word (&(Games[modi].time))
5
    temp.bonus = pgm_read_word (&(Games[modi].bonus));
6
    temp.options = pgm_read_byte(&(Games[modi].options));
7
    for(int i = 0; i < ((temp.options << 5) & 0x1F); i++)
8
    {
9
        temp.MeinArray[i] = pgm_read_word(&(Games[modi].MeinArray[i]));
10
    } 
11
    return temp;
12
}

Die letzten 3 Byte in options bestimmen dabei die größe des Arrays.
Allerdings: Muss ich das Array nicht vorher initialisieren? So geht es 
aber nicht:
1
temp.MeinArray = new uint16_t[((temp.options << 5) & 0x1F)];
#1812478
Lesenswert?

Samuel K. schrieb:
> Danke, das funktioniert. Kannst du mir auch sagen wie ich da noch ein
> Array reinpacke und initialisiere? Denn wenn ich einfach
>
1
> uint16_t* MeinArray;
2
>

Das ist aber kein Array.
Das ist ein Pointer!

Du kannst natürlich diesen Pointer auch dazu benutzen, ihn auf ein 
Array, welches seinerseits im Flash liegt, zeigen zu lassen.

Dann musst du mittels pgm_readword zuerst den Pointer aus dem Flash 
holen und den dann mit den notwendigen Offsets dazu benutzen um die 
eigentlichen Daten zu lesen. Ist ein 2-stufiger Prozess, aber machbar.
1
typedef struct 
2
{
3
    uint16_t  time;
4
    uint16_t  bonus;
5
    uint16_t *data;
6
} Game;
7

8
uint16_t data1[] PROGMEM = { 5, 6, 7, 8 };
9
uint16_t data2[] PROGMEM = { 10, 11, 12 };
10

11
Game myGames[2] PROGMEM =
12
   { {1, 50, data1},
13
     {2, 100, data2},
14
   };
OP #1812589
Lesenswert?

Gut ich hab das jetzt so gemacht.
1
Game GetGame(uint8_t modi)
2
{
3
    Game temp;
4
    temp.time = pgm_read_word (&(Games[modi].time));
5
    temp.bonus = pgm_read_word (&(Games[modi].bonus));
6
    temp.options = pgm_read_byte(&(Games[modi].options));
7
    if((temp.options << 5) & 0x1F > 0)                      //Zeile 222
8
    {
9
        uint16_t *ptr = pgm_read_word(&(Games[modi].data)); //Zeile 224
10
        temp.data = *ptr;                                   //Zeile 225
11
    }
12
    return temp;
13
}

Da kommen aber 3 Warnungen,  außerdem funktioniert mein Programm nur 
halber. Ich wollte jetzt nur wissen, ob das obrige stimmt, und ich den 
Fehler im rest des Progs suchen muss.

Das sind die Warnungen:

222 D:\Projekte\Atmega\Chessclock\Dev\chess.c [Warning] suggest 
parentheses around comparison in operand of &

224 D:\Projekte\Atmega\Chessclock\Dev\chess.c [Warning] initialization 
makes pointer from integer without a cast

225 D:\Projekte\Atmega\Chessclock\Dev\chess.c [Warning] assignment makes 
pointer from integer without a cast
#1812597
Lesenswert?

Samuel K. schrieb:

>         uint16_t *ptr = pgm_read_word(&(Games[modi].data)); //Zeile 224
>         temp.data = *ptr;                                   //Zeile 225

Wieso * ?
ptr ist bereits die Adresse im Flash an der die Daten stehen.
Wenn du an die eigentlichen Array-Daten heranwillst, dann musst du 
diesen Pointer seinerseits wieder in eine pgm_read_ ... Funktion 
stecken.


> Da kommen aber 3 Warnungen,

dann geh sie der Reihe nach durch, lies die Warnung, denk darüber nach 
was dir der COmpiler sagen will und korrigiere entsprechend.

> halber. Ich wollte jetzt nur wissen, ob das obrige stimmt,

das kommt drauf an, wie du temp.data weiter benutzt.


> 222 D:\Projekte\Atmega\Chessclock\Dev\chess.c [Warning] suggest
> parentheses around comparison in operand of &

Da gehts um die Abfrage im if
  * bist du sicher, dass du ein &  haben willst
    oder doch eher ein &&

> 224 D:\Projekte\Atmega\Chessclock\Dev\chess.c [Warning] initialization
> makes pointer from integer without a cast

Rechts vom = steht ein INteger (nämlich das Ergebnis von pgm_read_word), 
rechts steht eine Pointer Variable. Ohne casting ist so etwas illegal. 
Man kann nicht einfach einem Pointer eine Zahl zuweisen

> 225 D:\Projekte\Atmega\Chessclock\Dev\chess.c [Warning] assignment makes
> pointer from integer without a cast

Dasselbe in Grün, wobei ich mir ziemlich sicher bin, das du das so 
eigentlich gar nicht willst.
OP #1812605
Lesenswert?

Danke, ich hab nicht drangedacht das data ja ein pointer ist. Deswegen 
wollte ich den Wert zuweisen.

Ich kann das doch einfach korriegieren indem ich *temp.data schreibe.

Die IF Abfrage ist so beabsichtigt, die soll nämlich die letzten 3 Bytes 
rauspicken. Die sagen nämlich wie groß das Array ist. die IF prüft nur 
ob im Array Elemente sind. Wenn die letzten 3 Bytes 0 sind, ist data = 
0. Das hab ich von http://visual-c.itags.org/visual-c-c++/280042/. Das 
hab ich dann (hoffentlich richtig) für das splitten in 5 und 3 Bytes 
umgeändert.
#1812619
Lesenswert?

Samuel K. schrieb:

> Ich kann das doch einfach korriegieren indem ich *temp.data schreibe.

Nein.
data ist ein Pointer ins Flash!
Um an die Werte zu kommen, musst du pgm_read_xxxx benutzen!

> Die IF Abfrage ist so beabsichtigt, die soll nämlich die letzten 3 Bytes
> rauspicken.

Bytes?

> Die sagen nämlich wie groß das Array ist. die IF prüft nur
> ob im Array Elemente sind. Wenn die letzten 3 Bytes 0 sind, ist data =
> 0. Das hab ich von http://visual-c.itags.org/visual-c-c++/280042/. Das
> hab ich dann (hoffentlich richtig) für das splitten in 5 und 3 Bytes
> umgeändert.

Mir ist absolut nicht klar, was du da eigentlich machen willst. Ich 
denke aber nicht, das dein Code das macht, was du eigentlich machzen 
möchtest. Es ist immer gefährlich Code, den man nicht versteht einfach 
so zu übernehmen.
Aber auf jeden Fall

    if((temp.options << 5) & 0x1F > 0)

Dir ist klar, dass > eine höhere 'Wertigkeit' hat als & ?
Der COmpiler sieht das daher als

    if((temp.options << 5) & (0x1F > 0))

und ich denke nicht, das du das haben willst.
OP #1812621
Lesenswert?

Ach was schreib ich. Es muss doch
1
*temp.guillotine = pgm_read_word(ptr);
heißen, oder stimmt das auch nicht.

EDIT: Sorry ich war z langsam um das zu kapieren.

Karl heinz Buchegger schrieb:
> if((temp.options << 5) & (0x1F > 0))
>
> und ich denke nicht, das du das haben willst.

Nein, möchte ich nicht. Danke für dne Hinweis.

Das Option Byte gibt in den ersten 5 Bit versch. Optionen an. Der 
restlichen 3 Bit geben die Größe des Arrays an. Übrigens hab ich den 
Code ohne das > 0 in einem Interpreter getestet.
#1812634
Lesenswert?

Samuel K. schrieb:
> Ach was schreib ich. Es muss doch
>
1
> *temp.guillotine = pgm_read_word(ptr);
2
>
> heißen, oder stimmt das auch nicht.

Der * da vorne sieht unlogisch aus.

> Nein, möchte ich nicht. Danke für dne Hinweis.
>
> Das Option Byte gibt in den ersten 5 Bit versch. Optionen an. Der
> restlichen 3 Bit geben die Größe des Arrays an. Übrigens hab ich den
> Code ohne das > 0 in einem Interpreter getestet.

Ohne den tatsächlichen Vergleich > 0 ändert sich auch die Interpretation
OP #1812637
Lesenswert?

Wenn ich aber das jetzt so schreibe kommen wieder diese Warnungen. 
Irgendwie versteh ich das nicht.

224 D:\Projekte\Atmega\Chessclock\Dev\chess.c [Warning] initialization
makes pointer from integer without a cast

225 D:\Projekte\Atmega\Chessclock\Dev\chess.c [Warning] assignment makes
pointer from integer without a cast
1
uint16_t *ptr = pgm_read_word(&(Games[modi].data));
2
temp.data= pgm_read_word(ptr);

Ich habe vorher nicht wirklich gesagt was ich mit dem Code vorhabe: Ich 
möchte mit dem Code den Teil vom Flash in den Ram kopieren, so benötige 
ich nur den Speicherplatz der Struct Game.
#1812694
Lesenswert?

Samuel K. schrieb:
> Wenn ich aber das jetzt so schreibe kommen wieder diese Warnungen.
> Irgendwie versteh ich das nicht.
>

Was gibt es daran nicht zu verstehen?
Welchen Datentyp hast du links vom =
Welchen Datentyp hast du rechts vom =


Rechts vom = steht ein Integer (nämlich das Ergebnis von pgm_read_word),
rechts steht eine Pointer Variable. Ohne casting ist so etwas illegal.
Man kann nicht einfach einer Pointer Variablen eine Zahl zuweisen.


Das ist ein Datentypproblem! Du versuchst einer Pointervariablen einen 
Nichtpointerzuzuweisen. Und das geht nun mal nicht einfach so.

  temp.data = (uint16_t*)pgm_read_word(&(Games[modi].data));
#1812717
Lesenswert?

Samuel K. schrieb:
> Um das Zeug dann in den Ram zu kopieren, muss ich dann doch jedes elemnt
> lesen und den Zeiger dem Temp.data zuweisen, oder ?

Welches Zeugs?

Die Array Daten?

Ja, das musst du. Aber erst mal brauchst du den Pointer darauf.
Und du brauchst Speicher, an dem du die Array Daten ablegst. Woher weißt 
du wie groß das Array werden wird?

Daher: Es ist eventuell nicht so schlau, das komplette Array in den SRAM 
zu kopieren. Hol dir in der Verarbeitung des Arrays das jeweils 
benötigte Array Element aus dem Flash. Damit umgehst du eine Menge 
Probleme.
OP #1812795
Lesenswert?

Karl heinz Buchegger schrieb:
> Hol dir in der Verarbeitung des Arrays das jeweils
> benötigte Array Element aus dem Flash.

Das ganze wird ja eine Schachuhr. Problem: Es gibt voreingespielte 
Zeit-Modi und Manuelle. Deswegen wird die Zeit erst in den Ram kopiert, 
damit sie da beim manuellen Modus noch verändert werden kann. Die 
Methode GetGame  (in C eher funktion) soll einfach ein Game im Ram 
anlegen und den gewünschten Zeitmodi vom Flash darauf kopieren. Das 
Array wird höchtens 7 * 16bit groß, da man nur 3bits zur Größenangabe 
hat.

Also ich muss:
1. Zeiger aus dem Flash lesen, der auf das Flash-Array zeigt und das 
ganze zu einem Zeiger casten:
1
uint16_t *ptr = (uint16_t*)pgm_read_word(&(Games[modi].data));
2. Die einzelnen Einträge im Array auslesen damit sie im Ram sind und 
die Adresse vom ersten Eintrag speichern
1
uint16_t value[7];
2
for(int i = 0; i<((temp.options << 5) & 0x1F);i++)
3
{
4
    value[i] = pgm_read_word(ptr + 2*i);
5
    if(i == 0)
6
        temp.data= (uint16_t*)&value;
7
}
8
return temp;
3. Über temp.data müsste man dann auf das Array zugreifen können.

MAn könnte das auch besser mit malloc machen, aber damit hab ich noch 
nie was gemacht.

Stimmt das so?
OP #1812852
Lesenswert?

@shift
Danke, habs jetzt auch gemerkt.
Ich denke es muss (x << 5) & 224 heißen. bzw. 0xE0 in Hexa

Außerdem muss irgendwas gewaltig falsch sein: Wenn ich game_mode = 1 
nehme, ist die Zeit vom 1. Spieler witklich game_mode = 1 und die vom 
zweiten game_mode = 2.
1
void MainMenu(void)
2
{
3
    Game temp;
4
    SetAll(0);
5
    uint8_t key = 255;
6
    while(1)
7
    {
8
        Show2Digits(game_mode,0);
9
        key = GetKey();      
10
        switch(key)
11
        {
12
            case 6: 
13
                if(game_mode > 1)
14
                    game_mode--; 
15
                 break;
16
            case 5:
17
                temp = GetGame(game_mode);
18
                PlayManual(temp,temp);
19
                break;
20
            case 4:
21
                if(game_mode < MODI_COUNT)
22
                    game_mode++;
23
                break;
24
        };
25
    }
26
}
27

28
Game GetGame(uint8_t modi)
29
{
30
    Game temp;
31
    temp.time = pgm_read_word (&(Games[modi].time));
32
    temp.bonus = pgm_read_word (&(Games[modi].bonus));
33
    temp.options = pgm_read_byte(&(Games[modi].options));
34
    uint16_t *ptr = (uint16_t*)pgm_read_word(&(Games[modi].guillotine));
35
    uint16_t value[7];
36
    for(int i = 0; i<((temp.options << 5) & 224);i++)
37
    {
38
        value[i] = pgm_read_word(ptr + 2*i);
39
        if(i == 0)
40
             temp.guillotine = (uint16_t*)&value;
41
    }
42
    return temp;
43
}

irgendwo muss da ein Zeiger erhöht werden, so dass er dann auf den 
nächsten Modi zeigt. Oder ich übersehe wiedermal einen Fehler den jeder 
auf Anhieb sieht ;)
#1812946
Lesenswert?

Samuel K. schrieb:

> irgendwo muss da ein Zeiger erhöht werden, so dass er dann auf den
> nächsten Modi zeigt. Oder ich übersehe wiedermal einen Fehler den jeder
> auf Anhieb sieht ;)


Tu dir selbst einen Gefallen und verwende konstant dimensionierte 
Arrays.
1
struct game {
2
  uint16_t  member1;
3
  uint16_t  member2;
4
  uint16_t  used_entries;
5
  uint8_t   array[30];
6
};
7

8
struct game Games[] PROGMEM = {
9

10
   { 5, 8,   3, { 10, 20, 30 } },
11
   { 0, 1,   5, { 1, 2, 3, 4, 5 } },
12
};

Da Dynamik reinzubringen lohnt nicht (Flash hast du genug) und im SRAM 
mit dynamischer Allokierung rumzumurksen noch viel weniger.

Du bist noch nicht soweit um da mit Verpointerungen komplexere 
Datenstrukturen aufzubauen. Ganz abgesehen lohnt das nicht wegen 7 
Integer. Da kostet dich die Verwaltung mehr, als du mit dynamisch 
verpointerten Arrays sparen kannst.

PS: Zum Einlesen aus dem Flash baust du dir eine pgm_read_block Funktion 
nach dem Vorbild der sinngemäß gleichnamigen eeprom Funktion. Du willst 
das so benutzen
1
....
2

3
  struct game  currentGame;
4

5
  pgm_read_block( &currentGame, Games[0], sizeof( Games[0] ) );

pgm_read_block kopiert einfach die angegebene Anzahl Bytes aus dem Flash 
(adresse davon im 2.ten Argument) ins SRAM (Adresse davon im 1.ten 
Argument)
1
void pgm_read_block( void * dest, void * source, size_t byteCnt )
2
{
3
  uint8_t destPtr = (uint8_t *)dest;
4
  uint8_t srcPtr  = (uint8_t *)source;
5
  size_t i;
6

7
  for( i = 0; i < byteCnt; ++i )
8
    *destPtr++ = pgm_read_byte( srcPtr++ );
9
}
#1812968
Lesenswert?

1
    for(int i = 0; i<((temp.options << 5) & 224);i++)
Hör auf zu künsteln, wenn du nicht verstehst was das da macht.
Schreibs konventionell. Die paar Bytes für einen anständigen uint8_t 
Zähler hast du allemal. Du brauchst da nicht in einem options Feld 3 
Bits zu belegen.
OP #1812979
Lesenswert?

OK, ich gebe mich für den Anfang geschlagen, aber sobald ich das Projekt 
fertig hab würde ich es lieber so umändern wie ich es angefangen habe. 
Das ich nicht soweit bin kann schon sein, ist schließlich mein 1. 
größeres Projekt. Ich hab davor immer auf dem PC programmiert und musst 
bis auf einmal nie Speicherplatz sparen.

Ja es sind 7 Words, wo von aber von den 30 Einträge durschnittlich ein 
halber genutzt wird. 6.5  30  2 = 390 Byte einsparung. Nicht viel, 
aber dazu kommt später noch ein Array gleicher Größe aber mit Bytes. -> 
390 + 6.5 * 30 = 585 Byte. Immerhin 1/16 des Mega8.

Also ich würde mich freuen wenn es mir einmal anhand eines Beispiels 
zeigen würde, wie es geht. Es muss nicht mal auf mein Projekt 
abgerichtet sein.

Außerdem wundere ich mich immernoch, warum auf der Player2 Anzeige, wenn 
ich game_mode 1 einstelle, gamemode 2 angezeigt wird.
#1812981
Lesenswert?

Samuel K. schrieb:

> Ja es sind 7 Words, wo von aber von den 30 Einträge durschnittlich ein
> halber genutzt wird. 6.5  30  2 = 390 Byte einsparung. Nicht viel,
> aber dazu kommt später noch ein Array gleicher Größe aber mit Bytes. ->
> 390 + 6.5 * 30 = 585 Byte. Immerhin 1/16 des Mega8.

Du kriegst von Atmel kein Geld für nicht benutzten Speicher zurück.

Wenn du die Sache mit der Bitextraktion aus dem options Feld richtig 
gemacht hättest, hätt ich mich breit schlagen lassen. Aber so ist das 
momentan sinnlos. Da eröffnen wir einen Mehrfrontenkrieg, den du noch 
nicht gewinnen kannst.
Der Schlüssel: mal dir die Situation auf Papier auf.
#1813021
Lesenswert?

Samuel K. schrieb:

> 250 D:\Projekte\Atmega\Chessclock\Dev\chess.c invalid type argument of
> 'unary *' (have 'int')

Ich hab jetzt die Codestelle nicht rausgesucht.
Aber schaun wir mal, ob wir das auch nicht so klären können.

unary *
Was ist ein 'unary *'
Da geht es offenbar um den Operator *

Normalerweise bezeichnet der eine Multiplikation.

   a * b

und weil da links und rechts vom Operator jeweils ein Operand steht, 
also 2 Operanden vorhanden sind, bezeichnet man ihn als 'binary *'. Ein 
unary * ist das Gegenteil davon: Da hat der * nur 1 Operanden.

Das ganze ist wie bei -

  j = a - b;       das ist ein binary -
  j = -a;          hier handelt es sich um ein unary -

Wo kann ein unary * überhaupt vorkommen?
Na, bei der Dereferenzierung eines Pointers.

   i = *Ptr;

i erhält den Wert, auf den Ptr zeigt.

da ist ein unary * im Spiel. Und klarerweise muss Ptr auch ein Pointer 
Datentyp sein.

    i = *5;

ergibt keinen Sinn. 5 ist ein int und kein Pointer. Einen int kann man 
nicht dereferenzieren.

Nochmal zurück zur Fehlermeldung

invalid type argument of 'unary *' (have 'int')

Aha. unary * wird falsch verwendet. Wir wissen das er nur auf einen 
Pointer angewendet werden kann. Der Compiler gibt noch einen Hinweis: 
have int.

Anstelle eines Pointer Datentyps wurde also ein integer Datentyp 
angegeben.

Nach dem Vorgeplänkel sehen wir uns den Code an:
1
void pgm_read_block( void * dest, void * source, size_t byteCnt )
2
{
3
  uint8_t destPtr = (uint8_t *)dest;
4
  uint8_t srcPtr  = (uint8_t *)source;
5
  size_t i;
6

7
  for( i = 0; i < byteCnt; ++i )
8
    *destPtr++ = pgm_read_byte( srcPtr++ );
9
}

Es gibt nur einen unary *. Nämlich bei *destPtr
Und wir wissen auch schon, das destPtr ein Pointer sein müsste. Müsste?
1
  uint8_t destPtr = (uint8_t *)dest;
Hoppla! Da hab ich dann wohl einen Fehler beim Tippen gemacht und den 
Pointer * in der Definition vergessen. Beim anderen Pointer ditto.
1
void pgm_read_block( void * dest, void * source, size_t byteCnt )
2
{
3
  uint8_t* destPtr = (uint8_t *)dest;
4
  uint8_t* srcPtr  = (uint8_t *)source;
5
  size_t i;
6

7
  for( i = 0; i < byteCnt; ++i )
8
    *destPtr++ = pgm_read_byte( srcPtr++ );
9
}

Man beachte auch, wie jetzt plötzlich auch die Datentypen bei der 
Initialisierung von destPtr und SrcPtr übereinstimmen. Rechts vom = 
steht ein Ausdruck der einen Pointer ergibt, links vom = steht eine 
Variable, die ein Pointer ist.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren