fbpixel
Étiquettes : ,

Le NodeMCU ESP32 est basé sur le microprocesseur dual-core Xtensa 32-bit LX6 qui embarque l’OS FreeRTOS. Lorsqu’on utilise l’IDE Arduino, le programme s’exécute par défaut sur le cœur 1. Pour faire du multitasking, il est intéressant d’utiliser toutes les ressources du microprocesseur. Nous allons voir dans ce tutoriel comment exécuter des tâches sur les deux cœurs

Il existe une librairie FreeRTOS compatible pour les microcontrôleurs Arduino avec une architecture AVR (Uno, Nano, Mega, etc.).

Matériel

  • NodeMCU ESP32
  • Câble USB A Mâle/ Micro B Mâle

Description de l’OS FreeRTOS

FreeRTOS est un système d’exploitation temps-réel open-source et léger. Il est donc parfaitement adapté à des problématiques de gestion de tâches en fonction du temps ou d’occurrence d’événements. Espressif a intégré l’OS sur les dernières versions du SDK. Il est donc possible d’utiliser les fonctionnalité de cet OS afin d’exploiter le potentiel des deux coeurs du NodeMCU ESP32 avec l’IDE Arduino.

Code

Dans ce code, nous allons créer plusieurs tâches auxquels nous allons affecter une fonction qui s’exécutera à l’appelle de la tâche.

Création de la tâche affectée à un cœur

  xTaskCreatePinnedToCore(
                    task1Func,   /* Task function. */
                    "Task1",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    10,           /* priority of the task */
                    &Task1,      /* Task handle to keep track of created task */
                    0);          /* pin task to core 0 */ 

Définition de la boucle infinie exécuter à l’appel de la tâche

void task1Func( void * pvParameters ){
  for(;;){
    // place code here
  }
}

Il est bon de noter que la fonction delay() (ou vsTaskDelay) permet de bloquer la tâche et de passer la main à la prochaine tâche qui a la priorité la plus élevée.

TaskHandle_t Task1;
TaskHandle_t Task2;
TaskHandle_t Task3;

unsigned long previousTime,previousTime1,previousTime2,previousTime3;

void setup() {
  Serial.begin(115200); 

  //create a task on core 0 that will be execute task1Func() with priority 10
  xTaskCreatePinnedToCore(
                    task1Func,   /* Task function. */
                    "Task1",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    10,           /* priority of the task */
                    &Task1,      /* Task handle to keep track of created task */
                    0);          /* pin task to core 0 */                             
  delay(500); 

  //create a task on core 1 that will be execute task2Func() with priority 9
  xTaskCreatePinnedToCore(
                    task2Func,   /* Task function. */
                    "Task2",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    9,           /* priority of the task */
                    &Task2,      /* Task handle to keep track of created task */
                    1);          /* pin task to core 1 */
    delay(500); 

    xTaskCreatePinnedToCore(task3Func, "Task3", 10000, NULL, 8, &Task3, 0);
}

void task1Func( void * pvParameters ){
  for(;;){
    Serial.printf("%s running on core %d (priorite %d) - %dms\n", "Task1", xPortGetCoreID(), uxTaskPriorityGet( NULL ),millis()-previousTime1);
    previousTime1=millis();
    delay(200);//vTaskDelay( pdMS_TO_TICKS( 200 ) );
  }
}

void task2Func( void * pvParameters ){
  for(;;){
    Serial.printf("%s running on core %d (priorite %d) - %dms\n", "Task2", xPortGetCoreID(), uxTaskPriorityGet( NULL ),millis()-previousTime2);
    previousTime2=millis();
    delay(600);
    //vTaskDelay( pdMS_TO_TICKS( 600 ) );
  }
}

void task3Func( void *pvParameters )
{
  for( ;; )
  {
    Serial.printf("%s running on core %d (priorite %d) - %dms\n", "Task3", xPortGetCoreID(), uxTaskPriorityGet( NULL ),millis()-previousTime3);
    previousTime3=millis();
    delay(100);
    //vTaskDelay( pdMS_TO_TICKS( 100 ) );
  }
}
void loop() {
  Serial.printf("%s running on core %d (priorite %d) - %dms\n", "loop()", xPortGetCoreID(), uxTaskPriorityGet( NULL ),millis()-previousTime);
  previousTime=millis();
  delay(500);
}

Ils existent plusieurs fonctions pour créer des fonctionnalités plus avancées comme la synchronisation et la communication entre tâches. Veuillez vous reporter à la documentation.

Quelques fonctions à retenir:

  • xTaskCreate() pour créer une tâche
  • xTaskCreatePinnedToCore() pour créer une tâche sur un cœur particulier
  • vTaskDelayUntil() permet d’activer un fonction périodiquement
  • vTaskDelay() bloque une tâche pendant un certain nombre de tick d’horloge (utilise pdMS_TO_TICKS pour convertir une durée en nombre de ticks)
  • vTaskPrioritySet() modifie la priorité d’une tâche
  • vTaskDelete() pour supprimer une tâche

Résultat

Une fois le programme lancé, on observe les tâches s’exécuter de manière ordonnée.

esp32-multitasking-result Programme Multitâche avec ESP32 et FreeRTOS

Sources

Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie