Go Down

Topic: freeRTOS - problema temporizzazione blink (Read 446 times) previous topic - next topic

doppiozero

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
Code: [Select]
#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  :D )
"Doc, ma che diavolo è un Gigowatt? "

Runtime Clock Manager -- https://github.com/duezero/RCM---Runtime-Clock-Manager

gpb01

#1
Jun 14, 2018, 07:19 am Last Edit: Jun 14, 2018, 08:36 am by gpb01
Allora, butta quel codice :D

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:

Code: [Select]
#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 ;)

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
Search is Your friend ... or I am Your enemy !

doppiozero

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

Quote
2. 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.  :smiley-sweat:  

Quote
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..

Quote
Detto questo, il tuo codice riscritto correttamete diventa:
grazie, stasera lo provo  :)
"Doc, ma che diavolo è un Gigowatt? "

Runtime Clock Manager -- https://github.com/duezero/RCM---Runtime-Clock-Manager

gpb01

#3
Jun 14, 2018, 08:38 am Last Edit: Jun 14, 2018, 08:38 am by gpb01
... 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
Search is Your friend ... or I am Your enemy !

gpb01

#4
Jun 14, 2018, 08:43 am Last Edit: Jun 14, 2018, 08:43 am by gpb01
ok, però nell'esempio fornito a corredo della libreria l'hardware viene inizializzato nei task. Vedi esempio blink_analogRead.  :smiley-sweat: 
... 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
Search is Your friend ... or I am Your enemy !

gpb01

... 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 :D

Guglielmo
Search is Your friend ... or I am Your enemy !

doppiozero

Allora, ho fatto qualche prova.

il tuo codice Guglielmo non compila
Code: [Select]
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
Code: [Select]
#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
Code: [Select]
#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()  ::)

p.s. nei reference in pdf, quelli che sto leggendo, portTICK_PERIOD_MS non viene neanche menzionato   :smiley-yell:
"Doc, ma che diavolo è un Gigowatt? "

Runtime Clock Manager -- https://github.com/duezero/RCM---Runtime-Clock-Manager

gpb01

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:

Code: [Select]
#define configSUPPORT_STATIC_ALLOCATION     1
Guglielmo
Search is Your friend ... or I am Your enemy !

doppiozero

"Doc, ma che diavolo è un Gigowatt? "

Runtime Clock Manager -- https://github.com/duezero/RCM---Runtime-Clock-Manager

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
per inserire (lo sketch) il programma, dall'IDE clicca modifica, clicca copia per il forum poi vieni qui e incolla nel tuo post (ctrl+v) ;)

gpb01

#10
Jun 14, 2018, 09:13 pm Last Edit: Jun 14, 2018, 09:14 pm by gpb01
Perfetto, compila, grazie  :)
Non avevo dubbi 
Guglielmo
Search is Your friend ... or I am Your enemy !

gpb01

#11
Jun 14, 2018, 09:16 pm Last Edit: Jun 14, 2018, 09:16 pm by gpb01
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). :)

Guglielmo
Search is Your friend ... or I am Your enemy !

doppiozero

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 :)
"Doc, ma che diavolo è un Gigowatt? "

Runtime Clock Manager -- https://github.com/duezero/RCM---Runtime-Clock-Manager

Go Up