Go Down

Topic: Algunos consejos para usar TFT´s (Read 69460 times) previous topic - next topic

TFTLCDCyg

#210
Aug 27, 2020, 08:27 am Last Edit: Aug 27, 2020, 09:23 am by TFTLCDCyg
Afinando algunos detalles en la carga de imagenes BMP desde el lector SDIO del teensy 4

Con algunos ajustes extra en la rutina de carga de imágenes, podemos enviar al TFT y sin inconvenientes, imágenes de hasta 480x320 pixeles.

Originalmente, la rutina fue diseñada para imágenes de 320x240 pixeles, por lo que la ubicación de alguna imagen en cualquier parte de la pantalla, generaba problemas sobre todo en los límites máximos inferior y derecho. Con los ajustes es posible colocar imagenes de hasta 480x320 px, en algún punto de la superficie del TFT,  y lo mejor, respetando las dimensiones en cualquier rotación.

La condición es que la imagen bmp tenga orientación horizontal, para que la rutina de lectura de las lineas y columnas pueda coincidir con las dimensiones del TFT, incluso en los limites máximos 480 o 320.



Test de carga de imágenes desde el lector SDIO

T4x_ILI9488_BMP
ft81xmania.com/comunidad/

TFTLCDCyg

#211
Nov 09, 2020, 05:51 am Last Edit: Nov 09, 2020, 05:57 am by TFTLCDCyg
ST7735 1.8" 160x128 touch

Hace unos días tratando de localizar un GPS neo7, vi publicación titulada:
"8/10/12pin 1,8 pulgadas MÓDULO DE PANTALLA LCD 1,8 pulgadas TFT LCD puerto serial SPI módulo TFT pantalla a color"

Una de las imágenes que refiere al TFT de 12 pines, me llamó la atención:


Pedí un par de pantallas para experimentar con ellas. La librería de control que se puede usar es la ST7735_t3 que viene en el teensyduino, lo mejor es que puede funcionar en el teensy 4 o en el teensy 4.1. La librería de control para el panel táctil es la XPT2046_Touchscreen.



El pinout de la pantalla une el bus SPI del TFT con el panel táctil, para la pantalla bastan estas líneas:
Code: [Select]
VCC   3.3V
GND
CS1   10  
DC    9  
RES   8  
BLK   3.3V
MOS   11
CLK   13



Para agregar la funcionalidad del panel táctil, se deben conectar estos pines:
Code: [Select]
MIS   13
CS2    5
PEN    2


Este es el mapeo que permite usar la superficie del panel táctil:
Code: [Select]
x = map (p.x, 201, 3950, 0, 160);
y = map (p.y, 3850, 340, 0, 128);


Este es el constructor:
Code: [Select]
ST7735_t3 tft = ST7735_t3(TFT_CS, TFT_DC, TFT_RST);

Para activar el llamado del lector SDIO hay que definir:
Code: [Select]
#define SD_CS BUILTIN_SDCARD

O bien en algunos casos:
Code: [Select]
SD.begin(BUILTIN_SDCARD)

Los últimos ajustes de la librería SD para el teensy, permiten extender su alcance a la librería SDFat beta, por lo que ambas librerías puede usarse para la gestión de imágenes bmp o jpg. Lo que abre la posibilidad de usar el lector SDIO que viene en el teensy 4.1 o 3.6, o el SDIO externo en el teensy 4. Podría también ser factible usar el lector SDIO del teensy 3.5.

Las imágenes BMP se pueden gestionar con el ejemplo que proporcionó Adafruit desde las primeras versiones de la librería GFX: spitftbitmap, con algunos ajustes similares a los que se realizaron en la librería ILI9488_t3 para poder habilitar el uso de la librería SdFat beta y el lector SDIO.

Las imágenes jpg pueden ser gestionadas con la librería JPEGDEC, capaz de administrar la carga de imágenes de mayor formato que el de la pantalla:



Eliminando el cableado provisional y evitar aluno que otro fallo por falso contacto...


En conclusión, es el TFT con funcionalidad táctil más pequeño que podemos usar en nuestros proyectos, tiene apenas una resolución de 160x128 pixeles
ft81xmania.com/comunidad/

TFTLCDCyg

Grafica deslizante para presentar datos en el TFT

Aprovechando la velocidad del teensy 4 (nada menos que 600 mHz!!! en condiciones normales, con posibilidad de 1 GHz con un disipador y un ventilador adecuado)

Visualizar datos en un TFT en el entorno arduino resulta algo complejo de conseguir con las pantallas basadas en librerías estilo Adafruit/GFX: ILI9341, ILI9488, ILI9325, ST7735, etc. Como no disponen de memoria dedicada, todo el trabajo lo realiza el MCU. Es posible agregar algún shield basado en chips F103X, con la idea de que gestione los gráficos, pero la electrónica requerida nos queda fuera de alcance. Existen algunos ejemplos en la red pero es tal la cantidad de subrutinas y de código C rebuscado, que tan solo tratar de aislar el marco de la gráfica es todo un triunfo.

Después de muchos intentos y luego de la llegada de estas diminutas ST7735 de 1.8" táctiles (sigo sin salir del asombro!), decidí retomar el tema.

La aproximación la conseguí con la primitiva más simple: borrar y luego dibujar un pixel.  Para borrar un pixel en una determinada posición en el TFT, lo podemos conseguir colocando en esa posición un pixel con el color del fondo.


Otro concepto tiene que ver con los arrays de datos. Podemos usar un array para almacenar los datos actuales de algún sensor y en otro array podemos almacenar los datos previos de ese sensor. Aquí está la clave: los datos previos serán el conjunto de pixeles que al dibujarlos con el color del fondo, nos ayudarán a borrar los pixeles. Aquí todo muy bien como concepto, pero escribirlo en el código podría parecer complejo...

Al escribir el código traté de mantenerlo de forma modular, lo mas simple posible.

Primero debemos dibujar el marco para graficar. Esta función es fija
Code: [Select]
//posición X, posición Y, número de marcadores en X, número de marcadores en Y, color del marco
void MarcoG(int XM, int YM, int NDX, int NDY, uint16_t color)
{
  int Pitch = 10;  //espaciado fijo de 10 pixeles
    //Marco
  tft.drawRect(XM, YM, Pitch*NDX+1, Pitch*NDY+1, color);

    //Divisores eje X
  int altoMarcaX = 4;  //Número de pixeles de cada marcador
  for (int i=XM; i<=XM+Pitch*NDX; i+=Pitch)
  tft.drawLine(i, YM+Pitch*NDY, i, YM+Pitch*NDY+altoMarcaX, color);

    // Divisores eje Y
  int largoMarcaY = altoMarcaX;
  for (int i=YM; i<=YM+Pitch*NDY+1; i+=Pitch)
  tft.drawLine((XM-largoMarcaY), i, (XM-1), i, color);
}

