ARD17 – Pantalla táctil TFT

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

En este artículo vamos a aprender a usar una pantalla TFT táctil con nuestro Arduino. Lo primero que debemos saber es que, en el mercado, existen gran variedad de modelos, de distintos tamaños (desde 1,8″ hasta 7″) y con diferentes prestaciones en cuanto a resolución, paleta de colores, precisión, etc, así como con diferencias, evidentemente, de precio. En este artículo vamos a usar uno de los modelos que podemos adquirir pero, lo que aquí aprendamos será extrapolable, salvando las diferencias, a otros modelos.

LA PANTALLA

Vamos a usar una pantalla TFT de 2.4″ que vemos, por delante y por detrás, en la figura 17.1.

Esta pantalla puede adquirirse en este enlace, o en este otro, por un precio que, en el momento de escribir estas líneas, no llega a los 15 euros. No es que sea excesivamente cara pero mejor tratarla con cuidado :-).

EL MONTAJE

El montaje con estas pantallas es muy cómodo y fácil. Vienen de fábrica en formato shield, lo que quiere decir que tienen los pines coincidiendo con los de Arduino, de modo que, simplemente, se “enchufa” la plaquita de la pantalla en Arduino y listo. Solo tienes que tomar una precaución: cubre la parte superior del conectir USB de Arduino con un trocito de cinta aislante, porque el conector es metálico y podría rozar algún contacto de la placa de la TFT. En las 17.1, 17.2 y 17.3 que aparecen a continuación ves cómo queda conectado.

Figura 17.1 Un lateral de Arduino Uno con la TFT "pinchada"

Figura 17.1 Un lateral de Arduino Uno con la TFT “pinchada”

 

Figura 17.2 La TFT "pinchada" en Arduino Uno desde el otro lado. Se aprecia la cinta aislante que cubre el conector USB para evitar cortocircuitos.

Figura 17.2 La TFT “pinchada” en Arduino Uno desde el otro lado. Se aprecia la cinta aislante que cubre el conector USB para evitar cortocircuitos.

 

Figura 17.3 Perspectiva de la placa Shield TFT conectada a Arduino Uno

Figura 17.3 Perspectiva de la placa Shield TFT conectada a Arduino Uno

En las figuras 17.4 y 17.5 ves la placa de la TFT “suelta” (sin conectar), por delante y por detrás.

Figura 17.4 La TFT por delante.

Figura 17.4 La TFT por delante.

Figura 17.5 La TFT por detrás. Vemos el slot para micro SD.

Figura 17.5 La TFT por detrás. Vemos el slot para micro SD.

 

Observa en la vista trasera que hay un slot para microSD. Se puede meter una tarjeta con imágenes, que podremos visualizar en la pantalla. Lo veremos más adelante en este artículo.

Y ya está. Ese es todo el montaje. Solo queda enchufar Arduino al ordenador por USB, y listo.

La pantalla que estamos usando tiene tres funcionalidades:

  • Mostrar contenidos (claro, es una pantalla).
  • Función táctil.
  • Gestión de imágenes desde microSD

Veamos como empezar.

EL PRIMER SKETCH

En el primer sketch vamos a usar sólo la funcionalidad de visualización de datos, para empezar a familiarizarnos con estas técnicas. Lo primero que necesitamos es añadir a nuestro IDE la librería Adafruit_TFTLCD. Puedes descargarla en este enlace. Esta librería hace uso, a su vez, de la librería Adafruit_GFX, que también deberemos descargar e incorporar a nuestro IDE de Arduino. Puedes descargarla en este enlace. Una vez incorporada a nuestro IDE, podremos incluirla en los sketches que necesitemos. Veamos el primer listado completo y luego lo comentamos. Puedes leer sobre estas librerías en este enlace.

Figura 17.6. Una de las pantallas del ciclo de este primer sketch.

Figura 17.6. Una de las pantallas del ciclo de este primer sketch.

El sketch muestra una pantalla con unos textos y una línea. Después de cinco segundos, cambia a otra pantalla con unas figuras geométricas dibujadas. Cinco segundos más y La pantalla se llena con 50 líneas trazadas al azar. Después de otros cinco segundos, la pantalla se llena con 10000 puntos dibujados de forma aleatoria. Tras otros cinco segundos vuelve a la pantalla inicial. En las figuras 17.6 vemos una de estas pantallas.

Este sketch introduce varios conceptos interesantes. Aquí vamos a detallar los más relevantes pero, para entender bien el código, deberás leer los comentarios. He sido especialmente profuso, para que te sea fácil ver como opera.

