Display TFT con TS7785S e SPI, frequenza di aggiornamento bassa

Buongiorno,
ho acquistato un display TFT 128X160 con comunicazione SPI di una marca conosciuta (non so se si possono nominare ma nel dubbio non lo faccio).
Il monitor funziona correttamente ed usa un driver (già incorporato se ho capito bene) TS 7785s.
Ora io vorrei realizzare un piccolo videogioco simile a Tetris come dimostrazione per i miei allievi.
Sto usando la libreria GFX di Adafruit e volevo usare la tecnica del framework, ovvero ripulire lo schermo ogni volta e ridisegnare i vari elementi ad ogni loop.

Per rendere chiaro il mio problema ho scritto uno schetch che fa spostare una pallina dall'alto verso il basso quindi nel loop eseguo le seguenti istruzioni:

  1. pulisco lo schermo, o per meglio dire imposto il colore dello schermo nero
  2. incremento la variabile per la posizione Y della pallina
  3. ridisegno la pallina

Per far sembrare il tutto fluido si dovrebbero raggiungere almeno i 30 fps (frame per second).
Il risultato è davvero orribile perchè la frequenza di aggiornamento del display è davvero bassa, vi allego un'immagine insieme al codice mentre purtroppo il video ho provato a zipparlo ma ho solo 7zip che non è supportato dal sito.. Ho aggiunto anche un'altra forma rettangolare per rendere il tutto un po' più gravoso.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h> 
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735 
#include <Fonts/FreeSerif9pt7b.h> 
#include <Fonts/FreeSansBold12pt7b.h>

#define TFT_CS        10
#define TFT_RST        8 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC         9

// For 1.44" and 1.8" TFT with ST7735 use:
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
int posY = 0;

void setup() {
  // Use this initializer if using a 1.8" TFT screen: 
  tft.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab 
  tft.setSPISpeed(40000000);
}

void loop() {
  tft.fillScreen(ST77XX_BLACK); //Ripulisce lo schermo
  posY++; //sposta la pallina verso il basso di 1 pixel
  tft.fillRect(20,50,40,80,ST77XX_YELLOW); //Ridisegna il quadrato
  tft.fillCircle(50, posY, 20,0x6DF0 ); //ridisegna la pallina 
  
}

Nel video si sarebbe vista la pallina lampeggiare a causa dei bassi FPS (Frame per second)

Per collegare l'hardware ho seguito alcuni tutorial dove permettevano il collegamento senza altri driver o schede esterne. Il produttore però mi ha mandato questo ma non capisco di cosa si tratta e non è nemmeno compreso nel pacchetto. Sto aspettando con ansia la risposta alla mia domanda ma tarda ad arrivare.

Vi è mai capitato di utilizzare questi tipi di display? Quello secondo voi è un componente indispensabile??

Vorrei sapere se dipende invece dai limiti di Arduino, se ci sono delle librerie più efficaci o se semplicemente questo display non va bene perchè limitato e, in questo ultimo caso, se mi sapete indicare qualche altro display più prestante.

Vi ringrazio anticipatamente per la vostra solita e cordiale disponibilità e faccio un saluto a tutta la comunity :wink:

  1. Quel modulino in mezzo è un traslatore di livelli.
    Ovvero Arduino uno lavora a 5V mentre probabilmente quel display lavora a 3,3V
    Alimentazione ma ANCHE i segnali devono essere a 3,3V e non 5V. Poi lavora a 5V ma dopo un pò di tempo... lo friggi.
    Tipo questo: https://www.adafruit.com/product/395
  2. Per la velocità, secondo me è il display che è lento di suo.

Grazie mille, ora mi sembra chiaro quindi volendo con un ESP32 ad esempio non mi servirebbe !

Conosci qualche altra tipologia di display che può fare al al caso mio?
Della stessa dimensione se possibile :wink:

Hai provato le demo di adafruit?

Ciao.

La vedo dura impostare la velocità dell'SPI a 40 Mhz sull'Arduino Uno che ha una velocità di clock di 16 Mhz.

Ciao, Ale.

... per fortuna ci pensa la libreria SPI a correggere e selezionare la massima possibile :wink:

