LCD-Module - Der AVR hat viel zu sagen
Materialbedarf
Grundaufbau von LCD-Modulen
Mit Mikrokontrollern kann man Einiges anstellen. Sehr umfangreiche und komplexe Geräte sind mit diesen intelligenten Silizium-Käfern möglich. Bei größeren Projekten werden dann bald Anzeigen benötigt, welche etwas mehr als einen Zustand per Leuchtdiode oder eine einfache Zahl mit Hilfe von 7-Segment-Anzeigen, darstellen können. Hier kommen dann LCD-Display-Module zum Einsatz.
Auf dem Markt befinden sich etliche verschiedene Displayarten. Angefangen von sehr kleinen Displays mit einer Displaygröße von 1x8 Zeichen bis hin zu größeren Text-Displays die 4x40 Zeichen darstellen können. Außerdem sind verschiedene Hintergrundbeleuchtungen möglich. Die einfachsten sind auch ohne Beleuchtung erhältlich und kosten oft nur ein paar €.
Verschiedene LCD-Module
Das
hier verwendete Display der Firma Displaytech kann 16 Zeichen in 2
Zeilen darstellen. An der oberen Seite befinden sich die Anschlusspins
für die Verbindung zum Computersystem. In der Regel sind es 14 oder 16 Pins. Bei Displays anderer Hersteller, kann die Kontaktleiste sich auf einer anderen Seite befinden und unter Umständen auch anders angeordnet sein. Da auf jeden Fall das entsprechende Datenblatt durchsehen und ggf. die weiter unten gezeigten Bau- und Schaltpläne entsprechend abändern. Das Schöne an solchen Anzeigen ist es, dass diese relativ leicht anzusteuern sind. Man muss nur ein paar ganz wenige Regeln beachten. |
Um das Display anzusteuern, muss man natürlich wissen, welches Pin welche Funktion erfüllt. Hierzu kurz die Beschreibungen:
GND oder VSS | Der Masseanschluss des Displays. Dieser Pin muss mit 0V verbunden werden. |
Vcc oder VDD | Plusanschluss für eine Betriebsspannung von 4.5-5.5V. |
Vo | Dies ist der Pin für die Kontrasteinstellung. Die Kontrastspannung sollte bei dem hier verwendeten Display zwischen 0 und 1.5V liegen. Näheres dazu steht im entsprechenden Datenblatt. |
RS |
Legen wir hier ein 0-Signal an, sagen wir dem Display, dass wir dem Display Steuerdaten senden oder auslesen wollen. Wird ein 1-Signal angelegt, kann man Textdaten senden. |
R/W | Bei einem 0-Signal erwartet das Display Daten. Wird hier 1 angelegt, können Daten ausgelesen werden. |
E | Man muss hier ein 1-Signal anlegen, damit dass Display die angelegten Daten/Steuersignale verarbeitet. |
LED+/- | Besitzt das Display eine Hintergrundbeleuchtung, wird hier die Spannung hierfür angelegt. Den nötigen Vorwiderstand muss man aus dem Datenblatt entnehmen. Bei Displays mit 14 Pins, entfallen diese Anschlüsse. |
Das LCD-Modul wird zum Leben erweckt
Bevor man das Display verwenden kann, muss es leider noch für den Einsatz für Steckboards vorbereitet werden. Die Anschlüsse der meisten LCD-Module bestehen nur aus einfachen Lötaugen an denen man Kabel oder Stecker löten kann. Um nun das Modul auf dem Board einzusetzen müssen hier Stiftleisten oder besser, einzelne Pins ( Pins bei DES) eingelötet werden. Einzelne Pins haben den Vorteil, dass man diese ohne große Probleme wieder auslöten kann, wenn das Display anderweitig verwendet werden soll.
Hat
man nun das Display vorbereitet und die nebenstehende Schaltung
aufgebaut, kann man das Ganze in Betrieb nehmen. Den Trimmer sollte man jetzt in Richtung Masse drehen. Es sollte dann, in der oberen Hälfte des Displays, ein schwarzer Balken sichtbar werden. Damit meldet das Display: Ich bin in Ordnung und Einsatzbereit. Wenn wir nun das Display verwenden wollen, müssen wir natürlich erst einmal ein kleines Programm für den AVR schreiben. Auf die Ansteuerung des Eingangs RW verzichten wir hier und setzen diesen Pin dauerhaft auf 0, welches dem Display andeutet, dass wir nur darauf schreiben möchten. |
$regfile "m8def.dat" $crystal = 1000000 Config Portd = Output Config Portb.6 = Output Config Portb.7 = Output Lcd_data Alias Portd Lcd_rs Alias Portb.6 Lcd_en Alias Portb.7 Lcd_en = 0 : Waitms 50 Lcd_rs = 0 Lcd_data = 56 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 Lcd_data = 12 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 Lcd_data = 1 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 Lcd_data = 2 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 Lcd_rs = 1 Lcd_data = 65 : Lcd_en = 1 : Lcd_en = 0 : Waitus 40 Stop |
Als erstes müssen wir natürlich wieder die üblichen Einstellungen in Bascom vornehmen. Da der Datenport des Displays an Port D, RS an Port B6 und der Enable-Anschluss an Port B7 hängt, definieren wir diese Pins des AVR entsprechend als Ausgang. Da wir nun aber nicht jedes mal die Ports benennen wollen, was zukünftige Programme unübersichtlich macht, benennen wir die Anschlüsse für uns verständlicher um:
Lcd_data Alias Portd Lcd_rs Alias Portb.6 Lcd_en Alias Portb.7 |
Nun brauchen wir z.B. nur 'Lcd-Data' schreiben, wenn wir den Datenport des LCD-Moduls meinen.
Anschließend müssen wir die Startbedingungen für das Display festlegen.
Lcd_en = 0 : Waitms 50 |
Als erstes sorgen wir dafür, dass der Enable-Pin des Displays auf 0 ist. Somit spielt es vorerst keine Rolle, welchen Zustand die anderen Pins haben, da dass Displays diese momentan ignoriert. Dann müssen wir eine Zeit mit 'Waitms 50' warten. Dieser Befehl ist beim Einschalten wichtig. Das Display benötigt nach dem Anlegen der Betriebsspannung eine kurze Zeit um z.B. seinen Selbsttest durchzuführen.
Nun können wir beginnen das Display anzusteuern. Bevor wir aber die ersten Zeichen auf der Anzeige erscheinen lassen können, erwartet das LCD-Modul, dass wir ihm noch bestimmte Betriebsdaten vorgeben. Dazu müssen wir zu Beginn einige Steuerwörter übertragen. Aber wie geschieht das nun genau?
Um Steuerwörter oder Textdaten zum LCD-Modul zu übermitteln, müssen diese Daten erst einmal an den Datenbus angelegt werden. Als nächsten Schritt müssen wir RS so einstellen, dass das Display weiß, ob wir Steuer- oder Textdaten senden wollen. Bei einem 0-Signal erwartet die Anzeige Steuerdaten. Nun müssen wir nur noch das Enable-Signal kurz auf 1 schalten, damit die Anzeige die Daten übernimmt und entsprechend ausführt. |
Lcd_rs = 0 Lcd_data = 56 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 Lcd_data = 12 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 Lcd_data = 1 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 Lcd_data = 2 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 |
Da die ersten Daten, die wir zum Display senden wollen Steuerdaten sind, setzen wir den RS-Pin auf 0.
Als nächstes können wir dann die entsprechenden Daten an den Bus legen, welches wir zum Beispiel mit 'Lcd_data = 56' tun. Nun liegen auf dem LCD-Bus alle Bedingungen vor, so dass wir dem Modul sagen können, dass es die Daten übernehmen kann. Dies geschieht mit 'Lcd_en = 1' und gleich danach wird Enable wieder auf 0 gesetzt. Jetzt muss eine kurze Zeit gewartet werden. Das Ganze wird noch einige Male mit anderen Steuerwörtern wiederholt.
Auf die einzelnen Steuerwörter gehen wir später noch ein. Nur soviel: Wir aktivieren das Display für einen 8-Bit-Datenbus, sagen ihm, das wir beide vorhanden Zeilen verwenden wollen, er soll den Textcursor automatisch erhöhen, den Anzeigespeicher löschen und den Cursor auf den Startpunkt legen.
Sicherlich werden sich einige fragen, warum man nach dem pulsen des Enable-Signals immer 2 ms warten soll. Bei einigen Steuerbefehlen benötigt das Display die Zeit um diese auszuführen. Das Modul meldet zwar zurück, wenn es wieder bereit ist, aber dann hätten wir den RW-Pin auch mit verwalten müssen, die Daten auslesen und entsprechend verarbeiten. Der Aufwand wäre immens höher. Daher warten wir eine bestimmte Zeit in der das Display auf jeden Fall fertig ist. Die Abfrage des Status des Displays ist nur bei Anwendungen sinnvoll, wo es auf höchste Ausgabegeschwindigkeit ankommt, was wohl recht selten nötig sein wird.
Nun ist das Display eingestellt und wir können ihm die ersten Textdaten übermitteln. Dafür dienen die folgenden Befehle:
Lcd_rs = 1 Lcd_data = 65 : Lcd_en = 1 : Lcd_en = 0 : Waitus 40 Stop |
Da jetzt Textdaten übermittelt werden sollen, setzen wir den RS-Pin auf 1. Der Rest ist im Grunde wie bei den Steuerdaten. Datenwert an den Bus anlegen. Hier legen wir das Ascii-Zeichen für 'A' an. Enable kurz auf 1 setzen. Anschließend eine kurze Zeit warten. Die Wartezeit kann hier erheblich reduziert werden. Nach Ausführung des Befehls sehen wir ein 'A' auf der linken Seite des Displays stehen.
Es ist natürlich mühselig, ständig die ganzen Steuersignale manuell zu setzen. Das Ganze kann man auch mit kleinen Unterprogrammen erledigen. Eine Daten-Variable hilft uns den Sub-Routinen mitzuteilen, welche Daten übermittelt werden sollen. Dadurch entsteht ein neues Programm:
$regfile "m8def.dat"
' ### Hauptprogramm
' ### Hilfsroutinen zur Ansteuerung des LCD-Moduls
|
Hier ist gut zu sehen, dass das Hauptprogramm doch ein wenig Übersichtlicher und auch verständlicher ist. Der Endanwender will nicht unbedingt wissen, wie die Ansteuerung genau funktioniert.
Eine Ausgabe eines einzelnen Zeichens ist natürlich nicht gerade ein Weltwunder. Wir wollen ja ganze Texte ausgeben, wofür das LCD-Modul auch gedacht ist. Hierzu wird das Steuerprogramm mit einigen Variablen und einer weiteren Routine ergänzt:
$regfile "m8def.dat"
' ### Hauptprogramm
' ### Hilfsroutinen zur Ansteuerung des LCD-Moduls
|
Es fallen wohl zunächst die neu definierten Variablen auf.
Dim Cnt As Byte Dim Txt As String * 16 Dim Strarr(16) As Byte At Txt Overlay |
Die Variable Cnt benötigen wir für die Stringausgabe. Welche Zeichenkette ausgegeben werden soll, wird in der Variablen Txt festgelegt. Damit wir diese Variable später einfach Zeichenweise auslesen können, greifen wir zu einem Trick. Wir definieren noch ein Variablenfeld mit Byte-Variablen und weisen Bascom an, dieses an die selbe Speicheradresse wie die String-Variable anzulegen. Nun reicht es aus, wenn wir ein Zeichen auslesen wollen, einfach die Feldvariable mit der Zeichennummer abzufragen.
Dies wird in der Stringausgabe-Routine deutlich:
Write_string: For Cnt = 1 To Len(txt) Dat = Strarr(cnt) : Gosub Write_data Next Return |
Um eine Zeichenkette auszugeben, müssen wir die Zeichen einer nach dem anderen zum Display schicken. Hierzu öffnen wir erst einmal eine Zählschleife, der uns in Cnt die Werte von 1 bis zur maximalen Länge des gewünschten Strings übergeben.
Dank des Bytevariablenfeldes brauchen wir nun der Variablen Dat nur das aktuelle Feldelement zuzuweisen und haben somit gleich das richtige Zeichen. Anschließend rufen wir die Subroutine zur Ausgabe des Zeichens an das LCD-Modul auf.
Die Befehle der LCD-Module
Wollen wir das LCD-Modul komplett beherrschen, müssen wir natürlich auch wissen, welche Steuerwörter des Display versteht. Nachfolgend sollen die wichtigsten Steuerwörter vorgestellt werden. Welche Möglichkeiten es noch gibt, kann man im Datenblatt des Displays nachsehen.
Bei den nachfolgenden Beispielroutinen wird nun aber nur noch das Hauptprogramm gezeigt. Der Rest des Programms bleibt unverändert.
Display Clear
Der wohl einfachste Befehl ist der Befehl zum Bildschirm löschen. Dieser Befehl wird ausgelöst, wenn wir dem Display eine 1 als Steuerwort senden. Man muss aber bedenken, dass dieser Befehl nicht die sonstigen Einstellungen ändert. Es wird also die Position des Cursors z.B. nicht geändert. Soll dieser auch wieder auf den Anfang des Displays gesetzt werden, muss anschließend noch der Befehl 'Return Home' ausgeführt werden.
Dat = 56 : Gosub Write_ctrl : Dat = 12 : Gosub Write_ctrl Waitms 3000
Dat = 1 : Gosub Write_ctrl |
Bei diesem Programm erscheint erst 'Es funktioniert!' auf dem Display. Nach 3 sek. verschwindet dieser Text aber wieder, da der Bildschirm gelöscht wird.
Return Home
Immer wieder muss man die Ausgabe auf der Anzeige aktualisieren. Hierzu ist es notwendig, dass man den Cursor wieder an den Anfang des Displays setzt. Hierzu muss man dem Modul nur den Wert 2 als Steuerwort senden.
Dat = 56 : Gosub Write_ctrl : Dat = 12 : Gosub Write_ctrl Txt = "Guten" : Gosub Write_string
Waitms 500 : Dat = 2 :
Gosub Write_ctrl
Waitms 500 : Dat = 2 :
Gosub Write_ctrl |
Es erscheint nun abwechselnd 'Guten' und ' Tag ' auf dem Display.
Set DD Ram Address
Oft soll ja nicht das Ganze Display geändert/beschrieben werden. Will man nun an eine andere Position des Displays schreiben muss man den Cursor auf die neue Position setzen. Dies geschieht, indem wir neue Position im so genannten DD Ram ändern. Das DD Ram des Displays ist der Bildschirmspeicher. Dieser ist fortlaufend und nicht, wie beim PC gewohnt, in X und Y unterteilt.
Will man die Position neu setzen muss man das Steuerwort 128 addiert mit der neuen gewünschten Position senden. Das erste Zeichen ist also 128+0, das zweite 128+1 usw. Eine Besonderheit gibt es noch bei Displays welche mehrere Zeilen besitzen. Man könnte jetzt meinen bei Adresse 17 beginnt dann die neue Zeile bei unserem 2x16 Zeichen Moduls. Derjenige wird leider enttäuscht.
Die zweite Zeile beginnt hier ab Adresse &H40. Bei anderen Anzeigen kann dies anders sein. Da also vorher bitte ein Blick ins entsprechende Datenblatt werfen.
Die kleine Tabelle verdeutlicht dies, bei unserem Display, evtl. noch etwas:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | |
1. Zeile | &H00 | &H01 | &H02 | &H03 | &H04 | &H05 | &H06 | &H07 | &H08 | &H09 | &H0A | &H0B | &H0C | &H0D | &H0E | &H0F |
2. Zeile | &H40 | &H41 | &H42 | &H43 | &H44 | &H45 | &H46 | &H47 | &H48 | &H49 | &H4A | &H4B | &H4C | &H4D | &H4E | &H4F |
Hierzu noch kleines Beispielprogramm:
Dat = 56 : Gosub Write_ctrl : Dat = 12 : Gosub Write_ctrl Dat = 128 + &H00 : Gosub Write_ctrl : Txt = "1" : Gosub Write_string Dat = 128 + &H0A : Gosub Write_ctrl : Txt = "10" : Gosub Write_string Dat = 128 + &H40 : Gosub Write_ctrl : Txt = "2. Zeile" : Gosub Write_string Stop |
Es ist sehr schön zu sehen, wie die einzelnen Displaypositionen angesteuert werden um dann die entsprechenden Texte auszugeben.
Set Function
Bevor das Display nach dem Anlegen der Betriebsspannung verwendet werden kann, müssen erst die wichtigsten Parameter eingestellt werden. Hierzu muss ein Steuerbyte mit folgendem Aufbau gesendet werden:
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
0 |
0 |
1 |
DL |
N |
F |
X |
X |
Hierbei wird mit Hilfe des Bits DL festgelegt, ob das Display im 8-Bit Modus betrieben wird (DL=1) oder ob es Daten mit 4 Bit verarbeiten soll (DL=0).
Wird N auf 1 gesetzt, sagen wir dem Display, dass wir eine mehrzeilige Anzeige wünschen. Wird dieses Bit auf 0 gesetzt, wird nur die erste Zeile angesteuert.
Mit F legen wir die Zeichengröße fest. Somit kann man mit 5x7 Punkt Zeichen arbeiten (F=0) oder auch 5x10 Punkt (F=1). Der letzte Modus ist interessant, wenn das Display wichtige Werte anzeigen soll. Diese erscheinen dann groß auf der Anzeige.
Die beiden untersten Bits sollte man auf 0 setzen. Einige Hersteller nutzen diese um das Display z.B. in eine andere Sprache umzuschalten etc.
Display On/Off
Nach der Initialisierung der Anzeige sollte man dem Display noch mitteilen, wie es sich verhalten soll. Dazu dient folgendes Steuerbyte:
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
D |
C |
B |
Bei gesetzten Bit D wird das Display Aktiv. D.h. der Text ist sichtbar. Schaltet man dieses Bit auf 0, geht das Display aus. Einige Displays gehen hier auch in den Stromsparmodus. Dies ist vor allem bei batteriebetriebenen Geräten interessant.
Möchte man den Cursor sehen, muss das Bit C gesetzt werden.
Bit B bestimmt, ob der Cursor statisch oder blinken sein soll. Bei einer 1 blinkt dieser.
Definition eigener Symbole
Das schöne an den gängigen LCD-Modulen ist es, dass man hier bis zu 8 eigene Symbole definieren kann. Hierzu muss man natürlich erst einmal das gewünscht Symbol entwerfen. Jedes neue Symbol besteht aus 8 Bytes die die einzelnen Punktzeilen darstellen. Hierbei werden aber nur die unteren 5 Bit beachtet, da dass Display ja nur Zeichen mit 5 Punkten Breite darstellen kann.
Entwerfen wir einmal ein einfaches Symbol:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
X | X | X | . | . | . | . | . |
X | X | X | . | # | # | # | . |
X | X | X | # | . | . | . | # |
X | X | X | # | . | . | . | # |
X | X | X | # | . | . | . | # |
X | X | X | . | # | # | # | . |
X | X | X | . | . | . | . | . |
X | X | X | . | . | . | . | . |
Wie bekommen wir aber nun, dieses Symbol in das Display?
Hierzu müssen wir dem Modul mit Hilfe eines Steuerwortes mitteilen, dass wir ihm als nächstes Daten für das so genannte CG-Ram senden wollen. Das nächste Datenbyte, was dann gesendet wird, wird nicht ausgegeben, sondern im Zeichengenerator-Ram (CG-Ram) abgelegt. Diese Prozedur müssen wir für jede Zeile des Zeichens wiederholen.
Das Steuerbyte enthält neben dem Wert 64 noch die Speicheradresse der zu definierenden Symbolzeile. Byte 0 ist somit die erste Zeile des Zeichens 0, Byte 1 die zweite Zeile. Das Zeichen 1 fängt beim 8. Byte an, das 2. Zeichen bei Byte 16 usw.
Lassen wir doch mal das Display eine Art blinkende Lampe anzeigen:
Dat = 56 : Gosub Write_ctrl :
Dat = 12 : Gosub Write_ctrl |
Der 4 Bit Modus
Es gibt Eines, was bei Mikrokontrollern grundsätzlich Mangelware ist. Um die Portzahl nicht allzu extrem zu belasten, bieten die LCD-Module die Ansteuerung mit 4 Bit an. Hierbei werden die zu übermittelnden Daten in 2x4 Bit aufgeteilt und zu erst die oberen 4 Bit und Anschließend die unteren 4 Bit übertragen. Um dies zu testen, bauen wir die Schaltung ein wenig um.
Hier
werden die Leitung von Port D Bit 0 bis Port D Bit 3 aufgetrennt und die
offenen Anschlüsse auf der LCD-Seite auf 0 V gelegt. Nun wir das Modul nur noch durch Port D Bit 4 bis Port D Bit 7 mit Daten versorgt. Damit das Display nun angesteuert werden kann, muss die Software auch geändert werden. |
$regfile "m8def.dat" $crystal = 1000000 Dim Dat As Byte Dim Cnt As Byte Dim Txt As String * 24 Dim Strarr(24) As Byte At Txt Overlay Config Portd = Output Config Portb.6 = Output Config Portb.7 = Output Lcd_data Alias Portd Lcd_rs Alias Portb.6 Lcd_en Alias Portb.7 Lcd_en = 0 : Waitms 50 ' ### Hauptprogramm Dat = 40 : Gosub Write_ctrl : Dat = 12 : Gosub Write_ctrl Dat = 1 : Gosub Write_ctrl : Dat = 2 : Gosub Write_ctrl Txt = "Es funktioniert!" : Gosub Write_string Stop ' ### Hilfsroutinen zur Ansteuerung des LCD-Moduls Write_ctrl: Lcd_rs = 0 : Lcd_data = Dat And &HF0 : Lcd_en = 1 : Lcd_en = 0 : Waitus 40 Shift Dat , Left , 4 Lcd_data = Dat And &HF0 : Lcd_en = 1 : Lcd_en = 0 : Waitms 2 Return Write_data: Lcd_rs = 1 : Lcd_data = Dat And &HF0 : Lcd_en = 1 : Lcd_en = 0 : Waitus 40 Shift Dat , Left , 4 Lcd_data = Dat And &HF0 : Lcd_en = 1 : Lcd_en = 0 : Waitus 40 Return Write_string: For Cnt = 1 To Len(txt) Dat = Strarr(cnt) : Gosub Write_data Next Return |
Das Wichtigste ist hier, dass wir dem LCD-Modul mitteilen, das wir es nun im 4 Bit Modus betreiben wollen. Hierzu muss das Steuerwort 'Set Function' abgeändert werden. Hier wird das Bit DL auf 0 gesetzt. Somit hat das Steuerwort nun einen Wert von 40. Die Anweisung ändert sich dementsprechend:
Dat = 40 : Gosub Write_ctrl |
Da jetzt nur jeweils nur 4 Bit übertragen werden müssen, ist es auch notwendig, die Übertragungsroutinen entsprechend anzupassen.
Write_ctrl: |
Mit 'Lcd_data = Dat And &HF0' sorgen wir dafür, dass nur die oberen 4 Bit übertragen werden. Jetzt muss mit 'Shift Dat, Left, 4' die unteren 4 Bit nach oben geschoben werden, damit diese auch übertragen werden können. Anschließend erfolgt noch ein Übertragungsbefehl.
Die Routine zur Ausgabe der Daten ist ja fast gleich aufgebaut. Nur das hier das RS-Signal auf 1 gesetzt wird.
Bedingt durch die erhöhte Anzahl der Ausgaben, ist die Ausgabe ein wenig langsamer. Dies bemerken wir aber gar nicht. Daher wird der 4 Bit Modus sehr gerne, bei der Verwendung direkt am Kontroller, verwendet.