Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added XPT2046 touch screen digitizer for ILI9341 display #11159

Merged
merged 7 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions lib/lib_display/XPT2046_Touchscreen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# XPT2046 Touchscreen Arduino Library

XPT2046_Touchscreen is a library for the XPT2046 resistive touchscreen controllers used on many low cost TFT displays.

![ILI9431Test Example Program](doc/ILI9431Test.jpg)

## Setup Functions

First, create an instance of the library for your touchscreen. The digital pin
used for chip select is required. The normal MISO, MOSI and SCK pins will be
used automatically.

#define CS_PIN 8
XPT2046_Touchscreen ts(CS_PIN);

The use of the Touch interrupt pin can be optionally specified. If the Teensy
pin specified is actively connected to the T_IRQ display pin then the normal
touch calls will respond, but can be called more often as each call returns
without hardware access when no interrupt was recorded.

#define TIRQ_PIN 2
XPT2046_Touchscreen ts(CS_PIN, TIRQ_PIN);

In setup(), use the begin() function to initialize the touchscreen, and
optionally use setRotation(n), where n is 0 to 3, matching the rotation
setting in ILI9341_t3, Adafruit_ILI9341 or other Adafruit compatible TFT
libraries.

ts.begin();
ts.setRotation(1);

## Reading Touch Info

The touched() function tells if the display is currently being touched,
returning true or false.

if (ts.touched()) {
// do something....
}

You can read the touch coordinates with readData()

uint16_t x, y, z;
ts.readData(&x, &y, &z);

or with getPoint(), which returns a TS_Point object:

TS_Point p = ts.getPoint();
Serial.print("x = ");
Serial.print(p.x);
Serial.print(", y = ");
Serial.print(p.y);

The Z coordinate represents the amount of pressure applied to the screen.

## Adafruit Library Compatibility

XPT2046_Touchscreen is meant to be a compatible with sketches written for Adafruit_STMPE610, offering the same functions, parameters and numerical ranges as Adafruit's library.

## Using The Interrupt Pin : Built in support when connected nothing else is needed. When specified as above
no SPI calls are made unless a Touch was detected. On normal connections - this means the Teensy LED
won't blink on every touch query.

## Using The Interrupt Pin : Custom use would preclude the normal built in usage. The warning below is justified.

The XPT2046 chip has an interrupt output, which is typically labeled T_IRQ on many low cost TFT displays. No special software support is needed in this library. The interrupt pin always outputs a digital signal related to the touch controller signals, which is LOW when the display is touched. It also is driven low while software reads the touch position.

The interrupt can be used as a wakeup signal, if you put your microcontroller into a deep sleep mode. Normally, you would stop reading the touch data, then enable the interrupt pin with attachInterrupt(), and then configure your processor to wake when the interrupt occurs, before enter a deep sleep mode. Upon waking, you would normally disable the interrupt before reading the display, to prevent false interrupts caused by the process of reading touch positions.

You can also use the interrupt to respond to touch events. Setup might look similar to this:

SPI.usingInterrupt(digitalPinToInterrupt(pin))
attachInterrupt(digitalPinToInterrupt(pin), myFunction, FALLING);

However, inside your interrupt function, if the display is no longer being touched, any attempt to read the touch position will cause the interrupt pin to create another falling edge. This can lead to an infinite loop of falsely triggered interrupts. Special care is needed to avoid triggering more interrupts on the low signal due to reading the touch position.

For most applications, regularly reading the touch position from the main program is much simpler.

219 changes: 219 additions & 0 deletions lib/lib_display/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/* Touchscreen library for XPT2046 Touch Controller Chip
* Copyright (c) 2015, Paul Stoffregen, [email protected]
*
* 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, development funding 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.
*/

#include "XPT2046_Touchscreen.h"

#define Z_THRESHOLD 400
#define Z_THRESHOLD_INT 75
#define MSEC_THRESHOLD 3
#define SPI_SETTING SPISettings(2000000, MSBFIRST, SPI_MODE0)

static XPT2046_Touchscreen *isrPinptr;
void isrPin(void);

bool XPT2046_Touchscreen::begin(SPIClass &wspi)
{
_pspi = &wspi;
_pspi->begin();
pinMode(csPin, OUTPUT);
digitalWrite(csPin, HIGH);
if (255 != tirqPin) {
pinMode( tirqPin, INPUT );
attachInterrupt(digitalPinToInterrupt(tirqPin), isrPin, FALLING);
isrPinptr = this;
}
return true;
}

#if defined(_FLEXIO_SPI_H_)
#define FLEXSPI_SETTING FlexIOSPISettings(2000000, MSBFIRST, SPI_MODE0)
bool XPT2046_Touchscreen::begin(FlexIOSPI &wflexspi)
{
_pspi = nullptr; // make sure we dont use this one...
_pflexspi = &wflexspi;
_pflexspi->begin();
pinMode(csPin, OUTPUT);
digitalWrite(csPin, HIGH);
if (255 != tirqPin) {
pinMode( tirqPin, INPUT );
attachInterrupt(digitalPinToInterrupt(tirqPin), isrPin, FALLING);
isrPinptr = this;
}
return true;
}
#endif


ISR_PREFIX
void isrPin( void )
{
XPT2046_Touchscreen *o = isrPinptr;
o->isrWake = true;
}

TS_Point XPT2046_Touchscreen::getPoint()
{
update();
return TS_Point(xraw, yraw, zraw);
}

bool XPT2046_Touchscreen::tirqTouched()
{
return (isrWake);
}

bool XPT2046_Touchscreen::touched()
{
update();
return (zraw >= Z_THRESHOLD);
}

void XPT2046_Touchscreen::readData(uint16_t *x, uint16_t *y, uint8_t *z)
{
update();
*x = xraw;
*y = yraw;
*z = zraw;
}

bool XPT2046_Touchscreen::bufferEmpty()
{
return ((millis() - msraw) < MSEC_THRESHOLD);
}

static int16_t besttwoavg( int16_t x , int16_t y , int16_t z ) {
int16_t da, db, dc;
int16_t reta = 0;
if ( x > y ) da = x - y; else da = y - x;
if ( x > z ) db = x - z; else db = z - x;
if ( z > y ) dc = z - y; else dc = y - z;

if ( da <= db && da <= dc ) reta = (x + y) >> 1;
else if ( db <= da && db <= dc ) reta = (x + z) >> 1;
else reta = (y + z) >> 1; // else if ( dc <= da && dc <= db ) reta = (x + y) >> 1;

return (reta);
}

// TODO: perhaps a future version should offer an option for more oversampling,
// with the RANSAC algorithm https://en.wikipedia.org/wiki/RANSAC

void XPT2046_Touchscreen::update()
{
int16_t data[6];
int z;
if (!isrWake) return;
uint32_t now = millis();
if (now - msraw < MSEC_THRESHOLD) return;
if (_pspi) {
_pspi->beginTransaction(SPI_SETTING);
digitalWrite(csPin, LOW);
_pspi->transfer(0xB1 /* Z1 */);
int16_t z1 = _pspi->transfer16(0xC1 /* Z2 */) >> 3;
z = z1 + 4095;
int16_t z2 = _pspi->transfer16(0x91 /* X */) >> 3;
z -= z2;
if (z >= Z_THRESHOLD) {
_pspi->transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy
data[0] = _pspi->transfer16(0xD1 /* Y */) >> 3;
data[1] = _pspi->transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements
data[2] = _pspi->transfer16(0xD1 /* Y */) >> 3;
data[3] = _pspi->transfer16(0x91 /* X */) >> 3;
}
else data[0] = data[1] = data[2] = data[3] = 0; // Compiler warns these values may be used unset on early exit.
data[4] = _pspi->transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down
data[5] = _pspi->transfer16(0) >> 3;
digitalWrite(csPin, HIGH);
_pspi->endTransaction();
}
#if defined(_FLEXIO_SPI_H_)
else if (_pflexspi) {
_pflexspi->beginTransaction(FLEXSPI_SETTING);
digitalWrite(csPin, LOW);
_pflexspi->transfer(0xB1 /* Z1 */);
int16_t z1 = _pflexspi->transfer16(0xC1 /* Z2 */) >> 3;
z = z1 + 4095;
int16_t z2 = _pflexspi->transfer16(0x91 /* X */) >> 3;
z -= z2;
if (z >= Z_THRESHOLD) {
_pflexspi->transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy
data[0] = _pflexspi->transfer16(0xD1 /* Y */) >> 3;
data[1] = _pflexspi->transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements
data[2] = _pflexspi->transfer16(0xD1 /* Y */) >> 3;
data[3] = _pflexspi->transfer16(0x91 /* X */) >> 3;
}
else data[0] = data[1] = data[2] = data[3] = 0; // Compiler warns these values may be used unset on early exit.
data[4] = _pflexspi->transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down
data[5] = _pflexspi->transfer16(0) >> 3;
digitalWrite(csPin, HIGH);
_pflexspi->endTransaction();

}
#endif
// If we do not have either _pspi or _pflexspi than bail.
else return;

//Serial.printf("z=%d :: z1=%d, z2=%d ", z, z1, z2);
if (z < 0) z = 0;
if (z < Z_THRESHOLD) { // if ( !touched ) {
// Serial.println();
zraw = 0;
if (z < Z_THRESHOLD_INT) { // if ( !touched ) {
if (255 != tirqPin) isrWake = false;
}
return;
}
zraw = z;

// Average pair with least distance between each measured x then y
//Serial.printf(" z1=%d,z2=%d ", z1, z2);
//Serial.printf("p=%d, %d,%d %d,%d %d,%d", zraw,
//data[0], data[1], data[2], data[3], data[4], data[5]);
int16_t x = besttwoavg( data[0], data[2], data[4] );
int16_t y = besttwoavg( data[1], data[3], data[5] );

//Serial.printf(" %d,%d", x, y);
//Serial.println();
if (z >= Z_THRESHOLD) {
msraw = now; // good read completed, set wait
switch (rotation) {
case 0:
xraw = 4095 - y;
yraw = x;
break;
case 1:
xraw = x;
yraw = y;
break;
case 2:
xraw = y;
yraw = 4095 - x;
break;
default: // 3
xraw = 4095 - x;
yraw = 4095 - y;
}
}
}



