Visualizzazione post con etichetta FreeRTOS. Mostra tutti i post
Visualizzazione post con etichetta FreeRTOS. Mostra tutti i post

mercoledì 6 marzo 2019

Differenza FreeRTOs su Arduino AVR e SAMD21 e ESP-Wroom-32

La principale differenza nell'implementazione di FreeRTOs in ESP32 ed in Arduino Uno (ovvero AVR ATMega328) e' il fatto che Esp32 e' un processore dual core. FreeRTOs riesce a distribuire il carico del lavoro tra i core. Nell'esempio sottostante (ripreso da qui), fatto girare su Esp-Wroom-32, si vede che il main loop ed il task vengono eseguiti su due core differente in modo parallelo


Viene creato un task generico che fa un solo loop e poi si autodistrugge (vTaskDelete(NULL)) mentre il loop principale continua a rimanere in esecuzione.
-------------------------------------------------------------------------
void setup() {

  Serial.begin(9600);
  delay(1000);

  Serial.print("Setup: Executing on core ");
  Serial.println(xPortGetCoreID());

  xTaskCreate(
                    genericTask,       /* Task function. */
                    "genericTask",     /* String with name of task. */
                    10000,             /* Stack size in words. */
                    NULL,              /* Parameter passed as input of the task */
                    2,                 /* Priority of the task. */
                    NULL);             /* Task handle. */
 delay(2000); 

}

void loop() {
  Serial.print("Main Loop: Executing on core ");
  Serial.println(xPortGetCoreID());
  delay(1000);
}

void genericTask( void * parameter ){
    Serial.print("Created task: Executing on core ");
    Serial.println(xPortGetCoreID());
    vTaskDelete(NULL);
}

-----------------------------------------------------------------------------------------
Setup: Executing on core 1
Created task: Executing on core 0
Main Loop: Executing on core 1
Main Loop: Executing on core 1

Main Loop: Executing on core 1


 Se si usa #include <Arduino_FreeRTOS.h> si vedra' che non esiste nemmeno la funzione xprtoGetCoreID perche' ATMega328 e' un processore single core...i task vengono eseguiti su un unico core dividendo i tick del processore tra i vari task come un time sharing

Le cose non cambiano con la serie Arduino MKR che montano SAM D21 Cortex M0-+ (solo alcuni  Cortex di fascia alta sono multi core) 

martedì 5 marzo 2019

Blink con FreeRTOs

Questo e' il classico di Blink del pin 13 pero' compilato usando un task di FreeRTOS.
Questa configurazione permette di lanciare il task e poi di addormentare il processore.
Il classico esempio blink "consuma" circa 45 mA mentre con questo codice circa 20 mA

Un altro aspetto non trascurabile e' si puo' impostare il tempo di lampeggio sopra gli 8 secondi

Con il codice normale di Arduino l'unico metodo di risvegliare il processore e' quello derivante da un interrupt derivante dal watchdog (impostabile al massimo ad 8 secondi) oppure si deve utilizzare un interrupt esterno derivante da un RTC esterno

--------------------------------------------------------------------------------------
#include <Arduino_FreeRTOS.h>
#include <avr/sleep.h>  
#include <avr/power.h>  



void TaskBlink( void *pvParameters );

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

void loop()
{
power_adc_disable(); //ADC
power_spi_disable(); //SPI
power_twi_disable(); //I2C


//set_sleep_mode( SLEEP_MODE_PWR_DOWN ); 
set_sleep_mode( SLEEP_MODE_EXT_STANDBY); 

portENTER_CRITICAL();
sleep_enable();

#if defined(BODS) && defined(BODSE)
sleep_bod_disable();
#endif

portEXIT_CRITICAL();

sleep_cpu(); 

sleep_reset();
}

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

  pinMode(LED_BUILTIN, OUTPUT);

  for (;;) 
  {
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 7000 / portTICK_PERIOD_MS ); // wait for one second
  }
}

Queue con FreeRTOS su Arduino Uno

