Inzwischen haben Sie das nötigste know-how, alle möglichen Experimentierplatinen, eine größere Anzahl 2051er auf der
hohen Kante und betrauern vielleicht schon die ersten 'für ein höheres Ziel' gestorbenen Controller - im Papierkorb
begraben und unvergessen. Mit einem derartigen Erfahrungsschatz und ein wenig freier Zeit ausgestattet kribbelt es Ihnen
in den Fingern: machen wir mal was wirklich intetessantes. |
|
das Display eine komplexe Menüsteuerung
mit nur wenigen Tasten implementieren.
Die Ansteuerung einer 'nackten' LCD-Anzeige ist ziemlich schwierig. doch zum Glück gibt es Steuer-IC's, die
die Ansteuerung der Hardware übernehmen und selbst viel einfacher mit TTL-Pegeln angesprochen werden. Durchgesetzt hat
sich ein Controller, der ursprünglich von Hitachi eingesetzt wurde (HD44780), und mittlerweile aber in fast
allen intelligenten Displays zu finden ist. Grundlage zu diesem Beispiel ist ein LCD-Display mit 16 Zeichen und einer Zeile aus dem Conrad-Katalog für etwa 30 DM. Prinzipiell ist die Ansteuerung aber auch auf größere Displays oder solche mit anderer Anordnung mit geringen Änderungen übertragbar. Der Ansteuerchip ist intelligent genug, um den ASCII-Zeichensatz direkt zu verstehen und einen Cursor zu verwalten. Außerdem beherbergt er ein wenig RAM und Ansteuerlogik für die Grundfunktionen. zurück zum Anfang |
Jede Datenübertragung wird dadurch begonnen, daß die Daten- und Steuerleitungen mit den entsprechenden Pegeln programmiert werden. Dann wird Enable auf high gesetzt und etwa 2 Millisekunden gewartet. Anschließend wird Enable wieder auf low gesetzt, und die Daten- und Steuerleitungen können wieder anderweitig genutzt werden. |
Im 8 Bit-Modus werden alle Daten auf einmal versandt, im 4 Bit-Modus wird zuerst das High- und dann das Low-Nibble (höher- und niederwertigen 4 Bit eines Bytes)
übertragen. Dazu ist übrigens der Befehl 'SWAP A', der High- und Low-Nibble austauscht, gut geeignet.
Die Anschlußbelegung eines gängigen LCD's sieht folgendermaßen aus: |
Pin Nr. |
Symbol |
Funktion | ||
---|---|---|---|---|
1 |
GND |
Spannungsversorgung, Masse |
||
2 |
Vcc |
Spannungsversorgung, +5V |
||
3 |
Vee |
Kontrastregler, Spannungsteiler zwischen Versorgung 10k |
||
4 |
RS |
H: Dateneingabe |
L: Befehlseingabe |
|
5 |
RW |
H: Daten lesen |
L: Daten schreiben |
|
6 |
E |
Enable, wenn H werden Daten entgegengenommen |
||
7 |
D0 |
Datenbus bei 8 Bit - Ansteuerung |
||
8 |
D1 |
|||
9 |
D2 |
|||
10 |
D3 |
|||
11 |
D4 |
Datenbus bei 4 Bit - Ansteuerung |
|
|
12 |
D5 |
|||
13 |
D6 |
|||
14 |
D7 |
zurück zum Anfang |
Die erste Übertragung sollte unbedingt den Übertragungsmodus (4- oder 8 Bit) festlegen. Dies ist der
einzige Befehl, der in beiden Modi funktioniert. Daraufhin sendet man Kommandos, die diverse Einstellungen
nach den eigenen Wünschen vornehmen, beispielsweise einen blinkenden Cursor oder das Scrollverhalten festlegen.
Dann setzt man die DD (DisplayData) - Adresse auf 0 und sendet den Befehl 'clear display'. Nun kann man
mit 'write data' Zeichen auf dem Display ausgeben. Das Display beherrscht einen besonderen Scrollmodus, der es erlaubt, zuerst den gesamten Text in das RAM des Displaycontrollers zu schreiben und anschließend nur die interne Adresse, ab der der Displaycontroller Zeichen zur Anzeige bringt, zu verschieben. Man sendet also zuerst den Text und dann nur noch die Befehle, um den angezeigten Ausschnitt zu verschieben. Leider hat diese Ansteuerung einen Nachteil: weil bei mehrzeiligen Displays verschiedene Zeilen auch verschiedene Texte enthalten können, und die Zeichen beim Scrollen nicht von der rechten unteren Ecke der Anzeige zur linken oberen springen dürfen, sind |
die RAM-Adressen für die verschiedenen Anzeigezeilen nicht fortlaufend. Besitzt man also ein Display mit
zwei Zeilen und 8 Spalten und möchte einen 16 Zeichen langen Text an das Display schicken, so muß
man erst die ersten 8 Zeichen senden, dann die interne RAM-Adresse verstellen und kann dann erst den Rest
des Textes verschicken. Theoretisch kann man mit 'Function set' die Anzahl der Displayzeilen einstellen und
somit bei einzeiligen Displays alle Zeichen hintereinander senden - theoretisch, denn bei meinem hat
das N-Bit im 'function set' keine Auswirkungen auf das Verhalten gezeigt, so daß das einzeilige Display trotzdem
eine 'Lücke' im angezeigten RAM-Inhalt aufwies.Üblicherweise liegen die Zeilenanfänge für die erste Zeile
bei 00 und die zweite Zeile bei 40. Weiteres kann man dem Datenblatt, das mit einem
Display mitgeliefert wird, entnehmen. Wie die Steuerkommandos aussehen, kann man in der folgenden Tabelle nachlesen: |
Kommando | Code | Beschreibung | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | ||
Clear display | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | Löscht das Dsiplay und setzt den Cursor zurück auf Adresse 0 |
Cursor home | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | * | Setzt den Cursor auf Adresse 0 und bringt eine gescrollte Displayanzeige zurück auf 0. Der RAM-Inhalt bleibt dabei unverändert. |
Entry mode set | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S | Setzt die Cursor-Bewegungsrichtung(I/D), und ob das Display mitscrollt (S) |
Display On/Off control | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B | Schaltet das Display ein (D), den Cursor sichtbar (C) und bestimmt, ob der Cursor blinken soll (B) |
Cursor/display shift | 0 | 0 | 0 | 0 | 0 | 1 | S/L | R/L | * | * | Bestimmt, ob der Cursor verschoben oder das Display gescrollt werden soll (S/C) und in welcher Richtung das geschieht (R/L). Der RAM-Inhalt bleibt unverändert. |
Function set | 0 | 0 | 0 | 0 | 1 | DL | N | F | * | * | Setzt den Übertragungsmodus (DL), die Anzahl der Displayzeilen (N) und die Schriftart (F), sofern unterstützt. |
Set CGRAM address | 0 | 0 | 0 | 1 | CGRAM address | Setzt die Zeichengeneratoradresse. (Die ASCII-Zeichen 00h - 0Fh können selbst programmiert werden) Nach diesem Kommando werden die nächsten Lese- und Schreiboperationen im Zeichengenerator-RAM durchgeführt. | |||||
Set DDRAM address | 0 | 0 | 1 | DDRAM address | Setzt die Display RAM-Adresse. Nach diesem Kommando werden die nächsten Lese- und Schreiboperationen im Display-RAM durchgeführt. | ||||||
Read busy-flag and address counter | 0 | 1 | BF | Address counter | Liest das Busy-flag (BF), das anzeigt, ob eine interne Operation noch nicht abgeschlossen ist, und gibt die aktuelle RAM-Adresse zurück. | ||||||
Write to CGRAM or DDRAM | 1 | 0 | write data | Schreibt Daten an die eingestellte Adresse | |||||||
Read from CGRAM or DDRAM | 1 | 1 | read data | Liest daten von der eingestellten Adresse. Weil das Display viel langsamer ist als ein steuernder Microcontroller, sollten möglichst in der MCU die geschriebenen Daten zwischengespeichert werden, um das Lesen zu vermeiden. |
Bit | Einstellungen | |
---|---|---|
I/D | 0 = Cursor-Position -1 | 1 = Cursor-Position +1 |
S | 0 = Kein Scrolling | 1 = Display scrollt |
D | 0 = Display ausschalten | 1 = Display einschalten |
C | 0 = Cursor nicht anzeigen | 1 = Cursor anzeigen |
B | 0 = Cursor blinkt nicht | 1 = Cursor blinkt |
S/C | 0 = Cursor wird bewegt | 1 = Display wird gescrollt |
R/L | 0 = Scrollen nach links | 1 = Scrollen nach rechts |
DL | 0 = 4 Bit-Modus | 1 = 8 Bit-Modus |
BF | 0 = Bereit | 1 = Interne Operationen laufen ab, es werden keine Daten entgegengenommen |
zurück zum Anfang |
Diese Beispiel dient dazu, die Ansteuerung eines Displays zu zeigen. Wie schon weiter oben im Text bemerkt beherrscht mein Displaycontroller offenbar keine einzeilige Ansteuerung, so daß der 2051er einen Zähler mitführen muß, der die Anzahl der gesendeten Zeichen festhält und somit das Vorsetzen der RAM-Adresse des Displays steuert. | Damit die Ansteuerung selbst wirklich universell einsetzbar wurde, ist sie als Include-Datei für den Assembler konzipiert und empfängt alle notwendigen Parameter über Präcompilervariablen. Die Benutzung der Include-Bibliotheken sieht dann folgendermaßen aus: |
$NOMOD51 $INCLUDE (89C1051.MCU) ; Präcompilervariablen festlegen ENABLE EQU P3.2 ; IO der einzelnen Leitungen RS EQU P3.7 RW EQU P3.4 TEST EQU P3.5 DATEN EQU P1 ; der Datenport ; Speicher vergeben ; ein Byte für den Zähler im LCD-Modul LCD_RAM EQU 8 ; Adresse 8 vergeben ; Startadresse des Codes: org 00h ajmp start ; hier werden die Funktionen eingebunden $INCLUDE (time.inc) $INCLUDE (lcd.inc) ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Chip initialisieren ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - start: ; Stack auf 30h hinter Registerbänke und Bit-Area setzen mov sp,#30h ;---------------------------------------------------------------------------- ; Hauptprogramm ;---------------------------------------------------------------------------- main: acall F_LCD_init ; Display initialisieren mov DPTR,#text1 ; den Text ausgeben acall F_LCD_prints mov a,#2 ; 2 Sekunden warten acall F_wait_s ende: ajmp ende ; anhalten text1: DB 'Ausgabedemo!',0 ; der auszugebende String ENDDamit das Hauptprogramm unabhängig vom Anschluß über 8 oder 4 Datenleitungen eingesetzt werden kann, existieren 2 Bibliotheken, die genau gleich angesprochen werden können. Hier abgebildet ist die Bibliothek für den 8 Bit-Anschluß. Beide Bibliotheken benötigen zum Funktionieren eine weitere, die das Timing und die Zeitsteuerung übernimmt. ;---------------------------------------------------------------------------- ; Funktionen zum Ansteuern eines parallelen LCD-Displays ;---------------------------------------------------------------------------- ; F_LCD_init: muß zuerst aufgerufen werden ; F_LCD_clear: lösche Bildschirm ; F_LCD_printbd: Ausgabe eines Bytes als Zahl (ACC, dezimal) ; F_LCD_printc: Ausgabe eines Bytes als Char (ACC) ; F_LCD_prints: Ausgabe eines Strings (DPTR, Nullterminiert) ;---------------------------------------------------------------------------- ; benötigt werden: ; - time.inc ; - ein Byte RAM LCD_RAM ; - Pin Enable ENABLE ; - Pin RS RS ; - Pin R/W RW ; - Port für Daten DATEN ;---------------------------------------------------------------------------- F_LCD_clear: push ACC mov a,#1 acall F_LCD_send_b mov LCD_RAM,#0 pop ACC ret ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - F_LCD_init: ; LCD initialisieren push ACC ; 1.: einige ms warten, bis sich der Spannungspegel stabilisiert hat mov a,#10 acall F_wait_m clr RS clr RW clr ENABLE mov a,#38h acall F_LCD_send_b mov a,#1100b acall F_LCD_send_b mov a,#00111100b acall F_LCD_send_b acall F_LCD_clear pop ACC ret ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - F_LCD_printc: ; ausgabe eines Charakters aus ACC (benötigt ein Byte RAM - LCD_RAM) ; ist der Spalt im RAM erreicht? push ACC inc LCD_RAM mov a,LCD_RAM clr c subb a,#9 jnz F_LCD_printc_weiter ; RAM weitersetzen mov a,#128+40 acall F_LCD_send_b F_LCD_printc_weiter: pop ACC ; Zeichen ausgeben acall F_LCD_send_d ret ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - F_LCD_printbd: ; ausgabe eines Bytes als Zahl push ACC push 0 push B mov B,#100 div AB add a,#30h acall F_LCD_printc mov a,B mov B,#10 div AB add a,#30h acall F_LCD_printc mov a,B add a,#30h acall F_LCD_printc pop B pop 0 pop ACC ret ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - F_LCD_prints: ; ausgabe eines mit 0 terminierten Strings aus DPTR push ACC push 0 mov R0,#0 F_LCD_prints_anf: mov a,R0 movc a,@A+DPTR jz F_LCD_prints_weiter inc R0 acall F_LCD_printc mov a,R0 subb a,#16 jnz F_LCD_prints_anf F_LCD_prints_weiter: pop 0 pop ACC ret ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - F_LCD_send_b: ;sendet Befehle aus ACC an DATEN clr RS clr RW ajmp F_LCD_send ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - F_LCD_send_d: ;sendet Daten aus ACC an DATEN clr RW setb RS F_LCD_send: mov DATEN,a setb ENABLE push ACC mov a,#2 acall F_wait_m pop ACC clr ENABLE retDas komplette Beispiel inclusive der benötigten Bibliotheken kann von der Download-Page bezogen werden. zurück zum Anfang |
Hier finden sich die im Text verstreuten Literaturquellen und Links zum Thema noch einmal zum Nachschlagen und Wiederfinden | |
http://www.doc.ic.ac.uk/~ih/doc/lcd/index.html | HD44780 Instruction Set - näher erläutert |
http://www.8052.com/tutlcd.htm | Tutorial: Introduction to LCD Programming |
http://www.repairfaq.org/filipg/LINK/F_Tech_LCD.html | LCD Technical FAQ INDEX V (September_1996a) |
http://eio.com/datashet.htm | HD44780 (LCD II) (Dot Matrix LCD Controller & Driver) Data Sheets |
http://www.iaehv.nl/users/pouweha/lcd1.htm | How to control HD44780-based Character-LCD |
http://www.geocities.com/SiliconValley/Bay/8302/lcd.pdf | The Concise LCD Data Sheet |