This sample program demonstrates the drawing capability of the TFT LCD touchscreen. Users can select colors and draw on the screen by touching, with support for one-click canvas clearing.
The sample code demonstrated here is based on Arduino UNO R3. The operation logic of other microcontrollers is roughly the same.
The following content has been explained in detail in the DisplayString and GraphicsTest examples, so only a brief introduction is provided here:
Elegoo_GFX.h (core graphics library), Elegoo_TFTLCD.h (hardware driver library), and TouchScreen.h (touchscreen driver library) for LCD display and touch input functionality.Elegoo_TFTLCD tft object and TouchScreen ts object as the entry points for screen operations and touch input.The touchscreen uses four pins for coordinate detection:
#define YP A3 // must be an analog pin, use "An" notation!
#define XM A2 // must be an analog pin, use "An" notation!
#define YM 9 // can be a digital pin
#define XP 8 // can be a digital pin
There is a significant hardware design feature in the code: touchscreen pins are shared with LCD control pins:
YP = A3 shares the same pin as LCD_CS = A3XM = A2 shares the same pin as LCD_CD = A2This means the pin directions need to be switched during different operation phases. After reading the touch point in the loop() function, the pin directions are reconfigured:
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
This approach saves Arduino's limited digital pin resources and is a common hardware optimization technique.
The code defines the calibration range for the touchscreen, used to convert analog signals to screen coordinates:
#define TS_MINX 120
#define TS_MAXX 900
#define TS_MINY 70
#define TS_MAXY 920
These values are obtained through actual testing and represent the minimum/maximum analog readings on the X and Y axes of the touchscreen. Different touchscreens may require adjustment of these parameters for accurate coordinate mapping.
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
Creating a TouchScreen object requires five parameters:
XP: X+ pinYP: Y+ pinXM: X- pinYM: Y- pin300: Resistance between X plates (ohms), used to improve pressure detection accuracy#define BOXSIZE 40
#define PENRADIUS 3
int oldcolor, currentcolor;
The setup() function completes the initialization of the serial port, LCD screen, and touchscreen, and draws the color selection panel.
void setup(void) {
Serial.begin(9600);
Serial.println(F("Paint!"));
tft.reset();
uint16_t identifier = tft.readID();
if(identifier == 0x9325) {
Serial.println(F("Found ILI9325 LCD driver"));
} else if(identifier == 0x9328) {
Serial.println(F("Found ILI9328 LCD driver"));
} else if(identifier == 0x4535) {
Serial.println(F("Found LGDP4535 LCD driver"));
}else if(identifier == 0x7575) {
Serial.println(F("Found HX8347G LCD driver"));
} else if(identifier == 0x9341) {
Serial.println(F("Found ILI9341 LCD driver"));
} else if(identifier == 0x8357) {
Serial.println(F("Found HX8357D LCD driver"));
} else if(identifier==0x0101)
{
identifier=0x9341;
Serial.println(F("Found 0x9341 LCD driver"));
}else {
Serial.print(F("Unknown LCD driver chip: "));
Serial.println(identifier, HEX);
identifier=0x9341;
}
tft.begin(identifier);
tft.setRotation(2);
tft.fillScreen(BLACK);
tft.fillRect(0, 0, BOXSIZE, BOXSIZE, RED);
tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, YELLOW);
tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, GREEN);
tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, CYAN);
tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, BLUE);
tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, MAGENTA);
tft.drawRect(0, 0, BOXSIZE, BOXSIZE, WHITE);
currentcolor = RED;
pinMode(13, OUTPUT);
}
tft.reset(): Reset the LCD screentft.readID(): Read the driver chip ID and identify ittft.begin(identifier): Initialize the screen using the identified driver IDtft.setRotation(2): Set screen rotation to 180° to align touchscreen coordinates with display coordinatestft.fillScreen(BLACK);
Fill the entire screen with black as the canvas background.
tft.fillRect(0, 0, BOXSIZE, BOXSIZE, RED);
tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, YELLOW);
tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, GREEN);
tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, CYAN);
tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, BLUE);
tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, MAGENTA);
Draw six color selection boxes at the top of the screen, from left to right: RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA.
tft.drawRect(0, 0, BOXSIZE, BOXSIZE, WHITE);
currentcolor = RED;
Draw a white border around the first color box (RED) to indicate the currently selected color, and initialize currentcolor to RED.
The loop() function is the main loop of the Arduino program, responsible for detecting touch input and performing corresponding drawing operations.
#define MINPRESSURE 10
#define MAXPRESSURE 1000
void loop()
{
digitalWrite(13, HIGH);
TSPoint p = ts.getPoint();
digitalWrite(13, LOW);
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
if (p.y < (TS_MINY-5)) {
Serial.println("erase");
tft.fillRect(0, BOXSIZE, tft.width(), tft.height()-BOXSIZE, BLACK);
}
p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
p.y = (tft.height()-map(p.y, TS_MINY, TS_MAXY, tft.height(), 0));
if (p.y < BOXSIZE) {
oldcolor = currentcolor;
if (p.x < BOXSIZE) {
currentcolor = RED;
tft.drawRect(0, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*2) {
currentcolor = YELLOW;
tft.drawRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*3) {
currentcolor = GREEN;
tft.drawRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*4) {
currentcolor = CYAN;
tft.drawRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*5) {
currentcolor = BLUE;
tft.drawRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*6) {
currentcolor = MAGENTA;
tft.drawRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, WHITE);
}
if (oldcolor != currentcolor) {
if (oldcolor == RED) tft.fillRect(0, 0, BOXSIZE, BOXSIZE, RED);
if (oldcolor == YELLOW) tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, YELLOW);
if (oldcolor == GREEN) tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, GREEN);
if (oldcolor == CYAN) tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, CYAN);
if (oldcolor == BLUE) tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, BLUE);
if (oldcolor == MAGENTA) tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, MAGENTA);
}
}
if (((p.y-PENRADIUS) > BOXSIZE) && ((p.y+PENRADIUS) < tft.height())) {
tft.fillCircle(p.x, p.y, PENRADIUS, currentcolor);
}
}
}
digitalWrite(13, HIGH);
TSPoint p = ts.getPoint();
digitalWrite(13, LOW);
ts.getPoint() to get touch coordinates, returning a TSPoint object containing x, y coordinates and pressure value zif (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
Only when the pressure value is within the valid range (10-1000) is it considered a valid touch operation. A pressure value of 0 means no touch.
if (p.y < (TS_MINY-5)) {
Serial.println("erase");
tft.fillRect(0, BOXSIZE, tft.width(), tft.height()-BOXSIZE, BLACK);
}
When the raw Y coordinate of the touch point is less than the calibration minimum minus 5, it is considered that the user pressed the clear area at the bottom of the screen, and the entire canvas area (below the color panel) is filled with black.
p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
p.y = (tft.height()-map(p.y, TS_MINY, TS_MAXY, tft.height(), 0));
Convert the raw analog coordinates of the touchscreen to screen pixel coordinates:
map() function maps the raw coordinate range to the screen size rangeif (p.y < BOXSIZE) {
oldcolor = currentcolor;
if (p.x < BOXSIZE) {
currentcolor = RED;
tft.drawRect(0, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*2) {
currentcolor = YELLOW;
tft.drawRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*3) {
currentcolor = GREEN;
tft.drawRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*4) {
currentcolor = CYAN;
tft.drawRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*5) {
currentcolor = BLUE;
tft.drawRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, WHITE);
} else if (p.x < BOXSIZE*6) {
currentcolor = MAGENTA;
tft.drawRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, WHITE);
}
if (oldcolor != currentcolor) {
if (oldcolor == RED) tft.fillRect(0, 0, BOXSIZE, BOXSIZE, RED);
if (oldcolor == YELLOW) tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, YELLOW);
if (oldcolor == GREEN) tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, GREEN);
if (oldcolor == CYAN) tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, CYAN);
if (oldcolor == BLUE) tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, BLUE);
if (oldcolor == MAGENTA) tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, MAGENTA);
}
}
When the touch point is located in the color panel area (Y coordinate < BOXSIZE):
oldcolorcurrentcolor to the newly selected colorif (((p.y-PENRADIUS) > BOXSIZE) && ((p.y+PENRADIUS) < tft.height())) {
tft.fillCircle(p.x, p.y, PENRADIUS, currentcolor);
}
When the touch point is located in the canvas area (below the color panel and not exceeding the bottom of the screen):
tft.fillCircle() to draw a filled circle at the touch positionPENRADIUS (3 pixels)This sample program implements touchscreen drawing functionality through the following steps:
The key functions used in the program include:
ts.getPoint(): Get touch coordinates and pressure valuetft.fillRect(): Draw filled rectangle (used for color boxes and clearing canvas)tft.drawRect(): Draw outline rectangle (used for selected color box border)tft.fillCircle(): Draw filled circle (used for drawing)map(): Coordinate mapping functiondigitalWrite(): Control digital pin outputIf you need to modify the code display effect, you can refer to the following aspects for adjustment:
Modify Color Selection Box Size:
BOXSIZE constant to adjust the size of color boxesModify Pen Brush Radius:
PENRADIUS constant to adjust the size of drawn dotsModify Color Panel Colors:
fillRect() in the setup() function to add or replace colorsModify Pressure Detection Range:
MINPRESSURE and MAXPRESSURE to change touch sensitivityModify Screen Rotation Angle:
tft.setRotation() (0-3) to adjust the screen display orientationModify Touchscreen Calibration Parameters:
TS_MINX, TS_MAXX, TS_MINY, TS_MAXY to optimize coordinate mapping accuracyModify Clear Area Position:
p.y < (TS_MINY-5) to change the trigger area of the clear function