Zeiger auf 2D Char-Array - casten

OP #365297
Lesenswert?

Hi,
ich habe mehrere zweidimensionale Byte Arrays im PROGMEM liegen.
static char xpm_folder_opened[17][17] PROGMEM = {...}

Auf diese Arrays möchte ich mit einem Zeiger zugreifen.
Das funktioniert auch und zwar mit:

char (*symbol_ptr)[17];
symbol_ptr = &xpm_folder_opened[0];
symbol_ptr zeigt nun auf die 1. Zeile im Array.
Jetzt kann ich indem ich symbol_ptr inkrementiere auf die 2. Zeile
zugreifen etc....

So nun zu dem Problem.
Die Arrays haben nicht alle die selbe "länge".
Zum Beispiel habe ich noch ein array mit weniger Spalten/Zeilen.
static char xpm_folder_closed[15][15] PROGMEM = {...}

Lasse ich nun den symbol_ptr darauf zeigen und inkrementiere ihn,
Geht das Programm 17 Zeichen nach rechts um zur nächsten Zeile zu
gelangen, anstatt 15 Zeichen.
Die zweite Zeile beginnt also beim 3. Zeichen und nicht beim 1.

Man könnte das Problem natürlich lösen, indem man alle Arrays gleich
lang macht. Aber das raubt unnötig Speicher.

Ist es vielleicht irgendwie möglich aus einem char (*symbol_ptr)[17]
ein char (*symbol_ptr)[15] während der Laufzeit zu machen?
Hab da wüste cast variationen ausprobiert. Aber der Compiler mag das
nicht ;)

Hoffe ich konnte mein Problem anständig schildern.

Gruß,
Daniel
#365298
Lesenswert?

Hallo Daniel,

Deine Cast-Versuche werden so wohl nicht klappen. Überleg mal einen
geringfügig anderen Ansatz:

ich habe so etwas ähnliches für LCD-Graphik gemacht. Statt ein
zweidimensionales Array anzulegen, habe ich X- und Y-Größe plus die
Graphikdaten in einer Struktur gespeichert.

Vorteil: die Größen des Arrays sind explizit mit in der Struktur
verpackt, zum Bearbeiten musst Du nur einen Ptr auf die Struktur
übergeben.
1
typedef struct{
2
  uint8_t x;    // Länge der Garphik in Pixeln
3
  uint8_t y;    // Höhe der Graphik in Pixeln
4
  uint8_t data[];  // Graphik-Information
5
}  graphik_typ;
6

7
typedef graphik_typ *graphik_ptr;
8

9
const graphik_typ test_graphik PROGMEM = {
10
  10,
11
  10,
12
  { 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF,
13
    0xC0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xC0}
14
};
15

16
  /*** g sei ein Pointer auf test_graphik: */
17

18
  zeilen_laenge  = pgm_read_byte(&(g->x));
19
  spalten_laenge = pgm_read_byte(&(g->y));
20

21
  /* so erhält man ein Byte mit den Indizes zeile und spalte: */
22

23
  b = pgm_read_byte(&(g->data[spalte * zeilen_laenge + zeile]));

Viele Grüße, Stefan
OP #365301
Lesenswert?

ok :)

Also meine Bildchen liegen als .xpm im PROGMEN:
1
static char xpm_folder_opened[17][17] PROGMEM = {
2
"16 12 4 1",
3
".  c #000000",
4
"   c None",
5
"+  c #F0FF80",
6
"@  c #5B5746",
7
"   ....         ",
8
"  .++++.        ",
9
" .++++++.       ",
10
".............   ",
11
".@+@+@+@+@+@.   ",
12
".+@+............",
13
".@+.+++++++++++.",
14
".+@.++++++++++. ",
15
".@.++++++++++.  ",
16
".+.+++++++++.   ",
17
"..+++++++++.    ",
18
"...........     " };
Da möchte ich eigentlich auch nicht groß was drann ändern.
Ich habe mir nun eine Funktion geschrieben, die diese xpm Bilder auf
einem Display ausgeben.

