Rivista #229: “Cooperative Scheduler”

Tra i contenuti del fascicolo 229 (Ottobre 2018), attualmente in edicola, c’è anche l’articolo relativo allo Scheduler Cooperativo in grado di gestire i task da far eseguire alle board Arduino e compatibili.

Molto spesso gli utenti Arduino si trovano ad affrontare il problema di dover eseguire, in tempi ben definiti e quindi in maniera  pianificata, determinate azioni; la soluzione che verrebbe più immediata è l’utilizzo di una funzione insita in Arduino, che è la millis() e che possiamo considerare un contatore che ci comunica quanti millisecondi sono trascorsi da quando la board Arduino è stata avviata, ovvero dall’ultimo reset. Purtroppo, se è vero che millis() è lì pronta da utilizzare, è vero altrettanto che non è così facile come dirlo, perché occorre fare i conti con il corretto utilizzo e l’applicazione pratica di detta funzione. Per chi avesse trovato  ostico e tedioso districarsi tra le difficoltà di applicazione di millis(), abbiamo pensato a una soluzione semplice ed elegante per  rispondere all’esigenza di programmare temporalmente l’esecuzione di determinate azioni: si tratta dell’implementazione di un  semplicissimo “scheduler cooperativo” che richiede pochissime risorse e che può essere tranquillamente utilizzato anche su Arduino UNO, che notoriamente dispone di poca memoria.

COS’È UNO SCHEDULER COOPERATIVO?

Come dice il nome stesso, uno scheduler cooperativo è un programma che permette la messa in esecuzione, in modo “collaborativo” di diverse parti di programma immaginabili come delle semplici funzioni. Pensiamo al classico caso in cui si deve ogni T1 secondi leggere un sensore di temperatura, ogni T2 secondi leggere un sensore di umidità, ogni T3 secondi leggere un sensore di pressione, ogni T4 secondi leggere un RTC ed ogni T5 secondi aggiornare un LCD con i dati letti nelle varie fasi precedenti. Una strada è fare tutto nel loop(), decidendo quando fare le varie cose con l’utilizzo di millis(); un’ altra strada è fare la stessa cosa appoggiandosi su un meccanismo che usa lui correttamente la funzione millis() e che toglie al programmatore il compito di effettuare tutti i vari controlli temporali …

Il più semplice di questi meccanismi è un piccolo programma che viene chiamato “scheduler cooperativo” e che non richiede il diretto utilizzo di meccanismi hardware come, ad esempio, timer programmabili o interrupt, cui viene detto quali funzioni richiamare e con che periodicità richiamarle lasciando al programmatore il solo compito di scrivere delle piccole funzioni (… che, impropriamente, possiamo chiamare Task) che svolgono i vari compiti, senza preoccuparsi di come temporalmente richiamarle.

Ovviamente, affinché uno “Scheduler Cooperativo” funzioni correttamente occorre però rispettare alcune semplici regole:

  1. la somma delle durate di tutte le singole funzioni deve essere inferiore all’intervallo minimo richiesto allo scheduler per l’attivazione di una qualsiasi delle funzioni;
  2. diretta conseguenza del punto 1 è che, ovviamente, anche nessuna singola funzione deve durare più dell’intervallo minimo richiesto allo scheduler per l’attivazione di una qualsiasi delle funzioni; ma questo è comunque valido sia che si utilizzi direttamente la funzioni millis(), sia se ci si semplifichi la vita usando uno “scheduler cooperativo”.

Notate che nel caso non sia possibile rispettare queste regole, la cosa si complica ed occorre utilizzare un vero scheduler con tanto di gestione di interrupt, gestione di priorità e gestione del meccanismo di “prelazione” (preemptive), come, ad esempio, può essere FreeRTOS, che sarà però argomento di un articolo dedicato di futura pubblicazione.

Riprendendo l’esempio proposto qualche paragrafo indietro, avremo quindi:

  1. una semplice funzione che ha il solo compito di leggere il sensore di temperatura e di mettere il valore letto in una variabile.
  2. una semplice funzione che ha il solo compito di leggere il sensore di umidità e di mettere il valore letto in una variabile.
  3. una semplice funzione che ha il solo compito di leggere il sensore di pressione e di mettere il valore letto in una variabile.
  4. una semplice funzione che ha il solo compito di leggere il RTC e di mettere il valore letto in una variabile.
  5. una semplice funzione che ha il solo compito di prendere le variabili riempite dalle precedenti funzioni e visualizzarle su un LCD.

Si tratta di cinque funzioni, indipendenti tra di loro che iniziano, fanno (… in pochi millisecondi) ciò che devono fare e terminano.

Compito del programmatore sarà solo dire allo “scheduler cooperativo” che ci sono 5 funzioni (Tasks) e con che intervallo di tempo occorre richiamarle. Sempre facendo riferimento all’esempio, si potrebbe decidere di leggere la temperatura ogni 5 minuti (T1 = 30000 millisecondi), di leggere l’umidità anche essa ogni 5 minuti (T2 = 30000 millisecondi), di leggere la pressione ogni 10 minuti (T3 = 60000 millisecondi), di leggere il RTC ogni secondo (T4 = 1000 millisecondi) e di aggiornare il LCD anche esso ogni secondo (T5 = 1000 millisecondi) così da avere l’ora visualizzata sempre aggiornata, comunicando questi tempi allo “scheduler cooperativo” che se ne occuperà direttamente togliendo l’incombenza al programmatore.

L’articolo prosegue con i seguenti argomenti:

IMPLEMENTIAMO LO “SCHEDULER COOPERATIVO”

COMPLETIAMO LO SCHEDULER

DALLA TEORIA ALLA PRATICA

Il circuito demo per testare il funzionamento dello scheduler.

CONCLUSIONI

Come accennato in apertura, quello qui descritto è uno “scheduler cooperativo” in versione estremamente semplificata, che mette a disposizione un minimo di funzioni per il corretto funzionamento ed offre un parziale controllo dei task. Chi ne ha le capacità e la necessità, può prenderlo come base di partenza e sbizzarrirsi ad espanderlo con ulteriori funzionalità; chi invece vuole eliminarsi l’incombenza della gestione di un “super loop()”, in cui è a suo carico, tramite il corretto utilizzo della funzione millis(), tutto il controllo delle varie attività con le tempistiche richieste, può usarlo così come è, rendendo molto più semplice e leggibile il programma. Naturalmente, come anche spiegato nella parte introduttiva, uno “scheduler cooperativo” di questo tipo ha dei limiti e sicuramente non è adatto ad applicazioni in cui sia necessario dare una priorità ai vari task, fare in modo che un task possa interromperne un altro (che poi viene ripreso nel punto in cui era stato interrotto), permette la gestione di semafori, mutex, ecc.

Per fare queste cose è necessario un vero piccolo sistema operativo, magari capace di operare in “tempo reale”, in cui le varie attività hanno una durata stabilita a priori e che vengano attivate esattamente nel momento richiesto in modo deterministico. In un prossimo articolo affronteremo un sistema operativo del genere ed esamineremo alcune delle caratteristiche peculiari di uno dei più diffusi RTOS, FreeRTOS™, in grado di essere utilizzato anche su sistemi Arduino.

L’articolo completo è pubblicato sul numero 229, Ottobre 2018, acquistabile in tutte le edicole.

 

 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Main Menu