Kampis Elektroecke

Anschluss eines seriellen LCDs

Raspberry Pi

In diesem Artikel zeige ich, wie ein serielles LCD mit dem Raspberry Pi verbunden werden kann. Bei dem LCD handelt es sich um ein Standard 20×4 LCD, welches mit einem SerLCD Adapter ausgestattet wurde. Um mit dem LCD kommunizieren zu können, habe ich eine Python Klasse geschrieben, die ganz leicht in jedes Programm integriert werden kann.

Inbetriebnahme des Displays:

Für die erste Inbetriebnahme habe ich den Adapter mit einem 20×4 LCDs verbunden und über drei Leitungen an den Raspberry Pi angeschlossen:

Dabei kann die Rx-Leitung des Adapters direkt mit dem Tx-Pin der UART-Schnittstelle des Raspberry Pi verbunden werden. 

Wenn das LCD angeschlossen ist, kann mit dem ersten Funktionstest begonnen werden. Hierfür habe ich ein kleines Testprogramm in Python geschrieben, welches den Adapter auf ein 20×4 LCD einstellt und einen Text ausgibt. Der Text soll etwa mittig in Zeile 3 ausgegeben werden. Für die genaue Position ist ein Blick in das Datenblatt hilfreich. Da ich ein LCD mit 20 Zeichen besitze, beginnt die dritte Zeile bei der Cursorposition 20 (Tabelle Seite 3 im Datenblatt). Für eine Ausgabe in der Mitte, muss ich die Position 28 wählen.

Das fertige Programm sieht folgendermaßen aus:

import serial
UART = serial.Serial("/dev/serial0", 9600)
UART.write(chr(0xFE))
UART.write(chr(0x9C))
UART.write("Test")

Die Zeilen 3 und 4 sind für die Auswahl der Cursorposition zuständig. Die Cursorposition berechnet man durch den Wert aus der Tabelle (bei mir 28) + 128, da das 7. Bit auf High gesetzt werden muss. Der Wert 0xFE ist ein Steuerbefehl, damit der Adapter weiß, dass das nächste Byte ein Befehl ist. Das Resultat sieht dann so aus:

Erstellen einer Python-Klasse:

Jetzt ist es natürlich nicht so schön wenn das LCD in jedem Programm, wo das LCD integriert werden soll, so umständlich mit den ganzen Hex-Codes bedient werden muss. Aus diesem Grund habe ich eine Python-Klasse entworfen um das LCD ganz einfach benutzen zu können. Dadurch reduziert sich der notwendige Code auf ein Minimum. Die Anwendung der Klasse werde ich gleich noch etwas genauer erklären. Aber erst noch ein paar Worte zu dem Aufbau.

Eine Klasse, oder ein Objekt, kann man sich wie einen Werkzeugkoffer für eine bestimmte Anwendung vorstellen (z. B. um das Auto zu reparieren). In diesem Werkzeugkoffer sind alle notwendigen Werkzeuge und Materialien enthalten um die Aufgabe erfolgreich abschließen zu können. Und genau so ist es auch bei der Klasse für das LCD. Diese Klasse beinhaltet alle Funktionen die für eine Kommunikation mit dem LCD notwendig sind (andere Klassen wären z. B. RPi.GPIO oder smbus). Das Herzstück einer Klasse ist der sogenannte Konstruktor. Dies ist eine Funktion, oder auch Methode, die in keiner Klasse fehlen darf, da sie alle notwendigen Schritte einleitet um die Klasse mit dem Hauptprogramm, woraus sie aufgerufen wird, zu verbinden. In der Klasse für das LCD sieht sie so aus:

def __init__(self, Device, Baudrate, Zeichen):
    self.__Device= Device
    self.__Baudrate = Baudrate
    self.Zeichen = Zeichen
    self.UART = serial.Serial(self.device, self.baudrate)

    if(not(self.UART.isOpen()))
        self.UART.open()

Der Name des Konstruktors muss für alle Klassen immer gleich bleiben und darf nicht geändert werden. Der Konstruktor wird immer dann aufgerufen, wenn das Objekt im Programm instanziert wird. Dabei werden als Parameter die serielle Schnittstelle, die Baudrate und die Anzahl an Zeichen pro Reihe übergeben und in Variablen gespeichert. Mit dem Ausdruck self wird eine Referenz auf die aktuelle Instanz gemacht, sprich wenn die Klasse im Hauptprogramm genutzt wird, wird eine Instanz erzeugt und mit dem self weist man die Variablen und Methoden dieser Instanz zu. Beim Aufruf des Konstruktors wird zudem geschaut ob die angegebene serielle Schnittstelle (UART ist z. B. eine Instanz der Klasse serial ) offen ist. Ist sie nicht offen, wird sie geöffnet.

Alle weiteren Methoden in der Klasse dienen jetzt dazu um die Kommunikation mit dem Adapter zu bewerkstelligen. Die Methoden machen im Grunde nichts anderes als die im Datenblatt angegebenen Hexcodes an das LCD zu senden. Ein Beispiel ist z. B. die Methode Clr:

def Clr(self):
    self.UART.write(chr(0xFE))
    self.UART.write(chr(0x01))

Beim Aufruf der Methode wird die Referenz auf die Instanz übergeben (self ). Dieser Parameter muss bei jeder Methode angegeben werden, da es sonst zu Fehlern kommt, da zu viele Argumente an eine Methode übergeben werden. Als nächstes wird ein 0xFE zum Adapter gesendet, damit der Adapter weiß, dass das nächste Byte ein Steuerbefehl ist. Das Byte 0x01 ist dann der Befehl zum löschen des Displays.

Die Klasse beinhaltet zusätzlich folgende Funktionen:

Funktionsname Beschreibung
Clr Löscht das Display
Position(Zeile, Position) Bewegt den Cursor an die angegebene Position in der angegebenen Zeile
SetBrightness(Brightness) Setzt die Helligkeit der Hintergrundbeleuchtung auf einen bestimmten Wert. 0 ist aus und 30 ist max.
EnableBlink(Enable) Aktiviert (Enable = On) / Deaktiviert (Enable = Off) den blinkenden Cursor.
EnableUnderline(Enable) Aktiviert (Enable = On) / Deaktiviert (Enable = Off) den unterstrichenen Cursor.
EnableVisual(Enable) Aktiviert (Enable = On) / Deaktiviert (Enable = Off) die Displayanzeige.
SwitchSplash() Aktiviert / Deaktiviert den Splashscreen des Displays.
SetCursor(CursorPosition) Setzt den Cursor an die angegebene Stelle.
ChangeBaud(Baud) Ändert die Baudrate des Moduls. Möglich sind 2400, 4800, 9600, 19200 und 38400 Baud.
Write(Text) Schreibt einen Text auf das Display.

Auf diese Weise können die Funktionen des Adapters Stück für Stück in die Klasse übernommen werden. Die Anwendung der Klasse zeige ich euch jetzt noch an einem Beispiel. Für das Beispiel wird die Klasse in das selbe Verzeichnis wie das Hauptprogramm kopiert und anschließend wird sie in das Hauptprogramm importiert-

from LCD_Serial import LCD_Serial

Im nächsten Schritt wird die Instanz, also das Objekt, angelegt. Mein LCD ist an der seriellen Schnittstelle des Raspberry Pi (serial0) angeschlossen. Als Baudrate wird der Standardwert von 9600 angegeben und das Display hat 20 Zeichen pro Reihe:

LCD = LCD_Serial("/dev/serial0", 9600, 20)

Jetzt ist die Klasse LCD_Serial unter dem Namen LCD erzeugt worden und somit im Programm verfügbar. In der nächsten Zeile wird das Display erst einmal gelöscht:

LCD.Clr()

Danach beginnt eine while-Schleife die den Text, welcher unter der Variable String gespeichert ist, über das Display wandern lässt:

while True:
for I in range(1, 21 - len(String)):
    LCD.Position(3, I)
    LCD.Write(String)
    time.sleep(1)
    LCD.Clr()
    for I in range(21 - len(String), 0, -1):
        LCD.Position(3, I)
        LCD.Write(String)
        time.sleep(1)
        LCD.Clr()

