Forum: Compiler & IDEs Zweidimensionales Pointer-Array


von MuesLee (Gast)


Lesenswert?

Hallo zusammen,

ich habe mir für mein LCD eine Menüstruktur aufgebaut, die 
(Unter-)kategorien, Attribute, Auswahlgruppen (Radiogroups) und 
Funktionen enthalten kann. Die jeweiligen Typen sind dabei in Structs 
mit ihren jeweiligen Attributen gekapselt (unten folgt gleich ein Bsp). 
Um nun diese Typen in beliebiger Reihenfolge auflisten zu können habe 
ich einen Wrapper (ebenfalls ein Struct) geschrieben, in welchem der Typ 
angegeben wird, sowie ein Pointer zu dem entsprechenden Eintrag. Das 
ganze hat bislang auch super funktioniert.

Hier mal ein Bsp für solch einen Eintrag:

//Struct-Definition zu einem Menü-Attribut
struct attr {
   char name[20];
   struct entry *parent;
   char unit[4];
   long int lbound;
   long int ubound;
   unsigned long int *value;};
typedef struct attr attrib;
//
//
//
attrib at1 = {
   "Temperatur1", //Name
   &root, //Parent
   "\x01\x43", //Einheit
   0, //Min-Value
   50000, //Max-Value
   &temperatur1}; //Speicher

wrapper wr1 = {ATTRIBUTE,NULL,&at1,NULL,NULL};
//

Momentan kämpfe ich jedoch an einem sehr seltsamen Phänomen. Ich habe 
wie oben beschrieben auch Radiogroups auf diese Weise realisiert, die 
dann je nach Auswahl Menüeinträge ein- bzw. ausblenden. Demzufolge habe 
ich in die zugehörige Struct ein zweidimensionales Array aus Pointern 
auf die zugehörigen Wrapperknoten gelegt:

//Struct-Definition zur Radiogroup
struct select {
   char name[20];
   struct entry *parent;
   char selections[20][20];
   struct subnode *pWr[20][20];
   uint8_t *active;};
typedef struct select selection;

Je nach Auswahl (*active) wird dann also die entsprechende "Zeile" 
dieses 2D-Arrays referenziert und die dort hinterlegten Wrapper werden 
ausgeblendet. Hier ein Beispiel für solch eine Radiogroup:

selection s1 = {
   "Auswahl1", //Name
   &root, //Parent
   {"Aus","T-Gesteuert","Z-Gesteiert"}, //Bezeichner der RGroup
   {{&wr9},{&wr1,&wr4},{&wr11}}, //Zugehörige Wrapper
   &selection1}; //Aktuelle Auswahl

So viel zu dem Szenario. Jetzt das Problem. Wenn eine oder zwei solche 
Selections in das Menü mit eingebunden werden funktioniert alles tiptop 
- nur wenn mann mehrere Einträge dieser Art hinzufügt geht plötzlich gar 
nichts mehr. Nicht mal mehr die Main() wird aufgerufen? Ich habe nun 
schon Stunden lang den Code durchgesehen und werde einfach nicht fündig 
wo der Fehler sein könnte. Das Phänomen tritt nur bei diesen Selections 
auf. Ich kann also z.B. 4, 5 Attribute hinzufügen ohne Probleme. Da sich 
die Selection eigentlich nur durch dieses 2D-Array grundlegend von den 
anderen Eintragstypen unterscheidet wollte ich nun fragen, ob es 
vielleicht daran liegen könnte? Ich muss dazu gestehen, ich bin noch 
nicht so lange AVRler.

Viele Dank schon mal :-)

von Karl H. (kbuchegg)


Lesenswert?

Bau mal ein komplettes, minimales Programm und poste dieses.
Dann lässt sich leichter nachvollziehen was du da eigentlich
gemacht hast.

Und im Debugger ausprobieren kann man das dann auch.

