Touchscreen Edge Detection

Let me provide you with my calibration sketch. Provided 'as is' and without support. (it's been several years since i wrote this)
The idea is that when you use a toothpick and slide to the edge, you will find the correct extreme value

/*  TFT Touchscreen Calibrateter

 */
#include <EEPROM.h>        // for storage of the settings
#include <Adafruit_GFX.h>    // Core graphics library

#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;       // hard-wired for UNO shields anyway.
#include <TouchScreen.h>

#if defined(__SAM3X8E__)
#undef __FlashStringHelper::F(string_literal)
#define F(string_literal) string_literal
#endif


// most mcufriend shields use these pins and Portrait mode:
const uint8_t YP = A1;  // must be an analog pin, use "An" notation!
const uint8_t XM = A2;  // must be an analog pin, use "An" notation!
const uint8_t YM = 7;   // can be a digital pin
const uint8_t XP = 6;   // can be a digital pin


uint8_t SwapXY = 1; // depends on the TFT shield

uint16_t TS_LEFT = 930;
uint16_t TS_RT = 175;
uint16_t TS_TOP = 175;  // this is actually the usb end at the start of the program
uint16_t TS_BOT = 920;

volatile uint16_t TFT_X_RANGE = 240;  // making these variables volatile solves the issue that when changing
volatile uint16_t TFT_Y_RANGE = 320;  // their value (or even keeping it the same but writing to them)
                                      // caused an incorrect memory write with black area's on screen.


// For better pressure precision, we need to know the resistance
// between X+ and X- Use any multimeter to read it
// For the one we're using, its 300 ohms across the X plate
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 455);
TSPoint tp;

#define MINPRESSURE 5
#define MAXPRESSURE 1000
#define RELEASECYCLES 200

#define EEPROM_OFFSET 448  // set this to a matching value in the program 
                          // depending on the calibrated values, do not use 
                          // the low Eeprom adddresses since the graphics library
                          // appears to be using them.

#define SWAP(a, b) {uint16_t tmp = a; a = b; b = tmp;}

uint16_t identifier;
uint8_t orientation = 0;    //Portrait



// Assign human-readable names to some common 16-bit color values:
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF


void setup() {
  tft.begin(9600);
  tft.reset();
  identifier = tft.readID();
  tft.begin(identifier);
  tft.setRotation(orientation);  // we use orientation here to set the TFT rotation

//  TS_Rotation(orientation);  
  ts = TouchScreen(XP, YP, XM, YM, 455);     //  call the constructor 
  while (!TitleScreen()) Change_TFTXY_Settings(); 
  CalibrationScreen();
}

void loop() {
}

