Das Display des Moduls TTGO T-Display ist fest auf dem Modul montiert. Es weist 240 x 135 Pixel auf; der Farbraum ist 16 Bit, nicht rgb (24 Bit). Gesteuert wird das Display von dem Treiberbaustein ST7789 (s. Anhang: ST7789V_v1.6.pdf). Abb. 1 zeigt einen Ausschnitt des Block-Diagramms von diesem Baustein.
Hier erkennen wir schon, dass sich der Baustein über SPI steuern lässt.
Der folgende Ausschnitt aus ESP32-TFT(6-26) schematic.pdf (vgl. Anhang) zeigt, wie die Anschlüsse des Displays ST7889 auf dem Modul mit dem ESP32 verdrahtet sind.
Die Bedeutung der einzelnen Datenleitungen ist:
RESET: Reset des ST7789 (RESX)
CS: ST7789 auswählen (chipselect, CSX)
RS (CD): data command control (DCX/RS: Wenn RS = 1 ist, dann wird das übertragene Byte als Befehl gedeutet, sonst als Daten)
LEDK: steuert die Kathode der Hintergrundbeleuchtung (Wenn der Transistor in Abb. 2 durchschaltet, d. h. an IO04 ein High-Signal anliegt, dann ist LEDK = 0 und die Beleuchtung an.)
SCL: Taktsignal (DCX)
SDA: MOSI (Datenleitung: ST7789 arbeitet hier als Slave und empfängt die Daten vom ESP32)
Abb. 2 zeigt, dass diese Datenleitungen (im Fall von LEDK die Basis des Transistors) mit den Pins des ESP32 folgendermaßen verbunden sind:
RESET: IO23
CS: IO05
RS (DC): IO16
LEDK: über Transistor an IO04
SCL: IO18
SDA: IO19
Das stimmt auch mit dem Anschluss-Schema aus der Pin-Übersicht des TTGO T-Display überein (vgl. ttgo_pins.jpg im Anhang):
Diese Informationen benötigt man, wenn man das Display mit MicroPython steuern möchte. Davon handelt der nächste Abschnitt.
In den folgenden Abschnitten gehen wir davon aus, dass auf dem Modul die MikroPython-Firmware von Russ Hughes installiert ist.
2. Initialisierung des Displays
Zunächst importieren wir die Klassen Pin und SPI aus dem machine-Modul und bilden damit eine Instanz spi:
Code: Alles auswählen
from machine import Pin, SPI
spi = SPI(2, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19))
Nun kümmern wir uns um den ST7789-Treiber:
Code: Alles auswählen
import st7789
display = st7789.ST7789(spi, 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3) #landscape
display.init() #Display initialisieren
display.fill(0) #Display löschen
0: Portrait-Modus (Drehung um 0°)
1: Landscape-Modus (Drehung um 90°)
2: Inverser Portrait-Modus (Drehung um 180°)
3: Inverser Landscape-Modus (Drehung um 270°)
Wir benutzen im Folgenden immer den Modus 3. Damit werden z. B. Texte wie in Abb. 5 angezeigt.
Nun wird das Display mit display.init() initialisiert. Anschließend wird das Display gelöscht, indem man allen Pixeln den Wert 0 (Schwarz) zuweist: display.fill(0).
3. Koordinaten und Farbwerte
Sämtliche Pixel-Koordinaten beziehen sich auf die ausgewählte Orientierung. In unserem Fall (Landscape) hat die obere linke Ecke des Displays die Koordinaten x = 0 / y = 0 und die untere rechte Ecke die Koordinaten x = 239 / y = 134.
Der ST7789-Treiber unterstützt nur 16-Bit-Farben im RGB565-Format. Die ersten 5 Bit speichern den Rot-Wert ab, die nächsten 6 Bit den Grün-Wert und die letzten 5 Bit den Blau-Wert: RRRRRGGGGGGBBBBB. Auf diese Weise spart man gegenüber dem üblichen RGB-Format ein Drittel des Speicherplatz.
Das Modul st7786 stellt einige häufig benutzte RGB565-Farbwerte als Konstanten zur Verfügung: st7789.BLACK, st7789.BLUE, st7789.CYAN, st7789.GREEN, st7789.MAGENTA, st7789.RED, st7789.WHITE und st7789.YELLOW.
Daneben stellt dieses Modul auch eine Methode zur Umwandlung von RGB-Werten in das RGB565-Format zur Verfügung: st7789.color565(r, g, b).
4. Ausgabe von Texten
Zur Ausgabe eines Textes wird unserer Instanz display nur eine einzige Methode zur Verfügung gestellt: Mit
display.text(font, s, x, y[, fg, bg])
wird die Zeichenkette s mit dem angegebenen Font ausgegeben. Die linke obere Ecke des Text-Feldes wird durch die Koordinaten x und y vorgegeben. Die Parameter fg (foreground) und bg (background) sind optional; sie geben die Textfarbe bzw. die Hintergrundfarbe an. Lässt man sie weg, dann werden die Vorgabewerte WHITE (fg) bzw. BLACK (bg) benutzt.
Die von uns benutzte Firmware stellt bereits eine Reihe von Zeichensätzen zur Verfügung; sie müssen also nicht mehr auf dem ESP32 installiert werden. Die Zeichensätze, welche wir in unserem Programm einsetzen wollen, müssen allerdings importiert werden. Durch
import vga1_8x8 as font1
wird z. B. der Zeichensatz vga1_8x8 (kleine Schriftgröße, keine Sonderzeichen) importiert und steht dann unter dem Namen font1 zur Verfügung.
Hier eine Liste der Zeichensätze: vga1_16x32, vga1_8x16, vga1_8x8, vga1_bold_16x16, vga1_bold_16x32, vga2_16x16, vga2_16x32, vga2_8x16, vga2_8x8, vga2_16x16, vga2_bold_16x16, vga2_bold_16x32.
Der Name des Moduls beschreibt den Font-Typ: Die Ziffer hinter der Kennung "vga" gibt an, ob es sich um einen Standard-Zeichensatz (1) oder einen erweiterten Zeichensatz (2) handelt (s. u.). Taucht dahinter die Bezeichnung "bold" auf, handelt es sich um eine Fett-Schrift. Die folgende Angabe axb gibt die Größe der Punktmatrix an.
Der Font vga2_bold_16x32 stellt z. B. Zeichen in Fett-Schrift mit einer Breite von 16 Pixeln und einer Höhe von 32 Pixeln dar; es handelt sich um einen erweiterten Zeichensatz.
5. Sonderzeichen
Die Umlaute Ä, ä, Ö, ... sowie das ß-Zeichen gehören nicht zum Standard-ASCII-Zeichensatz. Letzterer umfasst nur Standardzeichen mit den Codes 0 bis 127. Unsere Display-Fonts vga2_16x16, vga2_16x32, vga2_8x16, vga2_8x8, vga2_16x16, vga2_bold_16x16, vga2_bold_16x32 besitzen einen erweiterten Zeichensatz: Neben dem Standardzeichensatz weisen sie neben vielen anderen Sonderzeichen auch die deutschen Umlaute auf (vgl. https://github.com/russhughes/st7789_mpy. Dies zeigt beispielhaft die folgende Zeichentabelle:
Das Zeichen 'ä' findet man z. B. in der Zeile 8 (Wir beginnen die Zählung hier bei 0.). Wir testen aus, ob der TTGO die Zeichenkette "Käse" korrekt anzeigt. Dazu benutzen wir das Programm:
Code: Alles auswählen
from machine import Pin, SPI
import vga2_16x32 as font1
import st7789
spi = SPI(2, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19))
display = st7789.ST7789(spi, 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
display.init()
display.fill(0) # loeschen; Bildschirm schwarz
display.text(font1, 'Käse', 30, 20)
Was wir hier sehen, ist merkwürdig: Statt des erwarteten Zeichens 'ä' finden wir gleich zwei völlig andere Zeichen. Der Grund ist folgender: MicroPython codiert das Zeichen 'ä' in der Zeichenkette nach dem UTF-8-Code mit den beiden Bytes 0xc3 und 0xa4. Tatsächlich haben die beiden am Display ausgegebenen Zeichen genau diese Code-Nummern in der Tabelle aus Abb. 6.
Das Zeichen 'ä' hat in der Tabelle aus Abb. 6 nun den Code 132 = 0x84. Es muss uns jetzt gelingen, dass MicroPython genau dieses Byte an das Display sendet. Dies können wir mit Hilfe eines Byte-Strings erreichen; die Methode display.text akzeptiert nämlich auch Byte-Strings. Für die Textausgabe benutzen wir nun also die Zeile
display.text(font1, b'K\x84se', 30, 20)
Damit erhalten wir dann die gewünschte Anzeige:
Mehr Informationen zu Strings und Byte-Strings findet man z. B. in der Einführung meines Skripts Wlan mit dem ESP32.
Wer nicht immer die Codes für die deutschen Sonderzeichen von Hand einfügen möchte, kann die folgende ersetzen-Funktion benutzen:
Code: Alles auswählen
def ersetzen(zk):
zk = zk.replace(b'Ä', b'\x8e')
zk = zk.replace(b'ä', b'\x84')
zk = zk.replace(b'Ö', b'\x99')
zk = zk.replace(b'ö', b'\x94')
zk = zk.replace(b'Ü', b'\x9a')
zk = zk.replace(b'ü', b'\x81')
zk = zk.replace(b'ß', b'\xe1')
return zk
# Beispiel:
s = b'Süße Äpfel in grünen Gärten'
display.text(font1, ersetzen(s), 2, 60)
6. Ausgabe von Grafiken
Unser display-Objekt besitzt folgende Grafik-Methoden:
• fill(color)
Füllt das gesamte Display mit der angegebenen Farbe
• pixel(x, y, color)
Gibt dem Pixel (x, y) den angegebenen Farbwert
• line(x0, y0, x1, y1, color)
Zeichnet eine Strecke mit der angegebenen Farbe von (x0, y0) nach (x1, y1)
• hline(x, y, length, color)
Zeichnet eine horizontale Strecke mit der angegebenen Farbe und Länge (in Pixeln), beginnend bei (x, y); schneller als line
• vline(x, y, length, color)
Zeichnet eine vertikale Strecke mit der angegebenen Farbe und Länge (in Pixeln), beginnend bei (x, y); schneller als line
• rect(x, y, width, height, color)
Zeichnet ein Rechteck mit den angegebenen Seitenlängen mit (x, y) als linker oberer Ecke
• fill_rect(x, y, width, height, color)
Füllt ein Rechteck ... s.o. ... mit der angegebenen Farbe
• blit_buffer(buffer, x, y, width, height)
Kopiert die Inhalte von Bytes() oder Bytearrays() in den internen Bildschirmspeichers. Beachten Sie: Jede Farbe erfordert 2 Bytes in dem Array.
..