Navigation
LCD-Ansteuerung:

Ich habe lange nach fertigen Routinen zur LCD-Ansteuerung gesucht, bin jedoch nie wirklich fündig geworden. Manchmal waren die Funktionen nicht für den MSP430F149, sondern für ein Schwestermodell, mal war die Implementation fehlerhaft (oder zumindest so labil, dass sie bei mir nicht lief) und mal fehlten mir einfach Funktionen. Also habe ich mich dafür entschlossen, anhand des Datenblattes des HD44780 (welches man z.B. hier findet) eine komplett eigene Funktions-Sammlung zu schreiben. Das klingt viel komplizierter, als es ist, denn das Datenblatt ist wirklich ausführlich und erklärt jedes benutzte Register haarklein. Da der von mir geschriebene Code schon deutlich umfangreicher ist, öffnet er sich hier in einem neuen Fenster.

Weil man für eine LCD-Ansteuerung sowieso schon ein wenig Programmierkenntnisse mitbringen sollte, erkläre ich ab jetzt nicht mehr jede Codezeile einzeln, sondern beschreibe nur noch den Funktionsumfang der einzelnen Subroutinen:

void wait(unsigned int i)
Diese Funktion wird benötigt, da das LCD-Display eine gewisse Zeit braucht, bis es die angelegten Befehle ausgeführt hat. Die genauen Zeiten hängen von der Taktrate des MSP430 ab und sind auf Seite 191 des Datenblattes zu finden.

void LCDClock(void)
Diese Funktion führt einen Clock-Zyklus durch. Jeder Befehl, den man an den HD44780 sendet, wird mit einer fallenden Flanke des E-Signals übernommen und ausgeführt. Das klingt kompliziert, bedeutet aber nichts anderes, als dass man sich alle Zeit der Welt nehmen kann um sein gewünschtes Datenwort anzulegen (z.B. an DB0 bis DB7 einen anzuzeigenden Buchstaben). Wenn das Signal dann komplett anliegt, muss man lediglich E (also Port 5.2 in der hier betrachteten Konfiguration) erst auf logisch "1" und dann auf "0" legen, um so eine fallende Flanke zu erzeugen. Genau dies wird in dieser Funktion realisiert.

void LCDClear(void)
Diese Funktion löscht das gesamte Display und stellt den Cursor auf das erste Zeichen in der ersten Zeile. Hierzu sind gar keine komplizierten Routinen nötig, denn der HD44780 bietet einen fertigen Befehl, der genau das bewirkt. Hierzu muss man (wie ebenfalls Seite 191 des Datenblattes entnehmen kann) lediglich 0x01 an DB0-DB7 schreiben und RS und R/W auf Null legen. Ersteres geschieht durch die Zuweisung P4OUT=0x01; (denn Port 4 ist ja mit DB0-DB7 fest verdrahtet!), letzteres erreichen wir durch P5OUT=0x04;. Eine hexadezimale 4 entspricht ja genau dem dritten Bit, also Port 5.2, also dem Enable-Signal. Man beachte, dass (wie bereits erwähnt) solche Befehle ständig mit LCDClock; abgeschlossen werden müssen, ansonsten passiert nichts.

void LCDOutc(char str)
So, und wie bekommen wir jetzt endlich mal einen Buchstaben auf das Display? ;-) Nun ja, wie man dem unten stehenden Bild (auch zu finden als Tabelle 4 auf Seite 184 des Datenblattes) entnehmen kann, verfügt der HD44870 über eine interne Datenbank, in der alle "normalen" Buchstaben und Zahlen, viele Sonderzeichen (deutsche Umlaute, Rechenzeichen usw.) und sogar einige japanische Schriftzeichen abgelegt sind.


Wie die Experten unter euch vielleicht schon erkannt haben, entspricht die Reihenfolge der lateinischen Buchstaben in dieser Datenbank -- dem CGRAM (Character Generator RAM) -- genau dem ASCII-Code. So setzt sich der Buchstabe "G" z.B. aus dem Lower Nibble 0111 (ein Nibble sind 4 Bits) und dem Higher Nibble 0100 zusammen. Insgesamt ergibt das 01000111, also eine dezimale 71. Und wer auf seiner Tastatur mal [ALT] + 71 drückt, wird sehen, dass das genau dem ASCII- Code des Buchstabens "G" entspricht. Für unsere Funktion bedeutet dies, dass sie einfach den anzuzeigenden Buchstaben eins zu eins an Port 4 durchreichen kann: P4OUT=str;. Nun liegt an Port 4 der ASCII-Code des Buchstabens, der der Funktion übergeben wurde, an. Als nächstes wird (neben E) noch RS auf high gelegt, da es sich beim Senden eines Buchstabens um einen Daten-Schreibzugriff handelt, der durch RS=high angezeigt werden muss (siehe auch Seite 192 des Datenblattes). Schließlich wird der Befehl mittels LCDClock; ausgeführt. Nun folgt eine Prüfung, ob die Zeile, in der gerade geschrieben wird, voll ist. Ist dies der Fall, so werden einige Hilfsvariablen verändert und es wird mittels P4OUT=0xC0; und P5OUT=0x04; die zweite Zeile des Displays angesprungen. Dies geschieht dadurch, dass eine neue Adresse in das DDRAM (Display Data RAM) geschrieben wird (vgl. erneut Seite 191 des Datenblattes). Der HD44780 sorgt nämlich ganz automatisch dafür, dass sich der Cursor immer genau an der Position befindet, die der Adresse im DDRAM entspricht. Oder anders herum gesagt: Schreiben wir die Adresse des ersten Feldes der zweiten Displayzeile in das DDRAM, so setzt der Controller mit der nächsten Clock automatisch den Cursor dort hin. Die Adressierung ist je nach Displaytyp unterschiedlich, bei dem hier betrachteten 2*20-Display erscheint sie auf den ersten Blick ein wenig unlogisch:


Man sieht, dass die Adressierung zwar an Position 1 mit 0x00 startet und an Position 20 mit 0x13 endet, es in der zweiten Zeile dann aber mit 0x40 weiter geht. Und genau diese "Unstetigkeit" müssen wir durch oben erwähnte Befehle umgehen. (0x40 ist 64 dezimal und 01000000 binär. Da DB7 fest "1" ist, ergibt sich also 11000000 binär, was 0xC0 hexadezimal ist -- einfach mal Datenblatt scharf angucken oder nachrechnen ...)

void LCDOuts(char* str)
Aufbauend auf der gerade erklärten Funktion zur Ausgabe eines einzelnen Zeichens ist es nun fast trivial, eine Funktion zu erstellen, die ganze Strings ausgeben kann. Ich habe es einfach dadurch realisiert, dass ich LCDOutc() so lange aufrufe, bis der behandelte String (der ja nichts anderes als ein Array of Char ist) leer ist. Nun kann man z.B. mit LCDOuts("Das ist ein sehr langer Testtext") ganz einfach beliebige Strings auf dem LCD-Display ausgeben; der Zeilenumbruch würde hier nach dem n in langer automatisch eingebaut.

void LCDInit(void)
Diese Funktion setzt wohl die meiste Datenblatt-Lektüre voraus, da sie ausschliesslich die Initialisierungs-Befehle von Seite 191f. im Datenblatt realisiert. Zuerst wird der Übertragungsmodus auf 8 Bit gesetzt sowie der Displaytyp definiert: 2 Zeilen, jedes Zeichen mit 5*8 Punkten. Zum Übertragungsmodus sei nur kurz gesagt, dass das Interface von MSP430 zum LCD-Display sowohl 8- als auch 4-bittig ausgelegt werden kann. Beim 4-bittigen Übertragungsmodus werden die Daten teilseriell in zwei Viererblöcken übertragen, was die gesamten Übertragungs-Routinen aber noch deutlich komplizierter macht. Da der MSP430 ja über 48 I/O-Leitungen verfügt, sollten wir hier aber nicht am falschen Ende geizen, sondern einfach den weitaus simpleren 8-Bit-Modus benutzen. Anschließend werden noch ein paar grundlegende Einstellungen wie Cursor-Art und Cursor-Scrollrichtung vorgenommen, deren Bedeutung sich beim Quelltext-Lesen aber eigentlich fast von selbst ergeben sollte.

Die Funktion des Hauptprogrammes verrate ich nicht, die möge der Leser zur Übung selber rausfinden ... :-)

Auf der nächsten Seite beschäftigen wir uns mit dem integrierten A/D-Wandler des MSP430 und werden ein kleines Thermometer aufbauen.