void CalibrationScreen() {
  tft.fillScreen(BLUE);
  tft.setTextSize(2);
  tft.setTextColor(BLACK);
  const char * title = "TFT Calibrater";            // header text
  tft.setCursor((TFT_X_RANGE/2) - strlen(title)*6,20);
  tft.print(title);

  tft.setCursor((TFT_X_RANGE/2) - 54,40);
  tft.print(String(TFT_X_RANGE)+" X "+String(TFT_Y_RANGE));
  tft.fillRect(TFT_X_RANGE/4,TFT_Y_RANGE/4,TFT_X_RANGE/2,TFT_Y_RANGE/2,RED);
  tft.setTextSize(1);
  const char * order = "Press the red rectangle";
  tft.setCursor((TFT_X_RANGE/2) - strlen(order)*3,TFT_Y_RANGE-50);
  tft.print(order);
  tft.drawRect(2,2,TFT_X_RANGE-4,TFT_Y_RANGE-4,RED);
  WaitReleaseReadPress(true);
  tft.fillRect((TFT_X_RANGE/8)*3,(TFT_Y_RANGE/8)*3,TFT_X_RANGE/4,TFT_Y_RANGE/4,YELLOW);
  while (!ConfirmRelease());
  tft.fillRect((TFT_X_RANGE/8)*3,(TFT_Y_RANGE/8)*3,TFT_X_RANGE/4,TFT_Y_RANGE/4,RED);
  
  uint16_t minx=500,miny=500,maxx=500,maxy=500;
  uint8_t minxq=4,minyq=4,maxxq=4,maxyq=4,i=0;
  tft.drawRect(2,2,TFT_X_RANGE-4,TFT_Y_RANGE-4,BLUE);
  tft.fillRect(0,  0,  TFT_X_RANGE,  TFT_Y_RANGE/4,  BLUE);
  tft.fillRect(0,  (TFT_Y_RANGE/4)*3  ,TFT_X_RANGE,  TFT_Y_RANGE/4,  BLUE);
  tft.fillRect((TFT_X_RANGE/8),(TFT_Y_RANGE/8),(TFT_X_RANGE/4)*3,(TFT_Y_RANGE/4)*3,RED);
  EdgeLookText();
  WaitReleaseReadPress(true);
  tft.fillScreen(BLUE);
  tft.fillRect(TFT_X_RANGE/4,TFT_Y_RANGE/4,TFT_X_RANGE/2,TFT_Y_RANGE/2,RED);  
  
  DrawArrow(0);
  FillQuad(i,CYAN);

  while (i<4) {
    tp = ts.getPoint();
    ResetPins();
    if (tp.z < MINPRESSURE || tp.z > MAXPRESSURE) continue;

    tft.setTextColor(YELLOW,BLUE);
    tft.setTextSize(2);
    tft.setCursor((TFT_X_RANGE/4)+20,(TFT_Y_RANGE/4)*(3-(i/2)*3)+20);
    tft.print("tp.x=" + String(tp.x));
    tft.setCursor((TFT_X_RANGE/4)+20,(TFT_Y_RANGE/4)*(3-(i/2)*3)+50);
    tft.print("tp.y=" + String(tp.y));
    tft.setTextSize(1);
    
    if (tp.x>maxx) {
      maxx=tp.x;
      maxxq=i;
      tft.setCursor((TFT_X_RANGE/4)*3+10,40);
      tft.print("MaxX="+String(maxx));
    }
    if (tp.x<minx) {
      minx=tp.x;
      minxq=i;
      tft.setCursor(10,40);
      tft.print("MinX="+String(minx));
    }
    if (tp.y>maxy) {
      maxy=tp.y;
      maxyq=i;
      tft.setCursor((TFT_X_RANGE/4)*3+10,(TFT_Y_RANGE/4)*3+40);
      tft.print("MaxY="+String(maxy));
    }
    if (tp.y<miny) {
      miny=tp.y;
      minyq=i;
      tft.setCursor(10,(TFT_Y_RANGE/4)*3+40);
      tft.print("MinY="+String(miny));
    }
    if (tp.x > 450 && tp.x < 570  && tp.y > 450 && tp.y < 570) { // exit
      FillQuad(i,BLUE);
      i++;
      tft.fillRect((TFT_X_RANGE/8)*3,(TFT_Y_RANGE/8)*3,TFT_X_RANGE/4,TFT_Y_RANGE/4,YELLOW);
      while (!ConfirmRelease());
      tft.fillRect((TFT_X_RANGE/4),(TFT_Y_RANGE/4),TFT_X_RANGE/2,TFT_Y_RANGE/2,RED);
      DrawArrow(i);
      FillQuad(i,CYAN);
    }
  }
  tft.fillScreen(YELLOW);
  tft.fillRect((TFT_X_RANGE/4),(TFT_Y_RANGE/4),TFT_X_RANGE/2,TFT_Y_RANGE/2,BLUE);
  tft.setTextColor(BLACK,YELLOW);
  tft.setTextSize(2);
  SetCursorInQuad(minxq,0); 
  tft.print("MinX");
  SetCursorInQuad(minxq,2);
  tft.print(String(minx));
  SetCursorInQuad(maxxq,0); 
  tft.print("MaxX");
  SetCursorInQuad(maxxq,2);
  tft.print(String(maxx));
  SetCursorInQuad(minyq,0); 
  tft.print("MinY");
  SetCursorInQuad(minyq,2);
  tft.print(String(miny));
  SetCursorInQuad(maxyq,0); 
  tft.print("MaxY");
  SetCursorInQuad(maxyq,2);
  tft.print(String(maxy));

  EepromSaveScreen();
  WaitReleaseReadPress(true);
  StoreInVar(minxq,minx-3);
  StoreInVar(maxxq,maxx+3);
  StoreInVar(minyq,miny-3);
  StoreInVar(maxyq,maxy+3);
  StoreValues();
  ReadValues();
  PrintValues();
}

void StoreInVar(uint8_t side,uint16_t value) {
  switch(side) {
    case 0: 
      TS_TOP=value;
      break;
    case 1:
      TS_RT=value;
      break;
    case 2:
      TS_BOT=value;
      break;
    case 3:
      TS_LEFT=value;
      break;
  }
}