Para fines prácticos, el marco estará formado por arreglos cuadrados de 10 pixeles por lado, por lo que cada celda representa 10 unidades en el eje X y 10 en el eje Y.

En el eje X se representa el número de datos y el en eje Y los datos del sensor, normalizados a marcadores de 10 unidades

Antes de dibujar algún pixel, debemos tener datos como línea base. Esta función es fija
Code: [Select]
void lineabaseG1(int BaseD)
{
  for (int i=0; i<maxlecturas; i++)
    {
       //AdquiereDatosG1();
       lecturaG1[i] = BaseD;  
    }    
}  


Para efectos prácticos, el array principal lo designé como lecturaG1[], almacenará los datos actuales y los datos previos estarán en lecturapreviaG1[], la línea base llena el array de datos actuales con algún valor dentro del rango de los datos del sensor.

Los datos los obtendremos con la función AdquiereDatosG1. En ella podemos colocar la rutina con la que adquirimos datos del sensor, en este caso coloqué alguna función simple para simular un termómetro con escala Celcius, con lecturas en el rango de 1 a 25. Cabe señalar que se debe agregar una operación de mapeo, escalado o factorización, con la finalidad de obtener valores que se puedan dibujar en el espacio de pixeles del TFT.

Si el sensor arroja valores en un rango de 0 a 10000, podemos dividir los datos del sensor entre 100, para que la lectura se adapte a la escala de la pantalla. En este ejemplo los valores corresponden 1 a 1. Esta función puede ser adaptada por el usuario
Code: [Select]
float velTCG1 = 1;
void AdquiereDatosG1()
{
   //TempCG1=random(20,30);

   TempCG1= TempCG1 + velTCG1;
   if (TempCG1>=25){velTCG1=-1;}
   if (TempCG1<=1){velTCG1=1;}
  
   if (TempCG1<=0){TempCG1=0;}
   LecturaTAG1 = TempCG1;
  
// Truco para conservar 2 decimales en la presentación de la temperatura
   //TAG1=TempCG1*100;
   TAG1=TempCG1*1;
}


Para efectos prácticos, TempCG1 es el dato actual del sensor, LecturaTAG1 y TAG1, son valores de respaldo que podemos manipular para efectos de presentación en pantalla, como señalamos previamente, nos permitirán escalar los valores del sensor para poder manejarlos dentro del marco de la gráfica, además nos permitirán  conservar valores decimales por ejemplo para la impresión de datos en pantalla.

La rutina que da vida a la gráfica continua de datos es esta (función fija)
Code: [Select]
long previousMillisG1 = 0;
long intervalG1 = 100;   //7000

int jG1;  // contador para recorrer los datos de la lista actual
void LineaDatosG1(int xinicial, int ybase, int NDX, int NDY, const int maxlecturas, uint16_t color) //permite ajustar el numero máximo de datos de las bases lecturaprevia y lectura
{
  int escala = 1;
  int yDatoTXT = ybase;
  ybase = ybase + 10*NDY;
  
  unsigned long currentMillisG1= micros();        
  if(currentMillisG1 - previousMillisG1 > intervalG1)
  {
    previousMillisG1 = currentMillisG1;
  
    lecturapreviaG1[jG1]=lecturaG1[jG1];  // almacena el dato actual
  // Recorre una posición. Los datos de la lista se recorren de adelante hacia atrás, para dejar libre el último espacio de la lista

     lecturaG1[jG1] = lecturaG1[jG1+1];  
     tft.drawPixel(jG1+xinicial, ybase-escala*lecturapreviaG1[jG1], ST77XX_BLUE); //borra el pixel previo
     tft.drawPixel(jG1+xinicial, ybase-escala*lecturaG1[jG1], color); //gráfica el pixel nuevo
     jG1++;

// Continua recorriendo los datos hasta llegar al último de la lista, en el que se colocará la nueva lectura de datos
      if (jG1==maxlecturas-1)
       {
         AdquiereDatosG1();
         lecturapreviaG1[jG1]=lecturaG1[jG1];        
         lecturaG1[jG1] = TempCG1;  
                
         tft.drawPixel(jG1+xinicial, ybase-escala*lecturapreviaG1[jG1], ST77XX_BLUE); //borra el pixel previo
         tft.drawPixel(jG1+xinicial, ybase-escala*lecturaG1[jG1], color); //gráfica el pixel nuevo
         tft.setCursor(xinicial+1, yDatoTXT+2); tft.setTextColor(color, ST77XX_BLUE); sprintf(TXP,"%2d", TAG1); tft.println(TXP);
         jG1=0;
       }
  }  
}

Se aceptan sugerencias de mejora.

Solo falta: lo que va en el encabezado, la parte modificable por el usuario de la gráfica dentro del sketch y la parte de presentación de la gráfica en el TFT.


Encabezado del sketch


En el encabezado para cada sensor debemos agregar estas líneas, el ejemplo muestra como se pueden manejar dos sensores de temperatura
Code: [Select]
char TXP[50];
const int maxlecturas = 139; // 139   // Variable que permite manipular el tamaño de todos los arrays de datos, esta relacinado con el largo del eje X, provisional

//sensor 1
float TempCG1 =20;        // dato decimal
int TAG1, LecturaTAG1;    // para manejar los decimales (experimental no usado aún
float lecturaG1[maxlecturas];  // base de datos actual
float lecturapreviaG1[maxlecturas]; // base de datos previa
//sensor 1

//sensor 2
float TempCG2 =20;        // dato decimal
int TAG2, LecturaTAG2;    // para manejar los decimales (experimental no usado aún
float lecturaG2[maxlecturas];  // base de datos actual
float lecturapreviaG2[maxlecturas]; // base de datos previa
//sensor 2


Para agregar un tercer sensor bastaría agregar estas líneas:
Code: [Select]
//sensor 3
float TempCG3 =20;        // dato decimal
int TAG3, LecturaTAG3;    // para manejar los decimales (experimental no usado aún
float lecturaG3[maxlecturas];  // base de datos actual
float lecturapreviaG3[maxlecturas]; // base de datos previa
//sensor 3