private:
  void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
    init_AlwaysInline(clock, bitOrder, dataMode);
  }
  void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
    __attribute__((__always_inline__)) {
    // Clock settings are defined as follows. Note that this shows SPI2X
    // inverted, so the bits form increasing numbers. Also note that
    // fosc/64 appears twice
    // SPR1 SPR0 ~SPI2X Freq
    //   0    0     0   fosc/2
    //   0    0     1   fosc/4
    //   0    1     0   fosc/8
    //   0    1     1   fosc/16
    //   1    0     0   fosc/32
    //   1    0     1   fosc/64
    //   1    1     0   fosc/64
    //   1    1     1   fosc/128

    // We find the fastest clock that is less than or equal to the
    // given clock rate. The clock divider that results in clock_setting
    // is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the
    // slowest (128 == 2 ^^ 7, so clock_div = 6).
    uint8_t clockDiv;

    // When the clock is known at compiletime, use this if-then-else
    // cascade, which the compiler knows how to completely optimize
    // away. When clock is not known, use a loop instead, which generates
    // shorter code.
    if (__builtin_constant_p(clock)) {
      if (clock >= F_CPU / 2) {
        clockDiv = 0;
      } else if (clock >= F_CPU / 4) {
        clockDiv = 1;
      } else if (clock >= F_CPU / 8) {
        clockDiv = 2;
      } else if (clock >= F_CPU / 16) {
        clockDiv = 3;
      } else if (clock >= F_CPU / 32) {
        clockDiv = 4;
      } else if (clock >= F_CPU / 64) {
        clockDiv = 5;
      } else {
        clockDiv = 6;
      }
    } else {
      uint32_t clockSetting = F_CPU / 2;
      clockDiv = 0;
      while (clockDiv < 6 && clock < clockSetting) {
        clockSetting /= 2;
        clockDiv++;
      }
    }
   ...
   ...

Guglielmo

Si è vero ma tanto in realtà avevo fatto solo una prova ma come conferma Guglielmo la velocità massima viene regolata in automatico. Mentre se la abbassi gli effetti si vedono ;)

Sono partito da quelle ma il problema è proprio la velocità di aggiornamento del display.
Non so se questo a sua volta dipenda dalla velocità di Arduino

Non sono ferrato su questi display, quindi prendi con le pinze...
Se togli il fillScreen la velocità della pallina aumenta?
Invece di cancellare l'intero schermo potresti cancellare solo la pallina nella vecchia posizione.

SI ovviamente se non ridisegno tutto e pulisco lo schermo la pallina va più veloce ma è più complicato fare un gioco in questo modo.....
Ma vi do una notiziona.
Dopo due giorni di sbarellamento sono riuscito a montare questo schermo su una ESP 32 e lo schermo è una scheggia,
Ho usato una libreria dedicata che si chiama TFT_eSPI e riesco ad ottenere i famigerati 30 FPS per poter realizzare qualsiasi cosa.

Morale della favola, il display va benissimo ma Arduino era un po lento per fare quello che volevo.

Se qualcuno fosse interessato a sapere come collegare questo tipo di schermo ad una ESP32 sono disponibile.
Non è stato per niente facile tra tutorial sbagliati del produttore e pin out errato.

Notte a tutti

Io ricordo che quella lib offre supporto per ridisegnare solo quella porzione, evidentemente ricordo male.

Evidentemente la SPI della ESP è più performante, poi sicuramente c'è il DMA e il dual core sicuramente aiuta.

Ottimo buono a sapersi.
Notte.

ehm mi scuso!!!
Mi sono entusiasmato troppo velocemente e prima di cantare vittoria avrei dovuto fare qualche prova in più.

Nonostante la velocità sia notevolmente aumentata, direi almeno triplicata se non di più, gli FPS non raggiungono i 30 al secondo necessari per un'immagine stabile.

A questo punto credo che il problema non sia lo schermo ma probabilmente quello che voglio fare io è proibitivo con i microcontrollori. Ridisegnare la grafica ad ogni loop stile renderizzazione della Play Station forse non è possibile. Pensavo che rimanendo nel 2D ce la si poteva fare.

Bisogna quindi cambiare ragionamento e tecniche di programmazione. <magari riprendendo quelle del Commodore 64.

Ho visto che ci sono dei giochi realizzati con Arduino in giro.
Qualcuno ne sa qualcosa ed ha un po' di esperienza da condividere???

PS: preso dalla curiosità comunque voglio provare a comprare qualche nuovo schermo. Ho visto che si sono anche altri driver oltre il TS 7735.

Dalla tua presentazione leggo:
"In passato ho realizzato anche dei videogiochi. In particolare ne ho realizzati 2 con Java ed uno con Unity3D."

