Das Display

Bei den aller neusten Raspberry OS Versionen kommt es durch Änderung der SPI Implementierung zu Problem. Deswegen empfehlen wir die Version: 2020-02-05 Raspbian Buster

Bevor wir anfangen müssen wir noch die Bibliothek für das Display installieren. Das können wir einfach mit den folgenden Terminal Befahlen machen:

$ sudo apt-get install build-essential python3-dev
$ git clone https://github.com/coding-world/Nokia_LCD
$ cd Nokia_LCD
$ sudo python3 setup.py install
Nokia 5510 Display angeschlossen an den Raspberry Pi
Anschlüsse am Raspberry PiAnschlüsse am Display
GPIO 241 – RST
GPIO 8 /SPI CE02 – CE
GPIO 233 – DC
GPIO 10 / SPI MOSI4 – Din
GPIO 11 / SPI CLK5 – CLK
3,3V6 – VCC
3,3V7 – BL
GND8 – GND

Wenn du das Display anschließt, ist es wichtig, dass du darauf achtest, ob das Display auch in dieser Reihenfolge beschriftet ist. Deswegen steht die Benennung der meisten Pins entweder auf der Vorder- oder Rückseite.

import Nokia_LCD as LCD
import Adafruit_GPIO.SPI as SPI

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import time

spiSettings = SPI.SpiDev(0, 0, max_speed_hz=4000000)
d = LCD.PCD8544(23, 24, spi=spiSettings)

d.begin(contrast=60)
d.clear()
d.display()

image = Image.new('1', (LCD.LCDWIDTH, LCD.LCDHEIGHT))
draw = ImageDraw.Draw(image)
draw.rectangle((0,0,84,48), outline=255, fill=255)

draw.ellipse((2,2,27,22), outline=0, fill=255)
draw.rectangle((35,2,54,22), outline=0, fill=255)
draw.polygon([(63,22), (73,2), (83,22)],
            outline=0, fill=255)

font = ImageFont.load_default()
draw.text((8,30), 'Hello World', font=font)

d.image(image)
d.display()

In Zeile 22 gibt es einen Zeilenumbruch. Alles was in Zeile 23 steht soll ans Ende der Zeile 22

Jetzt wird Schritt für Schritt das Programm erklärt. In Zeile 1 bis 7 werden erstmal alle notwendigen Bibliotheken importiert. Dazu benutzen wir in Zeile 1 und 2 das as Keyword um die Bibliotheken mit verkürzten Namen anzusprechen. Um nicht die ganze Bibliothek einbinden zu müssen, gibt es noch folgende Möglichkeit from NameDerBibliothek import BereichInDerBibliothek. Dadurch können wir nur einzelne Bereiche einbinden und sparen uns damit Platz.

In Zeile 9 und 10 legen wir zuerst die Kommunikation mit dem Display fest. Denn in diesem Beispiel sprechen wir die einzelnen Bildpunkte auf dem Display nicht direkt an, sondern reden nur mit dem Chip auf dem Display, welcher auch PCD8544 genannt wird. Dafür benutzen wir eine neue Art von Kommunikationsprotokoll, das SPI genannt wird. Dafür müssen wir aber wieder angeben, welche Pins wir benutzen wollen. In Zeile 9 übergeben wir deswegen den ersten beiden Parametern, welche SPI-Pins wir benutzen. Die SPI-Pins sind auf deinem Cheat-Sheet lila markiert. In Zeile 10 werden dann wieder zwei Parameter übergeben, die die Pins sind, die wir sonst noch benutzen. Wir speichern dieses Display-Objekt in der Variablen d.

Wenn dein Display nicht funktioniert, kann es auch daran liegen, dass du noch kein SPI aktiviert hast. Hier erfährst du wie du SPI aktivieren kannst. Ansonsten findest du noch mehr Fehlermeldungen unten.

In Zeile 12, 13 und 14 legen wir zum ersten Mal Parameter für die Anzeige auf dem Display fest. Vor allem, wenn vorher schon etwas beschrieben war, muss das erstmal gelöscht werden. Dafür gibt es die Funktion .clear(). Mit .display() beschreiben wir das Display. In diesem Fall mit einem leeren Inhalt. Bevor wir weitermachen, müssen wir auf die Grundlagen des Display eingehen. Insgesamt gibt es in der Breite 84 Pixel und in der Höhe 48 Pixel. Ein Pixel ist dabei ein Bildpunkt, welches sich entweder an- oder ausschalten lässt. Da das Display nur schwarz oder weiß kann, ist ein Pixel dann angeschaltet wenn er schwarz ist. Das heißt, wenn wir jetzt etwas darstellen wollen, müssten wir für jeden einzelnen Pixel definieren, ob dieser schwarz oder weiß ist. Da das ziemlich umständlich ist, gibt es mit Python die Möglichkeit, Texte und Formen zuerst zu malen und dann auf das Display zu übertragen. Da diese Methode deutlich einfacher ist, werden wir diese erstmal einsetzen.

Dafür erstellen wir in Zeile 16 ein Image-Objekt. Damit dieses auch weiß, wie hoch es sein soll, wird noch als Variable LCD.LCDWIDTH und LCD.LCDHEIGHT übergeben. Eine Besonderheit ist, dass diese beiden Werte zwischen normalen (runden) Klammern stehen. Wir kennen schon die Listen, die mit den eckigen Klammern [ ] erzeugt werden. Aber es gibt auch noch die Möglichkeit, sogenannte Tuples zu erstellen. Diese werden mit runden Klammern ( ) erzeugt und verhalten sich ähnlich wie Listen, lassen sich aber nach dem Erstellen nicht mehr verändern. Diese Art der Übergabe von Parametern wird in diesem Fall noch öfter vorkommen. Jetzt haben wir ein Bild erstellt und in dem Objekt image gespeichert. Damit wir dieses bearbeiten können, rufen wir die Funktion ImageDraw.Draw(image) auf und übergeben das Bild-Objekt. Dieses Objekt trägt dann den wunderschönen Namen draw (englisch für zeichnen).

Jetzt müssen wir noch eine weitere Grundlage fürs Arbeiten erzeugen. In Zeile 18 erzeugen wir mit der Funktion .rectangle() ein weißes Viereck (Rectangle ist englisch und bedeutet Rechteck). Dieses ist die Grundlage für unsere Zeichnungen. Die ersten Parameter sind dabei die Koordinaten des Rechtecks. Diese Koordinaten sind in Pixel angegeben und dabei gilt: (x , y Koordinate vom oberen linken Punkt zu x, y Koordinate vom unteren rechten Punkt). Mit outline wird festgelegt, ob die Umrandung des Rechtecks schwarz(0) oder weiß(255) sein sollen. Mit fill wird festgelegt, ob das Objekt gefüllt sein soll. Wieder steht 255 für weiß und 0 für schwarz. Nach diesem Prinzip werden alle nachfolgenden Zeichnungen aufgebaut sein. Zuerst werden die Koordinaten übergeben und dann können die Farbdetails festgelegt werden.

Deswegen geht es in Zeile 20 gleich weiter mit der Ellipse. Diese kann über die Funktion .ellipse() aufgerufen werden. Diese Funktion ist ziemlich ähnlich zur Funktion, die wir benutzt haben, um das Rechteck zu erzeugen. Die Parameter für die Koordinaten werden nach dem gleichen System, mit dem einzigen Unterschied, dass damit eine Ellipse erzeugt wird, übergeben. Das heißt, mit ein wenig Bedacht kannst du auch Kreise erzeugen.

In Zeile 21 kommt der gleiche Bekannte, das Rechteck, noch einmal zurück. Deswegen machen wir schnell mit dem Dreieck weiter, das mit .polygon() erzeugt wird. Die wichtigen Koordinaten sind dabei die drei Punkte für das Dreieck. Zwischen den runden Klammern ( ) steht deswegen immer eine Koordinatenpaar für eine Ecke.

Jetzt aber genug Mathe. Natürlich können wir nicht nur wunderschöne geometrische Objekte zeichnen, sondern auch ordentliche Texte darstellen. Dafür müssen wir erstmal eine Schriftart laden. Das machen wir in Zeile 24 und speichern das in der Variablen schrift. In Zeile 25 benutzen wir dann die .text() Funktion, um wirklich etwas zu schreiben. Der erste Parameter ist wieder das Koordinatenpaar, das angibt, wo die Schrift anfangen sollen, der zweite Parameter steht für die Schrift und der dritte Parameter beschreibt die vorher schon erwähnte Schriftart.

Jetzt haben wir alles erstellt und müssen das jetzt nur an das Display weitergeben. Dafür benutzten wir die .image() Funktion des Display-Objekts und übergeben das erstellte image. In Zeile 28 schließen wir dann das Programm ab und und geben die ganzen Daten an das Display mit .display() weiter.

Bei einem so langen Programm kann es leicht passieren, dass sich irgendwo ein kleiner Fehler einschleicht und deswegen alles nicht sofort klappt. Deswegen gehst du das Programm am besten genau durch.

Häufige Fehler Quellen

Manchmal hilft das ganze Fehlersuchen im Programmcode nichts wenn es bei der Installation oder beim Anschließen zu Fehlern gekommen ist. Deswegen hier eine Liste mit Fehlermeldungen und Lösungen.

raceback (most recent call last):
  File "ndisplay.py", line 9, in 
    spiSettings = SPI.SpiDev(0, 0, max_speed_hz=4000000)
  File "/usr/local/lib/python3.7/dist-packages/Adafruit_GPIO-1.0.4-py3.7.egg/Adafruit_GPIO/SPI.py", line 46, in __init__
SystemError: error return without exception set

Dieser Fehler kann an der der Version des Betriebsystems vom Raspberry Pi liegen. Am besten benutzt die Raspberry OS (vorher Raspbian) Version 2020-02-05 Raspbian Buster. Diese findest du auf der Offiziellen Raspberry Pi Seite

Traceback (most recent call last):
  File "display.py", line 1, in 
    import Nokia_LCD as LCD
ImportError: No module named Nokia_LCD

Hier gibt es zwei Wahrscheinliche Möglichkeiten: Entweder wurde die Bibliothek nicht richtig installiert (dann musst du die Schritte am Anfang wiederhohlen) oder du hast SPI nicht aktiviert.

Traceback (most recent call last):
  File "display.py", line 2, in 
    disp = LCD.PCD8544(DC, RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=4000000))
  File "/usr/local/lib/python3.4/dist-packages/Adafruit_GPIO-1.0.0-py3.4.egg/Adafruit_GPIO/SPI.py", line 42, in __init__
FileNotFoundError: [Errno 2] No such file or directon

Auch hier ist die Wahrscheinlichste Fehlerquelle das du SPI nicht aktivier Aktivieren, Neustarten und noch einmal Testen!

Traceback (most recent call last):
  File "display.py", line 9, in 
    spiSettings = SPI.SpiDev(0, 0, max_speed_hz=4000000)
  File "build/bdist.linux-armv7l/egg/Adafruit_GPIO/SPI.py", line 40, in __init__
ImportError: No module named spidev

Wichtig ist das du das Programm mit sudo python3 nameDerDatei.py ausführst und nicht nur mit python. Damit meinst du nämlich wirklich Python 2 und das kann zu Kompatibilitätsproblemen führen.