3-wire 9-bit SPI ST7789 TFT with FT6236 capacitive touch on ESP32-S3-Zero/CircuitPython

This video show Waveshare ESP32-S3-Zero running CircuitPython 9.0.0-beta.0, display on 1.54" 240x240 3-wire 9-bit SPI ST7789 TFT with capacitive touch driver FT6236G.



About 9-bit SPI:

The display module use ST7789 driver with 3-wire SPI, no DC pin.

On CircuitPython (9.0.0-beta.0 in this exercise), if the command (DC) pin is not specified, a 9-bit SPI mode will be SIMULATED by adding a data/command bit to every bit being transmitted, and splitting the resulting data back into 8-bit bytes for transmission. The extra bits that this creates at the end are ignored by the receiving device.
ref: https://docs.circuitpython.org/en/latest/shared-bindings/fourwire/index.html#fourwire.FourWire

About fourwire library:

Start from CircuitPython 9.0.0 Alpha 2:
Split off parts of displayio into multiple modules: busdisplay, fourwire, epaperdisplay, i2cdisplaybus, paralleldisplaybus. The existing displayio hierarchy will still be available 9.x.x, but is deprecated, and will be removed in 10.0.0.

About FocalTech capacitive touch driver for CircuitPython:

In the official CircuitPython library for FT6236 capacitive touch, adafruit_focaltouch.mpy, it check vend_id (should be vendor ID). But my module is not in the list, and RuntimeError of "Did not find FT chip" raised. My solution is to download the source code of adafruit_focaltouch.py, modified to skip vend_id checking, the video show how.

Libraries needed:

All the libraries used in the exercises:
- adafruit_cursorcontrol folder
- adafruit_display_shapes folder
- adafruit_display_text folder
- my_focaltouch (modified from adafruit_focaltouch.mpy)
- adafruit_simplemath.mpy
- adafruit_st7789.mpy

If you don't know how to download libraries and upload to device, read the video https://youtu.be/mdyNBVjnC2c?t=33

Connection:

	ST7789 		ESP32-S3-Zero
	=============================
	(3 wire 9bit SPI)
	GND		GND
	VCC		3V3
	SCL		GP12
	SDA		GP11
	RES		GP13
	DATA1		(NC)
	CS		GP10
	BL		GP7

	FT6236 cap. touch
	(I2C)
	CTP-SCL		GP9
	CTP-SDA		GP8
	CTP-INT		GP6 (option)
	CTP-RST		GP5 (option)

Exercise Code:

cpyS3_st7789_9bitSPI_FT6236_dummy.py, just a dummy code to make sure the libraries loaded and run correctly.
"""
CircuitPython exercise run on Waveshare ESP32-S3-Zero,
with 1.54" 240x240 3-wire 9-bit SPIST7789 TFT 
and capacitive touch FT6236G, WLK1501SPI-8P.

Dummy setup libraries for 3-wire 9-bit SPIST7789/FT6236G.

Test on:
dev. board:     Waveshare ESP32-S3-Zero
CircuitPython:  9.0.0-beta.0

Libraries needed, for this and following exercises:
- adafruit_cursorcontrol folder
- adafruit_display_shapes folder
- adafruit_display_text folder
- my_focaltouch (adafruit_focaltouch.mpy)
- adafruit_simplemath.mpy
- adafruit_st7789.mpy

If you don't know how to download libraries and upload to device, read:
https://youtu.be/mdyNBVjnC2c?t=33
"""
import os, sys
import board
import busio
import terminalio
import displayio
import fourwire
from adafruit_display_text import label
from adafruit_display_shapes.rect import Rect
import adafruit_st7789

import adafruit_focaltouch
import my_focaltouch
from digitalio import DigitalInOut, Direction
import adafruit_cursorcontrol.cursorcontrol

from adafruit_simplemath import map_range
import time

# Release any resources currently in use for the displays
displayio.release_displays()

#===init display ======================
#Connection to ST7789
DISP_SCL = board.IO12
DISP_SDA = board.IO11
DISP_RES = board.IO13
DISP_DC  = None
DISP_CS  = board.IO10
DISP_BL  = board.IO7
#Connection to FT6236G
TOUCH_SCL = board.IO9
TOUCH_SDA = board.IO8
TOUCH_INT = board.IO6  #optional
TOUCH_RST = board.IO5  #optional

