zweite schritte
Rundes 1.28-Zoll-IPS-LCD-Display
Anleitung getestet mit CircuitPython 10.0.0-beta.3
Bildbox 1 (klick hier)
zweite schritte
Rundes 1.28-Zoll-IPS-LCD-DisplayAnleitung getestet mit CircuitPython 10.0.0-beta.3


Bildbox 1 (klick hier)
Hardware
- Rundes 1,28-Zoll-IPS-LCD-Display WS-22668 (RP 2040) oder
- Rundes 1,28-Zoll-IPS-LCD-Display WS-28986 (RP 2350)
- USB-A zu USB-C Kabel
Nach dem ersten Schritt folgt der zweite Schritt. Den tun wir jetzt gemeinsam. Die Anleitung baut inhaltlich auf der vorhergehenden auf und
setzt die Einrichtung der Firmware und das Vorhandensein der Bibliotheken im 'lib-Ordner' voraus. Wer an dieser Stelle hinzugekommen ist, kann
sich die
zip-Datei hier
noch einmal herunterladen und einsteigen.
Los gehts
In diesem Teil zeige ich zuerst, wie man eine Bitmap-Datei auf dem Display lädt und anzeigt. Zunächst legt man einen Ordner
'images' im Laufwerk 'CIRCUITPY' an. Dort hin kopiert man die Bitmap-Datei. Ich habe ein Bild 'erde.bmp' vorbereitet, welches
Sie verwenden können. Dieses ist genau 240x240 Pixel groß. Achten Sie darauf, dass die Farbtiefe nicht 'True Color' (also nicht 24 Bit) sein
kann. Verwenden Sie max. 256 oder besser sogar nur 16 Farben. Das zahlt sich beim Speicherbedarf aus.
Im unteren Kasten sehen Sie den Anfang des Programms. Zunächst werden wieder die Module importiert, dann das Display (Zeilen 20 bis 28) und der Sensor
(Zeile 33) initialisiert. Auf die beiden Variable in Zeile 35 und 36 gehe ich später noch ein.
1 # SPDX-FileCopyrightText : 2023 Detlef Gebhardt, written for CircuitPython 2 # SPDX-FileCopyrightText : Copyright (c) 2023 Detlef Gebhardt 3 # SPDX-Filename : Earth-Clock 4 # SPDX-License-Identifier: GEBMEDIA 5 import time 6 import gc 7 import board 8 import busio 9 import displayio 10 import terminalio 11 from adafruit_display_text import label 12 from adafruit_display_shapes.roundrect import RoundRect 13 from adafruit_display_shapes.circle import Circle 14 import adafruit_imageload 15 from adafruit_ticks import ticks_ms 16 import math 17 import gc9a01 18 import my_qmi8658 19 import fourwire 20 # Alle verwendeten Ressourcen freigeben 21 displayio.release_displays() 22 # Display initialisieren 23 spi = busio.SPI(clock=board.GP10, MOSI=board.GP11) 24 display_bus = fourwire.FourWire(spi, command=board.GP8, chip_select=board.GP9, reset=board.GP12) 25 display = gc9a01.GC9A01(display_bus, width=240, height=240, backlight_pin=board.GP25) 26 group1 = displayio.Group() 27 display.rotation = 90 28 display.brightness = 1 29 30 display.root_group = group1 31 32 # Sensor initialisieren 33 sensor=my_qmi8658.QMI8658() 34 35 zeit = " Uhrzeit" 36 start = 0 37 38 image1, palette = adafruit_imageload.load( 39 "/images/erde.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette) 40 tile_grid1 = displayio.TileGrid(image1, pixel_shader=palette) 41 group1.append(tile_grid1) 42 43 # Rechteck mit abgerundeten Ecken 44 roundrect1 = RoundRect(60, 132, 120, 40, 10, fill=0x0, outline=0xffffff, stroke=3) 45 group1.append(roundrect1) 46 47 ## create the label for time 48 updating_label1 = label.Label( 49 font=terminalio.FONT, text=zeit, scale=2, color=0xffffff, 50 line_spacing=1) 51 # set label position on the display and add label 52 updating_label1.anchor_point = (0, 0) 53 updating_label1.anchored_position = (70, 140) 54 group1.append(updating_label1)
Uns interessiert das Laden der Bitmap-Datei. In Zeile 38 wird diese als 'image1' geladen und ein s.g. 'tile_grid' definiert. Dieses wird dann
unter 'group1' im Speicher abgelegt (Zeile 41). In unserem Fall wird das Bild sofort angezeigt, da wir den Befehl 'display.root_group = group1'
schon in Zeile 30 aufgerufen haben. Wenn man z.B. mehrere Bilder anzeigen will, die dann z. B. in 'group1', 'group2' und 'group3' abgelegt sind,
werden diese erst bei Aufruf von 'display.root_group = group1', 'display.root_group = group2' oder
'display.root_group = group3' angezeigt.
In den Zeilen 43 bis 45 sehen Sie, wie ein Rechteck mit abgerundeten Ecken angelegt wird und in den Zeilen 47 bis 54 wird ein 'label' für eine
Textausgabe angelegt. Damit soll später die Uhrzeit angezeigt werden. Deshalb wurde auch in Zeile 35 die Variable 'zeit' als String definiert.
Bevor Sie die 'while'-Schleife ergänzen und damit experimentieren, ändern Sie in Zeile 28 den Wert display.brightness = 1
auf 0. Damit ist bei Programmstart das Display abgeschaltet.
56 while True: 57 #read QMI8658 58 reading=sensor.Read_XYZ() 59 # Display wird um die y-Achse gekippt 60 wert_y = (10)*reading[0] 61 wert_x = (10)*reading[1] 62 #print(wert_y,wert_x) 63 64 if wert_y < -6: 65 display.rotation = 0 66 display.brightness = 1 67 start = time.monotonic() 68 if wert_y >= -6 and wert_y <= 6 and wert_x < -6: 69 display.rotation = 90 70 display.brightness = 1 71 start = time.monotonic() 72 if wert_y >= -6 and wert_y <= 6 and wert_x > 6: 73 display.rotation = 270 74 display.brightness = 1 75 start = time.monotonic() 76 if wert_y > 6: 77 display.rotation = 180 78 display.brightness = 1 79 start = time.monotonic() 80 # Display nach 3 sec abschalten 81 if (time.monotonic() - start) > 3: 82 display.brightness = 0 83
In der 'while'-Schleife wird jetzt wie in der vorigen Anleitung der Sensor ausgewertet. Je nachdem, wie das Display gehalten wird, wird die
Drehung angepasst. So ist das Bild immer aufrecht zu sehen. Hinzu kommt jetzt aber, dass erst beim Bewegen das Display eingeschaltet wird.
Legt man das Display ab, so schaltet es sich nach drei Sekunden wieder aus. Dazu wird 'time.monotonic()' in den Zeilen
81 bis 83 genutzt. 'time.monotonic()' zählt die Sekunden des Prozessortaktes im Hintergrund und unterbricht nicht den Programmablauf wie
z.B. 'time.sleep(x)'. Mit der Variable 'start' wird der aktuelle Wert von 'time.monotonic()' gesetzt und mit
(time.monotonic() - start) die Anzahl der vergangenen Sekunden erfasst. So wird in unserem Fall, wenn mindestens 3 Sekunden vergangen sind,
das Display ausgeschaltet.
Wenn wir das Beispiel jetzt zu einer einfachen Uhr erweitern, werden die Sekunden ohne Unterbrechnung fortwährend gezählt. Zur besseren
Übersicht gebe ich im nächsten Kasten den kompletten Programmcode an und werde ihn dann schrittweise erläutern.
1 # SPDX-FileCopyrightText : 2023 Detlef Gebhardt, written for CircuitPython 2 # SPDX-FileCopyrightText : Copyright (c) 2023 Detlef Gebhardt 3 # SPDX-Filename : Earth-Clock 4 # SPDX-License-Identifier: GEBMEDIA 5 import time 6 import gc 7 import board 8 import busio 9 import displayio 10 import terminalio 11 from adafruit_display_text import label 12 from adafruit_display_shapes.roundrect import RoundRect 13 from adafruit_display_shapes.circle import Circle 14 import adafruit_imageload 15 from adafruit_ticks import ticks_ms 16 import math 17 import gc9a01 18 import my_qmi8658 19 import fourwire 20 # Alle verwendeten Ressourcen freigeben 21 displayio.release_displays() 22 # Display initialisieren 23 spi = busio.SPI(clock=board.GP10, MOSI=board.GP11) 24 display_bus = fourwire.FourWire(spi, command=board.GP8, chip_select=board.GP9, reset=board.GP12) 25 display = gc9a01.GC9A01(display_bus, width=240, height=240, backlight_pin=board.GP25) 26 group1 = displayio.Group() 27 display.rotation = 90 28 display.brightness = 1 29 30 display.root_group = group1 31 32 # Sensor initialisieren 33 sensor=my_qmi8658.QMI8658() 34 35 zeit = "" 36 xpos = 120 37 ypos = 120 38 width = 240 39 height = 240 40 i = 0 41 j = -15 42 start = 0 43 44 image1, palette = adafruit_imageload.load( 45 "/images/erde.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette) 46 tile_grid1 = displayio.TileGrid(image1, pixel_shader=palette) 47 group1.append(tile_grid1) 48 49 # Rechteck mit abgerundeten Ecken 50 roundrect1 = RoundRect(60, 132, 120, 40, 10, fill=0x0, outline=0xffffff, stroke=3) 51 group1.append(roundrect1) 52 53 # Ziffernring 54 for i in range(1,13,1): 55 updating_label = label.Label( 56 font=terminalio.FONT, text=str(i), scale=2, color=0xffff00, 57 line_spacing=1) 58 #set label position on the display and add label 59 updating_label.anchor_point = (0, 0) 60 #updating_label.anchored_position = (xpos-10+int(90*math.cos(i*math.pi/6)),ypos-10-int(90*math.sin(i*math.pi/6))) 61 if i<10: 62 updating_label.anchored_position = (xpos-5+int(105*math.cos(-1*i*math.pi/6+ math.pi/2)), 63 ypos-12-int(105*math.sin(-1*i*math.pi/6+ math.pi/2))) 64 else: 65 updating_label.anchored_position = (xpos-10+int(105*math.cos(-1*i*math.pi/6+ math.pi/2)), 66 ypos-12-int(105*math.sin(-1*i*math.pi/6+ math.pi/2))) 67 group1.append(updating_label) 68 display.root_group = group1 69 70 ## create the label for time 71 updating_label1 = label.Label( 72 font=terminalio.FONT, text=zeit, scale=2, color=0xffffff, 73 line_spacing=1) 74 # set label position on the display and add label 75 updating_label1.anchor_point = (0, 0) 76 updating_label1.anchored_position = (70, 140) 77 group1.append(updating_label1) 78 79 # Stundenpunkt 80 xpos_hour = int(width/2 + 105*math.cos(j*math.pi/30)) 81 ypos_hour = int(height/2 + 105*math.sin(j*math.pi/30)) 82 circle_hour = Circle(xpos_hour, ypos_hour, 8, fill=0xff0000, outline=0x000000) 83 group1.append(circle_hour) 84 # Minutenpunkt 85 xpos_min = int(width/2 + 105*math.cos(j*math.pi/30)) 86 ypos_min = int(height/2 + 105*math.sin(j*math.pi/30)) 87 circle_min = Circle(xpos_min, ypos_min, 8, fill=0x00ff00, outline=0x000000 ) 88 group1.append(circle_min) 89 90 # Sekundenpunkt 91 xpos_sec = int(width/2 + 105*math.cos(j*math.pi/30)) 92 ypos_sec = int(height/2 + 105*math.sin(j*math.pi/30)) 93 circle_sec = Circle(xpos_sec, ypos_sec, 8, fill=0xffff00, outline=0x000000) 94 group1.append(circle_sec) 95 96 dunkel = False 97 read_x = 0 98 read_y = 0 99 100 while True: 101 #read QMI8658 102 reading=sensor.Read_XYZ() 103 wert_y = (10)*reading[0] 104 wert_x = (10)*reading[1] 105 # Display einschalten 106 if abs(wert_x - read_x) >= 1 or abs(wert_y - read_y) >= 1 and dunkel == True: 107 display.brightness = 1 108 start = time.monotonic() 109 dunkel = False 110 # Display nach 10 sec abschalten 111 if (time.monotonic() - start) > 10: 112 display.brightness = 0 113 dunkel = True 114 # 115 # Zeitstring zur Anzeige aufbereiten 116 # 117 current_time = time.localtime() 118 hour = current_time.tm_hour 119 minute = current_time.tm_min 120 second = current_time.tm_sec 121 updating_label1.text = "{:02}:{:02}:{:02}".format( hour,minute,second) 122 # Minuten- und Stundenpunkt setzen 123 # Minuten 124 xpos_min_neu = int(width/2 + 105*math.cos((minute-15)*math.pi/30)) 125 delta_min_x = xpos_min_neu - xpos_min 126 xpos_min = xpos_min_neu 127 ypos_min_neu = int(height/2 + 105*math.sin((minute-15)*math.pi/30)) 128 delta_min_y = ypos_min_neu - ypos_min 129 ypos_min = ypos_min_neu 130 circle_min.x = circle_min.x + delta_min_x 131 circle_min.y = circle_min.y + delta_min_y 132 # Stunden 133 xpos_hour_neu = int(width/2 + int(105*math.cos((hour)*math.pi/6 + minute/2*math.pi/180 - math.pi/2))) 134 delta_hour_x = xpos_hour_neu - xpos_hour 135 xpos_hour = xpos_hour_neu 136 ypos_hour_neu = int(height/2 + int(105*math.sin((hour)*math.pi/6 + minute/2*math.pi/180 - math.pi/2))) 137 delta_hour_y = ypos_hour_neu - ypos_hour 138 ypos_hour = ypos_hour_neu 139 circle_hour.x = circle_hour.x + delta_hour_x 140 circle_hour.y = circle_hour.y + delta_hour_y 141 # umlaufender Sekundenpunkt 142 xpos_neu = int(width/2 + 105*math.cos((second -15)*math.pi/30)) 143 delta_x = xpos_neu - xpos_sec 144 xpos_sec = xpos_neu 145 ypos_neu = int(height/2 + 105*math.sin((second -15)*math.pi/30)) 146 delta_y = ypos_neu - ypos_sec 147 ypos_sec = ypos_neu 148 circle_sec.x = circle_sec.x + delta_x 149 circle_sec.y = circle_sec.y + delta_y 150 151 read_x = wert_x 152 read_y = wert_y 153 gc.collect() 154 #print(gc.mem_free())
Bis zur Zeile 52 ist das Programm bis auf ein paar zusätzlich Variable unverändert.
Ab Zeile 53 bis 68 werden am Aussenring des Displays die Ziffern 1 bis 12 ,wie bei einer Uhr üblich, angezeigt. In den Zeilen
79 bis 94 werden anstelle von Uhrzeigern ein grüner Punkt für die Minuten und ein roter Punkt für die Stunden und ein
gelber Punkt für die Sekunden als Kreis mit dem Radius 8 Pixel definiert. Da sich die Punkte auf dem Aussenring bewegen sollen,
werden keine absoluten x- und y-Koordinaten verwendet, sondern später in Abhängigkeit von der Zeit berechnet.
In der 'while'-Schleife springen wir erst einmal bis zur Zeile 117. Hier wird bei jedem Durchlauf die aktuelle Zeit 'current_time'
mit Hilfe von 'time.localtime()' bestimmt. Daraus lassen sich in den Zeilen 118 bis 120 die Stunden, Minuten und Sekunden bestimmen.
In Zeile 121 wird die zeit digital als formatierter String angegeben.
Ab Zeile 122 werden der
'Minutenpunkt' und der 'Stundenpunkt' und der 'Sekundenpunkt' an die entsprechende Position gesetzt. Die Berechnung selbiger habe ich
hier und
hier in diesen Anleitungen
erklärt.
Ansonsten schaltet sich das Display immer nach zehn Sekunden aus, wenn keine Bewegung erfolgt (Zeilen 110 bis 113). Andenfalls wird die
Displayhelligkeit sofort auf den Wert 1 gesetzt (Zeilen 105 bis 109) und die Wartezeit neu gestartet. Damit das auch bei einer sehr geringen
Änderung der Sensorwerte funktioniert, wird immer der letzte Wert von 'wert_x' und 'wert_y' aus Zeile 103 und 104 in den Variablen
'read_x' und 'read_y' (Zeilen 151,152) zwischengespeichert. In Zeile 153 wird mit 'gc.collect()' nicht mehr benötigter Speicher frei
gegeben, was bei dem begrenzten Angebot beim RP 2040 besonders Sinn macht.
Viel Spass und Erfolg beim Ausprobieren.
Viel Spass und Erfolg beim Ausprobieren.