xpt2046 touchscreen control with the ILI9341 TFT & Arduino

Post here first, or if you can't find a relevant section!
Post Reply
g0mgx
Posts: 9
Joined: Fri Jan 10, 2020 1:41 pm

xpt2046 touchscreen control with the ILI9341 TFT & Arduino

Post by g0mgx »

Hi Guys

I'm trying to use the STM32 "blue pill" board with a TFT screen with touch controlled by XPT2046. Its a fairly standard looking 2.8" module available in the usual places.

I've got the TFT connected to SPI1 and the touch to SPI2 - I am trying to follow the information here:

https://github.com/PaulStoffregen/XPT2046_Touchscreen

The code gets so far and then seems to halt - I've added comments over the working and non working point in the code - I'm a bit stuck now! I've filled the code with Serial1.print statements to debug and here's the output of Serial1:

Image

I'm not "looping" in my main loop and either:
  • The SPI has dissapeared up its backside
  • I've screwed up the Serial comms somehow
  • Something else I cant think of is happening
If anyone would be kind enough to have a look at this and offer ideas over whats happening. Needless to say it doesnt work, but any pointers more than appreciated! I'm stumped as to why the code seems to halt.

Mark
G0MGX

Code: Select all

#include "SPI.h"
#include ".\Adafruit_GFX_AS.h"
#include ".\Adafruit_ILI9341_STM.h"
#include <XPT2046_Touchscreen.h>

#define TFT_CS     PA0                  
#define TFT_DC     PA2                
#define TFT_RST    PA1

// touch screen is on SPI2

#define T_CS       PA8
#define TIRQ_PIN   PA15

Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // Use hardware SPI

XPT2046_Touchscreen ts(T_CS, TIRQ_PIN);

int nFrames = 100;    // higher number, slower full-cycle annimation

void setup() {
  Serial1.begin(9600);
  Serial1.println("Starting");

  Serial1.println("Start TFT Gubbins");  
  tft.begin();
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_RED);
  tft.setCursor(120, 50);
  tft.setTextSize(5);
  tft.print("Test");
  tft.setCursor(80, 120);
  tft.print("Touch");
  Serial1.println("End TFT Gubbins");
  
  delay(2000);

  Serial1.println("Start Clear TFT");
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_RED);
  Serial1.println("End Clear TFT");

  // any call to ts breaks the SPI interface. Is ts on SPI2?
  Serial1.println("Start ts initialise");
  ts.begin();
  ts.setRotation(1);
  Serial1.println("End ts initialise");


}

boolean wastouched = true;

void loop() 
{
  Serial1.println("Loop Start");
 
  boolean istouched;
  TS_Point p;
  
  istouched = ts.touched();

  Serial1.println("After ps.touched call");
  Serial1.print("istouched is ");
  Serial1.println(istouched);

  
  if (istouched) 
  {
    Serial1.println("inside if(istouched)");
    p = ts.getPoint();
    Serial1.println("after getPoint call");
    Serial1.print("wastouched is ");
    Serial1.println(wastouched);   
    if (!wastouched) 
    {
      Serial1.println("Inside if (!wastouched)");
      Serial1.println("printing touch");
      tft.fillScreen(ILI9341_BLACK);
      tft.setTextColor(ILI9341_YELLOW);
      tft.setCursor(60, 80);
      tft.print("Touch");
    }
    Serial1.println("After if (!wastouched)");

    tft.setTextColor(ILI9341_GREEN);
    Serial1.println("After green text");
    tft.setTextSize(5);
    Serial1.println("After text size");
    tft.setCursor(100, 150);
    Serial1.println("After set cursor");  // this happens
    tft.print("x = ");
    Serial1.println("After print x=");    // this doesnt happen
    tft.print(p.x);
    tft.setCursor(100, 180);
    tft.print("y = ");
    tft.print(p.y);
    Serial1.print(", x = ");
    Serial1.print(p.x);
    Serial1.print(", y = ");
    Serial1.println(p.y);
  } 
  else 
  {
    if (wastouched) 
    {
      Serial1.println("printing no touch");
      tft.fillScreen(ILI9341_BLACK);
      tft.setTextColor(ILI9341_RED);
      tft.setCursor(120, 50);
      tft.print("No");
      tft.setCursor(80, 120);
      tft.print("Touch");
    }
  }
  wastouched = istouched;
  delay(100);
}
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: xpt2046 touchscreen control with the ILI9341 TFT & Arduino