Por supuesto, empezamos incluyendo la librería que necesitamos para gestionar la TFT:

#include <Adafruit_TFTLCD.h>

Observa que solo incluimos Adafruit.TFT_LCD, pero no Adafruit_GFX. Esto es porque, en la versión actual, la primera se encarga de incluir la segunda de forma transparente, por lo que no tenemos que hacerlo nosotros. Por supuesto, ambas deben estar incorporadas al IDE de Arduino. Sí, por alguna razón, tu IDE no pudiera incluir la segunda librería de forma automática (he oído de un par de casos, aunque a mí no me ha sucedido nunca), deberías añadir, al principio del sketch, la línea siguiente:

#include <Adafruit_GFX>

Lo primero sobre lo que quiero llamar la atención es en la definición de las constantes que hemos usado para los colores. Arduino soporta la sentencia #define, heredada del lenguaje C. He definido parte de los colores con #define y el resto con el sistema tradicional de definición de constantes de Arduino, para que veas que podemos emplearlos indistintamente.. Fíjate en el tipo de dato que son los colores: uint16_t. Este es, en realidad, un tipo int, sólo que especificándole a Arduino que es un entero en base 16.

Observa que los códigos de los colores se han expresado en hexadecimal, por lo que cada uno va precedido de la secuencia 0x. Esta secuencia no forma parte en sí misma del valor que representa al color, sino que se usa, precisamente, para decirle a Arduino que está tratando con un valor hexadecimal.

Los colores en Arduino se codifican sobre la base de dos palabras de ocho bits (dos bytes, 16bits en total), distribuidos del siguiente modo:

128 64   32 16  8  128 64   32 16 

Observa que dedicamos cinco bits al rojo, seis al verde y cinco al azul. Las tonalidades verdes tienen más posibles combinaciones que las rojas y azules. Esto es así porque, según los que de biología entienden, el ojo humano puede captar más tonalidades de verde.

Si queremos codificar un color, lo que hacemos es marcar en la tabla los bits de rojo, verde y azul que queremos activados. Luego, sumamos sus pesos específicos, lo que nos da dos números de entre 0 y 255. Uno de estos números por cada palabra de ocho bits. Traducimos esos números a hexadecimal, y ya está.

Para ponerte un ejemplo. Supongamos que queremos un color rojo puro. Esto significa los cinco bits del rojo activados, y los seis del verde y los cinco del azul desactivados. Por lo tanto, tenemos dos valores:  248 (en hexadecimal, F8) y 0 (en hexadecimal, 00). Así, el color rojo puro se codifica como 0xF800, como así lo vemos en el sketch.

Después de asignar nombres a los colores definimos los pines de Arduino que quedan conectados a unos pines específicos de la TFT. En concreto, son los siguientes:

  • CS. Chip Select.
  • CD. Command Data
  • WR. Write
  • RD. Read
  • RST. Reset

Como la TFT sólo encaja en Arduino en una posición, estos pines serán siempre los mismos, pero necesitamos hacer constar cuales son, para decírselo al constructor de la clase Adafruit_TFTLCD, así:

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

Además, podríamos no usar la función shell de la placa, y conectarla a Arduino mediante cableado. Es más engorroso pero, si necesitamos pines de Arduino libres para otros dispositivos, no nos quedaría más remedio. En ese caso, los pines “clave” de la TFT podrían ser otros.

Otro aspecto interesante que quiero destacar aquí está en la sección setup. Se trata de la línea que inicializa el TFT, mediante el método begin(), así:

objetoTFT.begin(0x9325);

Observa que, cómo argumento, recibe un valor hexadecimal, que se refiere al controlador que tiene la TFT. Cuando cambiamos de pantalla, el controlador puede ser otro. Debemos buscar en Internet el controlador de nuestra pantalla (lo especifica la datasheet). Si pones un controlador que no es el de tu pantalla, lo más normal que la veas en blanco, o con parpadeos, o con “lluvia” aleatoria pero, en todo caso, no te funcionarán los sketches. Si te sucede eso con este sketch, comprueba el número de controlador.

Quiero llamar tu atención sobre líneas como la siguiente:

objetoTFT.print("La TFT tiene\nuna anchura\nde ");

Observa la secuencia \n que aparece en el texto. La TFT no tiene una función de ajuste de palabra, así que, si el texto no cabe en una línea, se cortará al final de la misma, para seguir en la siguiente… aunque sea en medio de una palabra. Lo que hacemos al forzar un salto de línea es fragmentar la línea en trozos que quepan sin problemas, para que no se nos corten palabras.

