Tempo di ciclo lento

Dalla sperimentazione con il simulatore wokwi risulta che controllare il display LCD 16x2 via i2c risulta circa 5 volte più lento del collegamento parallelo 4-bit mode.

  • I2c circa 1.5ms per carattere, non oso immaginare quanto tempo ruba il clear() che nella libreria contiene un delayMicroseconds(2000).
  • 4-bit circa 316us per carattere

Con I2c anche stampando un carattere per ciclo di loop il tempo di ciclo minimo non scende sotto 1.5ms, che è troppo alto. Con 4-bit anche stampando 4 caratteri nello stesso ciclo di loop arrivo a circa 1.2ms. Questi tipi di display sono un collo di bottiglia per certe applicazioni. Ma cosa accade con esp32 o pi-pico restano un collo di bottiglia che limita il tempo di ciclo?

Avete mai affrontato questo problema?

Ciao.

Il problema principale è che vorresti scrivere a ogni giro di loop, il che sarebbe anche inutile per il tempo di risposta dell'occhio umano.

Qui il tema sembra già trattato. Dipende anche dalla libreria che hai selezionato: Speed of writing to a 16x2 LCD display

In verità di visualizzare veloce non mi interessa, mi interessa invece che non impegni la CPU per 1.5ms x 4 caratteri = 6ms. Ho quattro capi dati da aggiornare e non li aggiorno nello stesso ciclo di loop, ma nei 4 cicli successivi. In questo modo il tempo di ciclo e 6ms (o poco più), mentre se li scrivessi tutti all'interno di un ciclo il loop durerebbe 6 x 4 = 24ms. Mi andava benissimo il tempo di ciclo con la connessione parallela 4-bit che mi porta il tempo di ciclo a circ a 1.2ms.

Ovviamente non scrivo in continuazione anche quando non serve, ma solo quando c'è necessità, ma con un tempo di ciclo di 1.2ms male che vada le altre temporizzazione vendono eseguite in ritardo di 1.2ms. Non mi sta bene quando al posto di 1.2ms ci sono 6ms.

Pensate che un display di questo tipo sia disponibile anche con interfaccia SPI?
Però quelli che ho visto io credo non vadano bene, forse a me serve un Atmga168 che gestisce il display e riceve i comandi da SPI, li riceve e con i suoi tempi li visualizza nella speranza che mi lasci libero di ciclare in 100us.

Ciao.

Non ne ho idea, però io ho visto (e dovrei avere da qualche parte) display con interfaccia UART che poi sarebbe gestita a livello hardware e quindi in teoria con ancora meno impegno da parte della MCU visto che dovresti di fatto solo riempire un buffer.

L'altra alternativa è di usare un port expander che usi il bus SPI.
Alla fine i display LCD i2c in questione, non sono altro che un classico display abbinato ad un PCF8574.

Quei display richiedono delle temporizzazioni precise; l'interfaccia I2C o SPI, per essere più veloce, dovrebbe avere un buffer e poi generare lei stessa le temporizzazioni...

Io credo che le prove le dovresti fare con gli oggetti fisici, NON con simulatori che, relativamente ai "tempi" lasciano ... il tempo che trovano :grin:

Guglielmo

1 Like

Il problema non sta tanto nelle temporizzazioni, quanto nel modo in cui sono scritte le librerie e nell'inefficienza dell'ambiente Arduino:

Sulle librerie posso anche essere parzialmete d'accordo (specie per quelle di Adafruit, belle, ma ... certi mattoni :crazy_face:), sull'inefficienza meno ... è difatti lo scotto da pagare se si vuole la trasportabilità e la semplicità d'uso che si ha nell'utilizzare il framework Arduino.

In alternativa, se i tempi sono così stretti ... si prende una delle migliori librerie per LCD, si fa un bel fork su GitHub e ci si mettono dentro le mani per renderla specifica ed ottimizzata (quindi eliminando buona parte delle chiamate al framework Arduino) solo per una specifica MCU.

Ma a quel punto tanto vale passare ad un'altro ambiente di sviluppo (eventualmente anche a pagamento), dove magari ci sono già librerie ottimizzate, si programma a basso livello e si ottengo certamente programmi più veloci.

Guglielmo

1 Like

Anche lo pensavo, ma pensarlo è differente da saperlo con certezza, così sono rimasto stupito dalla precisione del timing.

L'immagine è uno screnshot di pulseview. Il file .vcd è generato dal simulatore wokwi o meglio dall'analizzatore logico.
Il codice che ha prodotto quel segnale è il seguente:

const byte PIN_LED = LED_BUILTIN; // byte è il tipo più adatto 
const byte PIN_D6 = 12;
bool ledState = LOW;  // bool è il tipo più adatto 
unsigned long previousMillis = 0;  

const long interval = 20;  // (2) 

bool D6State;

