Hallo,
Ich frage mich wie z.B. PORTB implementiert ist. Das habe ich bisher
herausgefunden:
Die Addresse des Ports kann man aus dem Datenblatt entnehmen oder aus
der avrlibc. Für den ATmega8 steht sie in der Datei iom8.h und lautet
0x18.
1 | #define PORTB _SFR_IO8(0x18)
|
Das Makro _SFR_IO8 ist in der Datei sfr_defs.h definiert und addiert je
nach verwendeten AVR noch einen Offset hinzu, in meinem Fall beträgt der
Offset 0x20. Danach wird diese Zahl (0x18+0x20) mit Hilfe eines weiteren
Makros _MMIO_BYTE als ein 8 Bit breiter Pointer mit dem Attribut
volatile interpretiert. Da es sich hier um ein Hardware Register handelt
kann der Compilier keine Annahmen für Optimierungen treffen, daher ist
das volatile notwenig.
Der so erzeuge Pointer wird sofort dereferenziert damit damit eine
einfach Zuweisung mit = möglich ist.
Man kann sich das alles zur Compilezeit mit folgenden Code anzeigen
lassen:
1 | #define STR(x) #x
|
2 | #define XSTR(x) STR(x)
|
3 | #pragma message "PORTB " XSTR(PORTB)
|
1 | note: '#pragma message: PORTB (*(volatile uint8_t *)((0x18) + 0x20))'
|
Nun will ich mir den erzeugten Assembler Code für diese Zeile Code
genauer ansehen: PORTB |= 0xf0;
Es werden 3 Instructionen erzeugt:
1 | in r24,0x18
|
2 | ori r24,lo8(-16)
|
3 | out 0x18,r24
|
Als erstes wird ein Wert von PORTB geladen, dann die OR Verknüpfung mit
der Konstante 0xf0 durchgeführt (die hier als signed Zahl angezeigt
wird, daher die -16) und zum Schluss wird das Ergebniss wieder nach
PORTB gespeicher. Es wird das Register r24 benutzt um den Wert zwischen
zu speichern und gelesen und geschrieben wird von Addresse 0x18.
Warum wird hier im Assembler Code Addresse 0x18 benutzt und nicht 0x38?
Also 0x18+0x20. Im C Code steht eindeutig 0x18+0x20. Also muss der
Compiler hier doch zusätzliches Wissen einprogrammiert haben um den
Offset für bestimme (oder alle?) Hardware Register wieder abzuziehen.
Weis hier jemand dazu etwas?
Sobald ich wieder etwas Zeit habe, werde ich von den anderen Sachen
berichten die ich herausgefunden habe.
Grüße