Las funciones que vimos:
Code: [Select]
void LineaDatosG1()
void lineabaseG1()
void AdquiereDatosG1()


Deben copiarse y modificarse para cada sensor que agreguemos, solo hay que sustituir el índice al final del nombre. En el interior de las funciones también se debe modificar el índice en cada variable.

Mas adelante podría ser factible simplificar a solo tres funciones para n sensores, sin tener que copiar, pero aun es un trabajo en progreso, posiblemente involucrará un array para el ID de los sensores.
ft81xmania.com/comunidad/

TFTLCDCyg

#213
Nov 11, 2020, 05:01 am Last Edit: Nov 11, 2020, 05:23 am by TFTLCDCyg
Configuración en el sketch

Normalmente no uso void loop() en los sketchs, esto permite crear presentaciones con menús completamente independientes unos de otros, lo que permite crear un sistema modular con el que podemos agregar nuevos elementos al menú principal sin afectar a los que ya funcionan. Además cada módulo puede funcionar si lo aislamos del resto para efectos de depuración. Este es el menu principal de trabajo, en este caso será lo que mostraremos en pantalla:

Code: [Select]
void MP()
{
 
 tft.setRotation(Rotacion);
 tft.fillScreen(ST77XX_BLUE);
 tft.setTextSize(1);
 tft.setCursor(10, 3); tft.setTextColor(ST77XX_WHITE);  tft.println("Grafica deslizante");

 lineabaseG1(10);
 int xbaseG1 = 30; //origen en X del TFT
 int ybaseG1 = 13; //origen en Y del TFT
 int NDXG1 = 10;   //mínimo 2, máximo 14, marcadores horizontales
 int NDYG1 = 5;    //minimo 4, máximo 10, marcadores verticales
 MarcoG(xbaseG1, ybaseG1, NDXG1, NDYG1, ST77XX_RED);

 lineabaseG2(1);
 int xbaseG2 = 30; //origen en X del TFT
 int ybaseG2 = 17+5+5*10; //origen en Y del TFT
 int NDXG2 = 10;   //mínimo 2, máximo 14, marcadores horizontales
 int NDYG2 = 5;    //minimo 4, máximo 10, marcadores verticales
 MarcoG(xbaseG2, ybaseG2, NDXG2, NDYG2, ST77XX_RED);
 
 while(1)
  {
   //Datos del sensor G1
   LineaDatosG1(xbaseG1+1, ybaseG1, NDXG1, NDYG1, (NDXG1*10)-1, ST77XX_GREEN);
   //Datos del sensor G2
   LineaDatosG2(xbaseG2+1, ybaseG2, NDXG2, NDYG2, (NDXG2*10)-1, ST77XX_YELLOW);
  }
}

El menú principal lo podemos llamar desde el setup del sketch.


La parte en la que podemos configurar la posición del marco, dimensiones y el número de marcadores en cada eje
se consigue con estas líneas:
Code: [Select]
lineabaseG1(10);
 int xbaseG1 = 30; //origen en X del TFT
 int ybaseG1 = 13; //origen en Y del TFT
 int NDXG1 = 10;   //mínimo 2, máximo 14, marcadores horizontales
 int NDYG1 = 5;    //mínimo 4, máximo 10, marcadores verticales
 MarcoG(xbaseG1, ybaseG1, NDXG1, NDYG1, ST77XX_RED);


Para agregar un nuevo sensor basta con copiar las líneas y modificar el índice. en el ejemplo, esto configura el marco del segundo sensor:
Code: [Select]
lineabaseG2(1);
 int xbaseG2 = 30; //origen en X del TFT
 int ybaseG2 = 17+5+5*10; //origen en Y del TFT
 int NDXG2 = 10;   //mínimo 2, máximo 14, marcadores horizontales
 int NDYG2 = 5;    //minimo 4, máximo 10, marcadores verticales
 MarcoG(xbaseG2, ybaseG2, NDXG2, NDYG2, ST77XX_RED);

Dado que estos parámetros son fijos en el skech, van en la parte inicial, después del setup.

Gráfica continua dentro de la sección ejecutable del sketch

Para el sensor 1:
Code: [Select]
  //Datos del sensor G1
   LineaDatosG1(xbaseG1+1, ybaseG1, NDXG1, NDYG1, (NDXG1*10)-1, ST77XX_GREEN);


Nuevamente para el sensor 2, basta con copiar la línea anterior, modificando el índice respectivo:
Code: [Select]
  //Datos del sensor G2
   LineaDatosG2(xbaseG2+1, ybaseG2, NDXG2, NDYG2, (NDXG2*10)-1, ST77XX_YELLOW);


La primer gráfica presenta datos en base a una función que oscila linealmente entre 1 y 25, cada 100 microsegundos, la segunda gráfica presenta valores aleatorios entre 20 y 30 cada 4000 microsegundos

Video del skecth Gráfica deslizante en ST7735 touch
ft81xmania.com/comunidad/

TFTLCDCyg

#214
Dec 31, 2020, 01:07 am Last Edit: Dec 31, 2020, 01:20 am by TFTLCDCyg
Conectando dos ILI9341 en un Teensy .6

El comportamiento del puerto SPI a veces nos puede dar dolores de cabeza, si no exploramos el comportamiento de los pines alternos a los clásicos 11-12-13 con los que estamos familiarizados en el entorno arduino. Es posible conectar dos pantallas SPI en una misma placa con la capacidad de controlarse de forma independiente.

En este caso exploraremos esta posibilidad en una placa Teensy 3.6. En ella los pines que acceden al bus SPI1, están identificados con el número 0 al final de su asignación; por ejemplo CS0, MISO0, MOSI0 y SCK0. Para el bus SPI12 el número al final es el 1. Hay que cuidar esta nomenclatura para no perdernos en el cableado de las pantallas. Algunos de estos pines SPI pueden funcionar en los dos buses SPI1 o SPI2, pero debo aclarar que no todos. Suena algo confuso, pero en la practica, hay que pasar un buen rato de prueba y error para dar con la combinación correcta de pines para la segunda pantalla.

Lo mejor es conectar la primer pantalla en un cabezal para evitarnos algún susto. En ella el cableado SPI  es el de siempre: 8-9-10-11-13 (RESET-DC-CS-MOSI-SCK). Confirmamos quela pantalla va como debe, subiendo algún sketch de la librería ILI9341_t3. Con este paso verificado, ya podemos avanzar al siguiente nivel: conectar la segunda pantalla a SPI1.

