Forum: Compiler & IDEs ARM7 Zeiger, Zeiger und nochmals Zeiger


von Harald H. (mirona)


Lesenswert?

Hallo ,

ich habe hier ein Problem mit Zeigern.
Ich lese aus einer SD Karte eine Bitmapdatei aus. Die Daten werden in 
einem Charakter Puffer gespeichert und sollen dann weiterverarbeitet 
werden. Das ganze läuft auf einem Atmal ARM7 AT91SAM7X256. Kompiliert 
wird mit Crossworks for ARM.
Nach dem lesen des ersten Blocks möchte ich den Bitmap Header auswerten.
da die Daten als Char eingelesen werden muß eine Umwandlung in die 
entsprechenden Datenformate erfolgen. Das zuerst gezeigte Programm 
funktioniert nur für die ersten beiden byte im Puffer BMPBuffer. Die 
Byte 2-5 enthalten als unsigned long die Größe der Bitmapdatei. Ich 
versuche einen void Zeiger auf das dritte Byte zu setzen und auf 
unsigned long zu casten. Der Pointer vptr enthält auch die entsprechende 
Adresse und zeigt auch den richtigen Wert an. Die Zuweisung an eine 
Variable vom Typ unsigned long führt aber ins leere. Der Prozessor ruft 
den abort handler auf.
1
// Dieses Programm funktioniert nicht!
2
3
#include "mmc.h"
4
#include "ff.h"
5
#include <stddef.h>
6
#include <stdlib.h>
7
#include "bmp.h"
8
#include <string.h>
9
10
#define buffersize 1024
11
12
void Read_BMP (void) {
13
  FRESULT fres;
14
  FATFS BMPfs;
15
  FIL *BMPFile;
16
  unsigned char BMPBuffer[buffersize];
17
  unsigned int *prdByte, rdByte;  // Anzahl der gelesenen Byte
18
  unsigned int i;
19
  struct BMPHeader tBMP;
20
  unsigned short us;
21
  unsigned long ul;
22
  void *vptr;
23
24
  prdByte = &rdByte;
25
26
  BMPFile = (FIL *)malloc (sizeof (FIL));
27
28
  if ((fres = f_mount (0, &BMPfs)) == FR_OK) {
29
    fres = f_open (BMPFile, "ML123456.bmp", 1);
30
    if (fres == FR_OK) {
31
      fres = f_read (BMPFile, BMPBuffer, buffersize, prdByte);
32
33
      vptr = (unsigned short *)BMPBuffer;  // funktioniert
34
      us = *(unsigned short *)vptr;
35
      tBMP.bfTYPE = us;
36
37
      vptr = (unsigned long *)&BMPBuffer[2]; // funktioniert nicht
38
      ul = *(unsigned long *)vptr;  // Absturz Aufruf des abort-handlers
39
      tBMP.bfSIZE = ul;
40
41
  }
42
  f_close (BMPFile);
43
44
}

Hier die nun funktionierende Variante. Ich kopiere die Pufferdaten ab 
dem dritten byte in einen neuen Puffer. Dessen Anfangsadresse kann ich 
wieder direkt bestimmen "vptr = (unsigned long +)buffer".
Und siehe da, alles funktioniert wie gewünscht.

Meine frage, kann sich jemand von den C Experten das Verhalten erklären?

hier der funktionierende Code.
1
// Das folgende Programm funktioniert!
2
3
#include "mmc.h"
4
#include "ff.h"
5
#include <stddef.h>
6
#include <stdlib.h>
7
#include "bmp.h"
8
#include <string.h>
9
10
#define buffersize 1024
11
12
void Read_BMP (void) {
13
  FRESULT fres;
14
  FATFS BMPfs;
15
  FIL *BMPFile;
16
  unsigned char BMPBuffer[buffersize];
17
  unsigned char buffer[54];
18
  unsigned int *prdByte, rdByte;  // Anzahl der gelesenen Byte
19
  unsigned int i;
20
  struct BMPHeader tBMP;
21
  unsigned short us;
22
  unsigned long ul;
23
  void *vptr;
24
25
  prdByte = &rdByte;
26
27
  BMPFile = (FIL *)malloc (sizeof (FIL));
28
29
  if ((fres = f_mount (0, &BMPfs)) == FR_OK) {
30
    fres = f_open (BMPFile, "ML123456.bmp", 1);
31
    if (fres == FR_OK) {
32
      fres = f_read (BMPFile, BMPBuffer, buffersize, prdByte);
33
34
      vptr = (unsigned short *)BMPBuffer;  // funktioniert
35
      us = *(unsigned short *)vptr;
36
      tBMP.bfTYPE = us;
37
38
      memcpy (buffer, &BMPBuffer[2], 54);
39
40
      vptr = (unsigned long *)buffer;
41
      ul = *(unsigned long *)vptr;
42
      tBMP.bfSIZE = ul;
43
44
  }
45
  f_close (BMPFile);
46
47
}
hier noch die Struktur des BMPHeaders
1
struct BMPHeader {
2
// BitmapFileHeader 14 Byte
3
  unsigned short bfTYPE;    // ASCI Zeichenkette "BM"
4
  unsigned long bfSIZE;    // Größe der Bitmap-Datei (unzuverlässig)
5
  unsigned long bfRESERVED;  // 0
6
  unsigned long bfOFFBITS;  // Offset der Bilddaten von Dateianfang an
7
// BitmapInfoHeader 40 Byte
8
  unsigned long biSIZE;    // 40 Größe der BitmapInfoheader struktur
9
  long biWIDTH;      // Höher der Bitmap in Pixel
10
  long biHEIGHT;      // Breite der Bitmap in Pixel
11
  unsigned short biPLANES;
12
  unsigned short biBITCOUNT;
13
  unsigned long biCOMPRESSION;
14
  unsigned long biSIZEIMAGE;  // Größe der Bilddatei in Byte (unkomprimiert)
15
  long biXPELSPERMETER;
16
  long biYPELSPERMETER;
17
  unsigned long biCLRUSED;
18
  unsigned long biCLRIMPORTANT;
19
};

von (prx) A. K. (prx)


Lesenswert?

Misalignment. Es ist nicht bei jedem Prozessor straflos möglich, auf 
Adressen zuzugreifen, deren Wert nicht zur Grösse des Datentyps passt. 
Bei einem 32-Bit Datentyp sollten also die beiden unteren Bits der 
Adresse 0 sein. Sind sie hier nicht.

Ich weiss grad nicht wie dieser ARM7 definiert ist. Auf ganz alten ARMs 
wäre Schrott geladen worden (die Bytes 0..3 um 16 Bits rotiert), manche 
Prozessoren laufen in eine Exception und manche basteln sich den Kram in 
mehreren Zugriffen zusammen (x86, Cortex-M3).

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Warum die Konstruktion mit malloc? Eine "lokale variable" vom Typ FIL 
und ein "&" bei den Fuktionsaufrufen zur FAT-library düfte ausreichen. 
free() fehlt ohnehin. Betr. BMP-Header parsen ist mglw. etwas aus einer 
meiner Basteileien hilfreich: 
http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/index_cortex.html#stm32_dpf 
(darin slideshow.c/LCD_DrawBMPFromFile()). Falls man nicht manual 
"zerpflücken" will, könnte man noch ausprobieren, BMPHeader mit einem 
packed-attribut zu versehen (siehe GNU GCC Dokumentation) - portabel 
wird der Code dadurch aber noch weniger.

von Harald H. (mirona)


Lesenswert?

Danke erstmal für die Antworten.

@A.K.
Misalignment kam mir natürlich nicht in den Sinn. Werde das aber auch 
überprüfen. Wenn dem so ist (Wahrscheinlichkeit sehr groß) werde ich 
wohl bei meinem Konstrukt bleiben, obwohl es mir nicht gefällt, oder 
irre ich mich da?

@Martin Thomas
Die Konstruktion mit malloc ist die einzige die bisher funktioniert. 
Habe ich auf drei Prozessoren ausprobiert (H8SX1668, ATmega644 und eben 
AT91SAM7X256).
Wenn ich nur die Adressenzuweisung einer FIL Variablen mache 
funktioniert nichts. In f_read wird dann dem Puffer BMPBuffer eine 
Adresse im ROM zugewiesen. Nicht mal unbedingt die Adresse 0. Auf dem 
ATmega644 habe ich das ganze mal genau unter die Lupe genommen und 
festgestellt, daß die übergebene Adresse irgendwann einfach mit 0 
überschrieben wird. Warum ist mir nicht klar. Die Routinen sind übrigens 
die ELM Chan Routinen.
Danke für das free (). Da ich noch nicht soweit bin, ist mir das Fehlen 
noch garnicht aufgefallen.

von (prx) A. K. (prx)


Lesenswert?

Harald Hermenau schrieb:

> wohl bei meinem Konstrukt bleiben, obwohl es mir nicht gefällt, oder
> irre ich mich da?

Im Prinzip ok, wenn man, wie Martin anführt, das malloc durch eine 
simple Variable ersetzt. Notfalls als union.

Die "packed" Variante geht auch. D.h. eine packed struct exakt so bauen, 
dass die richtigen Typen an der richtigen Stelle liegen 
(kontrollieren!). Dann kann man entweder direkt mit der arbeiten, wobei 
der Zugriff dabei etwas weniger effizient ist, oder man hat zwei 
structs, eine gepackte und eine normale, und führt einmalig eine 
Zuweisung der Elemente durch.

> Wenn ich nur die Adressenzuweisung einer FIL Variablen mache
> funktioniert nichts.

Beispielcode ist besser als Prosa.

von (prx) A. K. (prx)


Lesenswert?

Eine weitere Variante liest nicht en bloc, sondern passend zum Typ. Also
  bmp.var0 = f_read_u16(file);
  bmp.var1 = f_read_u32(file);
  ...
mit get_u16(file) beispielsweise als
  uint_least16_t r = f_read_u8(file);
  return r | f_read_u8(file) << 8;
wenn das BMP als little-endian definiert ist. Das funktioniert dann 
plattformunabhängig, d.h. auch wenn auf big-endian Hardware.

von Jürgen (Gast)


Lesenswert?

Lies das Byte für Byte und bau das mit Shifts zusammen (siehe A.K.),
das ist portabel und funktioniert immer.

Alles andere ist Murks und führt nur zu Problemen mit Endianness oder 
Alignment (wie hier geschehen).

von Harald H. (mirona)


Lesenswert?

@A.K.
Danke.
Das mit dem Misalignment wars erstmal. Habe es gerade ausprobiert. Auf 
durch 4 teilbaren Adressen funktionierts.
Das mit einer gepackten Struktur habe ich auch schon versucht. Ob 
richtig, sei mal dahingestellt. Es gab jedenfalls kein positives 
Ergebnis. Kann natürlich auch an mir gelegen haben, es nicht richtig 
gemacht zu haben. Vielleicht kann ich es nochmal rekonstruieren.

Die andere Geschicht mit der Adresszuweisung muß ich aucht erstmal 
wieder rekonstruieren, da ich den Code schon umgeschrieben habe und den 
nicht funktionierenden nicht weiter dokumentiert habe.

Die Variante mit dem "nicht en bloc" lesen werde ich auch mal ins Auge 
fassen, da ich mit dem H8SX1668 einen big-endian Prozessor vor mir habe.

Danke für die guten Tips.

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.