Forum: Mikrocontroller und Digitale Elektronik Verschachtelte Progmem-Arrays auslesen


von Christian (Gast)


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.

von Achim M. (minifloat)


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

von Christian (Gast)


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...

von Karl H. (kbuchegg)


Lesenswert?

Christian schrieb:

> Das Menü flimmert jetzt allerdings

Ich hab dein Programm nicht analysiert.
Aber Flimmern auf einem LCD ist meistens ein Zeichen für
   Löschen + Neu beschreiben
Und das ständig

von Christian (Gast)


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...

von Karl H. (kbuchegg)


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.

von Christian (Gast)


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...

von Christian (Gast)


Lesenswert?

Ok, hat sich erledigt, das Array menu_entry war noch nicht im Flash 
Bereich, weil ich ja schrittweise umstellen wollte...

Aber jetzt ist mir wenigstens alles klar, vielen Dank!

von Karl H. (kbuchegg)


Lesenswert?

Christian schrieb:
> 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].

Tja. Im Gegensatz du dir muss ich blind programmieren.
Du hast die einzelnen Indizes: Gib sie dir am LCD aus.

> Und ja, das l_ zeigt eine lokale Variable an.

Spar dir das.

von Karl H. (kbuchegg)


Lesenswert?

Christian schrieb:
> Ok, hat sich erledigt, das Array menu_entry war noch nicht im Flash
> Bereich, weil ich ja schrittweise umstellen wollte...

OK. Hab ich auch übersehen, dass da ein PROGMEM fehlt.
Wie gesagt: Ich muss blind programmieren. Direkt hier im Forum 
Eingabefeld. Da passiert schon mal das eine oder andere

von Christian (Gast)


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.

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.