Forum: Compiler & IDEs Pointer-Array auf mehrere unterArrays (PROGMEM) wie zugriff?


von Paul H. (powl)


Lesenswert?

Hi!

Habe hier zwar schon einen Thread aber der wird zu unübersichtlich.

Ich möchte ein quasi mehrdimensionales Array erstellen. Genauer genommen 
ein eindimensionales, in dem verschieden lange Ketten von Werten 
gespeichert sind.

const char scene1[] PROGMEM = { 41, 81, 65, 96 };
const char scene2[] PROGMEM = { 50, 24, 91, 36, 45 };

const char *lightshow[] PROGMEM = {
  scene1,
  scene2
};

Ist das so richtig? Ich möchtet nun in zwei ineinander verschachtelten 
for-Schleifen zunächst die Werte von scene1 nacheinander abrufen und 
dann eben die werte von scene2. Nur wie mache ich das?

pgm_read_byte(&lightshow[0][0]);

scheint leider nicht zu funktionieren. Gibt es vielleicht sogar noch 
eine geschicktere Methode um meine Daten zu speichern? Wieso muss im 
*lightshow-Array kein & vor scene1 und scene2? (Laut GCC-Tutorial 
zumindest nicht)

lg PoWl

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher wrote:
> Hi!
>
> Habe hier zwar schon einen Thread aber der wird zu unübersichtlich.
>
> Ich möchte ein quasi mehrdimensionales Array erstellen. Genauer genommen
> ein eindimensionales, in dem verschieden lange Ketten von Werten
> gespeichert sind.
>
> const char scene1[] PROGMEM = { 41, 81, 65, 96 };
> const char scene2[] PROGMEM = { 50, 24, 91, 36, 45 };
>
> const char *lightshow[] PROGMEM = {
>   scene1,
>   scene2
> };
>
> Ist das so richtig?

Kann man so machen.
Damit hast du im Speicher folgende Struktur aufgebaut


    lightshow
   +----------+                +----+----+----+----+
   |    o--------------------->| 41 | 81 | 65 | 96 |
   +----------+                +----+----+----+----+
   |    o------------+
   +----------+      |   +----+----+----+----+----+
                     +-->| 50 | 24 | 91 | 36 | 45 |
                         +----+----+----+----+----+


> Ich möchtet nun in zwei ineinander verschachtelten
> for-Schleifen zunächst die Werte von scene1 nacheinander abrufen und
> dann eben die werte von scene2.

Da wird es ein Problem geben. So wie deine konmplette Struktur
zur Zeit aufgebaut ist, kannst du die Längen der scenexx Arrays
zur Laufzeit nicht bestimmen. Die Länge des lightshow Arrays
geht noch. Aber die Zeiger dort drinnen zeigen ja jeweils auf
unterschiedlich lange scene-Arrays.

>
> pgm_read_byte(&lightshow[0][0]);


Das kann nicht gehen. Denn wenn du dir die Grafik ansiehst, dann
ist die komplette 'lightshow' ja kein monolithisches Gebilde,
sondern ist quer über den ganzen Speicher verstreut.

Um an ein Datenbyte zu kommen musst du daher:
zunächst mal den Wert von lightshow[i] holen (mittels pgm_read_word).
Laut Grafik ist das ein Pointer, der auf den Anfang des i-ten scene
Arrays zeigt. Daher musst du mit diesem Pointer dann einen weiteren
pgm_read_byte machen um an das tatsächliche Datenbyte zu kommen.

Also so was in der Art (das Problem der Array Längen überlasse
ich dir :-)
1
  const char* SceneStart;
2
  ....        SceneLength;
3
  unsigned char data;
4
5
  for( i = 0; i < LIGHTSHOW_LÄNGE; ++i ) {
6
7
    SceneStart  = pgm_readword( &lightshow[i] );
8
    SceneLength = .... ;
9
10
    for( j = 0; j < SceneLength; ++j ) {
11
      data = pgm_readByte( SceneStart + j );
12
13
      ... mach was mit data ...
14
    }
15
  }


> eine geschicktere Methode um meine Daten zu speichern? Wieso muss im
> *lightshow-Array kein & vor scene1 und scene2?

Weil die Nennung des Namens eines Arrays ohne einen angehängten Index
(also   scene1  versus  scene1[5]) automatisch als die Startadresse
des Arrays aufgefasst wird. scene1  ist also äquivalent zu &scene1[0]

von ATMega Guru (Gast)


Lesenswert?

hallo Paul,

also bei mir geht dein bespiel wunderbar
im falle von
pgm_read_byte(&lightshow[0][0]);
bekomme ich den wert 41
im falle von
pgm_read_byte(&lightshow[1][4);
bekomme ich den wert 45

alles ausprobiert auf einenm ATmega88 im simulator

g,
ATMega Guru

von Paul H. (powl)


Lesenswert?

@ Karl Heinz, wiedermal ein astreiner Post.

Habe vergessen zu erwähnen, dass die einzelnen scene-Ketten 
nullterminiert sind :-)

Also in lightshow stehen ja die Startadressen der Scene-Arrays. 
lightshow[x] enthält ja dann die Startadresse der x-ten Szene. 
lightshow[x][y] müsste demnach die Daten..? des y-ten Elements von Szene 
x enthalten. Und da pgm_read_byte eine Adresse haben möchte habe ich ein 
& davor gesetzt.

Ich dachte mir eher, der Compiler setzt das automatisch so um. Er ließt 
zuerst die Adresse der x-ten Scene und addiert dann noch die y drauf und 
ließt dann das enstprechende Element aus. Das würde doch in etwa deiner 
Beschreibung entsprechen, oder? Macht der Compiler das nicht 
automatisch?

@ATMega Guru, und das funktioniert bei dir im Simulator? Muss ich gleich 
selbst nochmal ausprobieren aber das widerspricht sich irgendwie mit den 
Aussagen deines Vorposters.

Ah, ich sehe grad, Karl Heinz hat noch was gecodet. Ich probiere jetzt 
beides mal aus!

lg PoWl

von Matthias L. (Gast)


Lesenswert?

>Also so was in der Art (das Problem der Array Längen überlasse
>ich dir :-)

Du könntest als ersten Wert in deinen Lightsow Array die Länge ablegen.

von Paul H. (powl)


Lesenswert?

Matthias Lipinsky wrote:
>>Also so was in der Art (das Problem der Array Längen überlasse
>>ich dir :-)
>
> Du könntest als ersten Wert in deinen Lightsow Array die Länge ablegen.

Oder eine Nullterminierung :-)

Habe grad mal folgendes Programm im AVR Simulator getestet: Auf PortB 
tut sich nix, folglich scheint es nicht zu funktionieren. Ich frage mich 
nur warum ich keinen Compile-Error bekomme bzw., was macht er denn da 
nun?
1
#include <avr/pgmspace.h>
2
3
const char scene1[] PROGMEM = { 10, 20, 30, 40, 0};
4
const char scene2[] PROGMEM = { 50, 60, 70, 80, 0};
5
6
const char *lightshow[] PROGMEM = {
7
  scene1,
8
  scene2
9
};
10
11
int main(void)
12
{
13
  char x, y, temp;
14
15
  while(1)
16
  {
17
    for(x=0; x<sizeof(lightshow); x++)
18
    {
19
      y = 0;
20
21
      while(1)
22
      {
23
        temp = pgm_read_byte(&lightshow[x][y]);
24
        y++;
25
26
        if(temp == 0)
27
        {
28
          break;
29
        }
30
      }
31
    }
32
  }
33
}

Vielen Dank für die Hilfe!

lg PoWl

von Uhu U. (uhu)


Lesenswert?

Paul Hamacher wrote:
> @ATMega Guru, und das funktioniert bei dir im Simulator? Muss ich gleich
> selbst nochmal ausprobieren aber das widerspricht sich irgendwie mit den
> Aussagen deines Vorposters.

Da kommen mal wieder schön die Tücken der Harvard-Architektur zum 
Vorschein:

Der Simulator läuft auf dem PC und der ist eine von-Neumann-Maschine. 
Der gesamte Speicher liegt in einem einzigen Adressraum und die 
Simulator-Entwickler haben sich nicht die Mühe gemacht, den 
Programm-Adressraum der Zielmaschine so nachzubilden, daß Pointer aus 
dem Daten- in den Programmspeicher (und umgekehrt) auf einen Fehler 
führen.

