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.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
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.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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
#include <Adafruit_TFTLCD.h> // Incluimos la librería para la TFT /* Definimos los colores para poder referirnos a ellos con su nombre en lugar de usar el código hexadecimal de cada uno. */ const uint16_t NEGRO = 0x0000; const uint16_t ROJO = 0xF800; const uint16_t VERDE = 0x07E0; #define BLANCO 0xFFFF #define AZUL 0x001F #define CIAN 0x07FF #define AMARILLO 0xFFE0 #define MAGENTA 0xF81F /* Guardamos los colores en una matriz para poder llamarlos por el índice cuando nos haga falta.*/ const uint16_t COLORES[] = {NEGRO, ROJO, VERDE, BLANCO, AZUL, CIAN, AMARILLO, MAGENTA}; // Definimos los pines de conexion de la TFT int const LCD_CS = A3; // Chip Select - Pin analogico 3 int const LCD_CD = A2; // Command/Data - Pin Analogico 2 int const LCD_WR = A1; // LCD Write - Pin Analogico 1 int const LCD_RD = A0; // LCD Read - Pin Analogico 0 int const LCD_RESET = A4; // LCD Reset - Pin Analogico 4 Adafruit_TFTLCD objetoTFT (LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); // Instanciamos la clase en un objeto que usaremos para manejar la TFT /* Definimos unas variables que usaremos en la tercera y cuarta parte del sketch */ int contador; int Xinicio; int Yinicio; int Xfinal; int Yfinal; int indiceColor; /* Almacenamos la anchura y altura, en píxeles, que tiene la pantalla. */ int anchura = objetoTFT.width(); int altura = objetoTFT.height(); void setup() { /* Iniciamos el LCD especificando el controlador de nuestro LC. En este caso el ILI9341. Otros controladores: 0x9325, 0x9328,0x7575, 0x9341, 0x8357. */ objetoTFT.begin(0x9325); /* Rellenamos el fondo en negro. */ objetoTFT.fillScreen(COLORES[0]); /* Establecemos la semilla de un aleatorio que usaremos más tarde. */ randomSeed(1); } void loop(){ /* Establecemos la orientación de la pantalla: 0 = vertical y 1 = horizontal */ objetoTFT.setRotation(0); // Establecemos la posición de la pantalla Vertical u Horizontal /* Posicionamos el cursor en donde queramos empezar a escribir. Recuerda que esta pantalla tiene 240 píxeles de ancho y 320 píxeles de alto. La esquina superior izquierda corresponde al pixel 0, 0. La posición horizontal va de 0 a 239 y la vertical de 0 a 319. */ objetoTFT.setCursor(40, 10); /* Definimos el tamaño de fuente, desde 1 (más pequeño) a 10 (más grande). */ objetoTFT.setTextSize(7); /* Establecemos el color del texto, usando las constantes que hemos definido antes. */ objetoTFT.setTextColor(COLORES[5]); /* Mostramos un texto con un salto de línea al final. */ objetoTFT.println("HOLA"); /* Trazamos una línea estableciendo las coordenadas X e Y del punto de inicio, X e Y del punto final, y el color. */ objetoTFT.drawLine(20, 90, 220, 90, COLORES[1]); /* Reposicionamos el cursor, como hicimos antes, y cambiamos el tamaño y color del texto. */ objetoTFT.setCursor(0, 130); objetoTFT.setTextSize(3); objetoTFT.setTextColor(COLORES[2]); /* Mostramos la anchura y altura de la TFT. */ objetoTFT.print("La TFT tiene\nuna anchura\nde "); objetoTFT.print(anchura); objetoTFT.print(" px y\nuna altura\nde "); objetoTFT.print(altura); objetoTFT.print(" px."); /* Introducimos una demora y borramos la pantalla (rellenándola de nuevo con el color del fondo. */ delay (5000); objetoTFT.fillScreen(COLORES[0]); /* Dibujamos un rectángulo sin relleno. Establecemos las coordenadas X e Y, la anchura y altura, y el color del borde. */ objetoTFT.drawRect(20, 10, 220, 25, COLORES[6]); /* Dibujamos un rectángulo con relleno, dentro de otro sin relleno. Para ambos establecemos los respectivos valores: Coordenadas X e Y de la esquina superior izquerda. Anchura y altura. Color. */ objetoTFT.drawRect(20, 40, 220, 25, COLORES[3]); objetoTFT.fillRect(22, 42, 216, 21, COLORES[1]); /* Dibujamos un círculo sin relleno. Establecemos los siguientes valores. Coordenadas X e Y del centro. Radio. Color. */ objetoTFT.drawCircle(60, 125, 50, COLORES[4]); /* Dibujamos un círculo sólido (con relleno). Establecemos los siguientes valores. Coordenadas X e Y del centro. Radio. Color. */ objetoTFT.fillCircle(180, 125, 50, COLORES[7]); /* Dibujamos un triangulo sin relleno. Establecemos las coordenadas X e Y de los tres vértices, y el color de la línea. */ objetoTFT.drawTriangle (120, 170, 20, 210, 220, 210, COLORES[5]); /* Dibujamos un triangulo con relleno. Establecemos las coordenadas X e Y de los tres vértices, y el color de la línea. */ objetoTFT.fillTriangle (120, 260, 20, 220, 220, 220, COLORES[2]); /* Dibujamos dos rectángulos, uno sin relleno y otro interior sólido. Esta vez tienen las ezquinas redondeadas. Fijamos los siguientes parámetros: Coordenadas X e Y de la esquina superior izquierda. Anchura y altura. Radio de las esquinas. Color. */ objetoTFT.drawRoundRect(20, 280, 220, 30, 10, COLORES[1]); objetoTFT.fillRoundRect(22, 282, 216, 26, 10, COLORES[6]); /* Introducimos una demora y borramos la pantalla (rellenándola de nuevo con el color del fondo. */ delay (5000); objetoTFT.fillScreen(COLORES[0]); /* Entramos en un bucle para crear 50 líneas al azar. */ for (contador = 0; contador < 50; contador ++){ /* Definimos las coodenadas de inicio y final, y el color de la línea, de forma aleatoria. */ Xinicio = random(240); Yinicio = random(320); Xfinal = random(240); Yfinal = random(320); indiceColor = random(8); objetoTFT.drawLine(Xinicio, Yinicio, Xfinal, Yfinal, COLORES[indiceColor]); } /* Introducimos una demora y borramos la pantalla (rellenándola de nuevo con el color del fondo. */ delay (5000); objetoTFT.fillScreen(COLORES[0]); /* Entramos en un bucle para crear 10000 puntos al azar. */ for (contador = 0; contador < 10000; contador ++){ /* Definimos las coodenadas de y el color del punto, de forma aleatoria. */ Xinicio = random(240); Yinicio = random(320); indiceColor = random(8); objetoTFT.drawPixel(Xinicio, Yinicio, COLORES[indiceColor]); } /* Introducimos una demora y borramos la pantalla (rellenándola de nuevo con el color del fondo. */ delay (5000); objetoTFT.fillScreen(COLORES[0]); /* Se dibuja un carácter en la pantalla. */ objetoTFT.drawChar(50, 50, 'A', ROJO, BLANCO, 10); /* Introducimos una demora y borramos la pantalla (rellenándola de nuevo con el color del fondo. */ delay (5000); objetoTFT.fillScreen(COLORES[0]); } |

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 | 4 | 2 | 1 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
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.
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.
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
#include <Adafruit_TFTLCD.h> // Incluimos la librería para el LCD #include <TouchScreen.h> // Incluimos la librería para el panel tactil /* Definimos los cinco pines necesarios para el LCD */ int const LCD_CS = A3; int const LCD_CD = A2; int const LCD_WR = A1; int const LCD_RD = A0; int const LCD_RESET = A4; /* 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; /* 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; /* Definimos las variables que almacenarán las coordenadas X e Y de cada pulsación. */ int X; int Y; int Z; /* Definimos una batería de colores. */ int const NEGRO = 0x0000; int const AZUL = 0x001F; int const ROJO = 0xF800; int const VERDE = 0x07E0; int const CIAN = 0x07FF; int const MAGENTA = 0xF81F; int const AMARILLO = 0xFFE0; int const BLANCO = 0xFFFF; /* Instanciamos el objeto para visualización. */ Adafruit_TFTLCD objetoTFT(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); /* Definimos el objeto que representará al panel táctil, incluyendpo la resisttencia medida entre los pines A2 y 6*/ TouchScreen objetoTactil = TouchScreen(XP, YP, XM, YM, 322); /* Definimos la función que hace la lectura del panel táctil cuando es invocada. */ void leerTactil(){ digitalWrite(13, HIGH); TSPoint pulsacion = objetoTactil.getPoint(); // Realizamos lectura de las coordenadas digitalWrite(13, LOW); /* La lectura ha cambiado dos pines. Inmediatamente después de leer debemos reestablecerlos. */ pinMode(XM, OUTPUT); pinMode(YP, OUTPUT); /* Mapeamos los valores analogicos leidos del panel tactil (0-1023) y los convertimos en valor correspondiente a la medida del LCD 320x240 (teniendo en cuenta que la pantalla que empleamos es "inversa"). También leemos, "en inverso" la presión (eje Z). */ 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); Z = pulsacion.z; } void setup(){ /* Inicializamos el objeto de la TFT con el controlador adecuado. */ objetoTFT.begin(0x9325); /* Rellenamos la pantalla de negro. */ objetoTFT.fillScreen(NEGRO); /* Definimos el color de texto ccomo blanco. */ objetoTFT.setTextColor(BLANCO); /* Establecemos el tamaño del texto. */ objetoTFT.setTextSize(4); /* El pin 13 debe definirse como de salida. En realidad, es el pin que coincide con el */ pinMode(13, OUTPUT); } void loop(){ /* Invocamos a la función que lee las coordenadas de una posible pulsación. */ leerTactil(); /* Borramos la pantalla para escribir las coordenadas de la última pulsación detectada. */ objetoTFT.fillScreen(NEGRO); /* Posicionamos el cursor y escribimos las coordenadas. */ objetoTFT.setCursor(0,35); objetoTFT.print ("X: "); objetoTFT.println (X); objetoTFT.print ("Y: "); objetoTFT.println (Y); objetoTFT.print ("Z: "); objetoTFT.println (Z); /* Introducimos una demora para que nos de tiempo a leer el contenido de la pantalla, antes de que cambie. */ delay(1000); } |
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 coordenadaX
de la última pulsación.pulsación.y
. Almacena la coordenadaY
de la última pulsación.pulsación.z
. Almacena la coordenadaZ
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
#include <Adafruit_TFTLCD.h> // Libreria de LCD #include <TouchScreen.h> // Libreria del panel tactil /* Definimos los cinco pines necesarios para el LCD */ int const LCD_CS = A3; int const LCD_CD = A2; int const LCD_WR = A1; int const LCD_RD = A0; int const LCD_RESET = A4; /* Definiimos 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; /* 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; /* Definimos las variables que almacenarán las coordenadas X e Y de cada pulsación. */ int X; // Variables que almacenaran la coordenada int Y; // X, Y donde presionemos y la variable Z int Z; // almacenara la presion realizada /* Definimos una batería de colores. */ int const NEGRO = 0x0000; int const AZUL = 0x001F; int const ROJO = 0xF800; int const VERDE = 0x07E0; int const CIAN = 0x07FF; int const MAGENTA = 0xF81F; int const AMARILLO = 0xFFE0; int const BLANCO = 0xFFFF; /* Definimos la presion máxima y minima 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; byte pulsado = 0; /* Instanciamos el objeto para visualización. */ Adafruit_TFTLCD objetoTFT(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); /* Definimos el objeto que representará al panel táctil, incluyendpo la resisttencia medida entre los pines A2 y 6*/ TouchScreen objetoTactil = TouchScreen(XP, YP, XM, YM, 322); /* Definimos la función que hace la lectura del panel táctil cuando es invocada. */ void leerTactil(){ digitalWrite(13, HIGH); TSPoint pulsacion = objetoTactil.getPoint(); // Realizamos lectura de las coordenadas digitalWrite(13, LOW); /* La lectura ha cambiado dos pines. Inmediatamente después de leer debemos reestablecerlos. */ pinMode(XM, OUTPUT); pinMode(YP, OUTPUT); /* Mapeamos los valores analogicos leidos del panel tactil (0-1023) y los convertimos en valor correspondiente a la medida del LCD 320x240 (teniendo en cuenta que la pantalla que empleamos es "inversa"). También leemos, "en inverso" la presión (eje Z). */ 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); Z = pulsacion.z; } void setup(){ /* Inicializamos el objeto de la TFT con el controlador adecuado. */ objetoTFT.begin(0x9325); /* Rellenamos la pantalla de negro. */ objetoTFT.fillScreen(NEGRO); /* Definimos el color de texto ccomo blanco. */ objetoTFT.setTextColor(BLANCO); /* Establecemos el tamaño del texto. */ objetoTFT.setTextSize(4); /* El pin 13 debe definirse como de salida. En realidad, es el pin que coincide con el */ pinMode(13, OUTPUT); } void loop(){ leerTactil(); // Realizamos lectura del panel para detectar presion y coordenadas /* Se comprueba si la presión supera el máximo. Hay que tener en cuenta que el valor Z es también inverso. Cuanto más aprietes, menor será, por lo que un valor máximo o superior al máxio indica que no se está pulsando la pantalla. En cambio, si aprietas con firmeza (sin pasarte) verás que el valor Z disminuye. */ if (Z > PRESION_MAXIMA) pulsado = 0; if(Z > PRESION_MINIMA && Z < PRESION_MAXIMA && pulsado == 0) { /* Aquí mostramos la presión ejercida. */ objetoTFT.fillRect(50, 280, 150, 30, ROJO); objetoTFT.setTextColor(BLANCO); objetoTFT.setCursor(60,290); objetoTFT.setTextSize(2); objetoTFT.print(Z); delay(150); pulsado = 1; } } |
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_MINIMA
y PRESION_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.
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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
/* Incluimos las librerías necesarias. */ /* Recuerda que Adafruit_TFTLCD incluye, a su vez, la librería Adafruit_GFX, automáticamente. */ #include <Adafruit_TFTLCD.h> // Libreria para visualización #include <TouchScreen.h> // Libreria del panel tactil /* Se definen los pines del LCD, del mismo modo que hicimos en el primer sketch. */ int const LCD_CS = A3; // Definimos los pines del LCD int const LCD_RS = A2; // para poder visualizar elementos graficos int const LCD_WR = A1; int const LCD_RD = A0; int const LCD_RESET = A4; /* Se definen los pines asignados al pánel táctil. Observa que hay dos pines (A1 y A2) que también se usan para las funciones de visualización de datos (para el LCD, propiamente dicho). Esto significa que, durante la ejecución del sketch, deberemos cambiar su uso (de salida o de entrada) según los necesitemos para la visualización o para la detección de pulsaciones en pantalla. */ int const YP = A1; int const XM = A2; int const YM = 7; int const XP = 6; /* Definimos unas constantes que usaremos para detectar una pulsación "real" en la pantalla, de modo que no se detecten pulsaciones "en falso", por un mínimo roce (al desplazar el dedo de un sitio a otro, por ejemplo. Al limitar también la presión máxima que se detecta, evitamos coger la mala costumbre de apretar demasiado la pantalla, con lo que podríamos dañarla. */ short const PRESION_MINIMA = 50; short const PRESION_MAXIMA = 300; /* Definimos los colores más habituales, en hexadecimal como aprendimos en el primer sketch. */ int const NEGRO = 0x0000; int const AZUL = 0x001F; int const ROJO = 0xF800; int const VERDE = 0x07E0; int const CIAN = 0x07FF; int const MAGENTA = 0xF81F; int const AMARILLO = 0xFFE0; int const BLANCO = 0xFFFF; /* Creamos la instancia de TouchScreen incluyendo los pines predefinidos, y la resistencia en ohmios medida según se detalla en el artículo. */ TouchScreen objetoTactil = TouchScreen(XP, YP, XM, YM, 322); /* Creamos la instancia del TFT, tal cómo aprendimos a hacerlo en el primer sketch. */ Adafruit_TFTLCD objetoTFT(LCD_CS, LCD_RS, LCD_WR, LCD_RD, LCD_RESET); // Instancia LCD /* Definimos las variables que almacenarán, en cada pulsación, las coordenadas X e Y de la misma, así como la presión ejercida, como coordenada Z. */ int X; int Y; int Z; boolean pulsado = true; char ultimaTecla; /* La siguiente función se usa para detectar una pulsación. Será invocada desde el cuerpo principal del sketch (sección loop) cada vez que queramos comprobar si se ha hecho una pulsación, y en qué lugar de la pantalla. */ void lecturaTactil(){ /* El pin 13 se pone con valor HIGH, para efectuar la lectura táctil. */ digitalWrite(13, HIGH); /* Realizamos la lectura de "lo que haya" en el panel táctil, si es que hay algo, es decir, la lectura se realiza, tanto si se está pulsando como si no. Con el resultado de la lectura, ya comprobaremos en seguida si se ha pulsado y dónde. */ TSPoint pulsacion = objetoTactil.getPoint(); /* Tras la lectura restauramos los valores iniciales de los pines XM, YP y 13, para poder seguir usando las funcionalidades de visualización de la TFT. */ pinMode(XM, OUTPUT); pinMode(YP, OUTPUT); digitalWrite(13, LOW); /* Las coordenadas x e y leidas se detectan sobre la base de 0 a 1023, que es lo que se lee por las entradas analógicas. El valor leido depende de la posición en la que se ha hecho la pulsación. Cómo esa pulsación debemos medirla en píxeles sobre el ancho y alto de la pantalla, debemos mapear esos valores en relación con la anchura y altura. */ X = map(1023 - pulsacion.x, 1023, 0, objetoTFT.width(), 0); Y = map(1023 - pulsacion.y, 1023, 0, objetoTFT.height(), 0); /* La presión se mide directamente. */ Z = pulsacion.z; } /* Definimos las variables que se usarán dentro de la función que dibuja los botones normales e inversos. */ uint16_t colorDeFondo; // El color de fondo del botón uint16_t colorDeGuarismo; // El color del guarismo y el borde del botón /* La siguiente función dibuja un botón en su posición, con su guarismo, y en normal o inverso. La variable esq_X es la coordenada X superior izquierda del botón La variable int esq_Y es la coordenada Y superior izquierda del botón La variable int num_X es la coordenada X posición del guarismo del botón La variable int num_Y es la coordenada Y posición del guarismo del botón La variable char guarismo es la el carácter que aparece en el botón La variable boolean normal es true para botón normal y false para botón inverso. */ void dibujarBoton (int esq_X, int esq_Y, int num_X, int num_Y, char guarismo, boolean normal){ if (normal){ // Se ha pedido un botón normal colorDeFondo = NEGRO; colorDeGuarismo = CIAN; } else { // Se ha pedido un botón inverso colorDeFondo = CIAN; colorDeGuarismo = NEGRO; } // Fin de comprobación normal o inverso. objetoTFT.drawRoundRect(esq_X, esq_Y, 76, 56, 6, colorDeGuarismo); objetoTFT.fillRoundRect(esq_X + 1, esq_Y + 1, 74, 54, 6, colorDeFondo); objetoTFT.setTextColor(colorDeGuarismo); objetoTFT.setCursor(num_X, num_Y); objetoTFT.print(guarismo); } void setup(){ /* Definimos el pin 13 como de salida. */ pinMode(13, OUTPUT); /* Inicializamos el TFT y dibujamos la botonera */ objetoTFT.begin(0x9325); objetoTFT.setRotation(0); objetoTFT.fillScreen(NEGRO); objetoTFT.setTextSize(3); objetoTFT.setTextColor(CIAN); /* Dibujamos la botonera "en reposo" (botones normales, no pulsados) */ dibujarBoton (2, 2, 35, 20, '1', true); // Botón 1 dibujarBoton (82, 2, 115, 20, '2', true); // Botón 2 dibujarBoton (162, 2, 193, 20, '3', true); // Botón 3 dibujarBoton (2, 62, 35, 80, '4', true); // Botón 4 dibujarBoton (82, 62, 115, 80, '5', true); // Botón 5 dibujarBoton (162, 62, 193, 80, '6', true); // Botón 6 dibujarBoton (2, 122, 35, 140, '7', true); // Botón 7 dibujarBoton (82, 122, 115, 140, '8', true); // Botón 8 dibujarBoton (162, 122, 193, 140, '9', true); // Botón 9 dibujarBoton (2, 182, 35, 200, 'C', true); // Botón C dibujarBoton (82, 182, 115, 200, '0', true); // Botón 0 dibujarBoton (162, 182, 193, 200, 'V', true); // Botón V } void loop(){ lecturaTactil(); if (Z > PRESION_MAXIMA){ pulsado = false; /* Se dibuja en "normal" el botón que corresponde al último pulsado. */ switch(ultimaTecla){ case '1': dibujarBoton (2, 2, 35, 20, '1', true); // Botón 1 normal break; case '2': dibujarBoton (82, 2, 115, 20, '2', true); // Botón 2 normal break; case '3': dibujarBoton (162, 2, 193, 20, '3', true); // Botón 3 normal break; case '4': dibujarBoton (2, 62, 35, 80, '4', true); // Botón 4 normal break; case '5': dibujarBoton (82, 62, 115, 80, '5', true); // Botón 5 normal break; case '6': dibujarBoton (162, 62, 193, 80, '6', true); // Botón 6 normal break; case '7': dibujarBoton (2, 122, 35, 140, '7', true); // Botón 7 normal break; case '8': dibujarBoton (82, 122, 115, 140, '8', true); // Botón 8 normal break; case '9': dibujarBoton (162, 122, 193, 140, '9', true); // Botón 9 normal break; case 'C': dibujarBoton (2, 182, 35, 200, 'C', true); // Botón C normal break; case '0': dibujarBoton (82, 182, 115, 200, '0', true); // Botón 0 normal break; case 'V': dibujarBoton (162, 182, 193, 200, 'V', true); // Botón V normal break; } /* Se borra la memoria del último botón pulsado. */ ultimaTecla = ' '; } else { if(Z > PRESION_MINIMA && Z < PRESION_MAXIMA) { if ((X >= 2 && X <= 78) && (Y >= 2 && Y <= 58)) { // Pulsado el botón 1 dibujarBoton (2, 2, 35, 20, '1', false); // Botón 1 inverso ultimaTecla = '1'; } else if ((X >= 82 && X <= 158) && (Y >= 2 && Y <= 58)) { // Pulsado el botón 2 dibujarBoton (82, 2, 115, 20, '2', false); // Botón 2 inverso ultimaTecla = '2'; } else if ((X >= 162 && X <= 198) && (Y >= 2 && Y <= 58)) { // Pulsado el botón 3 dibujarBoton (162, 2, 193, 20, '3', false); // Botón 3 inverso ultimaTecla = '3'; } else if ((X >= 2 && X <= 78) && (Y >= 62 && Y <= 118)) { // Pulsado el botón 4 dibujarBoton (2, 62, 35, 80, '4', false); // Botón 4 inverso ultimaTecla = '4'; } else if ((X >= 82 && X <= 158) && (Y >= 62 && Y <= 118)) { // Pulsado el botón 5 dibujarBoton (82, 62, 115, 80, '5', false); // Botón 5 inverso ultimaTecla = '5'; } else if ((X >= 162 && X <= 198) && (Y >= 62 && Y <= 118)) { // Pulsado el botón 6 dibujarBoton (162, 62, 193, 80, '6', false); // Botón 6 inverso ultimaTecla = '6'; } else if ((X >= 2 && X <= 78) && (Y >= 122 && Y <= 178)) { // Pulsado el botón 7 dibujarBoton (2, 122, 35, 140, '7', false); // Botón 7 inverso ultimaTecla = '7'; } else if ((X >= 82 && X <= 158) && (Y >= 122 && Y <= 178)) { // Pulsado el botón 8 dibujarBoton (82, 122, 115, 140, '8', false); // Botón 8 inverso ultimaTecla = '8'; } else if ((X >= 162 && X <= 198) && (Y >= 122 && Y <= 178)) { // Pulsado el botón 9 dibujarBoton (162, 122, 193, 140, '9', false); // Botón 9 inverso ultimaTecla = '9'; } else if ((X >= 2 && X <= 78) && (Y >= 182 && Y <= 238)) { // Pulsado el botón C dibujarBoton (2, 182, 35, 200, 'C', false); // Botón C inverso ultimaTecla = 'C'; } else if ((X >= 82 && X <= 158) && (Y >= 182 && Y <= 238)) { // Pulsado el botón 0 dibujarBoton (82, 182, 115, 200, '0', false); // Botón 0 inverso ultimaTecla = '0'; } else if ((X >= 162 && X <= 198) && (Y >= 182 && Y <= 238)) { // Pulsado el botón V dibujarBoton (162, 182, 193, 200, 'V', false); // Botón V inverso ultimaTecla = 'V'; } pulsado = true; } } } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <Adafruit_TFTLCD.h> // Libreria de LCD #include <SD.h> // Libreria de tarjeta SD #include <SPI.h> // Libreria bus SPI /* Definimos los pines como ya es habitual. */ #define LCD_CS A3 #define LCD_CD A2 #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 objetoTFT(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); void setup(){ /* Inicializamos la pantalla y le damos orientación horizontal */ objetoTFT.begin(0x9325); objetoTFT.setRotation(3); } void loop(){ /* Mostramos cada imagen en las coordenadas 0,0 usando la función bmpDraw que se carga en un sketch secundario. Introducimos una demora entre imágenes antes del cambio a la siguiente. */ bmpDraw("1.bmp", 0, 0); delay(1000); bmpDraw("2.bmp",0,0); delay(1000); bmpDraw("3.bmp",0,0); delay(1000); } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
#define BUFFPIXEL 20 uint8_t spi_save; 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("Loading image '"); Serial.print(filename); Serial.println('\''); // Open requested file on SD card SPCR = spi_save; if ((bmpFile = SD.open(filename)) == NULL) { Serial.print("File not found"); return; } // Parse BMP header if(read16(bmpFile) == 0x4D42) { // BMP signature Serial.print(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 SPCR = 0; 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; SPCR = spi_save; 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) { SPCR = 0; tft.pushColors(lcdbuffer, lcdidx, first); lcdidx = 0; first = false; } SPCR = spi_save; 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) { SPCR = 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("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; } |
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.
Pingback: LA LIBRERÍA TouchScreen » eldesvandejose.com
Pingback: ARD19 – Tarjeta Esplora con TFT » eldesvandejose.com
ótimo conteúdo. Muito bem explicado para iniciantes. Jamais conseguiria entender o funcionamento e o código sozinho