leOS - un semplice OS per schedulare piccoli task

Dopo aver letto ieri di come funziona lo scheduler sull'Arduino DUE, che è basato su uno specifico timer interno al micro (sysTick), stavo pensando se c'era modo di poter replicare la cosa in qualche modo anche sull'Arduino UNO/Leonardo e sugli altri microcontrollori che normalmente uso.

Alla fine è nato leOS2!
Che cos'ha di differente il leOS2 rispetto al leOS? Una cosa tanto semplice quanto "ganza". Esso usa il WatchDog Timer (WDT) al posto di un timer del microcontrollore!

Che cos'è il WDT? E' un timer utilizzato come contatore dal WatchDog, il circuito che normalmente viene utilizzato per resettare il microcontrollore nel caso il codice si blocchi durante l'esecuzione in loop infiniti oppure in eterna attesa di dati che non arrivano mai. Se il tempo prefissato come "campanello d'allarme" termina prima che il contatore sia stato resettato, il WatchDog viene attivato ed esso resetta il micro. Per far ciò il contatore viene incrementato da un oscillatore interno separato a 128 kHz tramite un prescaler che divide la frequenza in modo da avere diversi tempi di reset, da 16 ms in su.

Il WDT può essere utilizzato sia per resettare il microcontrollore sia per generare un semplice resetinterrupt. Quest'ultimo caso è quello che ci interessa: creando una ISR (Interrupt Service Routine) che intercetta il reset, possiamo gestire lo scheduler dei processi da eseguire in automatico senza utilizzare timer del microcontrollore, evitando quindi che il nostro sketch vada in conflitto con altre librerie che anch'esse usano lo stesso timer.

Il prezzo da pagare è la risoluzione degli intervalli: essi devono essere di 16 ms o multipli di esso per via del fatto che un "tick" non può essere più corto di 16 ms:
Fosc/Prescalermax -> 128.000 Hz / 2.048 = 62,5 Hz
1/62,5 = 0,016 s -> 16 ms
I metodi addTask() e modifyTask del leOS sono stati rivisti in modo che essi accettino il nuovo parametro in input. Se non si vuole fare a mano la conversione fra ms e tick (basta una semplice divisione per 16, comunque), si può usare il nuovo metodo convertMs(), che trasforma appunto i ms in tick. Ecco un esempio:

myOS.addTask(funzione, myOS.convertMs(1000));

Questa riga aggiunge la funzione "funzione" allo scheduler impostando un intervallo di 62 tick (1000/16=62,5->62).

In aggiunta a tutti i metodi del leOS, ho introdotto anche il metodo reset(). Siccome il WatchDog è spesso utilizzato per resettare un microcontrollore, se si vanno ad usare i metodi standard della libreria WDT.h per gestire il WatchDog si altera il funzionamento del leOS2 sia prima che dopo il reset. Per questo motivo, basta richiamare:

myOS.reset();

in qualunque punto del codice per resettare il microcontrollore senza

Atmel ha strutturato i suoi micro in modo che il circuito del WatchDog ed i registri che lo gestiscono siano uguali per quasi tutti i microcontrollori Atmega ed Attiny per cui la libreria può, in teoria, funzionare sulla maggior parte dei chip supportati dall'IDE e dai core disponibili senza uso di #define varie (esempio: Atmega88/168/328, Atmega644/1284, Atmega2560, Attiny84, Attiny85 ecc...). L'unico chip supportato dall'IDE che non è supportato dalla libreria è l'Atmega8 perché questo microcontrollore non ha la possibilità di sollevare un interrupt all'overflow del contatore del WatchDog ma solo un segnale di reset.

Ribadisco un concetto, chiarendolo meglio.
Il leOS2 non è preciso come il leOS né può gestire intervalli al millisecondo. La risoluzione è 16 ms, quindi 16 ms, 32 ms, 128 ms, 256 ms, 1024 ms sono valori precisi. 500 ms sono arrotondati a 496 (500/16=31,25->31 e 31*16=496).
Però il vantaggio è quello di poter ora utilizzare tutte le librerie che usano lo stesso timer impegnato dal precedente leOS.

Attendo repliche, consigli, segnalazioni di bug, suggerimenti e critiche :wink:

watchdog_timer_block.png

leOS2-2.0.2.zip (22.8 KB)