Forum: Compiler & IDEs Tutorial: Zeiger auf Werte im Flash


von Nichtswisser (Gast)


Lesenswert?

Hallo,

im avr-gcc-Tutorial steht im Abschnitt

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29

"Zeiger auf Werte im Flash sind ebenfalls 16 Bits "groß" (Stand avr-gcc 
3.4.x). Damit ist der mögliche Speicherbereich für "Flash-Konstanten" 
auf 64kB begrenzt."

Frage: Sind alle Pointer (auch auf 8-Bit-Variablen/Konstanten) die in 
den Flash zeigen, 16 Bit groß? Und wieso begrenzt dies den 
Speicherbereich auf 64kB?

von Daniel N. (Gast)


Lesenswert?

Ja alle Zeiger sind 16Bit groß.
2^16 = 64kB. Mehr kannst du mit 16bit nicht adressieren.

von Michael Wilhelm (Gast)


Lesenswert?

Mit 16 bit hat man Zugriff auf 65536 Adressen (2^16). Und das wird 
normal als 64KB geschrieben. Also von 0 bis 65535.

MW

von Rolf Magnus (Gast)


Lesenswert?

> Frage: Sind alle Pointer (auch auf 8-Bit-Variablen/Konstanten) die in
> den Flash zeigen, 16 Bit groß?

Wie groß die Variable ist, auf die er zeigt, spielt keine Rolle.

von Nichtswisser (Gast)


Lesenswert?

Ok, vielen Dank.

In dem aufgeführten Beispiel wird die Adresse eines Arrays im Flash auf 
einen 8-Bit-Zeiger gecastet. Gehen dann nicht Informationen verloren, 
wenn die Adresse immer 16 Bit groß ist?

von Nichtswisser (Gast)


Lesenswert?

Dieser Abschnitt ist gemeint:
1
ptrToArray = (uint8_t*)(pgm_read_word(&pgmPointerArray[1]));
2
    
3
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2
4
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse
5
    // von pgmFooByteArray2 abgelegt ist

In PointerArray[1] steht die Adresse von pgmFooByteArray2. Diese wird 
über pgm_read_word als 16-Bit-Wert ausgelesen. Dann wird sie auf einen 
8-Bit-Pointer gecasted.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ohne jetzt nachzusehen, warum das dort so programmiert wird:

Ein uint16_t * Pointer adressiert uint16_t, d.h. 16-Bit Elemente. Ein 
uint8_t * Pointer adressiert uint8_t, d.h. 8-Bit-Elemente.

Im obigen Fall könnte man also 8-bit Elemente aus einem eigentlich 
16-bit Elemente enthaltendem Feld auslesen.

Adresse  Daten
0000     01 02 03 04 05 06 07 08
0008     09 0A 0B 0C ...

uint16_t *p16 = (uint16_t *) 0x0002;
=> *p16     == 0x0403 (little-endian Endianess [1])
   *(p16+1) == 0x0605 (Ein Element weiter...)

uint8_t *p8 = (uint8_t *) 0x0002;
=> *p8      == 0x03 (Endianess spielt keine Rolle)
   *(p8+1)  == 0x04

[1] http://www.mikrocontroller.net/articles/Digitaltechnik#Endianness

von Nichtswisser (Gast)


Lesenswert?

Ja klar, sorry. Habe Größe des adressierten Bereichs und Größe des 
Pointers verwechselt. Dass die Adresse, auf die der Pointer zeigen soll, 
16 Bit groß ist, "weiß" er in deinem Beispiel ja durch die explizite 
Zuweisung (0x0002).

Würde man stattdessen die Adresse einer Variablen zuweisen, sagt der 
Compiler dem Pointer bescheid?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Nichtswisser wrote:
> Dass die Adresse, auf die der Pointer zeigen soll,
> 16 Bit groß ist, "weiß" er in deinem Beispiel ja durch die explizite
> Zuweisung (0x0002).

Nee, nicht durch das 0x0002, sondern dadurch, dass der Zeiger vom Typ 
uint16_t * ist. Solche Zeiger "wissen" per Definition, dass sie auf 
uint16_t grosse Elemente zeigen. D.h. der C Compiler weiss es und macht 
entsprechenden Maschinencode für den Zugriff. Das 0x0002 hätte ich auch 
als 2 schreiben können.

> Würde man stattdessen die Adresse einer Variablen zuweisen, sagt der
> Compiler dem Pointer bescheid?

Klar, wenn man "sagt Bescheid" umgangssprachlich für die Codegenerierung 
meint. Der Compiler warnt sogar bei Unterschieden, beim Typ der Variable 
und beim Typ des Elements auf das der Zeiger zeigt.

Solche Warnungen lassen sich unterdrücken, wenn man einen sog. cast 
anwendet. Man teilt dem Compiler mit, dass der angegebenen Wert einen 
definierten Typ haben soll. Man sollte allerdings vorsichtig mit solchen 
vasts umgehen bzw. genau wissen, was man macht, weil man einen Teil der 
sehr nützlichen Typüberprüfung des Compilers aushebelt.

Solche casts siehst du oben, wo dem Compiler mitgeteilt wird, dass der 
Wert 0x0002 bitte schön als uint16_t  oder als uint8_t  zu sehen ist.

von Oliver (Gast)


Lesenswert?

>Dieser Abschnitt ist gemeint:
1
ptrToArray = (uint8_t*)(pgm_read_word(&pgmPointerArray[1]));
2
    
3
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2
4
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse
5
    // von pgmFooByteArray2 abgelegt ist


Das Tutorial ist da manchmal etwas schwierig zu lesen. Dein Beispiel 
zeigt nicht das einfache Lesen eines Wertes aus dem Flash, sondern liest 
den pointer auf ein Array im Flash aus einem weiteren Array im Flash. 
Das eigentlich interessierende Array enthät dabei Daten vom Typ uint8_t.

Etwas klarer wird das vielleicht, wenn man sich die relevanten  Zeilen 
direkt untereinander hinschreibt:
1
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };
2
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };
legt zwei Arrays im Flash an, gefüllt mit uint8_t-Werten.

Mit
1
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };
wird ein weiteres Array im Flash angelegt, welches als Inhalt die 16-bit 
breiten Pointer auf die zuerst angelegten Arrays (im Flash) enthält.

Deine Beispielzeile
1
ptrToArray = (uint8_t*)(pgm_read_word(&pgmPointerArray[1]));

liest nun aus dem Array mit den Adressen den Pointer auf das Datenarray 
aus. Dieser pointer ist wie alle pointer 16-bit breit, muß also mit 
pgm_read_word gelesen werden, und zeigt auf ein Array, welches 
uint8_t-Werte enthält. Daher der cast auf uint8_t*.

Etwas verwirrend, aber eigentlich ganz einfach:-)

Oliver

von Nichtswisser (Gast)


Lesenswert?

Wir reden wohl aneinander vorbei.

Mir geht es um die Größe der ADRESSE. Also um die Organisation des 
Speichers.

Ein 8-Bit-Pointer zeigt auf einen 8-Bit-Wert. In diesem 8-Bit-Pointer 
steht aber u.U. eine 16-Bit-Adresse, weil der 8 Bit-Wert in einem 
Speicherbereich mit 16-Bit-Adressen abliegt, wie im Tutorial-Beispiel. 
Die Frage ist, wie der Pointer weiß dass er auf 0x1234 (16 Bit) und 
nicht auf 0x12 (8 Bit) zeigen soll.

von Nichtswisser (Gast)


Lesenswert?

@Oliver

Stimmt, ist wesentlich einfacher zu verstehen wenn man die Definitionen 
drüber schreibt ;) Das habe ich auch soweit verstanden.

Nun eben noch die Frage nach der Bit-Weite der Adresse.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Nichtswisser wrote:
> Wir reden wohl aneinander vorbei.

Ein Pointer steht für eine Adresse.

Alle AVR Adressen sind 16-Bit gross.

Alle Pointer sind 16-Bit gross.

Was unterschiedlich ist, ist die Grösse des Elements auf das der Pointer 
oder die Adresse zeigt.

> Die Frage ist, wie der Pointer weiß dass er auf 0x1234 (16 Bit) und
> nicht auf 0x12 (8 Bit) zeigen soll.

Er "weiss" es, weil du im Programm schreibst, dass es ein Pointer auf 
einen von dir gewünschten Datentyp ist. Der Compiler erzeugt dafür den 
entsprechenden MAschinencode, also dem Typ entsprechend viele und ggf. 
unterschiedliche Statements.

Z.B. Ein uint8_t * Pointer ist 16-Bit gross (wie alle Pointer) und ist 
äquivalent zu einer 16-Bit Adresse (wie alle Adressen), wobei beim 
Zugriff auf das worauf der Pointer zeigt (Dereferenzieren, also Lesen 
oder Schreiben) auf einen 8-Bit-Wert zugegriffen wird, weil der Pointer 
als uint8_t * definiert wurde.

von Oliver (Gast)


Lesenswert?

Nochmals kurz und knackig:

ALLE Pointer beim avr-gcc sind IMMER 16-bit breit. Egal, ob die auf die 
Adresse 0x0000 oder 0xffff zeigen. Und alle Pointer zeigen natürlich 
immer auf genau ein byte.

Der Compiler entnimmt er dem Datentyp, mit dem der Pointer definiert 
ist, welchen Datentyp ab dieser einen Adresse mit wievielen bytes lesen 
oder schreiben muß, und erstellt enrsprechenden Maschinencode.

Also:
uint8_t *p1; // 16-bit pointer zeigt auf die Stelle, von der aus ein 
byte (uint8_t) gelesen oder geschrieben werden muß

uint16_t *p2; // 16-bit pointer zeigt auf die Stelle, von der aus zwei 
byte (uint16_t) gelesen oder geschrieben werden müssen

uint32_t *p2; // 16-bit pointer zeigt auf die Stelle, von der aus vier 
byte (uint16_t) gelesen oder geschrieben werden müssen


typedef struct
{
   uint8_t array[30000];
} RiesenDatenTyp;

RiesenDatenTyp *p3; // 16-bit pointer zeigt auf die Stelle, von der aus 
30000 byte gelesen oder geschrieben werden müssen

Oliver

von Nichtswisser (Gast)


Lesenswert?

Wie sieht es denn bei anderen uC mit der Adressbreite aus?

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.