Le Queue, code, di FreeRTOs sono metodi per scambiare dati tra due task. In questo esempio una coda monodirezionale (un task invia dati e l'altro li riceve)


-----------------------------------------------------------------
#include <Arduino_FreeRTOS.h>
#include <queue.h>  

QueueHandle_t coda;
int codasize = 10;

void sender( void *pvParameters );
void receiver( void *pvParameters );

void setup() {

  Serial.begin(9600);
  coda = xQueueCreate(codasize,sizeof(int));
  
  if ( coda == NULL )   {
     Serial.println("Errore nella creazione");
  }

  xTaskCreate(sender,(const portCHAR *)"Sender",128,NULL,3,NULL);
  xTaskCreate(receiver,(const portCHAR *)"Receiver",128,NULL,2,NULL);

}

void loop()
{
}



void sender( void *pvParameters __attribute__((unused)) )  // This is a Task.
{
  for (;;)
  {
    for (int t=0;t<5;t++){
      xQueueSend(coda,&t,portMAX_DELAY);
    }
    vTaskDelay( 1000 / portTICK_PERIOD_MS );

  }
}


void receiver( void *pvParameters __attribute__((unused)) )  
{
  int elemento;
  for (;;)
  {
    for (int t=0;t<codasize;t++){
      xQueueReceive(coda,&elemento,portMAX_DELAY);
      Serial.print(elemento);
      Serial.print("|");
    }
    Serial.println();

    vTaskDelay( 1000 / portTICK_PERIOD_MS );
  }
}

Semafori binari in FreeRTOS con Arduino

I semafori binari (o Mutex) sono metodi per stabilire per un task per capire se una risorsa condivisa e' occupata da un altro task (e quindi non utilizzabile) oppure e' disponibile

In questo esempio sono creati due task che ogni 5 secondi si sovrappongono e cercano entrambi di scrivere sulla porta seriale.
----------------------------------------------------------------------------------

#include <Arduino_FreeRTOS.h>
#include <semphr.h>  
SemaphoreHandle_t semaforo;

void blink_even( void *pvParameters );
void blink_odd( void *pvParameters );

void setup() {

  Serial.begin(9600);


  if ( semaforo == NULL )   {
    semaforo = xSemaphoreCreateMutex();  
    if ((semaforo) != NULL)
      xSemaphoreGive((semaforo));  
  }

// crea i due task
  xTaskCreate(blink_even,(const portCHAR *)"BlinkEven",128,NULL,3,NULL);
  xTaskCreate(blink_odd,(const portCHAR *)"BlinkOdd",128,NULL,2,NULL);

}

void loop()
{
}



void blink_even( void *pvParameters __attribute__((unused)) )  
{
  for (;;)
  {// qui viene testato se il semaforo e' settato, se la risorsa e'occupata riprova per 5 tick ..in caso la risorsa
   // rimanga occupata abbandona
    if (xSemaphoreTake(semaforo, ( TickType_t ) 5 ) == pdTRUE )
    {
      Serial.println("Even");
      xSemaphoreGive(semaforo);
    }
    vTaskDelay( 5000 / portTICK_PERIOD_MS );

  }
}


void blink_odd( void *pvParameters __attribute__((unused)) )  
{
  for (;;)
  {
    if (xSemaphoreTake(semaforo, ( TickType_t ) 5 ) == pdTRUE )
    {
      Serial.println("Odd");
      xSemaphoreGive(semaforo);
    }
    vTaskDelay( 1000 / portTICK_PERIOD_MS );
  }
}

giovedì 9 giugno 2016

RealTime Operating Systems RTOS per Arduino

Una delle cose belle di Arduino e' che, pur nella sua semplicita', esegue in modo ripetibile e prevedibile un compito...ma in fondo e' uno solo. Se il sistema e' intrappolato, per esempio in un loop infinito, esistono sistemi basati sulla gestione degli interrupt per venire incontro a condizioni non prevedbili, come per esempio la pressione di un tasto da parte dell'utente

Un sistema decisamente piu' evoluto per gestire processi concorrenti su un processore da risorse cosi' ridotte come l'AVR ATMega328 e' quello di usare i sistemi operativi realtime, in breve RTOS (sistema operativo inteso come kernel ed API, non come interfaccia utente)

Con questi software si puo' avere la gestione di thread concorrenti con gestione della priorita' dei task, la gestione di semafori Mutex., binari o counting semaphore per sincronizzare i processi. Una caratteristica fondamentale dei sistemi RTOS e' quello di eseguire un task in un arco temporale (minimo e massimo) ben definito; se dopo il tempo massimo il task non ha finito il compito deve lasciare spazio agli altri task concorrenti. Questa caratteristica e' importante per sistemi robotizzati: per esempio se un automa si muove e contemporaneamente acquisisce dati, i sensori di urto devono avere la priorita' sull'acquisizione e non ci deve essere ritardo tra quando viene lanciato l'allarme di prossimita' e la conseguente azione. Su Linux (sempre che non monti un kernel modificato per essere realtime..la Nasa all'inizio degli anni 2000 aveva creato Flight Linux) puo' succedere che il sistema sia occupata in un task lungo e non rilasci in tempo le risorse per evitare l'urto del robot

Per Arduino esistono sostanzialmente FreeRTOS e ChibiOS

Come documentazione Chibios e' decisamente meglio documentato , FreeRTOS ha un libro a pagamento mentre nel file .zip con il sistema operativo  c'e' una cartella con gli esempi ma non relativi ad ATMega.

Un caso pratico in cui si puo' capire il vantaggio di un sistema RTOS della classica programmazione Arduino e' quello in cui si gestisce un flusso seriale ed in maniera indipendente si devono gestire anche l'acquisizione dei sensori


FreeRTOS
Per installare le libreriei di FreeRTOS nella IDE di Arduino e' sufficiente andare nel menu Sketch/Includi libreria/Gestione Librerie e cercare ed installare FreeRTOS. Stranamente non vengono installati degli esempi




ChibiOS
Per installare ChibiOS in Arduino IDE si parte scaricando le librerie da questo link.(esiste un sistema di sviluppo completo costituito da ChibiOS Studio ma e' possibile compilare per ATMega ma solo per ARM STM32) e si pone la sottodirectory /libraries/ChibiOS_AVR presente nel file zip dentro la directory libraries della IDE di Arduino (sotto Windows si trova nella propria C:\Documents and Settings\xxxxx\Documenti\Arduino\libraries\ChibiOS_AVR mentre in Linux e' semplicemente in libraries)

ChibiOS Studio

Dal punto di vista di scrittura del codice ci sono diversi similitudini. Prendendo due esempi semplici (riportati piu' in basso). Nella parte di setup vengono dichiarati i task che fanno riferimenti a due funzioni con l'indicazione della priorita'; all'interno della funzione del task sono riportate le azioni di ciascun thread FreeRTOS usa il termine task che corrisponde al thread di Chibios)

Esempio FreeRTOS. 
Due Task in cui uno lampeggia un led e l'altro legge i dati da una porta analogica e lo spedisce sulla seriale
---------------------------
#include <Arduino_FreeRTOS.h>
#include <croutine.h>
#include <event_groups.h>
#include <FreeRTOSConfig.h>
#include <FreeRTOSVariant.h>
#include <list.h>
#include <mpu_wrappers.h>
#include <portable.h>
#include <portmacro.h>
#include <projdefs.h>
#include <queue.h>
#include <semphr.h>
#include <StackMacros.h>
#include <task.h>
#include <timers.h>

#include <Arduino_FreeRTOS.h>

// define two tasks for Blink & AnalogRead
void TaskBlink( void *pvParameters );
void TaskAnalogRead( void *pvParameters );

// the setup function runs once when you press reset or power the board
void setup() {

  // Now set up two tasks to run independently.
  xTaskCreate(
    TaskBlink
    ,  (const portCHAR *)"Blink"   // A name just for humans
    ,  128  // Stack size
    ,  NULL
    ,  2  // priority
    ,  NULL );

  xTaskCreate(
    TaskAnalogRead
    ,  (const portCHAR *) "AnalogRead"
    ,  128 // This stack size can be checked & adjusted by reading Highwater
    ,  NULL
    ,  1  // priority
    ,  NULL );

  // Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
}

void loop()
{
  // Empty. Things are done in Tasks.
}

/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/

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

  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);

  for (;;) // A Task shall never return or exit.
  {
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
  }
}

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

  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  for (;;)
  {
    // read the input on analog pin 0:
    int sensorValue = analogRead(A0);
    // print out the value you read:
    Serial.println(sensorValue);
    vTaskDelay(1);  // one tick delay (15ms) in between reads for stability
  }
}
---------------------------

