Verschachtelte Progmem-Arrays auslesen

Gast #1812884
Lesenswert?

Hallo,

ich bin im Moment dabei eine Menüstruktur aufzubauen, habe dabei 
allerdings Probleme mit Arrays, welche im Progmem abgelegt sind.

Die Arrays sind:
menu[20][10], die erste Dimension ist jeweils ein ganzes Menü, die 
Zweite Dimension speichert ab dem zweiten Element den Index für das 
Array menu_entry, in welchem der Eintrag ist. Das erste Element gibt die 
Größe des Menüs in Menüpunkten an.

menu_entry[50][4], die erste Dimension ist jeweils ein Menüpunkt, die 
Zweite Dimension speichert im ersten Element den Index für das 
Stringarray, in welchem der String für diesen Menüpunkt steht.

Nun muss ich also zuerst aus dem menu-array den Index für das 
menu_entry-array holen, damit ich aus diesem den Index für das 
string-array holen kann, um dann eben diesen String anzuzeigen. 
Allerdings hakt es da ein wenig, soll heißen ich greife auf die falsche 
Adresse zu (komische Zeichen). Übrigens habe ich erstmal nur das 
menu-array in den Progmem gelegt, weil ich teilweise umsteigen wollte 
(wenn beide arrays "normal" habe ich das hingekriegt).
1
void lcd_write_menu(uint8_t p_menu)
2
  {
3

4
  PGM_P l_pgmpointer;
5
  uint8_t l_index;
6

7
  for(uint8_t i=0; i<menu[p_menu][0]; i++)
8
    {
9
    l_pgmpointer = (PGM_P) pgm_read_word( &menu[p_menu][i+1] ); //wenn ich hier statt i+1 eine konstante Zahl hinschreibe, funktioniert alles
10
    l_index = menu_entry[ pgm_read_byte(l_pgmpointer) ][0];
11
    l_pgmpointer = (PGM_P) pgm_read_word( &strd[l_index] );
12
    lcd_write_menu_entry( l_pgmpointer , i );
13
    lcd_write_line(" ",0,0);
14
    }
15
  if(selected_menu_entry>menu[p_menu][0] && selected_menu_entry<20) selected_menu_entry=menu[p_menu][0];
16
  if(selected_menu_entry>128) selected_menu_entry = 0;
17
  }
18

19
// Und hier nochmal die Arrays ausführlich, könnte man eventuell auch const machen
20

21
static uint8_t menu_entry[50][4] =
22
    {
23
      {8,0,0,0},
24
      {9,0,0,0},
25
      {10,0,0,0},
26
      {11,0,0,0},
27
      {12,0,0,0},
28
      {13,0,0,0},
29
      {14,0,0,0}
30
    };
31

32
//speichert pro Eintrag in der zweiten Dimension ein Index des Arrays menu_entry
33
//Erster Eintrag in zweiter Dimension: Anzahl der Menüpunkte
34
static uint8_t menu[20][10] PROGMEM = 
35
    {
36
      {7,0,1,2,3,4,5,6,7}
37
    };
38

39
const char strd00[] PROGMEM = "v1.0";
40
const char strd01[] PROGMEM = "Mo";
41
const char strd02[] PROGMEM = "Di";
42
const char strd03[] PROGMEM = "Mi";
43
const char strd04[] PROGMEM = "Do";
44
const char strd05[] PROGMEM = "Fr";
45
const char strd06[] PROGMEM = "Sa";
46
const char strd07[] PROGMEM = "So";
47
const char strd08[] PROGMEM = "Wecker 1";
48
const char strd09[] PROGMEM = "Wecker 2";
49
const char strd10[] PROGMEM = "Wecker 3";
50
const char strd11[] PROGMEM = "Wecker 4";
51
const char strd12[] PROGMEM = "Wecker 5";
52
const char strd13[] PROGMEM = "Nacht-Modus";
53
const char strd14[] PROGMEM = "Erweitertes Menü";
54
const char strd15[] PROGMEM = "An";
55
const char strd16[] PROGMEM = "Aus";
56

57

58
PGM_P strd[] PROGMEM =
59
  {
60
  strd00, strd01, strd02, strd03, strd04, strd05, strd06, strd07, strd08, strd09, strd10, strd11, strd12, strd13, strd14, strd15, strd16
61
  };

Hoffe ihr könnt mir helfen.
#1812988
Lesenswert?

Christian schrieb:
> l_pgmpointer = (PGM_P) pgm_read_word( &menu[p_menu][i+1] ); //wenn ich hier 
statt i+1 eine konstante Zahl hinschreibe, funktioniert alles

