Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten
davon sind sogenannte Schreib-/Leseregister. Das heisst, das Programm kann die
Inhalte der Register auslesen und beschreiben.
Einige Register haben spezielle Funktionen, andere wiederum könne für
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.
Einzelne Register sind bei allen AVR's vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen UART verfügen.
Die Namen der Register sind in den Headerdateien zu den entsprechenden
AVR-Typen definiert.
Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die
richtige Headerdatei ein.
Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei
definieren, wenn man Freude daran hat.
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal
die 8-Bit Register.
Hinweis: | Die folgenden Funktionen erwarten als Argument
für das jeweilige Portregister konstante Werte. Am besten verwenden wir
die entsprechenden defines aus der Headerdatei <io.h>. Wenn die
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:
<Dateiname>:<Zeilennummer>: warning: asm operand 0 probably
doesn't match constraints Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf die Low Level-Funktion __mmio ausweichen. |
Der gesamte Inhalt eines Registers kann mit dem Befehl
inp(<register>);
ausgelesen werden.
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:
bit_is_set (<port>, <pin>);
Die Funktion bit_is_set prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.
bit_is_clear (<port>, <pin>);
Die Funktion bit_is_clear prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.
Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl
outp (<wert>, <register>);
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden.
Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.
sbi (<register>, <bitnummer>);
Die Funktion sbi setzt ein beliebiges Bit eines Registers, das heisst,
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports
werden nicht verändert.
Das niederwertigste Bit hat die Bitnummer 0.
cbi (<register>, <bitnummer>);
Die Funktion cbi löscht ein beliebiges Bit eines Registers, das
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des
Ports werden nicht verändert.
Das niederwertigste Bit hat die Bitnummer 0.
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter
Zustand auf einem Bit erreicht ist.
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.
loop_until_bit_is_set(<register>, <bitnummer>);
Die Funktion loop_until_bit_is_set wartet in einer Schleife, bis das
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits
gesetzt ist wird die Funktion sofort wieder verlassen.
Das niederwertigste Bit hat die Bitnummer 0.
loop_until_bit_is_clear(<register>, <bitnummer>);
Die Funktion loop_until_bit_is_clear wartet in einer Schleife, bis das
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits
gelöscht ist wird die Funktion sofort wieder verlassen.
Das niederwertigste Bit hat die Bitnummer 0.
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann verwenden wir für den Portzugriff das Synonym __mmio.
__mmio(<port>)
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory
Mapped I/O).
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet
werden.
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein der folgenden Befehlszeilen verwendet werden:
__mmio (<port>) = __mmio (<port>) | (1
<< <bitnummer>) // Setzt ein Bit
__mmio (<port>) = __mmio (<port>) & ~(1 << <bitnummer>)
// Löscht ein Bit
Die anderen Bits des Ports bleiben unverändert.
|