Einfache Ein- und Ausgaben
In diesem Abschnitt soll ebenfalls auf der Basis der myAVR C++ Portdeklarationen die Realisierung von einfachen Eingaben veranschaulicht werden. Zuerst wollen wir kurz die Spezifik der Eingabe über Taster diskutieren. Das Bild verdeutlicht, dass ein Taster solange er nicht gedrückt ist mit seiner Verbindung bis zum Controller eine Antenne darstellt.
Offene Eingänge sind empfindliche Empfänger für allerlei Elektrosmog. Die Eingabe liefert faktisch willkürliche Signale. Abhilfe schaffen hier sogenannte PullDown- oder PullUp-Widerstände. Diese ziehen die Leitung auf ein definiertes Potenzial und entstören diese somit. Früher wurden diese PullUp-Widerstände in der Schaltung explizit eingebaut. AVR Controller verfügen über interne PullUp- und die Xmega Serie zusätzlich auch noch über interne PullDown-Widerstände. Diese müssen bei Bedarf einfach nur aktiviert werden. Dazu dienen die bereits angesprochenen Register, welche zu einem GPIO-Port gehören. Wenn ein Port als Eingang konfiguriert ist, wird beim AVR über das Ausgaberegister keine Ausgabe erzeugt, sondern der interne PullUp aktiviert.
// Port D Bit 2 als Eingang konfigurieren ddrD.bit2 = 0; // Port D Bit 2 PullUp aktivieren portD.bit2 = 1;
An dieser Stelle eine kurze Zusammenfassung des bisher gelernten. Die wichtigen Register für digitale Ein- und Ausgaben sind:
- ddrX, Konfiguration der Datenrichtung Eingang/Ausgang, 0 = Eingang, 1 = Ausgang
- portX, Ausgaben erzeugen, wenn es ein Ausgang ist oder PullUp aktivieren bei Eingängen
- pinX, Eingaben realisieren
Die eigentliche Auswertung des Tasters erfolgt als logischer Ausdruck zum Beispiel als Bedingung für eine Alternative oder eine Schleife. Auch die Wertzuweisung zu einer Variable ist möglich. Für das Bereitstellen der Eingabe selbst ist das Register pinX verantwortlich. Beachten Sie dabei die umgekehrte Logik des Tasters. Wenn pinX.bitN eine logische 1 liefert, ist der Taster nicht gedrückt, denn der PullUp zieht die Leitung gegen High. Ist der Taster gedrückt, zieht dieser die Leitung auf Masse. Somit liefert ein gedrückter Taster bei dieser Schaltung eine logische 0.
Eingabe von einem Taster in der Bedingung in einer Alternative auswerten:
if ( pinD.bit2 == 0 ) // wenn Taster an D.2 gedrückt { // dann das hier ausführen } else // sonst { // das hier ausführen }
Eingabe von einem Taster in der Bedingung in Schleife auswerten:
while ( pinD.bit2 == 0 ) // solange Taster an D.2 gedrückt ist { // wiederhole das hier }
Zustand von einem Taster in einer Variablen speichern:
bool tastenZusatnd; tastenZustand = pinD.bit2;
Die Möglichkeiten der Auswertung und Kombination mit anderen Ausdrücken in C und C++ sind schier unendlich. Wesentlich ist Schreibweise des Zugriffs auf einzelne Bits über den Punktoperator. Unser erstes Beispiel für eine Eingabe von einer Taste ist das Beispiel Lichtschalter 1. Dabei soll die rote LED an Port B Bit 0 eingeschaltet werden, wenn der Taster gedrückt ist. Ist der Taster nicht gedrückt, soll die LED aus sein. Schließen sie dazu mit einem der Patchkabel den Taster 1 an Port D Bit 2 an und die rote LED sollte ja von der letzten Übung noch mit Port B Bit 0 verbunden sein. Vergleichen Sie Ihre Schaltung mit dem Bild.
Die Initialisierungen von Taster und LED erfolgen in der Operation onStart, die Verarbeitung der Eingabe und das Realisiern der Ausgaben erfolgt in der Operation onWork.
Immer zuerst einen Entwurf estellen! Dann darüber nachdenken und ggf. den Entwurf korrigieren.
////////////////////////////////////////////////////////// // Lichtschalter 1 ENTWURF ////////////////////////////////////////////////////////// class Application : public Controller { public: void onStart() { // Port B Bit 0 als Ausgang konfigurieren // Port D Bit 2 als Eingang konfigurieren // Port D Bit 2 PullUp aktivieren } public: void onWork() { // wenn Taste an Port D Bit 2 gedrückt ist // dann LED an Port B Bit 0 einschalten // sonst // LED an Port B Bit 0 ausschalten } } app; //////////////////////////////////////////////////////////
Jetzt den Entwurf realisieren.
////////////////////////////////////////////////////////// // Lichtschalter 1 ////////////////////////////////////////////////////////// class Application : public Controller { public: void onStart() { ddrB.bit0 = 1; // Port B Bit 0 als Ausgang konfigurieren ddrD.bit2 = 0; // Port D Bit 2 als Eingang konfigurieren portD.bit2 = 1; // Port D Bit 2 PullUp aktivieren } public: void onWork() { if (pinD.bit2 == 0) // wenn Taste an Port D Bit 2 gedrückt ist { portB.bit0 = 1; // dann LED an Port B Bit 0 einschalten } else // sonst { portB.bit0 = 0; // LED an Port B Bit 0 ausschalten } } } app; //////////////////////////////////////////////////////////
Kompilieren, linken und brennen Sie die Anwendung. Die LED sollte jetzt nur dann leuchten, wenn die Taste gedrückt ist.
Bevor wir diesen Abschnitt verlassen und uns den etwas komfortableren Klassen für Taster und LEDs zuwenden, soll die LED noch bei Tastendruck blinken. Bei der Gelegenheit soll kurz angedeutet werden, dass mit den vorgestellten GPIO-Strukturen auch Bitoperationen möglich sind. Das Blinken bedeutet, dass die LED nach einer gewissen Wartezeit umgeschaltet werden muss. Die geeignete Bitoperation dafür ist das XOR. Schauen Sie sich die Schreibweise und Bedeutung des XOR-Operators in C/C++ ruhig noch mal an.
Kompilieren, linken und übertragen Sie die Anwendung auf den Controller. Die LED sollte jetzt blinken, wenn die Taste gedrückt ist. Variieren Sie die Wartezeit in Millisekunden.