leOS - un semplice OS per schedulare piccoli task

ma non puoi mettere come primissima istruzione una sei(), e quindi attivare la RI-chiamata dell'interrupt?

edit: al posto di sei() puoi usare:

"ISR_NOBLOCK(WDT_vect)" al posto di "ISR(WDT_vect)"

da avr-libc: <avr/interrupt.h>: Interrupts

lesto:
ma non puoi mettere come primissima istruzione una sei(), e quindi attivare la RI-chiamata dell'interrupt?

Potrei richiamare la ISR sempicemente mettendo ad 1 il bit WDIF, che segnala al micro quando è stato chiamato un interrupt dal WDT. Però il punto è un altro, se ho la CPU "insabbiata" in un codice atomico, l'ISR del watchdog non sarà mai eseguita, ma il WDT continuerà a contare per cui si arriverà al reset del micro anche se ciò non è voluto.

Spero poi di aver capito cosa intendevi dirmi :sweat_smile:

sì, ma permetti che un codice atomico che dura 16ms è errato di partenza, e infatti col mio sistema rendi il codice dalla ISR del watchdog NON atomica.
Se invece sei in un altro interrput (vedi timer, pwm, etc) allora DOVREBBE essere settato un flag che indica che è stat richiesto l'interrupt WDT che sarà eseguito appena possibile.

quindi, salvo che l'user non crei un suo interrupt bloccante che dura più di 16ms, non avrai problemi con la tua libreria.

se poi di default metti il reset a 0 (no reset), allora l'user deve fare un interrupt pessimo E tirarsi la zappa sul piede

lesto:
sì, ma permetti che un codice atomico che dura 16ms è errato di partenza

Su questo siamo d'accordo.
Ma il problema non siamo tanto né io né te, ma gli utenti che usano questa cosa.
Se leggi nelle varie pagine, vedrai come di errori derivanti dal fatto di non sapere come funziona il core di Arduino e le sue funzioni, quindi il rischio di ritrovarsi un task freezato c'è.

e infatti col mio sistema rendi il codice dalla ISR del watchdog NON atomica.

Lo vedo ora, non avevo notato la modifica.

Se invece sei in un altro interrput (vedi timer, pwm, etc) allora DOVREBBE essere settato un flag che indica che è stat richiesto l'interrupt WDT che sarà eseguito appena possibile.

quindi, salvo che l'user non crei un suo interrupt bloccante che dura più di 16ms, non avrai problemi con la tua libreria.

se poi di default metti il reset a 0 (no reset), allora l'user deve fare un interrupt pessimo E tirarsi la zappa sul piede

Vedo di rileggermi con calma tutto.

Mi son riletto con calma tutto... si può fare.
Rendendo lo schedulatore non atomico, anche i task che esso gestisce lo diventano. Quando il WDT va in timeout chiama nuovamente la corrispondente ISR. Questa decrementa il conta-timeout e controlla se non siamo arrivati a zero, in caso non rimette ad 1 il flag de WDIE per cui al successivo timeout il watchdog resetta bellamente il micro.
Se siamo invece ancora nella fase "normale", controlla se lo scheduler sta eseguendo un task oppure è libero. Se lo sta eseguendo, esce. Se è libero, ne lancia uno.

Dicevi così?

esatto. la sintesi non è il mio forte, a quanto pare :grin:

Perfect. Oggi butto giù qualche riga di codice e faccio i test.

Non funziona.
Rileggendo la documentazione che mi hai linkato, mi sembra di capire che comunque si possono verificare condizioni anomale, come ISR nidificate che possono saturare lo stack. Difatti facendo eseguire un paio di task insieme, di cui uno freezante, ad un certo punto i led che ho messo di debug lampeggiano stranamente, poi il tutto si resetta e riparte per bene, per poi riandare in tilt di lì a poco.

il problema degli stack lo hai solo quando si lanciano troppi interrupt senza che i precedenti siano completi.
Per questo devi fare attenzione a lanciare il task solo una volta, e in tutti gli altri casi lasciare il prima possibile l'interrupt libero.

che codice hai usato?

lesto:
il problema degli stack lo hai solo quando si lanciano troppi interrupt senza che i precedenti siano completi.
Per questo devi fare attenzione a lanciare il task solo una volta, e in tutti gli altri casi lasciare il prima possibile l'interrupt libero.

che codice hai usato?

Sì, difatti. Riflettendoci sono arrivato alla stessa conclusione.
Sto pian piano risolvendo :wink:

Stasera vedo se pubblico qualcosa, ora devo staccare.

Come promesso, ecco la versione 2.0.90 del leOS2.

