Tutorial: Zeiger auf Werte im Flash

Gast #721452
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?
Gast #721544
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?
Gast #721555
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.
Persönliche Seite #721589
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
Gast #721604
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?
Persönliche Seite #721635
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.
Gast #721661
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
Gast #721663
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.
Gast #721666
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.
Persönliche Seite #721717
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.
Gast #721748
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren