- Published on
Arduino et FreeRTOS - Chapite 1: Les tâches
- Authors
- Name
- Tawaliou ALAO
- @Tawal_Mc
Je voudrais préciser que tout le blog et mes futurs articles (d'ici la fin de ce mois) seront en anglais et plus en français. Désolé de l'annoncer ainsi mais je présenterai bientôt les raisons.
Cet article n'est que la suite de l'article introductif sur FreeRTOS que j'ai publié récemment: Introduction à FreeRTOS et donc n'est pas pour un débutant en système embarqué mais il faut juste être à l'aise avec les microcontrôleurs.
C'est quoi une tâche dans FreeRTOS
Déjà pour ceux qui se demandent ce qu'est FreeRTOS, j'en ai déjà parlé dans mon précédent article où j'y présente le RTOS(Real Time Operating System) dédié aux microcontrôleurs qu'est FreeRTOS. Vous pouvez aller le lire rapidement: Introduction à FreeRTOS - I'm waiting for you 😒.
Une tâche ou task
est une action que vous faites, une commande ou une routine que vous exécutez dans votre code/système.
Et comme on parle de code, en FreeRTOS une tâche n'est rien d'autre qu'une fonction écrite en C
qui exécute donc une/des actions(s) et qui
ne retourne jamais et au grand jamais
une quelconque valeur. Cette dernière partie est très importante:
vous faites tout ce que vous voulez mais jamais de mot clé return
sinon la 5ème grande guerre des ninjas 🤬.
Donc:
- Afficher dans le moniteur série (Serial monitor) la valeur retournée par un capteur de température chaque 5s est une tâche
- Eteindre et allumer une LED suivant une fréquence donnée est aussi une tâche
- ...Bref tout ce que nous avons l'habitude de faire avec notre ami Arduino.
Caractéristiques d'une tâche
Quelques notions importantes sont à prendre à compte quand on veut créer ou manipuler des tâches (je vous l'avais déjà dit, ce ne sont que des fonctions écrites en C):
task's state
ou l'état courant d'une tâche
Chaque tâche peut passer dans 4 états différents:
Running State
: ce dit d'une tâche qui est en cours d'exécution - très couranteReady State
: ce dit d'une tâche qui n'est pas en train de s'exécuter mais est prête à entrer en exécution - état intermédiaire entre lerunning state
et les autres étatsBlocked State
: ce dit d'une tâche qui n'est pas en cours d'exécution (bloquée) et qui attend un évènement pour débuter son exécution - un delay par exemple fait passer une tâche durunning state
aublocked state
Suspended State
: ce dit d'une tâche qui n'est pas en cours d'exécution non plus et elle n'est pas prise en compte par le gestionnaire de tâches dans ce cas - très rares sont les applications qui l'utilisent
Je vous offre une image que j'ai reproduite depuis le guide tutoriel et qui montre les relations entre ces différents états.
Les différents états d'une tâche (j'ai volontairement supprimé certains ajouts du manuel).
task's priority
ou priorité d'une tâche
Chaque tâche a une priorité d'exécution qui définit si elle doit s'exécuter prioritairement par rapport aux autres tâches ou si d'autres tâches ont plus d'importance
donc doivent
être exécutées avant cette dernière. La priorité d'une tâche est définie par un nombre allant de 0
à une valeur max
(elle est de 3
dans le cas d'Arduino) - des précisons seront fournies lors de son utilisation.
tick interrupt
ou interruption périodique
Je vous avais dit dans l'article précédent que FreeRTOS simulait sur nos micronctrôleurs (qui n'ont qu'un seul coeur) le multitasking
. En fait pour y arriver,
il procède à des interruptions périodiques (tick interrupt
ou periodic interrupt
) où il décide de la prochaine tâche à exécuter ce qui fait croire que plusieurs tâches sont en train de s'exécuter au même moment.
Du manuel on peut lire ceci
The number of tick interrupts that have occurred since the FreeRTOS
application started is called the tick count. The tick count is used as a
measure of time.
En gros il y a des interruptions périodiques qui surviennent depuis l'exécution de votre code tournant FreeRTOS et le nombre d'interruptions est
utilisé comme échelle de mesure du temps. Vous allez remarquer par la suite que pour définir nos delay
nous convertirons d'abord notre temps de pause
en tick period
. Ces interruptions se répètent chaque fois après un temps donné appelé time slice
.
En réalité nos tâches s'exécutent durant ce time slice
(valeur définie dans les fichiers de configurations de FreeRTOSConfig.h
en Hz qu'on peut rapporter en millisecondes) et à la fin de ce temps
le tick interrupt
survient et le gestionnaire de tâches définit alors la prochaine tâche à exécuter.
Waoh je vous assure quand je rédigeais l'article et j'ai pu expliquer avec des mots la notion de tick interrupt
, je me suis senti comme Thor avec son marteau devant les elfes, trop coool.
Pour la suite j'écris le code en anglais mais les commentaires sont en français, sorry
Halte! Trêve de bavardages, et si on créait des tasks
Pour créer une tâche, il faut respecter la signature suivante
/**
* prototype de la fonction qui représente notre tâche (elle ne renvoie rien);
* le paramètre pvParameters sera vu dans un prochain article
**/
void theNameOfTaskFunction( void *pvParameters );
et utiliser la fonction définie par FreeRTOS pour créer les tâches
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, /// pointeur sur la fonction-tâche: theNameOfTaskFunction dans notre cas
const char * const pcName, /// chaîne de carcatères représentant le nom de la fonction pour le debuggage uniquement (16 caractères maxi si Arduino)
uint16_t usStackDepth, /// l'espace-mémoire occupé par cette tâche, je vous avais parlé d'allocation de la mémoire par FreeRTOS pour l'exécution de tâches
void *pvParameters, /// le même paramètre passé à la fonction-tâche
UBaseType_t uxPriority, /// la priorité d'une tâche: [0;3]
TaskHandle_t *pxCreatedTask ); /// un pointeur sur la tâche pour des utilisations ultérieures, par exemple la supprimée.
BaseType_t
, TaskFunction_t
, UBaseType_t
, TaskHandle_t
, ...
sont juste des types définis par FreeRTOS reposant sur les types primitifs disponibles
en C et qui peuvent varier selon l'architecture (8bits, 16bits, ...) et qui leur permet d'avoir une consistence dans leur code. Don't be afraid 😈
Et Arduino dans le lot
Bien nous allons créer deux tâches simples
- Afficher un texte "Hi Embedded World!" chaque seconde dans le Serial monitor d'Arduino -
tâche 1
- Clignoter une LED chaque 3s -
tâche 2
Installation de FreeRTOS sous Arduino
Elle est très simple:
- Dans le gestionnaire de librairies (libraries manager) sous l'onglet outils (tools) de l'IDE Arduino
- Chercher
FreeRTOS
et installer cette dernière (le premier résultat de la recherche)
Nous allons tester le code suivant
#include <Arduino_FreeRTOS.h>
void TaskPrintText (void *pvParameters); /// prototype de la tâche 1 affichant: Hi Embedded World!
void TaskBlink (void *pvParameters); /// prototype de la tâche 2 faisant clignoter la led LED_BUILTIN (led 13 qui se trouve sur la carte Arduino)
void setup()
{
Serial.begin(9600);
while (!Serial)
{
;
}
/// Tâche 1
xTaskCreate(
TaskPrintText,
"PrintText", /// vous pouvez donner n'importe quel nom, FreeRTOS ne l'utilise pas. C'est juste pour le debuggage
128, /// taille de l'allocation de la mémoire que la tâche va utiliser, je la laise aussi à 128
NULL, /// notre fonction ne prend rien en paramètre donc ici on passe juste un NULL
1, /// tâche de priorité 1. Une tâche de priorité élevée sera toujours exécutée au détriment des tâches de priorités à moins de passer à un état bloquant
NULL); /// nous ne prévoyons pas utiliser un pointeur sur cette tâche pour des utilisations ultérieures donc on passe juste NULL
/// Tâche 2
xTaskCreate(
TaskBlink,
"Blink",
128,
NULL,
1, /// cette tâche aussi a la même priorité que la précédente pour être sûr qu'elle s'exécutera
NULL);
}
void loop()
{
/// nous ne mettons rien ici
}
void TaskPrintText(void *pvParameters)
{
(void)pvParameters;
/// une boucle infinie comme le loop habituel, mais ne comportant pas de return
for (;;)
{
Serial.println("Hi Embedded World!");
}
}
void TaskBlink(void *pvParameters)
{
(void)pvParameters;
pinMode(LED_BUILTIN, OUTPUT);
/// une boucle infinie comme le loop habituel, mais ne comportant pas de return
while (1)
{
digitalWrite(LED_BUILTIN, HIGH);
vTaskDelay(1000 / portTICK_PERIOD_MS); /// delay de 1s: conversion de 1000ms en tick period dont j'avais parlé
digitalWrite(LED_BUILTIN, LOW);
vTaskDelay(1000 / portTICK_PERIOD_MS); /// delay de 1s: conversion de 1000ms en tick period dont j'avais parlé
}
}
Alors
Voilà on a fait un tour de la notion de tâche et de quelques concepts qui lui sont liés. J'avoue que cet article est un peu long, et nous n'avions pas
vraiment testé les différentes possibilités offertes par FreeRTOS à travers les tâches. Mais je vous le promets, les prochains articles (au moins 2) porteront
uniquement sur les tâches et leur utilisation avec Arduino: comment communiquer entre deux tâches, des cas d'utilisation du rtos avec le module
rfid ou clock...Je vous prépare du lourd quand même.N'hésitez pas à me faire des retours dans mon mail ou sur mon Twitter ou Linkedin dont les références sont en dessous
ou simplement cliquer sur Partager •
.
Et sur ce: can you smell what TawalMc is cooking?