TFT LCD Touch 2.4» Shield para Arduino UNO

¡Tuto nuevo! Hoy os traigo un shield para Arduino UNO.

Siempre he dicho que todo proyecto por simple o complejo que sea, debería llevar una pantalla, y si esa pantalla es LCD TFT de 2.4», táctil, con lector de tarjetas MicroSD y encima, en formato Shield para Arduino UNO,estamos ante un producto de infinitas posibilidades 🙂

Así es el siguiente módulo que nos han cedido desde nuestra tienda colaboradora Electrohobby.

TFTFrontBack-pComo hemos dicho, este shield para Arduino UNO, tiene 3 funciones principales:

Pantalla LCD para visualización de gráficos o texto
Panel táctil sobre la pantalla LCD
Lector de tarjetas MicroSD para datalogging o almacenamiento de imágenes.

Así que después del salto, veremos como conectar, enchufar y usar este fantástico modulo 🙂

Las características principales del shield son las siguientes:

  • Pantalla LCD TFT de 2,4»
  • Chip controlador LCD ILI9341 (Enlace para descargar el Datasheet ILI9341)
  • 320×240 pixeles de resolución y 262.000 colores
  • Panel táctil resistivo de 4 hilos
  • Compatible con tensión de 5V ya que dispone de regulador de tensión en placa
  • Conexión Shield para Arduino UNO
  • Tamaño: 71 x 52 x 7 mm (L x A x H)

Conociendo las características del módulo, podemos pasar a la acción.

Conexión del shield con nuestro Arduino UNO

Al tener formato Shield para Arduino UNO, tendrá todos los pines conectados a nuestro Arduino, por lo que ya nos preocuparemos en el código de los sketchs de configurar los pines que utiliza.
Solo hay una manera de pincharlo en el Arduino UNO, haciendo coincidir los pines macho del shield con los pines hembra del Arduino

Como se aprecia en la imagen, debemos tener cuidado al pinchar el módulo, ya que éste cubre la totalidad de la placa Arduino, por lo que es recomendable cubrir el conector USB del Arduino, con una cinta aislante, para evitar que se produzca algún cortocircuito al pinchar el shield a fondo.

Procedemos a colocar nuestro Shield + Arduino en la tabla de Arduino UNO R3 de Electrohobby para poder trabajar con mayor comodidad y enchufamos el conector USB al Arduino.

TablaTFT-p

Configuración del shield y librerías necesarias

Antes de empezar, aclarar que vamos a utilizar la versión del IDE de Arduino 1.0.

Para poder usar todas las funciones del shield, necesitamos varias librerías con algunas modificaciones de código que he tenido que hacerles para poder utilizar el shield, por lo que no disponen de los ejemplos oficiales que traen las librerías por defecto, sino unos ejemplos que he preparado para poder poner en funcionamiento el shield TFT:

  •  Librería Adafruit_TFTLCD: Esta es la librería Adafruit_TFTLCD original, que incluye drivers de varias pantallas con diferentes chips (ILI9325, ILI9341,HX8347G, HX8357) , por lo que debemos seleccionar el chip correcto en el código del sketch para poder visualizar texto, gráficos e imágenes en nuestra pantalla LCD.
  • Librería Adafruit_GFX: Esta librería de Adafruit es la que nos proporcionará el código necesario para la realización de gráficos en la pantalla (puntos, círculos, lineas, etc)
  • Librería TouchScreen: Librería que se encarga de traducir e interpretar las coordenadas, para poder trabajar con el panel táctil resistivo de 4 hilos que incluye nuestro shield.
  • Librería SD: Es la librería que incluye el entorno de Arduino por defecto para el acceso y comunicación con tarjetas SD, o en nuestro caso, MicroSD.
  • Librería SPI: Las tarjetas SD o MicroSD se comunican mediante el bus SPI con nuestro Arduino, por lo que es necesaria esta librería para poder acceder a la tarjeta.

Descarga las librerías desde aqui: LIBRERÍAS PARA TFT SD 2.4″ SHIELD 

Es muy importante descargar TODAS las librerías y colocarlas correctamente en la carpeta de librerías del IDE de Arduino (Arduino>Libraries), si no están todas las librerías y bien situadas, el shield TFT LCD no funcionará.

UbicacionLibrerias

En el caso de utilizar la última versión de Arduino, 1.6.3, podemos obviar la colocación de las librerías SPI y SD ya que el propio entorno de Arduino trae una versión más actualizada de estas, por lo que podemos utilizar la versión de Arduino 1.0 o la última versión.

Colocadas las librerías, abrimos el entorno de Arduino 1.0  y procedemos a ver los 3 ejemplos de las 3 funciones principales de este shield.

1. Visualización de gráficos y texto en LCD

Nos dirigimos a File > Examples > Adafruit_TFTLCD > Ejemplo_Grafico
Y obtenemos el siguiente código:

#include <Adafruit_GFX.h> // Libreria de graficos
#include <Adafruit_TFTLCD.h> // Libreria de LCD 

// Pines de conexion del LCD
#define LCD_CS A3 // Chip Select - Pin analogico 3
#define LCD_CD A2 // Command/Data - Pin Analogico 2
#define LCD_WR A1 // LCD Write - Pin Analogico 1
#define LCD_RD A0 // LCD Read - Pin Analogico 0
#define LCD_RESET A4 // LCD Reset - Pin Analogico 4

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); // Instancia del LCD 

#define BLACK 0x0000 // Definimos los colores para poder referirnos a ellos con su nombre
#define RED 0xF800 // en lugar de usar el código hexadecimal de cada uno.
#define GREEN 0x07E0
#define WHITE 0xFFFF
#define BLUE 0x001F
#define CYAN 0x07FF
#define YELLOW 0xFFE0
#define MAGENTA 0xF81F

void setup(void)
{
 tft.begin(0x9341); // Iniciamos el LCD especificando el controlador de nuestro LC. En este caso el ILI9341.
 // Otros controladores: 0x9325, 0x9328,0x7575, 0x9341, 0x8357.

 tft.fillScreen(BLACK); // Colocamos el fondo del LCD en Negro
}

