Objetivos
Material requerido.
Arduino UNO o MEGA. | |
3.2” TFT color de Seed Studio con touch panel De 320×240 pixels. |
Las pantallas táctiles
En la sesión anterior estuvimos jugando con los gráficos en una pantalla TFT de 3.2” y dejamos pendiente probar el panel táctil que incorporan de serie, para ver que tal resultado dá y hasta qué punto pueden ser prácticas para el manejo en unas pantallas de relativo pequeño tamaño.
No tenía muy claro que la precisión fuera suficiente para permitirnos hacer aplicaciones prácticas basadas en el panel táctil, así que me puse a hacer pruebas para salir de dudas y tengo que decir que el resultado ha sido de lo más satisfactorio.
La precisión con la que el panel reconoce la presión, especialmente de un stylus o similar, es magnífico y nos permite usarlo en programas que no necesitan botones, deslizadores o pontenciómetros adicionales, sino que podemos usar con confianza la característica táctil de estos paneles.
Así que pensando, pensando, se me ocurrió ampliar el programa anterior de dibujo de las figuras de Lissajous, añadiendo un panel táctil que nos permitan configurar los tres parámetros de entrada: n, p y fase.
Bastaría con dibujar tres deslizadores para proporcionar el valor de los parámetros de 1 a 9 para los valores de n y p, y de 0 a 360º para el ángulo de fase.
Además vamos a precisar de un botón de OK y otro de Cancel para devolver los valores al programa de dibujo, lo que nos presenta un problema interesante para trabajárnoslo.
Necesitaremos también, algún modo de presentar valores numéricos en la pantalla para saber que valores vamos adoptando, lo que nos va a permitir hacer un repaso general a todas las primitivas disponibles en las librerías de SeedStudio.
Así que vamos directamente al lio que vamos a tener trabajo.
Descripciones varias
En la última sesión se me olvidó incluir una descripción de los pines que utiliza este Shield TFT, por lo que es buen momento para corregir este descuido:
Como este display utiliza el bus SPI para comunicarse con Arduino, reserva los pines correspondientes para el control, además del digital pin D4 para el control del lector de tarjetas SD y el D5 para el Chip select del display.
Para leer el panel táctil se reserva los pines analógicos del A0 al A3. El manual describe que el panel táctil se parece a un conjunto de 4 resistencias como estas:
Cuando tocamos la pantalla aparece una nueva resistencia, como R touch, de modo que leyendo la tensión de las 4 resistencias podemos calcular la posición en la que se ha tocado la pantalla.
La verdad es que no entiendo nada, pero como usando las librerías proporcionadas por SeedStudio, da lo mismo que no lo entienda, podemos usarlas tranquilamente.
Y según el manual la unión de los pines es más o menos así:
- Arduino A0 a Y-
- Arduino A1 a X-
- Arduino A2 a Y+
- Arduino A3 a X+
Programando el panel táctil
Para empezar necesitamos la librería táctil Seeed_TFT_v2.0.zip, además de la librería Touch_Screen_Driver-master y de la SPI, por lo que necesitamos algunos includes al principio de nuestro programa:
#include <SeeedTouchScreen.h> #include <TFTv2.h> #include <SPI.h> TouchScreen ts = TouchScreen(XP, YP, XM, YM); //init TouchScreen port pins
La última línea, crea una instancia de touch screen llamada ts para que podamos leer el panel y necesitamos inicializarla mediante:
Tft.TFTinit(); //init TFT library
Ya dijimos que necesitamos tres deslizadores p, n y fase para el programa además de dos botones para OK y Cancel. Vamos a dibujar todo esto con una función llamada initPlot:
void initPlot() { Tft.fillRectangle(30, 24, 40, 250, GRAY1); // Slider 1 Tft.fillRectangle(30, 24, 40, 22*P+50, BLUE); // PLot Status Slider 1 Tft.fillRectangle(90, 24, 40, 250, GRAY1); // Slider 2 Tft.fillRectangle(90, 24, 40, 22*N+50, BLUE); // PLot Status Slider 2 Tft.fillRectangle(150, 24, 40, 250, GRAY1); // Fase Tft.fillRectangle(150, 24, 40, 280*F/360 +50, BLUE); Tft.fillRectangle(0, 24, 22,110, GREEN); Tft.fillRectangle(0,162, 22,110, RED); }
Aquí tenéis una foto con el resultado:
Los 3 deslizadores son de fondo gris, pero en la foto aparecen como azul oscuro y el nivel delos mismos se marca en azul. Además en rojo el botón cancel y en verde el OK.
La parte básica de leer el panel táctil son estas líneas:
Point p = ts.getPoint(); p.x = map(p.x, TS_MINX, TS_MAXX, 0, 240); //mapear lecturas en coordenadas X,Y p.y = map(p.y, TS_MINY, TS_MAXY, 0, 320);
Creamos un objeto tipo Point que tiene dos valores p.x y p.y que corresponden a los valores de x e y del punto tocado. Recordad que con la alimentación de Arduino a nuestra derecha, el punto (0,0) queda en la esquina inferior derecha, pero con la y en la horizontal y la x en vertical para que no sea demasiado fácil.
Y para que al principio sepamos donde estamos pulsando, conviene que hagamos una función que nos informe sw donde tocamos:
void referencias(int x, int y) { Tft.fillRectangle(0, 294, 240, 30, BLACK); // Borra valores de x e y Tft.drawString("x = ",0,300,2,RED); // Plot x Tft.drawNumber( x, 48, 300,2 , RED) ; Tft.drawString("y = ",150,300,2,RED); // Plot y Tft.drawNumber( y, 200, 300,2 , RED) ; }
Con lo que veréis que los valores de x e y bailan lo suyo.
Aquí os dejo una copia inicial del programa que ha dibujado esta imagen Prog_45_2 y que os servirá para mostrar las coordenadas del punto en el que se pulsa.
Lo siguiente que vamos a necesitar es alguna manera de saber que estamos tocando dentro de uno de los deslizadores o botones para moverlos o activarlos. Lo llamaremos SenseControl:
void SenseControl(int x, int y) { if ( (x>20 && x< 65) && ( y>15 && y<270)) // Slider 1 P = map( y,15,270,1,10); if ( (x>90 && x< 133) && ( y>15 && y<270)) // Slider 2 N = map( y,15,270,1,10); if ( (x>145 && x< 195) && ( y>15 && y<270)) // Slider 3 { F = map( y,15,270,-3,360); if (F <0 ) F = 0 ; } if ( x<24) if ( y>16 && y <130) // Boton verde OK Serial.println("OK"); else if ( y>150 && y <270) // Boton rojo cancel Serial.println("CANCEL"); }
Donde mapeamos los valores correspondientes a n y p y entre 0 y 10, y hasta 360º para la fase.
Esta función comprueba que los puntos donde tocamos queden dentro del recuadro correspondiente (poco más o menos) y mapea una variable P,N o F a valore de 1 a9 o de 0 a 360º en función de la altura del deslizador que se toca.
Y ya solo nos falta el loop:
void loop() { Point p = ts.getPoint(); p.x = map(p.x, TS_MINX, TS_MAXX, 0, 240); // mapear las lecturas p.y = map(p.y, TS_MINY, TS_MAXY, 0, 320); referencias(p.x, p.y) ; delay(200); SenseControl(p.x, p.y) ; if ( P0 != P ) { Tft.fillRectangle(30, 24, 40, 250, GRAY1); // Slider 1 Tft.fillRectangle(30, 24, 40, 22*P+50, BLUE); // PLot Status Slider 1 plotVal(); } else if ( N0 != N ) { Tft.fillRectangle(90, 24, 40, 250, GRAY1); // Slider 2 Tft.fillRectangle(90, 24, 40, 22*N+50, BLUE); // PLot Status Slider 2 plotVal(); } else if (F != F0) { Tft.fillRectangle(150, 24, 40, 250, GRAY1); // Fase Tft.fillRectangle(150, 24, 40, 250*F/360 , BLUE); plotVal(); } N0 = N ; P0 = P ; F0 = F ; }
Cuando detectamos que al tocar en los distintos rectángulos los valores de P,N o F cambian, redibujamos el fondo gris y la parte en azul, para mostrar el avance del deslizador.
Me encantaría escribir OK y Cancel en los botones Verde y Rojo, pero no parece haber modo de girar las letras.
Aquí tenéis el programa completo Prog_45_1
Uniendo el panel táctil y el que dibuja las curvas
El programa anterior mueve los valores de los deslizadores directamente desde el loop principal lo que no nos conviene nada para unirlo con el programa de ploteo de las curvas de Lissajous que vimos en la sesión anterior, por lo que vamos a tener que tocarlo un poquito (Pero poco).
No vamos a usar ninguna idea nueva y se trata solo de adaptar ambos programas para que se mezclen sin hacer grumos.
- Lo que va a generar más de un problemilla, porque no sé si os habéis dado cuenta que hemos cambiado la referencia de origen en ambos programas.
Para ello he mezclado un par de las funciones anteriores procesControl() y senseControl() en una única función , lo que básicamente procesa todo el tema del panel de control y los deslizadores, desde una bucle while, del que solo se sale cuando pulsamos el botón de OK (Devuelve true) o de CANCEL (Que devuelve false). Por lo demás es exactamente lo mismo de antes:
bool procesControl() { initPlot() ; while (true) { Point p = ts.getPoint(); p.x = map(p.x, TS_MINX, TS_MAXX, 0, 240); / p.y = map(p.y, TS_MINY, TS_MAXY, 0, 320); referencias(p.x, p.y) ; delay(200); int x = p.x ; int y = p.y ; if ( (x>20 && x< 65) && ( y>15 && y<270)) // Slider 1 P = map( y,15,270,1,10); if ( (x>90 && x< 133) && ( y>15 && y<270)) // Slider 2 N = map( y,15,270,1,10); if ( (x>145 && x< 195) && ( y>15 && y<270)) // Slider 3 { F = map( y,15,270,-3,360); if (F <0 ) F = 0 ; } if ( x<24) if ( y>16 && y <130) // Boton verde OK return(true); else if ( y>150 && y <270) // Boton rojo cancel return(false); if ( P0 != P ) { Tft.fillRectangle(30, 24, 40, 250, GRAY1); // Slider 1 Tft.fillRectangle(30, 24, 40, 22*P+50, BLUE); // PLot Status Slider plotVal(); } else if ( N0 != N ) { Tft.fillRectangle(90, 24, 40, 250, GRAY1); // Slider 2 Tft.fillRectangle(90, 24, 40, 22*N+50, BLUE); // PLot Status Slider 2 plotVal(); } else if (F != F0) { Tft.fillRectangle(150, 24, 40, 250, GRAY1); // Fase Tft.fillRectangle(150, 24, 40, 250*F/360 , BLUE); plotVal(); } N0 = N ; P0 = P ; F0 = F ; } }
Además he ampliado el loop principal que dibuja las curvas, para que pueda saltar a la función de proceso de los controles:
void loop() { for ( long i = 0 ; i <= 360 ; i++) { float y = 160 + cos( p * i * k + fase) * 120 ; float x = 120 + sin( n * i * k + fase) * 110 ; if (inicio) { x0 = x ; y0 = y ; inicio = false ; } Tft.drawLine(x0,y0,x,y, CYAN); x0 = x ; y0 = y ; } // ……………………………………………………….. Ref() ; Point q = ts.getPoint(); q.x = map(q.x, TS_MINX, TS_MAXX, 0, 240); q.y = map(q.y, TS_MINY, TS_MAXY, 0, 320); int x = q.x ; int y = q.y ; //referencias(p.x, p.y) ; delay(200); if ( x<120 ) { Tft.fillRectangle(0, 0, 240, 320, BLACK); // Borra display if ( procesControl()) ; // SI OK { Tft.fillRectangle(0, 0, 240, 320, BLACK); // Borra display n = N ; p = P ; fase = F * 2 * PI / 360 ; } } }
Fíjate que no hay diferencia en la función que plotea las curvas, pero le hemos añadido más código tras la línea de puntos, que, básicamente, comprueba si se toca la pantalla para de x<120, o sea la parte inferior con la alimentación de Arduino a la derecha.
Si es así, lanza procesControl () para que modifiques los parámetros y a la vuelta asigna los valores de n, p y fase a los que traemos de nuevos y redibuja la curva. No hay más.
Aquí tenéis el programa Prog_45_9 (Si, me temo que ha habido varias versiones) y un mini video mostrando el uso del conjunto.
Resumen de la sesión