<reponame>peterhinch/micropython-tft-gui
# touch_bytecode.py
# Adapted for (and requires) uasyncio V3

# The MIT License (MIT)
# 
# Copyright (c) 2016 <NAME> (c) 2016-2020 <NAME>
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Class supporting the resisitve touchpad of TFT LC-displays
# First example: Controller XPT2046
# It uses Y5..Y8 of PyBoard
#
import pyb, stm
import uasyncio as asyncio

# define constants
#
PCB_VERSION = 2

#if PCB_VERSION == 1:
#    CONTROL_PORT = stm.GPIOB
#    T_CLOCK = const(1 << 15)  ## Y8 = B15
#    T_DOUT  = const(1 << 14)  ## Y7 = B14
#    T_DIN   = const(1 << 13)  ## Y6 = B13
#    T_IRQ   = const(1 << 12)  ## Y5 = B12
    
if PCB_VERSION == 2:
    CONTROL_PORT = stm.GPIOC
    T_CLOCK = const(1 << 5)  ## X12 = C5
    T_DOUT  = const(1 << 4)  ## X11 = C4
    T_DIN   = const(1 << 7)  ## Y2  = C7
    T_IRQ   = const(1 << 6)  ## Y1  = C6

# T_CS is not used and must be hard tied to GND

T_GETX  = const(0xd0)  ## 12 bit resolution
T_GETY  = const(0x90)  ## 12 bit resolution
T_GETZ1 = const(0xb8)  ## 8 bit resolution
T_GETZ2 = const(0xc8)  ## 8 bit resolution
#
X_LOW  = const(10)     ## lowest reasonable X value from the touchpad
Y_HIGH = const(4090)   ## highest reasonable Y value 

class TOUCH:
#
# Init just sets the PIN's to In / out as required
# async: set True 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", asyn = False, *, 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)
            self.pin_d_out = pyb.Pin("Y7", pyb.Pin.OUT_PP)
            self.pin_d_in  = pyb.Pin("Y6", pyb.Pin.IN)
            self.pin_irq   = pyb.Pin("Y5", pyb.Pin.IN)
        else:
            self.pin_clock = pyb.Pin("X11", pyb.Pin.OUT_PP)
            self.pin_clock.value(0)
            self.pin_d_out = pyb.Pin("X12", pyb.Pin.OUT_PP)
            self.pin_d_in  = pyb.Pin("Y1", pyb.Pin.IN)
            self.pin_irq   = pyb.Pin("Y2", pyb.Pin.IN)
# set default values
        self.ready = False
        self.touched = False
        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 asyn:
            self.asynchronous = True
            asyncio.create_task(self._main_thread())

# 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, 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
#           False = Do not wait for a release
# wait: Wait for a touch or not?
#       False: Do not wait for a touch and return immediately
#       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
# 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 = 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
    async def _main_thread(self):
        buff = self.buff
        buf_length = self.buf_length
        buffptr = 0
        nsamples = 0
        await asyncio.sleep(0)
        while True:
            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
                    self.ready = True
                    self.x, self.y = self.do_normalize((meanx, meany))
            sample = self.raw_touch()  # get a touch
            if sample == None:
                self.touched = False
                self.ready = False
                nsamples = 0    # Invalidate buff
            else:
                self.touched = True
                buff[buffptr] = sample # put in buff
                buffptr = (buffptr + 1) % buf_length
                nsamples = min(nsamples + 1, buf_length)
            await asyncio.sleep(0)

# Asynchronous get_touch
    def get_touch_async(self):
        if self.ready:
            self.ready = False
            return self.x, self.y
        return None
# 
# do_normalize(touch)
# calculate the screen coordinates from the touch values, using the calibration values
# touch must be the tuple return by get_touch
#
    def do_normalize(self, touch):
        xmul = self.calibration[3] + (self.calibration[1] - self.calibration[3]) * (touch[1] / 4096)
        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)
        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