void loop(void)
{
 tft.setRotation(0); // Establecemos la posición de la pantalla Vertical u Horizontal

 tft.setCursor(40, 10); // Situamos el cursor en la posicion del LCD deseada,
 // (X, Y) siendo X el ancho (240 px max.) e Y el alto (320 px max.) 

 tft.setTextSize(5); // Definimos tamaño del texto. (Probado tamaños del 1 al 10)

 tft.setTextColor(CYAN); // Definimos el color del texto 

 tft.println("Texto"); // Escribimos nuestro texto en el LCD. Realizará un salto de linea
 // automatico si el texto es mayor que el tamaño del LCD

 tft.drawLine(20, 65, 200, 70, GREEN); // Dibujamos una linea (Punto inicio X, Punto inicio Y, Punto final X, Punto final Y, Color)

 int X = tft.width(); // Almacenamos en la variable entera X el ancho del LCD en pixeles mediante tft.width();
 int Y = tft.height(); // Almacenamos en la variable entera Y el alto del LCD en pixeles mediante tft.height();

 tft.setCursor(15, 90); // Situamos el cursor en una nueva posicion del LCD

 tft.setTextSize(3); // Definimos tamaño del texto.

 tft.setTextColor(RED); // Definimos el color del texto 

 tft.print("X="); tft.print(X, DEC); // Imprimimos por pantalla el valor de las variables en decimal
 tft.print(" Y="); tft.println(Y, DEC);

 tft.drawRect(20, 125, 200, 25, YELLOW); // Dibujamos un cuadrado/rectangulo sin color de relleno
 // (Punto inicial X, Punto inicial Y, Longitud X,Longitud Y, Color)

 tft.fillRect(20, 165, 60, 60, BLUE); // Dibujamos un cuadrado/rectangulo relleno de color
 //(Punto inicial X, Punto inicial Y, Longitud X,Longitud Y, Color)

 tft.drawCircle(120, 195, 30, WHITE); // Dibujamos un circulo sin color de relleno
 //(Punto inicial X, Punto inicial Y, Radio del circulo, Color)

 tft.fillCircle(120, 195, 20, WHITE); // Dibujamos un circulo relleno de color
 //(Punto inicial X, Punto inicial Y, Radio del circulo, Color) 

 tft.drawTriangle // Dibujamos un triangulo sin color de relleno
 (190, 163, // (Vertice superior X, Vertice superior Y,
 160, 225, // Vertice inferior izquierdo X, vertice inferior izquierdo Y,
 222, 225, CYAN); // Vertice inferior derecho X, Vertice inferior derecho Y, Color)

 tft.fillTriangle // Dibujamos un triangulo relleno de color
 (190, 240, // (Vertice superior X, Vertice superior Y,
 160, 302, // Vertice inferior izquierdo X, vertice inferior izquierdo Y,
 222, 302, MAGENTA); // Vertice inferior derecho X, Vertice inferior derecho Y, Color)

 tft.drawRoundRect(20, 245, 130, 60, 20, RED); // Dibujamos un cuadrado/rectangulo con los bordes redondeados sin color de relleno
 // (Punto inicial X, Punto inicial Y, Longitud X,Longitud Y, Radio de los vertices, Color)

 tft.fillRoundRect(35, 255, 100, 40, 15, YELLOW); // Dibujamos un cuadrado/rectangulo con los vertices redondeados relleno de color
 // (Punto inicial X, Punto inicial Y, Longitud X,Longitud Y, Radio de los vertices, Color)
 delay(10000);
}

Analizamos un poco el código por partes y vemos que tenemos las librerías necesarias para trabajar con el LCD y la librería para la realización de gráficos,  la declaración de los pines necesarios, y la instancia del LCD con los pines correspondientes. Utilizaremos para el LCD todos pines analógicos, del A0 al A4, puesto que es un shield no podemos modificarlos.

#include <Adafruit_GFX.h> // Libreria de graficos
#include <Adafruit_TFTLCD.h> // Libreria de LCD

// Pines de conexion del LCD
#define LCD_CS A3  // Chip Select - Pin analogico 3
#define LCD_CD A2 // Command/Data - Pin Analogico 2
#define LCD_WR A1 // LCD Write - Pin Analogico 1
#define LCD_RD A0 // LCD Read - Pin Analogico 0
#define LCD_RESET A4 // LCD Reset - Pin Analogico 4

// Instancia del LCD
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

Back

Posteriormente en el código tenemos la declaración de los colores que utilizaremos en el LCD para dar color a las formas, texto y fondo.

Entramos en el void setup(void) y vemos que la primera linea, es la llamada a la función tft.begin(0x9341);, que iniciará el LCD y le indicará que drivers y registros debe utilizar, por lo que debemos colocar en la variable de esta función, el driver correspondiente a nuestro LCD.

void setup(void)
{
  tft.begin(0x9341); // Especificamos el controlador de nuestro LCD.
                        En este caso el ILI9341.
// Otros controladores: 0x9325, 0x9328,0x7575, 0x9341, 0x8357.

 tft.fillScreen(BLACK); // Colocamos el fondo del LCD en Negro
}

La librería Adafruit_TFTLCD incluye drivers para los siguientes chips:
– ILI932X → 0x9325 / 0x9328
– HX8347G → 0x7575
– ILI9341 → 0x9341 (En nuestro caso este es el driver correcto)
– HX8357 → 0x8357

Y seguido vemos la linea de código que hace la llamada a la función que «pintará» el fondo de nuestro LCD, tft.fillScreen(COLOR);, del color que elijamos, en mi caso negro.
Esto se realiza en el void setup(void); para que solo lo realice una vez.

Vamos al void loop() y vemos todas las funciones que incluye la librería para la realización de gráficos, cuadrados, círculos, líneas, y la escritura de texto, con todos sus parámetros.
Vamos a analizar las primeras lineas y funciones:


void loop()
{
tft.setRotation(0); // Establecemos la pantalla Vertical u Horizontal
tft.setCursor(40, 10);  // Situamos el cursor
tft.setTextSize(5); // Definimos tamaño del texto.
tft.setTextColor(CYAN); // Definimos el color del texto
tft.println("Texto"); // Escribimos nuestro texto en el LCD
tft.drawLine(20, 65, 200, 70, GREEN); // Dibujamos una linea
int X = tft.width(); // Almacenamos en X el ancho del LCD en px
int Y = tft.height(); // Almacenamos en Y el alto del LCD en px
tft.print("X=");
tft.print(X, DEC);  // Imprimimos el valor de las variables en decimal
tft.print(" Y=");
tft.println(Y, DEC);
tft.drawRect(20, 125, 200, 25, YELLOW); // Dibujamos un cuadrado
tft.fillRect(20, 165, 60, 60, BLUE); // Dibujamos un cuadrado relleno
tft.drawCircle(120, 195, 30, WHITE); // Dibujamos un circulo
tft.fillCircle(120, 195, 20, WHITE); // Dibujamos un circulo relleno
}

Si cargamos el Ejemplo_Grafico nuestro Arduino + LCD veremos lo siguiente:

EjemploGrafico-p

En el archivo Ejemplo_Grafico.ino que cargamos en nuestro Arduino, al final del código están todas las funciones con las descripciones de los parámetros necesarios para la realización de gráficos y texto a modo de «chuleta». Podremos dibujar a placer en nuestro LCD.

2. Utilizando el panel táctil del Shield TFT LCD 

Este shield incluye sobre la pantalla un panel táctil resistivo de 4 hilos que nos permitirá obtener las coordenadas X e Y donde pulsemos sobre la pantalla, y por tanto, poder realizar acciones que van desde pintar sobre nuestro LCD como si fuera un «lienzo digital» o dibujar botones en nuestro LCD que al pulsar sobre ellos realicen una determinada acción.

¿Qué significa que tiene un panel táctil resistivo? 

Pues bien, una pantalla táctil resistiva consiste principalmente en dos capas separadas de material plástico conductor con una determinada resistencia a la corriente eléctrica, que al pulsar en un punto determinado de la capa exterior, ésta hace contacto con la capa interior y midiendo la resistencia calcula el punto exacto donde se ha pulsado en un eje de coordenadas X e Y.

Algunos tipos de pantallas resistivas permiten también la medición de un eje Z, es decir, la presión que se está realizando sobre el punto en concreto de la pantalla.

Para poder utilizar el panel táctil de nuestro shield TFT,  nos dirigimos en el IDE de Arduino 1.0 a File > Examples > Adafruit_TFTLCD > Ejemplo_Tactil


#include <Adafruit_GFX.h> // Libreria de graficos
#include <Adafruit_TFTLCD.h> // Libreria de LCD
#include <TouchScreen.h> // Libreria del panel tactil

 // Pines necesarios para los 4 pines del panel tactil
#define YP A1 // Pin analogico A1 para ADC
#define XM A2 // Pin analogico A2 para ADC
#define YM 7
#define XP 6 

// Definimos la presion máxima y minima que podemos realizar sobre el panel
#define MINPRESSURE 1
#define MAXPRESSURE 1000

// Para mejor precision de la presion realizada, es necesario
// medir la resistencia entre los pines X+ y X-.
// En Shield TFT 2.4" LCD se mide entre los pines A2 y 6
// Instancia del panel tactil (Pin XP, YP, XM, YM, Resistencia del panel)
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 364); 

short TS_MINX = 150; // Coordenadas del panel tactil para delimitar
short TS_MINY = 120; // el tamaño de la zona donde podemos presionar
short TS_MAXX = 850; // y que coincida con el tamaño del LCD
short TS_MAXY = 891; 

#define LCD_CS A3 // Definimos los pines del LCD
#define LCD_CD A2 // para poder visualizar elementos graficos
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4

#define BLACK 0x0000 // Definimos los colores
#define BLUE 0x001F // que utilizaremos para
#define RED 0xF800 // el texto y los elementos graficos
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); // Instancia LCD 

int X; // Variables que almacenaran la coordenada
int Y; // X, Y donde presionemos y la variable Z
int Z; // almacenara la presion realizada

int a = 0; // variable "flag" para control rebotes

#define ledA 5 // Anodo del led conectado pin digital 5
#define ledK 3 // Catodo del led conectado pin digital 3

void setup(void)
{
 tft.begin(0x9341); // Iniciamos el LCD especificando el controlador ILI9341. 

 tft.fillScreen(CYAN); // Pintamos la pantalla de Rojo

 pinMode(13, OUTPUT);
 pinMode(ledA,OUTPUT);
 pinMode(ledK, OUTPUT);

 tft.drawRect(40, 20, 160, 60, RED); // Dibujamos un "boton"

 tft.setCursor(60,35); // Colocamos el cursor
 tft.setTextSize(4); // Especificamos el tamaño del texto
 tft.setTextColor(RED); // Definimos el color del texto
 tft.println("BOTON"); // Escribimos por pantalla
}