Con java avrai usato una libreria grafica, giusto?
Secondo me il problema è proprio qui:

In java come pure in C++ puoi creare una classe astratta Forma e altre più specializzate, Quadrato, cerchio ecc.
Quando crei istanza di cerchio chiami il metodo fill() per riempirlo. Per traslare la posizione alle coordinate crei il metodo move(x,y) il quale rimuove (cancella) la pallina e la ridisegna alle nuove coordinate.

Comunque controlla bene le api delle librerie di adafruit perché ha me il dubbio rimane, io continuo a ricordare che la lib offre supporto per questo tipo di animazione.

Scusa ma non ho il tempo e nemmeno l'hardware per verificare di persona quanto ricordo.

Ciao.

Si in Java uso Lib GDX .
Lì c'è un ciclo chiamato render, simile alla funzione loop di Arduino e funziona come ho descritto all'inizio del post.

-loop
pulisco lo schermo
disegno tutti gli elementi
incremento le variabili per far spostare gli oggetti
-ritorno

Cos'ì via all'infinito.

Tra l'altro anche con la libreria che sto usando TFT_eSPI esiste una classe Sprite, ovvero quella che serve per disegnare gli oggetti, che c'è anche in Java con GDX ma speravo risolvesse invece niente da fare.

non esiste un metodo move(x,y) io disegno una pallina con
tft.fillCircle(x,y,widht,color);
se uso questa istruzione due volte con coordinate diverse mi stampa due palline, la prima non viene cancellata quindi devo continuamente ripulire lo schermo e ridisegnare se voglio avere l'effetto della pallina in movimento. In generale i rendering o come vogliamo chiamarli frameworks lavorano in questo modo. Anche quando abbiamo delle classi tipo la classe Sprite che hanno metodi come move(x,y); in realtà il meccanismo sotto è sempre questo, ovvero di cancellare tutto e ridisegnare ad ogni ciclo.
Per fare ciò ovviamente ci vuole molta velocità e i computer di oggi non hanno di questi problemi.
In alternativa si possono usare le tecniche tipo quelle che si utilizzavano con il Commodore 64 ad 1MHz ovvero disegno un cerchio giallo con bordo nero su sfondo nero, in questo modo quando la palla si sposta lascia una scia nera dovuta dal bordo dello stesso colore dello sfondo, ma quando si devono gestire tanti oggetti è demoralizzante e molto complicato.

mai fatta una cosa simile, io ho sempre salvato l'area di screen buffer che intendevo sovrascrivere.

Si utilizza sempre lo stesso modo di fare dal 64 passando per MS-DOS, EGA, CGA e VGA, si salva il contenuto della memoria che si intende sovrascrivere, non conosco altro modo.

Ho fatto la domande sulle librerie proprio per questo motivo, se apri le librerie (se hai il sorgente) vedi come sono fatte.

Citazione:
The "Animated_dial" example shows how dials can be created using a rotated Sprite for the needle. To run this example the TFT interface must support reading from the screen RAM (not all do). The dial rim and scale is a jpeg image, created using a paint program.

https://github.com/Bodmer/TFT_eSPI

Ciao.

Una cosa l'ho capita....che non ci capisco tanto :slight_smile:

Ho capito cosa intendi, salvi la parte da sovrascivere in modo da ripristinarla successivamente.
A me nel 1998 mi aveva suerito questa cosa dei bordi il mio prof di informatica, probabilmente non ci capiva niente nemmeno lui :roll_eyes: oppure andava bene solo per il caso specifico.

Come al solito sei stato utilissimo, non avevo notato quegli esempi ora studio un po' meglio.....cosa che avrei dovuto fare prima , abbandonando le mie convinzioni errate.

Grazie

Ottimo, direi. Se il tuo display non permette di leggere il contenuto della memoria schermo non puoi fare questo tipo di animazione.

Detta così non saprei ma mi fa tornare alla mente le operazioni booleane sugli oggetti immagine, cioè una pallina si sposta di un pixel il contenuto di ram da ripristinare ha la forma di mezza luna. Stessa mezza luna viene riscritta. Ma qua stiamo pensando a basso livello, comunque non ho link e altro sull'argomento perché è tanto tempo che non mi occupo di grafica a questi livelli, del resto le librerie grafiche fanno tutto loro in modo semplice, ma questo vale per il PC e per molte operazioni c'è supporto hardware nella scheda video, ecco perché fanno presto..

Ciao.