From d2f4e9e79be19d846ff799439275b45ccf0b46a4 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Sat, 23 Apr 2016 11:35:03 +0100 Subject: [PATCH] asynctouch.py replaced by generic version of touch.py --- tft_gui/buttontest.py | 4 +- tft_gui/slidetest.py | 4 +- tft_gui/{asynctouch.py => touch.py} | 136 +++++++++++++++++++--------- tft_gui/ui.py | 2 +- 4 files changed, 97 insertions(+), 49 deletions(-) rename tft_gui/{asynctouch.py => touch.py} (67%) diff --git a/tft_gui/buttontest.py b/tft_gui/buttontest.py index c26713e..37c7af2 100644 --- a/tft_gui/buttontest.py +++ b/tft_gui/buttontest.py @@ -3,7 +3,7 @@ import gc from font14 import font14 from tft import TFT, LANDSCAPE from usched import Sched -from asynctouch import TOUCH +from touch import TOUCH from button import Button, Buttonset, RadioButtons from ui import CIRCLE, RECTANGLE, CLIPPED_RECT #gc.collect() @@ -66,7 +66,7 @@ def test(duration = 0): print('Testing TFT...') objsched = Sched() # Instantiate the scheduler mytft = TFT("SSD1963", "LB04301", LANDSCAPE) - mytouch = TOUCH(objsched, "XPT2046") + mytouch = TOUCH("XPT2046", objsched) mytft.backlight(100) # light on # Button assortment diff --git a/tft_gui/slidetest.py b/tft_gui/slidetest.py index 4b81405..6f3d579 100644 --- a/tft_gui/slidetest.py +++ b/tft_gui/slidetest.py @@ -3,7 +3,7 @@ import gc from font10 import font10 from tft import TFT, LANDSCAPE from usched import Sched -from asynctouch import TOUCH +from touch import TOUCH from slider import Slider from button import Button from ui import CLIPPED_RECT @@ -84,7 +84,7 @@ def test(duration = 0): print('Test TFT panel...') objsched = Sched() # Instantiate the scheduler mytft = TFT("SSD1963", "LB04301", LANDSCAPE) - mytouch = TOUCH(objsched, "XPT2046") + mytouch = TOUCH("XPT2046", objsched) mytft.backlight(100) # light on Button(objsched, mytft, mytouch, (400, 240), font = font10, callback = doquit, fgcolor = (255, 0, 0), height = 30, text = 'Quit', shape = CLIPPED_RECT) diff --git a/tft_gui/asynctouch.py b/tft_gui/touch.py similarity index 67% rename from tft_gui/asynctouch.py rename to tft_gui/touch.py index ef590cf..d015386 100644 --- a/tft_gui/asynctouch.py +++ b/tft_gui/touch.py @@ -1,8 +1,7 @@ -# asynctouch.py Asynchronous version of XPT2046 touchscreen controller. - +# # The MIT License (MIT) # -# Copyright (c) 2016 Robert Hammelrath, Peter Hinch +# Copyright (c) 2016 Robert Hammelrath # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -58,9 +57,14 @@ Y_HIGH = const(4090) ## highest reasonable Y value class TOUCH: # # Init just sets the PIN's to In / out as required -# - def __init__(self, objsched, controller = "XPT2046", calibration = None, raw = False): - self.raw = raw +# objsched: scheduler if asynchronous operation intended +# confidence: confidence level - number of consecutive touches with a margin smaller than the given level +# which the function will sample until it accepts it as a valid touch +# margin: Difference from mean centre at which touches are considered at the same position +# delay: Delay between samples in ms. (n/a if asynchronous) +# + DEFAULT_CAL = (-3917, -0.127, -3923, -0.1267, -3799, -0.07572, -3738, -0.07814) + def __init__(self, controller = "XPT2046", objsched = None, *, confidence = 5, margin = 50, delay = 10, calibration = None): if PCB_VERSION == 1: self.pin_clock = pyb.Pin("Y8", pyb.Pin.OUT_PP) self.pin_clock.value(0) @@ -74,37 +78,38 @@ class TOUCH: self.pin_d_in = pyb.Pin("Y1", pyb.Pin.IN) self.pin_irq = pyb.Pin("Y2", pyb.Pin.IN) # set default values - self.margin = 50 * 50 # tolerance margin: standard deviation of distance of touch from mean; store square - self.buff = [[-self.margin, -self.margin] for x in range(5)] # confidence == 5. Impossible coords - if calibration: - self.calibration = calibration - else: # default values for my tft - self.calibration = (-3917,-0.127,-3923,-0.1267,-3799,-0.07572,-3738,-0.07814) - self.position = [0, 0] self.ready = False self.touched = False - objsched.add_thread(self.main_thread()) + self.x = 0 + self.y = 0 + self.buf_length = 0 + cal = TOUCH.DEFAULT_CAL if calibration is None else calibration + self.asynchronous = False + self.touch_parameter(confidence, margin, delay, cal) + if objsched is not None: + self.asynchronous = True + objsched.add_thread(self._main_thread()) -# -# Now I'm up to get my touches -# -# # set parameters for get_touch() +# res: Resolution in bits of the returned values, default = 10 # confidence: confidence level - number of consecutive touches with a margin smaller than the given level # which the function will sample until it accepts it as a valid touch # margin: Difference from mean centre at which touches are considered at the same position +# delay: Delay between samples in ms. # - def touch_parameter(self, confidence = 5, margin = 50, calibration = None): - confidence = max(min(confidence, 25), 5) - margin = max(min(margin, 100), 1) - m = margin * margin - if confidence != len(self.buff): - self.buff = [[-m, -m] for x in range(confidence)] # impossible values - self.margin = margin * margin # store the square value - if calibration: - self.calibration = calibration -# -# get_touch(): get a touch value; Parameters: + def touch_parameter(self, confidence = 5, margin = 50, delay = 10, calibration = None): + if not self.asynchronous: # Ignore attempts to change on the fly. + confidence = max(min(confidence, 25), 5) + if confidence != self.buf_length: + self.buff = [[0,0] for x in range(confidence)] + self.buf_length = confidence + self.delay = max(min(delay, 100), 5) + margin = max(min(margin, 100), 1) + self.margin = margin * margin # store the square value + if calibration: + self.calibration = calibration + +# get_touch(): Synchronous use. get a touch value; Parameters: # # initital: Wait for a non-touch state before getting a sample. # True = Initial wait for a non-touch state @@ -114,11 +119,57 @@ class TOUCH: # True: Wait until a touch is pressed. # raw: Setting whether raw touch coordinates (True) or normalized ones (False) are returned # setting the calibration vector to (0, 1, 0, 1, 0, 1, 0, 1) result in a identity mapping - -# updates self.position - def main_thread(self): +# timeout: Longest time (ms, or None = 1 hr) to wait for a touch or release +# +# Return (x,y) or None +# + def get_touch(self, initial = True, wait = True, raw = False, timeout = None): + if self.asynchronous: + return None # Should only be called in synhronous mode + if timeout == None: + timeout = 3600000 # set timeout to 1 hour +# + if initial: ## wait for a non-touch state + sample = True + while sample and timeout > 0: + sample = self.raw_touch() + pyb.delay(self.delay) + timeout -= self.delay + if timeout <= 0: # after timeout, return None + return None +# buff = self.buff - buf_length = len(buff) + buf_length = self.buf_length + buffptr = 0 + nsamples = 0 + 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 <= self.margin: # got one; compare against the square value + if raw: + return (meanx, meany) + else: + return self.do_normalize((meanx, meany)) +# get a new value + sample = self.raw_touch() # get a touch + if sample == None: + if not wait: + return None + nsamples = 0 # Invalidate buff + else: + buff[buffptr] = sample # put in buff + buffptr = (buffptr + 1) % buf_length + nsamples = min(nsamples +1, buf_length) + pyb.delay(self.delay) + timeout -= self.delay + return None + +# Asynchronous use: this thread maintains self.x and self.y + def _main_thread(self): + buff = self.buff + buf_length = self.buf_length buffptr = 0 nsamples = 0 yield # Initialisation complete, wait for scheduler to start @@ -129,11 +180,7 @@ class TOUCH: dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length if dev <= self.margin: # got one; compare against the square value self.ready = True - if self.raw: - self.position[0] = meanx - self.position[1] = meany - else: - self.do_normalize((meanx, meany)) + self.x, self.y = self.do_normalize((meanx, meany)) sample = self.raw_touch() # get a touch if sample == None: self.touched = False @@ -143,13 +190,14 @@ class TOUCH: self.touched = True buff[buffptr] = sample # put in buff buffptr = (buffptr + 1) % buf_length - nsamples = min(nsamples +1, buf_length) + nsamples = min(nsamples + 1, buf_length) yield - def get_touch(self): +# Asynchronous get_touch + def get_touch_async(self): if self.ready: self.ready = False - return self.position + return self.x, self.y return None # # do_normalize(touch) @@ -161,9 +209,9 @@ class TOUCH: xadd = self.calibration[2] + (self.calibration[0] - self.calibration[2]) * (touch[1] / 4096) ymul = self.calibration[7] + (self.calibration[5] - self.calibration[7]) * (touch[0] / 4096) yadd = self.calibration[6] + (self.calibration[4] - self.calibration[6]) * (touch[0] / 4096) - self.position[0] = int((touch[0] + xadd) * xmul) - self.position[1] = int((touch[1] + yadd) * ymul) - + x = int((touch[0] + xadd) * xmul) + y = int((touch[1] + yadd) * ymul) + return (x, y) # # raw_touch(tuple) # raw read touch. Returns (x,y) or None diff --git a/tft_gui/ui.py b/tft_gui/ui.py index 7cde3e8..6eb310a 100644 --- a/tft_gui/ui.py +++ b/tft_gui/ui.py @@ -28,7 +28,7 @@ class touchable(object): while True: yield if mytouch.ready: - x, y = mytouch.get_touch() + x, y = mytouch.get_touch_async() for obj in cls.touchlist: if obj.enabled: obj.touched(x, y)