89 changes: 89 additions & 0 deletions lib/lib_display/XPT2046_Touchscreen/XPT2046_Touchscreen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* Touchscreen library for XPT2046 Touch Controller Chip
* Copyright (c) 2015, Paul Stoffregen, [email protected]
*
* 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, development funding 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.
*/

#ifndef _XPT2046_Touchscreen_h_
#define _XPT2046_Touchscreen_h_

#include "Arduino.h"
#include <SPI.h>

#if defined(__IMXRT1062__)
#if __has_include(<FlexIOSPI.h>)
#include <FlexIOSPI.h>
#endif
#endif

#if ARDUINO < 10600
#error "Arduino 1.6.0 or later (SPI library) is required"
#endif

class TS_Point {
public:
TS_Point(void) : x(0), y(0), z(0) {}
TS_Point(int16_t x, int16_t y, int16_t z) : x(x), y(y), z(z) {}
bool operator==(TS_Point p) { return ((p.x == x) && (p.y == y) && (p.z == z)); }
bool operator!=(TS_Point p) { return ((p.x != x) || (p.y != y) || (p.z != z)); }
int16_t x, y, z;
};

class XPT2046_Touchscreen {
public:
constexpr XPT2046_Touchscreen(uint8_t cspin, uint8_t tirq=255)
: csPin(cspin), tirqPin(tirq) { }
bool begin(SPIClass &wspi = SPI);
#if defined(_FLEXIO_SPI_H_)
bool begin(FlexIOSPI &wflexspi);
#endif

TS_Point getPoint();
bool tirqTouched();
bool touched();
void readData(uint16_t *x, uint16_t *y, uint8_t *z);
bool bufferEmpty();
uint8_t bufferSize() { return 1; }
void setRotation(uint8_t n) { rotation = n % 4; }
// protected:
volatile bool isrWake=true;

private:
void update();
uint8_t csPin, tirqPin, rotation=1;
int16_t xraw=0, yraw=0, zraw=0;
uint32_t msraw=0x80000000;
SPIClass *_pspi = nullptr;
#if defined(_FLEXIO_SPI_H_)
FlexIOSPI *_pflexspi = nullptr;
#endif
};

#ifndef ISR_PREFIX
#if defined(ESP8266)
#define ISR_PREFIX ICACHE_RAM_ATTR
#elif defined(ESP32)
// TODO: should this also be ICACHE_RAM_ATTR ??
#define ISR_PREFIX IRAM_ATTR
#else
#define ISR_PREFIX
#endif
#endif

#endif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading