Der Touchsensor
CST816D


auf dem 1.69-Zoll-IPS-LCD-Display
240 x 280 Pixel - RP2350


Anleitung getestet mit CircuitPython 10.0.0




Bildbox 1 (klick hier)




Hardware

- 1,69-Zoll-IPS-LCD-Touch-Display WS-30126 (RP 2350)
- USB-A zu USB-C Kabel

Firmware

- CircuitPython 10.0.0

Unser Board ist mit einem kapazitiven 1.69 Zoll Touch-LCD-Display ausgestattet. Auf der Seite 'erste Schritte' habe ich bereits gezeigt, wie man es in Betrieb nimmt. Jetzt geht es um die Nutzung der Touchfunktion. Angesteuert wird der CST816D-Chip mit dem I2C-Bus. Der I2C-Bus ist die serielle Kommunikationsschnittstelle, die über zwei Leitungen (SCL für den Takt und SDA für die Daten) die Kommunikation ermöglicht. Wenn man den Bus scannt, erfährt man, dass er die Adresse 0x15 nutzt. Der CST816D nutzt zusätzlich einen Interrupt Request (IRQ), um eine Datenübertragung anzufordern oder eine Aktion abzuschließen und außerdem ein Reset-Signal (RST).

Über folgende Pins sind diese ansprechbar:

I2C_SDA = board.GP6
I2C_SDL = board.GP7
I2C_IRQ = board.GP21
I2C_RST = board.GP22

Dem Rechnung tragend, habe ich wie schon beim Acc-Sensor einen Treiber für MicroPython als Grundlage verwendet und auf CircuitPython angepasst. Das Ergebnis ist ein Treiber 'my_cst816d.mpy'. Die Bibliothek können Sie als zip-Datei hier herunterladen , entpacken und in den lib-Ordner ihres 'CIRCUITPY'-Laufwerkes kopieren. Beim Entpacken stellen Sie fest, dass sich ein zweiter Treiber 'my_cst816d_r.mpy' in der zip-Datei befindet. Auch den kopieren Sie schon mal mit in den 'lib_Ordner'. Der wird bei der schon erwähnten Orientierung des Displays (USB nach rechts oder USB nach links) gebraucht. Waveshare geht offensichtlich davon aus, dass der USB-Anschluss im Normalfall nach links zeigt.


1. touch.get_point()

Ich zeige zuerst, wie der Sensor durch Antippen genutzt wird. Dazu lege ich auf dem Display vier Rechtecke (mit abgerundeten Ecken) an und zwar: im oberen Bereich, mittleren Bereich, unten links und unten rechts (Zeilen 40 bis 55). Die Füllfarbe ist gün. Tippt man nun auf eines der Rechtecke, so ändert es die Farbe in rot.

        


Sollte es zumindest. Aber wie man sieht, sind nach dem Antippen des oberen Rechtecks im linken Bild oben und unten, sowie rechts und links vertauscht. Da liegt daran, dass Waveshare, wie schon erwähnt, davon ausgeht, dass der USB-Anschluss nach links zeigt.

  
  