void loop()
{
 lecturaPanel(); // Realizamos lectura del panel para detectar presion y coordenadas

 // Si la pulsación del eje X se produce entre los puntos 40 y 160
 // Y la pulsacion del eje Y se produce entre los puntos 20 y 60
 // Y la presión realizada esta entre el margen determinado
 if((X > 40 && X < 200) && (Y > 20 && Y < 60) && (Z > MINPRESSURE && Z < MAXPRESSURE))
 {
 if (a == 0) // Si la variable flag esta en 0
 {
 tft.fillRect(40, 20, 160, 60, RED); // Dibujamos nuestro boton relleno de color

 tft.setCursor(70,25); // Colocamos el cursor
 tft.setTextSize(3); // Especificamos el tamaño del texto
 tft.setTextColor(WHITE); // Definimos el color del texto
 tft.println("BOTON"); // Escribimos por pantalla
 tft.setCursor(60,50); // Colocamos el cursor
 tft.println("PULSADO"); // Escribimos por pantalla 

 //AQUI REALIZAMOS LA ACCION QUE DESEEMOS: ENCENDER EL LED
 digitalWrite(ledA, HIGH);
 digitalWrite(ledK, LOW);

 a = 1; // Ponemos la variable flag en 1
 delay(150);
 }
 else if (a == 1) // Si la variable flag esta en 1
 {
 tft.fillRect(41, 21, 158, 58, CYAN); // Dibujamos el fondo de nuestro boton en cyan

 tft.setCursor(60,35); // Colocamos el cursor
 tft.setTextSize(4); // Especificamos el tamaño del texto
 tft.setTextColor(RED); // Definimos el color del texto
 tft.println("BOTON"); // Escribimos por pantalla

 //AQUI REALIZAMOS LA ACCION QUE DESEEMOS: APAGAR EL LED
 digitalWrite(ledA,LOW);
 digitalWrite(LedK, LOW);

 a = 0; // Ponemos la variable flag en 0 para evitar los rebotes
 delay(150);
 }
 }
}

void lecturaPanel()
{
 digitalWrite(13, HIGH);
 TSPoint p = ts.getPoint(); // Realizamos lectura de las coordenadas
 digitalWrite(13, LOW);

 pinMode(XM, OUTPUT); // La librería utiliza estos pines como entrada y salida
 pinMode(YP, OUTPUT); // por lo que es necesario declararlos como salida justo
 // despues de realizar una lectura de coordenadas. 

 // Mapeamos los valores analogicos leidos del panel tactil (0-1023)
 // y los convertimos en valor correspondiente a la medida del LCD 320x240
 X = map(p.x, TS_MAXX, TS_MINX, tft.width(), 0);
 Y = map(p.y, TS_MAXY, TS_MINY, tft.height(), 0);
 Z = p.z;
}

Este sketch lo he preparado específicamente para poder ver todas las funciones de la pantalla táctil de un modo simple.

Analizamos el código por partes:


#include <Adafruit_GFX.h> // Libreria de graficos
#include <Adafruit_TFTLCD.h> // Libreria de LCD
#include <TouchScreen.h> // Libreria del panel tactil

// Pines necesarios para los 4 pines del panel tactil
#define YP A1 // Pin analogico A1 para ADC
#define XM A2 // Pin analogico A2 para ADC
#define YM 7
#define XP 6

// Definimos la presion máxima y minima que podemos realizar 
#define MINPRESSURE 1
#define MAXPRESSURE 1000

// Para mejor precision de la presion realizada, es necesario
// medir la resistencia entre los pines X+ y X-.
// En Shield TFT 2.4" LCD se mide entre los pines A2 y 6
// Instancia del panel tactil (Pin XP, YP, XM, YM, Resistencia eje X)
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 364);

int X; // Variables que almacenaran la coordenada
int Y; // X, Y donde presionemos y la variable Z
int Z; // almacenara la presion realizada

#define ledA 5 // Pin digital 5 - Anodo del led
#define ledK 3 // Pin digital 3 - Catodo del led

Además de las librerías de los gráficos y del LCD que hemos visto anteriormente, debemos incorporar la librería TouchScreen.h para poder «leer» las pulsaciones que realizamos sobre el panel táctil.

Vemos que 2 de los pines del panel táctil coinciden con 2 pines del LCD, por lo que posteriormente veremos que debemos declarar esos pines como entrada o salida en medio del sketch en función de quien de los dos elementos los vaya a utilizar, pero para el panel táctil es necesario que estos dos pines estén conectados en dos pines de Arduino que dispongan de ADC  (conversor analógico-digital) para poder obtener las lecturas del panel.

Tenemos dos variables MINPRESSURE y MAXPRESSURE que podemos modificar al gusto para establecer la presión mínima o máxima que debemos hacer sobre la pantalla para detectar la pulsación, puesto que este panel táctil también es capaz de detectar «el eje Z».

Seguido, la declaración de la instancia del panel táctil, donde apreciamos que debemos colocar los pines que utilizará el panel táctil previamente declarados y en último lugar, debemos colocar el valor de la resistencia a la corriente eléctrica que tiene el panel. Cuanto más preciso sea este valor respecto a la resistencia real que tiene nuestro panel, mejor respuesta dará a la hora de detectar las pulsaciones.


TouchScreen ts = TouchScreen(XP, YP, XM, YM, 364);

Bien, para medir la resistencia del eje X de nuestro panel, si miramos los pines del panel táctil, vemos que el pin XP = Digital 6 y pin XM = Analógico 2, por lo que procedemos a hacer la medición de la resistencia con un multímetro de la siguiente manera:

Resistencia-p

En mi caso, la resistencia es de 364 Ω y lo colocamos en la instancia del Touchscreen.

Si queremos visualizar elementos en la pantalla LCD, debemos declarar, igual que en el ejemplo gráfico, los pines de utilización del LCD, y su instancia, etc.

El código importante de este sketch es la siguiente parte, la función lecturaPanel();


void lecturaPanel()
{
 digitalWrite(13, HIGH);
 TSPoint p = ts.getPoint(); // Realizamos lectura de las coordenadas
 digitalWrite(13, LOW);

 // La librería utiliza estos pines como entrada y salida
 pinMode(XM, OUTPUT); // por lo que es necesario declararlos
 pinMode(YP, OUTPUT); // como salida justo despues de realizar una
                      //  lectura de coordenadas. 

 // Mapeamos los valores analogicos leidos del panel tactil (0-1023)
 // y los convertimos en valor correspondiente a la medida del LCD
 X = map(p.x, TS_MAXX, TS_MINX, tft.width(), 0);
 Y = map(p.y, TS_MAXY, TS_MINY, tft.height(), 0);
 Z = p.z;
}

Como vemos, realizamos la lectura constantemente del panel, para poder detectar las pulsaciones en todo momento, por lo que debemos llamar a esta función desde el void loop().