void EepromSaveScreen() {
  tft.setTextColor(YELLOW,BLUE);
  tft.setTextSize(1);
  const char * line1 = "The Values found";
  const char * line2 = "Will be stored in";
  const char * line3 = "The Eeprom as 16";
  const char * line4 = "bit U-integers";
  const char * line5 = "Press to continue";
  tft.setCursor((TFT_X_RANGE/2) - strlen(line1)*3,((TFT_Y_RANGE/2)-24));
  tft.print(line1);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line2)*3,((TFT_Y_RANGE/2)-12));
  tft.print(line2);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line3)*3,((TFT_Y_RANGE/2)));
  tft.print(line3);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line4)*3,((TFT_Y_RANGE/2)+12));
  tft.print(line4);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line5)*3,((TFT_Y_RANGE/2)+30));
  tft.print(line5); 
}

void StoreValues() {
  Write16bitEeprom(EEPROM_OFFSET,TFT_X_RANGE);
  Write16bitEeprom(EEPROM_OFFSET+2,TFT_Y_RANGE);
  Write16bitEeprom(EEPROM_OFFSET+4,TS_TOP);
  Write16bitEeprom(EEPROM_OFFSET+6,TS_RT);
  Write16bitEeprom(EEPROM_OFFSET+8,TS_BOT);
  Write16bitEeprom(EEPROM_OFFSET+10,TS_LEFT);
}

void Write16bitEeprom(uint16_t location, uint16_t uinteg) {
  EEPROM.write(location,lowByte(uinteg));
  EEPROM.write(location+1,highByte(uinteg));
}

void ReadValues() {     //copy the following 2 functions into your program
  TFT_X_RANGE=Read16bitEeprom(EEPROM_OFFSET);
  TFT_Y_RANGE=Read16bitEeprom(EEPROM_OFFSET+2);
  TS_TOP=Read16bitEeprom(EEPROM_OFFSET+4);
  TS_RT=Read16bitEeprom(EEPROM_OFFSET+6);
  TS_BOT=Read16bitEeprom(EEPROM_OFFSET+8);
  TS_LEFT=Read16bitEeprom(EEPROM_OFFSET+10);
}

uint16_t Read16bitEeprom(uint16_t location) {
  return( ((uint16_t) EEPROM.read(location+1)<<8) | (uint16_t) EEPROM.read(location));
}

void PrintValues() {
  tft.fillScreen(GREEN);
  tft.fillRect((TFT_X_RANGE/4),(TFT_Y_RANGE/4),TFT_X_RANGE/2,TFT_Y_RANGE/2,WHITE);
  tft.setTextColor(BLACK);
  tft.setTextSize(2);
  SetCursorInQuad(0,1); 
  tft.print(String(TS_TOP));
  SetCursorInQuad(1,1); 
  tft.print(String(TS_RT));
  SetCursorInQuad(2,1); 
  tft.print(String(TS_BOT));
  SetCursorInQuad(3,1); 
  tft.print(String(TS_LEFT));
  tft.setCursor(TFT_X_RANGE/2-15,TFT_Y_RANGE/2-24);
  tft.print(String(TFT_X_RANGE));
  tft.setCursor(TFT_X_RANGE/2-3,TFT_Y_RANGE/2-6);
  tft.print("X");
  tft.setCursor(TFT_X_RANGE/2-15,TFT_Y_RANGE/2+12);
  tft.print(String(TFT_Y_RANGE));
}


void SetCursorInQuad(uint8_t quad, uint8_t line) {
  switch(quad) {
    case 0:
      tft.setCursor((TFT_X_RANGE/8)*3+line*7,20+line*15);
      break;
    case 1:
      tft.setCursor((TFT_X_RANGE/4)*3+5+line*7,(TFT_Y_RANGE/8)*3+20+line*15);
      break;
    case 2:
      tft.setCursor((TFT_X_RANGE/8)*3+line*7,(TFT_Y_RANGE/4)*3+20+line*15);
      break;
    case 3:
      tft.setCursor(5+line*7,(TFT_Y_RANGE/8)*3+20+line*15);
      break;
  }
  return;
}


