Anschluß eines LCD-Displays

Pinbelegung   LCD-Kommandos   Beispiel für die Ansteuerung   Bibliographie
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.

Allgemeines zu intelligenten LCD-Displays

LCD-Modul, Vorderansicht
Viele Microcontrolleranwendungen benötigen eine Ausgabemöglichkeit nicht nur über eine 7-Segment-Anzeige, sondern auch für kurze Texte, Meßwerte oder sogar kleine Animationen. Dazu bieten sich nach dem rapiden Preisverfall der letzten Jahre LCD-Displays an, die auch in zahlreichen kommerziellen Produkten ihre Anwendung finden und auch eigenen Projekten einen professionellen Anstrich geben. So kann man über
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

Anschlußbelegung

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 LCD-Kommandos

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

Beispiel für die Ansteuerung

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
END
Damit 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. Rückseite des LCD-Moduls

;----------------------------------------------------------------------------
;  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
        ret
Das komplette Beispiel inclusive der benötigten Bibliotheken kann von der Download-Page bezogen werden.
zurück zum Anfang

Bibliographie

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

zurück zum Anfang

Startseite Seite vor
Erik Buchmann
EMail an: Owner@ErikBuchmann.de