Auf diese Weise kann der serielle LCD Adapter auf ganz einfache Weise in jedes Programm integriert werden.

Alle Python-Skripte gibt es in meinem GitHub-Repository zum Download.

Zurück zu UART & USB

6 Kommentare

  1. Vielen Dank für die Anleitungen für die Nutzung des LCD-Displays und des RFID-Lesers.

    Nun würde ich gerne beides zugleich nutzen – den RFID-Leser zum Erkennen von Kundenkarten, das Display zum Anzeigen z.B. des Guthabens nach Abruf aus einer Datenbank. Der UART kann aber wohl nicht für beide Aufgaben zugleich genutzt werden, auch wenn ich für’s Display nur UART_TXD und für den Leser nur UART_RXD brauche. Jedenfalls kommt da ziemlich Unsinn auf dem Display an…

    Gibt es eine Python-Klasse, die mir erlaubt, auf Standard-GPIO-Pins eine UART-Schnittstelle zu erzeugen?

    1. Hallo Joachim,

      komisch. Theoretisch sollte es aber gehen, wenn du Rx und Tx einzeln verwendest….
      Leider kenne ich keine Klasse die einen Software UART auf zwei IOs bereit stellt.
      Was spricht den gegen einen USB/UART Wandler?

      Gruß
      Daniel

      1. Hallo Daniel,

        evtl. werde ich mir demnächst einen solchen Wandler besorgen, aber zunächst habe ich folgende Lösung gefunden:

        Die Python-Klasse Serial hat eine Methode setBreak(), die die Übertragung von Daten über die TX-Leitung verhindert. Wenn ich nun vor dem Lesen vom RFID-Empfänger den Aufruf UART.setBreak(True) setze, dann gibt es keine unsinnige Ausgabe mehr auf dem Display.

        Außerdem ist damit anscheinend auch ein zweites, an mehreren Stellen im Netz beschriebenes Problem behoben: Programme wie dein Musterprogramm brechen nun nicht mehr nach mehreren Durchläufen mit Exceptions wie IOError oder OSError ab, was ohne setBreak immer wieder geschah.

        Will ich zwischendurch etwas auf dem Display ausgeben, muss nur die entsprechende UART.write()-Anweisung in setBrerak(False) und setBreak(True) eingerahmt werden.

        Gruß
        Joachim

  2. Hallo Daniel,
    ich habe Dein Buch Raspberry Pi „Der praktische Einstieg“ geschenkt bekommen.
    Bin jetzt auf der Suche nach dem richtigen Display. Aus allen Beschreibungen die ich bisher gelesen habe geht nicht genau hervor welches Display und braucht man immer den SerLCD ? Sind wirklich nur 3 Kabel für das Display notwendig oder kommen nochmal zwei für die Stromversorgung dazu ?

    Viele Grüße
    MHer

    1. Hallo MHer,

      ja, das Display mit dem Wandler verwendet wirklich nur drei Leitungen :) (Vcc, GND und TxD).
      Als Wandler kannst du z.B. den hier verwenden:

      http://www.watterott.com/de/Serial-Enabled-LCD-Backpack

      Dazu dann noch ein x-beliebiges Display mit einem HD44780 Controller (z.B. das hier – wenn du das Bestellen willst, mach das aber über Watterott….einfach eine E-Mail schreiben und sagen das du das Display bei Adafruit bestellen möchtest und ob die das machen könnten…dann sparst du dir den hohen Versand aus Amerika :) ):.

      https://www.adafruit.com/products/198

      Als Komplettlösung gibt es so etwas auch:

      http://www.watterott.com/de/Serial-Enabled-16×2-LCD-Black-on-Green-5V

      Ansonsten kannst du das LCD von Adafruit auch einzeln bestellen und am Pi betrieben. Dann braucht das LCD aber 8 I/O Minimum vom Raspberry Pi und die Ansteuerung ist komplexer, da du den Controller des LCDs händisch bedienen musst.
      Hoffe das hilft dir weiter. Ansonsten einfach melden :)

      Gruß
      Daniel

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert