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
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.
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)
ok :)
Also meine Bildchen liegen als .xpm im PROGMEN:
1
staticcharxpm_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?
>>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.
>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
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
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
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 ;)
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 ;)
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
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
> 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!
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.
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
> 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).
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.