touch_irq = DigitalInOut(TOUCH_INT)
touch_irq.direction = Direction.INPUT
touch_res = DigitalInOut(TOUCH_RST)
touch_res.direction = Direction.OUTPUT
touch_res.value = True
time.sleep(0.1)
touch_res.value = False
time.sleep(0.1)
touch_res.value = True
time.sleep(0.1)

disp_spi = busio.SPI(clock=DISP_SCL,
                     MOSI=DISP_SDA)
touch_i2c = busio.I2C(TOUCH_SCL, TOUCH_SDA)
ft = my_focaltouch.My_FocalTouch(touch_i2c, debug=False, irq_pin=touch_irq)

"""
On CircuitPython 9
FutureWarning: FourWire moved from displayio to fourwire

If the command pin is not specified, a 9-bit SPI mode will be SIMULATED.
ref: https://docs.circuitpython.org/en/latest/shared-bindings/fourwire/index.html#fourwire.FourWire
"""
#display_bus = displayio.FourWire(disp_spi, command=DISP_DC, chip_select=DISP_CS, reset=DISP_RES)
display_bus = fourwire.FourWire(disp_spi, command=DISP_DC, chip_select=DISP_CS, reset=DISP_RES)

#--- Setup display ---
# for 1.54" 240x240 3-wire 9-bit SPI ST7789 TFT
disp_width = 240
disp_height = 240
display = adafruit_st7789.ST7789(display_bus,
                                 width=disp_width,
                                 height=disp_height,
                                 rowstart=40,
                                 backlight_pin=DISP_BL)

# Once a Display is setup, before switche to display any given group of layers,
# the default CircuitPython terminal will be shown; work as REPL display terminal.
#=======================================
info = os.uname()[4] + "\n" + \
       sys.implementation[0] + " " + os.uname()[3] + "\n" + \
       adafruit_st7789.__name__  + " " + adafruit_st7789.__version__  + "\n" + \
       str(display.width) + "x" + str(display.height) + "\n" + \
       adafruit_focaltouch.__name__ + " " + adafruit_focaltouch.__version__ + "\n" + \
       my_focaltouch.__name__ + " " + my_focaltouch.__version__
print("=======================================")
print(info)
print("ft.chip:", ft.chip)
print("=======================================")
time.sleep(2)

while True:
    pass
print("~ bye ~")



cpyS3_st7789_9bitSPI_color.py, for color testing of the display.
"""
CircuitPython exercise run on Waveshare ESP32-S3-Zero,
to display on IPS with ST7789 3-wire 9-bit SPI driver
and capacitive touch FT6236G, WLK1501SPI-8P.

Color test

Test on:
dev. board:     Waveshare ESP32-S3-Zero
CircuitPython:  9.0.0-beta.0

Libraries needed:
- adafruit_st7789.mpy
- my_focaltouch.py (adafruit_focaltouch.mpy)
- adafruit_cursorcontrol folder
- adafruit_display_text folder
"""
import os, sys
import board
import busio
import terminalio
import displayio
import fourwire
from adafruit_display_text import label
import adafruit_st7789

from digitalio import DigitalInOut, Direction
import adafruit_cursorcontrol.cursorcontrol

import time

# Release any resources currently in use for the displays
displayio.release_displays()

#===init display ======================
#Connection to ST7789
DISP_SCL = board.IO12
DISP_SDA = board.IO11
DISP_RES = board.IO13
DISP_DC  = None
DISP_CS  = board.IO10
DISP_BL  = board.IO7
#Connection to FT6236G
TOUCH_SCL = board.IO9
TOUCH_SDA = board.IO8
TOUCH_INT = board.IO6  #optional
TOUCH_RST = board.IO5  #optional

touch_irq = DigitalInOut(TOUCH_INT)
touch_irq.direction = Direction.INPUT
touch_res = DigitalInOut(TOUCH_RST)
touch_res.direction = Direction.OUTPUT
touch_res.value = True
time.sleep(0.1)
touch_res.value = False
time.sleep(0.1)
touch_res.value = True
time.sleep(0.1)

disp_spi = busio.SPI(clock=DISP_SCL,
                     MOSI=DISP_SDA)

"""
On CircuitPython 9
FutureWarning: FourWire moved from displayio to fourwire

If the command pin is not specified, a 9-bit SPI mode will be SIMULATED.
ref: https://docs.circuitpython.org/en/latest/shared-bindings/fourwire/index.html#fourwire.FourWire
"""
#display_bus = displayio.FourWire(disp_spi, command=DISP_DC, chip_select=DISP_CS, reset=DISP_RES)
display_bus = fourwire.FourWire(disp_spi, command=DISP_DC, chip_select=DISP_CS, reset=DISP_RES)

#--- Setup display ---
# for 1.54" 240x240 3-wire 9-bit SPI ST7789 TFT
disp_width = 240
disp_height = 240
display = adafruit_st7789.ST7789(display_bus,
                                 width=disp_width,
                                 height=disp_height,
                                 rowstart=40,
                                 backlight_pin=DISP_BL)

print("=======================================")
print("coXXect.blogspot.com")
print("=======================================")
time.sleep(1)
# Make the display context
bgGroup = displayio.Group()

"""
show() is deprecated and will be removed in CircuitPython 9.0.0. Use .root_group = group instead.
https://docs.circuitpython.org/en/8.2.x/shared-bindings/displayio/index.html#displayio.Display.show
"""
#display.show(bgGroup)
display.root_group = bgGroup

bg_bitmap = displayio.Bitmap(display.width, display.height, 1)  # with one color
bg_palette = displayio.Palette(1)
bg_palette[0] = 0xFFFFFF  # White
bg_sprite = displayio.TileGrid(bg_bitmap, pixel_shader=bg_palette, x=0, y=0)
bgGroup.append(bg_sprite)

colorSet = ((0xFF0000, "RED"),
            (0x00FF00, "GREEN"),
            (0x0000FF, "BLUE"),
            (0xFFFFFF, "WHITE"),
            (0x000000, "BLACK"))

color_bitmap = displayio.Bitmap(display.width-2, display.height-2, 1)  # with one color
color_palette = displayio.Palette(1)
color_palette[0] = 0x000000  # BLACK
color_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=1, y=1)
bgGroup.append(color_sprite)

# Draw label
text_group = displayio.Group(scale=2, x=2, y=120)
text = "coXXect.blogspot.com"
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFFFF)
text_group.append(text_area)  # Subgroup for text scaling
bgGroup.append(text_group)

time.sleep(1)
text_group.x = 15
text_area.text = "coXXect"
text_area.color = 0xFFFF00  #yellow
text_group.scale = 5

for i in colorSet:
    time.sleep(0.5)
    color_palette[0] = i[0]
    text_area.text = i[1]
    text_area.color = i[0] ^ 0xFFFFFF

while True:
    pass
print("~ bye ~")


cpyS3_st7789_9bitSPI_FT6236.py, read Focal Touch and display a cursor on screen following the touch. Actually, the display can detect more than one touch point, but I handle one only.
"""
CircuitPython exercise run on Waveshare ESP32-S3-Zero,
to display on IPS with ST7789 3-wire 9-bit SPI driver
and capacitive touch FT6236G, WLK1501SPI-8P.

Read touch to move cursor

Test on:
dev. board:     Waveshare ESP32-S3-Zero
CircuitPython:  9.0.0-beta.0

Libraries needed:
- adafruit_st7789.mpy
- my_focaltouch.py (adafruit_focaltouch.mpy)
- adafruit_cursorcontrol folder
- adafruit_display_text folder
"""
import os, sys
import board
import busio
import terminalio
import displayio
import fourwire
from adafruit_display_text import label
import adafruit_st7789

import my_focaltouch
from digitalio import DigitalInOut, Direction
import adafruit_cursorcontrol.cursorcontrol

import time

# Release any resources currently in use for the displays
displayio.release_displays()

#===init display ======================
#Connection to ST7789
DISP_SCL = board.IO12
DISP_SDA = board.IO11
DISP_RES = board.IO13
DISP_DC  = None
DISP_CS  = board.IO10
DISP_BL  = board.IO7
#Connection to FT6236G
TOUCH_SCL = board.IO9
TOUCH_SDA = board.IO8
TOUCH_INT = board.IO6  #optional
TOUCH_RST = board.IO5  #optional

touch_irq = DigitalInOut(TOUCH_INT)
touch_irq.direction = Direction.INPUT
touch_res = DigitalInOut(TOUCH_RST)
touch_res.direction = Direction.OUTPUT
touch_res.value = True
time.sleep(0.1)
touch_res.value = False
time.sleep(0.1)
touch_res.value = True
time.sleep(0.1)

disp_spi = busio.SPI(clock=DISP_SCL,
                     MOSI=DISP_SDA)
touch_i2c = busio.I2C(TOUCH_SCL, TOUCH_SDA)
ft = my_focaltouch.My_FocalTouch(touch_i2c, debug=False, irq_pin=touch_irq)

"""
On CircuitPython 9
FutureWarning: FourWire moved from displayio to fourwire

If the command pin is not specified, a 9-bit SPI mode will be SIMULATED.
ref: https://docs.circuitpython.org/en/latest/shared-bindings/fourwire/index.html#fourwire.FourWire
"""
#display_bus = displayio.FourWire(disp_spi, command=DISP_DC, chip_select=DISP_CS, reset=DISP_RES)
display_bus = fourwire.FourWire(disp_spi, command=DISP_DC, chip_select=DISP_CS, reset=DISP_RES)

#--- Setup display ---
# for 1.54" 240x240 3-wire 9-bit SPI ST7789 TFT
disp_width = 240
disp_height = 240
display = adafruit_st7789.ST7789(display_bus,
                                 width=disp_width,
                                 height=disp_height,
                                 rowstart=40,
                                 backlight_pin=DISP_BL)

print("=======================================")
print("coXXect.blogspot.com")
print("=======================================")
time.sleep(1)
# Make the display context
bgGroup = displayio.Group()

"""
show() is deprecated and will be removed in CircuitPython 9.0.0. Use .root_group = group instead.
https://docs.circuitpython.org/en/8.2.x/shared-bindings/displayio/index.html#displayio.Display.show
"""
#display.show(bgGroup)
display.root_group = bgGroup

bg_bitmap = displayio.Bitmap(display.width, display.height, 1)  # with one color
bg_palette = displayio.Palette(1)
bg_palette[0] = 0xFFFFFF  # White
bg_sprite = displayio.TileGrid(bg_bitmap, pixel_shader=bg_palette, x=0, y=0)
bgGroup.append(bg_sprite)

color_bitmap = displayio.Bitmap(display.width-2, display.height-2, 1)  # with one color
color_palette = displayio.Palette(1)
color_palette[0] = 0x000000  # BLACK
color_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=1, y=1)
bgGroup.append(color_sprite)

# Draw label
text_group = displayio.Group(scale=2, x=2, y=120)
text = "coXXect.blogspot.com"
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFFFF)
text_group.append(text_area)  # Subgroup for text scaling
bgGroup.append(text_group)

# Draw labels to display touch x/y
x_group = displayio.Group(scale=2, x=0, y=20)
x_text = "---"
x_area = label.Label(terminalio.FONT, text=x_text, color=0xFFFFFF)
x_group.append(x_area)
bgGroup.append(x_group)
y_group = displayio.Group(scale=2, x=0, y=40)
y_text = "---"
y_area = label.Label(terminalio.FONT, text=y_text, color=0xFFFFFF)
y_group.append(y_area)
bgGroup.append(y_group)

time.sleep(1)
text_group.x = 15
text_area.text = "coXXect"
text_area.color = 0xFFFF00  #yellow
text_group.scale = 5

# initialize the mouse cursor object
# place over all others
mouse_cursor = adafruit_cursorcontrol.cursorcontrol.Cursor(
    display, display_group=bgGroup)
mouse_cursor.x = int(display.width/2)
mouse_cursor.y = int(display.height/2)

while True:
    # if the screen is being touched print the touches
    if ft.touched:
        touches = ft.touches
        
        #sometimes ft.touches is [], empty!
        #so I check len to prevent IndexError: index out of range
        if len(touches)>0:
            #print(touches)
            x = touches[0]['x']
            y = touches[0]['y']
            mouse_cursor.x = x
            mouse_cursor.y = y
            x_area.text = str(x)
            y_area.text = str(y)
    else:
        print('no touch')

    time.sleep(.3)



cpyS3_st7789_9bitSPI_FT6236_visualSquare.py, a exercise of Visual Illusion.
"""
CircuitPython exercise run on Waveshare ESP32-S3-Zero,
to display on IPS with ST7789 3-wire 9-bit SPI driver
and capacitive touch FT6236G, WLK1501SPI-8P.

Visual illusion:
Both square have the same gray level, but visually changed.

Test on:
dev. board:     Waveshare ESP32-S3-Zero
CircuitPython:  9.0.0-beta.0

Libraries needed:
- adafruit_st7789.mpy
- my_focaltouch.py (adafruit_focaltouch.mpy)
- adafruit_cursorcontrol folder
- adafruit_display_text folder
"""
import os, sys
import board
import busio
import terminalio
import displayio
import fourwire
from adafruit_display_shapes.rect import Rect
import adafruit_st7789

import my_focaltouch
from digitalio import DigitalInOut, Direction

from adafruit_simplemath import map_range
import time

# Release any resources currently in use for the displays
displayio.release_displays()

#===init display ======================
#Connection to ST7789
DISP_SCL = board.IO12
DISP_SDA = board.IO11
DISP_RES = board.IO13
DISP_DC  = None
DISP_CS  = board.IO10
DISP_BL  = board.IO7
#Connection to FT6236G
TOUCH_SCL = board.IO9
TOUCH_SDA = board.IO8
TOUCH_INT = board.IO6  #optional
TOUCH_RST = board.IO5  #optional

touch_irq = DigitalInOut(TOUCH_INT)
touch_irq.direction = Direction.INPUT
touch_res = DigitalInOut(TOUCH_RST)
touch_res.direction = Direction.OUTPUT
touch_res.value = True
time.sleep(0.1)
touch_res.value = False
time.sleep(0.1)
touch_res.value = True
time.sleep(0.1)

disp_spi = busio.SPI(clock=DISP_SCL,
                     MOSI=DISP_SDA)
touch_i2c = busio.I2C(TOUCH_SCL, TOUCH_SDA)
ft = my_focaltouch.My_FocalTouch(touch_i2c, debug=False, irq_pin=touch_irq)

"""
On CircuitPython 9
FutureWarning: FourWire moved from displayio to fourwire

If the command pin is not specified, a 9-bit SPI mode will be SIMULATED.
ref: https://docs.circuitpython.org/en/latest/shared-bindings/fourwire/index.html#fourwire.FourWire
"""
#display_bus = displayio.FourWire(disp_spi, command=DISP_DC, chip_select=DISP_CS, reset=DISP_RES)
display_bus = fourwire.FourWire(disp_spi, command=DISP_DC, chip_select=DISP_CS, reset=DISP_RES)

#--- Setup display ---
# for 1.54" 240x240 3-wire 9-bit SPI ST7789 TFT
disp_width = 240
disp_height = 240
display = adafruit_st7789.ST7789(display_bus,
                                 width=disp_width,
                                 height=disp_height,
                                 rowstart=40,
                                 backlight_pin=DISP_BL)

# Make the display context
bgGroup = displayio.Group()

"""
show() is deprecated and will be removed in CircuitPython 9.0.0. Use .root_group = group instead.
https://docs.circuitpython.org/en/8.2.x/shared-bindings/displayio/index.html#displayio.Display.show
"""
#display.show(bgGroup)
display.root_group = bgGroup

# background
no_of_color = display.height
bg_bitmap = displayio.Bitmap(display.width, display.height, no_of_color)
bg_palette = displayio.Palette(no_of_color)
bg_sprite = displayio.TileGrid(bg_bitmap, pixel_shader=bg_palette, x=0, y=0)

for i in range(no_of_color):
    lv = int(map_range(i, 0, no_of_color, 0, 0xFF))
    bg_palette[i] = lv*0x10000 + lv*0x100 + lv
    
for y in range(display.height):
    for x in range(display.width):
        bg_bitmap[x, y] = y

bgGroup.append(bg_sprite)
# end of background 

sq = Rect(20, 20, 50, 50, fill=0xA0A0A0)
bgGroup.append(sq)

sq2 = Rect(80, 20, 50, 50, fill=0xA0A0A0)
bgGroup.append(sq2)

while True:
    # if the screen is being touched print the touches
    if ft.touched:
        touches = ft.touches
        
        #sometimes ft.touches is [], empty!
        #so I check len to prevent IndexError: index out of range
        if len(touches)>0:
            #print(touches)
            x = touches[0]['x']
            y = touches[0]['y']
            sq.y = y

    else:
        print('no touch')

    time.sleep(.3)





Updated@2024-02-11

As stated above, starting in CircuitPython 9.x FourWire moved from displayio to fourwire. In order to make it work on both CircuitPython 8 and 9, the "import fourwire" statement can be modified as:
try:
    from fourwire import FourWire
except ImportError:
    from displayio import FourWire

And the "display_bus = fourwire.FourWire(disp_spi, ..." statement change to:
display_bus = FourWire(disp_spi, ...

Comments

Popular posts from this blog

MicroPython/ESP32-C3 + 1.8" 128x160 TFT ST7735 SPI, using boochow/MicroPython-ST7735 library.

CameraWebServe: ESP32-S3 (arduino-esp32) + OV5640 camera module