Die Auswertung des Inkrementalgebers erfolgt mit dem Keyboard Interrupt (siehe Projekt Test_Encoder (Kapitel 0, Seite 28). Die Konfiguration und Freigabe des Keyboard-Interrupts soll in der Funktion „v_InitEncoder()“ erfolgen. Diese Funktion und die ISR sollen im Sourcemodul Encoder_Lib.c abgespeichert werden. Die Bewegung des Encoders wird in der Variablen ucValue abgespeichert. Die LEDs an Port 2 sollen den Wert von ucValue enthalten. Erstellen sie das Projekt aus Tabelle 48.
|
Projektname |
Verzeichnis |
Verwendete Sourcemodule |
|
Test_EncoderLib |
Test_EncoderLib |
TestEncLib.c |
|
|
..\Library |
Encoder_Lib.c, PortConfig.c |
Tabelle 48 Projekt Test_EncoderLib
|
|
#include <REG932.H> #include <Encoder_Lib.h> #include <PortConfig.H>
void main(void) { v_PortConfig(Port2, AllPins, BiDir); // Port 2 konfiguriert v_InitEncoder(); // Keyboard-Interrupt initialisiert EA = 1; // allgemeine Interruptsperre aufheben
while(1) // Endlosschleife { P2 = cValue; // Wertuebernahme an Port 2 } } |
Listing 42 Inhalt von TestEncLib.c
|
|
#include <REG932.H> #include <Encoder_Lib.h>
static unsigned char ucActEncVal; char cValue;
void v_InitEncoder(void) { ucActEncVal = P0 & 0xA0; // Abspeichern der akt. Position cValue = 0; // Ruecksetzen
KBPATN = P0 & 0xE0; // Setzen des Keyboard-Patterns KBCON = 0x00; KBMASK = 0xE0; // Maske fuer Portpin 5, 6 und 7 EKBI = 1; // ISR fuer Keyboard-Int. freigeben }
void v_KeyboardInt(void) interrupt 7 { KBPATN = P0 & 0xE0; // Neues Keyboard-Pattern setzen
if (ucActEncVal != (P0 & 0xA0)) // Abfrage, ob Aenderung beim { // Inkrementalgebers erfolgt ist switch(P0&0xA0) { case 0x00: if (ucActEncVal == 0x20) cValue++; else cValue--; break; case 0x20: if (ucActEncVal == 0xA0) cValue++; else cValue--; break; case 0x80: if (ucActEncVal == 0x00) cValue++; else cValue--; break; case 0xA0: if (ucActEncVal == 0x80) cValue++; else cValue--; break; } ucActEncVal = P0 & 0xA0; // Abspeichern der neuen Positon } // des Inkrementalgebers KBCON = 0x00; // Loeschen des Keyboard Flags } |
Listing 43 Inhalt von Encoder_Lib.c
Jedes moderne Gerät wird seit einiger Zeit mit einer „Einknopf“-Bedienung ausgeliefert. Das Bedienelement für diese Funktion besteht aus einer Kombination aus Taster und Inkrementalgeber. Mit dem Taster wird zwischen den Rubriken ausgewählt bzw. die Eingabe bestätigt. Der Inkrementalgeber scrollt zwischen den Rubriken bzw. zwischen den Unterpunkten.
Erstellen sie ein Projekt, das den Cursor auf dem LCD mit dem Inkrementalgeber bewegt. Die Umschaltung zwischen horizontal und vertikal erfolgt mit der Taste des Inkrementalgebers. Die LCD wird über den Port 2 angesteuert. Das Kommando „Cursor and Display Shift“ des HD44780 (siehe [2], Kapitel 5, „Kurzbeschreibung der einzelnen Befehle“) setzt den Cursor auf eine Adresse im DD-RAM. Dieses Kommando zur Umsetzung des Cursors kann mit der Steueranwiesung „\n“ (printf(), v_SM_Printf()) erreicht werden. Die Adresszuordnung der Cursorposition im DD-RAM können sie der Tabelle 49 entnehmen. Die Tabelle 50 enthält die Projektstruktur.
|
Display Position |
1 |
2 |
3 |
4 |
5 |
... |
13 |
14 |
15 |
16 |
|
DD-RAM Adr (Zeile 1) |
0x00 |
0x01 |
0x02 |
0x03 |
0x04 |
... |
0x0C |
0x0D |
0x0E |
0x0F |
|
Zeile 2 |
0x40 |
0x41 |
0x42 |
0x43 |
0x44 |
... |
0x4C |
0x4D |
0x4E |
0x4F |
Tabelle 49 DD-RAM-Adressen bei einem 2x16 Zeichen Display
Die Variablen „ucHPos“ und „ucVPos“ enthalten die aktuelle Position des Cursors. Sie sind lokal in der Funktion „main()“ vom Datentyp unsigned char definiert (siehe Listing 44, ). Die Bitvariable „btIsHor“ speichert den Wechsel zwischen horizontaler und vertikaler Bewegung (siehe ‚). Nach der Initalisierung von Port 2, dem Inkrementalgeber und der LCD-Anzeige wird auf eine Zustandsänderung des Inkrementalgebers gewartet (siehe ƒ). Bei einer Betätigung der Taste oder Bewegung des Inkrementalgebers wird die Auswertung begonnen. Ist die Taste betätigt worden, wird der Zustand von btIsHor gewechselt (siehe „). Danach wird abgewartet, bis die Taste wieder losgelassen wird (siehe …). Wurde der Inkrementalgeber gedreht, wird zuerst die Richtung überprüft (siehe †). Ist der Wert von cValue positiv, wird jetzt noch geprüft, ob es sich um eine horizontale Bewegung handelt (btIsHor = 1) und ob der Cursor bereits am linken Ende der Zeile steht (siehe ‡). Ist dies der Fall, wird „ucHPos“ um eins erhöht. Enthält btIsHor den Wert 0, wird „ucVPos“ auf 1 gesetzt (siehe ˆ). Ist der Wert negativ, finden die gleichen Überprüfungen statt, in diesem Fall auf die Minimalwerte. Vor dem Umsetzen des Cursors wird „cValue“ auf 0 gesetzt (siehe ‰). Das Umsetzen des Cursors erfolgt in diesem Beispiel mit der Funktion „v_SM_Printf()“ (siehe Š). Die Variable „ucVPos“ wird mit 0x40 multipliziert, da die zweite Zeile mit dieser Startadresse beginnt (siehe Tabelle 49).
|
Projektname |
Verzeichnis |
Verwendete Sourcemodule |
|
Test_LCDCursor |
Test_SteuerungLCDCursor |
TestSteuerungLCDCursor.c |
|
|
..\Library |
Encoder_Lib.c, PortConfig.c, CGRAM_V7.c, sm_printf.c, LCDLib_4bitPort.c, itoa.a51, bcd2hex.a51 |
Tabelle 50 Projekt Test_LCDCursor
|
‚
ƒ
„ …
†
‡
ˆ
‰ Š
|
#include <REG932.H> #include <Encoder_Lib.h> #include <PortConfig.H> #include <lcd_def.h> #include <sm_printf.h>
void main(void) { unsigned char ucHPos =0, ucVPos =0; bit btIsHor = 1; v_PortConfig(Port2, AllPins, BiDir); // Port 2 konfigurieren v_InitEncoder(); // Inkrementalgeber konfigurieren uc_LCDIni(_2LINE | _7DOTS); // Initialisierung des HD44780 EA = 1; // allg. Interruptsperre aufheben
while(1) { while(cValue == 0 && EncSwitch == 1); if (EncSwitch == 0) { // wechseln zwischen hor. und ver. Bewegung btIsHor = !btIsHor; while (EncSwitch == 0); // Warten bis Taste losgelassen } else // Auswertung der Cursorbewegung { if (cValue >0) { if (btIsHor == 1 && ucHPos < 0x0F) ucHPos++; else if (btIsHor == 0) ucVPos = 1; } else { if (btIsHor == 1 && ucHPos > 0) ucHPos--; else if (btIsHor == 0) ucVPos = 0; } cValue = 0; v_SM_Printf( "\n%c",ucVPos * 0x40 + ucHPos); } } } |
Listing 44 Inhalt von TestSteuerungLCDCursor.c
Im vorangegangenen Abschnitt haben sie den Umgang mit dem Inkrementalgeber in Verbindung mit dem LCD gelernt. Jetzt soll eine Menüführung mit Hilfe des Inkrementalgebers entstehen. In Abhängigkeit der Drehrichtung sollen unterschiedliche Menüpunkte aus dem Array „aucHauptMenu“ (siehe Listing 45, ) angezeigt werden. Das Array selbst liegt im Speicherbereich „code“. Als Endekennung für die Liste ist eine Zeile mit Minuszeichen vorhanden (siehe ‚). Damit man erkennt, dass die obere Zeile selektierbar ist, wird der Cursor nach Ausgabe des Textes an das erste Zeichen gesetzt (siehe ƒ). Nach dem Start des Programms werden die ersten zwei Zeilen des Arrays auf dem LCD ausgegeben. Das Programm wartet nun solange, bis der Inkrementalgeber gedreht wurde (siehe „). Ist der Wert von cValue positiv, wird überprüft, ob das untere Ende des Menüs ausgegeben ist. Diese Längenüberprüfung erfolgt mit dem sizeof()-Operator (siehe …). Dieser Operator liefert eine Konstante, in diesem Fall die Gesamtlänge des Arrays (sizeof(aucHauptmenu)) und die Länge eines Eintrags (sizeof(aucHauptmenu[0])), zurück. Damit lässt sich errechnen, wie viele Menüeinträge vorhanden sind. Man hätte an dieser Stelle auch eine Konstante mit der Länge 5 einfügen können. Wird ein weiterer Eintrag hinzugefügt bzw. ein Eintrag entfernt, müsste in diesem Fall die Konstante um eins erhöhen bzw. erniedrigt werden. Beim sizeof()-Operator wird dies automatisch beim Compilieren angepasst.
F !! Eine ausführliche Beschreibung zum sizeof()-Operator finden sie im Kapitel 11.9 [1]. !!
Nachdem der neue Offsetwert in der Variablen „ucMenuOff“ abgespeichert ist, wird die Variable cValue auf den Wert 0 gesetzt (siehe †). Das Programm gibt nun den zweiten und dritten Eintrag auf dem LCD aus und wartet danach wieder auf eine Änderung des Inkrementalgebers.
|
Projektname |
Verzeichnis |
Verwendete Sourcemodule |
|
Test_LCDMenu |
Test_SteuerungLCDMenu |
TestSteuerungLCDMenu.c |
|
|
..\Library |
Encoder_Lib.c, PortConfig.c, CGRAM_V7.c, sm_printf.c, LCDLib_4bitPort.c, itoa.a51, bcd2hex.a51 |
Tabelle 51 Projekt Test_LCDMenu
|
‚
ƒ „
…
†
|
#include <REG932.H> #include <Encoder_Lib.h> #include <PortConfig.H> #include <lcd_def.h> #include <stdio.h>
unsigned char code aucHauptMenu[][17] ={"Ausgabe ", "Einstellungen ", "Test ", "Reset ", "----------------"};
void main(void) { unsigned char ucMenuOff = 0; v_PortConfig(Port2, AllPins, BiDir); // Port 2 konfigurieren v_InitEncoder(); // Inkrementalgeber konfigurieren uc_LCDIni(_2LINE | _7DOTS); // Initialisierung des HD44780 EA = 1; // allg. Interruptsperre aufheben
while(1) { printf( "\n%c%s",0, aucHauptMenu[ucMenuOff]); printf( "\n%c%s",0x40, aucHauptMenu[ucMenuOff + 1]); printf("\n%c",0x0); while(cValue == 0 && EncSwitch == 1); if (cValue > 0) { if (ucMenuOff < ((sizeof(aucHauptMenu)/ sizeof(aucHauptMenu[0]))-2)) ucMenuOff++; } else { if (ucMenuOff > 0) ucMenuOff--; } cValue =0; } } |
Listing 45 Inhalt von TestSteuerungLCDMenu.c
Erstellen sie ein Projekt, das Untermenüs für jeden Eintrag anbietet. Mit Drücken der Taste des Inkrementalgebers soll der ausgewählte Menüeintrag übernommen werden. In der zweiten Zeile sollen die Untereinträge angezeigt und mit Hilfe des Inkrementalgebers verändert werden.
Die Untermenüs sind als eigenständige Arrays angelegt worden (siehe Listing 46, ). Somit wird nur soviel Speicherplatz belegt, wie jedes Untermenü benötigt. Bei einem mehrdimensionalen Array würde der Platzverbrauch immer vom größten Untermenü berechnet werden. Die Ausgabe auf dem LCD hängt jetzt von der Variablen „ucMenu“ ab. Enthält sie den Wert HAUPTMENU, werden beide Zeilen beschrieben, ansonsten nur die zweite Zeile (siehe ‚). Beim drücken der Taste wird „ucMenu“ überprüft (siehe ƒ). Befindet man sich im Hauptmenü, wird der aktuelle Offset von „ucMenuOff“ direkt als Menüeintrag übernommen. Diese direkte Zuweisung ist nur deshalb möglich, weil dem HAUPTMENU der Wert „-1“ zugewiesen ist (siehe „). Andernfalls wird überprüft, ob man sich im ersten Untereintrag befindet (siehe …). In dem Fall wird zurück ins Hauptmenü verzweigt. Das switch-Konstrukt wurde um die Untereinträge erweitert (siehe †).
|
Projektname |
Verzeichnis |
Verwendete Sourcemodule |
|
Test_2DimLCDMenu |
Test_SteuerungLCDUnterMenu |
Test_2DimLCDMenu.c |
|
|
..\Library |
Encoder_Lib.c, PortConfig.c, CGRAM_V7.c, sm_printf.c, LCDLib_4bitPort.c, itoa.a51, bcd2hex.a51 |
Tabelle 52 Projekt Test_2DimLCDMenu
|
‚
ƒ
„
…
†
|
#include <REG932.H> #include <Encoder_Lib.h> #include <PortConfig.H> #include <lcd_def.h> #include <stdio.h> enum enMenu {HAUPTMENU = -1, AUSGABE, EINSTELLUNGEN, TEST, RESET};
unsigned char code aucHauptMenu[][17] ={"Ausgabe ", "Einstellungen ", "Test ", "Reset ", "----------------"};
unsigned char code aucAusgabeMenu[][17] ={"Aus. Hauptmenu ", "Aus. Eintrag 2 ", "Aus. Eintrag 3 ", "Aus. Eintrag 4 ", "----------------"};
unsigned char code aucEinstellMenu[][17] ={"Ein. Hauptmenu ", "Ein. Eintrag 2 ", "Ein. Eintrag 3 ", "Ein. Eintrag 4 ", "----------------"};
unsigned char code aucTestMenu[][17] ={"Test Hauptmenu ", "Test Eintrag 2 ", "Test Eintrag 3 ", "Test Eintrag 4 ", "----------------"};
unsigned char code aucResetMenu[][17] ={"Res. Hauptmenu ", "Res. Eintrag 2 ", "Res. Eintrag 3 ", "Res. Eintrag 4 ", "----------------"};
void main(void) { unsigned char ucMenuOff = 0; char ucMenu = HAUPTMENU; v_PortConfig(Port2, AllPins, BiDir); // Port 2 konfigurieren v_InitEncoder(); // Inkrementalgeber konfigurieren uc_LCDIni(_2LINE | _7DOTS); // Initialisierung des HD44780 EA = 1; // allg. Interruptsperre aufheben
while(1) { if (ucMenu == HAUPTMENU) { printf( "\n%c%s",0, aucHauptMenu[ucMenuOff]); printf( "\n%c%s",0x40, aucHauptMenu[ucMenuOff + 1]); printf("\n%c",0x0); } else { switch(ucMenu) { case AUSGABE: printf( "\n%c%s",0x40, aucAusgabeMenu[ucMenuOff]); break; case EINSTELLUNGEN: printf( "\n%c%s",0x40, aucEinstellMenu[ucMenuOff]); break; case TEST: printf( "\n%c%s",0x40, aucTestMenu[ucMenuOff]); break; case RESET: printf( "\n%c%s",0x40, aucResetMenu[ucMenuOff]); break; } printf("\n%c",0x40); }
while(cValue == 0 && EncSwitch == 1); if (EncSwitch == 0) { if (ucMenu == HAUPTMENU) { ucMenu = ucMenuOff; ucMenuOff = 0; } else { if (ucMenuOff == 0) ucMenu = HAUPTMENU; } while (EncSwitch == 0); } if (cValue > 0) { switch(ucMenu) { case HAUPTMENU: if (ucMenuOff < ((sizeof(aucHauptMenu)/ sizeof(aucHauptMenu[0]))-2)) ucMenuOff++; break; case AUSGABE: if (ucMenuOff < ((sizeof(aucAusgabeMenu)/ sizeof(aucAusgabeMenu[0]))-2)) ucMenuOff++; break; case EINSTELLUNGEN: if (ucMenuOff < ((sizeof(aucEinstellMenu)/ sizeof(aucEinstellMenu[0]))-2)) ucMenuOff++; break; case TEST: if (ucMenuOff < ((sizeof(aucTestMenu)/ sizeof(aucTestMenu[0]))-2)) ucMenuOff++; break; case RESET: if (ucMenuOff < ((sizeof(aucResetMenu)/ sizeof(aucResetMenu[0]))-2)) ucMenuOff++; break; } } else { if (ucMenuOff > 0) ucMenuOff--; } cValue =0; printf("\n%c",0x3); } } |
Listing 46 Inhalt von Test_2DimLCDMenu.c