qRTOS, un quasi RTOS per Arduino.

Spesso appare la domanda “come faccio per eseguire più compiti simultaneamente ?”, la risposta è sempre la stessa, Arduino non ha un processore multicore pertanto può fare solo una cosa per volta, però dato che la fa molto velocemente si può ottenere “l’illusione” di farne diverse assieme.
Solitamente viene consigliato di utilizzare la millis() per gestire i tempi di esecuzione, va bene però è un sistema poco pratico e confusionario.
Esiste una soluzione migliore ed è quella di realizzare un RTOS molto minimale che garantisce, entro certi limiti, di poter eseguire dei compiti (task) a determinati intervalli determinato in modo abbastanza preciso.
Quanto sto per andare ad illustrare è proprio come realizzare un semplice RTOS per Arduino, l’ho definito quasi RTOS perché implementa solo la possibilità di lanciare dei task a intervalli predeterminati, un vero RTOS fa molte più cose :slight_smile:
Il funzionamento di qRTOS è basato sull’utilizzo del timer del watchdog, esattamente come fa il LeOS2 di Leo72, però c’è una sostanziale differenza, prima di tutto il mio qRTOS non è una libreria come normalmente viene intesa su Arduino, sono una ISR e delle funzioni pronte all’uso mirate a questo specifico scopo, scritte in modo ottimale per minimizzare l’impatto sul tempo cpu e l’uso minimale delle risorse, inoltre, a differenza di quanto avviene nel LeOS2, i task sono eseguiti all’esterno della ISR del watchdog, sono interrompibili da eventuali interrupt, non bloccano l’esecuzione di altri eventuali processi richiamati da interrupt.
Nel file allegato trovate uno sketch di esempio che fa lampeggiare il led sul pin 13 ogni 160ms, invia due stringhe sulla seriale, una ogni 960ms e una ogni 1920ms, il tutto senza codice all’interno della loop :slight_smile:
In pratica la loop è fatta in questo modo:

 void loop()
{
  Sequencer();
  
}

Tutto il lavoro lo fa la funzione “Sequencer()”, è uno semplice scheduler che gestisce i processi in coda in base ai timing assegnati ai task.
Il “trucco” sta nella ISR del watchdog:

//ISR watchdog
ISR(WDT_vect, ISR_NOBLOCK)
{
  // incrementa contatori
  Count_Task1++;
  Count_Task2++;
  Count_Task3++;
  Count_Task4++;
  Count_Task5++;
  Count_Task6++;

  // verifica timeout
  // Task 1
  if (Count_Task1 >= Time_Task1)
  {
    Count_Task1 = 0;
    Flag_Task1 = HIGH;
  }

................  
}

Questa ISR non fa altro che incrementare le variabili Count_Taskn ogni 16 ms, overflow del timer watchdog, e se uno dei contatori raggiunge il valore definito in “Time_Taskn” setta il flag “Flag_Task1”.
La funzione “Sequencer”, invocata continuamente nella loop, non fa altro che verificare quali flag sono HIGH, li resetta e lancia la funzione abbinata, nell’esempio sono denominate myTaskn però potete mettere i nomi che preferiti.
L’esempio prevede fino a sei diversi task, non dovete necessariamente utilizzarli tutti, potete aumentarli a piacere, entro i limiti operativi del micro, semplicemente facendo copia e incolla delle varie porzioni della ISR e di Sequencer cambiando i relativi nomi.

qRTOS.zip (1.7 KB)

Yeha :slight_smile:
bel lavoro, non siamo ai livelli di semplicita' d'uso della TexasInstrument, ma le risorse impiegate sono molto minori.

Ricordo dal lavoro di Leo che sulle mega c'era un problema causato dal bootloader, come era la storia ?
Si doveva aggiornare ad una diversa versione di bootloader

Si ma ... il link dovè?

???
alla fine del testo, dove e' sempre stato :slight_smile:

Testato:
Ricordo dal lavoro di Leo che sulle mega c’era un problema causato dal bootloader, come era la storia ?
Si doveva aggiornare ad una diversa versione di bootloader