void FillQuad(uint8_t quad,uint16_t color) {
  switch (quad) {
    case 0:
      tft.fillRect(TFT_X_RANGE/4,0,TFT_X_RANGE/2,TFT_Y_RANGE/4,color);
      break;
    case 1:
      tft.fillRect((TFT_X_RANGE/4)*3,TFT_Y_RANGE/4,TFT_X_RANGE/4,TFT_Y_RANGE/2,color);
      break;
    case 2:
      tft.fillRect(TFT_X_RANGE/4,(TFT_Y_RANGE/4)*3,TFT_X_RANGE/2,TFT_Y_RANGE/4,color);
      break;
    case 3:
      tft.fillRect(0,TFT_Y_RANGE/4,TFT_X_RANGE/4,TFT_Y_RANGE/2,color);
  }
}


void DrawArrow(uint8_t direct) {
  if (direct>3) return;
  if (direct%2) {  // horizontal
    for (uint8_t i=0; i<10; i++) {
      if ((i) && (i<9))  tft.drawLine(TFT_X_RANGE/2-(TFT_X_RANGE/16)*3, TFT_Y_RANGE/2-5+i, 
                          TFT_X_RANGE/2+(TFT_X_RANGE/16)*3, TFT_Y_RANGE/2-5+i, BLACK);
      if (direct/2) { // to left
        tft.drawLine(TFT_X_RANGE/2-(TFT_X_RANGE/16)*3, TFT_Y_RANGE/2-5+i,
                    TFT_X_RANGE/2,    TFT_Y_RANGE/2-5+i-(TFT_Y_RANGE/16)*2,BLACK);
        tft.drawLine(TFT_X_RANGE/2-(TFT_X_RANGE/16)*3, TFT_Y_RANGE/2-5+i,
                    TFT_X_RANGE/2,    TFT_Y_RANGE/2-5+i+(TFT_Y_RANGE/16)*2,BLACK);                    
      }
      else {  // to right
        tft.drawLine(TFT_X_RANGE/2+(TFT_X_RANGE/16)*3, TFT_Y_RANGE/2-5+i,
                    TFT_X_RANGE/2,    TFT_Y_RANGE/2-5+i-(TFT_Y_RANGE/16)*2,BLACK);
        tft.drawLine(TFT_X_RANGE/2+(TFT_X_RANGE/16)*3, TFT_Y_RANGE/2-5+i,
                    TFT_X_RANGE/2,    TFT_Y_RANGE/2-5+i+(TFT_Y_RANGE/16)*2,BLACK);        
      }
    }
  }
  else {  // vertical
  for (uint8_t i=0; i<10; i++) {
    if ((i) && (i<9))  tft.drawLine(TFT_X_RANGE/2-5+i,  TFT_Y_RANGE/2-(TFT_Y_RANGE/16)*3,  
                          TFT_X_RANGE/2-5+i, TFT_Y_RANGE/2+(TFT_Y_RANGE/16)*3, BLACK);
      if (!(direct/2)) { // to Bottom
        tft.drawLine(TFT_X_RANGE/2-5+i, TFT_Y_RANGE/2-(TFT_Y_RANGE/16)*3, 
                    TFT_X_RANGE/2-5+i-(TFT_X_RANGE/16)*2,  TFT_Y_RANGE/2,    BLACK);
        tft.drawLine(TFT_X_RANGE/2-5+i,  TFT_Y_RANGE/2-(TFT_Y_RANGE/16)*3, 
                    TFT_X_RANGE/2-5+i+(TFT_X_RANGE/16)*2,  TFT_Y_RANGE/2,    BLACK);                    
      }
      else {  // to Top
        tft.drawLine(TFT_X_RANGE/2-5+i,  TFT_Y_RANGE/2+(TFT_Y_RANGE/16)*3, 
                    TFT_X_RANGE/2-5+i-(TFT_X_RANGE/16)*2,  TFT_Y_RANGE/2,    BLACK);
        tft.drawLine(TFT_X_RANGE/2-5+i,  TFT_Y_RANGE/2+(TFT_Y_RANGE/16)*3, 
                    TFT_X_RANGE/2-5+i+(TFT_X_RANGE/16)*2,  TFT_Y_RANGE/2,    BLACK);
      }        
    }
  }
}

void EdgeLookText() {
  tft.setTextColor(BLACK,RED);
  tft.setTextSize(1);
  const char * line1 = "Look for the edge's";
  const char * line2 = "extreme value in the";
  const char * line3 = "lightblue area, using";
  const char * line4 = "a toothpick, and when";
  const char * line5 = "done press the center";
  const char * line6 = "of this red square for";
  const char * line7 = "the next edge.";
  const char * line8 = "Press to continue";
  tft.setCursor((TFT_X_RANGE/2) - strlen(line1)*3,((TFT_Y_RANGE/2)-48));
  tft.print(line1);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line2)*3,((TFT_Y_RANGE/2)-36));
  tft.print(line2);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line3)*3,((TFT_Y_RANGE/2)-24));
  tft.print(line3);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line4)*3,((TFT_Y_RANGE/2)-12));
  tft.print(line4);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line5)*3,((TFT_Y_RANGE/2)));
  tft.print(line5); 
  tft.setCursor((TFT_X_RANGE/2) - strlen(line6)*3,((TFT_Y_RANGE/2)+12));
  tft.print(line6);  
  tft.setCursor((TFT_X_RANGE/2) - strlen(line7)*3,((TFT_Y_RANGE/2)+24));
  tft.print(line7);
  tft.setCursor((TFT_X_RANGE/2) - strlen(line8)*3,((TFT_Y_RANGE/2)+48));
  tft.print(line8);
}

bool TitleScreen() {
  tft.fillScreen(WHITE);
  tft.setTextSize(2);
  tft.setTextColor(BLACK);
  const char * title = "TFT Calibrater";
  tft.setCursor((TFT_X_RANGE/2) - strlen(title)*6,20);
  tft.print(title);
  delay(1000);
  tft.setTextSize(1);
  const char * story1 = "Your screen should be";
  const char * story2 = "in Portait mode, where";
  const char * story3 = "the USB-end is the top.";
  const char * story4 = "And this text should be";
  const char * story5 = "centered, left to right,";
  const char * story6 = "and top to bottom, with";
  const char * story7 = "a frame around it.";
  const char * story8 = "If so, press the screen,";
  const char * story9 = "if not, press and hold";
  const char * story10 = "for 3 seconds";
  tft.setCursor((TFT_X_RANGE/2) - strlen(story1)*3,((TFT_Y_RANGE/2)-54));
  tft.print(story1);
  tft.setCursor((TFT_X_RANGE/2) - strlen(story2)*3,((TFT_Y_RANGE/2)-42));
  tft.print(story2);
  tft.setCursor((TFT_X_RANGE/2) - strlen(story3)*3,((TFT_Y_RANGE/2)-30));
  tft.print(story3);
  tft.setCursor((TFT_X_RANGE/2) - strlen(story4)*3,((TFT_Y_RANGE/2)-18));
  tft.print(story4);
  tft.setCursor((TFT_X_RANGE/2) - strlen(story5)*3,((TFT_Y_RANGE/2)-6));
  tft.print(story5); 
  tft.setCursor((TFT_X_RANGE/2) - strlen(story6)*3,((TFT_Y_RANGE/2)+6));
  tft.print(story6);  
  tft.setCursor((TFT_X_RANGE/2) - strlen(story7)*3,((TFT_Y_RANGE/2)+18));
  tft.print(story7);
  tft.setCursor((TFT_X_RANGE/2) - strlen(story8)*3,((TFT_Y_RANGE/2)+30));
  tft.print(story8);
  tft.setCursor((TFT_X_RANGE/2) - strlen(story9)*3,((TFT_Y_RANGE/2)+42));
  tft.print(story9);
  tft.setCursor((TFT_X_RANGE/2) - strlen(story10)*3,((TFT_Y_RANGE/2)+54));
  tft.print(story10);
  tft.drawRect(2,2,TFT_X_RANGE-4,TFT_Y_RANGE-4,BLACK);
  WaitReleaseReadPress(true);
  uint32_t moment=millis();
  while (!ConfirmRelease()) if ((millis()-moment)>3000) return false;
  return true;
}

void Change_TFTXY_Settings() {
  tft.fillScreen(RED);
  delay(3000);
}

bool ConfirmRelease() {
  uint8_t prs=0;
  while (prs<RELEASECYCLES) {
    tp = ts.getPoint();
    ResetPins();
    if (tp.z > MINPRESSURE && tp.z < MAXPRESSURE) return false ;
    delay(1);
    prs++;
  }
  return true;
}

void WaitReleaseReadPress(bool waitrelease) {
  if (waitrelease) {
    while (!ConfirmRelease());
  }
  while (tp.z < MINPRESSURE || tp.z > MAXPRESSURE) {
    tp = ts.getPoint();
    ResetPins();
  }
  delay (100);
}

void ResetPins(){
  pinMode(XM, OUTPUT);  // reset the pins after use.
  pinMode(YP, OUTPUT);
  pinMode(XP, OUTPUT);
  pinMode(YM, OUTPUT);
}