Probier statt
"(&menu[p_menu][i+1])"

mal die Version ohne doppelte Adressauflösung:

"(menu[p_menu] + i + 1)"

Dadurch steht im Argument von pgm_read_word() gleich eine Adresse. Ein 
verschachteltes Array ist ja eigentlich nix anderes als ein 
Pointer-Pointer auf ein Word. Die werden vom Compiler nur voneinander 
Unterschieden, damit der Benutzer/Programmierer besser zurechtkommt.

Ich vermute nämlich eine "falsche" Makro-Umsetzung innerhalb von 
pgmspace, wobei das doch eigentlich auch für zweidimensionale Arrays 
gehen sollte?!

mfg mf
Gast #1813014
Lesenswert?

Hm schonmal besser als vorher.

Das Menü flimmert jetzt allerdings, er zeigt einmal auf allen 
Menüpunkten "Wecker 4" an, was heißen würde er nimmt menu_entry[3][0] 
und menu[0][4], weil hier ja der Index 3 drinsteht.
Allerdings flimmern hinter diesen "Wecker 4" Schriftzügen komische 
Zeichen, also wieder falsche Speicheradressierung. Ich weiß nur nicht wo 
das jetzt herkommt...
Gast #1813345
Lesenswert?

Ja, war ein kleiner Fehler, jetzt flimmerts nicht mehr, aber ich bekomme 
immer noch nicht die richtige Anzeige.

Die Funktion sieht jetzt so aus:
1
void lcd_write_menu(uint8_t p_menu)
2
  {
3

4
  PGM_P l_pgmpointer;
5
  uint8_t l_index;
6

7
  for(uint8_t i=0; i<pgm_read_byte(menu[p_menu]); i++)
8
    {
9
    l_pgmpointer = (PGM_P) pgm_read_word( menu[p_menu]+1+i );
10
    l_index = menu_entry[ pgm_read_byte(l_pgmpointer) ][0];
11
    l_pgmpointer = (PGM_P) pgm_read_word( &strd[l_index] );
12
    lcd_write_menu_entry( l_pgmpointer , i );
13
    lcd_write_line(" ",0,0);
14
    }
15
  if(selected_menu_entry>pgm_read_byte(&menu[p_menu]) && selected_menu_entry<20) selected_menu_entry=pgm_read_byte(&menu[p_menu]);
16
  if(selected_menu_entry>128) selected_menu_entry = 0;
17
  }

Wieso muss vor dem menu[p_menu] eigentlich kein Adress-Operator, ohne 
diesen wird doch eigentlich der Inhalt dieses Array-Eintrages 
ausgegeben, oder nicht? Trotzdem scheint es so (eher) richtig zu sein, 
obwohl das auch so nicht funktioniert.

Erster Eintrag ist Wecker 1, dann komische Zeichen, dann Wecker 4, dann 
komische Zeichen, dann Wecker 2, dann zwei Einträge komische Zeichen.
Also zwischendurch wieder falsche Adresse.
Um auf diese Strings zu kommen müsste man zuerst menu[0][1], dann 
menu[0][4] und dann menu[0][2] auslesen, wenn man die komischen Zeichen 
mal weg lässt.
Irgendwas passt nicht...
#1813370
Lesenswert?

Christian schrieb:

> Irgendwas passt nicht...

Da passt vieles nicht

Das waren die Daten
1
static uint8_t menu_entry[50][4] =
2
    {
3
      {8,0,0,0},
4
      {9,0,0,0},
5
      {10,0,0,0},
6
      {11,0,0,0},
7
      {12,0,0,0},
8
      {13,0,0,0},
9
      {14,0,0,0}
10
    };
11

12
static uint8_t menu[20][10] PROGMEM = 
13
    {
14
      {7,0,1,2,3,4,5,6,7}
15
    };