La funcion ts.getPoint(); devuelve las coordenadas X e Y de la pulsación detectada, y utilizando estas coordenadas devueltas desde el ADC en un valor comprendido entre 0 y 1023
(8 bits), simplemente las mapeamos para convertir dicho valor en un valor comprendido entre 0 y 240 para el eje X, y un valor comprendido entre 0 y 320 para el eje Y, que corresponderían con el tamaño en pixeles de nuestra pantalla.

En el void loop(); simplemente dibujo un cuadrado con texto «Botón» que al pulsar en las coordenadas que se encuentran dentro de este cuadrado, cambie de color y muestre otro texto diferente y a su vez, encienda el led que tenemos conectado en el pin digital 5. Si volvemos a pulsar el botón, el led se apagará y el botón volverá a su «estado de reposo».

Este código realizará lo siguiente:

El otro ejemplo que incluye la librería, Ejemplo_Pintar, es una modificación y traducción de comentarios al español que he hecho del ejemplo TFTPaint de la librería de Adafruit, y que nos permite pintar con diferentes colores sobre el LCD como si fuera un «lienzo digital»:

3. Visualizando imagenes BMP en el LCD

Por último pero no menos importante, vamos a visualizar en el LCD, imágenes en formato BMP de 24 bits almacenadas en la tarjeta MicroSD del shield.

Como podemos observar en la siguiente imagen, el shield incluye un lector de tarjetas MicroSD conectado a los pines correspondientes al bus SPI, ya que utilizan este bus para comunicarse.
(Clic aqui para conocer como se comunican las tarjeta SD y Arduino)

Los pines del bus SPI se corresponden con los siguientes:

– Pin digital 10 – CS / SS (En librería Arduino SD / Este pin es configurable vía software)
– Pin digital 11 – MOSI
– Pin digital 12 – MISO
– Pin digital 13 – CLKSD-p

A tener en cuenta que las imágenes que almacenemos en nuestra MicroSD, deben ser imágenes en formato BMP de 24 bits y de tamaño máximo 320 x 240 pixeles.
Otro tipo de formatos de imagen NO los reconocerá.

Almacenamos varias imágenes en nuestra MicroSD recordando los nombres exactos que les dimos para cargarlas desde el código del sketch, e introducimos la MicroSD en nuestro Shield. 

Procedemos a cargar el ejemplo: File > Examples > Adafruit_TFTLCD > Ejemplo_ImagenesSD

Y obtenemos el siguiente código:


#include <Adafruit_GFX.h> // Libreria de graficos
#include <Adafruit_TFTLCD.h> // Libreria de LCD
#include <SD.h> // Libreria de tarjeta SD
#include <SPI.h> // Libreria bus SPI

#define LCD_CS A3 // Definimos los pines del LCD
#define LCD_CD A2 // para poder visualizar elementos graficos
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4

// Los pines del puerto SPI vienen configurados por libreria, por lo que
// solamente debemos colocar el pin correspondiente al Chip Select del
// bus SPI correspondiente a la conexion con la tarjeta SD
#define SD_CS 10

// En la tarjeta SD debemos colocar imagenes en formato BMP de 24 Bits!
// Otro tipo de formato de imagen no se puede visualizar por pantalla.

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); // Instancia LCD

void setup()
{
 Serial.begin(9600); // Iniciamos el puerto serie para comprobar
 // la comunicacion con la tarjeta microSD
 tft.reset();

 tft.begin(0x9341); // Iniciamos el LCD especificando el controlador ILI9341.

Serial.print(F("Inicializando tarjeta SD..."));

 if (!SD.begin(SD_CS)) // Si se produce un error al intentar acceder
 { // a la tarjeta SD, lo mostramos por el Serial Monitor
 Serial.println(F("Error!"));
 return;
 }
 Serial.println(F("OK!"));

}

void loop()
{
 tft.setRotation(0); // Establecemos la posicion de la pantalla Vertical

 bmpDraw("1.bmp", 0, 0); // Mostramos una imagen en las coordenadas 0,0

 delay(1000);

 tft.setRotation(3); // Establecemos la posicion de la pantalla Horizontal

 bmpDraw("2.bmp",0,0); // // Mostramos otra imagen en las coordenadas 0,0

 delay(1000);
}

// Esta funcion abre un archivo Windows bitmap (BMP) y lo muestra por
// pantalla en las coordenadas especificadas. Se puede acelerar el
// proceso de muestreo leyendo muchos pixeles a la vez en lugar de
// leer pixel a pixel, incrementando el tamaño de la siguiente variable
// BUFFPIXEL, utilizaremos mas memoria RAM del Arduino pero se realizará
// la carga de la imagen mas rapido.
// Un buffer de 20 pixeles es un valor equilibrado.

#define BUFFPIXEL 20

