Das Display: Eigenschaften und Programmierung mit MicroPython

Hier werden einzelne Projekte mit MicroPython vorgestellt
Antworten
Heinrichs
Beiträge: 173
Registriert: Do 21. Okt 2010, 18:31

Das Display: Eigenschaften und Programmierung mit MicroPython

Beitrag von Heinrichs » Mi 21. Okt 2020, 10:43

1. Eigenschaften

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.

st7789_block_diagramm_ausschnitt.jpg
Abb. 1: Ausschnitt aus dem Blockdiagramm des ST7789
st7789_block_diagramm_ausschnitt.jpg (58.73 KiB) 6217 mal betrachtet

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.

schematic_st7789.jpg
Abb. 2: Anschluss des ST7789 an den ESP32
schematic_st7789.jpg (61.92 KiB) 6215 mal betrachtet

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):

anschluss_schema.jpg
Abb. 3: Ausschnitt aus
anschluss_schema.jpg (7.3 KiB) 6208 mal betrachtet

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))
Der erste Parameter gibt an, dass der ESP32 den Hardware-SPI-Kanal VSPI (id = 2) benutzen soll. Der zweite gibt die Baudrate an. Der dritte Parameter mit dem Bezeichner polarity gibt den Ruhezustand des Clock-Signals an (vgl. Abb. 4; der phase-Parameter hat standardmäßig den Wert 0.). Die letzten beiden Parameter geben die GPIOs für das SCK/SCL- bzw. das SDA/MOSI-Signal an (s. o.).

spi_mode2.jpg
Abb. 4: SPI-Übertragung im Mode 2 (nach https://diyi0t.com/spi-tutorial-for-arduino-and-esp8266/)
spi_mode2.jpg (10.66 KiB) 6198 mal betrachtet

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
Zunächst wird das Modul st7789 mit der Klasse ST7789 importiert; anschließend wird eine Instanz display erzeugt: Der erste Parameter gibt an, welches SPI-Objekt von display benutzt werden soll; der zweite und dritte Parameter geben die Anzahl der Pixel in horizontaler und vertikaler Richtung (im Portrait-Modus, s. u.) an. Darauf folgen die Zuweisungen der Signalleitungen reset, cs, dc und backlight, s. o.). Der letzte Parameter gibt die Orientierung an (Drehung jeweils im Uhrzeigersinn):

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.

ttgo_daytime_display_klein.jpg
Abb. 5: Inverser Landscape-Modus
ttgo_daytime_display_klein.jpg (33.92 KiB) 6197 mal betrachtet

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:

vga2_16x16.jpg
Abb. 6: Erweiterter Zeichensatz (vga2_16x16)
vga2_16x16.jpg (32.95 KiB) 6181 mal betrachtet

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)
Das Ergebnis ist in der folgenden Abbildung 7 zu sehen:

käse_1.jpg
Abb. 7: Ausgabe von 'Käse'
käse_1.jpg (9.04 KiB) 6170 mal betrachtet

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:

käse_2.jpg
Abb. 8: Ausgabe von b'K\x84se'
käse_2.jpg (14.52 KiB) 6169 mal betrachtet

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.

..
Dateianhänge
Befehlsuebersicht_TTGO_Display.pdf
Befehlsübersicht
(54.43 KiB) 797-mal heruntergeladen
ttgo_pins.jpg
Pin-Übersicht vom TTGO T-Display
ttgo_pins.jpg (106.15 KiB) 6135 mal betrachtet
ESP32-TFT(6-26) schematic.pdf
Schaltplan zum TTGO T-Display
(375.83 KiB) 792-mal heruntergeladen
ST7789V_v1.6.pdf
Datenblatt zum ST7789-Controller
(3.08 MiB) 881-mal heruntergeladen

Antworten