Ahora viene lo interesante. No todos los pines PWM nos pueden servir para conectar la segunda pantalla. Dado que el MCU es muy compacto, pensé que seria solo cuestión de conectar en los pines alternos, pero para mi sorpresa no todos funcionan.

Luego de algunas horas de prueba y error, encontré la combinación correcta para CS-DC-RESET de la segunda pantalla. MOSI y SCK van a 11 y 13 respectivamente, para el bus SPI1.

Estos son los dos constructores que podemos usar:

Code: [Select]
#define TFT_RST  8
#define TFT_DC   9
#define TFT_CS  10
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST);

#define TFT_RST2 21
#define TFT_DC2  20
#define TFT_CS2  15
ILI9341_t3 tft2 = ILI9341_t3(TFT_CS2, TFT_DC2, TFT_RST2);


Se crean dos instancias para poder usar la misma librería ILI9341_t3: tft y tft2

En el setup debemos iniciar por separado cada tft.

Code: [Select]
tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLUE);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(1);
  tft.setCursor(320-80,10);
  tft.print("First TFT");
  
  tft2.begin();
  tft2.setRotation(3);
  tft2.fillScreen(ILI9341_RED);
  tft2.setTextColor(ILI9341_WHITE);
  tft2.setTextSize(1);
  tft2.setCursor(320-80,10);
  tft2.print("Second TFT");


Esto nos permitirá controlar de forma independiente cada pantalla:


Luego de un rato de meditarlo, me gustaría usar el lector SDIO del teensy 3.6, para, poder acceder a las imagenes de un lector microSD, con apoyo de la librería SdFat beta: ¿será posible enviar imágenes independientes bmp o jpg a cada pantalla?...
ft81xmania.com/comunidad/

TFTLCDCyg

#215
Feb 07, 2021, 03:54 am Last Edit: Feb 07, 2021, 04:20 am by TFTLCDCyg
El proyecto: Video-GPS

Luego de un par de años intentado armar un dispositivo que me permita obtener información de posicionamiento, distancias en tiempo real, altura, o temperatura ambiente; además de contar con algo de entretenimiento en los ratos de trabajo frente a la PC cuando reviso los datos, en el propio trabajo durante los trayectos largos hasta la zona en la que debo obtener información; finalmente pude integrar los elementos necesarios, aunque en la mesa de trabajo realmente son pocos, creo que cumplen con el objetivo y dan la posibilidad de incrementar sensores: brújula digital, acelerómetro, intensidad UV, intensidad de luz natural, etc.

Incluso he visto un contador Geiger para arduino, ¡solo que sus dimensiones son demasiados grandes debido a la electrónica y del propio tubo del detector!



Reproductores de archivos de video con audio hay muy pocas opciones, con casi nada de información detrás de ellos.

Las modificaciones a la librería para el gameduino 23X creada por James Bowman, que hemos definido como GD23ZUTX, han permitido usar el lector SDIO de las placas teensy 3.5, 3.6 y 4.1, y lo mejor de todo es: capaz de reproducir videos con audio. Los cuales hasta el momento solo habían estado disponibles de forma nativa para el arduino UNO y el MEGA, con tamaños y velocidades limitadas por los 16 Mhz de micros AVR. En el due la velocidad de reproducción es muy limitada. En estas placas se usa la librería SD como base.

GD23ZUTX emplea la librería SdFat beta, ya que esta versión es 100% compatible con teensy 4.x

Componentes del proyecto

Para darles una idea del lío en el que me he metido, los elementos que integran el proyecto son:

TFT

Pantalla táctil capacitiva con superficie activa de 3.5", con una resolución máxima de 800x480 px, gracias al chip EVE FT813: NHD-3.5-320240FT-CSXN-CTP

Tiene una característica interesante, puede emplearse en exteriores aun con luz de sol.



MCU

Teensy 4.1

Para impulsar el conjunto este micro es excelente ya que puede modificarse su frecuencia de operación desde 150 MHz hasta 816 Mhz sin ventilador extra, solo con un disipador; pudiendo llegar hasta 1.008 GHz pero con disipación activa.

Con únicamente 150 MHz puede con la tarea que le he encomendado, minimizando lo mas posible su consumo de energía.

GPS

Intenté varias opciones, pero las mejores son estas dos variantes:

NEO-7M


NEO-6M


Para mejorar la recepción conseguí una antena similar a esta, con conector SMA


El GPS 6M, puede utilizarse con la antena, consiguiendo un conector SMA a IPEX hembra, como este:

En condiciones adecuadas pueden enlazar sostenidamente entre 8 y 10 satélites, gracias a la antena.

Batería recargable

Uno de los elementos mas complicados es la fuente de energía, aunque cabe la posibilidad de instalar una batería ligera como las empleadas para drones, no es problema conseguir algo más robusto para los recorridos largos. Encontré esta opción

POWER BANK de 20000mAh marca 1Hora (Hecho en México) cuenta con una salida de 1A y una de 2.1A.



En la primer prueba, usando el conector de 1A, el dispositivo ha estado funcionando de forma continua durante mas de 16 horas y apenas está al 55%  de su capacidad. Con dos baterías de este tipo vaya que podría cumplir con recorridos muy exigentes (por la situación actual en confinamiento). Cuenta con un indicador LCD de muy bajo consumo que reporta la capacidad restante.

Almacenamiento

EEPROM grado industrial 24FC512-I/ST
MicroSD de 32 Gb Kingston CANVAS select Plus

La memoria EEPROM la utilicé como medio para almacenar los datos del GPS, aunque se pueden almacenar en la memoria microSD, decidí hacerlo de esta forma mas bien por un tema didáctico. En campo ha resultado ser muy rápido el guardado y posterior lectura de la eeprom.

En la microSD va el contenido multimedia: imágenes JPG, PNG, conjunto de assets, arreglos de celdas verticales y video en formato AVI compatible con el decodificador JPG del chip FT813.

Con apoyo de la librería SdFat beta, la librería modificada GD23ZUTX va muy bien con el lector SDIO del teensy 4.1, por lo que fue posible implementar el reproductor de video.

La pantalla cuenta con una salida de audio, por lo que es posible reproducir video con audio con una calidad decente.