Das Simulationsergebnis von ATMega Guru könnt ihr getrost unter Zufall 
buchen...

von Oliver (Gast)


Lesenswert?

>und die Simulator-Entwickler haben sich nicht die Mühe gemacht, den
>Programm-Adressraum der Zielmaschine so nachzubilden, daß Pointer aus
>dem Daten- in den Programmspeicher auf einen Fehler führen.

Warum auch? Zeiger vom Daten- in den Programmspeicher sind ja nun weder 
unüblich, noch verboten, und eine Verwechslung durch den Programmierer 
führt in der Realität auch nicht zu einem Fehler, es wird halt nur auf 
die gleiche Adresse im "falschen" Speicherbereich zugegriffen. Was vom 
Programmieren tatscählich gewollt ist, kann kein Simulator der Welt 
erahnen.

Das der Simulationstest von ATMega Guru funktioniert, dürfte daran 
liegen, daß Compiler/Linker die Scene-Arrays zufällig 
hintereinanderliegend im Speicher angelegt haben. Darauf darf man sich 
aber nicht verlassen.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher wrote:

> Also in lightshow stehen ja die Startadressen der Scene-Arrays.
> lightshow[x] enthält ja dann die Startadresse der x-ten Szene.
> lightshow[x][y] müsste demnach die Daten..? des y-ten Elements von Szene
> x enthalten.

Im Prinzip ja, wenn da nicht noch die Harvard Architektur wäre.

   &lightshow[x][y]

wird vom Compiler so umgesetzt

   (*(lightshow + x * sizeof(const char*))) + y

Das Problem ist das dereferenzieren an dieser Stelle

  (*(lightshow + x * sizeof(const char*)))

Der Compiler weiss ganz einfach nicht, dass er mit der erhaltenen
Adresse im Flash Speicher den Lookup machen müsste um die
korrekte Adresse zu erhalten, mit der dann weiter gerechnet
wird.
Wenn es den Flash Speicher nicht geben würde und alles im SRAM
liegen würde, dann würde das so funktionieren. Aber du hast
ja alles ins Flash verschoben (was ja auch gut ist).
Der vorderste *, also die Dereferenzierung, darf nicht im SRAM
geschehen, sondern im Flash. Das macht der Compiler aber nicht
von alleine, sondern du musst ihm unter die Arme greifen.

> Ich dachte mir eher, der Compiler setzt das automatisch so um. Er ließt
> zuerst die Adresse der x-ten Scene und addiert dann noch die y drauf und
> ließt dann das enstprechende Element aus. Das würde doch in etwa deiner
> Beschreibung entsprechen, oder? Macht der Compiler das nicht
> automatisch?

Das würde auch funktionieren, wenn alles im SRAM liegen würde.

von Uhu U. (uhu)


Lesenswert?

Oliver wrote:
> Warum auch? Zeiger vom Daten- in den Programmspeicher sind ja nun weder
> unüblich, noch verboten, und eine Verwechslung durch den Programmierer
> führt in der Realität auch nicht zu einem Fehler, es wird halt nur auf
> die gleiche Adresse im "falschen" Speicherbereich zugegriffen. Was vom
> Programmieren tatscählich gewollt ist, kann kein Simulator der Welt
> erahnen.

Na ja, ich finde, daß solches Verhalten zumindest ein bemerkenswerter 
Mangel am Simulator ist. Er müßte wenigstens irgendwelchen Unsinn 
liefern, wenn ein Prgramm läuft, das mit der Adressierung nicht zurecht 
kommt. Auf jeden Fall müßten controllertypspezifische Speichergrenzen 
überwacht werden.

Die irrtümlich erwarteten Ergebnisse zu liefern, ist die mit Abstand 
schlechteste Lösung des Problems.

Der Punkt, der Entwickler verleitet, keine Trennung der Adressräume zu 
implentieren, ist natürlich die Performance-Strafe, die das mit sich 
bringt. Es sollte zumindest eine Option vorhanden sein, mit der man 
Harvard-Verhalten erzwingen kann.

von Paul H. (powl)


Lesenswert?

Hi,