void bmpDraw(char *filename, int x, int y) {

File bmpFile;
 int bmpWidth, bmpHeight; // W+H in pixels
 uint8_t bmpDepth; // Bit depth (currently must be 24)
 uint32_t bmpImageoffset; // Start of image data in file
 uint32_t rowSize; // Not always = bmpWidth; may have padding
 uint8_t sdbuffer[3*BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
 uint16_t lcdbuffer[BUFFPIXEL]; // pixel out buffer (16-bit per pixel)
 uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
 boolean goodBmp = false; // Set to true on valid header parse
 boolean flip = true; // BMP is stored bottom-to-top
 int w, h, row, col;
 uint8_t r, g, b;
 uint32_t pos = 0, startTime = millis();
 uint8_t lcdidx = 0;
 boolean first = true;

if((x >= tft.width()) || (y >= tft.height())) return;

Serial.println();
 Serial.print(F("Loading image '"));
 Serial.print(filename);
 Serial.println('\'');
 // Open requested file on SD card
 if ((bmpFile = SD.open(filename)) == NULL) {
 Serial.println(F("File not found"));
 return;
 }

// Parse BMP header
 if(read16(bmpFile) == 0x4D42) { // BMP signature
 Serial.println(F("File size: ")); Serial.println(read32(bmpFile));
 (void)read32(bmpFile); // Read & ignore creator bytes
 bmpImageoffset = read32(bmpFile); // Start of image data
 Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
 // Read DIB header
 Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
 bmpWidth = read32(bmpFile);
 bmpHeight = read32(bmpFile);
 if(read16(bmpFile) == 1) { // # planes -- must be '1'
 bmpDepth = read16(bmpFile); // bits per pixel
 Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
 if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

goodBmp = true; // Supported BMP format -- proceed!
 Serial.print(F("Image size: "));
 Serial.print(bmpWidth);
 Serial.print('x');
 Serial.println(bmpHeight);

// BMP rows are padded (if needed) to 4-byte boundary
 rowSize = (bmpWidth * 3 + 3) & ~3;

// If bmpHeight is negative, image is in top-down order.
 // This is not canon but has been observed in the wild.
 if(bmpHeight < 0) {
 bmpHeight = -bmpHeight;
 flip = false;
 }

// Crop area to be loaded
 w = bmpWidth;
 h = bmpHeight;
 if((x+w-1) >= tft.width()) w = tft.width() - x;
 if((y+h-1) >= tft.height()) h = tft.height() - y;

// Set TFT address window to clipped image bounds
 tft.setAddrWindow(x, y, x+w-1, y+h-1);

for (row=0; row<h; row++) { // For each scanline...
 // Seek to start of scan line. It might seem labor-
 // intensive to be doing this on every line, but this
 // method covers a lot of gritty details like cropping
 // and scanline padding. Also, the seek only takes
 // place if the file position actually needs to change
 // (avoids a lot of cluster math in SD library).
 if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
 pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
 else // Bitmap is stored top-to-bottom
 pos = bmpImageoffset + row * rowSize;
 if(bmpFile.position() != pos) { // Need seek?
 bmpFile.seek(pos);
 buffidx = sizeof(sdbuffer); // Force buffer reload
 }

for (col=0; col<w; col++) { // For each column...
 // Time to read more pixel data?
 if (buffidx >= sizeof(sdbuffer)) { // Indeed
 // Push LCD buffer to the display first
 if(lcdidx > 0) {
 tft.pushColors(lcdbuffer, lcdidx, first);
 lcdidx = 0;
 first = false;
 }
 bmpFile.read(sdbuffer, sizeof(sdbuffer));
 buffidx = 0; // Set index to beginning
 }

// Convert pixel from BMP to TFT format
 b = sdbuffer[buffidx++];
 g = sdbuffer[buffidx++];
 r = sdbuffer[buffidx++];
 lcdbuffer[lcdidx++] = tft.color565(r,g,b);
 } // end pixel
 } // end scanline
 // Write any remaining data to LCD
 if(lcdidx > 0) {
 tft.pushColors(lcdbuffer, lcdidx, first);
 }
 Serial.print(F("Loaded in "));
 Serial.print(millis() - startTime);
 Serial.println(" ms");
 } // end goodBmp
 }
 }

bmpFile.close();
 if(!goodBmp) Serial.println(F("BMP format not recognized."));
}

// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(File f) {
 uint16_t result;
 ((uint8_t *)&result)[0] = f.read(); // LSB
 ((uint8_t *)&result)[1] = f.read(); // MSB
 return result;
}

uint32_t read32(File f) {
 uint32_t result;
 ((uint8_t *)&result)[0] = f.read(); // LSB
 ((uint8_t *)&result)[1] = f.read();
 ((uint8_t *)&result)[2] = f.read();
 ((uint8_t *)&result)[3] = f.read(); // MSB
 return result;
}

Vemos y analizamos por partes el código hasta el void loop()

En este caso, debemos añadir además de las librerías ya utilizadas para realización de gráficos y utilización del LCD, las librerías correspondientes a la tarjeta, <SD.h> y al bus SPI, <SPI.h>


#include <Adafruit_GFX.h> // Libreria de graficos
#include <Adafruit_TFTLCD.h> // Libreria de LCD
#include <SD.h> // Libreria de tarjeta SD
#include <SPI.h> // Libreria bus SPI

#define LCD_CS A3 // Definimos los pines del LCD
#define LCD_CD A2 // para poder visualizar elementos graficos
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4

// Los pines del puerto SPI vienen configurados por libreria, 
// debemos colocar el pin correspondiente al Chip Select del
// bus SPI correspondiente a la conexion con la tarjeta SD
#define SD_CS 10

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

Declaramos los pines del LCD del mismo modo que en el ejemplo gráfico y el TouchScreen.

Dado que los pines del zócalo de la tarjeta MicroSD del shield, están conectados a los pines del bus SPI de Arduino, no es necesario declararlos puesto que vienen declarados en la librería SPI de Arduino, solamente debemos declarar el pin CS que será el que determine la comunicación entre el maestro (Arduino) y el esclavo (MicroSD).
Por tanto, comprobamos y el shield tiene el pin CS en el correspondiente al pin Digital 10. 

void setup()
{
 Serial.begin(9600); // Iniciamos el puerto serie para comprobar 
                     // la comunicacion con la tarjeta microSD 

 tft.begin(0x9341); // Iniciamos el LCD controlador ILI9341.

 Serial.print(F("Inicializando tarjeta SD..."));

 if (!SD.begin(SD_CS)) // Si se produce un error al intentar acceder
 {                     // a tarjeta SD, mostramos por Serial Monitor
    Serial.println(F("Error!"));
    return;
 }
   Serial.println(F("OK!"));
}

Iniciamos el Serial Monitor para poder obtener resultados del proceso de comunicación con la tarjeta SD y comprobar que todo ha sido correcto.
Inicializamos el LCD con el driver correcto e intentamos acceder a la tarjeta SD mediante la funcion SD.begin(SD_CS); Si todo va bien, veremos un «OK!» en el Serial Monitor y podremos acceder a nuestro SD para visualizar nuestras imágenes.

Y por último, vemos el void loop()


void loop()
{
  tft.setRotation(0); // Establecemos posicion del LCD Vertical
  bmpDraw("1.bmp", 0, 0); // Mostramos imagen en las coordenadas 0,0
  delay(1000); // Tiempo de espera para mostrar la imagen
  tft.setRotation(3); // Establecemos posicion del LCD Horizontal
  bmpDraw("2.bmp",0,0); // Mostramos imagen en las coordenadas 0,0
  delay(1000); // Tiempo de espera para mostrar la imagen
}

En este caso, establecemos la rotación de la pantalla, en vertical u horizontal mediante la función tft.setRotation(0); en función de las imágenes que vayamos a mostrar por el LCD.

Y para visualizar las imágenes, llamamos a la función bmpDraw(«NombreImagen.bmp», X, Y);, pasando como parámetros el nombre EXACTO que tiene nuestra imagen almacenada en la MicroSD y las coordenadas X e Y de donde queremos mostrarla en la pantalla.
Esta función viene incluida en el sketch tftbmp de la librería de Adafruit y funciona muy bien.

Y así podemos visualizar imágenes almacenadas en la tarjeta microSD:

Resumiendo, es un fantástico módulo shield que podremos utilizar en muchos proyectos diferentes añadiendo un elemento actual y moderno como son las pantallas táctiles, visualizando gráficos a todo color ya sean imágenes, botones o texto y pudiendo leer y escribir datos o imágenes desde la tarjeta MicroSD. 

Agradecer a Electrohobby por el módulo shield y nos vemos en el siguiente tuto!

😀


 Resolución de problemas

Si teniendo todas las librerías descargadas y correctamente colocadas, al iniciar cualquiera de los sketchs propuestos en este tutorial, lo que vemos es la pantalla llena de pixeles de colores:

Pixeles-p
Es posible que el chip de nuestro LCD sea diferente al que estamos configurando, por lo que debemos probar con los diferentes drivers que incluye esta librería de Adafruit para poder inicializar correctamente el LCD.

Modificando la siguiente linea de código de nuestro sketch, cambiamos el valor entre paréntesis por alguno de los otros controladores disponibles y vamos probando para comprobar cuál es el driver que necesita nuestro LCD.


tft.begin(0x9341); // Iniciamos el LCD especificando el controlador

// Controladores disponibles:  0x9325, 0x9328, 0x7575, 0x9341, 0x8357


 