void setup() {
  pinMode(PIN_D6, OUTPUT);
  pinMode(PIN_LED, OUTPUT);
}
// 165 cicli di loop in 1ms
// il ciclo 166 rimane basso per 12us a causa della ISR del timer0
void loop() {
    D6State = !D6State;
    digitalWrite(PIN_D6, D6State);
 
    unsigned long currentMillis = millis();

    if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis; 

        // ! Inizio Azione ! 
		// inverte lo stato senza fare uso delle if else
        ledState = !ledState; 

        digitalWrite(PIN_LED, ledState);
        // ! Fine Azione !
  }
}

Io non ho trovato altra spiegazione in merito a quelle linee bianche ogni 1ms, io penso sia la ISR del timer0 che interrompe il loop e un ciclo si loop dura più tempo del precedente.

Che ne pensi? Io sono rimasto meravigliato.

PS: Attenzione io sto procedendo come fossi un principiante a cui non funziona più bene l'applicazione che gli funzionava quando usava l'interfaccia parallela 4-bit e cerca il motivo.

Avevo fatto gli stessi ragionamenti. Riguardo alla UART mi sono detto che torna comoda per il debug, preferendo la SPI. Ho trovato una breakout di adafruit con su PCF8574 e MCP2308(non sono sicuro) comunque la potevi collegare via SPI ma il software non usa la SPI hardware ma software con il vantaggio di potere impiegare qualunque pin, ma la gestione software impegna la CPU.

Il PCF lavora solo a 100KHz quindi anche qui non si può spingere il bus i2c a 400KHz.

Un backpack con ATmega168 non mi pare esista, anche se non ho cercato specificamente. Con SPI in teoria potrei spingermi a 8MHz. Con I2c al massimo 400KHz.

Ciao.

... sempre in "simulazione" ... :roll_eyes:

Se vuoi avere tempi veritieri, specie con quel hardware e le librerie, devi provare "fisicamente" e con un vero analizzatore di stati logici ... li puoi fare veramente i calcoli esatti, tutto il resto sono "supposizioni" che magari sono esatte o magari no.

Ma devi usare per forza Arduino UNO? Una qualche scheda più veloce?

Guglielmo

No non è obbligatorio. Nota che il problema non è mio, mi è stato segnalata questo problema e io ho voluto indagare con gli strumenti in mio possesso. Certamente se il problema fosse mio e avessi attrezzatura me lo farei io il backpack con ATmega168 (o altro). La mia è solo ricerca pura che ha evidenziato che il display può essere un collo di bottiglia insuperabile via software.

Cioè se devo scrivere "Ciao" li invio con lcd.print("Ciao") e mi costa circa 6ms, se invio un carattere ad ogni ciclo di loop guadagno ma sotto a 1.5ms non si scende in nessun modo.

Ma tu pensi che con una scheda più veloce cambi qualcosa? Via i2c non dovrebbe cambiare nulla sempre 100KHz del PCF8574.

Ok ma riguardo al segnale è sensato pensare che sia la ISR del timer0 di millis che si prende circa 96 cicli CPU. Io mi do altra spiegazione ed è pure sensata, ma sono pronto a ricredermi.

Ciao.

No, con una scheda più veloce (e con più SRAM) cambi proprio logica ... questa è una tipica problematica da FreeRTOS™, un OS real-time che ti gestisce queste cose a interrupt e priorità dei task.

Si, si, mi sembra abbastanza ragionevole, basta che guardi la ISR di Timer 0:

ISR(TIMER0_OVF_vect)
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
}

... fa un discreto numero di cose :wink:

Guglielmo

Sto indagando proprio su come riesce a stupirmi proprio nelle tempistiche che anche osservando il disassemblato e facendo qualche calcolo a spanne ciclo di clock più o meno ci siamo, questo mi ha spinto ad indagare ancora più a fondo, ma il problema secondo me è la convinzione che non possa essere preciso nelle tempistiche perché non è reale ma una simulazione ma intanto è fedele in tutte le sperimentazioni che ho portato avanti. Fedele nel PWM, fedele con le ISR, precisissimo con la seriale e il baudrate. Come dire dovrò prima o poi scoprire che sbaglia e magari di tanto in qualche occasione.

Il che esclude i principianti arduino anche evoluti. Una alternativa che ho consigliato è quella del PCF per espandere le porte e mantenere il display 4-bit (o anche 8-bit se avesse pin e se dimezzasse il tempo di esecuzione) e relay e opto, led ecc li comanda tramite il PCF.

Intanto ho imparato tantissime cose che non si trovano sui libri. Ho imparato a ricavare il tempo di ciclo medio, cioè accumulo +1 in un contatore ad ogni ciclo di loop, poi con millis temporizzo 1000ms e calcolo 1000 / contatore.
Ad esempio se contatore vale 274368, il tempo di ciclo medio vale:

1000 / 274368 = 0,00364474ms ( 3.64us )

A secondo di cosa ha da fare la cpu il tempo di ciclo medio sale.
Occhio che non vuole dire che il loop viene eseguito in 3.64us ma che alcuni cicli durano talmente poco e qualcuno è lungo 10 volte tanto. Trovo che sia una metrica interessante.

Ciao.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.