#
    def raw_touch(self):
        x  = self.touch_talk(T_GETX, 12)
        y  = self.touch_talk(T_GETY, 12)
        if x > X_LOW and y < Y_HIGH:  # touch pressed?
            return (x, y)
        else:
            return None
#
# Send a command to the touch controller and wait for the response
# cmd is the command byte
# int is the expected size of return data bits
# port is the gpio base port
#
# Straight down coding of the data sheet's timing diagram
# This is the slow bytecode implementations
# Clock low & high cycles must last at least 200ns
# At the moment it is set to about 25us each, 
# 1050µs total at 168 MHz clock rate.
# Total net time for a 12 bit sample: ~ 1050 µs
#
    def touch_talk(self, cmd, bits):

        global CONTROL_PORT
        gpio_bsr = CONTROL_PORT + stm.GPIO_BSRRL
        gpio_idr = CONTROL_PORT + stm.GPIO_IDR
#
# now shift the command out, which is 8 bits 
# data is sampled at the low-> high transient
#
        stm.mem16[gpio_bsr + 2] = T_CLOCK # Empty clock cycle before start, maybe obsolete
        mask = 0x80  # high bit first
        for i in range(8):
            stm.mem16[gpio_bsr + 2] = T_CLOCK # set clock low in the beginning
            if cmd & mask:
                stm.mem16[gpio_bsr + 0] = T_DOUT # set data bit high
            else:
                stm.mem16[gpio_bsr + 2] = T_DOUT # set data bit low
            for i in range(1): pass #delay
            stm.mem16[gpio_bsr + 0] = T_CLOCK # set clock high
            mask >>= 1
        stm.mem16[gpio_bsr + 2] = T_CLOCK | T_DOUT# Another clock & data, low
        stm.mem16[gpio_bsr + 0] = T_CLOCK # clock High
#
# now shift the data in, which is 8 or 12 bits 
# data is sampled after the high->low transient
#
        result = 0
        for i in range(bits):
            stm.mem16[gpio_bsr + 2] = T_CLOCK # Clock low
            if stm.mem16[gpio_idr + 0] & T_DIN: # get data
                bit = 1
            else:
                bit = 0
            result = (result << 1) | bit # shift data in
            stm.mem16[gpio_bsr + 0] = T_CLOCK # Clock high
#
# another clock cycle, maybe obsolete
#
        stm.mem16[gpio_bsr + 2] = T_CLOCK # Another clock toggle, low
        stm.mem16[gpio_bsr + 0] = T_CLOCK # clock High
        stm.mem16[gpio_bsr + 2] = T_CLOCK # Clock low
# now we're ready to leave
        return result


# touch_bytecode.py
# Adapted for (and requires) uasyncio V3

# The MIT License (MIT)
# 
# Copyright (c) 2016 <NAME> (c) 2016-2020 <NAME>
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Class supporting the resisitve touchpad of TFT LC-displays
# First example: Controller XPT2046
# It uses Y5..Y8 of PyBoard
#
import pyb, stm
import uasyncio as asyncio

# define constants
#
PCB_VERSION = 2

#if PCB_VERSION == 1:
#    CONTROL_PORT = stm.GPIOB
#    T_CLOCK = const(1 << 15)  ## Y8 = B15
#    T_DOUT  = const(1 << 14)  ## Y7 = B14
#    T_DIN   = const(1 << 13)  ## Y6 = B13
#    T_IRQ   = const(1 << 12)  ## Y5 = B12
    
if PCB_VERSION == 2:
    CONTROL_PORT = stm.GPIOC
    T_CLOCK = const(1 << 5)  ## X12 = C5
    T_DOUT  = const(1 << 4)  ## X11 = C4
    T_DIN   = const(1 << 7)  ## Y2  = C7
    T_IRQ   = const(1 << 6)  ## Y1  = C6

# T_CS is not used and must be hard tied to GND

T_GETX  = const(0xd0)  ## 12 bit resolution
T_GETY  = const(0x90)  ## 12 bit resolution
T_GETZ1 = const(0xb8)  ## 8 bit resolution
T_GETZ2 = const(0xc8)  ## 8 bit resolution
#
X_LOW  = const(10)     ## lowest reasonable X value from the touchpad
Y_HIGH = const(4090)   ## highest reasonable Y value 

class TOUCH:
#
# Init just sets the PIN's to In / out as required
# async: set True 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", asyn = False, *, 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)
            self.pin_d_out = pyb.Pin("Y7", pyb.Pin.OUT_PP)
            self.pin_d_in  = pyb.Pin("Y6", pyb.Pin.IN)
            self.pin_irq   = pyb.Pin("Y5", pyb.Pin.IN)
        else:
            self.pin_clock = pyb.Pin("X11", pyb.Pin.OUT_PP)
            self.pin_clock.value(0)
            self.pin_d_out = pyb.Pin("X12", pyb.Pin.OUT_PP)
            self.pin_d_in  = pyb.Pin("Y1", pyb.Pin.IN)
            self.pin_irq   = pyb.Pin("Y2", pyb.Pin.IN)
# set default values
        self.ready = False
        self.touched = False
        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 asyn:
            self.asynchronous = True
            asyncio.create_task(self._main_thread())

# 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, 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
#           False = Do not wait for a release
# wait: Wait for a touch or not?
#       False: Do not wait for a touch and return immediately
#       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
# 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 = 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
    async def _main_thread(self):
        buff = self.buff
        buf_length = self.buf_length
        buffptr = 0
        nsamples = 0
        await asyncio.sleep(0)
        while True:
            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
                    self.ready = True
                    self.x, self.y = self.do_normalize((meanx, meany))
            sample = self.raw_touch()  # get a touch
            if sample == None:
                self.touched = False
                self.ready = False
                nsamples = 0    # Invalidate buff
            else:
                self.touched = True
                buff[buffptr] = sample # put in buff
                buffptr = (buffptr + 1) % buf_length
                nsamples = min(nsamples + 1, buf_length)
            await asyncio.sleep(0)

# Asynchronous get_touch
    def get_touch_async(self):
        if self.ready:
            self.ready = False
            return self.x, self.y
        return None
# 
# do_normalize(touch)
# calculate the screen coordinates from the touch values, using the calibration values
# touch must be the tuple return by get_touch
#
    def do_normalize(self, touch):
        xmul = self.calibration[3] + (self.calibration[1] - self.calibration[3]) * (touch[1] / 4096)
        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)
        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
#
    def raw_touch(self):
        x  = self.touch_talk(T_GETX, 12)
        y  = self.touch_talk(T_GETY, 12)
        if x > X_LOW and y < Y_HIGH:  # touch pressed?
            return (x, y)
        else:
            return None
#
# Send a command to the touch controller and wait for the response
# cmd is the command byte
# int is the expected size of return data bits
# port is the gpio base port
#
# Straight down coding of the data sheet's timing diagram
# This is the slow bytecode implementations
# Clock low & high cycles must last at least 200ns
# At the moment it is set to about 25us each, 
# 1050µs total at 168 MHz clock rate.
# Total net time for a 12 bit sample: ~ 1050 µs
#
    def touch_talk(self, cmd, bits):

        global CONTROL_PORT
        gpio_bsr = CONTROL_PORT + stm.GPIO_BSRRL
        gpio_idr = CONTROL_PORT + stm.GPIO_IDR
#
# now shift the command out, which is 8 bits 
# data is sampled at the low-> high transient
#
        stm.mem16[gpio_bsr + 2] = T_CLOCK # Empty clock cycle before start, maybe obsolete
        mask = 0x80  # high bit first
        for i in range(8):
            stm.mem16[gpio_bsr + 2] = T_CLOCK # set clock low in the beginning
            if cmd & mask:
                stm.mem16[gpio_bsr + 0] = T_DOUT # set data bit high
            else:
                stm.mem16[gpio_bsr + 2] = T_DOUT # set data bit low
            for i in range(1): pass #delay
            stm.mem16[gpio_bsr + 0] = T_CLOCK # set clock high
            mask >>= 1
        stm.mem16[gpio_bsr + 2] = T_CLOCK | T_DOUT# Another clock & data, low
        stm.mem16[gpio_bsr + 0] = T_CLOCK # clock High
