Mathematische Grundlagen

Rundes 1.28-Zoll-IPS-LCD-Display 240 x 240 Pixel
- RP2040, 6-Achsen-Sensor - Waveshare 22668

Ein wenig Mathematik muss sein:
- kleiner Exkurs in die Winkelfunktionen -



Bildbox 1 (klick hier)

Hardware

- Rundes 1,28-Zoll-IPS-LCD-Display
- RP Pico 2040 (inclusive)
- USB-A zu USB-C Kabel


Unser Display ist rund. Deshalb kommt man z.B. bei der Darstellung von Punkten, Zeigern u.ä. nicht ohne die Winkelfunktionen Sinus und Cosinus aus. Gehen wir kurz auf die Definitionen von Sinus und Cosinus ein.

sin(alpha) = Gegenkathete / Radius
Da die Gegenkathete hier der y-Koordinate im Koordinatensystem entspricht folgt daraus:
y = r * sin(alpha)
Analog gilt dann für den Cosinus:
cos(alpha) = Ankathete / Radius
Dabei entspricht die Ankathete der x-Koordinate, also:
x = r * cos(alpha)
Klicken Sie auf die Bildbox 1 (oben) und sehen sich die Abbildungen dazu an.

Auf den beiden unteren Fotos wird das für die 'Sekundenpunkte', 'Minutenpunkte', 'Stundenpunkte' bzw. die Position der Zeigerspitzen genuzt.




Los gehts mit einem Beispiel

Das Display hat 240 x 240 Pixel. Der Mittelpunkt liegt also bei P(120/120), so dass wir als Koordinaten nachfolgend immer xpos = 120 und ypos = 120 nutzen.