El resto del código son usos normales de los métodos de la librería Adafruit_TFT_LCD, que puedes ver en el artículo correspondiente.

EL PANEL TÁCTIL

Figura 17.7. Representación esquemática de un panel táctil resistivo.

Figura 17.7. Representación esquemática de un panel táctil resistivo.

El panel táctil de las pantallas TFT de bajo coste es de los llamados resistivos. Este tipo de paneles detectan un punto de presión, de modo que da lo mismo si pulsamos con un dedo o con un objeto romo (para no dañar el panel evitaremos objetos punzantes o afilados) de metal, plástico o cualquier otro material. Por contra existen las pantallas táctiles capacitivas (las que incorporan los teléfonos móviles de gama media y alta) que lo que detectan es la capacidad eléctrica del cuerpo humano, por lo que deben ser pulsados con el dedo.

Un panel táctil resistivo está formado por dos láminas con unos hilos de una aleación de plata en los bordes, como ves en la figura 17.7.

La gran ventaja de este tipo de paneles es su bajo coste. Se nota al tacto una pantalla con panel resistivo, porque parece como que “se hunde” ligeramente al pulsar con el dedo, mientras que una capacitiva es rígida.

A la hora de usar este tipo de pantallas para funciones táctiles, la configuración debe ser dinámica. Esto quiere decir que debemos incluir en el sketch un valor que varía de una pantalla a otra. En concreto deberemos medir la resistencia en ohmios que hay en la pantalla (teniéndola desconecta de Arduino), entre los terminales LCD_RS y LCD_D6. Para ello podemos usar un polímetro cualquiera. En los bazares de barrio se venden por entre cinco y doce euros, según el modelo y la tienda. Los profesionales, que se venden en tiendas de electrónica, pueden llegar a costar trescientos euros o más, pero nosotros no necesitamos ese tipo de aparato para nada (menos mal…. 😕 ). Si no encuentras en tu zona y quieres adquirirlo por Internet, hay una gran variedad en este enlace.En la figura 17.8 vemos uno de los más económicos, pero suficiente para nuestras necesidades.

Figura 17.8. Polímetro digital económico.

Figura 17.8. Polímetro digital económico.

Pondremos el selector rotatorio en la posición de 2000 Ω y arrimaremos las puntas de prueba a los pines indicados, con cuidado de no tocar ninguno de los adyacentes. La punta negra la arrimamos a LCD_D6 y la roja a LCD_RS. En la pantalla digital nos aparecerá una lectura de algo más de 300 (en mi caso, 322). Anota ese número, porque te hará falta en el sketch.

EL SEGUNDO SKETCH

En este segundo ejemplo vamos a aprender a hacer uso de las funciones táctiles de nuestra TFT. Para ello vamos a emplear la librería TouchScreen, que podemos descargar en este enlace, y cuyos métodos podemos ver en este artículo.

El sketch que vamos a usar en este segundo ejemplo es muy simplón, si me permites la expresión, pero nos permitirá sentar las bases del funcionamiento del panel táctil, para luego ver otro sketch más interesante. Antes que nada, te voy a hacer una advertencia. No esperes el mismo resultado que normalmente obtienes en un smartphone o una tablet. Estas pantallas no son de gran calidad y la detección de los toques no siempre es tan precisa como nos gustaría, pero es suficiente para muchas aplicaciones. El código es el siguiente:

Este código muestra en pantalla las coordenadas X e Y de la última pulsación detectada, así como la coordenada Z, que corresponde a la presión ejercida con el dedo sobre la pantalla. Después demora un segundo, y vuelve a leer la pulsación. Una cosa que observarás es que te da coordenadas incluso si ni estás pulsando la pantalla. En el próximo sketch aprenderemos a solucionar esta tendencia.

Veamos los puntos clave. Nos centraremos en la operativa del panel táctil, ya que la parte de visualización ya la conocemos del apartado anterior. En primer lugar nos vamos a fijar en que, al igual que se definen unos pines clave para el LCD, también definimos unos pines clave para el panel táctil, así:

/* Definimos los cuatro pines necesarios para el panel táctil. */
int const YP = A1;
int const XM = A2;
int const YM = 7;
int const XP = 6;

Son los que corresponden a los pines LCD_RS, LCD_RW, LCD_D6 y LCD_D7 de la pantalla. Si no conectas tu pantalla como shied, sino cableada, o si, aunque la uses como shield, tu modelo tiene estos pines en otros diferentes, deberás de modificar estas líneas de código. Observa que hay dos pines comunes con los definidos para el LCD: los que hemos llamado XM e YP. Volveremos sobre este particular en seguida.

El siguiente punto interesante que vemos es que se definen dos constantes, una con el valor 0 y otra con 1023. Como las lecturas de la pulsación las vamos a hacer por pines analógicos, las usaremos para acotar los valores leídos.

/* Definimos los valores máximo y mínimo que se leerán en
las entradas de las coordenadas X e Y del panel táctil.
Como entran por analógicas, los valores podrán llegar de 0 a 1023. */
short TS_MIN_X_Y = 0;
short TS_MAX_X_Y = 1023;

También declaramos tres valores donde se almacenarán las coordenadas X, Y y Z de la pulsación (o, en su caso, de la no pulsación).

/* Definimos las variables que almacenarán las
coordenadas X e Y de cada pulsación. */
int X;
int Y;
int Z;

Ahora vamos a fijarnos en como creamos un objeto de la clase TouchScreen, cuya librería puedes conocer mejor aquí. Observa que, aparte de pasarle al constructor los cuatro pines que hemos definido para el panel táctil, como quinto argumento le pasamos el valor resistivo que medimos antes con el polímetro. Esto ayudará a mejorar el control de las pulsaciones.

/* Definimos el objeto que representará al panel táctil,
incluyendo la resistencia medida entre los pines A2 y 6*/
TouchScreen objetoTactil = TouchScreen(XP, YP, XM, YM, 322);

A continuación encontramos una función que hace la lectura del panel táctil cuando es invocada. Lo de poner el pin 13 a LOW antes de leer, y a HIGH inmediatamente después es para que la lectura pueda realizarse correctamente. La lectura en sí se realiza en la línea siguiente:

TSPoint pulsacion = objetoTactil.getPoint();

Esto crea un objeto (que hemos llamado pulsacion) de la clase TSPoint, que forma parte de la librería TouchScreen, y que lee la pulsación. Inmediatamente después, redefinimos los pines comunes XM (A2) e YP (A1) cómo de salida. Esto se hace porque, al crear el objeto TSPoint (al efectuar la lectura), estos pines se definen internamente cómo de entrada, y necesitamos que sean de salida para la visualización. Por lo tanto, después de la lectura, incluimos las siguientes líneas:

/* La lectura ha cambiado dos pines. Inmediatamente después de leer
debemos reestablecerlos. */
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);

El objeto pulsacion, de la clase TSPoint, tiene tres propiedades:

  • pulsacion.x. Almacena la coordenada X de la última pulsación.
  • pulsación.y. Almacena la coordenada Y de la última pulsación.
  • pulsación.z. Almacena la coordenada Z de la última pulsación.

Las coordenadas X e Y se leen, como hemos dicho, como un valor entre 0 y 1023, por lo que, para determinar la ubicación de la pulsación en píxeles, deberemos mapearlas sobre la base de la anchura y la altura de la pantalla. Pero este modelo en concreto de pantalla lee “en inverso”, es decir, el valor 0 de X corresponde al lateral derecho y el 1023 al izquierdo; así mismo, el valor 0 de Y corresponde a la parte inferior y el 1023 a la parte superior de la pantalla. Por lo tanto, no mapeamos los valores directamente, sino restándolos de 1023, así:

X = map(1023 - pulsacion.x, TS_MAX_X_Y, TS_MIN_X_Y, objetoTFT.width(), 0);
Y = map(1023 - pulsacion.y, TS_MAX_X_Y, TS_MIN_X_Y, objetoTFT.height(), 0);

Por último leemos el valor Z, así:

Z = pulsacion.z;

En la sección loop invocamos a la función, mostramos los valores leídos por pantalla, demoramos un segundo y repetimos el ciclo.

Cómo puedes comprobar, este sketch es de un funcionamiento bastante tosco, nos da lecturas de coordenadas incluso si no estamos pulsando y, desde luego, no tiene ninguna utilidad práctica, pero nos ha permitido sentar las bases de configuración del panel táctil.

EL TERCER SKETCH

Vamos a ver ahora un sketch que hace uso del panel táctil, mucho más depurado, para ver como podemos sacarle partido a todo esto.

En este sketch lo primero que nos llama la atención es que hay dos constantes nuevas:

