Comprendere il funzionamento del “time slice” in FreeRTOS (Lezione 3)

In FreeRTOS, il “time slice” è un intervallo di tempo predefinito durante il quale un task può eseguire prima di essere interrotto da un altro task con la stessa priorità.

Il sistema operativo utilizza il “time slice” per garantire che tutti i task abbiano l’opportunità di eseguire e che nessun task monopolizzi la CPU.

Il valore predefinito del “time slice” può essere modificato durante la configurazione di FreeRTOS.

In FreeRTOS, quando un task è in esecuzione, un timer di schedulazione attiva il conteggio in background.

Questo timer viene utilizzato per determinare quando il task deve essere interrotto a causa del superamento del suo “time slice”.

Il timer di schedulazione è configurato in modo da generare un’interruzione alla fine del “time slice” del task, in modo che il sistema operativo possa gestire la transizione tra i task in modo efficiente.

In FreeRTOS, se non ci sono altri task pronti per l’esecuzione, il task corrente continuerà la sua esecuzione senza essere interrotto dal sistema operativo.

Ciò significa che il task corrente continuerà ad eseguire fino a quando non viene interrotto dal superamento del suo “time slice”, o fino a quando non termina la sua esecuzione.

Tuttavia, è importante notare che anche se non ci sono altri task pronti, il sistema operativo continuerà a gestire le interruzioni, come quelle generate dal timer di schedulazione, e a gestire altre operazioni di sistema, come la gestione della memoria.

il “time slice” è un modo per garantire una pianificazione equa tra i task con la stessa priorità in FreeRTOS.

Grazie al “time slice”, ogni task con la stessa priorità ha la possibilità di eseguire per un periodo di tempo predefinito prima di essere interrotto dal sistema operativo. Ciò migliora la stabilità e l’affidabilità del sistema, garantendo che tutti i task vengano eseguiti in modo tempestivo.

Figura 1

Se si scrive un codice che alterna tra livello alto e basso un’uscita dell’ESP32 utilizzando due task con la stessa priorità, e si visualizza lo stato dell’uscita con un oscilloscopio, si può osservare che la durata dell’impulso generato è di un secondo.

Da questo si deduce  che entrambi i task sono configurati con lo stesso “time slice” di un secondo.

Ciascuno dei task viene eseguito per un secondo prima di essere interrotto dall’altro task con la stessa priorità.

Tuttavia, è importante notare che la precisione della durata dell’impulso dipende dalla precisione del timer utilizzato dal sistema operativo, dalla precisione dell’interruzione del timer di schedulazione e dalle prestazioni del microcontrollore utilizzato.

figure 1

Capire l’utilizzo della funzione “taskYIELD()” in FreeRTOS

In FreeRTOS, la funzione “taskYIELD()” consente a un task di cedere volontariamente il controllo della CPU ad un altro task.

Ciò significa che anziché aspettare che il timer di schedulazione interrompa l’esecuzione del task, il task può cedere il controllo della CPU,  la funzione “taskYIELD()” , in FreeRTOS, comunica allo schedulatore di passare il controllo all’altra attività.

In questo modo si può ottimizzare l’esecuzione dei task, in quanto si evitano attese eccessive tra i task, consentendo una maggiore flessibilità nell’utilizzo della CPU.

Tuttavia è importante notare che l’utilizzo eccessivo di questa funzione può causare un aumento del sovraccarico di sistema e una riduzione delle prestazioni, a causa dell’elevato numero di cambi di contesto.

Pertanto, è importante utilizzare questa funzione con moderazione e solo quando necessario per ottimizzare l’esecuzione dei task.

Nell’esempio riportato di seguito solo il task,  che commuta l’uscita allo stato alto, è stato modificato con l’aggiunta della funzione taskYIELD(), mentre l’altro task utilizza l’intero intervallo di tempo come nell’esempio precedente.

// task_yield.ino 
// MIT License (see file LICENSE)

#define GPIO  12

static void gpio_on(void *argp) {
  for (;;) {
    for ( short x=0; x<1000; ++x )
      digitalWrite(GPIO,HIGH);
    taskYIELD();
  }
}

static void gpio_off(void *argp) {
  for (;;) {
    digitalWrite(GPIO,LOW);
  }
}

void setup() {
  int app_cpu = xPortGetCoreID();

  pinMode(GPIO,OUTPUT);
  delay(1000);
  printf("Setup started..\n");

  xTaskCreatePinnedToCore(
    gpio_on,
    "gpio_on",
    2048,
    nullptr,
    1,
    nullptr,
    app_cpu
  );
  xTaskCreatePinnedToCore(
    gpio_off,
    "gpio_off",
    2048,
    nullptr,
    1,
    nullptr,
    app_cpu
  );
}

void loop() {
  vTaskDelete(xTaskGetCurrentTaskHandle());
}

Il codice che hai fornito crea due task, “gpio_on” e “gpio_off“, che vengono eseguiti contemporaneamente sullo stesso core dell’ESP32.

Il task “gpio_on” esegue un ciclo che imposta l’uscita GPIO al livello alto per 1000 volte, quindi cede volontariamente il controllo della CPU chiamando la funzione taskYIELD().

Il task “gpio_off” imposta semplicemente l’uscita GPIO al livello basso in un ciclo senza fine.

In questo modo si ottiene un’alternanza tra l’uscita al livello alto e basso con una frequenza molto alta, dipende dall’effettiva velocità di esecuzione del task e dalla velocità del processore.

Durante la configurazione, i task vengono creati e vengono associati e eseguito sullo stesso core dell’ESP32.

La funzione loop() in questo codice chiama “vTaskDelete(xTaskGetCurrentTaskHandle())” che elimina il task correntemente in esecuzione, questo significa che non ci sarà alcun loop principale in questo codice e i task “gpio_on” e “gpio_off” continueranno ad essere eseguiti in modo indipendente e in parallelo uno rispetto all’altro.

In questo caso, eliminare il task corrente non ha alcun effetto significativo sul funzionamento del sistema, poiché i task “gpio_on” e “gpio_off” continueranno ad essere eseguiti senza interruzioni.

L’eliminazione del task loop consente al microcontrollore ESP32 di eseguire solo i task “gpio_on” e “gpio_off”, garantendo che i tempi di commutazione dell’uscita non vengano influenzati da alcun altro task, come il task loop.

In questo modo si garantisce che i tempi di commutazione dell’uscita siano precisi e stabili, e non vengano influenzati da eventuali altri task in esecuzione sul microcontrollore, che in questo caso specifico era solo il task loop

Il codice d’esempio nel post in questione è stato preso da GitHub, una piattaforma di sviluppo software che consente agli utenti di archiviare, condividere e collaborare su progetti di codice sorgente.

Se hai domande o curiosità riguardo al codice o al post, non esitare a contattarci tramite il modulo di contatto sottostante. Saremo felici di aiutarti!

Lascia una risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Ti sei iscritto alla newsletter

There was an error while trying to send your request. Please try again.

Quattrodispositivi utilizzerà le informazioni fornite in questo modulo per restare in contatto con te e fornire aggiornamenti del sito