ILI9341 and XPT2046 for Teensy Touchscreen 320x240 display

Status
Not open for further replies.
First off, thank you for following up. You're correct there is no evidence of any physical damage but I've been in electronics long enough to know ICs don't show damage often. Anyway, Wiring follows the documentation at PJRC site. VCC red, GND blk, CS TCS yel, RESET brn, DC org, SDI T_DIN grn, SCK pur, T_CLK grey, LED red, SDO T_DO blu, T_IRG org


IMG_0678.jpg

I hope you see my error.
 
OK - Guess no one found an error. I discovered that the touch does work correctly after all but just not for Teensy 3.6. Soon as I put it on a Teensy 3.2 all example sketches starting working with problems. I am not sure why this is so but on person told me the CS pin pulse is so fast and short on the 3.6 he had to add a 47p cap to the pin to slow it down enough to work. I did not try this but it might offer why the 3.6 does not work while the 3.2 does.

So I offer my findings and will stop banging my head against the wall with the 3.6! If someone has a code fix for the Teensy or other ideas to make it work I'd love to hear them.
 
Mirrored X/Y Axis issue?

Hi Everyone!

I'm trying to build a "simple" encoder based measuring tool using a the IL9341 touch screen I bought from Paul.. I noticed his nifty breakout/test board, hopped over to OSH Park and ordered a set because they were just the thing I needed! Hooked it all up, added IR9341_t3 libraries to Arduino 1.8.7 and ran a few tests - all seems to work, happy days..

Next step, plagiarize a smarter person's work to hack up a few simple touchscreen buttons.. (I'm building a G-Scale railroad car with speed and distance measuring capabilities.. Well, I'm going to TRY.. :p ) Anyway, a little googling around lead me to defragster's awesome examples base for the very work I want.. A modest TFT screen to display some formatted data from a capacitive encoder and a touch screen to implement a few buttons to calibrate, reset data and set units and such..

But when I upload the code examples, they don't seem to work exactly as expected, and the issue SEEMS to be a mirroring of both X and Y axis the touch screen uses...

IE:, when the "ROT" button is in the upper left corner, I have to touch the lower right corner to activate a rotation.. On a narrower scale, I can see the same effect on buttons in the center.. This is more pronounced on screens where the buttons are even number x even number, but it remains consistent at all times.

If I touch "Rot", the serial console spits out:
Code:
button #20
button #20
button #20

Okay, I pressed it THREE times there..

It occurred to me as I wrote this that I said "everything works", but that doesn't mean I understand the correct value of "working"!! So, the photo shows the touch coordinates as generated by the "IL9341Test" example.. Is the "upper right" supposed to be the higher numbers and lower left the lowest numbers? I don't know if that's "correct" orientation or not.. The product photo is sort of conviently in the middle, so that's hard to use as a gauge..

orientation.JPG

My display is version 1.2, the Teensy is a 3.2, the display TFT Display test board does not appear to have a revision. The code base in question is ColorButtonsMark3 pulled from github.

I'll be upfront - my C-foo is weak at best.. I can see a few rotation key words, but I don't recognize anything that would appear to address my situation..

So - did I miss something in the RTFM part that would fix this? I'm excited to see what I can come up with using this example base, but I pretty sure I need to understand why it's not working for me first..

Thanks for any input!!

Sincerely,

-ET-
 
Last edited:
The product photo is sort of conviently in the middle, so that's hard to use as a gauge.

I so lie.. The product page INDEED shows that my numbers are backwards.. Paul's finger is at the lower left.

display_ili9341_touch.jpg


My values for the same position are backwards?

wrong.JPG

Okay, I answer one of my own questions - can someone suggest why I'm seeing reverse values?

Thanks so much!!
 
The numbers need to be changed with screen rotation. I did a cool map function that can move to any orientation. It looks like your screen is the same orientation there.

Is this the 'ColorButtonsMark3' code used: Defragster/XPT2046_Touch_Examples … ColorButtonsMark3/ButtonMap.cpp#L31

That link goes to the map with rotation I did, it needs to be oriented with the screen X,Y to map properly.

Ah! I see!

Code:
//TS_MAP tsMap[5] = { {200, 3700, 325, 3670 }, { 0, 319, 0, 239 }, { 319, 0, 0, 239 }, { 319, 0, 239, 0 }, { 0, 319, 239, 0 } };
// wrong orientation for my screen!!
TS_MAP tsMap[5] = { {3700, 200, 3670, 325 }, { 0, 319, 0, 239 }, { 319, 0, 0, 239 }, { 319, 0, 239, 0 }, { 0, 319, 239, 0 } };

...makes your example code run as expected on my screen! On to more hacking of examples!

Thanks so much for your prompt response!!

-ET-
 
Ah! I see!

Code:
//TS_MAP tsMap[5] = { {200, 3700, 325, 3670 }, { 0, 319, 0, 239 }, { 319, 0, 0, 239 }, { 319, 0, 239, 0 }, { 0, 319, 239, 0 } };
// wrong orientation for my screen!!
TS_MAP tsMap[5] = { {3700, 200, 3670, 325 }, { 0, 319, 0, 239 }, { 319, 0, 0, 239 }, { 319, 0, 239, 0 }, { 0, 319, 239, 0 } };

...makes your example code run as expected on my screen! On to more hacking of examples!

Thanks so much for your prompt response!!

-ET-

@mtrcycllvr , hope you are still having Teensy fun - glad I saw your post quickly and you used it to work!

Just looking back at this code myself and looking closer at this. The goal of the MAP array was to leave the first set of screen Min/Max values unchanged and then index the calculation from there 1,2,3,4 based on screen rotation.

The swap you did should be the same as setting screen rotation to [3] = { 319, 0, 239, 0 } - that results in the swap of the direction.

This should be the same if in the source a change of :: int16_t TS_Rotate = 1; to int16_t TS_Rotate = 3;

Or at RunTime in setup:: ts.ButtonRotate( 3 );

