Los escudos TFT son pantallas LCD sensibles al tacto para mostrar imágenes y crear interfaces de usuario, con gráficos más o menos complejos, para accionar los microcontroladores de Arduino. En este tutorial, usamos el escudo TFT de 3,5″ de Kuman (muy cercano al de 2,8″) pero este tutorial puede aplicarse a otros escudos o módulos LCD. Revise cuidadosamente los pines usados y la compatibilidad de la biblioteca.
Hardware
- Computadora
- Arduino UNO o Mega
- Cable USB A macho a B macho
- TFT LCD Shield
- Tarjeta SD
Principio de funcionamiento
El escudo TFT está equipado con una pantalla táctil LCD que permite mostrar imágenes en color y crear interfaces con curvas y botones para interactuar y ver los datos del Arduino. También está equipado con un lector de tarjetas micro SD que puede ser usado para guardar imágenes u otros datos.
Esquema
El escudo TFT se coloca directamente en una tarjeta Arduino UNO o Mega. El escudo utiliza casi todos los alfileres de la ONU de Arduino. Asegúrate de no usar los mismos alfileres para otros módulos.
Para su información los alfileres disponibles son:
- pin analógico A5
- pin digital 0
- pin digital 1
Si no utiliza la funcionalidad táctil de la pantalla, también puede utilizar los pines: A1, A2, 6 y 7.
Si está usando otro escudo o módulo LCD, asegúrese de revisar el pinout.
Código
Para utilizar el objeto TftScreen, utilizamos las librerías SD.h, Adafruit_GFX.h, MCUFRIEND_kbv.h, et TouchScreen.h que permiten gestionar la comunicación con la tarjeta SD, la creación de gráficos, la gestión de la pantalla y la pantalla táctil. Este ejemplo ofrece un manejo muy simple de la pantalla.
//Libraries #include <SD.h>//https://www.arduino.cc/en/reference/SD #include <Adafruit_GFX.h>//https://github.com/adafruit/Adafruit-GFX-Library #include <MCUFRIEND_kbv.h>//https://github.com/prenticedavid/MCUFRIEND_kbv #include <TouchScreen.h> //https://github.com/adafruit/Adafruit_TouchScreen //Constants #define SD_CS 10 #define BLACK 0 #define GREY 21845 #define BLUE 31 #define RED 63488 #define GREEN 2016 #define DARKGREEN 1472 #define CYAN 2047 #define MAGENTA 63519 #define YELLOW 65504 #define GOLD 56768 #define WHITE 65535 //Touch screen configuration #define MINPRESSURE 200 #define MAXPRESSURE 1000 // ALL Touch panels and wiring is DIFFERENT // copy-paste results from TouchScreen_Calibr_native.ino //3.5 Parameters const int XP = 8, XM = A2, YP = A3, YM = 9; //320x480 ID=0x9486 const int TS_LEFT = 144, TS_RT = 887, TS_TOP = 936, TS_BOT = 87; //2.8 Parameters //const int XP = 8, XM = A2, YP = A3, YM = 9; //240x320 ID=0x9341 //const int TS_LEFT = 907, TS_RT = 120, TS_TOP = 74, TS_BOT = 913; TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); TSPoint p; bool down; int pixel_x, pixel_y; //Touch_getXY() updates global vars //Variables int currentPage = 0, oldPage = -1; //Objects MCUFRIEND_kbv tft; // Button calibration Adafruit_GFX_Button page1_btn, page2_btn; int margin = 5; int btnWidth = 100; int btnHeight = 40; int btnY = 200; void setup() { //Init Serial USB Serial.begin(9600); Serial.println(F("Initialize System")); //Init tft screen uint16_t ID = tft.readID(); if (ID == 0xD3D3) ID = 0x9486; //for 3.5" TFT LCD Shield , 0x9341 for 2.8" TFT LCD Shield tft.begin(ID); tft.setRotation(1);//0-PORTRAIT 1-PAYSAGE 2-REVERSE PORTRAIT 3-REVERSE PAYSAGE //Uncomment if you are using SD /*if (!SD.begin(SD_CS)) { Serial.println(F("initialization failed!")); return; }*/ currentPage = 0; // Indicates that we are at Home Screen } void loop() { down = Touch_getXY(); switch (currentPage) { case 0: if (currentPage != oldPage) { Serial.println(F("Page 1")); drawPage1(); } page2_btn.press(down && page2_btn.contains(pixel_x, pixel_y)); //if (page2_btn.justReleased()) page2_btn.drawButton(); if (page2_btn.justPressed()) { currentPage = 1; } break; case 1: if (currentPage != oldPage) { Serial.println(F("Page 2")); drawPage2(); } page1_btn.press(down && page1_btn.contains(pixel_x, pixel_y)); //if (page1_btn.justReleased()) page1_btn.drawButton(); if (page1_btn.justPressed()) { currentPage = 0; } break; } } void drawPage1() { /* function drawHomePage */ tft.setRotation(1); tft.fillScreen(BLACK); //Title tft.setCursor(0, 10); tft.setTextSize(3); tft.setTextColor(WHITE, BLACK); tft.print("This is page 1"); // Prints the string on the screen tft.drawLine(0, 32, 319, 32, DARKGREEN); // Draws the red line //text tft.setTextSize(3); tft.setTextColor(RED, BLACK); tft.setCursor(tft.width() / 2. - 14 * 3 * 3, 80); tft.print(" Go to page 2 "); //Button page2_btn.initButton(&tft, tft.width() / 2., 200, 2 * btnWidth, btnHeight, WHITE, GREEN, BLACK, "Page 2", 2); page2_btn.drawButton(false); oldPage = currentPage; } void drawPage2() { /* function drawHomePage */ tft.setRotation(1); tft.fillScreen(GREY); //Title tft.setCursor(0, 10); tft.setTextSize(3); tft.setTextColor(WHITE, GREY); tft.print("This is page 2"); // Prints the string on the screen tft.drawLine(0, 32, 319, 32, DARKGREEN); // Draws the red line //text tft.setTextSize(3); tft.setTextColor(GREEN, BLACK); tft.setCursor(tft.width() / 2. - 14 * 3 * 3, 80); tft.print(" Go to page 1 "); //Button page1_btn.initButton(&tft, tft.width() / 2., 200, 2 * btnWidth, btnHeight, WHITE, RED, BLACK, "Page 1", 2); page1_btn.drawButton(false); oldPage = currentPage; } /************************************************************************************ UTILITY FUNCTION *************************************************************************************/ bool Touch_getXY(void) { p = ts.getPoint(); pinMode(YP, OUTPUT); //restore shared pins pinMode(XM, OUTPUT); digitalWrite(YP, HIGH); digitalWrite(XM, HIGH); bool pressed = (p.z > MINPRESSURE && p.z < MAXPRESSURE); if (pressed) { if (tft.width() <= tft.height()) { //Portrait pixel_x = map(p.x, TS_LEFT, TS_RT, 0, tft.width()); //.kbv makes sense to me pixel_y = map(p.y, TS_TOP, TS_BOT, 0, tft.height()); } else { pixel_x = map(p.y, TS_TOP, TS_BOT, 0, tft.width()); pixel_y = map(p.x, TS_RT, TS_LEFT, 0, tft.height()); } } return pressed; }
Bono: Ejemplo de interfaz gráfica
En este ejemplo, crearemos una interfaz gráfica de usuario para mostrar las diferentes características que se pueden desarrollar con el escudo TFT.
#include <Adafruit_GFX.h> #include <MCUFRIEND_kbv.h> MCUFRIEND_kbv tft; #include <TouchScreen.h> #include <Fonts/FreeSans9pt7b.h> #include <Fonts/FreeSans12pt7b.h> #include <Fonts/FreeSerif12pt7b.h> #include <FreeDefaultFonts.h> #define MINPRESSURE 200 #define MAXPRESSURE 1000 // ALL Touch panels and wiring is DIFFERENT // copy-paste results from TouchScreen_Calibr_native.ino //3.5 Calibration const int XP = 8, XM = A2, YP = A3, YM = 9; //320x480 ID=0x9486 const int TS_LEFT = 144, TS_RT = 887, TS_TOP = 936, TS_BOT = 87; /*PORTRAIT CALIBRATION 320 x 480 x = map(p.x, LEFT=144, RT=887, 0, 320) y = map(p.y, TOP=936, BOT=87, 0, 480) Touch Pin Wiring XP=8 XM=A2 YP=A3 YM=9 LANDSCAPE CALIBRATION 480 x 320 x = map(p.y, LEFT=936, RT=87, 0, 480) y = map(p.x, TOP=887, BOT=144, 0, 320)*/ //2.8 Calbiration //const int XP=8,XM=A2,YP=A3,YM=9; //240x320 ID=0x9341 //const int TS_LEFT=907,TS_RT=120,TS_TOP=74,TS_BOT=913; /*PORTRAIT CALIBRATION 240 x 320 x = map(p.x, LEFT=907, RT=120, 0, 240) y = map(p.y, TOP=74, BOT=913, 0, 320) Touch Pin Wiring XP=8 XM=A2 YP=A3 YM=9 LANDSCAPE CALIBRATION 320 x 240 x = map(p.y, LEFT=74, RT=913, 0, 320) y = map(p.x, TOP=120, BOT=907, 0, 240)*/ TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); TSPoint p; Adafruit_GFX_Button on_btn, off_btn, page1_btn, page2_btn, page3_btn; Adafruit_GFX_Button ok_btn, cncl_btn, plus_btn, minus_btn; Adafruit_GFX_Button menu_btn, info_btn, back_btn; int pixel_x, pixel_y; //Touch_getXY() updates global vars // Button calibration int margin = 5; int btnWidth = 100; int btnHeight = 40; int btnY = 200; // Software variable bool enable_nuit = false; int parameter = 50, old_parameter = 50; long temp0 = 60; long temp1 = 25.5; long temp2 = 40; long temp3 = 35.6; #define BLACK 0x0000 #define GREY 0x5555 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define DARKGREEN 0x05C0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define GOLD 0xDDC0 #define WHITE 0xFFFF enum pageId { MENU, SENSOR, CMD, PARAM, INFO }; unsigned int currentPage = MENU, oldPage = -1; void setup(void) { Serial.begin(9600); //init TFTTouch uint16_t ID = tft.readID(); Serial.print("TFT ID = 0x"); Serial.println(ID, HEX); Serial.println(F("Calibrate for your Touch Panel")); if (ID == 0xD3D3) ID = 0x9486; //for 3.5" TFT LCD Shield , 0x9341 for 2.8" TFT LCD Shield tft.begin(ID); tft.setRotation(1); //0-PORTRAIT 1-PAYSAGE 2-REVERSE PORTRAIT 3-REVERSE PAYSAGE //tft.setFont(&FreeSmallFont); /*if (!SD.begin(SD_CS)) { Serial.println(F("initialization failed!")); return; }*/ currentPage = MENU; // Indicates that we are at Home Screen Serial.println("Home Page"); } bool down ; void loop(void) { switch (currentPage) { case MENU: //Menu page if (currentPage != oldPage) drawMenuScreen(); page1_btn.press(down && page1_btn.contains(pixel_x, pixel_y)); page2_btn.press(down && page2_btn.contains(pixel_x, pixel_y)); page3_btn.press(down && page3_btn.contains(pixel_x, pixel_y)); if (page1_btn.justReleased()) page1_btn.drawButton(); if (page2_btn.justReleased()) page2_btn.drawButton(); if (page3_btn.justReleased()) page3_btn.drawButton(); if (page1_btn.justPressed()) { page1_btn.drawButton(true); currentPage = SENSOR; } if (page2_btn.justPressed()) { page2_btn.drawButton(true); currentPage = CMD; } if (page3_btn.justPressed()) { page3_btn.drawButton(true); currentPage = PARAM; } break; case SENSOR: if (currentPage != oldPage) drawSensorScreen(); readSensor(); updateTemp(); menu_btn.press(down && menu_btn.contains(pixel_x, pixel_y)); info_btn.press(down && info_btn.contains(pixel_x, pixel_y)); if (menu_btn.justReleased()) menu_btn.drawButton(); if (info_btn.justReleased()) info_btn.drawButton(); if (menu_btn.justPressed()) { menu_btn.drawButton(true); currentPage = MENU; } if (info_btn.justPressed()) { info_btn.drawButton(true); currentPage = INFO; } break; case CMD: if (currentPage != oldPage) drawCmdScreen(); on_btn.press(down && on_btn.contains(pixel_x, pixel_y)); off_btn.press(down && off_btn.contains(pixel_x, pixel_y)); back_btn.press(down && back_btn.contains(pixel_x, pixel_y)); if (back_btn.justReleased()) back_btn.drawButton(); if (on_btn.justReleased()) on_btn.drawButton(); if (off_btn.justReleased()) off_btn.drawButton(); if (back_btn.justPressed()) { back_btn.drawButton(true); currentPage = MENU; } if (on_btn.justPressed()) { on_btn.drawButton(true); tft.fillRect(tft.width() / 2. - (btnWidth + 40) / 2, btnY - 4 * margin - (btnHeight) / 2 - (btnHeight + 40), btnWidth + 40, btnHeight + 40, GREEN); Serial.println(F("Set ON")); } if (off_btn.justPressed()) { off_btn.drawButton(true); tft.fillRect(tft.width() / 2. - (btnWidth + 40) / 2, btnY - 4 * margin - (btnHeight) / 2 - (btnHeight + 40), btnWidth + 40, btnHeight + 40, RED); Serial.println(F("Set OFF")); } break; case PARAM: //consigne if (currentPage != oldPage) drawParamScreen(); plus_btn.press(down && plus_btn.contains(pixel_x, pixel_y)); minus_btn.press(down && minus_btn.contains(pixel_x, pixel_y)); ok_btn.press(down && ok_btn.contains(pixel_x, pixel_y)); cncl_btn.press(down && cncl_btn.contains(pixel_x, pixel_y)); if (plus_btn.justReleased()) plus_btn.drawButton(); if (minus_btn.justReleased()) minus_btn.drawButton(); if (ok_btn.justReleased()) ok_btn.drawButton(); if (cncl_btn.justReleased()) cncl_btn.drawButton(); if (plus_btn.justPressed()) { plus_btn.drawButton(true); parameter += 1; Serial.print(F("Consigne : ")); Serial.println(parameter); drawTextInRect(tft.width() / 2., 60 + 3 * 4 + 6 * 8, parameter, 4, RED, BLACK); } if (minus_btn.justPressed()) { minus_btn.drawButton(true); parameter -= 1; Serial.print(F("Consigne : ")); Serial.println(parameter); drawTextInRect(tft.width() / 2., 60 + 3 * 4 + 6 * 8, parameter, 4, RED, BLACK); } if (ok_btn.justPressed()) { ok_btn.drawButton(true); Serial.println(F("Valider")); validateScreen(); delay(1000); currentPage = MENU; } if (cncl_btn.justPressed()) { cncl_btn.drawButton(true); parameter = old_parameter; Serial.println(F("Annule ")); cancelScreen(); delay(1000); currentPage = MENU; } break; case INFO: if (currentPage != oldPage) drawInfoScreen(); if (Touch_getXY()) { currentPage = MENU; } break; } if (oldPage == currentPage){ down = Touch_getXY(); }else{ down=false; } } /************************************************************************************ SCREENS DEFINTION ************************************************************************************/ void drawMenuScreen() { tft.fillScreen(BLACK); tft.setTextSize(2); // Title tft.setTextColor(WHITE, BLACK); tft.setCursor(0, 10); tft.print("Exemple de menu"); // Prints the string on the screen tft.drawLine(0, 32, tft.width() * 0.6, 32, DARKGREEN); // Draws the red line tft.setTextColor(WHITE, BLACK);//((255, 255, 255), (0,0,0)); tft.setCursor(0, 80); tft.setTextColor(GREEN, BLACK);//((255, 255, 255), (0,0,0)); // Button page1_btn.initButton(&tft, tft.width() / 2. , tft.height() / 2. - (1.*btnHeight + margin), 2 * btnWidth, btnHeight, WHITE, GREEN, BLACK, "SENSOR", 2); page2_btn.initButton(&tft, tft.width() / 2., tft.height() / 2., 2 * btnWidth, btnHeight, WHITE, GREEN, BLACK, "COMMAND", 2); page3_btn.initButton(&tft, tft.width() / 2., tft.height() / 2. + (1.*btnHeight + margin), 2 * btnWidth, btnHeight, WHITE, GREEN, BLACK, "PARAMETER", 2); page1_btn.drawButton(false); page2_btn.drawButton(false); page3_btn.drawButton(false); //Button frame tft.drawRoundRect(tft.width() / 2. - 1.5 * btnWidth, tft.height() / 2. - (1.5 * btnHeight + 2 * margin), 2 * btnWidth + btnWidth, 3 * btnHeight + 4 * margin, 10, GREEN); oldPage = currentPage; } void readSensor() { temp0 = temp0 + random(-25, 25) / 10; temp1 = temp1 + random(-25, 25) / 10; temp2 = temp2 + random(-25, 25) / 10; temp3 = temp3 + random(-25, 25) / 10; } void drawSensorScreen() { tft.fillScreen(BLACK); tft.drawLine(tft.width() / 2., 0, tft.width() / 2., tft.height(), WHITE); tft.drawLine(0, tft.height() / 2., tft.width(), tft.height() / 2., WHITE); updateTemp(); tft.setTextSize(1); // bouton centré X,Y menu_btn.initButton(&tft, tft.width() / 2. - btnWidth - margin , tft.height() - btnHeight / 2., btnWidth, btnHeight, WHITE, GREY, BLACK, "MENU", 2); info_btn.initButton(&tft, tft.width() / 2. + btnWidth + margin, tft.height() - btnHeight / 2., btnWidth, btnHeight, WHITE, GREY, BLACK, "INFO", 2); menu_btn.drawButton(false); info_btn.drawButton(false); oldPage = currentPage; } void updateTemp() { //temp1 if (parameter < temp0) { //(abs(parameter-temp0)<1){ drawTextInRect(tft.width() / 4., tft.height() / 4., temp0, 5, GREEN, 255); } else { drawTextInRect(tft.width() / 4., tft.height() / 4., temp0, 5, RED, 255); } drawTextInRect(3 * tft.width() / 4., tft.height() / 4., temp1, 5, RED, 255); drawTextInRect(tft.width() / 4., 3 * tft.height() / 4., temp2, 5, RED, 255); drawTextInRect(3 * tft.width() / 4., 3 * tft.height() / 4., temp3, 5, RED, 255); } void drawCmdScreen() { tft.setRotation(1); //PORTRAIT tft.setTextSize(1); tft.fillScreen(BLACK); back_btn.initButton(&tft, 60 , 20, btnWidth, btnHeight, BLACK, BLACK, WHITE, "<- Back", 2); // bouton centré X,Y on_btn.initButton(&tft, tft.width() / 2. - btnWidth / 2. , btnY, btnWidth, btnHeight, WHITE, GREEN, BLACK, "ON", 2); off_btn.initButton(&tft, tft.width() / 2. + btnWidth / 2. + margin, btnY, btnWidth, btnHeight, WHITE, RED, BLACK, "OFF", 2); back_btn.drawButton(false); on_btn.drawButton(false); off_btn.drawButton(false); tft.fillRect(tft.width() / 2. - (btnWidth + 40) / 2, btnY - 4 * margin - (btnHeight) / 2 - (btnHeight + 40), btnWidth + 40, btnHeight + 40, RED); oldPage = currentPage; } void validateScreen() { tft.setRotation(1); tft.fillScreen(BLACK); tft.setTextSize(5); tft.setTextColor(BLACK, GREEN); tft.setCursor(tft.width() / 2. - 6 * 6 * 4, tft.height() / 2. - 1 * 6); tft.print(" VALIDE "); } void cancelScreen() { tft.setRotation(1); tft.fillScreen(BLACK); tft.setTextSize(5); tft.setTextColor(BLACK, RED); tft.setCursor(tft.width() / 2. - 6 * 6 * 4, tft.height() / 2. - 1 * 6); tft.print(" ANNULE "); } void drawParamScreen() { tft.setRotation(1); tft.fillScreen(BLACK); //Title tft.setTextSize(3); tft.setTextColor(WHITE, BLACK); tft.setCursor(tft.width() / 2. - 9 * 3 * 3, 50); tft.print("Parameter"); ok_btn.initButton(&tft, 2 + btnWidth / 2., btnHeight / 2. + margin, btnWidth, btnHeight, WHITE, DARKGREEN, BLACK, "Valider", 2); cncl_btn.initButton(&tft, tft.width() - btnWidth / 2 - 2, btnHeight / 2. + margin, btnWidth, btnHeight, WHITE, RED, BLACK, "Annuler", 2); ok_btn.drawButton(false); cncl_btn.drawButton(false); drawTextInRect(tft.width() / 2., 60 + 3 * 4 + 6 * 8, parameter, 4, RED, BLACK); plus_btn.initButton(&tft, tft.width() / 2. - btnWidth / 2. , 60 + 3 * 4 + 6 * 8 + (btnWidth - 30), btnWidth - 20, btnWidth - 30, WHITE, GREEN, BLACK, "+", 5); minus_btn.initButton(&tft, tft.width() / 2. + btnWidth / 2. + margin, 60 + 3 * 4 + 6 * 8 + (btnWidth - 30), btnWidth - 20, btnWidth - 30, WHITE, GREEN, BLACK, "-", 5); plus_btn.drawButton(false); minus_btn.drawButton(false); oldPage = currentPage; } void drawInfoScreen() { tft.setRotation(1); tft.fillScreen(BLACK);//(100, 155, 203) tft.drawRoundRect(10, 50, tft.width() - 20, tft.height() - 60, 5, GREEN); //tft.fillRect (10, 10, 60, 36); tft.setTextSize(2); tft.setTextColor(WHITE, BLACK); tft.setCursor(70, 18); tft.print("Information sur l'exemple"); tft.setTextSize(2); tft.setTextColor(WHITE, BLACK); tft.setCursor(25, 80); tft.print("En cas de probleme avec ce tutoriel,\n n'hesitez pas a nous contacter"); tft.setCursor(20, 245); tft.print("www.aranacorp.com" ); oldPage = currentPage; } /************************************************************************************ UTILITY FUNCTION *************************************************************************************/ void drawTextInRect(int x, int y, long temp, int tsize, unsigned int fColor, unsigned int bColor) { int marg = 10; char buf[12]; int nbChar = strlen(itoa(temp, buf, 10)) + 2; if (bColor != 255) tft.fillRect(x - nbChar * 3 * tsize - marg, y - nbChar * 1 * tsize - marg, nbChar * 6 * tsize + 2 * marg, nbChar * 2 * tsize + 2 * marg, bColor); tft.setTextSize(tsize); tft.setTextColor(fColor, BLACK); //tft.setCursor(x-strlen(*text)*3*tsize+marg, y+rheight/2.+marg); tft.setCursor(x - nbChar * 3 * tsize, y - nbChar * 1 * tsize); tft.print(temp); //while(*text) tft.print(*text++); tft.write(0xF7); tft.print("C"); } bool Touch_getXY(void) { p = ts.getPoint(); pinMode(YP, OUTPUT); //restore shared pins pinMode(XM, OUTPUT); digitalWrite(YP, HIGH); digitalWrite(XM, HIGH); bool pressed = (p.z > MINPRESSURE && p.z < MAXPRESSURE); if (pressed) { if (tft.width() <= tft.height()) { //Portrait pixel_x = map(p.x, TS_LEFT, TS_RT, 0, tft.width()); //.kbv makes sense to me pixel_y = map(p.y, TS_TOP, TS_BOT, 0, tft.height()); } else { pixel_x = map(p.y, TS_TOP, TS_BOT, 0, tft.width()); pixel_y = map(p.x, TS_RT, TS_LEFT, 0, tft.height()); } } return pressed; }
Aplicaciones
- Crear una interfaz gráfica para impulsar su proyecto Arduino
Fuentes
- https://www.arduino.cc/en/reference/SD
- https://github.com/adafruit/Adafruit-GFX-Library
- https://github.com/prenticedavid/MCUFRIEND_kbv
Encuentre otros tutoriales y ejemplos en el generador de código automático
Arquitecto de Código