Raspberry-Pi-Projekte: Einfaches Text-LCD

Prof. Jürgen Plate, Christian Steiner, Serhat Erbas, Nguyen Duc Khanh

Raspberry Pi: Einfaches Text-LCD mit I2C-Interface

Allgemeines

Einfache Text-Display mit zwei bis vier Zeile zu je 20 bis 40 Zeichen und HD44780 Controller werden meist parallel angeschlossen. Dazu werden vier oder 8 Datenleitungen und drei Steuerleitungen benötigt. So belegt ein solches Display minimal sieben GPIO_Ports. Deshalb wird hier auf ein Display mit angebauten I2C-Interface beschrieben, das so nur zwei Leitungen braucht. Die Wahl fiel auf das SainSmart IIC/I2C/TWI Serial 2004 20x4 LCD, das über Amazon bestellt wurde. Es gibt aber auch Steckplatinen ohne Display, die ähnliche Funktionaität besitzen.

p> Die gängigen Dot-Matrix-LCDs mit einer Zeichenmatrix von 5x8 Pixeln haben zwei oder vier Zeilen mit je 16 oder 20 Zeichen. Die Kontakte sind meist einreihig mit 16 Polen, es gibt aber auch Displays mit seitlich zwei Reihen. Die Kontakte sind üblicherweise von links nach rechts mit 1 bis 16 nummeriert. Zumindest ist der Pin 1 markiert. An den Kontakten 15 und 16 ist normalerweise die Hintergrundbeleuchtung (LED) angeschlossen. Die Zuordnung von Anode und Kathode ist oft nicht immer eindeutig. Hier hilft nur ausprobieren. Ganz frühe LCDs hatten nur einen 14-poligen Anschluss, die Hinterleuchtung wurde an separaten Pins mit Energie versorgt. Es gibt in seltenen Fällen auch eine 15-polige Anschllussleiste, bei der die Kathode der LED mit dem GND-Anschluss des Controllers verbunden ist und Pin 15 an die Anode der LED angeschlossen ist. Hintergrundinfos zu LCD finden Sie im Skript zu Displays.

Ob die Hintergrundbeleuchtung eventuell einen Vorwiderstand benötigt, ist von Modell zu Modell verschieden. Im Zweifelsfall muss man auch hier ausprobieren, indem ein Widerstand von 470 Ω zwischen 5 V und LED geschaltet wird. Durch Messen des Stroms kann man dann auf einen internen Widerstand schließen.

Die folgende Tabelle zeigt die gängigste Pin-Belegung eines üblichen Text-LCDs. Leider gibt es auch Anzeigen, bei denen die Stromversorgungsanschlüsse (Pins 1 und 2) vertauscht sind. Also auf jeden Fall einen Blick ins Datenblatt werfen.

Pin DisplayFunktionBeschreibungPin ArduinoBemerkung
1VSSGND GND 
2VDDVcc+5 V 
3V0Kontrasteinstellung+5 Vüber Poti 10 kΩ
4RSRegister Select12 digital 
5R/W GND
6E Enable 11 digital 
7DB0Datenleitung 0--4-Bit-Modus
8DB1Datenleitung 1-4-Bit-Modus
9DB2Datenleitung 2-4-Bit-Modus
10DB3Datenleitung 3-4-Bit-Modus
11DB4Datenleitung 45 digital 
12DB5Datenleitung 54 digital 
13DB6Datenleitung 63 digital 
14DB7Datenleitung 72 digital 
15LED - Gemeinsame Kathode GND 
16LED + Gemeinsame Anode+5 Vüber Widerstand 10 Ω

Bemerkungen:

Bei den LCDs mit zweireihigem 14-poligen Anschluss sind die Pinnummern meist der Bedruckung auf der Oberseite zu entnehmen. Die Pin 15 und 16 für die Versorgung der LED-Hintergrundbeleuchtung sind meist auf der gegenüber liegenden Seite zu finden. Die Pinbelegung entsprichtim Übrigen der Tabelle oben.

Achtung: Beim Anschluss-Schema von Displays mit zweireihigem Steckverbinder sind bei einigen Displays die Anschlüsse Vdd und Vs vertauscht! Das müssen Sie beachten, wenn Sie beispielsweise das Controllerboard getrennt vom Display beschaffen und das Display selbst anlöten. Lesen Sie vor dem Anschließen auf jeden Fall das Datenblatt.

Die üblichen LCD-Anzeigen sind in der Regel mit einem Hitachi HD44780 Controller (oder einem oder kompatiblen Typ) und dem Displaytreiber HD44100 asgerüstet. Sie erlauben maximal 80 Stellen, was bedeutet, dass das größte Display der Typ mit 4x20 Zeichen (alternativ 2x40 Zeichen) ist. Displays mit mehr Stellen werden über mehrere Controller angesteuert.