Die jeweils neue x-Koordinate ergibt sich dann aus
xpos_neu = int(xpos + 100*math.cos((alpha)*math.pi/30)
und die y-Koordinate
ypos_neu = int(ypos + 100*math.sin((alpha)*math.pi/30).

Da 2*pi bekanntlich 360 Grad entsprechen, lassen sich mit pi/30 die 60 'Positionen' (360 / 180 * 30 = 60) darstellen. Mit der 100 wird in unserem Fall der Radius festgelegt. Hier nun das Beispielprogramm:

  
  

1  # SPDX-FileCopyrightText: 2023 Detlef Gebhardt, written for CircuitPython
2  # SPDX-FileCopyrightText: Copyright (c) 2023 Detlef Gebhardt
3  #
4  # SPDX-License-Identifier: GEBMEDIA
5  import time
6  import gc
7  import board
8  import busio
9  from busio import I2C
10 import displayio
11 import terminalio
12 import gc9a01
13 from adafruit_display_text import label
14 from adafruit_display_shapes.circle import Circle
15 import random
16 import math
17
18 # Release any resources currently in use for the displays
19 displayio.release_displays()
20
21 # Make the displayio SPI bus and the GC9A01 display
22 spi = busio.SPI(clock=board.LCD_CLK, MOSI=board.LCD_DIN)
23 display_bus = displayio.FourWire(spi, command=board.LCD_DC, chip_select=board.LCD_CS, reset=board.LCD_RST)
24 display = gc9a01.GC9A01(display_bus, width=240, height=240, rotation=90, backlight_pin=board.LCD_BL)
25
26 # Make the display context
27 group1 = displayio.Group()
28 display.root_group = group1
29
30 width = 240
31 height = 240
32 xpos = 120
33 ypos = 120
34 radius = 120
35 i = 0
36
37# Make some circle:
38 circle = Circle(xpos, ypos, 120, fill=0xff0000, outline=0x000000)
39 group1.append(circle)
40 circle = Circle(xpos, ypos, 115, fill=0x000000, outline=0x000000)
41 group1.append(circle)
42 circle = Circle(xpos, ypos, 85, fill=0xffff00, outline=0xffff00)
43 group1.append(circle)
44 circle = Circle(xpos, ypos, 10, fill=0x0000ff, outline=0xffff00)
45 group1.append(circle)
46
47 while True:
48     for i in range (60):
49         xpos_neu = int(width/2 + 100*math.cos((i -15)*math.pi/30))
50         delta_x = xpos_neu - xpos
51         xpos = xpos_neu
52         ypos_neu = int(height/2 + 100*math.sin((i -15)*math.pi/30))
53         delta_y = ypos_neu - ypos
54         ypos = ypos_neu
55         circle.x = circle.x + delta_x
56         circle.y = circle.y + delta_y
57         time.sleep(0.05)
58         gc.collect()
59    #print(gc.mem_free())
  

Ein paar Erläuterungen. Die Programmzeilen 5 bis 28 entsprechen dem vorigen Beitrag zur Initialisierung des Displays. Hinzu gekommen sind die Module 'from adafruit_display_shapes.circle import Circle' und 'import math', welche für die Kreisdarstellung und die mathematischen Berechnungen gebraucht werden. In den Zeilen 37 bis 45 werden Kreise dargestellt, welche nacheinander das Display rot, dann mit kleinerem Durchmesser schwarz und schließlich gelb einfärben. Zuletzt wird ein kleiner blauer Kreis als Punkt in dem schwarzen Ring dargestellt. Sie sehen, dass die Syntax für den Kreis immer

circle = Circle(xpos, ypos, radius, fillcolor, outlinecolor)

ist. Es folgt die 'while' Schleife in Zeile 47. Dort wird in den Zeilen 56 und 57 eine Eigenschaft bei 'circle' genutzt, welche mit 'circle.x' bzw. 'circle.y' die neue x und y Position des Mittelpunktes annehmen kann, ohne den Kreis neu definieren zu müssen. Stattdessen wird in den Zeilen 49 bis 54 der neue geänderte Wert berechnet und als 'delta_x' und 'delta_y' zu den Mittelpunktkoordinaten addiert. Dass hat den riesigen Vorteil, dass kein zusätzlicher Speicher belegt wird, denn dann würde ganz schnell ein 'MemoryError' das Programm beenden.
Ich habe das Ganze in eine 'for i in range (60)' Schleife gepackt, deshalb steht in Zeile 49 und 52 beim Sinus bzw. Cosinus das 'i'. Die subtrahierte 15 hat ihren Sinn, weil ich das Display bei der Initialisierung um 90 Grad gedreht habe (siehe Zeile 24) und die Bewegung oben beginnen soll. Experimentieren Sie mit dem Beispiel und demnächst geht es weiter.

Zuvor gibt es aber noch eine kleine Zugabe. Probieren Sie das folgende Programm aus.
Zum Zeitpunkt als diese Seite erstellt wurde, war der Treiber für den ACC-Sensor unter CircuitPython noch nicht fertig. Deshalb wurde mit Hilfe der Zufallsfunktion 'random' ein 'wert = random.randint(min,max)' als ganzzahliger Zufallswert zwischen 'min' und 'max' erzeugt. Dieser wurde für die x- und y-Koordinaten verwendet, so dass der Punkt hin- und hersprang, bis er den weißen Punkt traf.

  
  

1   # SPDX-FileCopyrightText: 2023 Detlef Gebhardt, written for CircuitPython
2   # SPDX-FileCopyrightText: Copyright (c) 2023 Detlef Gebhardt
3   #
4   # SPDX-License-Identifier: GEBMEDIA
5   import time
6   import gc
7   import board
8   import busio
9   from busio import I2C
10  import displayio
11  import terminalio
12  import gc9a01
13  import my_qmi8658
14  from adafruit_display_text import label
15  from adafruit_display_shapes.circle import Circle
16  import random
17
18  # Release any resources currently in use for the displays
19  displayio.release_displays()
20
21  # Make the displayio SPI bus and the GC9A01 display
22  spi = busio.SPI(clock=board.LCD_CLK, MOSI=board.LCD_DIN)
23  display_bus = displayio.FourWire(spi, command=board.LCD_DC, chip_select=board.LCD_CS, reset=board.LCD_RST)
24  display = gc9a01.GC9A01(display_bus, width=240, height=240, rotation=90, backlight_pin=board.LCD_BL)
25
26  # Make the display context
27  group1 = displayio.Group()
28  display.root_group = group1
29
30  # acc initialisieren
31  sensor=my_qmi8658.QMI8658()
32
33  width = 240
34  height = 240
35  xpos = 120
36  ypos = 120
37  radius = 120
38  xpos_neu = 0
39  ypos_neu = 0
40
41  # Make some circle:
42  circle = Circle(xpos, ypos, 120, fill=0xff0000, outline=0x000000)
43  group1.append(circle)
44  circle = Circle(xpos, ypos, 110, fill=0x000000, outline=0x000000)
45  group1.append(circle)
46  circle_m = Circle(xpos, ypos, 16, fill=0xffffff, outline=0x000000)
47  group1.append(circle_m)
48  circle_p = Circle(xpos, ypos, 10, fill=0x0000ff, outline=0xffff00)
49  group1.append(circle_p)
50
51  while True:
52      #read QMI8658
53      val=sensor.Read_XYZ()
54      # Display wird rel. zur x-Achse bewegt
55      wert_x = (100)*val[0]
56      # Display wird rel. zur y-Achse bewegt
57      wert_y = (50)*val[1]
58      # x-Werte
59      ## Ausschlag nach rechts
60      if wert_x < 0 and wert_x >= -10:
61          x = +5
62      # Ausschlag nach links
63      if wert_x >= 0 and wert_x < 10:
64          x = -5
65      # y-Werte
66      ## Ausschlag nach oben
67      if wert_y >= 0 and wert_y < 10:
68          y = -5
69      # Ausschlag nach unten
70      if wert_y < 0 and wert_y >= -10:
71          y = +5
72      circle_p.x = circle_p.x + x
73      circle_p.y = circle_p.y + y
74      display.refresh()
75      time.sleep(0.05)
76      gc.collect()
77      #print(gc.mem_free())
  

Dies ist jetzt insofern anders, dass der Sensor genutzt wird. Wenn sich das Display bewegt, werden die sich ändernten Werte genutzt, um die Koordinaten des 'Punktes' zu beeinflussen.
Erinnert Sie das auch an ein Spiel aus der Kindheit, bei dem eine Kugel in einer kleinen Dose in ein Loch jongliert werden mußte. In unserem Fall steckt die Idee dahinter, dass dies unter Nutzung des Beschleunigungssensors, über den unser rundes Display verfügt, gemacht wird. Wenn Sie Lust haben, können Sie das kleine Gimmick gerne noch erweitern.


Viel Spass und Erfolg beim Ausprobieren.