zweite schritte
Rundes 1.28-Zoll-IPS-LCD-Display
Anleitung für CircuitPython 9.0.x
Bildbox 1 (klick hier)
zweite schritte
Rundes 1.28-Zoll-IPS-LCD-DisplayAnleitung für CircuitPython 9.0.x
Bildbox 1 (klick hier)
Hardware
- Rundes 1,28-Zoll-IPS-LCD-Display
- RP Pico 2040 (inclusive)
- 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. Beachten Sie, dass ich in Zeile 30 die Syntax auf die Firmware ab 9.0.x angepasst habe. 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 20 # Alle verwendeten Ressourcen freigeben 21 displayio.release_displays() 22 # Display initialisieren 23 spi = busio.SPI(clock=board.LCD_CLK, MOSI=board.LCD_DIN) 24 display_bus = displayio.FourWire(spi, command=board.LCD_DC, chip_select=board.LCD_CS, reset=board.LCD_RST) 25 display = gc9a01.GC9A01(display_bus, width=240, height=240, backlight_pin=board.LCD_BL) 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 = ticks_ms() 68 if wert_y >= -6 and wert_y <= 6 and wert_x < -6: 69 display.rotation = 90 70 display.brightness = 1 71 start = ticks_ms() 72 if wert_y >= -6 and wert_y <= 6 and wert_x > 6: 73 display.rotation = 270 74 display.brightness = 1 75 start = ticks_ms() 76 if wert_y > 6: 77 display.rotation = 180 78 display.brightness = 1 79 start = ticks_ms() 80 # Display nach 3 sec abschalten 81 if (ticks_ms() - start)/ 1000 > 3: 82 display.brightness = 0 83 start = 0
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 'ticks_ms()' in den Zeilen
81 bis 83 genutzt. 'ticks_ms()' zählt die Millisekunden des Prozessortaktes im Hintergrund und unterbricht nicht den Programmablauf wie
z.B. 'time.sleep(x)'. Mit der Variable 'start' wird der aktuelle Wert von 'ticks_ms()' gesetzt und mit
(ticks_ms() - start)/ 1000 die Anzahl der vergangenen Sekunden erfasst. So wird in unserem Fall, wenn mindestens 3 Sekunden vergangen sind,
das Display ausgeschaltet. Der Wert von 'start' muss dann wieder auf Null gesetzt werden.
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 20 # Alle verwendeten Ressourcen freigeben 21 displayio.release_displays() 22 # Display initialisieren 23 spi = busio.SPI(clock=board.LCD_CLK, MOSI=board.LCD_DIN) 24 display_bus = displayio.FourWire(spi, command=board.LCD_DC, chip_select=board.LCD_CS, reset=board.LCD_RST) 25 display = gc9a01.GC9A01(display_bus, width=240, height=240, backlight_pin=board.LCD_BL) 26 group1 = displayio.Group() 27 display.rotation = 90 28 display.brightness = 0 29 30 display.root_group = group1 31 32 # Sensor initialisieren 33 sensor=my_qmi8658.QMI8658() 34 35 zeit = "00:00:00" 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 current_time = time.localtime() 91 hour = current_time.tm_hour 92 minute = current_time.tm_min 93 second = current_time.tm_sec 94 stellen = 1 95 96 while True: 97 #read QMI8658 98 reading=sensor.Read_XYZ() 99 # Display wird um die y-Achse gekippt 100 wert_y = (10)*reading[0] 101 wert_x = (10)*reading[1] 102 #print(wert_y,wert_x) 103 if current_time.tm_min <= 1 and current_time.tm_hour == 0 and stellen == 1: 104 if wert_y < -6 and display.brightness == 1: 105 display.rotation = 0 106 start = ticks_ms() 107 if minute < 59: 108 minute += 1 109 time.sleep(0.6) 110 else: 111 minute = 0 112 time.sleep(0.6) 113 if wert_y > 6 and display.brightness == 1: 114 display.rotation = 180 115 start = ticks_ms() 116 if hour < 24: 117 hour += 1 118 time.sleep(0.6) 119 else: 120 hour = 0 121 time.sleep(0.6) 122 if wert_y >= -6 and wert_y <= 6 and wert_x < -6: 123 display.rotation = 90 124 display.brightness = 1 125 start = ticks_ms() 126 if wert_y >= -6 and wert_y <= 6 and wert_x > 6: 127 display.rotation = 270 128 display.brightness = 1 129 start = ticks_ms() 130 # Display nach 5 sec abschalten 131 if (ticks_ms() - start)/ 1000 > 5: 132 display.brightness = 0 133 start = 0 134 stellen = 0 135 136 # Sekunden erfassen und Minuten und Stunden weiterzaehlen 137 current_time = time.localtime() 138 second = current_time.tm_sec 139 if second == 59: 140 minute += 1 141 if minute == 60: 142 minute = 0 143 hour += 1 144 if hour == 24: 145 hour = 0 146 time.sleep(1) 147 # 148 # Zeitstring zur Anzeige aufbereiten 149 # 150 zeit = "{:02}:{:02}:{:02}".format( hour,minute,second) 151 updating_label1.text = zeit 152 # Minuten- und Stundenpunkt setzen 153 # Minuten 154 xpos_min_neu = int(width/2 + 105*math.cos((minute-15)*math.pi/30)) 155 delta_min_x = xpos_min_neu - xpos_min 156 xpos_min = xpos_min_neu 157 ypos_min_neu = int(height/2 + 105*math.sin((minute-15)*math.pi/30)) 158 delta_min_y = ypos_min_neu - ypos_min 159 ypos_min = ypos_min_neu 160 circle_min.x = circle_min.x + delta_min_x 161 circle_min.y = circle_min.y + delta_min_y 162 # Stunden 163 xpos_hour_neu = int(width/2 + int(105*math.cos((hour)*math.pi/6 + minute/2*math.pi/180 - math.pi/2))) 164 delta_hour_x = xpos_hour_neu - xpos_hour 165 xpos_hour = xpos_hour_neu 166 ypos_hour_neu = int(height/2 + int(105*math.sin((hour)*math.pi/6 + minute/2*math.pi/180 - math.pi/2))) 167 delta_hour_y = ypos_hour_neu - ypos_hour 168 ypos_hour = ypos_hour_neu 169 circle_hour.x = circle_hour.x + delta_hour_x 170 circle_hour.y = circle_hour.y + delta_hour_y
Bis zur Zeile 53 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 88 werden anstelle von Uhrzeigern ein grüner Punkt für die Minuten und ein roter Punkt für die Stunden 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 den Zeilen 90 bis 93 werden die Sekunden, Minuten und Stunden aus 'time.localtime()' gewonnen, solange das Display am Rechner
angeschlossen ist.
In der 'while'-Schleife springen wir erst einmal bis zur Zeile 136. Ab hier werden die Sekunden gezählt und jeweils die Minuten
und Stunden erhöht. Die Pause in der Zeile 146 ist notwendig, da der MC schnell genug ist, innerhalb der 'Sekunde 59' mehrfach die
Schleife zu durchlaufen und so die Minute mehrfach erhöhen würde. Auf die Ganggenauigkeit wirkt sich die Pause nicht aus, da
ja durch second = current_time.tm_sec (Zeile 138) die Sekunde korrekt bleibt.
Die Zeilen 150 bis 151 dienen der Anzeige der aktuellen Zeit. Wobei die Werte im String für die Zeit in Zeile 150 zweistellig
formatiert sind. In 151 wird die Variable 'text', die in den Zeilen 70 bis 77 angelegt wurde, mit der Zeit belegt. Ab Zeile 153 werden der
'Minutenpunkt' und der 'Stundenpunkt' an die entsprechende Position gesetzt. Die Berechnung selbiger habe ich
hier und
hier in diesen Anleitungen
erklärt.
Bleibt noch die Erläuterung der Zeilen 97 bis 134.
Wenn das Display am Rechner angeschlossen ist, startet es ja mit der aktuellen sekundengenauen Zeit. Wird das Programm als 'main.py' oder 'code.py'
gespeichert, startet das Display auch mit externer Stromversorgung (z.B. Powerbank), aber mit der Zeit 00:00:00, denn dann beginnt der
interne Timer vom 1.1.2020 um 00:00 Uhr an zu zählen. Deshalb braucht es eine Möglichkeit, die Uhr zu stellen:
Dazu nutzen wir den ACC-Sensor. Kippen Sie innerhalb der ersten zwei Minuten nach dem Start das Display leicht in Ihre Richtung, schaltet sich das
Display ein. Jetzt halten Sie das Display am USB-Anschluss und drehen es nach oben. In dieser Position laufen die Minuten aufsteigend. Achten Sie darauf,
das Display rechtzeitig wieder nach unten zu nehmen, wenn der gewünschte Minutenwert erreicht ist. In der entgegengesetzten Richtung - nach
unten - stellen Sie die Stunden ein. Das Ganze funktioniert nur in den ersten zwei Minuten, damit sich bei späterer Bewegung am Arm des Trägers
die Zeit nicht ungewollt ändert. Wird die Uhr hingegen am Rechner gestartet, wäre ein Verstellen der Uhrzeit beim Start nur um 00:00 Uhr
möglich (aber nicht erforderlich).
Ansonsten schaltet sich das Display immer für jeweils fünf Sekunden ein, wenn der Träger es zu sich dreht, um die Zeit abzulesen. Das
funktioniert aufgrund der Displaydrehung um 90 bzw. 270 Grad am linken wie am rechten Arm. Probieren Sie es aus. Ursprünglich hatte ich eine Variante getestet, bei der
zwei winzige Taster ins Gehäuse integriert waren. Aufgrund der Fertigungsqualität der Taster (100 Stück 5 Euro plus Versand) hat sich das
aber nicht bewährt. Ausserdem war es nicht gerade einfach, die Kabel an den Anschlüssen der Header mit dem Rastermass 1.25 mm anzulöten.
Viel Spass und Erfolg beim Ausprobieren.
Viel Spass und Erfolg beim Ausprobieren.