Schaltung

Auf der Display-Rückseite sitzt eine winzige Platine, die mit einem I2C-Baustein bestückt ist. Das folgende Bild zeigt einen Stromlaufplan fur eine Platine mit einem LCD mit HD44780-Controller und I2C-Portexpander. In diesem Projekt wurde darauf verzichtet, eine Platine zu entwerfen, da das fertige Moduls kostengünstiger war. Die Schaltung ist sehr einfach, es werden nur ein LCD mit HD44780-Controller, ein Portexpander PCF8574T in SMD-Ausführung, einPotentiometer 10 kOhm und ggf. Jumper für die LED-Hintergrundbeleuchtung benötigt.

Um das LCD Display an den Raspberry Pi anzuschliessen sind nur vier Leitungen notwendig:

Dieses Display wird bei Amazon für den Adruino beworben, es funktioniert aber auch wunderfein mit dem Raspberry Pi. Wichtigste Änderung ist hier die niedrigere Betriebsspannung des GPIO von 3.3 Volt. Sobald das Display mit Strom versorgt wird, geht die Hintergrundbeleuchtung an, aber es ist keinerlei Anzeige zu sehen - was normal ist. Da das LCD-Modul nur mit 3,3 Volt versorgt wird, ist der Textkontrast etwas schwach. Dieser läßt sich aber mit einem kleinen Schraubendreher auf der Rückseite des Moduls am blauen Potenziometer einstellen. Bei zu hohen Kontrast kann mn sogar die Positionen der einzelen Zeichematrizen erkennen.

Die oben gezeigte "Huckepack-Platine" ist nicht mit Pullup-Widerständen für die Leitungen des I2C-Busses bestückt. Diese kann problemlos statt der 3,3 Volt auch mit 5 Volt betrieben werden, da sich auf dem Raspberry Pi bereits Pullup-Widerstände befinden und so die Datenleitungen mit für den RasPi verträglichen 3,3 Volt betrieben werden.

Anders verhält es sich mit den "Huckepack-Platinen" anderer Hersteller. Im folgenden Bild sehen Sie ein Board, das neben den internen Pullups für die Adresseinstellung auch Pullups für die I2C-Leitungen SDA und SCL besitzt. Hier wäre ein Betrieb mit 5 V für den Raspberry Pi fatal. Abhilfe bietet entweder das Zwischenschalten eines so genannten Level-Shifters oder das Auslöten der beiden Pullups. Deren Wert beträgt meist 4,7 kΩ; im Bild sind sie mit "SDA" und "SCL" gekennzeichnet.

Software

Um die I2C-Adresse des LCD zu finden, kann der Befehl i2cdetect -y 1 verwendet werden, wozu Root-Berechtigung nötig ist. In diesem Fall ist die I2C-Adresse 0x3F. Für die Programmierung des LCD wurde die Programmiersprache Python verwendet. Es ist auch möglich, das LCD in C oder einer anderen Programmierprache zu betreiben. Um auf dem Raspberry Pi den I2C-Bus verwenden zu können, muss er frei geschaltet sein (siehe Raspberry Pi: I2C-Konfiguration und -Programmierung).

Das nachfolgende Bild zeigt das Display-Register des HD44780. Zu beachten ist, dass die zweite Zeile des Displays, der dritten Zeile im Register entspricht. Für den Lineshift-Befehl müssen dann die entsprechenden Werte verwendet werden.

Der LCD-Treiber beinhaltet alle Kommandos die das LCD verwendet. Die Kommandos können dazu im Datenblatt des HD44780 nachgelesen werden. Es gibt z. B. Funktionen, um einen Text am Display anzuzeigen oder auch den Inhalt zu löschen. Näheres kann dem Datenblatt des HD44780 entnommen werden. Der LCD-Treiber verwendet dabei den I2C-Treiber von Python, der dort mittes import smbus eingebunden wird. Der Treiber für das LCD wird mittels import lcddriver in eigene Python-Programme eingebunden.

Folgende Methoden sind in der Library implementiert:

Die Bibliothek können Sie herunterladen, siehe Link unten.

Beispiel: Test der oben aufgeführten Methoden

Auch dieses Programm können Sie herunterladen, siehe Link unten.

#!/usr/bin/python
# -*- coding: latin-1 -*-

import lcdlib
from datetime import datetime
from time import sleep

# LCD-Adresse
ADDRESS = 0x3f

# Objekt erzeugen und Display initialisieren
lcd = lcdlib.lcd(ADDRESS,4,16)

# Bildschirm löschen
lcd.clear()

# Datum als String in der 2. Zeile (Zeilennummer 1) ausgeben
dateString = datetime.now().strftime('%d.%m.%y')
lcd.display_string(dateString, 1, 4)

# Uhrzeit als String in der 2. Zeile (Zeilennummer 1) ausgeben
timeString = datetime.now().strftime('%H:%M:%S')
lcd.display_string(timeString, 2, 4)

# Test fuer setcursor, "Welle..." ausgeben 
lcd.set_cursor(3,3);
lcd.write_string("Hello World! ")

# Neues Zeichen definieren und ausgeben
Pattern = [0b11111,0b10001,0b10001,0b10101,0b10101,0b10001,0b10001,0b11111]
lcd.create_char(4,Pattern)
str = chr(4)+' '+chr(4)+' '+chr(4)+' '+chr(4)+' '
lcd.display_string(str, 0, 4)

# Cursor einschalten
lcd.set_cursor(0,7);
lcd.cursor_on()
sleep(1)

# Cursor auf Blinken umschalten
lcd.cursor_blink()
sleep(2)

# Cursor wieder aus
lcd.cursor_off()
sleep(1)

# Text 2x nach rechts und 2x nach links schieben
lcd.move_right()
sleep(1)
lcd.move_right()
sleep(1)
lcd.move_left()
sleep(1)
lcd.move_left()
sleep(1)

# Display an und aus
lcd.display_off()
sleep(1)
lcd.display_on()

# Hintergrundbeleuchtung an und aus
lcd.backlight_off()
sleep(1)
lcd.backlight_on()

# letzte Ausgabe
lcd.display_string("HI", 0, 0)

Beispiel: Prozessorstatus anzeigen

Das folgende Programm kann im Hintergund laufen (Kommando: python cpuinfo.py &). Es zeigt die wichtigsten Infos über den Zustand des Raspberyy Pi an:

Die benötigten Daten holt sich das Programm aus den entsprechenden (Pseudo-)Dateien des /proc- und /sys-Dateisystems. Die Anzeige wird alle 10 Sekunden aktualisiert.
#!/usr/bin/python
# -*- coding: latin-1 -*-

import sys
import re
from subprocess import Popen, PIPE
from time import sleep
import lcdlib

# LCD-Adresse
#ADDRESS = 0x3f
ADDRESS = 0x27

# Objekt erzeugen und Display initialisieren
# Angabe: Adresse, Anzahl Zeilen, Anzahl Spalten
lcd = lcdlib.lcd(ADDRESS,4,20)

# Bildschirm löschen
lcd.clear()
lcd.backlight_on()

# Anzahl CPUs ermitteln
process = Popen("cat /proc/cpuinfo | grep processor | wc -l",\
   stdout=PIPE, shell=True)
cpus = int(process.stdout.readline().rstrip())

while True:
  # Uptime in Sekunden
  fd = open("/proc/uptime");
  uptime = float(fd.readline().rstrip().split()[0])
  fd.close()

  # Load Average und Anzahl Prozesse ermitteln
  fd = open("/proc/loadavg");
  # line:  akt., 5 min., 15 min., aktpro (aktiv/gesamt), letzte PID
  line = fd.readline().rstrip()
  fd.close()
  load = line.split();
  # Maximum der letzen 15 Minuten
  maxload = float(load[0])
  if float(load[1]) > maxload:
    maxload = float(load[1])
  if float(load[2]) > maxload:
    maxload = float(load[2])
  # Prozesse insgesamt
  aktpro = int(load[3].split('/')[1])
  # davon laufende Prozesse
  running = int(load[3].split('/')[0])

  # CPU-Temperatur ermitteln
  fd = open("/sys/class/thermal/thermal_zone0/temp")
  temp = float(fd.readline().rstrip())/1000.0
  fd.close()

  # Speicherbelegung ermitteln
  fd = open("/proc/meminfo");
  memtotal = int(re.sub('\s+',' ',fd.readline().rstrip()).split()[1])
  memfree = int(re.sub('\s+',' ',fd.readline().rstrip()).split()[1])
  memused = memtotal - memfree
  fd.close()

  # Ausgabe auf LCD
  output = "Up: %dm %ds" % (int(uptime/60), int(uptime)%60)
  lcd.display_string(output, 0, 0)
  output = "CPUs: %d" % cpus
  lcd.display_string(output, 0, 13)
  output = "Proz.: %d/run: %d" % (aktpro, running)
  lcd.display_string(output, 1, 0)
  output = "Load: %.2f" % maxload
  lcd.display_string(output, 2, 0)
  output = "T: %.2f" % temp
  lcd.display_string(output, 2, 12)
  output = "MEM: %d/%d" % (memfree, memused)
  lcd.display_string(output, 3, 0)

  # Pause
  sleep(10)

Links


Copyright © Hochschule München, FK 04, Prof. Jürgen Plate und die Autoren
Letzte Aktualisierung: