Skip to content

Commit

Permalink
rewrite touch calibrator example with hysterisis and other fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
barry-ha committed Dec 25, 2023
1 parent 030c8a3 commit eee8aae
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 130 deletions.
92 changes: 19 additions & 73 deletions examples/TFT_Touch_Calibrator/TFT_Touch_Calibrator.ino
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
TFT Touchscreen Calibrator - Touch screen with X, Y and Z (pressure) readings
Version history:
2023-12-24 improved debounce by adding hysteresis
2020-11-23 created touch calibrator
2022-05-20 updated calibration constants X_MIN_OHMS, X_MAX_OHMS, Y_MIN_OHMS, Y_MAX_OHMS
Expand All @@ -20,7 +21,7 @@
a. use larger X ohms to move apparent screen response left
b. use larger Y ohms to move apparent screen response down
5. Recompile and run this again
6. Once you're satisifed with the values, you can copy them directly into Griuino.ino
6. Once you're satisifed with the values, you can copy them directly into Griduino.ino
Coordinate system:
X and Y are in terms of the native touchscreen coordinates, which corresponds to
Expand All @@ -32,7 +33,7 @@
2. Adafruit 3.2" TFT color LCD display ILI-9341 https://www.adafruit.com/product/1743
How to: https://learn.adafruit.com/adafruit-2-dot-8-color-tft-touchscreen-breakout-v2
SPI Wiring: https://learn.adafruit.com/adafruit-2-dot-8-color-tft-touchscreen-breakout-v2/spi-wiring-and-test
Touchscreen: https://learn.adafruit.com/adafruiplt-2-dot-8-color-tft-touchscreen-breakout-v2/resistive-touchscreen
Touchscreen: https://learn.adafruit.com/adafruit-2-dot-8-color-tft-touchscreen-breakout-v2/resistive-touchscreen
*/

Expand All @@ -47,64 +48,21 @@
// ------- Identity for splash screen and console --------
#define PROGRAM_NAME "TFT Touch Calibrator"

#define SCREEN_ROTATION 1 // 0=portrait, 1=landscape, 2=portrait 180-deg, 3=landscape 180-deg

// ---------- extern
extern bool newScreenTap(Point *pPoint, int orientation); // Touch.cpp
extern uint16_t myPressure(void); // Touch.cpp
// extern bool TouchScreen::isTouching(void); // Touch.cpp
extern void mapTouchToScreen(TSPoint touch, Point *screen, int orientation);
extern void setFontSize(int font); // TextField.cpp
extern bool newScreenTap(TSPoint *pPoint, int orientation); // Touch.cpp
extern void initTouchScreen(void); // Touch.cpp

// ---------- Hardware Wiring ----------
// Same as Griduino platform - see hardware.h

// create an instance of the TFT Display
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// ---------- Touch Screen
TouchScreen ts = TouchScreen(PIN_XP, PIN_YP, PIN_XM, PIN_YM, XP_XM_OHMS);

// ------------ definitions
const int howLongToWait = 6; // max number of seconds at startup waiting for Serial port to console
#define SCREEN_ROTATION 1 // 1=landscape, 3=landscape 180 degrees

#define gScreenWidth 320 // pixels wide, landscape orientation

// ============== touchscreen helpers ==========================

void mapTouchToScreen(TSPoint touch, Point *screen) {
// convert from X+,Y+ resistance measurements to screen coordinates
// param touch = resistance readings from touchscreen
// param screen = result of converting touch into screen coordinates
//
// Measured readings in Barry's landscape orientation were:
// +---------------------+ X=876
// | |
// | |
// | |
// +---------------------+ X=160
// Y=110 Y=892
//
// Typical measured pressures=200..549

// setRotation(1) = landscape orientation = x-,y-axis exchanged
// map(value in_min,in_max, out_min,out_max)
screen->x = map(touch.y, Y_MIN_OHMS, Y_MAX_OHMS, 0, tft.width());
screen->y = map(touch.x, X_MAX_OHMS, X_MIN_OHMS, 0, tft.height());

// keep all touches within boundaries of the screen
screen->x = constrain(screen->x, 0, tft.width());
screen->y = constrain(screen->y, 0, tft.height());

if (tft.getRotation() == eSCREEN_ROTATE_180) {
// if display is flipped, then also flip both x,y touchscreen coords
screen->x = tft.width() - screen->x;
screen->y = tft.height() - screen->y;
}
return;
}

