mikrocontroller.net

Forum: Compiler & IDEs Zeiger auf 2D Char-Array - casten


Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht 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.
typedef struct{
  uint8_t x;    // Länge der Garphik in Pixeln
  uint8_t y;    // Höhe der Graphik in Pixeln
  uint8_t data[];  // Graphik-Information
}  graphik_typ;

typedef graphik_typ *graphik_ptr;

const graphik_typ test_graphik PROGMEM = {
  10,
  10,
  { 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF,
    0xC0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xC0}
};

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

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

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

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

Viele Grüße, Stefan

Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Stefan.
Leider hilft mir das auch nicht wirklich weiter.
Ich hole mal etwas mehr aus:
(Bevor ich das tue, müsste ich wissen wie man hier "code" einfügt)
test

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
<eckige Klammer auf> c <eckige Klamme zu>
 ... hier steht Dein Code
<eckige Klammer auf> /c <eckige Klamme zu>

so long, Stefan

Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok :)

Also meine Bildchen liegen als .xpm im PROGMEN:
static char xpm_folder_opened[17][17] PROGMEM = {
"16 12 4 1",
".  c #000000",
"   c None",
"+  c #F0FF80",
"@  c #5B5746",
"   ....         ",
"  .++++.        ",
" .++++++.       ",
".............   ",
".@+@+@+@+@+@.   ",
".+@+............",
".@+.+++++++++++.",
".+@.++++++++++. ",
".@.++++++++++.  ",
".+.+++++++++.   ",
"..+++++++++.    ",
"...........     " };
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:
print_xpm(20, 30, 0, FOLDER_CLOSED);
Soll er der "geöffneten Ordner" zeichnen.
Rufe ich
print_xpm(20, 50, 0, FOLDER_OPENED);
 auf,
soll er den "geschlossenen Ordner" zeichnen.

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

  
  if(symbol == FOLDER_OPENED)   
    symbol_ptr = &xpm_folder_opened[0];    
  
  else
  if(symbol == FOLDER_CLOSED)   
    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?

Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht 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:
  symbol_ptr   = &xpm_folder_opened[0][0];
  symbol_zsize = 17;
  ...
  print_xpm(20, 50, 0, symbol_ptr, symbol_zsize);
Statt:
  symbol_ptr++
schreibst Du jetzt:
  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

Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht 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 ;)

Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ahhhh, hatte grad nen Gedankenblitz. Den Cast braucht man um zu sagen,
dass der zurückgelieferte 16Bit Wert für den Char Pointer genutzt
werden soll. Oder so ähnlich ;)

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genau.
Du sagst mit dem cast dem Compiler:
"Interpretiere den von pgm_read_word gelieferten 16-Bit-Wert als
Pointer auf ein uint8_t"

Autor: Daniel N. (bipak)
Datum:

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

  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

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
poste mal, wie xpm_folder_closed jetzt genau definiert ist.

Gibt der folgende Code eine Null korrekt aus?
const uint8_t PROGMEM flash_null = 0x30;

  blub = pgm_read_byte(&flash_null);
  put_char(20, 30, blub, 0, LCD_WHITE, LCD_RED); 
Gruß, Stefan

Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Morgen :)

die Null wird tadellos angezeigt.

xpm_folder_closed sieht so aus:
const char *xpm_folder_closed[] PROGMEM = {
"16 12 3 1",
".  c #000000",
"   c None",
"X  c #F0FF80",
"   ....         ",
"  .XXXX.        ",
" .XXXXXX.       ",
".............   ",
".XXXXXXXXXXX.   ",
".XXXXXXXXXXX.   ",
".XXXXXXXXXXX.   ",
".XXXXXXXXXXX.   ",
".XXXXXXXXXXX.   ",
".XXXXXXXXXXX.   ",
".XXXXXXXXXXX.   ",
".............   "};

Gruß,
Daniel

Autor: Daniel N. (bipak)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gib doch nicht so schnell auf.

Ich frage mich, was
#
const char *xpm_folder_closed[] PROGMEM = {.....

soll. pointer + array deklaration? Frage mich, was der Compiler damit
anfängt.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich frage mich, was

>      const char *xpm_folder_closed[] PROGMEM = {.....

> soll.

Es deklariert ein array of pointers, was ist daran
ungewöhnlich?

Allerdings (und das ist sicher nicht im Sinne des Erfinder):
das Array ist im progmem, die Strings, deren Zeiger ins Array
eingetragen werden, stehen aber im RAM!

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aahh, verstehe. Danke Jörg.
Habe wegen dem PROGMEM eigentlich nur Zeichenketten erwartet. Warum
sollten Zeiger am Start im PROGMEM festgelegt werden, das hatte mich
eben auch gewundert.

Autor: Daniel Nöthen (bip)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aha...
Das könnte wohl die Ursache dafür sein, dass ich merkwürdige Effekte
beobachte ;)
Wie sähe denn eine korrekte Definition aus?
Muss ich die Strings mit PSTR("blabla") ins Array schreiben?

Gruß,
Daniel

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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...

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

Autor: Simon K. (simon) Benutzerseite
Datum:

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

Das ist mir ja klar, allerdings kann dann nicht mehr so schnell auf
diese Pointer zugegriffen werden. Von daher macht das sowieso nicht
soviel Sinn.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> ...allerdings kann dann nicht mehr so schnell auf
> diese Pointer zugegriffen werden.

Na ja, 6 Takte Zugriffszeit statt 4 Takte, was soll's.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ist defintiv langsamer! :-)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aber nur, wenn der RAM kein externer ist, sonst braucht's dort
auch noch einen Zyklus mehr pro Zugriff. ;-)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.