Touch LCD Shield

Touch LCD Shield, OpenMV Cam’e 2,3 inçlik 320x240 kapasitif çoklu dokunmatik bir ekran kazandırarak kamera çıktısını bir ana bilgisayar olmadan önizlemenizi (ve girdi kabul etmenizi) sağlar. İki Qwiic başlığı, ek I2C cihazlarını zincirlemeyi kolaylaştırır.

Touch LCD Shield

Tam veri sayfası, fotoğraflar ve sipariş için bkz. Touch LCD Shield ürün sayfası.

Öne çıkanlar

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

  • Kapasitif çoklu dokunmatik girdi

  • PWM ile kontrol edilebilir arka aydınlatma

  • Kolay I2C cihaz zincirlemesi için iki Qwiic konektörü

Pin Dağılımı

Touch LCD Shield Pin Dağılımı

Pin referansı

Pin

İşlev

P0

LCD MOSI (ekrana SPI verisi)

P1

LCD TE (yırtılma efekti çıkışı)

P2

LCD SCLK (SPI saat sinyali)

P3

LCD SSEL (SPI çip seçimi)

P4

Dokunmatik / Qwiic SCL (I²C saat sinyali — Qwiic başlıklarıyla paylaşılır)

P5

Dokunmatik / Qwiic SDA (I²C verisi — Qwiic başlıklarıyla paylaşılır)

P6

LCD arka aydınlatma

P7

Dokunmatik / LCD RESET_N

P8

LCD RS (veri / komut seçimi)

P9

Dokunmatik INT_N

3,3V hattı

LCD ve dokunmatik denetleyicilerini besler

GND hattı

Ortak toprak

Kullanım

Shield’i display.SPIDisplay sınıfı üzerinden sürün. Kamera çerçevelerini 320×240 LCD’ye aktarın:

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

Ayarlanabilir parlaklık için arka aydınlatmayı PWM aracılığıyla sürün. machine.PWM nesnesini küçük bir arka aydınlatma denetleyici sınıfının içine sarın ve bunu backlight argümanı aracılığıyla SPIDisplay sınıfına geçirin — SPIDisplay, seviyeyi güncellemesi gerektiğinde nesne üzerinde backlight(value) çağrısı yapar:

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

Yerleşik FT6x36 kapasitif denetleyiciden çoklu dokunmatik girdi okuyun — bu denetleyici P4/P5 üzerinden kameranın I²C veri yoluna, sıfırlama P7’ye ve IRQ P9’a bağlıdır. Aşağıdaki örnek, dokunmayı canlı kamera akışıyla birlikte kullanarak LCD üzerinde bir parmağın bastırıldığı her yere kırmızı bir daire çizer:

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