// ----- console Serial port helper
void waitForSerial(int howLong) {
// Adafruit Feather M4 Express takes awhile to restore its USB connx to the PC
Expand Down Expand Up @@ -168,7 +126,7 @@ void showActivityBar(int row, uint16_t foreground, uint16_t background) {
static int addDotX = 10; // current screen column, 0..319 pixels
static int rmvDotX = 0;
static int count = 0;
const int SCALEF = 128; // how much to slow it down so it becomes visible
const int SCALEF = 32; // how much to slow it down so it becomes visible

count = (count + 1) % SCALEF;
if (count == 0) {
Expand Down Expand Up @@ -232,8 +190,8 @@ TextField txtScreen[] = {
{"xxx", x3, row5, cVALUE}, // [5] current X value
{"X ", x4, row5, cXGROUP, ALIGNRIGHT}, // [6]
// row 6
{"Threshhold:", xul, row6, cLABEL}, // [7]
{TOUCHPRESSURE, x2, row6, cLABEL}, // [8]
{"Threshhold:", xul, row6, cLABEL}, // [7]
{START_TOUCH_PRESSURE, x2, row6, cLABEL}, // [8]
// row 7
// row 8
// row 9
Expand Down Expand Up @@ -263,11 +221,11 @@ void startScreen() {
}
}
void updateScreen(TSPoint tp) {
txtScreen[5].print(tp.x); // current X value
txtScreen[12].print(tp.y); // current Y value
if (tp.z > 0) {
txtScreen[4].print(tp.z); // current pressure value
}
txtScreen[5].print(tp.x); // current X value
txtScreen[12].print(tp.y); // current Y value
}

void labelAxis() {
Expand Down Expand Up @@ -330,9 +288,9 @@ void labelAxis() {
void setup() {

// ----- init TFT display
tft.begin(); // initialize TFT display
tft.setRotation(1); // 1=landscape (default is 0=portrait)
tft.fillScreen(ILI9341_BLACK); // note that "begin()" does not clear screen
tft.begin(); // initialize TFT display
tft.setRotation(eSCREEN_ROTATE_0); // 1=landscape (default is 0=portrait)
tft.fillScreen(ILI9341_BLACK); // note that "begin()" does not clear screen

// ----- init TFT backlight
pinMode(TFT_BL, OUTPUT);
Expand All @@ -351,7 +309,7 @@ void setup() {
Serial.println(__FILE__); // Report our source code file name

// ----- init touch screen
ts.pressureThreshhold = TOUCHPRESSURE;
initTouchScreen();

startScreen();
labelAxis();
Expand All @@ -365,27 +323,15 @@ void setup() {
//=========== main work loop ===================================

void loop() {
// a point object holds x y and z coordinates
TSPoint p = ts.getPoint(); // read touch screen

// we have some minimum pressure we consider 'valid'
// pressure of 0 means "no press"
if (p.z > ts.pressureThreshhold) {
Serial.print("x,y = ");
Serial.print(p.x);
Serial.print(",");
Serial.print(p.y);
Serial.print("\tPressure = ");
Serial.println(p.z);
}

// if there's touchscreen input, handle it
Point touch;
if (newScreenTap(&touch, tft.getRotation())) {
TSPoint screenLoc;
if (newScreenTap(&screenLoc, tft.getRotation())) {

// debug: show where touched
const int radius = 1;
tft.fillCircle(touch.x, touch.y, radius, cTOUCHTARGET);
updateScreen(p);
tft.fillCircle(screenLoc.x, screenLoc.y, radius, cTOUCHTARGET);
updateScreen(screenLoc);
}

// small activity bar crawls along bottom edge to give
Expand Down
98 changes: 41 additions & 57 deletions examples/TFT_Touch_Calibrator/Touch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,28 @@
Software: Barry Hansen, K7BWH, barry@k7bwh.com, Seattle, WA
Hardware: John Vanderbeck, KM7O, Seattle, WA
* Purpose: Contains the touchscreen code to get it out of the way
*
*/
Purpose: Contains the touchscreen code to get it out of the way
#include "Adafruit_GFX.h" // Core graphics display library
#include "TouchScreen.h" // Touchscreen built in to 3.2" Adafruit TFT display
*/

// ---------- constants
#define SCREENWIDTH 320
#define SCREENHEIGHT 240
#include <Adafruit_GFX.h> // Core graphics display library
#include <TouchScreen.h> // Touchscreen built in to 3.2" Adafruit TFT display
#include "constants.h" // Griduino constants, colors, typedefs
#include "hardware.h" // Griduino pin definitions

// ---------- Touch Screen
// For touch point precision, we need to know the resistance
// between X+ and X- Use any multimeter to read it
// This sketch has just one touch area that covers the entire screen
#if defined(SAMD_SERIES)
// Adafruit Feather M4 Express pin definitions
#define PIN_XP A3 // Touchscreen X+ can be a digital pin
#define PIN_XM A4 // Touchscreen X- must be an analog pin, use "An" notation
#define PIN_YP A5 // Touchscreen Y+ must be an analog pin, use "An" notation
#define PIN_YM 9 // Touchscreen Y- can be a digital pin
#elif defined(ARDUINO_AVR_MEGA2560)
// Arduino Mega 2560 and others
#define PIN_XP 4 // Touchscreen X+ can be a digital pin
#define PIN_XM A3 // Touchscreen X- must be an analog pin, use "An" notation
#define PIN_YP A2 // Touchscreen Y+ must be an analog pin, use "An" notation
#define PIN_YM 5 // Touchscreen Y- can be a digital pin
#else
#warning You need to define pins for your hardware

#endif
extern TouchScreen ts;

// ------------ typedef's
struct Point {
int x, y;
};
TouchScreen ts = TouchScreen(PIN_XP, PIN_YP, PIN_XM, PIN_YM, XP_XM_OHMS);

void initTouchScreen(void) {
ts.pressureThreshhold = START_TOUCH_PRESSURE;
}

// ============== touchscreen helpers ==========================

void mapTouchToScreen(TSPoint touch, Point *screen, int orientation) {
void mapTouchToScreen(TSPoint touchOhms, TSPoint *screenCoord, int orientation) {
// convert from X+,Y+ resistance measurements to screen coordinates
// param touch = resistance readings from touchscreen
// param screen = result of converting touch into screen coordinates
// param touchOhms = resistance readings from touchscreen
// param screenCoord = result of converting touchOhms into screen coordinates
//
// Measured readings in Barry's landscape orientation were:
// +---------------------+ X=876
Expand All @@ -60,22 +38,28 @@ void mapTouchToScreen(TSPoint touch, Point *screen, int orientation) {
// Typical measured pressures=200..549

// setRotation(1) = landscape orientation = x-,y-axis exchanged
// map(value in_min,in_max, out_min,out_max)
screen->x = map(touch.y, 150, 880, 0, SCREENWIDTH);
screen->y = map(touch.x, 860, 130, 0, SCREENHEIGHT);
if (orientation == 3) {
// map(value in_min,in_max, out_min,out_max)
screenCoord->x = map(touchOhms.y, X_MIN_OHMS, X_MAX_OHMS, 0, gScreenWidth);
screenCoord->y = map(touchOhms.x, Y_MAX_OHMS, Y_MIN_OHMS, 0, gScreenHeight);
screenCoord->z = touchOhms.z;

// keep all touches within boundaries of the screenCoord
screenCoord->x = constrain(screenCoord->x, 0, gScreenWidth);
screenCoord->y = constrain(screenCoord->y, 0, gScreenHeight);

if (orientation == eSCREEN_ROTATE_180) {
// if display is flipped, then also flip both x,y touchscreen coords
screen->x = SCREENWIDTH - screen->x;
screen->y = SCREENHEIGHT - screen->y;
screenCoord->x = gScreenWidth - screenCoord->x;
screenCoord->y = gScreenHeight - screenCoord->y;
}
return;
}

bool gTouching = false; // keep track of previous state
bool newScreenTap(Point *pPoint, int orientation) {
bool newScreenTap(TSPoint *pScreenCoord, int orientation) {
// find leading edge of a screen touch
// returns TRUE only once on initial screen press
// if true, also return screen coordinates of the touch
// if true, also return screen coordinates and touch pressure

bool result = false; // assume no touch
if (gTouching) {
Expand All @@ -92,16 +76,17 @@ bool newScreenTap(Point *pPoint, int orientation) {
gTouching = true;
result = true;

// touchscreen point object has (x,y,z) coordinates, where z = pressure
// touchscreen point object has (x/Ohms,y/Ohms,z/Pressure)
TSPoint touch = ts.getPoint();

// convert resistance measurements into screen pixel coords
mapTouchToScreen(touch, pPoint, orientation);
mapTouchToScreen(touch, pScreenCoord, orientation);
Serial.print("Screen touched at (");
Serial.print(pPoint->x);
Serial.print(pScreenCoord->x);
Serial.print(",");
Serial.print(pPoint->y);
Serial.println(")");
Serial.print(pScreenCoord->y);
Serial.print(") pressure=");
Serial.println(pScreenCoord->z);
}
}
// delay(10); // no delay: code above completely handles debouncing without blocking the loop
Expand All @@ -114,14 +99,14 @@ bool newScreenTap(Point *pPoint, int orientation) {
// 2020-05-03 CraigV and barry@k7bwh.com
uint16_t myPressure(void) {
pinMode(PIN_XP, OUTPUT);
digitalWrite(PIN_XP, LOW); // Set X+ to ground
pinMode(PIN_YM, OUTPUT);
digitalWrite(PIN_XP, LOW); // Set X+ to ground
pinMode(PIN_YM, OUTPUT); //
digitalWrite(PIN_YM, HIGH); // Set Y- to VCC

digitalWrite(PIN_XM, LOW);
pinMode(PIN_XM, INPUT); // Hi-Z X-
digitalWrite(PIN_YP, LOW);
pinMode(PIN_YP, INPUT); // Hi-Z Y+
pinMode(PIN_XM, INPUT); // Set X- to Hi-Z
digitalWrite(PIN_YP, LOW); //
pinMode(PIN_YP, INPUT); // Set Y+ to Hi-Z

int z1 = analogRead(PIN_XM);
int z2 = 1023 - analogRead(PIN_YP);
Expand All @@ -133,17 +118,16 @@ uint16_t myPressure(void) {
// Note - For Griduino, if this function takes longer than 8 msec it can cause erratic GPS readings
// so we recommend against using https://forum.arduino.cc/index.php?topic=449719.0
bool TouchScreen::isTouching(void) {
#define TOUCHPRESSURE 200 // Minimum pressure we consider true pressing
static bool button_state = false;
uint16_t pres_val = ::myPressure();

if ((button_state == false) && (pres_val > TOUCHPRESSURE)) {
if ((button_state == false) && (pres_val > START_TOUCH_PRESSURE)) {
Serial.print(". pressed, pressure = ");
Serial.println(pres_val); // debug
button_state = true;
}

if ((button_state == true) && (pres_val < TOUCHPRESSURE)) {
if ((button_state == true) && (pres_val < END_TOUCH_PRESSURE)) {
Serial.print(". released, pressure = ");
Serial.println(pres_val); // debug
button_state = false;
Expand Down

0 comments on commit eee8aae

Please sign in to comment.