La novità è la possibilità di passare allo schedulatore un valore di timeout nel begin della libreria.
Se non si passa nulla viene considerato il valore 0, cioè task eseguiti all'infinito senza controllo sullo stato degli stessi. Se, cioè, un task blocca la CPU, tutto lo sketch si congelerà.
Se invece si passa un valore di timeout (da specificare in ticks, oppure da convertire da milisecondi in ticks usando la nuova funzione convertMs), lo schedulatore imposta il WDT in modalità "interrupt + system reset". Ogni volta che il timer del watchdog va in timeout, viene sollevato un interrupt e contemporaneamente viene resettato il flag WDIE. Se nella ISR non si reimposta ad 1 tale flag, al timeout successivo il WDT resetterà direttamente il micro.
La novità della introduzione del timeout risiede nel fatto che la ISR è stata resa non atomica di modo che essa possa essere chiamata più volte dal WDT. All'interno della ISR si controlla se c'è un task in esecuzione: se si trova, si modifica un contatore. Quando si è passato il numero di tick impostati come timeout ed il task risulta ancora in esecuzione, allora si può ragionevolmente pensare che esso si sia bloccato e si resetta il flag WDIE, in modo che al successivo controllo il WDT resetti il micro.
In allegato trovate un nuovo sketch di esempio denominato "leOS2_reset_after_timeout".

Per provare collegate 2 LED sui pin 7 ed 8. Nello sketch vengono creati 2 task: il primo fa lampeggiare uno dei due led, mentre il secondo freeza la CPU in un loop senza fine dopo 5 secondi.
Modificando la chiamata al metodo begin() potete variare il modo in cui il WatchDog tratta il freeze: se non passate un timeout, vedrete che dopo 5 secondi di lampeggio, il LED si blocca acceso e non succede più nulla. Se passate invece un timeout vedrete che, dopo che il LED si blocca acceso, il micro viene resettato trascorso il tempo preimpostato (ci accorgiamo del reset perché ad ogni avvio il micro fa lampeggiare un paio di volte il secondo LED che abbiamo collegato).

La libreria è alla versione 2.0.90 volutamente, considero questa release una beta. Attendo i vostri pareri prima di rilasciarla come 2.1.0 stabile.
http://www.leonardomiliani.com/?p=516

Un test.... una prova... un commento... :cold_sweat:

:fearful:

Ho letto la discussione che ha portato a leos2. Tanto di cappello per questo nuovo lavoro sul "wdt-based scheduler" :slight_smile:
Ho letto anche l'articolo sul tuo sito: interessante e ben scritto IMHO.

Appena ho un minuto voglio provare con calma il leos2.

Nel frattempo ti segnalo un typo:

core Tiny per i microcontrollorti Attiny

tuxduino:
Nel frattempo ti segnalo un typo:

core Tiny per i microcontrollorti Attiny

Corretto.

ciao, con la versione 1 come faccio ad aumentare l'intervallo minimo da 1000 a 2000 millisecondi?
ho guardato in leOS.cpp ma l'unico 1000 che trovo è F_CPU == 1000000UL

d407336:
ciao, con la versione 1 come faccio ad aumentare l'intervallo minimo da 1000 a 2000 millisecondi?
ho guardato in leOS.cpp ma l'unico 1000 che trovo è F_CPU == 1000000UL

Domanda sibillina :sweat_smile:
Se intendi aumentare l'intervallo di esecuzione di uno sketch, questo lo imposti tu durante l'aggiunta del task allo scheduler:
myOS.addTask(funzione, intervallo)
Basta mettere "intervallo" a 2000 ed il task sarà eseguito ogni 2000 millisecondi.

Se stai guardando nel file .cpp, cosa stai cercando? L'impostazione del timer 2 è fatta per avere un overflow ogni millisecondo, quindi 1 ms è l'intervallo minimo.

Quindi mi basta mettere 2000 come intervallo in addTask() ?
Sul tuo sito però ho letto una cosa diversa: leOS, un semplice SO per Arduino – Leonardo Miliani

Con miaFunzione indicate la funzione che volete schedulare, con intervallo un numero non più grande di 1000 (potete modificare questo valore nel file leOS.cpp) che indica ogni quanti ms volete far ripetere la vostra funzione.

E' una refuso probabilmente di vecchie versioni. Quella pagina la aggiorno via via, ma qualcosa mi può essere scappato. Correggo subito :wink:

PS: ricordati che puoi anche cambiare questo valore "in corsa", ossia anche dopo che il task è stato inserito nello scheduler, usando la funzione modifyTask e passando il nuovo intervallo.

ok, grazie mille!

per il problema dei lavori gravosi che avevo chiesto qualche pagina fà, è una buona idea fare con leOS i compiti meno gravosi che voglio siano fatti sempre con un intervallo preciso, e fare con looper il resto?