Esempio ChibiOs 
Due Task che fanno lampeggiare il led con l'uso di un semaforo per scambiarsi i dati tra i due thread
----------------------------
// Example to demonstrate thread definition, semaphores, and thread sleep.
#include <ChibiOS_AVR.h>

// The LED is attached to pin 13 on Arduino.
const uint8_t LED_PIN = 13;

// Declare a semaphore with an inital counter value of zero.
SEMAPHORE_DECL(sem, 0);
//------------------------------------------------------------------------------
// Thread 1, turn the LED off when signalled by thread 2.

// 64 byte stack beyond task switch and interrupt needs
static THD_WORKING_AREA(waThread1, 64);

static THD_FUNCTION(Thread1, arg) {

  while (!chThdShouldTerminateX()) {
    // Wait for signal from thread 2.
    chSemWait(&sem);

    // Turn LED off.
    digitalWrite(LED_PIN, LOW);
  }
}
//------------------------------------------------------------------------------
// Thread 2, turn the LED on and signal thread 1 to turn the LED off.

// 64 byte stack beyond task switch and interrupt needs
static THD_WORKING_AREA(waThread2, 64);

static THD_FUNCTION(Thread2, arg) {
  pinMode(LED_PIN, OUTPUT);
  while (1) {
    digitalWrite(LED_PIN, HIGH);

    // Sleep for 200 milliseconds.
    chThdSleepMilliseconds(200);

    // Signal thread 1 to turn LED off.
    chSemSignal(&sem);

    // Sleep for 200 milliseconds.
    chThdSleepMilliseconds(200);
  }
}
//------------------------------------------------------------------------------
void setup() {

  chBegin(chSetup);
  // chBegin never returns, main thread continues with mainThread()
  while(1) {
  }
}
//------------------------------------------------------------------------------
// main thread runs at NORMALPRIO
void chSetup() {

  // start blink thread
  chThdCreateStatic(waThread1, sizeof(waThread1),
    NORMALPRIO + 2, Thread1, NULL);

  chThdCreateStatic(waThread2, sizeof(waThread2),
    NORMALPRIO + 1, Thread2, NULL);

}
//------------------------------------------------------------------------------
void loop() {
  // not used
}




Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...