was es mit der menu Variablen auf sich hat, hat ich ehrlich gesagt nicht 
wirklich auf Anhieb durchschaut. Für meinen Geschmack verwendest du da 
ein wenig zu oft zu sehr unstrukturierte uint8_t Arrays, als ob structs 
nie erfunden worden wären
1
void lcd_write_menu(uint8_t p_menu)
2
  {
3

4
  PGM_P l_pgmpointer;
5
  uint8_t l_index;
6

7
  for(uint8_t i=0; i<pgm_read_byte(menu[p_menu]); i++)

Schreibs aus:   & menu[p_menu][0]

menu ist ein 2D Array. Und da es sich nicht ändert, ist es eine gute 
Idee diesen Wert erst einmal vorab zu bestimen (Dein { Schema macht mich 
wahnsinng, das du da noch siehst welche } zu welcher { gehört ... 
Respekt )
1
void lcd_write_menu(uint8_t p_menu)
2
{
3
  PGM_P l_pgmpointer;
4
  uint8_t l_index;
5

6
  uint8_t entryCount = pgm_read_byte( &menu[p_menu][0] );
7

8
  for( uint8_t i = 0; i < entryCount; i++ )

Sicherheitshalber auch kontrollieren (mit einer Ausgabe) ob da 
tatsächlich die 7 daher kommen.

Wie gehts weiter?

     {7,0,1,2,3,4,5,6,7}

nach der 7 stehen dann die 7 Nummern der Menüpunkte. Die liest du mit
1
    l_pgmpointer = (PGM_P) pgm_read_byte( menu[p_menu]+1+i );

wieder: Schreibs aus! Ausserdem kriegst du von dort keinen Pointer. Wenn 
du die Zahlen der Reihe nach ausliest, hast du erst mal nur die Zahlen
1
     entryIndex = pgm_read_byte( &menu[p_Menu][i+1] );

Mit diesem Index kannst du jetzt ins menu_entry Array gehen und dort die 
Zeile auswählen. Spalte 0 sagt dir welche Textnummer anzuzeigen ist
1
     textIndex = pgm_read_byte( &menu_entry[ entryIndex ][0] );

mit diesem text Index gehts dann weiter ins strd Array (das diesmal 
Pointer enthält) aus dem man den Pointer zum Text bekommt
1
     textPointer = pgm_read_word( &strd[ textIndex ] );

und mit diesem Pointer kann dann der Text ausgegeben werden.


1
void lcd_write_menu(uint8_t p_menu)
2
{
3
  uint8_t entryIndex, textIndex;
4
  PGM_P textPointer;
5

6
  uint8_t entryCount = pgm_read_byte( &menu[p_menu][0] );
7

8
  for( uint8_t i = 0; i < entryCount; i++ )
9
  {
10
    entryIndex = pgm_read_byte( &menu[p_Menu][i+1] );
11
    textIndex = pgm_read_byte( &menu_entry[ entryIndex ][0] );
12
    textPointer = (PGM_P)pgm_read_word( &strd[ textIndex ] );
13

14
    lcd_write_menu_entry( l_pgmpointer , i );
15
  }
16

17
  ...
18
}

Wenn du den Überblick verlierst, dan mal dir deine Datenstruktur auf und 
verbinde die Einzelteile mit Pfeilen, die dich bei der Programmierung 
leiten, was du von wem bekommst und was du damit tun musst.

Noch ein Hinweis:
Man kann sich Indizes (als uint8_t) auch ganz einfach auf einem LCD 
ausgeben lassen um zu kontrollieren, ob man das richtige gelesen hat 
oder nicht. Alle Indizes müssen genau so aus den entsprechenden Arrays 
gelesen werden, wie du sie beim Aufbau der Datenstruktur angegeben hast. 
Lediglich Pointerwerte sind eher sinnlos beim Ausgeben.

PS: was sagt der Prefix l_ aus?
Sag jetzt bitte nicht, das der eine lokale Variable kennzeichnet.
Gast #1813386
Lesenswert?

Hm, deine Version funktioniert aber auch nicht wie gewollt, es zeigt 
mehrmals den Text aus strd[0] an, und dann mehrmals den Text aus 
strd[15].

Und eigentlich wollte ich das ganze mit structs machen, allerdings gab 
es dabei andauernd Fehlermeldungen, also hab ichs einfach mit ner 
zweiten Dimension gelöst.

Und ja, das l_ zeigt eine lokale Variable an. Ich weiß nicht wie das im 
Mikrocontroller-Bereich gehandhabt wird, aber vorher habe ich mit Basic 
schon für den PC programmiert, da wurde das so gemacht. Wobei Basic und 
C natürlich auch wieder nen Unterschied ist...
Gast #1813420
Lesenswert?

Ja, ist mir klar, ist schon schwer genug fremde Codes zu analysieren, 
selbst wenn man den gesamten Code vorliegen hat, weil die 
Programmierarten/-style schon sehr unterschiedlich sind.

Und den Fehler hab ich auch durch Ausgeben der Indizes auf dem LCD 
gefunden. Das mache ich normal auch immer, nur vorher hab ich erst nur 
(fälschlicherweise) Pointer verwendet, wo ja eine Ausgabe auf dem 
Display normalerweise nicht viel aussagt.

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