Existen dispositivos bluetooth de recepción/transmisión de audio que pueden ayudar a enviar la señal a parlantes portátiles, aunque también se puede usar un cable de audio de 3.5 mm. En este caso conseguí este modelo


Las piezas reunidas:





El PCB para prototipos mide 8x12 cm, es de doble cara. No voy a diseñar uno, creo que es mejor dejarlo así, con toda la contaminación que existe hoy día. Quizás algún día algo lejano me anime con una placa a medida, pero no es el objetivo por el momento.

Test de la primer prueba sin contratiempos!!!

El primer cambio entre la pantalla de inicio y el menú principal siempre me daba un error, posiblemente no salía del bucle de imágenes de forma correcta. Al intentar navegar entre los botones de los diferentes menus secundarios, saltaban errores, estaban relacionados al primero. Aislé cada menú del loop principal, de hecho no lo usa, así quedó el setup:

Code: [Select]
void setup()
{
  strip.begin();  strip.show();  // Turn all LEDs off
  Wire.begin();

  DatosAlmacenados=eep.read(99);
 
  ss.begin(GPSBaud);

  Wire.beginTransmission(MAX6697);
   Wire.write(T0);
   Wire.write(T1);
   Wire.write(T2);   
   Wire.write(T3);   
   Wire.write(T4);
   Wire.write(T5);   
   Wire.write(T6);
  Wire.endTransmission();
 
  GD.begin();
  IDEMCU();
  MenuP();
  //Mira_Mapa();
}
void loop(){}


Mas adelante usaré leds APA102 o DotStar con la idea de agregar alguna iluminación de aviso, es por ello que está el llamado en el setup. Adicionalmente instalaré un chip MAX6697 para monitoreo de temperatura mediante sondas pequeñas, de allí el llamado a la librería Wire, aún estoy en el diseño de la librería para este chip, ya que no existe hasta el momento.

PD: una pena!, pasé por alto recargar la batería del cel, por eso el video termina abruptamente, mil disculpas por ese detalle.
ft81xmania.com/comunidad/

gatul


TonyDiana


TFTLCDCyg

#218
Feb 07, 2021, 07:18 am Last Edit: Feb 07, 2021, 07:34 am by TFTLCDCyg
Gracias, sigo asombrado, no pensé que hoy por fin vería (bueno ayer) todo reunido y creo que funcionando XD.

Aún falta tramo por recorrer, pero los primeros pasos ya están hechos. Subiré el código completo en cuanto ya no salten errores, aun hay algunos puntos por afinar y me gustaría depurar un poco más las líneas, si es que es posible, y no perderme entre algunos cientos de ellas.

Posiblemente una carcasa vendría bien para proteger el PCB, me gusta la idea de diseñarla y luego tener la posibilidad de imprimirla, ya veremos. También me gustaría agregar algún sistema de sujeción para el antebrazo, para tener los brazos libres.

Así es como se ven los lugares en los que toca ir a trabajar:



Ahora si deseo saber cuanto he caminado para confirmar los datos que a veces me pasan por teléfono, ni como ir a la computadora más cercana, tengo que saberlo en el sitio, el GPS ya me permite hacer esta tarea:


La batería por el momento no irá en la carcasa, la idea es usar el GPS con alguna mochila que tenga su propio powerbank, o con algún cargador de pared en el escritorio o algún cargador en el auto, en fin, cualquier fuente de alimentación tipo microUSB.

Depuración del código

Algunas instrucciones enviadas a la pantalla parece que generan alguna especie de interferencia en la salida de audio. Por ejemplo estas dos líneas generan un ruido de fondo antes de reproducir un archivo de video:

Code: [Select]
GD.ColorA(25);
GD.cmd_gradient(0, 0, 0x000045, 0, 271, 0x010000);


La primera reduce mediante PWM la intensidad del color de la pantalla, generando una transparencia en cualquier objeto que se dibuje: imagen, botó, slider, etc. La segunda produce un degradado horizontal de dos colores, posiblemente el coprocesador utilice alguna subrutina interna que tenga que ver con ColorA.

Al sustituir por un color simple el fondo de pantalla, el ruido en la salida de audio se reduce notablemente, esta secuencia básica parece ser una buena solución:

Code: [Select]
  GD.ClearColorRGB(0x000010);
   GD.Clear();
   GD.get_inputs();


El uso de instrucciones que conservan los parámetros iniciales del coprocesador podría ser un camino ideal. Esto lo se puede conseguir con estas dos líneas:

Code: [Select]
GD.SaveContext();
GD.RestoreContext();


La primer línea guarda los parámetros gráficos previos a ella: color de fondo, color de botones, fondo de botones, y parece que valores PWM. La tarea pendiente es rastrear todos y cada uno de los cambios realizados en el entorno gráfico, con la idea de conservarlos hasta el justo el momento antes de la reproducción del primer archivo de video. También es la de agregar los necesarios para que no se pierda la configuración inicial.

En la ruta intermedia podrían usarse gradientes y transparencias sin afectar la señal de audio. Como son pantallas que casi nadie usa y menos a este punto, parece toca hacer la brecha en medio de una jungla sin explorar...

PD: nadie dijo que sería fácil. Gracias Tomas aunque te adelantaste en este camino, seguimos al pie de la letra tus consejos. ¡Grande por siempre hermano!
ft81xmania.com/comunidad/

TFTLCDCyg

#219
Feb 08, 2021, 12:55 am Last Edit: Feb 08, 2021, 12:56 am by TFTLCDCyg
Para prevenir algún fallo en el puerto microUSB del teensy 4.1, debido al uso frecuente, decidí construir un cable-puente hacia un puerto microUSB secundario que pueda ser reparado si es que se llegase a requerir.

Tomé un cable usb en buen estado, con capacidad de transferencia de datos 100% funcional y lo recorté, dejando el conector microUSB con unos 13 cm de largo. El cabezal en el que instalé el conector microUSB hembra secundario, cuenta con pines extralargos, con la finalidad de incluso poder usar un cable con 4 pines en lugar del conector típico.





El cable usb de la batería no posee hilos de transferencia de datos, solo tiene hilos para GND y 5V, esta característica reduce en algo el ruido en la salida de audio del chip FT813.





El cabezal secundario puede ser utilizado de forma normal para actualizar alguna característica del programa mediante el IDE de arduino.
ft81xmania.com/comunidad/

TFTLCDCyg

