looper - un semplice schedulatore senza timer/interrupt

ahahaha, ah ragione astro, sono proprio un UCAS :grin:

Tengo a precisare che la scemenza immane non era quello che avevi detto tu ma la soluzione che avevo adottato io ;) ;)

bhe alla luce dei fatti la mia è una scemenza.. è valida per ambienti multithread, dove sprecare CPU vuol dire rubare risorse ad altri processi/thread, ma sull'arduino la tua soluzione è più che ottima :grin:

leo ma a questo punto possiamo dire di avere inventato il “mio” Delay2.0 ?

cioe’ posso usare in autonomia myDelay, sostituendolo ai delay normali di arduino, senza creare nessun AddJob ?

cioe’ questo funziona e non e’ bloccante ?

BLINK 2.0 
(idea Testato - creazione Leo :-) )

void loop() {
  digitalWrite(13, HIGH);  
  myScheduler.myDelay(1000);
  myScheduler.scheduler();
  digitalWrite(13, LOW);    
  myScheduler.myDelay(1000);
  myScheduler.scheduler();
}

funziona e puoi eliminare le due righe myScheduler.scheduler();

Testato: leo ma a questo punto possiamo dire di avere inventato il "mio" Delay2.0 ?

Guarda che l'ho chiamato myDelay, e non testatoDelay :P

cioe' posso usare in autonomia myDelay, sostituendolo ai delay normali di arduino, senza creare nessun AddJob ?

Lo puoi fare ma a questo punto è inutile la lib.

cioe' questo funziona e non e' bloccante ?

BLINK 2.0 
(idea Testato - creazione Leo :-) )

void loop() {  digitalWrite(13, HIGH);    myScheduler.myDelay(1000);  myScheduler.scheduler();  digitalWrite(13, LOW);      myScheduler.myDelay(1000);  myScheduler.scheduler(); }

Funziona ma non va bene ed il myDelay è bloccante per il codice così come il delay. Non è bloccante per i job inseriti nello scheduler. Ma se non usi nessun job nello scheduler, a che ti serve myDelay?

Ti spiego perché il tuo codice non va bene. Questo andrebbe bene:

BLINK 2.0 
(idea Testato - creazione Leo :-) )

void loop() {
  digitalWrite(13, HIGH);  
  myScheduler.myDelay(1000);
  digitalWrite(13, LOW);    
  myScheduler.myDelay(1000);
}

Non so se hai visto il codice di myDelay che ho postato un paio di messaggi sopra per capire come funziona ma in pratica si tratta di un ciclo while che chiama costantemente lo scheduler finché non sono trascorsi i ms passati come parametro. Se il myDelay è l'ultima istruzione del loop, puoi evitare di chiamare lo scheduler perché l'ultima cosa fatta da myDelay è appunto l'invocazione di esso.

Ma a questo punto il codice qui sopra non va bene lo stesso perché nello scheduler non c'è nulla. Che lo fai a fare uno sketch del genere? :P Il codice giusto per avere un Blink non bloccante è invertire le cose e mettere il codice del lampeggio in un job e lasciare tutto il resto nel loop. Ad esempio, questo fa il blink mentre ad intervalli di 1 secondo stampa un valore sulla seriale:

#include "looper.h"
looper myScheduler;

byte led1Status = 0;
const byte LED1 = 13;
int counter = 0;

void setup() {
    pinMode(LED1, OUTPUT);
    Serial.begin(19200);
    myScheduler.addJob(flashLed1, 100);
}

void loop() {
    Serial.println(++counter);
    myScheduler.myDelay(1000);
}

void flashLed1() {
    led1Status^=1;
    digitalWrite(LED1, led1Status);
}

Questo è un esempio concreto del delay 2.0, che non ferma l'esecuzione dei job in background ma funziona invece come delay classico per il codice principale.

Se nemmeno lesto ha capito che mydelay è bloccante figurati se lo capivo io.

Ma quindi il delay2.0, mio e basta come diritti di autore, intedo come semplive comando da mettere nel loop, non si può tecnicamente fare ? O è difficile farlo ?

Il myDelay ha un senso se ragioni in termini di RTOS o pseudotali. Gli RTOS sono basati su un multitasking di tipo senza prelazione o cooperativo (cooperativo) o con prelazione (preemptive). Questi ultimi sono quelli che dopo un certo periodo di tempo congelano lo stato di un task indipendentemente dalla volontà di quest'ultimo di essere bloccato. Quelli di tipo cooperativo passano il controllo al SO in modo volontario. In questo modo lo scheduler può passare il controllo della CPU ad un altro task. Sull'Arduino DUE è stato implementato una specie di RTOS cooperativo in cui il codice della delay è stato rivisto ed ora fa un po' ciò che fa myDelay. Durante l'esecuzione del delay la CPU non è bloccata in un ciclo inutile ma salta da un task all'altro.

myDelay cerca di fare il verso a questo comportamento, con tutti i limiti di uno scheduler non gestito da interrupt né capace di salvare lo stato della CPU e dei suoi registri per congelare un task. Semplicemente fintanto che bisogna stare fermi ad aspettare viene comunque eseguito lo scheduler.

Rammentiamoci che delay è un'istruzione valida ed utile in alcuni casi. Se metto un delay significa che voglio bloccare il codice per un certo lasso di tempo. Magari, però, potrei voler bloccare solo una determinata parte di codice. I job, appunto, potrei volerli continuare ad eseguire. Quindi myDelay fa proprio questo: ferma il codice nel punto in cui è stata messa ma permette di continuare a fare altro.

Può risultare utile. Pensa ad esempio all'attesa dell'inizializzazione di qualcosa. Pensa ad esempio, per riallacciarsi al problema della Ethernet shield, al delay(300) che c'è in Ethernet.begin. Se ci metto myDelay(300) la libreria Etherner attende che il W5100 abbia fatto la sua inizializzazione ma nel contempo posso continuare ad eseguire altri compiti.

Testato: Se nemmeno lesto ha capito che mydelay è bloccante figurati se lo capivo io.

:~ ho datol'impressione di non averlo capito?

la myDelay è a tutti gli effetti una delay() che però al contrario della vera delay permette a looper di funzionare. detta terra terra :D

Nota mi è più chiaro, grazie,
Vediamo se ho capito tentando di trovare l anello che manca. Io un idea c’è l ho, e magari davvero chiudiamo la questione.

  • il delay ufficiale blocca tutto e va bene cosi
  • il myDelay ( a questo punto tuo) è una via di mezzo, utile anch’esso
  • il testatoDelay deve semplicemente nascondere ciò che facciamo con millis dietro ad una funzione, quindi il classico calcolo di millis a cui passiamo il valore che vogliamo

testatoDelay(1000);

Testato: - il testatoDelay deve semplicemente nascondere ciò che facciamo con millis dietro ad una funzione, quindi il classico calcolo di millis a cui passiamo il valore che vogliamo

E' ciò che fa myDelay. Sembra che sia tutto bloccato ma in realtà mentre è "ferma" può eseguire altre cose.

io voglio anche ammettere che non mi spieghi bene, ma voi ci mettete di vostro :P

volete sapere meglio di un principiante di cosa ha bisogno un principiante ? non e' il vostro campo, fatevi da parte :)

allora a noi esseri infimi che non capiamo nulla, ne' abbiamo voglia di capire nulla, serve questo

BLINK 2.0 
(idea Testato - creazione (se ha voglia e mi riesco a spiegare) Leo :-) )

void loop() 
{
  digitalWrite(13, HIGH);  
  testatoDelay(1000);
  digitalWrite(13, LOW);    
  testatoDelay(1000);
  nel_frattempo_che_lampeggi_fai_infinite_cose();
}