96 comentarios en “TFT LCD Touch 2.4» Shield para Arduino UNO

  1. Solucionado, cambiando la asignación de pines, quedando así. No se muy bien pq.

    // Pines necesarios para los 4 pines del panel táctil
    #define YP 7
    #define XM A1 // Pin analogico A1 para ADC
    #define YM A2 // Pin analogico A2 para ADC
    #define XP 6

  2. He probado el «Ejemplo_pintar», pero cuando se traza una línea vertical en la pantalla aparece una línea horizontal.
    Usted sabe por qué sucede esto?

  3. Holla

    En el siguiente código se creó 4 botones y deseo sólo el «botón // //Testa botao amarelo» llamado y deligasse el LED 5. Pero cuando pulso los otros botones el LED 5 parpadea. Me pregunto cómo puede este eleminar destellos del LED 5 cuando pulso los otros botones?

    //Programa : Teste Display Touch Arduino – Touchscreen
    //Autor : FILIPEFLOP

    #include // Core graphics library
    #include // Hardware-specific library
    #include

    #define YP A1 // Y+ is on Analog1
    #define XM A2 // X- is on Analog2
    #define YM 7 // Y- is on Digital7
    #define XP 6 // X+ is on Digital6

    #define TS_MINX 125
    #define TS_MINY 85
    #define TS_MAXX 965
    #define TS_MAXY 905

    TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

    //Definicao de cores
    #define BLACK 0x0000
    #define BLUE 0x001F
    #define RED 0xF800
    #define GREEN 0x07E0
    #define CYAN 0x07FF
    #define MAGENTA 0xF81F
    #define YELLOW 0xFFE0
    #define WHITE 0xFFFF

    //PP_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
    Adafruit_TFTLCD tft(A3, A2, A1, A0, A4);

    // Armazena o estado dos botões
    bool valor_botao1 = 0;
    bool valor_botao2 = 0;
    bool valor_botao3 = 0;
    bool valor_botao4 = 0;

    #define MINPRESSURE 10
    #define MAXPRESSURE 1000

    #define ledA 5 //led pin5

    void setup(void)
    {
    Serial.begin(9600);
    Serial.println(«TFT Test»);
    //identifier == 0x9341;
    tft.reset();
    delay(500);
    //uint16_t identifier = tft.readRegister(0x0);
    //Serial.print(«Driver encontrado: «);
    //Serial.println(identifier, HEX);

    pinMode(5, OUTPUT);

    tft.begin(0x9341);
    //tft.initDisplay();
    tft.fillScreen(BLACK);
    tft.setRotation(1);

    // Inicio – Texto e botoes
    tft.drawRoundRect(5, 15, 312, 50, 5, WHITE);
    tft.drawRoundRect(255, 15, 62, 50, 5, WHITE);
    tft.setTextColor(YELLOW);
    tft.setTextSize(3);
    tft.setCursor(15, 30);
    tft.println(«Garagem»);

    tft.drawRoundRect(5, 70, 312, 50, 5, WHITE);
    tft.drawRoundRect(255, 70, 62, 50, 5, WHITE);
    tft.setTextColor(GREEN);
    tft.setTextSize(3);
    tft.setCursor(15, 85);
    tft.println(«Sala»);

    tft.drawRoundRect(5, 125, 312, 50, 5, WHITE);
    tft.drawRoundRect(255, 125, 62, 50, 5, WHITE);
    tft.setTextColor(BLUE);
    tft.setTextSize(3);
    tft.setCursor(15, 140);
    tft.println(«Cozinha»);

    tft.drawRoundRect(5, 180, 312, 50, 5, WHITE);
    tft.drawRoundRect(255, 180, 62, 50, 5, WHITE);
    tft.setTextColor(RED);
    tft.setTextSize(3);
    tft.setCursor(15, 195);
    tft.println(«Resetar»);

    //Preenchimento OFF
    tft.setTextColor(WHITE);
    tft.setCursor(260, 30);
    tft.println(«OFF»);
    tft.setCursor(260, 85);
    tft.println(«OFF»);
    tft.setCursor(260, 140);
    tft.println(«OFF»);
    tft.setCursor(260, 195);
    tft.println(«OFF»);
    }

    void loop()
    {
    TSPoint p = ts.getPoint();
    pinMode(XM, OUTPUT);
    digitalWrite(XM, LOW);
    pinMode(YP, OUTPUT);
    digitalWrite(YP, HIGH);
    pinMode(YM, OUTPUT);
    digitalWrite(YM, LOW);
    pinMode(XP, OUTPUT);
    digitalWrite(XP, HIGH);

    if (p.z > MINPRESSURE && p.z 240)
    {
    Serial.print(«py: «);
    Serial.print(p.y);
    Serial.print(» px: «);
    Serial.print(p.x);

    //Testa botao amarelo
    if (p.y > 190 & p.y 130 & p.y 75 & p.y 0 & p.y < 70)
    {
    if (valor_botao4 == 0)
    {
    tft.fillRoundRect(256, 181, 60, 48, 5, RED);
    mostra_on(269,195);
    valor_botao4 = !valor_botao4;
    digitalWrite(ledA, LOW);
    }
    else
    {
    tft.fillRoundRect(256, 181, 60, 48, 5, BLACK);
    mostra_off(260,195);
    valor_botao4 = !valor_botao4;
    digitalWrite(ledA, LOW);
    }
    }
    }
    }
    }

    void mostra_on1(int x, int y)
    {
    tft.setTextColor(BLACK);
    tft.setCursor(x, y);
    tft.println("ON");
    delay(100);
    }

    void mostra_off1(int x, int y)
    {
    tft.setTextColor(WHITE);
    tft.setCursor(x, y);
    tft.println("OFF");
    delay(100);
    }

    void mostra_on(int x, int y)
    {
    digitalWrite(ledA, LOW);
    tft.setTextColor(BLACK);
    tft.setCursor(x, y);
    tft.println("ON");
    delay(100);
    }

    void mostra_off(int x, int y)
    {
    digitalWrite(ledA, LOW);
    tft.setTextColor(WHITE);
    tft.setCursor(x, y);
    tft.println("OFF");
    delay(100);
    }

  4. Buenos dias, de antemano agradezco su tutorial y la libreria tan bien explicada, quisiera solicitar su colaboración ante algo extraño en mi pantalla lcd, compila y carga el codigo bien pero siempre divide la pantalla en 2 y suplica la letra e imagenes de la pantalla en las 2 mitades, quiero saber si es algun error de codigo (libreria) o si se debe a un defecto de la pantall que compre gracias.

  5. Hola, lo que me sucede en mi pantalla es que cuando cargo las imágenes en la sd y las voy a visualizar en la pantalla, no me sale la imagen con los colores correctos, no es la pantalla porque los gráficos y pintar me sale muy bien, solo es que cuando quiero ver imágenes me sale con los colores distorsionados; que será?
    MUCHAS GRACIAS.

  6. hola. estoy intentando descargar las librerias pero me dic que no existe. podes pasarme el link correcto? gracias.

  7. Hola, he intentato visualizar imagenes con el ejemplo y me dice que no las encuentra en la SD. Alguien ha tenido ese inconveniente?. He hecho todo lo correcto grabarlas como bmp 320×240, 24bit. Gracias

  8. Yo medí la resistencia con el mutímetro entre los pines A2 y D6 y no me ha marcado nada. Seguí probando con los otros pines y me marcó 418 Ohms entre los pines A2 y D8. El problema es q de ser así la configuración de pines de mi placa es distinta. Probé seteando otros pines pero recibo valores aleatorios. ¿A alguien mas no le funcionó con los pines de todos estos ejemplos?

    • A mi, estoy intentando con una infinidad de ejemplos y llevo ya, una amplia investigación acerca de esto, sin embargo nadie me ha podido resolver, mi pantalla esta conectada correctamente y la asignación de pines la he movido infinidad de veces, sin embargo mi pantalla sigue en blanco, no hace prácticamente nada, he llegado a pensar que es la SD y que de alguna forma podría ayudar pero no creo tenga la menor relación, sin embargo, probare con esta opción «No se pierde nada». Si encuentras una solución te lo agradeceré me lo compartas…

      • Yo pude visualizar imágenes en el lcd, tanto gráficos creados con la librería GFX como archivos desde la SD. Pero tengo problemas con el táctil. No se cuales son los 4 pines o si definitivamente el táctil de la pantalla q me compré no funciona. Tenía el problema de la pantalla blanca pero lo solucioné iniciando la tft pasando como parámetro 0x9325 o 0x9328.

  9. hola bien explicado el post, mi problema es que la pantalla touch no funciona… que puede ser? estoy usando arduino uno, hay que hacer alguna reforma en valores de tension sobre la placa arduino?

  10. Buenos dyas.Soy Anghel Valentin de Grao de Castellony tengo de poner en marcha un invento reconocido de la Oficina de Inventos y Marcas Registradas(puedo enviar la documentacion).Necesito una pantalla TFT con sus componentes que tiene la hora local mas que puedo a una hora concreta del dia o de noche que puede salir un impulso o un pequeño voltaje a un pequeño releo para ensender una LED para 2-5 segundos .Puedo enviar la esquema electrica y otros datos. Espero la respuesta por e-mail o telefono 687 933 963. Gracias.

  11. Buenos Dias!!! puedo hacer funcionar este Display con Arduino Leonardo o en Arduino ProMicro?? o solamente funciona con Arduino Uno?, Muchas Gracias por sus respuestas… Saludos..

Replica a Mauro Cancelar la respuesta