The goal was allowing the user to rotate the screen and touch with it one or more times after startup without code change.
 
@mtrcycllvr , hope you are still having Teensy fun - glad I saw your post quickly and you used it to work!

Just looking back at this code myself and looking closer at this. The goal of the MAP array was to leave the first set of screen Min/Max values unchanged and then index the calculation from there 1,2,3,4 based on screen rotation.

The swap you did should be the same as setting screen rotation to [3] = { 319, 0, 239, 0 } - that results in the swap of the direction.

This should be the same if in the source a change of :: int16_t TS_Rotate = 1; to int16_t TS_Rotate = 3;

Or at RunTime in setup:: ts.ButtonRotate( 3 );

The goal was allowing the user to rotate the screen and touch with it one or more times after startup without code change.

Hey, thanks for the reply and the further distillation! Yeah, see that's the problem, my c-foo is so weak, I couldn't see that, which, well, read on... And I TOTALLY get that your trying to solve the very problem I'm currently facing...

I'm sorry, I did build a thing, and no, I did not build it from your model.. Of course, now there are RFC's that actually make sense, and I'm kicking myself for not just buckling down and figuring out how to meld your library with my desires in the first place..

IMG_3275.JPG

So, in the next few days I'm going to take another stab at using your library for a 'version' of my tool that has the sort of flexibility it really could use..

Your clarification fits nicely with my recent notice of the sticker on the most recent display that suggested pairs of library "orientation" values that had previously escaped my notice..

Ultimately it's all about me and my general lack of c++-foo.. ;-)

I'll be sure to poke back here if I get in trouble - honestly, my goal is pretty simple:

decode/track encoder data
display data in user selectable scales in a few "formats"
landscape/portrait mode for said display

I just got intimidated as I didn't immediately understand much of your code and "thought" my requirements were so simple, they'd NEVER change, right? ;-)

https://github.com/mtrcycllvr/g-scale_train_distance_car

Again, thanks.. You may hear more lame questions from me shortly!! The latest circuit boards (not up on github yet) are out for fab, so updated FW is the next big step!

-ET-
 
Glad it is of some value to you - hopefully it will teach you something good. That looks like a very nice build!

I'm not sure that code is the best example of anything - but it was working as I left it, and when I run it again now … only now looking at it again to integrate the map_code() to suggest inclusion in the lib because that same 2046 touch controller showed up on a new 320x480 display - except it doesn't seem to act as nicely … and my first attempt to port my own code for that has put me off it for a week.
 
Hey, just a quick post to relate that I able to achieve my first goal - using abstracted button code to create a simple multi-screen menu system..

I wanted to reach out with a big "THANK YOU" to Defragster and all who helped put this code together!

I know that I saw at least one "how can I leverage this?" question that lead me to this post to begin with, so I thought I'd post my current project sketch so others can see how I adapted Defragster's button abstraction code to my relatively simple project and compare the two.. Again, my C-foo is weak, so no laughing at my work! ;-) Constructive criticism/suggestions always welcome, however..

The goal of the project is to made a G-Scale "data" car that will measure speed and distance in both selected scales ("G-Guage", Garden Gauge, represents five possible scales!) and real world (1:1) scale... There are lots of examples of "a magnet or two on the wheel" approach to this, but when I started considering the SPEED aspect of this it became clear that more data would be required. Anyway, I wanted to share this project with other old train farts like me, which made me think this would be a good time to try integrating a touch screen into my project.

That opened up a whole new can of worms... So, I actually found this thread, got started, but my c-foo failed me and I backed out to creating my own static button code for version 1. That worked great.. For version 1. Of course then came the RFC's, and I found myself wishing I'd spent more time trying to grok Defragster's button abstraction code.. Which ultimately lead to a few nights of intense study and experimentation..

So here is my new start, version 2.x using ColorButtonsMark2.ino example provided.. I somehow could just NOT figure out how to get "Mark3" to correctly orient on my screen, and again, weak c-foo plays a role..

BUT, I am VERY happy with the resulting code functionality - it made creating and fine tuning a multi-screen menu system that features flexible rotation options a simple evenings worth of coding and is SOO much simpler than manipulating static button code over and over again!! Still a work in progress, but it's a WORKING work in progress, which is thrilling..

So.. THANK YOU!

-ET-

Code:
/*

Dialog only TEST derived from defragster button abstraction code..
https://github.com/Defragster/XPT2046_Touch_Examples 
Specifically, this is an adapted version of ColorButtonsMark2.ino
(Thanks, Defragster!)

Attempt number one - can I just implement an easy menu system
based on this abstraction code..  

Interweaving my own data collection from the encoder is easy-pie if
I can get a handle on button management..

Eric Timberlake, March 25th, 2019..
*/

float VERSION = 2.3;

/*
Version 2.0 -
  - Just get basic abstraction code setup, create buttons for all the screens
    trigger actions on touch..   Still some work to debounce and de-double hit
    (button on screen 1 overlays button on screen 2) is a problem...
    Eric Timberlake, 3.25.2019

  - Next: wrap head around screen rotation and preserving rotation state..
          an "info" or "about" screen, perhaps an easter egg in the config
          screen?

Version 2.1 -
  - something.....  Got Rotation and basic menu working, got rotation to
    store persistently, but THEN....
  - Dog gone it - SOMETHING effed up - this will not compile any more - I think
    I was tired and touched/altered something, but for the life of me, can't 
    find out why!!  Claims "ButtonInit" is previously delcared variable or field.
    I sure can't see how or why..   Very frustrating to have a working outline just 
    stop working..

Version 2.2 - 
  - *(^) if I know - copy entire file - fails.  Cut and paste in pieces back into
    original code example and it works fine, just like it was..  WTF?  What follows
    is that reconstruction with NOT A CLUE IN THE WORLD what went wrong..  Obviously
    I created an unclosed brace or something of the sort somewhere..
  - So, we have basic rotation, but not all the required portrait screens and 
    button deffinitions..  We need to set a decision tree on when to be using 
    landscape vs. poitrait versions.
  - I need to figure a way to stop button re-trigger?
  DONE!  Well, done-ish for now..  I'll investigate superior button re-trigger
  detection later.  For now, time to start designing data output screens!

Version 2.3 -
  - start working on calibration routine and various displays? Try actually attaching
    the encoder?  ;-)

*/

