freeRTOS - problema temporizzazione blink

Ciao, sto provando freeRTOS con una mega2560 pro mini.

Il programma di prova dovrebbe far accendere il led integrato (pin13) per 20ms, ad intervalli di x millisecondi.
Finchè l'intervallo di lampeggio sta sotto i 1000ms tutto ok, sopra i 1000ms il comportamento è bizzarro. Se imposto 1100 ms ottengo una frequenza di 30Hz(invece di 0.9Hz), se imposto 1500ms ottengo 2.23Hz (invece di 0.66Hz), se imposto 2000ms ottengo 1.04Hz (invece di 0.5Hz)

Non riesco a capire dove sta l'inghippo, uso la funzione vTaskDelayUntil() e pdMS_TO_TICKS()

il codice

#include <Arduino_FreeRTOS.h>

void TaskBlink(void *pvParameters);

void setup() {

  xTaskCreate(TaskBlink, (const portCHAR *)"Blink", 128, NULL, 2, NULL);
}

void loop() {
  // put your main code here, to run repeatedly:

}

void TaskBlink(void *pvParameters)  // This is a task.
{
  pinMode(LED_BUILTIN, OUTPUT);
  TickType_t xLastWakeTime;
  const TickType_t xPeriod = pdMS_TO_TICKS(2000);
  xLastWakeTime = xTaskGetTickCount();
  for (;;) // A Task shall never return or exit.
  {
    vTaskDelayUntil( &xLastWakeTime, xPeriod );
    digitalWrite(LED_BUILTIN, HIGH);
    vTaskDelay( pdMS_TO_TICKS( 20 ) );
    digitalWrite(LED_BUILTIN, LOW);
  }
}

mi servirebbe una dritta (magari da un mod a caso, che mi pare sia esperto :smiley: )

Allora, butta quel codice :smiley:

  1. Per evitare problemi, su Arduino usa sempre l'allocazione statica delle risorse e non quella dinamica

  2. l'inizializzazione del HW si fa nel setup(), NON nei task

  3. ricorda sempre che stai su Arduino e NON su un PIC32 (dove il "tick" è 1 msec) ... su Arduino il "tick" del sistema operativo, per ragioni di compatibilità con le librerie che usano i timers, è fatto con il WatchDog ed il "tick" minimo è di 15 msec.
    Task che terminano prima dello scadere del tick ritornano correttamente il controllo al SO, ma operazioni che coinvolgono i ticks vanno fatte tenendone conto.

  4. Per quanto detto sopra, i periodi calcolati sui tick devono ovviamente essere multipli del tick o ... verranno arrotondati. Per cui, vTaskDelay(20 / portTICK_PERIOD_MS); ti darà, in reltà, il ritardo di circa 1 tick, difatti 20/16 = 1.25 (operazioni con interi arrotondata) e quindi di circa 15 msec.

Detto questo, il tuo codice riscritto correttamete diventa:

#include <Arduino_FreeRTOS.h>
#include <task.h>

#define  RITARDO  2000

StackType_t   uxStackBuffer_T1[192];
StaticTask_t  xTaskBuffer_T1;

void MyTask1(void * pvParameters) {
   for (;;) {
      digitalWrite(LED_BUILTIN, HIGH);    // LED acceso
      vTaskDelay(20 / portTICK_PERIOD_MS);
      digitalWrite(LED_BUILTIN, LOW);     // LED spento
      vTaskDelay(RITARDO / portTICK_PERIOD_MS);
   }
}

void setup() {
   pinMode(LED_BUILTIN, OUTPUT);
   digitalWrite(LED_BUILTIN, LOW);
   //
   xTaskCreateStatic(MyTask1, "Task1", 192, NULL, 1, uxStackBuffer_T1, &xTaskBuffer_T1);
}

void loop() {

}

... dove, per divertirti a cambiare l'intervallo x di cui tu parli, ti basta andare a correggere il valore nella #define RITARDO.

Provalo e vedi come va :wink:

Guglielmo

P.S.: Tieni sempre a mente che, su un Arduino a 16 MHz hai i seguenti valori costanti definiti dal OS:
portUSE_WDTO = 0 (che corrisponde a WDTO_15MS)
configTICK_RATE_HZ = 62
portTICK_PERIOD_MS = 16

  1. Per evitare problemi, su Arduino usa sempre l'allocazione statica delle risorse e non quella dinamica

ok, annotato

  1. l'inizializzazione del HW si fa nel setup(), NON nei task

ok, però nell'esempio fornito a corredo della libreria l'hardware viene inizializzato nei task. Vedi esempio blink_analogRead. :sweat_smile:

su Arduino il "tick" del sistema operativo, per ragioni di compatibilità con le librerie che usano i timers, è fatto con il WatchDog ed il "tick" minimo è di 16 msec.
Task che terminano prima dello scadere del tick ritornano correttamente il controllo al SO, ma operazioni che coinvolgono i ticks vanno fatte tenendone conto.

