Touch LCD Shield

Touch LCD Shield memberi OpenMV Cam layar sentuh kapasitif multi-sentuh 2,3 inci berukuran 320x240 sehingga Anda dapat melihat pratinjau output kamera (dan menerima input) tanpa komputer host. Dua header Qwiic memudahkan penghubungan perangkat I2C tambahan.

Touch LCD Shield

Untuk datasheet lengkap, foto, dan pemesanan, lihat halaman produk Touch LCD Shield.

Sorotan

  • TFT LCD 2,3 inci, 320x240, RGB565 16-bit

  • Input sentuh kapasitif multi-sentuh

  • Lampu latar dapat dikontrol dengan PWM

  • Dua konektor Qwiic untuk penghubungan perangkat I2C yang mudah

Pinout

Pinout Touch LCD Shield

Referensi pin

Pin

Fungsi

P0

LCD MOSI (data SPI ke layar)

P1

LCD TE (output efek tearing)

P2

LCD SCLK (clock SPI)

P3

LCD SSEL (chip select SPI)

P4

Touch / Qwiic SCL (clock I²C — berbagi dengan header Qwiic)

P5

Touch / Qwiic SDA (data I²C — berbagi dengan header Qwiic)

P6

Lampu latar LCD

P7

Touch / LCD RESET_N

P8

LCD RS (pemilih data / perintah)

P9

Touch INT_N

Rel 3,3V

Memberi daya pada kontroler LCD dan sentuh

Rel GND

Ground bersama

Penggunaan

Kendalikan shield melalui kelas display.SPIDisplay. Alirkan bingkai kamera ke LCD 320×240:

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())

Kendalikan lampu latar melalui PWM untuk kecerahan yang dapat disesuaikan. Bungkus machine.PWM dalam kelas kontroler lampu latar kecil dan teruskan ke SPIDisplay melalui argumen backlight-nya — SPIDisplay memanggil backlight(value) pada objek tersebut setiap kali perlu memperbarui level:

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())

Baca input multi-sentuh dari kontroler kapasitif FT6x36 on-board — terhubung ke bus I²C kamera pada P4/P5 dengan reset pada P7 dan IRQ pada P9. Contoh di bawah ini menggabungkan sentuhan dengan streaming kamera langsung, menggambar lingkaran merah pada LCD di mana pun jari ditekan:

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())