Per quanto mi ricordo io il problema era utilizzare il watchdog per il reset perché il vecchio bootloader delle mega non lo disattivava, di conseguenza si entrava in una serie infinita di reset, unico modo per risolvere era spegnere e riaccendere.
Il LeOS2 prevede anche la possibilità di resettare il micro, probabilmente il problema era riferito a questo e non al normale uso del LeOS2.
Ovviamente il qRTOS non può competere con dei veri RTOS realizzati per micro a 32bit che dispongono di supporto hardware per queste cose, come minimo il sysclock tramite timer dedicato e relativi interrupt specifici.
Però il qRTOS permette di risolvere molti problemi, con impatto quasi nullo sulla flash e sulla ram, relativi alla gestione di task ripetitivi, a partire dal banale heartbeat fino ad arrivare alla gestione della telemetria inviata in automatico ogni tot ms, il tutto trasparente al normale lavoro del micro in quanto nella loop puoi far quello che ti pare a patto di non inserirvi dei delay o delle operazioni che richiedono troppo tempo.
Da notare che è possibile aggiungere un ulteriore flag per decidere se un task va eseguito oppure no, in pratica diventa possibile attivare/disattivare l’esecuzione di un processo in funzione delle necessità del programma.

O cavolo..
so fuso..
passo e chiudo
:smiley:

Vespucci:
Si ma ... il link dovè?

Devi scaricare lo zip allegato al post :slight_smile:

su ArduinoUno:

Lo sketch usa 2.610 byte (8%) dello spazio disponibile per i programmi. Il massimo è 32.256 byte.
Le variabili globali usano 237 byte (11%) di memoria dinamica, lasciando altri 1.811 byte liberi per le variabili locali. Il massimo è 2.048 byte.

approfitto per una cosa che non mi e' mai stata chiara, oltre a gestire il WDT via sw come hai fatto tu, esso si puo' attivare anche da Fuse.
Cosa succede se lo si attiva via fuse ? Il tuo codice continua a funzionare ?

E uscendo da questo caso qui particolare, normalmente a cosa serve e come si usa il WDT attivato via fuse ?

Se attivi il watchdog da fuse funziona esclusivamente come reset, allo scadere del tempo previsto, da 16 ms a 8 secondi, se il relativo contatore non viene azzerato il processore esegue un reset hardware.
Da software è possibile utilizzare il watchdog in tre differenti modi:
Modo reset, è lo stesso che viene attivato da fuse.
Modo interrupt, ed è quello che sto utilizzando, in questa modalità allo scadere del tempo viene richiamata la relativa ISR senza nessun reset.
Modo interrupt e reset, in questa modalità prima viene richiamata la ISR e al suo termine viene eseguito il reset hardware, questa modalità consente di salvare lo stato macchina prima del reset.
Da notare che il watchdog utilizza un oscillatore rc dedicato e tutta la sua circuiteria è indipendente e separata da quella del micro, questo perché essendo un sistema di emergenza il suo funzionamento deve essere garantito anche in caso di grave crash del micro.

Ma l'uno esclude l'altro ? cioe' se si attiva il fuse e si carica qRTOS in che modo poi funziona ? il registro e' sempre lo stesso quindi al boot e' in modalita' reset e poi con il tuo sketch subito viene messo in modalita' interrupt ?
Ho cercato anche sul forum AVR ma c'e' chi dice che le istruzioni via sw non hanno effetto se si ha il fuse attivo, chi dice il contrario :cold_sweat:

Testato:
cioe' se si attiva il fuse e si carica qRTOS in che modo poi funziona ?

Non funziona, se setti il fuse viene bloccato il registro di configurazione del watchdog e lavora esclusivamente in modalità reset.
qRTOS puoi usarlo solo se il watchdog viene attivato da software.

Mmm ... a pagina 51 del datasheet dice :

The Watchdog always on (WDTON) fuse, if programmed, will force the Watchdog Timer to System Reset mode. With the fuse programmed the System Reset mode bit (WDE) and Interrupt mode bit (WDIE) are locked to 1 and 0 respectively. To further ensure program security, alterations to the Watchdog set-up must follow timed sequences. The sequence for clearing WDE and changing time-out configuration is as follows: .......