von MuesLee (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

anbei mal ein kleines Programm zur Veranschaulichung (ps ich arbeite mit 
einem Mega644 / AVR Studio Service Pack 4 (build 498)). Ich hab in der 
Datei den Block angegeben, ab welchem der Fehler auftritt:

Im Debugger lautet die Meldung wie folgt:
AVR Simulator: Invalid opcode 0xffff at address 0x0078f9
AVR Simulator: Uninitialized stack pointer used at 0x0075

Viele Grüße
Peter

von Karl H. (kbuchegg)


Lesenswert?

Du überläufst deinen Stack.

Für jedes 'selection' Objekt sind 1224 Bytes fällig. Da sind
die 4K, die du im SRAM hast, schnell aufgebraucht.

struct select {
  char name[20];
  struct entry *father;
  char selections[20][20];
  struct subnode *pWr[20][20];
  uint8_t *active;
};

selections  20*20     400 Bytes
pwr 20*20*2           800 Bytes
                     ----
                     1200 Bytes

Du wirst dir da was anderes überlegen müssen.

von MuesLee (Gast)


Lesenswert?

Hallo Karl-Heinz,

vielen Dank für die aufschlussreiche Antwort. Wäre es sinnvoll/denkbar 
das ganze über PROGMEM zu lösen?

Viele Grüße
Peter

von Karl H. (kbuchegg)


Lesenswert?

PROGMEM könnte gehen.
Die Frage die ich allerdings stellen würde ist:
Wie voll werden denn deine Arrays werden? Sind in den
Arrays 90% der Positionen mit anderen Werten als 0 besetzt?
Wenn ja: Dann sind 2D Arrays eine vernünftige Lösung. Du musst
jetzt nur noch einen Weg finden, die in den AVR zu quetschen.
PROGMEM könnte da helfen.

Wenn nicht: Durchsuche mal das Web nach 'sparse Arrays'. Es
gibt Techniken wie man bei konzeptionellen 2D Arrays die
nur 'spärlich besetzt' sind, den nicht benutzten Speicher
auf Kosten von mehr Verwaltungsinformation einsparen kann.

Ich hab jetzt nicht im Detail durchdacht, was du da vor hast.
Da die Arrays aber quadratisch sind: Kann es sein, dass sie
um die Diagonale spiegelsymetrisch sind? Dass also
selections[i][j] == selections[j][i]

Auch in dem Fall könnte man noch ein bischen einsparen.
Sowohl in dem Fall als auch bei sparse Arrays kommt man
aber um dynamische Speicherallokierung nicht mehr rum.

von MuesLee (Gast)


Lesenswert?

Hallo,

also, ich hab mir jetzt folgendes überlegt: Ich lege ein weiteres Struct 
mit den Pointern auf die jeweiligen Wrapper sowie die Auswahlbezeichner 
an, welches komplett im Flash liegt. Das ganze habe ich mal so 
auscodiert:

###########

struct select {
   char name[20];
   struct entry *father;
   struct invisib *fadeout; //Pointer auf Struct im Flash
   uint8_t *active;};
typedef struct select selection;

struct invisib {
  char selections[20][20]; //Auswahlbezeichner
  struct subnode *pWr[20][20];}; //Wrapper die ausgeblendet werden
typedef const PROGMEM struct invisib invisible;

###########

Ein Menüeintrag einer solchen Auswahl könnte folglich so aussehen:

###########

node conf = {"Konfiguration",&root,{}};
wrapper wr1 = {CATEGORY,&conf,NULL,NULL,NULL};

const PROGMEM invisible isel1 = {{"Test1","Test2"},{{&wr1},{}}};

selection sel1 = {"Auswahl",&root,&isel1,&selection1};
wrapper wr2 = {SELECTION,NULL,NULL,NULL,&sel1};

###########

Ich habe jetzt jedoch noch folgendes Problem. Wie lese ich dieses Struct 
wieder aus dem Flash aus, bzw. greife auf die Indizes der Arrays in 
diesem Struct zu? Wenn ich das richtig gesehen habe gibt es in 
<pgmspace.h> keine "pgm_read_block" o.ä. wie es bei eeprom.h der Fall 
ist.

Viele Grüße
Peter

von Karl H. (kbuchegg)


Lesenswert?

> Wenn ich das richtig gesehen habe gibt es in
> <pgmspace.h> keine "pgm_read_block" o.ä. wie es bei eeprom.h der Fall
> ist.

Kann man aber leicht selbst machen.

von Werner B. (Gast)


Lesenswert?


von MuesLee (Gast)


Lesenswert?

Hallo,

ich hab das ganze jetzt über memcpy_P gelöst. Allerdings funktioniert 
der Zugriff auf das Pointer-Array nicht. Mit den Strings gehts im 
Debugger?

char flashstring[20];
wrapper* flashwrapper;

void getFlash(uint8_t mode, uint8_t i, uint8_t j, invisible *readable)
{
  if (mode == 0)
    memcpy_P(flashstring,
             readable->selections[i],
             sizeof(readable->selections[i]));
  if (mode == 1)
    memcpy_P(flashwrapper,
             readable->pWr[i][j],
             sizeof(readable->pWr[i][j]));
}

Außerdem kommt beim Kompilieren noch folgender Warnhinweis:
... initialization discards qualifiers from pointer target type!
für die oben stehende Zeile:
selection sel1 = {"Auswahl",&root,&isel1,&selection1};

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.