#
# now shift the data in, which is 8 or 12 bits 
# data is sampled after the high->low transient
#
        result = 0
        for i in range(bits):
            stm.mem16[gpio_bsr + 2] = T_CLOCK # Clock low
            if stm.mem16[gpio_idr + 0] & T_DIN: # get data
                bit = 1
            else:
                bit = 0
            result = (result << 1) | bit # shift data in
            stm.mem16[gpio_bsr + 0] = T_CLOCK # Clock high
#
# another clock cycle, maybe obsolete
#
        stm.mem16[gpio_bsr + 2] = T_CLOCK # Another clock toggle, low
        stm.mem16[gpio_bsr + 0] = T_CLOCK # clock High
        stm.mem16[gpio_bsr + 2] = T_CLOCK # Clock low
# now we're ready to leave
        return result
# touch_bytecode.py # Adapted for (and requires) uasyncio V3 # The MIT License (MIT) # # Copyright (c) 2016 <NAME> (c) 2016-2020 <NAME> # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # Class supporting the resisitve touchpad of TFT LC-displays # First example: Controller XPT2046 # It uses Y5..Y8 of PyBoard # # define constants # #if PCB_VERSION == 1: #    CONTROL_PORT = stm.GPIOB #    T_CLOCK = const(1 << 15)  ## Y8 = B15 #    T_DOUT  = const(1 << 14)  ## Y7 = B14 #    T_DIN   = const(1 << 13)  ## Y6 = B13 #    T_IRQ   = const(1 << 12)  ## Y5 = B12 ## X12 = C5 ## X11 = C4 ## Y2  = C7 ## Y1  = C6 # T_CS is not used and must be hard tied to GND ## 12 bit resolution ## 12 bit resolution ## 8 bit resolution ## 8 bit resolution # ## lowest reasonable X value from the touchpad ## highest reasonable Y value # # Init just sets the PIN's to In / out as required # async: set True 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) # # set default values # 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. # # Ignore attempts to change on the fly. # store the square value # 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 #           False = Do not wait for a release # wait: Wait for a touch or not? #       False: Do not wait for a touch and return immediately #       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 # timeout: Longest time (ms, or None = 1 hr) to wait for a touch or release # # Return (x,y) or None # # Should only be called in synhronous mode # set timeout to 1 hour # ## wait for a non-touch state # after timeout, return None # # got one; compare against the square value # get a new value # get a touch # Invalidate buff # put in buff # Asynchronous use: this thread maintains self.x and self.y # got one; compare against the square value # get a touch # Invalidate buff # put in buff # Asynchronous get_touch # # do_normalize(touch) # calculate the screen coordinates from the touch values, using the calibration values # touch must be the tuple return by get_touch # # # raw_touch(tuple) # raw read touch. Returns (x,y) or None # # touch pressed? # # Send a command to the touch controller and wait for the response # cmd is the command byte # int is the expected size of return data bits # port is the gpio base port # # Straight down coding of the data sheet's timing diagram # This is the slow bytecode implementations # Clock low & high cycles must last at least 200ns # At the moment it is set to about 25us each, # 1050µs total at 168 MHz clock rate. # Total net time for a 12 bit sample: ~ 1050 µs # # # now shift the command out, which is 8 bits # data is sampled at the low-> high transient # # Empty clock cycle before start, maybe obsolete # high bit first # set clock low in the beginning # set data bit high # set data bit low #delay # set clock high # Another clock & data, low # clock High # # now shift the data in, which is 8 or 12 bits # data is sampled after the high->low transient # # Clock low # get data # shift data in # Clock high # # another clock cycle, maybe obsolete # # Another clock toggle, low # clock High # Clock low # now we're ready to leave