questa era l'altra domanda che volevo fare. Quindi in pratica, se un task termina in <16ms lo scheduler fa partire subito il successivo o aspetta comunque 16ms? Immagino sia tutto scritto nelle guide ma già che ci sono ne approfitto..

Detto questo, il tuo codice riscritto correttamete diventa:

grazie, stasera lo provo :slight_smile:

doppiozero:
... questa era l'altra domanda che volevo fare. Quindi in pratica, se un task termina in <16ms lo scheduler fa partire subito il successivo o aspetta comunque 16ms?

Come ti ho scritto ... "Task che terminano prima dello scadere del tick ritornano correttamente il controllo al SO ...", quindi, il successivo parte subito.

Guglielmo

doppiozero:
ok, però nell'esempio fornito a corredo della libreria l'hardware viene inizializzato nei task. Vedi esempio blink_analogRead. :sweat_smile:

... NON è buona pratica !

Anzi, la cosa migliore, sarebbe proprio avere una funzione dedicata all'inizializzazione del HW (... funzione che trovi in tutti gli esempi sul sito di freertos con il nome di prvSetupHardware())

Guglielmo

... ah ... occhio, in vTaskDelay() di NON scendere sotto il valore di portTICK_PERIOD_MS, altrimenti la divisione per detto valore, troncata all'intero, da ZERO e ... è un valore che quella funzione NON gradisce affatto :smiley:

Guglielmo

Allora, ho fatto qualche prova.

il tuo codice Guglielmo non compila

In function 'void setup()':
sketch_jun14b:22: error: 'xTaskCreateStatic' was not declared in this scope
    xTaskCreateStatic(MyTask1, "Task1", 192, NULL, 1, uxStackBuffer_T1, &xTaskBuffer_T1);

e non sono riuscito a sistemarlo.

Se invece faccio un ibrido tra i due allora funziona:

questo

#include <Arduino_FreeRTOS.h>

void TaskBlink(void *pvParameters);

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
  xTaskCreate(TaskBlink, (const portCHAR *)"Blink", 192, NULL, 1, NULL);

}

void loop() {
  // put your main code here, to run repeatedly:

}

void TaskBlink(void *pvParameters)  // This is a task.
{
  for (;;) // A Task shall never return or exit.
  {

    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay(20 / portTICK_PERIOD_MS);
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay(2000 / portTICK_PERIOD_MS);
  }
}

e anche questo

#include <Arduino_FreeRTOS.h>

void TaskBlink(void *pvParameters);

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
  xTaskCreate(TaskBlink, (const portCHAR *)"Blink", 128, NULL, 2, NULL);

}

void loop() {
  // put your main code here, to run repeatedly:

}

void TaskBlink(void *pvParameters)  // This is a task.
{

  TickType_t xLastWakeTime;
  const TickType_t xPeriod = 2000 / portTICK_PERIOD_MS;
  xLastWakeTime = xTaskGetTickCount();
  for (;;) // A Task shall never return or exit.
  {
    vTaskDelayUntil( &xLastWakeTime, xPeriod );
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 20 / portTICK_PERIOD_MS ); // wait for 20millisecond
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  }
}

nel mio codice il problema principale sembra essere pdMS_TO_TICKS() ::slight_smile:

p.s. nei reference in pdf, quelli che sto leggendo, portTICK_PERIOD_MS non viene neanche menzionato :stuck_out_tongue_closed_eyes:

Ti da errore perché occorre abilitare le funzioni statiche nel file di configurazione.

Vai nella cartella dei sorgenti della libreria e edita il file "FreeRTOSConfig.h", alla linea 68 modifica in:

#define configSUPPORT_STATIC_ALLOCATION     1

Guglielmo

Perfetto, compila, grazie :slight_smile:

ho letto qui che pdMS_TO_TICKS() può avere dei problemi a causa dell'overflow nel calcolo che viene eseguito tra interi... non so se può esserti d'aiuto

doppiozero:
Perfetto, compila, grazie :slight_smile:

Non avevo dubbi
Guglielmo

Patrick_M:
ho letto qui che pdMS_TO_TICKS() può avere dei problemi a causa dell'overflow nel calcolo che viene eseguito tra interi... non so se può esserti d'aiuto

Si, direi che probabilmente è il suo caso ...
... ma non vale la pena andare a mettere le mani nei sorgenti e modificare, anche perché poi, ogni release il problema si ripete.

La cosa migliore è usare la sintassi che gli ho mostrato (e, su Arduino, ricordarsi che comunque sotto una certa risoluzione NON si va causa il periodo di tick minimo usato). :slight_smile:

Guglielmo

Patrick_M:
ho letto qui che pdMS_TO_TICKS() può avere dei problemi a causa dell'overflow nel calcolo che viene eseguito tra interi... non so se può esserti d'aiuto

grazie della segnalazione :slight_smile: