forked from embedded/micropython-ili9341
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.9 KiB
Python
136 lines
4.9 KiB
Python
"""XPT2046 Touch module."""
|
|
from time import sleep
|
|
|
|
|
|
class Touch(object):
|
|
"""Serial interface for XPT2046 Touch Screen Controller."""
|
|
|
|
# Command constants from ILI9341 datasheet
|
|
GET_X = const(0b11010000) # X position
|
|
GET_Y = const(0b10010000) # Y position
|
|
GET_Z1 = const(0b10110000) # Z1 position
|
|
GET_Z2 = const(0b11000000) # Z2 position
|
|
GET_TEMP0 = const(0b10000000) # Temperature 0
|
|
GET_TEMP1 = const(0b11110000) # Temperature 1
|
|
GET_BATTERY = const(0b10100000) # Battery monitor
|
|
GET_AUX = const(0b11100000) # Auxiliary input to ADC
|
|
|
|
def __init__(self, spi, cs, int_pin=None, int_handler=None,
|
|
width=240, height=320,
|
|
x_min=100, x_max=1962, y_min=100, y_max=1900):
|
|
"""Initialize touch screen controller.
|
|
|
|
Args:
|
|
spi (Class Spi): SPI interface for OLED
|
|
cs (Class Pin): Chip select pin
|
|
int_pin (Class Pin): Touch controller interrupt pin
|
|
int_handler (function): Handler for screen interrupt
|
|
width (int): Width of LCD screen
|
|
height (int): Height of LCD screen
|
|
x_min (int): Minimum x coordinate
|
|
x_max (int): Maximum x coordinate
|
|
y_min (int): Minimum Y coordinate
|
|
y_max (int): Maximum Y coordinate
|
|
"""
|
|
self.spi = spi
|
|
self.cs = cs
|
|
self.cs.init(self.cs.OUT, value=1)
|
|
self.rx_buf = bytearray(3) # Receive buffer
|
|
self.tx_buf = bytearray(3) # Transmit buffer
|
|
self.width = width
|
|
self.height = height
|
|
# Set calibration
|
|
self.x_min = x_min
|
|
self.x_max = x_max
|
|
self.y_min = y_min
|
|
self.y_max = y_max
|
|
self.x_multiplier = width / (x_max - x_min)
|
|
self.x_add = x_min * -self.x_multiplier
|
|
self.y_multiplier = height / (y_max - y_min)
|
|
self.y_add = y_min * -self.y_multiplier
|
|
|
|
if int_pin is not None:
|
|
self.int_pin = int_pin
|
|
self.int_pin.init(int_pin.IN)
|
|
self.int_handler = int_handler
|
|
self.int_locked = False
|
|
int_pin.irq(trigger=int_pin.IRQ_FALLING | int_pin.IRQ_RISING,
|
|
handler=self.int_press)
|
|
|
|
def get_touch(self):
|
|
"""Take multiple samples to get accurate touch reading."""
|
|
timeout = 2 # set timeout to 2 seconds
|
|
confidence = 5
|
|
buff = [[0, 0] for x in range(confidence)]
|
|
buf_length = confidence # Require a confidence of 5 good samples
|
|
buffptr = 0 # Track current buffer position
|
|
nsamples = 0 # Count samples
|
|
while timeout > 0:
|
|
if nsamples == buf_length:
|
|
meanx = sum([c[0] for c in buff]) // buf_length
|
|
meany = sum([c[1] for c in buff]) // buf_length
|
|
dev = sum([(c[0] - meanx)**2 +
|
|
(c[1] - meany)**2 for c in buff]) / buf_length
|
|
if dev <= 50: # Deviation should be under margin of 50
|
|
return self.normalize(meanx, meany)
|
|
# get a new value
|
|
sample = self.raw_touch() # get a touch
|
|
if sample is None:
|
|
nsamples = 0 # Invalidate buff
|
|
else:
|
|
buff[buffptr] = sample # put in buff
|
|
buffptr = (buffptr + 1) % buf_length # Incr, until rollover
|
|
nsamples = min(nsamples + 1, buf_length) # Incr. until max
|
|
|
|
sleep(.05)
|
|
timeout -= .05
|
|
return None
|
|
|
|
def int_press(self, pin):
|
|
"""Send X,Y values to passed interrupt handler."""
|
|
if not pin.value() and not self.int_locked:
|
|
self.int_locked = True # Lock Interrupt
|
|
buff = self.raw_touch()
|
|
|
|
if buff is not None:
|
|
x, y = self.normalize(*buff)
|
|
self.int_handler(x, y)
|
|
sleep(.1) # Debounce falling edge
|
|
elif pin.value() and self.int_locked:
|
|
sleep(.1) # Debounce rising edge
|
|
self.int_locked = False # Unlock interrupt
|
|
|
|
def normalize(self, x, y):
|
|
"""Normalize mean X,Y values to match LCD screen."""
|
|
x = int(self.x_multiplier * x + self.x_add)
|
|
y = int(self.y_multiplier * y + self.y_add)
|
|
return x, y
|
|
|
|
def raw_touch(self):
|
|
"""Read raw X,Y touch values.
|
|
|
|
Returns:
|
|
tuple(int, int): X, Y
|
|
"""
|
|
x = self.send_command(self.GET_X)
|
|
y = self.send_command(self.GET_Y)
|
|
if self.x_min <= x <= self.x_max and self.y_min <= y <= self.y_max:
|
|
return (x, y)
|
|
else:
|
|
return None
|
|
|
|
def send_command(self, command):
|
|
"""Write command to XT2046 (MicroPython).
|
|
|
|
Args:
|
|
command (byte): XT2046 command code.
|
|
Returns:
|
|
int: 12 bit response
|
|
"""
|
|
self.tx_buf[0] = command
|
|
self.cs(0)
|
|
self.spi.write_readinto(self.tx_buf, self.rx_buf)
|
|
self.cs(1)
|
|
|
|
return (self.rx_buf[1] << 4) | (self.rx_buf[2] >> 4)
|