Forum: Compiler & IDEs PROGMEM und 2 dimensionaler array


von Daniel (Gast)


Lesenswert?

Hallo,

ich komme mit dem Tutorial nicht ganz zurecht.
Ich habe einen 2 dimensionalen Array, den ich gerne im Flash ablegen 
würde:
1
const uint16_t 2dArray [dim1] [dim2] = { {0},{0}};

Wenn ich jetzt in meinem Programm mir eine Zeile des Arrays z.B. 2dArry 
[0] [von 0 bis 100] (Annahme Vorbelegung ungleich 0 wie hier) auslesen 
möchte, wie mache ich das?

Das Auslesen soll z.B. einfach in einen eindimensionalen Array erfolgen:
1
uint16_t 1dArray [100] = {0};

aus dem Tutorial werde ich nicht ganz schlau:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Wort_lesen

Benötige ich hierfür Zeiger?

Merci und LG
Daniel

: Verschoben durch Moderator
von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

1
const char ein[] PROGMEM    = "ein";
2
const char zwei[] PROGMEM   = "zwei";
3
const char drei[] PROGMEM   = "drei";
4
const char vier[] PROGMEM   = "vier";
5
const char fuenf[] PROGMEM  = "fuenf";
6
const char sechs[] PROGMEM  = "sechs";
7
const char sieben[] PROGMEM = "sieben";
8
const char acht[] PROGMEM   = "acht";
9
const char neun[] PROGMEM   = "neun";
10
11
const char* EINSER[9] PROGMEM = {ein, zwei, drei, vier, fuenf, sechs, sieben , acht, neun};

Zugriff:
1
funktion_P((char*)pgm_read_word(&EINSER[einer-1]));

Oder wo is das problem?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Daniel schrieb:
> Hallo,
>
> ich komme mit dem Tutorial nicht ganz zurecht.
> Ich habe einen 2 dimensionalen Array, den ich gerne im Flash ablegen
> würde:
> const uint16_t 2dArray [dim1] [dim2] = { {0},{0}};
>
> Wenn ich jetzt in meinem Programm mir eine Zeile des Arrays z.B. 2dArry
> [0] [von 0 bis 100] (Annahme Vorbelegung ungleich 0 wie hier) auslesen
> möchte, wie mache ich das?

Als erstes wählst du einen gültigen C-Identifier und als zweitest legst 
du die Daten ins Flash:
1
#include <avr/pgmspace.h>
2
3
#define dim1 10
4
#define dim2 20
5
6
const uint16_t Array2D[dim1][dim2] PROGMEM = { {0},{0}};
7
8
uint16_t lese (size_t d1, size_t d2)
9
{
10
    return pgm_read_word (&Array2D[d1][d2]);
11
}
 
oder mit einem aktuellen avr-gcc:
 
1
#define dim1 10
2
#define dim2 20
3
4
const __flash uint16_t Array2D[dim1][dim2] = { {0},{0}};
5
6
uint16_t lese (size_t d1, size_t d2)
7
{
8
    return Array2D[d1][d2];
9
}

Martin Wende schrieb:
> const char* EINSER[9] PROGMEM = { ... };
> Oder wo is das problem?

Das ist kein 2-dimensionales Array, sondern ein 1-dimensionales 
Pointer-Array.

von Daniel (Gast)


Lesenswert?

Ich weiß nicht, ob ich ganz verstanden wurde, daher versuche ich es 
nochmal an einem Beispiel.

Angenommen mein Array2D hat im Prinzip 5 Arrays à 20 Stellen, also 
dim1=5 und dim2=20.
Nun möchte ich vom 1. Array2D alle 20 Stellen in die Variable Array1D 
einlesen, also Array2D [0] [Stelle 0 bis 20] in eine Variable Array1D 
[20] mit ebenfalls 20 Stellen lesen. Funktioniert dies so:
1
#include <avr/pgmspace.h>
2
3
#define dim1 5
4
#define dim2 20
5
6
const uint16_t Array2D[dim1][dim2] PROGMEM = { {0},{0}};
7
uint16_t Array1D [dim2] = {0};
8
9
for (uint_8t i=0, i++, i<dim2){
10
    Array1D [i] = pgm_read_word (&Array2D[0][i]);
11
}