1  # SPDX-FileCopyrightText : 2025 Detlef Gebhardt, written for CircuitPython 10.0.0
2  # SPDX-FileCopyrightText : Copyright (c) 2025 Detlef Gebhardt
3  # SPDX-Filename          : multi-uhr-touch for Waveshare round 1.28 touch LCD
4  # SPDX-License-Identifier: dgebhardt.de
5  import time
6  import board
7  import busio
8  import displayio
9  import terminalio
10 import fourwire
11 from adafruit_st7789 import ST7789
12 import my_cst816d
13 from adafruit_display_shapes.roundrect import RoundRect
14 from adafruit_display_shapes.circle import Circle
15
16 touch = my_cst816d.Touch_CST816D(force_high_before_i2c=True)
17
18 # Release any resources currently in use for the displays
19 displayio.release_displays()
20 cs=board.GP9
21 dc=board.GP8
22 sck=board.GP10
23 mosi=board.GP11
24 reset=board.GP13
25 bl=board.GP25
26 # Release any resources currently in use for the displays
27 displayio.release_displays()
28 spi = busio.SPI(sck, mosi)
29 display_bus = fourwire.FourWire(spi, command=dc, chip_select=cs, reset=reset)
30 display = ST7789(display_bus, rotation=0, width=240, height=320, backlight_pin=bl, rowstart=20, colstart=0)
31 display.brightness = 1
32 # Make the display context
33 main = displayio.Group()
34 display.root_group = main
35
36 # Make the display context
37 main_screen = displayio.Group()
38 display.root_group = main_screen
39
40 # oberer Bereich
41 # x > 20 and x < 220 and y > 0 and y < 90
42 roundrect_o= RoundRect(20,0,200,90,10,fill=0x009900, outline=0x00ff00)
43 main_screen.append(roundrect_o)
44 # mittlerer Bereich
45 # x > 40 and x < 200 and y > 110 and y < 170
46 roundrect_f= RoundRect(40,115,160,40,20,fill=0x009900, outline=0x00ff00)
47 main_screen.append(roundrect_f)
48 # unten links
49 # x > 25 and x < 105 and y > 180 and y < 270
50 roundrect_ul= RoundRect(25,180,80,90,10,fill=0x009900, outline=0x00ff00)
51 main_screen.append(roundrect_ul)
52 # unten rechts
53 # x > 135 and x < 215 and y > 180 and y < 270
54 roundrect_ur= RoundRect(135,180,80,90,10,fill=0x009900, outline=0x00ff00)
55 main_screen.append(roundrect_ur)
56
57 while True:
58     # Touchberuehrung registrieren
59     if touch.touched() :
60         x, y = touch.get_point()
61         # oberer Bereich auswaehlen
62         if x > 20 and x < 220 and y > 0 and y < 90:
63             roundrect_o.fill = 0xff0000
64             roundrect_f.fill = 0x009900
65             roundrect_ul.fill = 0x009900
66             roundrect_ur.fill = 0x009900
67         # mittleren Bereich auswaehlen
68         if x > 40 and x < 200 and y > 110 and y < 170:
69             roundrect_f.fill = 0xff0000
70             roundrect_o.fill = 0x009900
71             roundrect_ul.fill = 0x009900
72             roundrect_ur.fill = 0x009900
73         # unten links auswaehlen
74         if x > 25 and x < 105 and y > 180 and y < 270:
75             roundrect_o.fill = 0x009900
76             roundrect_f.fill = 0x009900
77             roundrect_ul.fill = 0xff0000
78             roundrect_ur.fill = 0x009900
79         # unten rechts auswaehlen
80         if x > 135 and x < 215 and y > 180 and y < 270:
81             roundrect_o.fill = 0x009900
82             roundrect_f.fill = 0x009900
83             roundrect_ul.fill = 0x009900
84             roundrect_ur.fill = 0xff0000
  

Ich möchte aber wegen der späteren Uhranwendung den USB-Anschluss rechts haben. Deshalb habe ich den Treiber 'my_cst816d_r.mpy' vorgesehen. Mit einer kleinen Änderung kann dann alles so bleiben, wie ursprünglich im oberen Kasten angegeben. Die Änderungen sind:

Zeile 12: import my_cst816d_r
Zeile 16: touch = my_cst816d_r.Touch_CST816D(force_high_before_i2c=True)
zwischen Zeile 60 und Zeile 61 neu einfügen
Zeile 61 neu: x = 240 - x
Zeile 62 neu: y = 270 - y
Die weiteren Zeilen verschieben sich dadurch um zwei.


2. touch.get_gesture()

Es folgen die Gesten. Der Treiber erkennt z.Z. die Gesten:

"UP"                  : nach oben
"DOWN"             : nach unten
"LEFT"               : nach links
"RIGHT"             : nach rechts
"LONG_PRESS"    : lange gedrückt
"DOUBLE_CLICK" : Doppelklick

Ich rufe die Gesten in einer Funktion nach einem Klick auf. Dadurch führt der Sensor zunächst einen 'Reset' aus und holt die x- und y-Koordinate für den Anfang der Bewegung. Nun wartet 'er' in einer Schleife, ob eine Bewegung erfolgt. ( gesture = touch.get_gesture()). Wenn ja, wird diese ausgewertet, wie z.B.: if gesture == "RIGHT". (Siehe Kasten).

  
  
x, y = touch.get_point()
    x = 225 - x
    y = 265 - y
    while True:
        gesture = touch.get_gesture()
        if gesture is not None:
            if gesture == "RIGHT": #(Punkt nach rechts)     
                x += 5
                if x > 225:
                    x = 225
  

Zum Testen habe ich das Beispiel von oben etwas erweitert. Im Startscreen kann auf die Rechteckfelder getippt werden. Dabei ändert sich jetzt nicht die Farbe, sondern der Bildschirm wird kurz abgedunkelt. Dies dient der Kontrolle, ob die Berührung erfasst wurde. Beim Tippen auf das obere Rechteck wird jetzt eine Funktion oben_go() aufgerufen und ein zweiter Screen mit braunem Hintergrund angezeigt. Dort befindet sich ein gelber Punkt.

        


Wischen Sie nun über den Bildschirm nach oben, unten, rechts oder links, so bewegt sich der Punkt in diese Richtung bis der Bildschirmrand erreicht ist. Das können Sie beliebig lange fortsetzen, oder die Bewegung durch einen kurzen Klick auf das Display beenden. Mit 'LONG_PRESS' kommen Sie aus der Funktion zurück in den Startscreen. Damit haben Sie sicher sofort Ideen, wie Sie daraus etwas Sinnvolles machen können. Im unteren Kasten ist der komplette Quellcode zum angegebenen Beispiel. Kopieren Sie ihn in die Thonny-IDE und probieren es aus.

  
  
1   # SPDX-FileCopyrightText : 2025 Detlef Gebhardt, written for CircuitPython 10.0.0
2   # SPDX-FileCopyrightText : Copyright (c) 2025 Detlef Gebhardt
3   # SPDX-License-Identifier: dgebhardt.de
4   import time
5   import board
6   import busio
7   import displayio
8   import terminalio
9   import fourwire
10  from adafruit_st7789 import ST7789
11  import my_cst816d_r
12  from adafruit_display_shapes.roundrect import RoundRect
13  from adafruit_display_shapes.circle import Circle
14  from adafruit_display_text import label
15
16  touch = my_cst816d_r.Touch_CST816D(force_high_before_i2c=True)
17
18  # Release any resources currently in use for the displays
19  displayio.release_displays()
20  cs=board.GP9
21  dc=board.GP8
22  sck=board.GP10
23  mosi=board.GP11
24  reset=board.GP13
25  bl=board.GP25
26  # Release any resources currently in use for the displays
27  displayio.release_displays()
28  spi = busio.SPI(sck, mosi)
29  display_bus = fourwire.FourWire(spi, command=dc, chip_select=cs, reset=reset)
30  display = ST7789(display_bus, rotation=0, width=240, height=320, backlight_pin=bl, rowstart=20, colstart=0)
31  display.brightness = 1
32  # Make the display context
33  main = displayio.Group()
34  display.root_group = main
35
36  # Make the display context
37  main_screen = displayio.Group()
38  group_screen = displayio.Group()
39  display.root_group = main_screen
40
41  # oberer Bereich
42  # x > 20 and x < 220 and y > 0 and y < 90
43  roundrect_o= RoundRect(20,0,200,90,10,fill=0x009900, outline=0x00ff00)
44  main_screen.append(roundrect_o)
45  # mittlerer bereich
46  # x > 40 and x < 200 and y > 110 and y < 170
47  roundrect_f= RoundRect(40,115,160,40,20,fill=0x009900, outline=0x00ff00)
48  main_screen.append(roundrect_f)
49  # unten links
50  # x > 25 and x < 105 and y > 180 and y < 270
51  roundrect_ul= RoundRect(25,180,80,90,10,fill=0x009900, outline=0x00ff00)
52  main_screen.append(roundrect_ul)
53  # unten rechts
54  # x > 135 and x < 215 and y > 180 and y < 270
55  roundrect_ur= RoundRect(135,180,80,90,10,fill=0x009900, outline=0x00ff00)
56  main_screen.append(roundrect_ur)
57
58  # the label for the function
59  label_f = label.Label(font=terminalio.FONT, text="click\nhere", scale=2, color=0xffffff, line_spacing=1)
60  label_f.anchor_point = (0, 0)
61  label_f.anchored_position = (90, 15)
62  main_screen.append(label_f)
63
64  background_func = displayio.Bitmap(240, 280, 1)
65  pal_color = displayio.Palette(3)
66  pal_color[0] = 0x800000
67  group_screen.append(displayio.TileGrid(background_func, pixel_shader=pal_color))
68
69  circle = Circle(120, 90, 8, fill=0xffff00, outline=None)
70  group_screen.append(circle)
71
72  def oben_go():
73      x, y = touch.get_point()
74      x = 225 - x
75      y = 265 - y
76      while True:
77          gesture = touch.get_gesture()
78          if gesture is not None:
79              if gesture == "RIGHT": #(Punkt nach rechts)
80                  x += 5
81                  if x > 225:
82                      x = 225
83              if gesture == "LEFT": #(Punkt nach links)
84                  x -= 5
85                  if x < 0:
86                      x = 10
87              if gesture == "UP": #(Punkt nach oben)
88                  y -= 5
89                  if y < 0:
90                      y = 0
91              if gesture == "DOWN": #(Punkt nach unten)
92                  y += 5
93                  if y > 270:
94                      y = 265
95              circle.x = x
96              circle.y = y
97              # oben_go verlassen
98              if gesture == "LONG_PRESS" or gesture == "DOUBLE_CLICK":
99                  display.root_group = main_screen
100                 display.refresh()
101                 break
102             gesture = None
103         time.sleep(.2)
104
105 while True:
106     # Touchberuehrung registrieren
107     if touch.touched() :
108         x, y = touch.get_point()
109         x = 240 - x
110         y = 270 - y
111         # oberer Bereich auswaehlen
112         if x > 20 and x < 220 and y > 0 and y < 90:
113             display.brightness = 0.01
114             time.sleep(0.2)
115             display.brightness = 1
116             display.root_group = group_screen
117             oben_go()
118             time.sleep(0.5)
119         # mittleren Bereich auswaehlen
120         if x > 40 and x < 200 and y > 110 and y < 170:
121             display.brightness = 0.01
122             time.sleep(0.2)
123             display.brightness = 1
124         # unten links auswaehlen
125         if x > 25 and x < 105 and y > 180 and y < 270:
126             display.brightness = 0.01
127             time.sleep(0.2)
128             display.brightness = 1
129         # unten rechts auswaehlen
130         if x > 135 and x < 215 and y > 180 and y < 270:
131             display.brightness = 0.01
132             time.sleep(0.2)
133             display.brightness = 1
  

Zum Abschluss noch einige Bemerkungen. Die Anpassung des Treibers CST816D ist noch nicht ideal. Sie war durch den von Raspberry dokumentierten Hardware-Bug E9 beim RP2350 besonders schwierig. Der I2C-Bus nutzt genau die betroffenen GPIO-Pins. Aufgrund des Bugs bleibt der Bus häufig im Zustand 'Hight' und ist dann nicht mehr ansprechbar. Um das zu umgehen führe ich nach jedem Zugriff einen 'Reset' durch und zwinge die Pins SDA und SCL in den Zustand 'Low'. Dadurch ist immer eine kurze Pause notwendig. Bemerkbar wird das für Sie z.B. in Zeile 103. Wenn Sie die Pause hier weglassen, bekommen Sie die Fehlermeldung 'Device not responding at 0x15'. Mit diesem Wissen im Hintergrund habe ich eine Anwendung für eine Digitaluhr mit 'Stellfunktion', 'Stoppuhr' und 'Timer' programmiert die Sie in der nächsten Anleitung finden.




Viel Spass und Erfolg beim Ausprobieren.