Quindi, rispettando la sequenza descritta successivamente, sembrerebbe che comunque sia possibile alterare la cosa ... :roll_eyes:

Guglielmo

gpb01:
Quindi, rispettando la sequenza descritta successivamente, sembrerebbe che comunque sia possibile alterare la cosa ... :roll_eyes:

Quella sequenza permette di disabilitare il watchdog da software anche se attivato da fuse, non di cambiarne la modalità di funzionamento che rimane bloccata in quella di reset, al massimo puoi cambiare il valore del prescaler e relativo tempo per l'overflow.
Se poi è possibile cambiare anche il modo di funzionamento il data sheet non lo dice esplicitamente, anzi dice "Reset mode bit (WDE) and Interrupt mode bit (WDIE) are locked to 1 and 0 respectively".
Toccherebbe fare una prova pratica se con il watchdog attivo da fuse una volta bloccato da software è possibile cambiare anche il registro WDIE, da come il datasheet presenta la cosa direi di no.

Non ho guardato il codice però dalla descrizione risalgo alla definizione di RTOS coopertativo. Potessi spendere due parole in più sulle caratteristiche faresti un poco di chiarezza.

Questo tipo di RTOS funziona solo se un task dura meno del tick che in questo caso vale 16ms, però ha tanti altri vantaggi e praticamente è l'unico RTOS che conviene usare quando le risorse sono così limitate. Ci sono altri vantaggio rispetto a RTOS in time sharing, come ad esempio la semplicità di determinare quando tempo CPU viene impiegato da ogni task.

Mentre RTOS in time sharing anche se non ha il limiti di durata di un task consuma tante risorse incluso il tempo CPU. Il costo maggiore si ha ad ogni cambio di contesto e per ogni task c'è uno stack. Inoltre c'è da gestire le risorse comuni e quindi si fa ricorso ai semafori e con essi si rischia di piantare il codice.

Ciao.

MauroTec:
Non ho guardato il codice però dalla descrizione risalgo alla definizione di RTOS coopertativo. Potessi spendere due parole in più sulle caratteristiche faresti un poco di chiarezza.

Definirlo cooperativo è una esagarazione :slight_smile:
In pratica è uno dei primi "trucchi" che si imparano con la programmazione embedded sulle piccole mcu, ovvero prendi un timer e dedicalo a clock di sistema tramite il quale scandire le varie operazioni.
Purtroppo su mcu ad 8 bit manca totalmente il supporto hardware per qualunque operazione tipica di un RTOS, ma anche del più semplice OS, le cose tocca inventarsele sacrificando delle risorse di sistema, del resto se ti serve un vero RTOS non usi certo una piccola mcu 8 bit, vai diretto su micro a 32 bit :slight_smile:

Questo tipo di RTOS funziona solo se un task dura meno del tick che in questo caso vale 16ms,

Consentimi un commento cattivo, se una funzione dura più di 16 ms vuol dire che hai sbagliato qualcosa mentre la scrivevi :slight_smile:

appena mi viene voglia faccio una prova con debug hw e vediamo se si smuove il registro del wdt con fuse attivo.

sul mio quadropode, 32u4 infognato di cinematica inversa, la funzione per farlo avanzare dura 2 secondi :smiley:

Definirlo cooperativo è una esagarazione :slight_smile:

Forse, però adesso che ho visto il codice, si può escludere il time sharing di sicuro per cui basta aggiungere alcune cose riorganizzare e abbiamo un RTOS cooperativo.

Consentimi un commento cattivo, se una funzione dura più di 16 ms vuol dire che hai sbagliato qualcosa mentre la scrivevi :slight_smile:

Si, oppure non hai per niente capito come funziona la cosa e e ci dai di delay pensando che c'è sotto l'RTOS che fa magie. Comunque è una caratteristica e non un difetto.

Si possono aggiungere delle macro e funzioni al fine di creare un loop locale per ogni task e ad ogni loop si verifica se il task ha superato il tempo limite e in questo caso impone un return allo scheduler.

Ciao.

x iscrizione