von Daniel (Gast)


Lesenswert?

Könnte vielleicht jemand kurz darüber schauen, ob der obige 
"Gedankencode" so funktioniert?
Danke.

von Zettel (Gast)


Lesenswert?

Wie wäre es wenn Du es einfach versuchst?

Du kannst ja nicht Dein ganzes restliches Leben lang jedesmal Andere 
fragen bevor Du einen Code eintippst und den Compiler startest.
Uns (oder viele von uns) ist das einfach zu viel.

Darüberhinaus wird das Indizieren in jedem C-Buch erklärt.

Falls Du hingegen ein spezifisches Problem hast, dann formuliere es 
auch.
Aber frage nicht: Geht das so?

von Daniel (Gast)


Lesenswert?

Hallo,

danke für die Antwort. Ich kann das Programm natürlich schreiben, 
allerdings habe ich kein HyperTerminal oder LCD, welches mir den Inhalt 
der Variablen anzeigt bzw. ich so die Richtigkeit prüfen kann.
Das Programm oben kann ich durch den Compiler jagen, schon, aber das 
sagt mir noch nichts aus, ob tatsächlich in den Variablen das ankommt, 
was soll.
Nun nochmal Compiler freundlich:
1
#include <avr/pgmspace.h>
2
3
#define dim1 5
4
#define dim2 20
5
6
const uint16_t Array2D[dim1][dim2] PROGMEM = { {0},{0}};
7
uint16_t Array1D [dim2] = {0};
8
9
for (uint8_t i=0; i<dim2; i++){
10
    Array1D [i] = pgm_read_word (&Array2D[0][i]);
11
}

von Minimalist (Gast)


Lesenswert?

> allerdings habe ich kein HyperTerminal oder LCD, welches mir den Inhalt
> der Variablen anzeigt bzw. ich so die Richtigkeit prüfen kann.

Ne UART-USB Bridge gehört echt zur Minimalausrüstung, ohne die geht gar 
nix. Investier ein paar $$$ dafür, das erleichtert dir das Hobby 
ungemein!

von Minimalist (Gast)


Lesenswert?

Daniel schrieb:
> Das Programm oben kann ich durch den Compiler jagen, schon, aber das
> sagt mir noch nichts aus, ob tatsächlich in den Variablen das ankommt,
> was soll.

Und - AFAIK - im avr-studio ist ein Simulator enthalten. Einfache Sachen 
wie dein Programm kannst du damit sicher mal durchspielen.

von Daniel (Gast)


Lesenswert?

Minimalist schrieb:
> UART-USB Bridge

Ist bereits bestellt. Aber zunächst ohne wäre eine Hilfe aus dem Forum 
schön gewesen...

von Karl H. (kbuchegg)


Lesenswert?

In dem Fall ist dann nachdenken angesagt.

Angenommen, da wäre kein Flash im Spiel und du hättest ganz normale 
Arrayzugriffe.
Wie löst du dann deine Aufgabenstellung?

Du würdest wohl sowas hier schreiben
1
uint16_t Array2D[dim1][dim2] = { {0},{0}};
2
uint16_t Array1D [dim2] = {0};
3
4
...
5
6
for (uint8_t i=0; i<dim2; i++){
7
    Array1D [i] = Array2D[0][i];
8
}

Es ist leicht zu sehen, dass das genau das macht, was du vorhast. Oder?