so, ich habe das mal folgendermaßen probiert, leider funktioniert das 
(im Simulator) leider auch nicht :-( Noch ein Fehler?
1
#include <avr/pgmspace.h>
2
3
const char scene1[] PROGMEM = { 10, 20, 30, 40, 0};
4
const char scene2[] PROGMEM = { 50, 60, 70, 80, 0};
5
6
const char *lightshow[] PROGMEM = {
7
  scene1,
8
  scene2
9
};
10
11
int main(void)
12
{
13
  char x, temp, *pointer;
14
15
  while(1)
16
  {
17
    for(x=0; x<sizeof(lightshow); x++)
18
    {
19
      pointer = pgm_read_word(&lightshow[x]);
20
21
      while(1)
22
      {
23
        temp = pgm_read_byte(pointer++);
24
25
        if(temp == 0)
26
        {
27
          break;
28
        }
29
      }
30
    }
31
  }
32
}

von Oliver (Gast)


Lesenswert?

>Die irrtümlich erwarteten Ergebnisse zu liefern, ist die mit Abstand
>schlechteste Lösung des Problems.

Das Programm von AVRGuru wird auch auf der echten Hardware das selbe, 
richtige Ergebnis liefern. Der Simulator macht das schon richtig.

Oliver

von Frank W. (Firma: DB1FW) (frankw) Benutzerseite


Lesenswert?

Paul Hamacher wrote:
> Matthias Lipinsky wrote:
> Habe grad mal folgendes Programm im AVR Simulator getestet: Auf PortB
> tut sich nix, folglich scheint es nicht zu funktionieren.

Wieso sollte sich auf PortB was tun ?
Ich kann keine Ausgabe auf PortB in dem Programm finden.
Oder hab ich was übersehen ?

Frank

von Paul H. (powl)


Lesenswert?

Frank Wallenwein wrote:
> Wieso sollte sich auf PortB was tun ?
> Ich kann keine Ausgabe auf PortB in dem Programm finden.
> Oder hab ich was übersehen ?

Oh nein ich bin so doof... 6 Stunden Schlaf waren wohl eindeutig zu 
wenig um zu bemerken, dass ich die Anweisung mit dem PortB garnicht in 
mein Testprogramm übernommen habe.

Nichtsdestotrotz danke für den Hinweis ;-)

lg PoWl

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher wrote:
> Hi,
>
> so, ich habe das mal folgendermaßen probiert, leider funktioniert das
> (im Simulator) leider auch nicht :-( Noch ein Fehler?

Fehler schon. Der hat aber erst mal nichts mit den Flash Zugriffen
zu tun.

sizeof liefert die Größe eines 'Dings' in Bytes.

int a[5];

sizeof(a) ist daher die komplette Größe des Arrays in Bytes. Die
willst du aber nicht. Du willst ja wissen wieviele Elemente das
Array umfasst. Daher muss diese bytegröße noch durch die Bytegröße
eines Array Elements dividiert werden.

   sizeof(a) / sizeof(a[0])   ergibt dann tatsächlich 5


>     for(x=0; x<sizeof(lightshow); x++)

      for( x=0; x < sizeof(lightshow)/sizeof(lightshow[0]); x++ )


Abgesehen davon, sollte das aber schon funktionieren. Hast du
dir mal die Pointer Werte im Simulator angesehen, ob die realistisch
sind (in dem Fall ist auch der Memory-View des Simulators eine
echte Hilfe, da man mit ihm nachsehen kann, was an einer bestimmten
Speicherstelle liegt.)


Ich kann das hier leider auf meinem derzeitigen Rechner nicht
testen, da ich DevCpp / MingW auf der Maschine habe und das
anscheinend das Zusammenspiel AVR-Studio / WinAvr in irgendeiner
Form stört. ( Create process error )

von Paul H. (powl)


Lesenswert?

Danke Karl Heinz, du hast mir soeben schon die Antworten geliefert, ohne 
dass ich überhaupt die Fragen gestellt habe. Es solle noch viel mehr 
Forenuser deiner Sorte geben xD

habe mich shcon gewundert was diese sizeof-konstruktion im tutorial zu 
bedeuten hatte und dass meine for schleife zu weit läuft.

ich korrigiere das mal, und die methode von oben (&lightshow[x][y]) 
probier ich auch nochmal aus wobei die sich beim praxistest schon nicht 
bewährt hat.

lg PoWl

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.