Touch LCD Shield¶
De Touch LCD Shield voorziet de OpenMV Cam van een capacitief 2,3 inch 320x240 multi-touch-scherm, zodat u de camera-uitvoer kunt bekijken (en invoer kunt accepteren) zonder hostcomputer. Twee Qwiic-headers maken het eenvoudig om extra I2C-apparaten aan elkaar te koppelen.
Raadpleeg voor het volledige datasheet, foto’s en bestelinformatie de productpagina van de Touch LCD Shield.
Hoogtepunten¶
2,3 inch TFT-LCD, 320x240, 16-bit RGB565
Capacitieve multi-touch-invoer
Via PWM regelbare achtergrondverlichting
Twee Qwiic-connectoren voor eenvoudig koppelen van I2C-apparaten
Pinout¶
Pinreferentie¶
Pin |
Functie |
|---|---|
P0 |
LCD MOSI (SPI-data naar scherm) |
P1 |
LCD TE (tearing-effect-uitgang) |
P2 |
LCD SCLK (SPI-klok) |
P3 |
LCD SSEL (SPI-chipselectie) |
P4 |
Touch / Qwiic SCL (I²C-klok — gedeeld met Qwiic-headers) |
P5 |
Touch / Qwiic SDA (I²C-data — gedeeld met Qwiic-headers) |
P6 |
LCD-achtergrondverlichting |
P7 |
Touch / LCD RESET_N |
P8 |
LCD RS (data-/commandoselectie) |
P9 |
Touch INT_N |
3,3V-rail |
Voedt de LCD- en touch-controllers |
GND-rail |
Gemeenschappelijke aarde |
Gebruik¶
Stuur de shield aan via de display.SPIDisplay-klasse. Stream cameraframes naar het 320×240-LCD:
import csi
import time
import display
import image
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
lcd = display.SPIDisplay(width=320,
height=240,
bgr=True,
vflip=False,
hmirror=False)
clock = time.clock()
while True:
clock.tick()
lcd.write(csi0.snapshot(), hint=image.CENTER | image.SCALE_ASPECT_KEEP)
print(clock.fps())
Stuur de achtergrondverlichting aan via PWM voor regelbare helderheid. Verpak machine.PWM in een kleine achtergrondverlichtingscontrollerklasse en geef die door aan SPIDisplay via het argument backlight — SPIDisplay roept backlight(value) op het object aan wanneer het niveau moet worden bijgewerkt:
import csi
import time
import display
import image
from machine import Pin, PWM
class PWMBacklight:
"""Drives a backlight pin with machine.PWM (0–100 %)."""
def __init__(self, pin, frequency=200):
self._pwm = PWM(Pin(pin), freq=frequency, duty_u16=0)
def backlight(self, value):
self._pwm.duty_u16(int(value * 65535 / 100))
def deinit(self):
self._pwm.deinit()
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
lcd = display.SPIDisplay(width=320,
height=240,
bgr=True,
vflip=False,
hmirror=False,
backlight=PWMBacklight("P6"))
lcd.backlight(50) # 0–100
clock = time.clock()
while True:
clock.tick()
lcd.write(csi0.snapshot(), hint=image.CENTER | image.SCALE_ASPECT_KEEP)
print(clock.fps())
Lees multi-touch-invoer van de ingebouwde capacitieve FT6x36-controller — aangesloten op de I²C-bus van de camera op P4/P5 met reset op P7 en IRQ op P9. Het onderstaande voorbeeld combineert touch met live camerastreaming en tekent een rode cirkel op het LCD waar een vinger wordt ingedrukt:
from time import sleep_ms
from array import array
from machine import Pin, SoftI2C
import csi
import display
import image
import time
_DEFAULT_ADDR = const(0x38)
_DEV_MODE = const(0x00)
_TD_STATUS = const(0x02)
class FT6X36:
FLAG_PRESSED = 0
FLAG_RELEASED = 1
FLAG_MOVED = 2
def __init__(
self,
bus,
reset_pin,
irq_pin,
address=_DEFAULT_ADDR,
width=320,
height=240,
reverse_x=False,
reverse_y=False,
touch_callback=None,
):
self.bus = bus
self.address = address
self.width = width
self.height = height
self.reverse_x = reverse_x
self.reverse_y = reverse_y
self.touch_callback = touch_callback
# reset_pin=None skips the reset pulse — useful when another
# peripheral on the same line (e.g. the LCD) has already done it.
if reset_pin is not None:
self.rst_pin = Pin(reset_pin, Pin.OUT_PP, value=0)
else:
self.rst_pin = None
self.irq_pin = None
self.irq_pin_label = irq_pin
# Reset the touch panel controller.
self.reset()
# Put the controller into normal operating mode.
self._write_reg(_DEV_MODE, 0x00)
# Scratch buffer for points (x, y, flag, id) — chip max 2.
self.points_data = [array("H", [0, 0, 0, 0]) for _ in range(2)]
self._touch_points_old = 0
self._touch_points = 0
def _read_reg(self, reg, size=1, buf=None):
# FT6X36 expects two separate START/STOP transactions
# (no repeated start), so don't use readfrom_mem here.
self.bus.writeto(self.address, bytes([reg]))
if buf is not None:
self.bus.readfrom_into(self.address, buf)
else:
return self.bus.readfrom(self.address, size)
def _write_reg(self, reg, val, size=1):
if size == 1:
buf = bytes([reg, val & 0xFF])
else:
buf = bytes([reg, val & 0xFF, val >> 8])
self.bus.writeto(self.address, buf)
def reset(self):
if self.irq_pin is not None:
self.irq_pin.irq(handler=None)
if self.rst_pin is not None:
self.rst_pin(0)
sleep_ms(1)
self.rst_pin(1)
sleep_ms(39)
self.irq_pin = Pin(self.irq_pin_label, Pin.IN, Pin.PULL_UP)
if self.touch_callback is not None:
self.irq_pin.irq(
handler=self.touch_callback,
trigger=Pin.IRQ_FALLING,
hard=False,
)
def read_points(self):
regs = self._read_reg(_TD_STATUS, 13)
n_points = min(regs[0] & 0x0F, 2)
for i in range(0, n_points):
base = 1 + i * 6
x = ((regs[base] & 0xF) << 8) | regs[base + 1]
y = ((regs[base + 2] & 0xF) << 8) | regs[base + 3]
if self.reverse_x:
x = self.width - 1 - x
if self.reverse_y:
y = self.height - 1 - y
self.points_data[i][0] = x
self.points_data[i][1] = y
self.points_data[i][2] = regs[base] >> 6
self.points_data[i][3] = regs[base + 2] >> 4
# Mark previously-active slots as released so the caller
# sees a release event after a finger lifts.
for i in range(n_points, 2):
self.points_data[i][2] = self.FLAG_RELEASED
# Latch touch count: rising immediate, falling debounced one read.
if n_points >= self._touch_points:
self._touch_points = n_points
elif n_points <= self._touch_points_old:
self._touch_points = self._touch_points_old
self._touch_points_old = n_points
return self._touch_points, self.points_data
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
lcd = display.SPIDisplay(width=320,
height=240,
bgr=True,
vflip=False,
hmirror=False)
# The LCD and touch controllers share P7 as a reset line. The LCD
# has already pulsed it during its own init, so init the touch
# controller after with reset_pin=None to skip a redundant pulse.
bus = SoftI2C(scl=Pin("P4"), sda=Pin("P5"), freq=100_000)
touch = FT6X36(bus, reset_pin=None, irq_pin="P9", reverse_y=True)
clock = time.clock()
# Some sensors return less than 240 lines at QVGA (e.g. 320x200 on
# the N6). The display centers the frame, so map touch Y to image Y.
y_offset = (touch.height - csi0.height()) // 2
while True:
clock.tick()
img = csi0.snapshot()
n, points = touch.read_points()
for i in range(n):
x, y, flag, tid = points[i]
if flag != FT6X36.FLAG_RELEASED:
iy = y - y_offset
if 0 <= iy < csi0.height():
img.draw_circle(
(x, iy, 18), color=(255, 0, 0), thickness=2
)
lcd.write(img, hint=image.CENTER | image.SCALE_ASPECT_KEEP)
print(clock.fps())