#220
Feb 13, 2021, 01:13 am Last Edit: Feb 13, 2021, 01:18 am by TFTLCDCyg
Nuevamente me he dado a la tarea de modificar la librería para gameduino 23X de James Bowman. Esta librería está diseñada para funcionar en placas AVR, arduino due , algunas STM32, ESP32, ESP8266 y en las placas teensy.

No obstante su gran valor, creo que merece impulsar la parte multimedia apoyándola de la librería SdFat beta de Greiman. Con la gran ayuda que en su momento me bridó mi hermano Tomas con su trabajo en placas STM32, con la colaboración de Javier y del amigo de oriente nopnop2002, luego de varias semanas de prueba y error, aislando las optimizaciones de la librería GD23ZUTX, decidí aventurarme a modificar directamente la última versión de la librería de James, para que funcione exclusivamente con las placas teensy que tienen lector SDIO (3.5, 3.6 y 4.1) o que pueden incorporar un lector externo microSD con estas características (teensy 4).

La idea es poder agregar fácilmente pantallas de las familias FT81x o BT81x, con ajustes mínimos. No ha sido tarea fácil conjuntarlo todo, ya que se debe rastrear los cambios uno por uno, descartando aquellas instrucciones que aplican para otras plataformas y que no intervienen en las rutinas de interés.

Lo fundamental de la librería SdFat-beta es el llamado al lector SDIO, para ello hay que distribuir correctamente estas instrucciones, de lo contrario no funcionarán.

Code: [Select]
SdFat32 SD
SD.begin(SdioConfig(FIFO_SDIO))
File32 r o File2 archivo


Gracias a Javier conseguimos la codificación correcta para que dos instrucciones fundamentales: load y safeload, puedan cargar en pantalla los archivos de assets o de imágenes, mediante SdFat, que en la experimentación actual, funcionan correctamente para SdFat-beta

Originalmente esas dos funciones trabajan con la librería SD. La versión para SdFat es esta:

Code: [Select]
byte GDClass::load(const char *filename, FUNC_POINTER progress)
{
//SdFat beta V2
   File32 archivo;
   archivo = SD.open(filename);
//SdFat beta V2
  return (loadSdFat(archivo, progress));
}
byte GDClass::loadSdFat(File32& archivo, FUNC_POINTER progress)
{
  int offset=0;
  int offsetFT;
  int bytesDisponibles;
  int wbn;
  GD.__end();
   if (archivo) {
   int tamArchivo=archivo.available();
   while( offset < tamArchivo)
    {
        uint16_t m = ( (archivo.available() > TAM_BUFFER_SD) ? TAM_BUFFER_SD : archivo.available());
        archivo.read(buf,m);
        offsetFT=0;
        bytesDisponibles=m;
        while (offsetFT < m)
        {
           uint16_t n = ( (bytesDisponibles > TAM_BUFFER_FT) ? TAM_BUFFER_FT : bytesDisponibles);
           wbn = (n + 3) & ~3;   // force 32-bit alignment
           GD.resume();
           if (progress)
           (*progress)((offset+offsetFT), tamArchivo);
            #ifdef DUMMY_GDCOPYRAM
             memcpy (FTbuf, buf+offsetFT, wbn );
            #else
              GD.copyram((buf+offsetFT), wbn);
             #endif             
             offsetFT+=n;
             bytesDisponibles-=n;
             GDTR.stop();
        }
        offset+=m;         
    }
    GD.resume();

    #ifdef TEENSYDUINO
      SPI.endTransaction();
    #endif
    return 1;
  }
  GD.resume();
     #ifdef TEENSYDUINO
       SPI.endTransaction();
     #endif
     return 0;
}


Se agregaron un par de instrucciones de finalización de comunicación SPI para cesar el llamado al lector SDIO, ya que después de cargado el archivo en la GRAM de la pantalla, ya no es necesario que el bus esté activo, al dejarlo activo, salta una pantalla de error que no necesariamente tiene que ver con la ausencia o error en el archivo de imagen jpg o arreglo de assets. Aplica también para la reproducción de archivos de audio.

Las familias FT81x o BT81x, soportan la reproducción de archivos avi con audio. Gracias a nopnop2002, ajustó la rutina MoviePlayer para cargar videos desde un lector externo en placas STM32 como la Blue Pill STM32F103

Tocaba entonces unir loadsdfat, con MoviePlayer para que ambas puedan usar la librería SdFat-beta dentro de la librería modificada gameduino 23X y para complicar más las cosas, en las placas teensy 4 o 4.1.

La clase MoviePlayer adaptada por nopnop2002 es:

Code: [Select]
class MoviePlayer
{
  uint32_t mf_size, mf_base, wp;
  //Reader r;
  File r;
  void loadsector() {
    byte buf[512];
    GD.__end();
    //r.readsector(buf);
    int32_t c = r.curPosition();
    int32_t p = r.peek();
//Serial.print("curPosition=" + String(c));
//Serial.print(" peek=" + String(p));
    int n = r.read(buf,512);
//Serial.println(" n=" + String(n));
    GD.resume();
    GD.wr_n(mf_base + wp, buf, 512);
    wp = (wp + 512) & (mf_size - 1);
//Serial.println("wp=" + String(wp));
  }

public:
  int begin(const char *filename) {
Serial.println("MoviePlayer begin filanem=" + String(filename));
    if (ft8xx_model == 0) GD.alert("FT800 is not support MoviePlayer");
    mf_size = 0x40000UL;
    mf_base = 0x100000UL - mf_size;
    GD.__end();
    //if (!r.openfile(filename)) {
    if (!r.open(filename)) {
      Serial.println("MoviePlayer open failed");
      return 0;
    }
Serial.println("MoviePlayer open ok");
    GD.resume();

    wp = 0;
    while (wp < (mf_size - 512)) {
      loadsector();
    }
Serial.println("MoviePlayer loadsector ok");

    GD.cmd_mediafifo(mf_base, mf_size);
Serial.println("MoviePlayer mediafifo ok");
Serial.print("REG_MEDIAFIFO_WRITE=");
Serial.println(REG_MEDIAFIFO_WRITE,HEX);
    GD.cmd_regwrite(REG_MEDIAFIFO_WRITE, wp);
    GD.finish();

    return 1;
  }
  int service() {
    //if (r.eof()) {
    if (r.peek() == -1) {
      return 0;
    } else {
      //byte buf[512];

      uint32_t fullness = (wp - GD.rd32(REG_MEDIAFIFO_READ)) & (mf_size - 1);
      while (fullness < (mf_size - 512)) {
        loadsector();
        fullness += 512;
        GD.wr32(REG_MEDIAFIFO_WRITE, wp);
      }
      return 1;
    }
  }
  void play() {
Serial.println("MoviePlayer play start");
    GD.cmd_playvideo(OPT_MEDIAFIFO | OPT_FULLSCREEN);
    GD.flush();
    while (service())
      ;
    GD.cmd_memcpy(0, 0, 4);
    GD.finish();
Serial.println("MoviePlayer play end");
  }
};


Tanto MoviePlayer como loadsdfat, se reacondicionaron para funcionar con SdFat-beta, considerando las instrucciones base.

Tras varias semanas de prueba y error, finalmente conviven todas ellas en la librería modificada GDTeensy4X. El objetivo secundario consistió en aislar las posibles fuentes de ruido en la señal de audio de los videos.

Tres de ellas son relevantes y obligaron a modificar la rutina GD.begin(), parece que contribuyen a reducir el ruido de fondo:

Code: [Select]
Suprimir la activación del bus SPI para cualquier lector externo
Habilitar el llamado al lector SDIO antes de activar los registros de la pantalla
Modificar la rutina GD.begin() para activar el registro de la pantalla REG_PCLK, hasta el primer llamado a GD.swap()


La rutina GD.begin() quedó de esta forma:
Code: [Select]
void GDClass::begin(uint8_t options, int cs) {

 GDTR.begin0();
 byte external_crystal = 0;
 SD.begin(SdioConfig(FIFO_SDIO));

 begin1:
 GDTR.begin1();
 
  #if(SizeFT813==51)
   GD.wr32(REG_PCLK, 2);
  #endif
  #if(SizeFT813==35)
   GD.wr32(REG_PCLK, 6);//5
  #endif

  ClearColorRGB(0x000000);
  Clear();
  swap();
  finish();


Esta es la fracción original presente en la libreria para gameduino:

Code: [Select]
void GDClass::begin(uint8_t options, int cs, int sdcs) {
#if defined(ARDUINO) || defined(ESP8266) || defined(ESP32) || defined(SPIDRIVER)
  GDTR.begin0(cs);
  if (STORAGE && (options & GD_STORAGE))
    SD.begin(sdcs);
#endif

  byte external_crystal = 0;
begin1:
  GDTR.begin1();

  Clear();
  swap();
  finish();


He podido conectar tres pantallas FT813:

- 3.5" NHD en la teensy 4.1
- 5" Riverdi en la teensy 4
- 4.3" NHD en la teensy 3.6

Todas funcionan correctamente con las modificaciones en la librería y lo mas importante con apenas ruido de fondo.



PD: la instrucción SdFat32 SD debe ir en el encabezado del archivo ccp, inmediatamente después del llamado a la libreria SdFat
ft81xmania.com/comunidad/

TFTLCDCyg

#221
Feb 18, 2021, 10:07 am Last Edit: Feb 18, 2021, 10:11 am by TFTLCDCyg
Ya con la librería avanzada, y para continuar con mas pruebas sobre su comportamiento, me aventuré a darle un vistazo a la librería arduinoFFT, compilé algunos ejemplos con el teensy 4, para mi sorpresa no saltaron errores graves, solo algunas advertencias. La versión de la librería es:

Code: [Select]
02/10/18 v1.4

Revisando en la red vi este proyecto, me pareció que podía usarlo como base, ya que explica muy bien el uso de la librería y de dónde salen los valores de frontera que se deben usar para las diferentes bandas en el análisis FFT. Está enfocado a ESP32 y leds direccionables, pero no fue complicado aislar las rutinas principales para explorar su funcionamiento en la pantalla FT813 de 5".
Luego de depurar algunas líneas, por fin veo una FFT en la pantalla...

PD: ya es algo tarde, mas adelante subiré algunos comentarios, adjunto el sketch.
ft81xmania.com/comunidad/

TFTLCDCyg

#222
Feb 18, 2021, 06:22 pm Last Edit: Feb 18, 2021, 06:24 pm by TFTLCDCyg
Al cabo de varios años de experimentar con la familia de pantallas EVE-X (FT800, FT81X, BT81X), me he percatado que presentan un detalle que no se refiere en algún documento técnico: la persistencia de imagen luego de mantener pixeles estáticos durante períodos muy prolongados.

Una solución simple es que los pixeles se puedan refrescar mediante un barrido frecuente. Esta es la rutina de protección que parece soluciona ese detalle:

Code: [Select]
int XProtector=0, velXProtector=1, color;
void Protector01()
{
 XProtector=XProtector+velXProtector;
 if(XProtector>=GD.w)
   {
    velXProtector=-1;
    color = color + 1;
    if (color>=3){color=0;}
   }
 if(XProtector<=0)
   {
    velXProtector=1;
    color = color + 1;
    if (color>=3){color=0;}
   }
 GD.SaveContext();
   if (color==0)
    {
     GD.ColorRGB(0xff0000);
    }
   if (color==1)
    {
     GD.ColorRGB(0x00ff00);
    }
   if (color==2)
    {
     GD.ColorRGB(0x0000ff);
    }
   //sprintf(TXP,"color = %d", color); GD.cmd_text(GD.w-41, 5, 20, 0, TXP);    
   GD.LineWidth(0.3 * 16);
   GD.Begin(LINES);
   GD.Vertex2f((XProtector)*16,(0)*16);  GD.Vertex2f((XProtector)*16,(GD.h)*16);
 GD.RestoreContext();
}


Dibuja una línea vertical de apenas 5/16 de espesor, al llegar a un extremo cambia de canal de color y regresa hacia el otro extremo, la secuencia es R-G-B, de esta forma se evita la aparición de imágenes persistentes de fondo. Dadas sus características no interfiere con el funcionamiento del programa que se esté ejecutando en primer plano, sugiero que esta rutina se agregue al final, para que el 100% de pixeles reciba esa actualización frecuente.
ft81xmania.com/comunidad/

TFTLCDCyg

#223
Feb 18, 2021, 08:17 pm Last Edit: Feb 18, 2021, 08:21 pm by TFTLCDCyg
Librería arduinoFFT en teensy 4.x

La matriz de vectores que se puede obtener del procesamiento de una señal de audio mediante rutinas FFT tiene dos características:

1. Actualización muy rápida. 40000 Hz no es una cifra muy alta para los estándares de frecuencia que podemos encontrar en algún MCU comercial para arduino: 16 MHz, 150 MHz , 216 Mhz o 600 MHz.

2. Cantidad de datos a presentar. "40,000 ciclos en un segundo", no es algo menor, es un mundo de datos que se deben manejar para dibujar algo en el TFT. Le podemos complicar la vida al MCU si queremos monitorear digamos 16 bandas o mejor aún 24 bandas. Veamos que sucedería en el TFT con este último número: 24 bandas.

Cada banda la podemos traducir en una barra vertical que bien podría ser de 1 pixel de ancho por digamos 34 pixeles de altura (usando la función map podemos conseguir esta conversión).  Veamos los números:

1 x 34 x 24 = 34 x 24 pixeles = 816 pixeles

Realmente siguen sin ser muchos números, para un TFT normal de 320 x 240 = 76,800 pixeles o bien 800 x 480 = 384,000 pixeles.

Ahora bien, recordemos la tasa de adquisición de datos promedio es de 40 kHz. En un segundo, el TFT debe conseguirlo y permitirnos procesar esa información en algo visible. ¿Dije un pixel de ancho?, vamos que esa es una línea diminuta. Para algo con mejor visibilidad, pensemos que cada barra tendrá 5 pixeles de ancho por 150 pixeles de alto



Para manejar esa cantidad de pixeles, es mejor obtener vector que nos permita dibujar 24 barras de 5 x 150 pixeles, con una determinada separación (PitchX), algo así como:

Code: [Select]
int YhB = 150;
int PitchB = 5, PitchX = 5;
#define NUM_BANDS   24

int xbase = (320-(NUM_BANDS*PitchB + (NUM_BANDS-1)*PitchX))/2;
int ybase = (240+(YhB))/2;

void BarrasTFT()
{
  for (int j = 0; j < 24; j++)
     {
      tft.fillRect(xbase + j*PitchX + j*PitchB, ybase-YhB, PitchB, YhB, ILI9341_YELLOW);
     }
}



Es decir: 5 x 150 x 24 =18,000 pixeles

El siguiente paso es conseguir que las barras verticales tengan movilidad y sean capaces de representar los datos en forma porcentual. Podemos aproximar esta situación con el movimiento de dos barras, una inferior representará el dato actual y la superior el complemento de ese porcentaje.
ft81xmania.com/comunidad/

TFTLCDCyg

#224
Feb 18, 2021, 09:40 pm Last Edit: Feb 20, 2021, 02:06 am by TFTLCDCyg
El ancho de frecuencias que vamos a monitorear en este ejemplo va de 80 Hz a 14k Hz, repartidos en 24 bandas.
Code: [Select]
    //24 bands, 14kHz top band
      if (i<=2 )            bandValues[0]  += (int)vReal[i];  // 80
      if (i>=2   && i<=3  ) bandValues[1]  += (int)vReal[i];  // 100
      if (i>=3   && i<=4  ) bandValues[2]  += (int)vReal[i];  // 125
      if (i>=4   && i<=5  ) bandValues[3]  += (int)vReal[i];  // 157
      if (i>=5   && i<=6  ) bandValues[4]  += (int)vReal[i];  // 196
      if (i>=6   && i<=7  ) bandValues[5]  += (int)vReal[i];  // 246
      if (i>=7  &&  i<=9  ) bandValues[6]  += (int)vReal[i];  // 308
      if (i>=8  && i<=11  ) bandValues[7]  += (int)vReal[i];  // 385
      if (i>=11  && i<=14 ) bandValues[8]  += (int)vReal[i];  // 482
      if (i>=14  && i<=17 ) bandValues[9]  += (int)vReal[i];  // 604
      if (i>=17  && i<=22 ) bandValues[10] += (int)vReal[i];  // 756
      if (i>=22  && i<=27 ) bandValues[11] += (int)vReal[i];  // 946
      if (i>=27  && i<=34 ) bandValues[12] += (int)vReal[i];  // 1184
      if (i>=34  && i<=43 ) bandValues[13] += (int)vReal[i];  // 1482
      if (i>=43  && i<=53 ) bandValues[14] += (int)vReal[i];  // 1855
      if (i>=53  && i<=67 ) bandValues[15] += (int)vReal[i];  // 2322
      if (i>=67  && i<=84 ) bandValues[16] += (int)vReal[i];  // 2907
      if (i>=84  && i<=105) bandValues[17] += (int)vReal[i];  // 3639
      if (i>=105 && i<=131) bandValues[18] += (int)vReal[i];  // 4555
      if (i>=131 && i<=164) bandValues[19] += (int)vReal[i];  // 5702
      if (i>=164 && i<=206) bandValues[20] += (int)vReal[i];  // 7138
      if (i>=206 && i<=258) bandValues[21] += (int)vReal[i];  // 8935
      if (i>=258 && i<=322) bandValues[22] += (int)vReal[i];  // 11184
      if (i>=322          ) bandValues[23] += (int)vReal[i];  // 14000


El hilo con la señal de audio podemos conectarlo a algún pin Ax, en este caso escogí el pin 16 que equivale a A2 en el teensy 4. La señal proviene del canal derecho de la tarjeta de audio de la PC.

La función que nos permite dibujar las "barras animadas" del analizador de espectro quedó así:

Code: [Select]
void BarrasTFT()
{
  for (int j = 0; j < 24; j++)
     {
      tft.fillRect(xbase + j*PitchX + j*PitchB, ybase-Datos[j], PitchB, Datos[j], ILI9341_GREEN); //inferior
      tft.fillRect(xbase + j*PitchX + j*PitchB, ybase-TOP, PitchB, TOP-Datos[j], ILI9341_RED);    //superior
     }
}


Agregué la rutina con la librería arduinoFFT, para poder convertir correctamente los datos en valores porcentuales y ver su comportamiento real en el TFT.

Mantener el equivalente a una tasa de refresco, se puede conseguir con la instrucción millis o micros. Luego del procesamiento FFT, podemos llamar a la función que dibuja las barras de esta forma:

Code: [Select]
  unsigned long currentMillis0 = millis();
  if(currentMillis0 - previousMillis0 > 42)
  {
    previousMillis0 = currentMillis0;
    BarrasTFT();
  }




MCU: Teensy 4
TFT: ILI9341 2.8"
Librerías: ILI9341_t3, JPGDEC, SD, SPI, arduinoFFT

Adjunto el skecth.
ft81xmania.com/comunidad/

Go Up