/* Definimos la presión máxima y mínima que vamos a detectar en el panel.
De este modo evitaremos que se detecten pulsaciones si no se están realizando. */
int const PRESION_MINIMA = 120;
int const PRESION_MAXIMA = 220;

Estos valores (que yo he obtenido experimentando con mi pantalla y que, probablemente, tú tendrás que cambiar en tu código, definen la presión mínima y la máxima que se debe detectar en la pantalla. En el código incluiremos un filtro de forma que cualquier pulsación que no esté en ese rango de presión (valor Z), se descarte cómo pulsación “fantasma”. Así evitaremos lo que nos ocurría en el ejemplo anterior, que se detectaban pulsaciones incluso cuando no se estaba pulsando la pantalla.

También tenemos una variable que se usará para evitar rebotes, es decir, que una pulsación se tome como si fueran varias seguidas:

byte pulsado = 0;

Observa en la sección loop donde se detalla cómo se evitan las pulsaciones “fantasma” (en los comentarios del código). Además, verás que la variable pulsado hace que no se tengan en cuenta las lecturas hasta que no sueltes y vuelvas a pulsar. Cómo puedes ver, el código es muy simple, una vez se entienden los conceptos. Lo único que tienes que hacer es probar con diferentes presiones de pulsación, leyendo los valores, y ajustando las constantes PRESION_MINIMAPRESION_MAXIMA en tu sketch a los límites de tu pantalla en particular.

MÁS SOBRE TÁCTIL

Figura 17.9. Pantalla donde se ve la botonera dibujada.

Figura 17.9. Pantalla donde se ve la botonera dibujada.

En realidad poco más nos queda ya que hablar sobre la pantalla táctil. A partir de aquí es recoger las bases fundamentales que ya conocemos, leernos la documentación de la librería TouchScreen (en este enlace), y seguir experimentando. Quiero compartir contigo un código que muestra en pantalla una botonera imitando a las de los cajeros automáticos. Los botones los he creado con una función, por aquello de optimizar código, que sabes que tanto me gusta. Son negros, con el texto y el borde en cian, y sobre una pantalla también negra, para no complicarnos demasiado. Al pulsar uno, se dibuja en inverso (color cian, con el guarismo interior en negro).

El código puede parecer un poco complejo pero, si lo analizas verás que es más de lo mismo. A ti te dejo echarle imaginación y hacer algo con los números que se pulsen. Por ejemplo, puedes encadenar cuatro, para formar un PIN y encender debajo una zona verde o roja, según coincida con una clave prememorizada. Puedes incorporar parte del código que vimos en el artículo sobre teclados. El teclado que se dibuja al inicio lo ves en la figura 17.9.

El código está profusamente comentado, para que te resulte viable seguirlo y entenderlo, aunque está pensado para que te suponga un cierto estímulo intelectual, que es cómo se aprende (al menos, cómo aprendo yo).

LA TARJETA SD

Nuestra pantallita tiene, en la parte trasera, un slot para introducir una tarjeta microSD. Veamos cómo podemos usar esta tarjeta para almacenar imágenes que un sketch mostrará en la pantalla. Tenemos que usar imágenes en formato .bmp, con una profundidad de color de 24 bits, y con unas dimensiones coincidentes con las de la pantalla que vamos a emplear (en este caso, 320 x 240 píxeles). Las imágenes usadas en este ejemplo te las dejo para descargar en este enlace.

El sketch es el siguiente:

ATENCIÓN. La función bmpDraw() NO es parte del núcleo de Arduino, ni de ninguna de las librerías que empleamos. Es otro código aparte, reproducido a continuación:

Deberás guardarlo en tu disco, en un archivo con la extensión .ino, e incluirlo en tu sketch principal, tal cómo se explica en este microtutorial.

Si sigues estos pasos, verás las imágenes en tu TFT. La mayor o menos calidad con la que se vean depende, desde luego, de la calidad de la pantalla.

COMENTARIO FINAL

Aunque hemos usado las librerías SPI y SD, el funcionamiento de tarjetas SD con Arduino es tema para otro artículo. Hablaremos de ello en su momento, en esta misma serie.

     

3 comentarios:

  1. Pingback: LA LIBRERÍA TouchScreen » eldesvandejose.com

  2. Pingback: ARD19 – Tarjeta Esplora con TFT » eldesvandejose.com

  3. Belmiro Fernandes

    ótimo conteúdo. Muito bem explicado para iniciantes. Jamais conseguiria entender o funcionamento e o código sozinho

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *