Servus! Kann mir jemand sagen was die folgende Zeile auf sich hat? #define CCNT ((uint32_t *)0x500004) /* address of CCNT Register */ Warum steht das uint32_t dort wo es steht und wozu das * ? Danke!
Was bedeutet das genau? Ist das wieder nur eine "vereinfachende" Schreibweise? Kann man das anders schreiben?
Der * in diesem Zusammenhang ist ein 'Pointer'-Sternchen. Sinn der Sache ist es einen Pointer zu konstruieren, der auf eine bestimmte Speicherstelle zeigt. Wenn du eine Variable erzeugst int i; oder auch uint32_t j; dann kümmert sich der Compiler darum, dass diese Variable irgendwo im Speicher erzeugt wird. Du musst dich nicht darum kümmern, wo dies konkret ist. Nun gibt es aber Fälle, an denen du eine 'Variable' an eine bestimmte Speicherstelle bugsieren möchtest. Stell dir einfach vor, du hättest eine bestimmte Hardware, sagen wir mal eine Uhr, die im Speicher an einer bestimmten Stelle auftaucht. Dann wäre es doch schön, wenn man auf diese Uhr wie auf jede andere Variable zugreifen könnte. Nur: Ich muss den Compiler dazu bringen, eine bestimmte Speicheradresse zu benutzen. Nun: Eine Speicheradresse ist auch nur eine Zahl. Aber in C hat jede Variable neben einer Speicheradresse auch noch eine 2-te Information: den Datentyp der sich hinter dieser Adresse versteckt. uin32_t j; erzeugt eine Variable, der Compiler reserviert Speicher dafür und kennt damit auch die Adresse an der j angelegt wird. Aber er weiss noch mehr: Ihm wurde mitgeteilt, dass es sich um einen uint32_t handelt. Also um einen Integer mit 32 Bit und dieser Integer beinhaltet kein Vorzeichen. Eine Speicheradresse ist auch nur eine Zahl. Schon: aber umgekehrt gilt das nicht. Eine Zahl ist keine Speicher- adresse. Denn dazu fehlt uns noch die Information was sich denn an dieser Speicheradresse verbirgt. Aber ich kann dem Compiler diese zusätzliche Information beibringen. Ich kann eine Zahl zu einem Pointer casten, indem ich dem Compiler sage, welcher Typ sich denn am Ziel des Pointer verbirgt. (uint32_t *) 5 sagt dem Compiler: 5 nimm die 5 * ) 5 und fasse diese 5 mal als Pointer auf (also als Speicheradresse) (uint32_t *) 5 und zwar zeigt dieser Pointer auf einen uint32_t zusammengenommen ist das also die Information, dass sich an der Speicheradresse 5 eine unsigned 32 Bit Zahl verbirgt. Wenn ich die auslesen möchte, dann mache ich uint32_t j; j = * (uint32_t *)5; Der erste * ist der 'Dereferenzier'-*, der zweite * ist der Pointer-* der dafür sorgt, dass die 5 als Speicheradresse aufgefasst werden. Möchte ich an die Speicheradresse 5 einen uint32_t schreiben *(uint32_t *)5 = j; Und damit der original-Autor nicht jedesmal diesen Rattenschwanz schreiben muss, hat er sich dafür ein #define gemacht. Dies hat dann auch den Vorteil, dass * die spezifische Zahl nur an einer Stelle vorkommt. * Im Programmtext anstelle der Zahl eine etwas aussage- kräftigere Bezeichnung verwendet werden kann #define RTC ( (uint32_t*)5 ) j = *RTC; *RTC = j; Sieht doch gleich viel besser aus.
Was bedeutet eigentlich der parameter volatile in der Funktion? #define write32(value,adress) *(volatile UINT32 *)adress = value
volatile ist eine Attributierung eines Datentyps (ein sog. Qualifizierer, so wie 'const'). Er sagt dem Compiler, dass sich eine Variable auch ausserhalb des Kenntnisbereiches des Compilers verändern kann und der Compiler daher aggresive Optimierungen mit dieser Variablen unterlassen soll. Worum gehts? Es geht im weitesten Sinne immer um das hier: int i = 5; while( 1 ) { Ausgabe( i ); } Innerhalb der Schleife gibt es für i keine Möglichkeit jemals verändert zu werden. Daher optimieren viele Compiler das so, dass sie den Wert von i in einem CPU internen Register zwischenspeichern und die Ausgabe von diesem Register aus machen. Das spart Zeit, weil ja kein Speicherzugriff gemacht werden muss um den Wert von i zu erhalten. Nur: Wenn sich hinter dem i in Wirklichkeit zb. eine Hardware- schaltung verbirgt, dann gibt es sehr wohl eine Möglichkeit wie i seinen Wert ändern kann. Nur ist das für den Compiler nicht ersichtlich, wenn er an der Schleife arbeitet. Das volatile teilt ihm dann mit: Spar die Optimierungen des Zugriffs. Du musst immer an den Speicher ran, und kannst den Wert nicht irgendwo zwischenspeichern.
Hallo nochmal, Ich dachte ich habe es verstanden, jetzt sehe ich das Makro hier: #define read32(adr) *(volatile UINT32 *)adr Das Makro soll eine Adresse auslesen. Aber muss nicht irgendwo noch ein "=" stehen?
Ja, vor dem Aufruf des Makros: UINT32 bla; bla = read32(0xdeadface); Machst Du einen manuellen Textersatz des Makros, ergibt sich bla = * (volatile UINT32 *) 0xdeadface; Also wird der (von mir willkürlich vorgegebene) Wert 0xdeadface als Pointer auf UINT32 interpretiert (das macht der Ausdruck in der Klammer) und dann dereferenziert, sprich: der Wert bestimmt, der an der Adresse 0xdeadface steht, was der Stern vor der Klammer erledigt. Also steht in bla der Inhalt der Speicheradresse 0xdeadface Das "volatile" weist den Compiler an, keinerlei Optimierungen betreffend den als Adresse angegebenen Wert durchzuführen. Anmerkung: 0xdeadface ist auf vielen 32-Bit-Systemen keine gültige Adresse für UINT32-Zugriffe, da nicht ohne Rest durch 4 teilbar (misalignment). Auf x86-PCs geht das, aber beispielsweise auf ARMen nicht.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.