in questo momento: - se usi delay standard essendo bloccante blocchi le infinite cose per 2 secondi - se crei un job dove mettere le infinite cose il gioco funge, ma come vedi tu stesso il programma diventa piu' grande e ostico. - se invece vieni incontro al popolo che aspetta e pende dalle tue dita, creando il testatoDelay, avrai fatto la cosa piu' importante ed utile per la community, superiore alla stessa swRTC che ormai il mondo intero ci invidia :)

Testato: ``` BLINK 2.0 (idea Testato - creazione (se ha voglia e mi riesco a spiegare) Leo :-) )

void loop() {  digitalWrite(13, HIGH);    testatoDelay(1000);  digitalWrite(13, LOW);      testatoDelay(1000);  nel_frattempo_che_lampeggi_fai_infinite_cose(); }

Questo codice non va bene :P Ti dimentichi che la CPU del microcontrollore esegue il codice in maniera sequenziale, ossia una istruzione dietro l'altra. Tu stai cercando un delay che non sia bloccante ma in realtà vuoi un delay che dica al compilatore: ecco, hai trovato la newDelay? Bene, ignorala e passa oltre.

Ora però ti spiego cosa succede col tuo codice: 1) loop 2) metti su HIGH il led 13 3) testatoDelay? allora salta avanti 4) metti su LOW il led 13 5) altra testatoDelay? salta avanti 6) ho del codice da eseguire, bene lo eseguo 7) torno a 1)

Ecco cosa farebbe testatoDelay ;) In realtà tu vorresti qualcosa tipo un blocco di codice da mettere nella tua testatoDelay da eseguirlo solo dopo l'intervallo desiderato. Come lo vuoi te non si può fare, va fatto in un'altra maniera, ad esempio così:

testatoDelay(1000, digitalWrite(13, HIGH));  
testatoDelay(1000, digitalWrite(13, LOW));    
codice_da_eseguire

Ma così dovrei prendere il puntatore alla funzione e passarlo poi ad uno scheduler che lo infilerebbe nell'elenco dei lavori da eseguire, poi ogni volta dovrei passarglielo come parametro e tutte le volte dovrebbe controllare che funzione è, se è da eseguire o se è da saltare. Ma allora non è meglio la myDelay attuale? E poi statureresti la delay intesa come funzione che ferma il codice. Può esserci un'occasione in cui la delay serve, con la tua delay la si salta a piè pari.

Ma il delay standard perche dovrebbe essere toccato ? Quello resta, e se mi serve lo uso. Cosa facciamo ora con millis ? If questo - quello = x allora fai questaltro Else vai avanti. Se più avanti mi serve delay standard lo uso. Non vedere la mia idea come sostituto del delay, ma come nuovo tipo di delay. Io dico solo di nascondere sotto una nuova funzione di looper questo delay nuovo fatto con if e millis

Mi spiace, io non riesco a capire questo tuo delay. Non riesco a capire proprio cos'è che vuoi fare. Io ho inteso che quel che vuoi fare tu è ciò che ti ho spiegato nel precedente post. Tu però mi dici che ti serve un delay diverso. E che non riesco a focalizzare. :~

purtroppo io meglio di cosi' non posso spiegarmi, perche' non ne ho le capacita'.

tento di resettare almeno per portare a conclusione il discorso con un si o con un no:

l'attuale semplicissima funzione, integrata nel core di arduino, delay(1000) e' bloccante, ed e' giusto cosi', nessuno vuole eliminarla. ora la domanda semplice: potra' tecnicamente un domani esistere, creata dal team arduinico stesso ed integrato nel core, una newdelay(1000); che non sia bloccante ? ripeto cosi' come l'ho scritta, cioe' senza dover includere librerie, senza dover richiamare schedulator, senza dover creare job o altre funzioni, intendo semplicemente mettere newdelay(1000); in un qualsiasi punto dello sketch ?

Se mi dici No, non e' tecnicamente possibile riuscire a gestire un delay non bloccante con questi pochi parametri a disposizione, cioe' il nome newdelay ed il parametro 1000, io mi metto l'anima in pace, e l'essere umano dovra' usare looper o la tecnica millis fino alla fine dei tempi. :)

Scusa ma non capisco neanche io. Che dovrebbe ritardare(delay) questa nuova funzione?

Credo intenda che la nuova “newdelay” dovrebbe attendere come l’attuale delay ma senza bloccare gli interrupt.

Ho visto nel core.cpp che la delay si basa sulla micros() che internamente chiama cli(); quella cli() ferma gli interrupt?

La delayMicroseconds invece è fatta in maniera molto diversa. Alla fine poi c’e’ un pezzo in asm. Quella è bloccante rispetto agli interrupt?

La millis() anche lei momentaneamente blocca gli interrupt:

unsigned long millis()
{	unsigned long m;
	uint8_t oldSREG = SREG;
	// disable interrupts while we read timer0_millis or we might get an
	// inconsistent value (e.g. in the middle of a write to timer0_millis)
	cli();
	m = timer0_millis;
	SREG = oldSREG;
	return m;
}

ma senza bloccare ad esempio gli interrupt

non tirare inmezzo gli interrupt, questa librerira NON li usa. ed è per questo che ha creato la cosa leo.

m = timer0_millis;

questa è l'unica iustruzione per cui gli interrupt sono bloccati, poi li riattiva con

SREG = oldSREG;

quindi parliamo di una decina di clock, 20 a star larghi direi, andando a naso.

La delayMicroseconds invece è fatta in maniera molto diversa

non hai timer così precisi, quindi si basa sul fatto che sa che ad una certa frequenza di clock certe istruzioni impiegano tot clock, e quindi tot clock * frequenza = tempo.... e tanta imprecisione :)

potra' tecnicamente un domani esistere, creata dal team arduinico stesso ed integrato nel core, una newdelay(1000); che non sia bloccante ?

ni. nel senso: la delay non bloccante come fa a sapere che codice deve eseguire al termine del 1000? risposta: gli passi come parametro una funzione, che conterrà il codice da eseguire ogni tot. e già esiste: looper(non usa interrupt quindi ogni tatnto devi chiamare scheduler(), meno preciso, ma permette funzioni lunghe) e leOS(interrupt, basta settare il dalay e la funzione ed eventualmente il tempo di ripetizione, ma essendo la funzione eseguita in interrupt ha i classici svantaggi)

Ora, tu dici creata dal team. io ti dico: perché la SoftSerial attuale è esterna, e poi integrata nel core perchè considerata "degno sostituto". Lo stesso POTREBBE avvenire con il leOS o lo sheduler.

senza dover includere librerie

Anche la delay che usi è una libreria.. solo che la include in automatico arduino IDE. Quindi si può fare, ma ci vuole una ottima ragione.

Quindi risposta generale: NO: se non chiami sheduler (come leOS), allora sei bloccato dalle limitazioni interrupt. Se usi looper() e metti sheduler() nel main(nascosto) dell'arduino a fine di ogni loop ( come per SerialEvent() )se il tuo loop() è pieno di delay non avrai mai i task eseguiti con la giusta tempistica. Quindi non esiste un testatoDelay() come lo intendi.. niente "silver bullet". A meno che non si riesca a creare un VERO scheduler con due thread: uno per la loop e uno, con priorità superiore, per task schedulati, cche magari lancia ogni sotto-task in un thread a parte. Però a questo punto... che thread a precedenza sugli altri?

lesto:

ma senza bloccare ad esempio gli interrupt

non tirare inmezzo gli interrupt, questa librerira NON li usa. ed è per questo che ha creato la cosa leo.

A quale libreria ti riferisci, non capisco!? La delay del core o quella di @leo ?