Panel táctil y TFTs

Objetivos

 

  • Probar el panel táctil o (touch screen)de un display TFT de 3.2”.
  • Ver los comandos básicos para determinar la posición.
  • Aprender a usar algunos sliders y botones.
  •  

    Material requerido.

    Version R3   Arduino UNO o MEGA.

    Display 3.2" de 320x240 color
     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:

    pines requeridos

    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:

    resistencias equivalentes

    resistencias equivalentes

    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:

    Dibujando controles

    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.

    Panel TFT

    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.

  • Hay alguna que otra cosa más como adaptar tipos de algunas variables para que coincidan, modificar que no imprima el valor de la fase en radianes, que no hay quién lo entienda y en su lugar plotee el valor en grados y cosillas parecidas de ajuste.
  • Como cuando empecé con el dibujo de las curvas no tenía aun en mente, el tema del touch screen, hay cosas a posteriori que he tenido que encajar un poco a martillazos para que encajen.
  • Así que convendría pulir un poco el programa reescribiéndolo desde el principio para dejarlo presentable y poderlo enseñar, pero creo que voy a dejároslo a vosotros. Si alguno se anima lo publicamos aquí para la posteridad.
  •  

    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

     

  • Hemos jugado con el panel táctil para ver que funciona bastante bien y que nos permite evitar añadir  potenciómetros y deslizadores adicionales para el control de nuestra aplicación.
  • Vimos que ha sido bastante sencillo definir zonas activas como botones y deslizadores a nuestro proyecto.
  • Y a pesar de la improvisación de la mezcla hemos sido capaces de combinar los dos programas previos en una aplicación única que nos permite modificar las curvas de Lissajous mediante 3 deslizadores y dos botones.
  •  

    Deja una respuesta