Desweiteren möchte ich das diese Funktion dynamisch auf die Bilder
zugreifen kann.
Rufe ich also in der main() auf:
1
print_xpm(20, 30, 0, FOLDER_CLOSED);
Soll er der "geöffneten Ordner" zeichnen.
Rufe ich
1
print_xpm(20, 50, 0, FOLDER_OPENED);
 auf,
soll er den "geschlossenen Ordner" zeichnen.

Dafür hab ich in meiner xpm.c folgendes stehen:
1
   char (*symbol_ptr)[17];
2

3
  
4
  if(symbol == FOLDER_OPENED)   
5
    symbol_ptr = &xpm_folder_opened[0];    
6
  
7
  else
8
  if(symbol == FOLDER_CLOSED)   
9
    symbol_ptr = &xpm_folder_closed[0];

Dann lese ich den Array Zeilenweise mit memcpy_P ein.
Das geht allerdings nur solange gut, wie jede Zeile im Array 17 Zeichen
hat.
Gibt es echt keine Möglichkeit dem Compiler zu sagen, dass er jetzt nur
noch 15bytes im Speicher höhere gehen soll, anstatt 17 Zeichen, um mit
symbol_ptr++; zur nächsten Zeile zu gelangen?
OP #365302
Lesenswert?

>>Das geht allerdings nur solange gut, wie jede Zeile im Array 17
Zeichen hat.

Öhm nur damits niemanden verwirrt ;)
Logisch das jede Zeile in einem Array das ich mit char x[17][17];
deklariere 17 Zeichen hat.
Ich meinte, das es nur geht wenn bei jedem Array die Zeichen pro Zeile
17 betragen.
#365303
Lesenswert?

>Gibt es echt keine Möglichkeit dem Compiler zu sagen, dass er jetzt
>nur noch 15bytes im Speicher höhere gehen soll, anstatt 17 Zeichen,
>um mit symbol_ptr++; zur nächsten Zeile zu gelangen?

Nein, gibt es nicht. Woher soll der Compiler diese Information auch
bekommen? Die müsste ja in der Variable symbol_ptr mit drinstecken.

Ich würde symbol_ptr als Ptr auf char definieren und zusätzlich in
einer Variable die Länge der Zeile mitführen:
1
  symbol_ptr   = &xpm_folder_opened[0][0];
2
  symbol_zsize = 17;
3
  ...
4
  print_xpm(20, 50, 0, symbol_ptr, symbol_zsize);
Statt:
1
  symbol_ptr++
schreibst Du jetzt:
1
  symbol_ptr += symbol_zsize);

Eine andere Alternative wäre, Deine Arrays etwas anders zu definieren:
statt als
zweidimensionales Array
als
Array aus Pointern auf Strings.

Das würde Einiges an Speicher sparen, wenn die Zeilen nicht immer
komplett gefüllt sind, aber auch einige Programmumbauten erforden ...

Viele Grüße, Stefan
OP #365304
Lesenswert?

Danke!
Also die zweite Methode erscheint mir sehr sinvoll. Dann könnte ich
auch die transparenten Pixel an den Rändern rauslassen.

Normalerweise liegen die xpm's auch als char *x[] {...} vor,
allerdings hab ich es damit überhaupt nicht gebacken bekommen.
Nunja, ich werde nach der Mittagspause noch ein wenig weiterforschen
:)

Gruß,
Daniel
#365305
Lesenswert?

Hi Daniel,

beim AVR musst Du höllisch aufpassen mit den Daten im PROGMEM. Wenn Du
ein Array aus Pointern hast, musst Du den gewünschten Pointer aus dem
Array auch mit pgm_read_word einlesen. Und mit dem so erhaltenen
Pointer kannst Du dann die eigendlichen Bytes mit pgm_read_byte lesen
...

Darüber bin ich selbst schon des öfteren gestolpert, vielleicht war es
ja auch Dein Problem.

Viele rüße, Stefan
OP #365306
Lesenswert?

OK danke für den Hinweis. Man kann den pointer aber auch für andere
PROGMEM Funktionen verwenden, oder?
...
Versteh das im Tutorial irgendwie nicht.
Ich definiere einen Zeiger. Und weise ihm mit
ptrToArray = (uint8_t*)(pgm_read_word(&pgmPointerArray[1]));
Die Adresse zu.
Wozu überhaupt der Cast? Schneidet der mir nich von der 16Bit Adresse
8Bit weg? Bin leicht verwirrt ;)
OP #365309
Lesenswert?

Soviel zur Theorie.. Irgendwas mag hier immernoch nicht so recht
stimmen.
Nichtmals ein simples auslesen eines einziges Bytes funktioniert :(
1
  char blub;
2
  uint8_t *pgm_ptr;
3
 
4
  pgm_ptr = (char*)pgm_read_word(&xpm_folder_closed[0]);  
5
  blub = pgm_read_byte(pgm_ptr);
6

7
  put_char(20, 30, blub, 0, LCD_WHITE, LCD_RED);

Merkwürdig ist, dass er nicht irgend einen Mist ausgibt,
sondern das Display leer bleibt.

Ein simples put_char(20, 30, 'C', 0, LCD_WHITE, LCD_RED);
gibt mir auch ein 'C' auf dem Display aus.
Sollte doch eigentlich alles stimmen.

Gruß,
Daniel
OP #365311
Lesenswert?

Guten Morgen :)

die Null wird tadellos angezeigt.

xpm_folder_closed sieht so aus:
1
const char *xpm_folder_closed[] PROGMEM = {
2
"16 12 3 1",
3
".  c #000000",
4
"   c None",
5
"X  c #F0FF80",
6
"   ....         ",
7
"  .XXXX.        ",
8
" .XXXXXX.       ",
9
".............   ",
10
".XXXXXXXXXXX.   ",
11
".XXXXXXXXXXX.   ",
12
".XXXXXXXXXXX.   ",
13
".XXXXXXXXXXX.   ",
14
".XXXXXXXXXXX.   ",
15
".XXXXXXXXXXX.   ",
16
".XXXXXXXXXXX.   ",
17
".............   "};

Gruß,
Daniel
OP #365312
Lesenswert?

So, ich hab es jetzt anders gelöst.
Basiert auf Deinem ersten Vorschlag. Nur dass ich die Zeilenlänge nicht
beim Funktionaufruf übergebe, sondern es aus dem xpm file aus der 1.
Zeile auslese.
Das Ergebnis ist eigentlich genau das, was ich ursprünglich wollte.

Vielleicht fällt Dir ja noch ein, was das Problem mit dem pointer Array
sein könnte. Wäre jedenfalls mal interessant zu wissen, wieso das nicht
funktioniert :)

Gruß,
Daniel
Moderator Persönliche Seite #365317
Lesenswert?

> Warum sollten Zeiger am Start im PROGMEM festgelegt werden, das
> hatte mich eben auch gewundert.

Man spart RAM, aber natürlich nur 2 Bytes pro Zeiger.  Das Auslagern
der Strings in den ROM würde deutlich mehr sparen...

Die Lösung des Problems wird schon darauf hinsaus laufen, was Stefan
in

http://www.mikrocontroller.net/forum/read-2-345349.html#345432

geschrieben hat: die XPM-Arrays komplett in den ROM, und dann für
jedes XPM-Bildchen einen Descriptor bauen, der neben der Adresse noch
die Dimensionen des Array mit speichert.  Dieser Descriptor wird dann
an die Verarbeitungsfunktion übergeben (Zeiger auf struct).

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