#include <SPI.h>
#include <Wire.h>

//#define LC 1
#if LC
#include "Adafruit_ILI9341.h"
#else
#include <ILI9341_t3.h>
#endif
#include <XPT2046_Touchscreen.h>

#define CS_PIN 8
//XPT2046_Touchscreen ts(CS_PIN);  // Param 2 - NULL - No interrupts and TeensyDuino version 1.26 libs
// Second PARAM on XPT2046_Touchscreen requires modified interrupt aware XPT2046_Touchscreen library
#define TIRQ_PIN  2
//XPT2046_Touchscreen ts(CS_PIN, 255);  // Param 2 - 255 - No interrupts
XPT2046_Touchscreen ts(CS_PIN, TIRQ_PIN);  // Param 2 - Touch IRQ Pin - interrupt enabled polling
// Second PARAM on XPT2046_Touchscreen requires modified interrupt aware XPT2046_Touchscreen library

#define TFT_CS 10
#define TFT_DC  9
// MOSI=11, MISO=12, SCK=13
#ifdef LC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
// ADAFRUIT MISSING COLORS
#define ILI9341_NAVY        0x000F      /*   0,   0, 128 */
#define ILI9341_DARKCYAN    0x03EF      /*   0, 128, 128 */
#define ILI9341_ORANGE      0xFD20      /* 255, 165,   0 */
#define ILI9341_PINK        0xF81F
#else
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
#endif

#include <font_Arial.h>                       // from ILI9341_t3, font
#include <font_CourierNew.h>                  // from ILI9341_t3, font
#include <font_CourierNewBold.h>              // from ILI9341_t3, font
#include <font_AwesomeF100.h>                 // from ILI9341_t3 library - symbols font..
#include <Encoder.h>                          // optimized encoder library to track motion easily!!
#include <Adafruit_FRAM_SPI.h>                // access tools for FRAM storage where we store "stuff"..

uint8_t FRAM_CS = 22;                         // use CS pin 22 for chip select / SS
Adafruit_FRAM_SPI fram = Adafruit_FRAM_SPI(); // use hardware SPI 
// a little FRAM address tracking...
uint16_t SAVEDODO = 0x00;                     //FRAM address for long odometer 0x00, 01 02 and 03
uint16_t SAVEDUNITS = 0x04;                   //FRAM addres for byte units     0x04
uint16_t SAVEDTRIP = 0x05;                    //FRAM address for long trip     0x05. 06 07 and 08
uint16_t SAVEDSCALE = 0x10;                   //FRAM address for long scale    0x10, 11 12 and 13
uint16_t ALTSCALE = 0x20;                     //FRAM address for byte scale array pointer 0x20
uint16_t TSROTATE = 0x30;                     //FRAM address for integer screen orientation, 0-3
uint16_t SERIALNM = 0x40;                     //FRAM address for a permanent serial number - we only
                                              // ever READ THIS a separate program sets it at prod time..


// -----------------------------------------
// --- Button Data Starts here
// -----------------------------------------
int16_t TS_Rotate = 1;  // INVALID until set on startup
static int16_t TS_iiSlide = -1;
#define TS_SLIDET 40  // Reject SLIDE buttons farther apart than this

struct TS_MAP {
  int16_t xt;
  int16_t xb;
  int16_t yl;
  int16_t yr;
};
// Zero is calibration for mapping to pixel, 1-4 follow screen rotation for mapping math

//TS_MAP tsMap[5] = { {200, 3700, 325, 3670 }, { 0, 319, 0, 239 }, { 319, 0, 0, 239 }, { 319, 0, 239, 0 }, { 0, 319, 239, 0 } };
// restore original TS_MAP and use "ts.ButtonRotate( 3 );" in void setup()
// re-restore - ts.ButtonRotate( 3 ); isn't working...
TS_MAP tsMap[5] = { {3700, 200, 3670, 325 }, { 0, 319, 0, 239 }, { 319, 0, 0, 239 }, { 319, 0, 239, 0 }, { 0, 319, 239, 0 } };
int16_t TS_xMax = 0; // Current Screen orientation x,y Max values
int16_t TS_yMax = 0;

struct TS_BUTTON {
  char *text; // Literal string put into FLASH on compile leaving a pointer that is user changeable
  int16_t tx, ty; // Top Left X,Y
  int16_t ww, hh; // Width, Height
  uint16_t fgc; // forground text/frame color
  uint16_t bgc; // background color button or empty frame
  uint8_t btype;  // for toggle/slider two bytes TYPE & info PAIRING number
  uint8_t info;  // for toggle/slider two bytes TYPE & info PAIRING number
  byte bState; // button state
  byte fontsz; // font size
  byte bId; // Button ID
  uint8_t TS_data; // Toggle FRAME stores state value here
};

#define TS_JITTER 250 // ms Threshold for Toggle/Slider re-activate
#define TS_FBUTN 0x01 // Frame Button
#define TS_RBUTN 0x03 // Rounded Button
#define TS_FRAME  0x10  // FRAME for two TOGGLE button areas
#define TS_TOGOFF 0x20  // Off Toggle
#define TS_TOGON  0x30
#define TS_ASLIDE 0x40 // Slide Button : A position
#define TS_BSLIDE 0x50 // Slide Button : B position
int TS_bCount = 0;  // Set at run time from sizeof()
// -----------------------------------------
// --- Button Data Ends here
// -----------------------------------------

// -----------------------------------------
// --- USER Button Data Starts here
//        { text *, tx, ty, ww, hh, fgc, bgc, btype, info, bState, fontsz, bId, TS_data }    << Struct looks like this
// REQUIRED LAST ITEM :: {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
// -----------------------------------------

uint16_t gobuttonclr=0x34C4;
uint16_t cancelclr=0xD165;
//unsigned long menuclr=0x5E23;
uint16_t menuclr=0xEFE0;
int16_t stdbuttonwidth=147;
int16_t stdLborder=7;

// main screen buttons...
TS_BUTTON mainscreen_l[] = {
  {(char *)"Reset", 10, 195, 85, 40, menuclr, 325, TS_FBUTN, 0, 0, 2, 1, 0} ,
  {(char *)" BIG", 105, 195, 85, 40, menuclr, 325, TS_FBUTN, 0, 0, 2, 2, 0} ,
  {(char *)"Config", 200, 195, 85, 40, menuclr, 325, TS_FBUTN, 0, 0, 2, 3, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};
TS_BUTTON mainscreen_p[] = {
  {(char *)"Reset", 10, 225, 85, 40, 65535, 325, TS_FBUTN, 0, 0, 2, 1, 0} ,
  {(char *)" BIG", 104, 225, 85, 40, 65535, 325, TS_FBUTN, 0, 0, 2, 2, 0} ,
  {(char *)"Config", 10, 273, 85, 40, 65535, 325, TS_FBUTN, 0, 0, 2, 3, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};

// configuration choices selection dialog buttons - just rough form for now
TS_BUTTON configscreen_l[] = {
  {(char *)"Rotation", stdLborder, 15, stdbuttonwidth, 35, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 4, 0} ,
  {(char *)"Calibration", stdLborder, 60, stdbuttonwidth, 35, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 5, 0} ,
  {(char *)"Scale", stdLborder, 105, stdbuttonwidth, 35, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 6, 0} ,
  {(char *)"Units", stdLborder, 150, stdbuttonwidth, 35, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 7, 0} ,
  {(char *)"CANCEL", stdLborder, 195, stdbuttonwidth, 35, cancelclr, 325, TS_FBUTN, 0, 0, 2, 8, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};
TS_BUTTON configscreen_p[] = {
  {(char *)"Rotation", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 4, 0} ,
  {(char *)"Calibration", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 5, 0} ,
  {(char *)"Scale", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 6, 0} ,
  {(char *)"Units", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 7, 0} ,
  {(char *)"CANCEL", stdLborder, 220, stdbuttonwidth, 40, cancelclr, 325, TS_FBUTN, 0, 0, 3, 8, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};

// scale selection dialog buttons - just rough form for now
TS_BUTTON scalescreen_l[] = {
  {(char *)"1:32 Scale", stdLborder, 5, stdbuttonwidth, 32, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 9, 0} ,
  {(char *)"1:29 Scale", stdLborder, 44, stdbuttonwidth, 32, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 10, 0} ,
  {(char *)"1:24 Scale", stdLborder, 83, stdbuttonwidth, 32, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 11, 0} ,
  {(char *)"1:22.5 Scale", stdLborder, 122, stdbuttonwidth, 32, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 12, 0} ,
  {(char *)"1:20.5 Scale", stdLborder, 161, stdbuttonwidth, 32, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 13, 0} ,
  {(char *)"CANCEL", stdLborder, 198, stdbuttonwidth, 32, cancelclr, 325, TS_FBUTN, 0, 0, 2, 14, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};
TS_BUTTON scalescreen_p[] = {
  {(char *)"1:32 Scale", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 9, 0} ,
  {(char *)"1:29 ", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 10, 0} ,
  {(char *)"1:24 ", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 11, 0} ,
  {(char *)"1:22.5 ", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 12, 0} ,
  {(char *)"1:20.5", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 13, 0} ,
  {(char *)"CANCEL", stdLborder, 220, stdbuttonwidth, 40, cancelclr, 325, TS_FBUTN, 0, 0, 2, 14, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};

// Units selection dialog buttons - just rough form for now
TS_BUTTON unitsscreen_l[] = {
  {(char *)"Imperial", stdLborder, 5, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 15, 0} ,
  {(char *)"Metric", stdLborder, 55, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 16, 0} ,
  {(char *)"CANCEL", stdLborder, 105, stdbuttonwidth, 40, cancelclr, 325, TS_FBUTN, 0, 0, 2, 17, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};
TS_BUTTON unitsscreen_p[] = {
  {(char *)"Ipmerial", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 15, 0} ,
  {(char *)"Metric", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 16, 0} ,
  {(char *)"CANCEL", stdLborder, 220, stdbuttonwidth, 40, cancelclr, 319, TS_FBUTN, 0, 0, 3, 17, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};

// Calibration start/help screen dialog
TS_BUTTON calibrationstartscreen_l[] = {
  {(char *)"Start, 1 Meter", stdLborder, 5, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 18, 0} ,
  {(char *)"Start, 2 Meter", stdLborder, 55, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 19, 0} ,
  {(char *)"CANCEL", stdLborder, 105, stdbuttonwidth, 40, cancelclr, 325, TS_FBUTN, 0, 0, 2, 20, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};
TS_BUTTON calibrationstartscreen_p[] = {
  {(char *)"Start 1M", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 18, 0} ,
  {(char *)"Start 2M", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 19, 0} ,
  {(char *)"CANCEL", stdLborder, 220, stdbuttonwidth, 40, cancelclr, 319, TS_FBUTN, 0, 0, 3, 20, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};

// Calibration stop/set dialog
TS_BUTTON calibrationstopscreen_l[] = {
  {(char *)"Set", stdLborder, 140, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 21, 0} ,
  {(char *)"CANCEL", stdLborder, 190, stdbuttonwidth, 40, cancelclr, 325, TS_FBUTN, 0, 0, 2, 22, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};
TS_BUTTON calibrationstopscreen_p[] = {
  {(char *)"Set", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 21, 0} ,
  {(char *)"CANCEL", stdLborder, 220, stdbuttonwidth, 40, cancelclr, 319, TS_FBUTN, 0, 0, 3, 22, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};

// Set rotation dialog!
TS_BUTTON rotationscreen_l[] = {
  {(char *)"Rotate", stdLborder, 10, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 23, 0} ,
  {(char *)"Set", stdLborder, 60, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 2, 24, 0} ,
  {(char *)"CANCEL", stdLborder, 110, stdbuttonwidth, 40, cancelclr, 325, TS_FBUTN, 0, 0, 2, 25, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};
TS_BUTTON rotationscreen_p[] = {
  {(char *)"Rotate", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 23, 0} ,
  {(char *)"Set", stdLborder, 220, stdbuttonwidth, 40, 65535, gobuttonclr, TS_FBUTN, 0, 0, 3, 24, 0} ,
  {(char *)"CANCEL", stdLborder , stdbuttonwidth, 220, 40, cancelclr, 319, TS_FBUTN, 0, 0, 3, 25, 0} ,
  {(char *)".", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0, 0, 0}
};


// -----------------------------------------
// --- USER Button Data Ends here
// -----------------------------------------


uint16_t bg1 = ILI9341_BLUE;                  // define a background color in one place..
int16_t beepPin = 23;                         // we have a 4KHz buzzer attached here - drive low for tone!
int16_t beepTime = 50;                        // beep duration, longer is louder and more irritating..
uint8_t prevTS_Rotate = 0;                    // when we cancel, what rotation do we cancel back to?


void setup(void)
{

  fram.begin(FRAM_CS, 2);                     // intialize fram instance to save various data to..  Testing reveils 2 byte addressing..
  fram.setAddressSize(2);                     // ... so we set that here.

  Serial.begin(9600);
  while ( !Serial && (millis() < 2000)) ;
  tft.begin();
  if (!ts.begin()) Serial.println("Unable to start touchscreen.");
  else Serial.println("Touchscreen started.");
  
  // doesnt' work - mark2 versus mark3?
  //ts.ButtonRotate( 3 );                       // set button to line up with DISPLAY - for some reason we're mirrored with my hardware?

  tft.setFont(Arial_16);                      // sort of globally affect the button fonts..  What happens after displaying data??

  TS_Rotate = framReadByte(TSROTATE);         // recall last set rotation from FRAM

  ButtonInit( 0 );
  tft.fillScreen(bg1);
  if ((TS_Rotate % 2)) {
    ButtonInit( &mainscreen_l[0] );
  }
  else {
    ButtonInit( &mainscreen_p[0] );
  }
  ButtonDraw( 0 );
  pinMode(beepPin, OUTPUT);                   // setup beeper driving pin
  digitalWrite(beepPin, HIGH);                // make sure we're not beeping!  ;-)
  beep(50);
}



void loop()
{

   /* 
  We do stuff here - check encoder for changes, if so, update data, then appropriate
  display updates..  THEN, look to see if someone is pressing a button and act on it.
  */


  // See if there's any  touch data for us
  static elapsedMillis userTime;
  uint16_t userDebounce = 1555;
  static int16_t lastbValue;
  boolean istouched = false;
  int16_t bValue;
  int16_t x, y;
  istouched = TS_GetMap( x, y, true );
  if (istouched && ButtonHit(x , y, bValue))
  {
    if ( lastbValue == bValue ) userDebounce = userTime;

    // Main Screen...
    // buttons 1 2 3 are on main screen
    if ( 1 == bValue ) {  // reset trip meter data
      // do something that actually resets the trip meter here!
      beep(beepTime);
      Serial.println("Reset the trip meter data!!");
      delay(1000);  //stop button rehits crudely?
    }
    if ( 2 == bValue ) {  // full screen display
      // some sort of data only, press and hold for 3 seconds to return?
      beep(beepTime);
      Serial.println("Enter Full Screen Mode!!");
      delay(1000);  //stop button rehits crudely?
    }
    if ( 3 == bValue ) { // goto the sconfig screen..
      prevTS_Rotate = TS_Rotate;
      Serial.println("Go to the Configuration selection screen...");
      configscreen();                     // redraw the config screen
    }

    // Config Screen actions...
    // buttons 4, 5, 6, 7 and 8 are located on the config screen..  8 is cancel.
    if ( 4 == bValue ) {                   // goto rotation screen
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      ButtonInit( &rotationscreen_l[0] );
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Goto rotation screen...");
    }
    if ( 5 == bValue ) {                  // goto calibration screen
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      ButtonInit( &calibrationstartscreen_l[0] );
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Goto calibration screen...");
    }
    if ( 6 == bValue ) {                  // goto scale selection screen
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      ButtonInit( &scalescreen_l[0] );
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Goto scale selection...");
      delay(1500);
    }
    if ( 7 == bValue ) {                  // go to units
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      ButtonInit( &unitsscreen_l[0] );
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Go to units selection...");
    } 
    if ( 8 == bValue ) {                  // cancel
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      if ((TS_Rotate % 2)) {
        ButtonInit( &mainscreen_l[0] );
      }
      else {
        ButtonInit( &mainscreen_p[0] );
      }
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Return from config screen unchanged...");
    }

    // Scale Select Screen actions...
    // buttons 9 - 14 are to scelect scale
    if ( 9 == bValue ) {                  // 1:32
      // do something to set scale to 1:32nd here
      configscreen();                     // redraw the config screen
      Serial.println("Select 1:32nd scale and return to main screen...");
    }
    if ( 10 == bValue ) {                 // 1:29
      // do something to set scale to 1:29th here
      configscreen();                     // redraw the config screen
      Serial.println("Select 1:29th scale and return to main screen...");
    }
    if ( 11 == bValue ) {                 // 1:24
      // do something to set scale to 1:24th here
      configscreen();                     // redraw the config screen
      Serial.println("Select 1:24th scale and return to main screen...");
    }
    if ( 12 == bValue ) {                 // 1:22.5
      // do something to set scale to 1:22.5 here
      configscreen();                     // redraw the config screen
      Serial.println("Select 1:22.5 scale and return to main screen...");
    }
    if ( 13 == bValue ) {                 // 1:20.5
      // do something to set scale to 1:20.5 here
      tft.fillScreen(bg1);
      configscreen();                     // redraw the config screen
      Serial.println("Select 1:20.5th scale and return to main screen...");
    }
    if ( 14 == bValue ) {                 // cancel
      // Don't do anything and return to the main screen...
      configscreen();                     // redraw the config screen
      Serial.println("cancel from select scale screen...");
    }

    // Units Select Screen actions...
    // units select are buttons 15, 16 and 17..
    if ( 15 == bValue ) {                 // imperial units select
      // do something to set units to Imperial here
      configscreen();                     // redraw the config screen
      Serial.println("Set units to Imperial measure...");
    }
    if ( 16 == bValue ) {                 // metric
      // do something to set units to Metric here
      configscreen();                     // redraw the config screen
      Serial.println("Set units to Metric measure...");
    }
    if ( 17 == bValue ) {                 // cancel
      // return to main screen without doing anything
      configscreen();                     // redraw the config screen
      Serial.println("Return to main screen from units select...");
    }


    // Calibration start screen...
    // Calibration start is button 18, cancel is 19..
    if ( 18 == bValue ) { // calibration start using 1M reference
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      ButtonInit( &calibrationstopscreen_l[0] );
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Start calibration count using 1 meter reference...");
    }
    if ( 19 == bValue ) { // calibration start using 2m reference
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      ButtonInit( &calibrationstopscreen_l[0] );
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Start calibration count using 2 meter reference...");
    }
    if ( 20 == bValue ) {                 // cancel
      // return to main screen without doing anything
      configscreen();                     // redraw the config screen
      Serial.println("Cancel from set units screen without saving changes...");
    }

    // Calibration stop screen...
    // Calibration stop is button 18, cancel is 19..
    if ( 21 == bValue ) {                  // calibration stop at reference distance..
      // do seomthing here to save encoder data!!
      configscreen();                     // redraw the config screen
      Serial.println("Stop calibration count using selected reference...");
    }
    if ( 22 == bValue ) {                 // cancel
      // return to main screen without doing anything
      configscreen();                     // redraw the config screen
      Serial.println("Cancel from set units setup without saving changes...");
    }


    // Rotation start screen...
    // Rotation is button 18, cancel is 19..
    if ( 23 == bValue ) {                 // Rotate the screen
      tft.fillScreen(bg1);
      ButtonRotate( 1 + TS_Rotate );
      ButtonInit( 0 );
      ButtonInit( &rotationscreen_l[0] );
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Rotate the screen here...");
    }
    if ( 24 == bValue ) {                 // save the current rotation!
      // save rotation settings in some persistent fashion here.
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      ButtonInit( &configscreen_l[0] );
      prevTS_Rotate = TS_Rotate;
      framWriteByte(TSROTATE, TS_Rotate);
      ButtonDraw( 0 );
      beep(beepTime);
      Serial.println("Save rotation settings...");
    }
    if ( 25 == bValue ) {                 // cancel
      // return to main screen without doing anything
      tft.fillScreen(bg1);
      TS_Rotate = prevTS_Rotate;          // set back to original rotation
      configscreen();                     // redraw the config screen
      Serial.println("Cancel from set rotation screen without saving changes...");
    }

    lastbValue = bValue;
    userTime = 0;
  }
}         // end void loop








// -----------------------------------------
// --- Button Code Starts here
// -----------------------------------------
TS_BUTTON TS_NoButton[] = { {(char *)"X", -1, 0, 0, 0, 0, 0, TS_FBUTN, 0, 0, 0} };
TS_BUTTON *TS_pbtn = TS_NoButton;

#define TS_LIMIT 10 // millisecond limit to check touch value
#define TS_MINPRESS 800 // Z Pressure return minimum to use point
#define TS_DEBOUNCE 3 // TX_Map() debounce factor
boolean TS_GetMap( int16_t &xx, int16_t &yy, boolean twoHits )
{
  static int16_t lastxx = 500, lastyy = 500; // if twoHits: require two hits same point
  static TS_Point pp;
  static elapsedMillis tsLimit;
  static int16_t xxo = 500, yyo = 500;  // Store old/last returned x,y point
  if ( tsLimit < TS_LIMIT ) return false;
  if (!ts.touched()) return false;
  if ( tsLimit >= TS_SLIDET ) TS_iiSlide = -1;
  tsLimit = 0;
  pp = ts.getPoint();
  if ( pp.z < TS_MINPRESS ) return false;
  xx = map(pp.x, tsMap[0].xt, tsMap[0].xb, tsMap[TS_Rotate].xt, tsMap[TS_Rotate].xb);
  yy = map(pp.y, tsMap[0].yl, tsMap[0].yr, tsMap[TS_Rotate].yl, tsMap[TS_Rotate].yr);
  if (!(TS_Rotate % 2)) {
    int16_t swap = xx;
    xx = yy;
    yy = swap;
  }
  // Debounce by +/-# pixel to minimize point jitter
  if ( ((xxo - TS_DEBOUNCE) <= xx) && ((xxo + TS_DEBOUNCE) >= xx) ) xx = xxo; else xxo = xx;
  if ( ((yyo - TS_DEBOUNCE) <= yy) && ((yyo + TS_DEBOUNCE) >= yy) ) yy = yyo; else yyo = yy;
  if (  twoHits && (xx != lastxx || yy != lastyy) ) {
    lastxx = xx; lastyy = yy;
  }
  if ( 500 == lastyy) TS_iiSlide = -1;
  lastxx = 500; lastyy = 500;
  return true;
}

// int8_t ButtonType( int16_t idBut ) {   return buttons[idBut].btype; }

void ButtonDraw( int16_t idBut ) {
  int ii = 0;
  while ( ii < TS_bCount ) {
    if ((( TS_pbtn[ii].tx + TS_pbtn[ii].ww ) <= TS_xMax ) && (( TS_pbtn[ii].ty + TS_pbtn[ii].hh ) <= TS_yMax)) {
      if ( !idBut || idBut == TS_pbtn[ii].bId ) {
        if ( TS_TOGOFF == TS_pbtn[ii].btype || TS_FBUTN == TS_pbtn[ii].btype || TS_RBUTN == TS_pbtn[ii].btype || TS_BSLIDE == TS_pbtn[ii].btype ) {
          if (TS_RBUTN == TS_pbtn[ii].btype) {
            tft.fillRoundRect(TS_pbtn[ii].tx, TS_pbtn[ii].ty, TS_pbtn[ii].ww, TS_pbtn[ii].hh, TS_pbtn[ii].hh / 2, TS_pbtn[ii].bgc);
          }
          else
            tft.fillRect(TS_pbtn[ii].tx, TS_pbtn[ii].ty, TS_pbtn[ii].ww, TS_pbtn[ii].hh, TS_pbtn[ii].bgc);
          tft.setCursor(TS_pbtn[ii].tx + 6, TS_pbtn[ii].ty + (TS_pbtn[ii].hh / 3));
          tft.setTextColor(TS_pbtn[ii].fgc);  // ForeGround Text color same as Frame
          tft.setTextSize(TS_pbtn[ii].fontsz);
          tft.println(TS_pbtn[ii].text);
        }
        else if ( TS_TOGON == TS_pbtn[ii].btype || TS_ASLIDE == TS_pbtn[ii].btype )
          tft.fillRect(TS_pbtn[ii].tx, TS_pbtn[ii].ty, TS_pbtn[ii].ww, TS_pbtn[ii].hh, TS_pbtn[ii].fgc);
      }
    }
    ii++;
  }
  ii = 0;
  while ( ii < TS_bCount ) {
    if ((( TS_pbtn[ii].tx + TS_pbtn[ii].ww ) <= TS_xMax ) && (( TS_pbtn[ii].ty + TS_pbtn[ii].hh ) <= TS_yMax)) {
      if ( !idBut || idBut == TS_pbtn[ii].bId ) {
        if ( TS_FRAME == TS_pbtn[ii].btype || TS_FBUTN == TS_pbtn[ii].btype )
          tft.drawRect(TS_pbtn[ii].tx, TS_pbtn[ii].ty, TS_pbtn[ii].ww, TS_pbtn[ii].hh, TS_pbtn[ii].fgc);
      }
    }
    ii++;
  }
}

boolean ButtonHit( int16_t xx, uint16_t yy, int16_t &bHit ) {
  static elapsedMillis toggleTime;  // Debounce/Jitter Toggle and Slider
  int16_t ii = 0, iiOn = -1, iiHit = -1, idTog = 0, iiFrame = -1;
  if ( toggleTime > 10000 ) toggleTime = 1000;
  while ( ii < TS_bCount ) {
    if  ( (xx >= TS_pbtn[ii].tx && xx <= TS_pbtn[ii].tx + TS_pbtn[ii].ww) && (yy >= TS_pbtn[ii].ty && yy <= TS_pbtn[ii].ty + TS_pbtn[ii].hh)) {
      if ( TS_ASLIDE == TS_pbtn[ii].btype ) {
        TS_iiSlide = TS_pbtn[ii].info; // track slide start - do nothing now
        return false;
      }
      else if ( (TS_FBUTN == TS_pbtn[ii].btype) || TS_RBUTN == TS_pbtn[ii].btype ) {
        bHit =  TS_pbtn[ii].bId;
        TS_iiSlide = -1;
        return true;
      }
      else if (TS_TOGOFF == TS_pbtn[ii].btype) {
        iiHit = ii;
        idTog = (TS_pbtn[ii].info);
        TS_iiSlide = -1;
      }
      else if ( (TS_iiSlide == TS_pbtn[ii].info) && (TS_BSLIDE == TS_pbtn[ii].btype)) {
        iiHit = ii;
        idTog = (TS_pbtn[ii].info);
      }
    }
    ii++;
  }
  ii = 0;
  while ( ii < TS_bCount ) {
    if ( ((TS_TOGON == TS_pbtn[ii].btype) || (TS_ASLIDE == TS_pbtn[ii].btype)) && (idTog == TS_pbtn[ii].info) )
      iiOn = ii;
    if ( (TS_FRAME == TS_pbtn[ii].btype) && (idTog == TS_pbtn[ii].info) )
      iiFrame = ii;
    ii++;
  }

  if ( -1 != iiOn && -1 != iiFrame ) {
    if ( toggleTime < TS_JITTER ) {
      TS_iiSlide = -1;
      return false;
    }
    TS_pbtn[iiFrame].TS_data = !TS_pbtn[iiFrame].TS_data;
    bHit = TS_pbtn[iiHit].bId;
    if ( TS_iiSlide == TS_pbtn[iiOn].info ) {
      TS_pbtn[iiOn].btype = TS_BSLIDE;
      TS_pbtn[iiHit].btype = TS_ASLIDE;
    }
    else {
      TS_pbtn[iiOn].btype = TS_TOGOFF;
      TS_pbtn[iiHit].btype = TS_TOGON;
    }
    ButtonDraw( TS_pbtn[iiOn].bId );  // Limit Redraw to Framed TS_pbtn - not all buttons
    ButtonDraw( TS_pbtn[iiHit].bId );
    ButtonDraw( TS_pbtn[iiFrame].bId );
    TS_iiSlide = -1;
    toggleTime = 0;
    return true;
  }
  return false;
}

void  ButtonRotate( int setrotate )
{
  TS_Rotate = setrotate;
  if ( TS_Rotate > 4 ) TS_Rotate = 1;
  if ( TS_Rotate < 1 ) TS_Rotate = 4;
  tft.setRotation(TS_Rotate);
  if ((TS_Rotate % 2)) {
    TS_xMax = tsMap[1].xb;
    TS_yMax = tsMap[1].yr;
  }
  else {
    TS_xMax = tsMap[1].yr;
    TS_yMax = tsMap[1].xb;
  }
  /*Serial.print( "\nTS_xMax =" );
  Serial.println( TS_xMax );
  Serial.print( "TS_yMax =" );
  Serial.println( TS_yMax );*/
}

void ButtonInit( TS_BUTTON *userbuttons )
{
  if ( 0 == userbuttons ) {
    TS_pbtn = TS_NoButton;
    tft.fillScreen(ILI9341_BLUE);
    ButtonRotate( TS_Rotate );
  }
  else TS_pbtn = userbuttons;
  TS_bCount = 0;
  while ( -1 != TS_pbtn[TS_bCount].tx ) {
    TS_bCount++;
  }
  /*Serial.print( "Buttons count =" );
  Serial.println( TS_bCount );
  Serial.println( sizeof( TS_BUTTON) );*/
  ButtonDraw( 0 );
}
// -----------------------------------------
// --- Button Code Ends here
// -----------------------------------------


// Eric's datacar support routines start here...

void configscreen(void) {                                   // a lot of routines return to config screen..
      tft.fillScreen(bg1);
      ButtonInit( 0 );
      ButtonInit( &configscreen_l[0] );
      ButtonDraw( 0 );
      beep(beepTime);
      delay(1000);  //stop button rehits crudely?
}

void beep(int beeptime) {                                     // Elemental Beep function - not much going on here anyway...
  digitalWrite(beepPin, LOW);
  delay(beeptime);                                            // ..cause we just don't care, just make it beep the old fashioned way...
  digitalWrite(beepPin, HIGH);
}

void framWritelong(uint16_t address, unsigned long value) {   // write a long to four consecutive bytes.
  byte four = (value & 0xFF);
  byte three = ((value >> 8) & 0xFF);
  byte two = ((value >> 16) & 0xFF);
  byte one = ((value >> 24) & 0xFF);
  fram.writeEnable(true);                                     //Write the 4 bytes into the eeprom memory.
  fram.write8(address, four);
  fram.writeEnable(false);
  fram.writeEnable(true);
  fram.write8(address + 1, three);
  fram.writeEnable(false);
  fram.writeEnable(true);
  fram.write8(address + 2, two);
  fram.writeEnable(false);
  fram.writeEnable(true);
  fram.write8(address + 3, one);
  fram.writeEnable(false);
}

void framWriteByte(uint16_t address, uint8_t value) {         // write a single byte..
  fram.writeEnable(true);
  fram.write8(address, value);
  fram.writeEnable(false);
}

unsigned long framReadlong(uint16_t address) {                 //Read the 4 bytes from the eeprom memory.
  long four = fram.read8(address);
  long three = fram.read8(address + 1);
  long two = fram.read8(address + 2);
  long one = fram.read8(address + 3);
  //Return the recomposed long by using bitshift.
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

uint8_t framReadByte(uint16_t address) {                       // read single byte
  return fram.read8(address);
}
 
I got some of these PJRC displays that are in common use - mine wired up as indicated on these pages:

Color 320x240 Touchscreen, 2.8 inch, ILI9341 Controller

> this unit is the same smaller screen controller but without the XPT2046 TOUCH controller:
Color 320x240 Display, 2.2 inch, ILI9341 Controller

My current example set of Touch enabled ILI9341 sketches:
https://github.com/Defragster/XPT2046_Touch_Examples

There are two other active threads with Touch posts - hoping to merge activity on this to here from:
2.8 ILI9341 restive touch miso not connected ?

Highly optimized ILI9341 (320x240 TFT color display) library


Hey, is this lib as well possible for 4.0 as possible for 3.5? thanks!
 
Before I "bomb" the forum with all of my code and wiring to get help for my problem of "mutally exclusive" touch and TFT display, I thought I might ask for a clarification of SISSPEED referenced in the below quote. It might be the clue to why I can get beautifully performing TFT displays and terrific touch response showing all the right "decodable" touch values..... but not when both are active. I have a new Teensy 4.0 (my first Teensy experience and I am impressed) and a generic 3.2" TFT SPI display(MSP3218 running on 3.3V) using:
#include <XPT2046_Touchscreen.h> and
#include <Adafruit_ILI9341.h> plus all of the other stuff

My program works great but can't work with both
tft.begin(); and
ts.begin();
(?) I am wondering if the two header files are running at different speeds as described in the quote, hence my request for clarification on SPISPEED. As I said above, each works just fine separately..... Great TFT but no touch reponse with "// ts.begin();" and great touch response with "ts.begin();" but no TFT.... makes no difference where in the program the "begin" statements are located.
How to identify and sync the speeds on both inclusions?
I would be glad to post everything I have if this isn't a SPISPEED problem.



Hi all,

Just to inform ... it was a usermistake not due to wrong wiring but a simple code-mishap. The library I was using for SPI had a setting for the SPISPEED that was different from the SPISPEED the ILI9341 is using ... it seems that the variable for the SPISPEED of the XPT2046 touch was set to 20Mhz instead of the 2Mhz normally used (according to https://github.com/PaulStoffregen/XPT2046_Touchscreen/blob/master/XPT2046_Touchscreen.cpp). Now things are as planned ... sorry to have bothered anybody.

regards
Cor
 
additional info on my SPI configuration...
Both TFT and touch are using the same MOSI(11), MISO(12) and SCK(13) lines with TFT_CS(10) and CS_PIN(2). Like I said before, the "ts.begin();" statement determines whether the touch only works or the TFT only works.
 
Last edited:
Hi all,

Just to inform ... it was a usermistake not due to wrong wiring but a simple code-mishap. The library I was using for SPI had a setting for the SPISPEED that was different from the SPISPEED the ILI9341 is using ... it seems that the variable for the SPISPEED of the XPT2046 touch was set to 20Mhz instead of the 2Mhz normally used (according to https://github.com/PaulStoffregen/XPT2046_Touchscreen/blob/master/XPT2046_Touchscreen.cpp). Now things are as planned ... sorry to have bothered anybody.

regards
Cor

Hi @CorBee, how did you set the speed to the same value (hopefully 20Mhz) in code?
 
Status
Not open for further replies.
Back
Top