www.mikrocontroller.net

Forum: GCC Pointer-Array auf mehrere unterArrays (PROGMEM) wie zugriff?

Autor: Paul Hamacher (powl)
Datum: 15.05.2008 10:34

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
Autor: Karl heinz Buchegger (kbuchegg) (Moderator)
Datum: 15.05.2008 11:08

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 :-)
  const char* SceneStart;
  ....        SceneLength;
  unsigned char data;

  for( i = 0; i < LIGHTSHOW_LÄNGE; ++i ) {

    SceneStart  = pgm_readword( &lightshow[i] );
    SceneLength = .... ;

    for( j = 0; j < SceneLength; ++j ) {
      data = pgm_readByte( SceneStart + j );

      ... mach was mit data ...
    }
  }


> 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]
Autor: ATMega Guru (Gast)
Datum: 15.05.2008 11:15

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
Autor: Paul Hamacher (powl)
Datum: 15.05.2008 11:29

@ 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
Autor: Matthias Lipinsky (lippy)
Datum: 15.05.2008 11:29

>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.
Autor: Paul Hamacher (powl)
Datum: 15.05.2008 11:39

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?
#include <avr/pgmspace.h>

const char scene1[] PROGMEM = { 10, 20, 30, 40, 0};
const char scene2[] PROGMEM = { 50, 60, 70, 80, 0};

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

int main(void)
{
  char x, y, temp;

  while(1)
  {
    for(x=0; x<sizeof(lightshow); x++)
    {
      y = 0;

      while(1)
      {
        temp = pgm_read_byte(&lightshow[x][y]);
        y++;

        if(temp == 0)
        {
          break;
        }
      }
    }
  }
}

Vielen Dank für die Hilfe!

lg PoWl
Autor: Uhu Uhuhu (uhu)
Datum: 15.05.2008 11:42

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...
Autor: Oliver (Gast)
Datum: 15.05.2008 11:51

>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
Autor: Karl heinz Buchegger (kbuchegg) (Moderator)
Datum: 15.05.2008 11:53

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.
Autor: Uhu Uhuhu (uhu)
Datum: 15.05.2008 11:59

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.
Autor: Paul Hamacher (powl)
Datum: 15.05.2008 12:01

Hi,

so, ich habe das mal folgendermaßen probiert, leider funktioniert das
(im Simulator) leider auch nicht :-( Noch ein Fehler?
#include <avr/pgmspace.h>

const char scene1[] PROGMEM = { 10, 20, 30, 40, 0};
const char scene2[] PROGMEM = { 50, 60, 70, 80, 0};

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

int main(void)
{
  char x, temp, *pointer;

  while(1)
  {
    for(x=0; x<sizeof(lightshow); x++)
    {
      pointer = pgm_read_word(&lightshow[x]);

      while(1)
      {
        temp = pgm_read_byte(pointer++);

        if(temp == 0)
        {
          break;
        }
      }
    }
  }
}
Autor: Oliver (Gast)
Datum: 15.05.2008 12:22

>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
Autor: Frank Wallenwein (frankw)
Datum: 15.05.2008 12:28

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
Autor: Paul Hamacher (powl)
Datum: 15.05.2008 12:30

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
Autor: Karl heinz Buchegger (kbuchegg) (Moderator)
Datum: 15.05.2008 12:31

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 )
Autor: Paul Hamacher (powl)
Datum: 15.05.2008 12:50

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

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

  • Suchfunktion und Betreffsuche benutzen - vielleicht gibt es schon einen ähnlichen Beitrag
  • Aussagekräftigen Betreff wählen
  • Im Betreff angeben um welchen Controllertyp es geht (AVR, PIC, ...)
  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
  • JPEG-Dateien (.jpg) nur für Fotos und Scans verwenden
  • Schaltpläne, Screenshots usw. als PNG oder GIF anhängen

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [pre]vorformatierter Text (z.B. Code in anderen Sprachen)[/pre]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel






webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net