Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind jedem Port 3 Register zugeordnet:
DDRx |
Datenrichtungsregister für Port x (x entspricht A, B, C oder D). |
PORTx |
Datenregister für Port x (x entspricht A,
B, C oder D). Wird ein Port als Eingang geschaltet, so können mit diesem Register die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv). |
PINx | Eingangadresse für Port x (x entspricht A,
B, C oder D). Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn PINx ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden. |
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden.
Um dies zu erreichen wird das Datenrichtungsregister des entsprechenden Ports
beschrieben.
outp (<bits>, <port>);
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang verwendet werden muss das entsprechende Bit gelöscht sein.
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge definieren so schreiben wir folgende Zeile:
outp (0x1F, DDRB);
// Binär 00011111 = Hexadezimal 1F
Die Pins 5 bis 7 werden als Eingänge geschaltet.
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl verwendet werden:
outp (0xFF, DDRB);
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.
Am einfachsten ist es, digitale Signale mit dem Microcontroller zu erfassen bzw. auszugeben.
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen.
Dazu schreiben wir den entsprechenden Wert in das Portregister des
entsprechenden Ports.
Mit dem Befehl
outp (0x04, PORTB);
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer von 0 an gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).
Man beachte bitte, dass bei der Verwendung der outp-Funktion immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen.
Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig
machen.
Die Funktionen lassen sich wie folgt verwenden:
sbi (PORTB, 2); // Setzt Pin 2 auf Logisch 1 (EIN)
cbi (PORTB, 2); // Setzt Pin 2 auf Logisch 0 (AUS)
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.
Am einfachsten ist es , wenn die Signale direkt aus einer anderen digitalen
Schaltung übernommen werden können. Hat der Ausgang der entsprechenden
Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit
einem Eingangspin von unserem Controller verbinden.
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wird den Pegel
über entsprechende Hardware, z.B. einen Optokoppler, anpassen.
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden
werden.
Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.
Die Abfrage der Zustände der Portpins erfolgt über den Befehl
inp (<port>)
Wobei es hier sehr wichtig ist, als Portadresse nicht etwa das Portregister
PORTx zu verwenden, sondern die Porteingangsadresse PINx. Dies ist
ein oft gemachter Fehler!
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:
bPortD = inp (PIND);
Der Anschluss mechanischer Kontakte an den Microcontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (Active Low und Active High):
Active Low |
Active High |
|
|
Bei dieser Methode wird der Kontakt zwischen
den Eingangspin des Controllers und Masse geschaltet. Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen. Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es muss jedoch beachtet werden, dass über den Widerstand ein Strom in den Eingang fliesst, also sollte er nicht zu klein gewählt werden um den Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 Kiloohm eingebürgert. Die AVR's haben sogar an den meisten Pins softwaremässig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können. |
Hier wird der Kontakt zwischen die
Versorgungsspannung und Masse geschaltet. Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten, |
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden
über das Register PORTx aktiviert bzw. deaktiviert, wenn ein Pin als Eingang
geschaltet ist.
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up
Widerstand nicht aktiv.
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand
verwenden, aber nicht beide zusammen.
outp (0x00, DDRD); // Port D als
Eingang schalten
outp (0x00, PORTD); // Interne Pull-Up Widerstände aus
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up Widerstände deaktiviert.
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.
Soll nun mit einem schnellen Microcontroller gezählt werden, wie oft ein
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen
als mehrfache Impulse gezählt wird.
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen
werden.
Leider können wir mit unseren Controllern keine analogen Werte direkt ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren Kapitel behandelt.
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht dann von einem Wort. Für den Zugriff auf diese Register stellt die Funktionsbibliothek zwei spezielle Befehle zur Verfügung:
__inw (<port>);
Liest den aktuellen Wert eines 16-Bit Portregisters ein.
__outw (<wert>, <port>);
Schreibt einen Wert in ein 16-Bit Portregister.
|