Post by stevestrong »

I assume you are using the libmaple (Roger's) core.
Haven't you got some warnings during compilation/build?
What do you see on the TFT display?

I do not see any code section which would differentiate the SPI port used by XP chip from that of TFT.
Did you hardcode it in the XP library? If not, you should.
Otherwise the commands to TFT and XP will be both directed to SPI1, thus to TFT.

Or, you should call

Code: Select all

SPI.setModule(1);
before each group of functions which relate to TFT, and call

Code: Select all

SPI.setModule(2);
before each group of functions which relate to XP.
g0mgx
Posts: 9
Joined: Fri Jan 10, 2020 1:41 pm

Re: xpt2046 touchscreen control with the ILI9341 TFT & Arduino

Post by g0mgx »

Thanks for the input; the support files for the touchscreen library state that it is for SPI2 - but I am not convinced.

I've tried your suggestions but didnt get very far.

I then tried another touch library which you initialise with an SPI instance whereby you specify SPI2 - it's called Serasidis_XPT2046_touch and is in the STM32-master repository.

Code: Select all

SPIClass mySPI(2); //Create an SPI instance on SPI2 port.
XPT2046_touch ts(CS_PIN, mySPI); // Chip Select pin, SPI port
It doesnt use interrupts but seems to work fine. I've managed to get the software doing what I want (touch screen band selection for a ham radio RF amplifier). The TFT is on SPI1 and the Touch on SPI2 and its all working now.

I found that the touchscreen x,y coordinates are not the same as the TFT x,y so I have had to do some jiggery pokery to map the touch onto the graphics - but its all OK now. I have concerns my code is very inefficient, so I'll stick it down below if anyone would be kind enough to comment.

I'm new to STM32 and a struggling a little understanding what is and isnt compatible with the board.

Thanks again

Mark
G0MGX

Code: Select all

#include "SPI.h"
#include ".\Adafruit_GFX_AS.h"
#include ".\Adafruit_ILI9341_STM.h"
#include "XPT2046_touch.h"

// true for serial3 debug comms
const boolean debug = true;

// TFT Pins plus SPI1
const int TFT_CS =    PA0;                  
const int TFT_DC =    PA2;                
const int TFT_RST =   PA1;

// define the band switch pins
const int Band160M = PA9;
const int Band80M = PA10;
const int Band60M = PA13;
const int Band40M = PA14;
const int Band30M = PA15;
const int Band20M = PB3;
const int Band17M = PB4;
const int Band15M = PB5;
const int Band12M = PB6;
const int Band10M = PB7;
const int Band6M = PB8;
const int Band4M = PB9;

// define the analoge inputs for
// the forward and reflected
// power readings
const int ForwardPwr = PB0;
const int ReflectedPwr = PB1;

// touch screen is on SPI2
const int CS_PIN =    PA8;
const int KeyPadStarty = 110;
const int ButtonDepth = 53;
const int ButtonWidth = 50;

// global variables (always a bad idea)
boolean wastouched = true;
uint16_t xy[2];
int BandSelection = 20; // default band selection is 20M
int OldBandSelection = 160;

float Forward_Watts = 0.0;
float Reflected_Watts = 0.0;
float Calculated_SWR = 1.0;  

Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // Use hardware SPI

SPIClass mySPI(2); //Create an SPI instance on SPI2 port.
XPT2046_touch ts(CS_PIN, mySPI); // Chip Select pin, SPI port

void DisplayScreen()
{

  int BandList[12] = {160, 80, 60, 40, 30, 20, 17, 15, 12, 10, 6, 4};
  tft.setRotation(1);
  tft.fillScreen(ILI9341_BLACK);
  // fill rect startx, starty, width, depth, colour
  tft.fillRect(0, KeyPadStarty, 320, 140, ILI9341_RED);
  tft.setCursor(20, 10);
  tft.setTextColor(ILI9341_RED);    
  tft.setTextSize(4);
  tft.print("G0MGX LINEAR");
  tft.setTextColor(ILI9341_GREEN);  
  tft.setCursor(35, 50);
  tft.setTextSize(3);
  tft.print("FWD:   REF:");
  tft.setCursor(110, 80);
  tft.setTextSize (2);
  tft.setTextColor(ILI9341_YELLOW);
  tft.print("SWR:");

  for (int Bands = 0; Bands <= 11; Bands++)
  {
    DisplayButton(ILI9341_WHITE, BandList[Bands], ILI9341_BLACK);
  } 
}

void DisplayButton(int textcolour, int band, int boxcolour)
{
  int textxcoord; int textycoord; int boxxcoord; int boxycoord;
  switch (band)
  {
    case 160:
      textxcoord = 10;
      textycoord = 140;
      boxxcoord = 3;
      boxycoord = KeyPadStarty+10;
      break;
    case 80:
      textxcoord = 70;
      textycoord = 140;
      boxxcoord = 56;
      boxycoord = KeyPadStarty+10;
      break; 
    case 60:
      textxcoord = 123;
      textycoord = 140;
      boxxcoord = 109;
      boxycoord = KeyPadStarty+10;
      break; 
    case 40:
      textxcoord = 175;
      textycoord = 140;
      boxxcoord = 162;
      boxycoord = KeyPadStarty+10;
      break; 
    case 30:
      textxcoord = 230;
      textycoord = 140;
      boxxcoord = 215;
      boxycoord = KeyPadStarty+10;
      break; 
    case 20:
      textxcoord = 280;
      textycoord = 140;
      boxxcoord = 268;
      boxycoord = KeyPadStarty+10;
      break; 
    case 17:
      textxcoord = 15;
      textycoord = 195;
      boxxcoord = 3;
      boxycoord = KeyPadStarty+66;
      break;
    case 15:
      textxcoord = 70;
      textycoord = 195;
      boxxcoord = 56;
      boxycoord = KeyPadStarty+66;
      break; 
    case 12:
      textxcoord = 123;
      textycoord = 195;
      boxxcoord = 109;
      boxycoord = KeyPadStarty+66;
      break; 
    case 10:
      textxcoord = 175;
      textycoord = 195;
      boxxcoord = 162;
      boxycoord = KeyPadStarty+66;
      break; 
    case 6:
      textxcoord = 235;
      textycoord = 195;
      boxxcoord = 215;
      boxycoord = KeyPadStarty+66;
      break; 
    case 4:
      textxcoord = 287;
      textycoord = 195;
      boxxcoord = 268;
      boxycoord = KeyPadStarty+66;
      break; 
    default:
      break;
  }  
  tft.fillRect(boxxcoord, boxycoord, ButtonWidth, ButtonDepth, boxcolour); 
  tft.setTextColor(textcolour);  
  tft.setTextSize(2); 
  tft.setCursor(textxcoord, textycoord);
  tft.print(band); 

}  

void setup() 
{
  if (debug)
  {
    Serial3.begin(9600);
    Serial3.println("Starting");
  }
  
  tft.begin();
  tft.fillScreen(ILI9341_BLACK);
  
  ts.begin(); //Begin TouchScreen.

  // initialise digital outputs
  pinMode (Band160M, OUTPUT);
  digitalWrite (Band160M, LOW);
  pinMode (Band80M, OUTPUT);
  digitalWrite (Band80M, LOW);
  pinMode (Band60M, OUTPUT);
  digitalWrite (Band60M, LOW);
  pinMode (Band40M, OUTPUT);
  digitalWrite (Band40M, LOW);
  pinMode (Band30M, OUTPUT);
  digitalWrite (Band30M, LOW);
  pinMode (Band20M, OUTPUT);
  digitalWrite (Band20M, LOW);
  pinMode (Band17M, OUTPUT);
  digitalWrite (Band17M, LOW);
  pinMode (Band15M, OUTPUT);
  digitalWrite (Band15M, LOW);
  pinMode (Band12M, OUTPUT);
  digitalWrite (Band12M, LOW);
  pinMode (Band10M, OUTPUT);
  digitalWrite (Band10M, LOW);
  pinMode (Band6M, OUTPUT);
  digitalWrite (Band6M, LOW);
  pinMode (Band4M, OUTPUT);
  digitalWrite (Band4M, LOW);

  // Declare the inputs as INPUT_ANALOG:
  pinMode(ForwardPwr, INPUT_ANALOG);
  pinMode(ReflectedPwr, INPUT_ANALOG);
}

void ReadADCValues()
{
  int ForwardPower;
  int ReflectedPower;
  double Forward_dBm;
  double Reflected_dBm;
  
  float Pref_over_Pfwd = 0.0;
  float Root_Pref_over_Pfwd = 0.0;
  
  const float Forward_Alpha = 0.0948; 
  const float Forward_Beta = -37.658;
  const float Reflected_Alpha = 0.0931;
  const float Reflected_Beta = -35.413;
  
  ForwardPower = analogRead(ForwardPwr);
  ReflectedPower = analogRead(ReflectedPwr);



  // We use the constants defined before to solve the equation 
  // dbM = Alpha ADC + Beta (where ADC = ADC value and Alpha and Beta are constants)
  // We originally determine the values of Alpha and Beta from the spreadsheet

  // First lets do the Forward Power Channel

  Forward_dBm = ( ForwardPower * Forward_Alpha ) + Forward_Beta;
  Forward_Watts = (pow(10,(Forward_dBm/10)) / 1000);

  // Now let's do it again but for the reflected power values

  Reflected_dBm = ( ReflectedPower * Reflected_Alpha ) + Reflected_Beta;
  Reflected_Watts = (pow(10,(Reflected_dBm/10)) / 1000);

  if (Reflected_Watts < 0.01) // if the reflected power is small dont bother calculating the SWR
  {
    Pref_over_Pfwd = 0.0;
    Calculated_SWR = 1.0;
  }
  else
  {
    Pref_over_Pfwd = Reflected_Watts / Forward_Watts;
    Root_Pref_over_Pfwd = sqrt(Pref_over_Pfwd);
    Calculated_SWR = ((1 + Root_Pref_over_Pfwd) / ( 1 - Root_Pref_over_Pfwd));
  }
}

void SetLPF (int BandSelection)
{
  int OutputPins[12] = {Band160M, Band80M, Band60M, Band40M, Band30M, Band20M, Band17M, Band15M, Band12M, Band10M, Band6M, Band4M};
  int OutputValues[12] = {0,0,0,0,0,0,0,0,0,0,0,0};

  switch (BandSelection)
  {
    case 160:
      OutputValues[0] = 1;
      break;
    case 80:
      OutputValues[1] = 1;
      break;
    case 60:
      OutputValues[2] = 1;
      break;
    case 40:
      OutputValues[3] = 1;
      break;
    case 30:
      OutputValues[4] = 1;
      break;  
    case 20:
      OutputValues[5] = 1;
      break; 
    case 17:
      OutputValues[6] = 1;
      break;  
    case 15:
      OutputValues[7] = 1;
      break;  
    case 12:
      OutputValues[8] = 1;
      break;  
    case 10:
      OutputValues[9] = 1;
      break;  
    case 6:
      OutputValues[10] = 1;
      break;  
    case 4:
      OutputValues[11] = 1;
      break;  
  }
  for (int writeOutputs = 0; writeOutputs <=11; writeOutputs++)
  {
    if (debug)
    {
      Serial3.print("Writing to Pin: ");
      Serial3.print(OutputPins[writeOutputs]);
      Serial3.print(" value ");
      Serial3.println(OutputValues[writeOutputs]);
    }
    digitalWrite(OutputPins[writeOutputs],OutputValues[writeOutputs]);
  }
}

void CheckTouchScreen()
{

  int TFTWidth = 320;
  int TFTDepth = 240;
  // the touch x and y coordinates are on a different
  // scale to the TFT 
  // so we need to convert between them

  // these are my cal factors and are the left most x coordinate and top most y
  int CalFactorx = 258;
  int TouchScreenWidth = 3650 - CalFactorx;
  int CalFactory = 426;
  int TouchScreenDepth = 3740 - CalFactory;
  
  int XFactor = TouchScreenWidth / TFTWidth;
  int YFactor = TouchScreenDepth / TFTDepth;
  
  int TouchButtonWidth = ButtonWidth * XFactor;
  int TouchButtonDepth = ButtonDepth * YFactor;
  
  // define my box x y start coords
  // the TFT is 320 accross and 250 deep
  // these numbers are relative to that baseline
  // row 1
  int Box160M[2] = {3 * XFactor + CalFactorx, 120 * YFactor + CalFactory};
  int Box80M[2] = {56 * XFactor + CalFactorx, 120 * YFactor + CalFactory};
  int Box60M[2] = {109 * XFactor + CalFactorx, 120 * YFactor + CalFactory};
  int Box40M[2] = {162 * XFactor + CalFactorx, 120 * YFactor + CalFactory};
  int Box30M[2] = {215 * XFactor + CalFactorx, 120 * YFactor + CalFactory};
  int Box20M[2] = {268 * XFactor + CalFactorx, 120 * YFactor + CalFactory};
  // row 2
  int Box17M[2] = {3 * XFactor + CalFactorx, 176 * YFactor + CalFactory};
  int Box15M[2] = {56 * XFactor + CalFactorx, 176 * YFactor + CalFactory};
  int Box12M[2] = {109 * XFactor + CalFactorx, 176 * YFactor + CalFactory};
  int Box10M[2] = {162 * XFactor + CalFactorx, 176 * YFactor + CalFactory};
  int Box6M[2] = {215 * XFactor + CalFactorx, 176 * YFactor + CalFactory};
  int Box4M[2] = {268 * XFactor + CalFactorx, 176 * YFactor + CalFactory};
  
  static uint16_t xy[2];
  boolean istouched = ts.read_XY(xy);
  
  if (istouched) 
  {
    // this is a crude debounce to only act once per touch
    if (!wastouched) 
    {
      // determine if the x-coordinate is inside a button
      // this feels very messy and there must be a better way....
      
      if ((xy[0] >= Box160M[0]) && (xy[0] <= Box160M[0] + TouchButtonWidth) && 
          (xy[1] >= Box160M[1]) && (xy[1] <= Box160M[1] + TouchButtonDepth))
          {
            BandSelection = 160;
          }
       else if ((xy[0] >= Box80M[0]) && (xy[0] <= Box80M[0] + TouchButtonWidth) && 
          (xy[1] >= Box80M[1]) && (xy[1] <= Box80M[1] + TouchButtonDepth))
          {
            BandSelection = 80;
          }
       else if ((xy[0] >= Box60M[0]) && (xy[0] <= Box60M[0] + TouchButtonWidth) && 
          (xy[1] >= Box60M[1]) && (xy[1] <= Box60M[1] + TouchButtonDepth))
          {
            BandSelection = 60;
          }
       else if ((xy[0] >= Box40M[0]) && (xy[0] <= Box40M[0] + TouchButtonWidth) && 
          (xy[1] >= Box40M[1]) && (xy[1] <= Box40M[1] + TouchButtonDepth))
          {
            BandSelection = 40;
          }
       else if ((xy[0] >= Box30M[0]) && (xy[0] <= Box30M[0] + TouchButtonWidth) && 
          (xy[1] >= Box30M[1]) && (xy[1] <= Box30M[1] + TouchButtonDepth))
          {
            BandSelection = 30;
          }
       else if ((xy[0] >= Box20M[0]) && (xy[0] <= Box20M[0] + TouchButtonWidth) && 
          (xy[1] >= Box20M[1]) && (xy[1] <= Box20M[1] + TouchButtonDepth))
          {
            BandSelection = 20;
          }
       else if ((xy[0] >= Box17M[0]) && (xy[0] <= Box17M[0] + TouchButtonWidth) && 
          (xy[1] >= Box17M[1]) && (xy[1] <= Box17M[1] + TouchButtonDepth))
          {
            BandSelection = 17;
          } 
       else if ((xy[0] >= Box15M[0]) && (xy[0] <= Box15M[0] + TouchButtonWidth) && 
          (xy[1] >= Box15M[1]) && (xy[1] <= Box15M[1] + TouchButtonDepth))
          {
            BandSelection = 15;
          }
       else if ((xy[0] >= Box12M[0]) && (xy[0] <= Box12M[0] + TouchButtonWidth) && 
          (xy[1] >= Box12M[1]) && (xy[1] <= Box12M[1] + TouchButtonDepth))
          {
            BandSelection = 12;
          }
       else if ((xy[0] >= Box10M[0]) && (xy[0] <= Box10M[0] + TouchButtonWidth) && 
          (xy[1] >= Box10M[1]) && (xy[1] <= Box10M[1] + TouchButtonDepth))
          {
            BandSelection = 10;
          }
       else if ((xy[0] >= Box6M[0]) && (xy[0] <= Box6M[0] + TouchButtonWidth) && 
          (xy[1] >= Box6M[1]) && (xy[1] <= Box6M[1] + TouchButtonDepth))
          {
            BandSelection = 6;
          }
       else if ((xy[0] >= Box4M[0]) && (xy[0] <= Box4M[0] + TouchButtonWidth) && 
          (xy[1] >= Box4M[1]) && (xy[1] <= Box4M[1] + TouchButtonDepth))
          {
            BandSelection = 4;
          }
    }

  } 
  // if the band selection has changed act accordingly
  if (OldBandSelection != BandSelection)
  {
    DisplayButton(ILI9341_RED, BandSelection, ILI9341_WHITE);
    DisplayButton(ILI9341_WHITE, OldBandSelection, ILI9341_BLACK);
    SetLPF(BandSelection);
    OldBandSelection = BandSelection;
    // as we dont want to keep switching lets hold the boat a while here
    delay (500);
  }
  wastouched = istouched;
}

void UpdatePowerDisplay()
{
  int Forward_Watts_Int;
  int Reflected_Watts_Int;
  // this is the SWR reading
  tft.fillRect(160, 80, 60, 20, ILI9341_BLACK); 
  tft.setCursor(160, 80);
  tft.setTextSize (2);
  tft.setTextColor(ILI9341_YELLOW);
  if ((Calculated_SWR > 5.0) || (Calculated_SWR < 0.0))
    tft.print("Huge");
  else
    tft.print(Calculated_SWR,2);

  // this is the power
  tft.fillRect(110, 50, 50, 22, ILI9341_BLACK); 
  tft.setCursor(110, 50);
  tft.setTextSize(3);
  Forward_Watts_Int = round(Forward_Watts);
  if (Forward_Watts_Int > 10)
    tft.print("10");
  else
    tft.print(Forward_Watts_Int);  

  tft.fillRect(240, 50, 50, 22, ILI9341_BLACK); 
  tft.setCursor(240, 50);
  tft.setTextSize(3);
  Reflected_Watts_Int = round(Reflected_Watts);
  if (Reflected_Watts_Int > 10)
    tft.print("10");
  else
    tft.print(Reflected_Watts_Int);  
}

void MaintainPowerDisplay()
{
  static const unsigned long TimeInterval = 1000; // ms
  static unsigned long lastRefreshTime = 0;
  
  if(millis() - lastRefreshTime >= TimeInterval)
  {
    lastRefreshTime += TimeInterval;
    UpdatePowerDisplay();
  }
  
}

void loop() 
{
  boolean TimetoUpdate = false;
  // we need to setup the screen once
  DisplayScreen();
  while (true)
  {
    // then loop forever reading the power, checking the touch screen and
    // acting accordingly 
    ReadADCValues();
    CheckTouchScreen();
    delay(100);
    MaintainPowerDisplay();

  }
}
Post Reply

Return to “General discussion”