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?
> 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.
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?
// 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.
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?
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.
// 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
constuint8_tpgmFooByteArray1[]PROGMEM={18,3,70};
2
constuint8_tpgmFooByteArray2[]PROGMEM={30,7,79};
legt zwei Arrays im Flash an, gefüllt mit uint8_t-Werten.
Mit
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
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
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.
@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.
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.
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