So. Jetzt legst du Array2D ins Flash.
Als Folge davon kannst du nicht einfach mehr durch Angabe von 
Array2D[0][i] auf einen Wert zugreifen. Warum ist das so? Weil so ein 
Zugriff als Zugriff auf das SRAM aufgefasst werden würde und dort sind 
nun mal die Daten nicht. Ausnahmslos jeder Zugriff auf derartige Werte 
im Flash muss über eine Spezialfunktion gehen, der man die 
Speicheradresse angibt, von der man den Wert haben will. (Oder eben, wie 
Johann weiter oben schon geschrieben hat, man benutzt die Möglichkeiten 
der neueren gcc Versionen, die einem diese Arbeit abnehmen).

Array2D[0][i] kann man ja auch auffassen als *&Array2D[0][i]. Dh. als
  x        x ist irgendein Symbol. Es steht für etwas, zb eine Variable
  & x      nimm die Adresse von diesem x (was immer auch x ist)
  * & x    und liefere den Wert an dieser Adresse
           (vulgo: dereferenziere den Pointer)

Das ist genau das, was ja letztendes passiert, wenn du
  i = x;
schreibst. An i wird der Wert zugewiesen, der sich ergibt, wenn man an 
der Speicheradresse von x im Speicher nachsieht. Im eigentlichen Sinn 
(wenn man alle Operationen ausschreibt), dann hat man da ja geschrieben

  i = * & x;

(genau genommen steht da eigentlich:
  * & i = * & x;
denn auch i ist ja erst mal nur ein Symbol, welches für etwas steht, zb 
eine Variable. Daraus folgt aber auch, dass der * genau genommen für 
verschiedene Operationen steht, je nachdem ob er links oder rechts vom = 
auftaucht.
  links vom =     Speichere einen Wert unter der angegebenen Adresse
  rechts vom =    Liefere einen Wert von der angegebenen Adresse
Wir fassen aber beide 'Operationen' unter dem gemeinsamen Begriff 
"Dereferenzieren" zusammen, weil aus dem Zusammenhang immer implizit 
klar ist, welche der beiden möglichen Bedeutungen gemeint ist.
)

(und aus naheliegenden Gründen assoziiert der Compiler automatisch diese 
Operationskette, wenn du i = x; schreibst)

Anstatt dem Dereferenzierungs-* tritt dann eben im Falle eines Zugriffs 
im Flash die Spezialfunktion, die weiß, wie man auf das Flash korrekt 
zugreift, wenn man die Speicheradresse hat.

Also wird ganz einfach der * durch die entsprechende pgm_read_... 
Funktion ersetzt. Bei dir eben pgm_read_word, weil du weißt, dass du 
einen 16 Bit Wert haben willst. Aus

   * & x    fürs SRAM

wird also

  pgm_read_word( & x );     fürs Flash

x ist bei dir Array2D[0][i], also muss der Zugriff lauten

   pgm_read_word( & Array2D[0][i] );

Oder dann eben 'im ganzen Satz' (inklusive Verschieben des Arrays ins 
Flash)
1
const uint16_t Array2D[dim1][dim2] PROGMEM = { {0},{0}};
2
uint16_t Array1D [dim2] = {0};
3
4
...
5
6
for (uint8_t i=0; i<dim2; i++){
7
    Array1D [i] = pgm_read_word( &Array2D[0][i] );
8
}

Das alles ist völlig logisch, wenn man verstanden hat, was man da 
eigentlich tut und warum man es tut und ein ganz klein wenig darüber 
nachdenkt, wie Programmiersprachen eigentlich funktionieren.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:

> for (uint8_t i=0; i<dim2; i++){
>     Array1D [i] = Array2D[0][i];
> }

Sowas?

> #include <string.h>
> memcpy (Array1D, Array2D[0], sizeof (Array1D));

und im Flash:

> #include <avr/pgmspace.h>
> memcpy_P (Array1D, Array2D[0], sizeof (Array1D));

Vorsuaasetzung ist natürlich, daß Array1D ein Array ist und nicht nur 
ein Zeiger. In letzerem Fall ist die Längenberechnung (sizeof) 
anzupassen.

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.