Vous êtes sur la page 1sur 9

RTOS

(Real Time Operating System)


FreeRTOS (http://www.freertos.org) est un système d'exploitation temps réel (RTOS) léger et modulaire en
« open source » pour microcontrôleur. Il est adaptable à de nombreux microcontrôleurs.

TABLE DES MATIERES

Bibliothèque à inclure : ...................................................................................................... 1


Fichiers d’entête à inclure : ................................................................................................ 1
Primitives pour les fonctions d’interruption : .................................................................... 1
Ordonnanceur ..................................................................................................................... 2
Tâches................................................................................................................................. 2
Files (pipes) : ...................................................................................................................... 4
Sémaphores : ...................................................................................................................... 5
Timers logiciels : ................................................................................................................ 7
Exemple :............................................................................................................................ 8

Bibliothèque à inclure :
− librtos.a

Fichiers d’entête à inclure :


− event_groups.h
− FreeRTOS.h
− FreeRTOSBoardDefs.h
− FreeRTOSConfig.h
− list.h
− mpu_wrappers.h
− portable.h
− portmacro.h
− prodefs.h
− queue.h
− semphr.h
− task.h

Primitives pour les fonctions d’interruption :


RTOS propose parfois deux versions d’une même primitive. Lorsque c’est le cas, l’une d’entre elles est réservée
aux fonctions d’interruption. Dans les fonctions d’interruption il faut donc utiliser les primitives xxxFromISR
lorsqu’elles existent.

Remarque : Certaines primitives pour fonctions d’interruption ont un paramètre (le dernier) que l’on peut mettre
à NULL. Il s’agit d’un paramètre de retour (de type pointeur : int8_t_t *) indiquant si l’appel de la primitive a
provoqué un changement de tâche (il vaudra pdTRUE dans ce cas). Si on n’a pas besoin de cette information on
peut mettre ce paramètre à NULL
Ordonnanceur
L’ordonnanceur assure l’exécution en parallèle des tâches. Il doit être lancé par le programme (main) par la
primitive vTaskStartScheduler(). Il utilise les interruptions du Timer 0 pour changer de tâche. Sur le 2560
l’unité de temps de l’ordonnanceur (Tick) est 5 ms (200 Hz).

− Démarrage de l’ordonnanceur :
o vTaskStartScheduler()
C’est normalement la dernière instruction du main

− Arrêt/reprise des tâches :


o vTaskSuspendAll()
o xTaskResumeAll()
La première fonction suspend toutes les tâches. La seconde reprend toutes les tâches

− Nombre de tâches :
o uxTaskGetNumberOfTasks()
Renvoie le nombre de tâches définies

− Durée depuis le lancement de l’ordonnanceur :


o xTaskGetTickCount()
o TaskGetTickCountFromISR()
Renvoie dans un uint16_t le nombre de ticks de 5 ms modulo 216 depuis le lancement de l’ordonnanceur

Tâches
Les tâches sont exécutées en temps partagé par l’ordonnanceur. Une tâche ne s’exécute que si aucune tâche de
priorité supérieure ne peut s’exécuter.

− Déclaration d’un descripteur de tâche :


o TaskHandle_t * nom_du_descripteur
Ce descripteur est rempli lors de la création de la tâche (primitive xTaskCreate) et permet ensuite d’agir
sur cette tâche. Si aucune action ultérieure à son lancement n’est nécessaire sur une tâche, elle peut ne
pas avoir de descripteur.

Création :
o xTaskCreate(fct, nom, pile, param, priorité, descripteur)
 fct : fonction représentant la tâche. Elle doit être de la forme :
void fct(void *param)
 nom : nom de la tâche (voir pcTaskGetName) ce nom ne sert pas au fonctionnement
de la tâche mais peut être utile pour des traces
 pile : taille de la pile qui peut être : configMINIMAL_STACK_SIZE
 param : paramètre passé à la tâche qui peut être NULL ou un pointeur de type void *
 priorité : priorité initiale de la tâche qui peut être défini par tskIDLE_PRIORITY +
k (k = 1 à 3), tskIDLE_PRIORITY est la priorité de la tâche idle (0).
 descripteur : déclaré par : TaskHandle_t *. Il sera initialisé par xTaskCreate et
permettra par la suite de désigner la tâche (il peut être NULL si on n’a plus besoin de
désigner cette tâche).
 La valeur de retour de xTaskCreate est pdPASS si la création s’est bien faite et
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY sinon.

Exemples :

Création d’un tâche sans paramètre ni descripteur :

#define PRIORITE_TACHE_1 ( tskIDLE_PRIORITY + 1 )


xTaskCreate(tache1, "T1", configMINIMAL_STACK_SIZE, NULL, PRIORITE_TACHE_1, NULL);

La tâche :

void tache1(void *param ) { // Le paramètre ne sert à rien mais doit être présent
while (0==0) { // Activité de la tâche
….
}
}

Création d’une tâche avec paramètre et descripteur :

#define PRIORITE_TACHE_2 ( tskIDLE_PRIORITY + 3 )


TaskHandle_t descr2 ;
int valeur = 5;
xTaskCreate( tache2, "T2", configMINIMAL_STACK_SIZE, (void *)&valeur, PRIORITE_TACHE_2,
&descr2);

La tâche :

void tache2(void *paramV ) {


int *valeur = paramV; // récupération d’un pointeur sur le paramètre
while (0==0) {
if (*valeur !=0) { // Utilisation du paramètre
….
}
….
}
}

− Destruction :
o vTaskDelete(descripteur)
La tâche désignée par son descripteur est supprimée

− Priorité (de 0 à 4, 0 est la priorité de la tâche idle) :


o uxTaskPriorityGet(descripteur)
Renvoie la priorité actuelle de la tâche désignée par son descripteur

o vTaskPrioritySet(descripteur, priorité)
Modifie la priorité de la tâche désignée par son descripteur

− Cycle de vie :
o vTaskDelay(nbrTicks)
La tâche est mise en attente pour la durée donnée en nombre de « ticks » de 5ms

o vTaskDelayUntil(datePrec, nbrTicks)
La tâche est mise en attente jusqu’à une date précise. Utiliser xTaskGetTickCount() pour obtenir le
1er paramètre.

Exemple :

TickType tempsPrecedent; // date du démarrage précédent


tempsPrecedent = xTaskGetTickCount();// date du 1er demurrage
long periode = 20; // Période de répétition de la tâche (100 ms)
while (0==0) {
vTaskDelayUntil( & tempsPrecedent, periode); // tempsPrecedent est mis à jour
// faire quelque chose chaque ‘periode’ (exprimé en unités de 5ms)
}

o xTaskAbortDelay(descripteur)
La tâche désignée par son descripteur met fin à son attente

o vTaskSuspend(descripteur)
La tâche désignée par son descripteur est suspendue
o vTaskResume(descripteur)
o vTaskResumeFromISR(descripteur)
La tâche désignée par son descripteur est reprise

o vTaskYIELD()
La tâche passe son tour

− Nom de la tâche :
o pcTaskGetName(descripteur)
Renvoie le nom de la tâche (2ème paramètre de xTaskCreate)

Files (pipes) :
Les files permettent la communication asynchrone de données entre tâches et entre tâches et fonctions
d’interruption. Elles ont une taille maximale définie lors de leur création. La lecture dans une file est bloquante si
la file est vide au choix pour un délai donné ou indéfiniment. Il en va de même pour l’écriture dans une file
pleine.

− Déclaration :
o QueueHandle_t descripteur;
Déclaration d’un descripteur de file qui sera initialisé par xQueueCreate

− Création :
o xQueueCreate(nbrElements, tailleElement)
Renvoie un descripteur de type QueueHandle_t pour une file pouvant contenir jusqu’à
nbrElements éléments. La taille des éléments (tailleElement) est exprimée en octets (utiliser sizeof).

Exemple :
Création d’une file d’entiers de taille maximale 10 :

QueueHandle_t file;
file = xQueueCreate(10, sizeof(int));

− Suppression :
o vQueueDelete(descripteur)
Supprime la file désignée par son descripteur

− Vidage :
o xQueueReset(descripteur)
Vide la file désignée par son descripteur

Remarque : Dans les fonctions suivantes element est un pointeur de type (void *)

− Lecture bloquante :
o xQueueReceive(descripteur, element, nbrTicksAttente)
o xQueueReceiveFromISR(descripteur, element)
Attend l’arrivée d’une donnée dans la file désignée par son descripteur et la place dans la variable
pointée par element. Pour la première, l’attente est limitée à nbrTicksAttente unités de temps (5 ms)
ou infinie si ce paramètre vaut portMAX_DELAY. Pour la seconde l’attente est infinie. La valeur
de retour est pdPASS si une donnée a été récupérée et errQUEUE_EMPTY si la file est vide
jusqu’à la fin du délai d’attente.

o xQueuePeek(descripteur, element, nbrTicksAttente)


o xQueuePeekFromISR(descripteur, element)
Fonctionnent comme xQueueReceive et xQueueReceiveFromISR à la différence que la donnée
récupérée n’est pas enlevée de la file.

Exemple :
int recu;
xQueueReceive(file, &recu, portMAX_DELAY);
− Ecriture bloquante avec délai maximum d’attente :
o xQueueSendToBack(descripteur, element, nbrTicksAttente)
o xQueueSendToBackFromISR(descripteur, element, NULL)
Ajoute la donnée contenue dans la variable pointée par element à la fin de la file désignée par son
descripteur. Pour la première, l’attente est limitée à nbrTicksAttente unités de temps (5 ms) ou
infinie si ce paramètre vaut portMAX_DELAY. Pour la seconde l’attente est infinie. La valeur de
retour est pdPASS si une donnée a été déposée et errQUEUE_FULL si la file est pleine jusqu’à la
fin du délai d’attente.

o xQueueOverwrite(descripteur, element
o xQueueOverwriteFromISR(descripteur, element, NULL)
Fonctionnent comme xQueueSendToBack et xQueueSendToBackFromISR à la différence que la
donnée écrite remplace celle qui était en fin de file.

o xQueueSendToFront(descripteur, element, nbrTicksAttente)


o xQueueSendToFront FromISR(descripteur, element, NULL)
Fonctionnent comme xQueueSendToBack et xQueueSendToBackFromISR à la différence que la
donnée est ajoutée en début de file

Exemple :
int envoi;
envoi = 5 ;
xQueueSendToBack(file, &envoi, portMAX_DELAY);

− Taille :
o uxQueueSpacesAvailable(descripteur)
Renvoie la place disponible dans la file désignée par son descripteur en nombre d’éléments

o xQueueIsQueueEmptyFromISR(descripteur)
Indique si la file désignée par son descripteur est vide (renvoie pdFALSE si la file est vide)

o xQueueIsQueueFullFromISR(descripteur)
Indique si la file désignée par son descripteur est pleine (renvoie pdFALSE si la file n’est pas
pleine)

o xQueueMessagesWaiting(descripteur)
o xQueueMessagesWaitingFromISR(descripteur)
Renvoie le nombre d’éléments contenus dans la file désignée par son descripteur

Sémaphores :
Les sémaphores permettent la synchronisation entre tâches ou entre tâches et fonctions d’interruption. RTOS
propose 3 types de sémaphores (compteurs, binaires et mutex). L’opération P est bloquante si le sémaphore est
fermé, on peut choisir de limiter l’attente à un délai donné ou d’attendre indéfiniment.

− Déclaration :
o SemaphoreHandle_t descripteur;
Déclaration d’un descripteur de sémaphore qui sera initialisé par xSemaphoreCreateBinary ou
xSemaphoreCreateMutex ou xSemaphoreCreateCounting (voir ci-dessous).

Sémaphores binaires (le sémaphore ne peut être qu’ouvert ou fermé, il peut être ouvert par une tâche différente
de celle qui l’a fermé) :
− Création :
o xSemaphoreCreateBinary()
Renvoie un descripteur de type SemaphoreHandle_t (le sémaphore est créé fermé)

Exemple :
Création d’un sémaphore binaire :
SemaphoreHandle_t sem;
sem = xSemaphoreCreateBinary();
Mutex (le sémaphore ne peut être qu’ouvert ou fermé, il doit être ouvert par la tâche qui l’a fermé) :
− Création :
o xSemaphoreCreateMutex()
Renvoie un descripteur de type SemaphoreHandle_t (le sémaphore est créé fermé)

Exemple :
Création d’un mutex :
SemaphoreHandle_t sem;
sem = xSemaphoreCreateMutex();

Sémaphores compteurs (le sémaphore peut prendre N valeurs, il peut être décrémenté (opération P) par une
tâche différente de celle qui l’a incrémenté (opération V)) :
− Création :
o xSemaphoreCreateCounting(valeurMax, valeurInitiale)
Renvoie un descripteur de type SemaphoreHandle_t (le sémaphore est créé de sorte que sa valeur
maximale soit valeurMax et sa valeur initiale soit valeurInitiale)

Exemple :
Création d’un sémaphore compteur de valeur max 10 initialement à 0 :
SemaphoreHandle_t sem;
sem = xSemaphoreCreateCounting(10, 0);

Manipulation de sémaphores :
− Opération P :
o xSemaphoreTake(descripteur, nbrTicksAttente) .
Décrémente ou ferme le sémaphore désigné par son descripteur, cette opération est bloquante
jusqu’à ce que le sémaphore puisse être décrémenté ou fermé. La valeur de retour est pdPASS si le
sémaphore a bien été modifié et pdFAIL s’il ne l’a pas été à la fin du délai d’attente. Le délai
d’attente est défini par nbrTicksAttente en unités de temps (5 ms) ou infini si ce paramètre vaut
portMAX_DELAY, dans ce cas la valeur de retour ne pourra être que pdPASS puisque sinon la
tâche est bloquée.

Exemple :
Opération P sur un sémaphore :
SemaphoreHandle_t sem;
// Création du sémaphore (voir ci-dessous)
xSemaphoreTake(sem, portMAX_DELAY);

− Opération V :
o xSemaphoreGive(descripteur).
o xSemaphoreGiveFromISR(descripteur, NULL) .
Incrémente ou ouvre le sémaphore désigné par son descripteur, cette opération peut être bloquante
pour un sémaphore compteur qui a atteint sa valeur maximale. La valeur de retour est pdPASS si le
sémaphore a bien été incrémenté ou ouvert et pdFAIL s’il ne l’a pas été parce que la tâche ne
l’avait pas fermé (pour les Mutex seulement).

Exemple :
Opération V sur un sémaphore :
SemaphoreHandle_t sem;
// Création du sémaphore (voir ci-dessous)
xSemaphoreGive(sem);

− Suppression :
o vSemaphoreDelete(descripteur)
Supprime le sémaphore désigné par son descripteur

− Etat :
o uxSemaphoreGetCount(descripteur)
Renvoie la valeur actuelle du sémaphore
Timers logiciels :
Les timers logiciels permettent de définir autant de timers qu’on veut. Un timer est programmé pour un délai
répétitif ou non répétitif dont la durée (ou la période s’il est répétitif) est exprimée en unités de 5 ms. On associe
à chaque timer une fonction qui sera automatiquement exécutée à l’échéance de son délai ou à chaque période.

− Déclaration :
o TimerHandle_t descripteur ;
Déclaration d’un descripteur de timer qui sera initialisé par xTimerCreate.

− Création :
o xTimerCreate(nom, periode, autoReload, id, fctAppelée)
Renvoie un descripteur de type TimerHandle_t
 nom : nom du timer. Ce nom ne sert que pour les traces.
 période : durée ou période du timer en nombre de ticks (5 ms)
 autoReload vaut pdTRUE ou pdFALSE selon que le timer doit recommencer son
délai (période) ou pas (délai).
 id : est de type void * . En général c’est un numéro utilisé lorsque plusieurs timers
appellent la même fonction. Celle-ci pourra récupérer ce numéro par
pvTimerGetTimerID( descripteur). Pour assigner un numéro à un timer on peut faire :
long num = 3; // Numéro du timer = 3
uint16_t periode = 1000/5; // période de 1s
TimerHandle_t tim 3 = xTimerCreate("NomDuTimer3", periode,
pdTRUE, (void *)num, fctTimer3);
 fctAppelée : La fonction appelée à chaque fin de délai du timer. Elle est de type :
void fct(TimerHandle_t descripteur )

− Suppression :
o xTimerDelete(descripteur, nbrTicksAttente)
Supprime le timer désigné par son descripteur. L’attente de cette suppression est limitée à
nbrTicksAttente unités de temps (5 ms) ou infinie si ce paramètre vaut portMAX_DELAY.

− Démarrage / Arrêt :
o xTimerStart(descripteur, nbrTicksAttente)
o xTimerStartFromISR(descripteur, NULL)
Démarre le timer désigné par son descripteur. Pour la première fonction, l’attente de ce démarrage
est limitée à nbrTicksAttente unités de temps (5 ms) ou infinie si ce paramètre vaut
portMAX_DELAY.

o xTimerStop(descripteur, nbrTicksAttente)
o xTimerStopFromISR(descripteur, NULL)
Arrête le timer désigné par son descripteur. Pour la première fonction, l’attente de cet arrêt est
limitée à nbrTicksAttente unités de temps (5 ms) ou infinie si ce paramètre vaut portMAX_DELAY.

− Redémarrage :
o xTimerReset(descripteur, nbrTicksAttente)
o xTimerResetFromISR(descripteur, NULL)
Redémarre le timer désigné par son descripteur. Pour la première fonction, l’attente de ce
redémarrage est limitée à nbrTicksAttente unités de temps (5 ms) ou infinie si ce paramètre vaut
portMAX_DELAY.

− Etat :
o pvTimerGetTimerID( descripteur)
Renvoie l’ID (numéro) du timer désigné par son descripteur dont le type devra être converti par :
long id = ( long ) pvTimerGetTimerID(descripteur);

o xTimerGetExpiryTime(descripteur)
Renvoie le temps restant pour le timer désigné par son descripteur pour terminer son délai ou sa
période.
o xTimerGetPeriod(descripteur)
Renvoie la durée ou la période associée au timer désigné par son descripteur.

o xTimerIsTimerActive(descripteur)
Renvoie pdFALSE si le timer désigné par son descripteur est en marche, dans le cas contraire la
valeur n’est pas pdFALSE (mais pas forcément pdTRUE)

− Modification :
o xTimerChangePeriod(descripteur, periode, nbrTicksAttente)
o xTimerChangePeriodFromISR(descripteur, periode, NULL)
Modifie la durée ou la période associée au timer désigné par son descripteur par la valeur donnée
dans periode. Pour la première fonction, l’attente de cette modification est limitée à
nbrTicksAttente unités de temps (5 ms) ou infinie si ce paramètre vaut portMAX_DELAY.

Exemple :
Le programme suivant crée trois tâches infinies de même priorité :
− La 1ère fait clignoter une LED et envoie un message (compteur) à la 2ème tâche chaque seconde
− La 2ème attend le message de la 1ère, l’affiche sur l’écran LCD et débloque la 3ème par un sémaphore
− La 3ème se bloque sur un sémaphore (débloqué par la 2ème) puis incrémente un compteur et l’affiche sur
l’écran LCD

// Inclusions générales
//#include <stdlib.h>
//#include <string.h>
//#include <avr/io.h>
//#include <avr/sleep.h>
#include <stdbool.h>
#include <stdio.h>
#include "USART.h"
#include "rgb_lcd.h"

// Inclusions du noyau RTOS


#include "FreeRTOS.h" // Noyau
#include "task.h" // taches
//#include "timers.h" // timers (non utilisés ici)
#include "queue.h" // files
#include "semphr.h" // sémaphores

// Macros de mise à 0/1 d’un bit et de test de la valeur d’un bit


#define IS_BIT_SET(r, num) ((r & (1<<num)) != 0)
#define IS_BIT_CLR(r, num) ((r & (1<<num)) == 0)
#define SET_BIT(r, num) r = (r | (1<<num))
#define CLR_BIT(r, num) r = (r & (~(1<<num)))

// Priorités auxquelles les tâches sont créées


#define PRIORITE_TACHE_1 ( tskIDLE_PRIORITY + 1 )
#define PRIORITE_TACHE_2 ( tskIDLE_PRIORITY + 1 )
#define PRIORITE_TACHE_3 ( tskIDLE_PRIORITY + 1 )

// Tâches définies
void tacheUn( void *pvParameters );
void tacheDeux( void *pvParameters );
void tacheTrois( void *pvParameters );

// Descripteurs de tâches (T1 et T2 seulement)


TaskHandle_t ht1, ht2;

int colorR = 100; int colorG = 100; int colorB = 100; // couleur de l’écran LCD

QueueHandle_t file; // file de communication entre la tâche 1 et 2


SemaphoreHandle_t sem; // sémaphore utilisé par les tâches 2 et 3
int delai; // paramètre de la tâche 1 (durée du délai)
int main(void) {
USART_Init(115200); // init de la console pour printf
printf("RTOS \n"); // trace à la console
begin(16, 2,LCD_5x8DOTS); // init de l’écran LCD
setRGB(colorR, colorG, colorB); // couleur d'éclairage

file = xQueueCreate(2, sizeof(int)); // création de la file entre tâche 1 et 2


sem = xSemaphoreCreateBinary(); // création du sémaphore pour tâches 2 et 3

// Création des 3 tâches


int delai = 1000/5; // délai de 1 s (paramètre de la tâche 1)
xTaskCreate( tacheUn, /* Fonction qui implémente la tâche */
"T1", /* Nom de la tâche pour debbogage, non utilisé par l’ordonnanceur. */
configMINIMAL_STACK_SIZE, /* Taille de la pile de la tâche, en mots */
(void *)&delai, /* Paramètre de la tâche */
PRIORITE_TACHE_1, /* Priorité assignée à la tâche à sa création */
&h1); /* Descripteur de la tâche (non utilisé ici) */
xTaskCreate( tacheDeux, "T2", configMINIMAL_STACK_SIZE, NULL, PRIORITE_TACHE_2,
&h2); // pas de paramètre
xTaskCreate( tacheTrois, "T3", configMINIMAL_STACK_SIZE, NULL, PRIORITE_TACHE_3,
NULL); // pas de paramètre, pas de descripteur

// Démarrage de l’ordonnanceur
vTaskStartScheduler();

// Si tout se passe bien on n'arrive jamais ici puisque l’ordonnanceur tourne


// indéfiniment. Si on y arrive c'est qu'il n'y a pas assez de mémoire pour
// créer la tâche "idle"
while (0==0) {}
}

void tacheUn(void *paramDelai) { // Première tâche


SET_BIT(DDRH,5); // programmation de la ligne pour la LED
CLR_BIT(PORTH,5); // LED éteinte
int t1 = 5; // compteur servant de message pour la tâche 2
int *delai = paramDelai; // récupération du param et conversion en pointeur d'entier
while (0==0) {
vTaskDelay(*delai); // délai reçu en paramètre
SET_BIT(PINH,5); // clignotement de la LED
xQueueSendToBack(file, &t1, portMAX_DELAY); // envoi message par file
t1++; // incrémentation du compteur envoyé par la file
}
}

void tacheDeux(void *pvParameters ) { // deuxième tâche


int t2; // variable pour recevoir le message par file
while (0==0) {
xQueueReceive(file, &t2, portMAX_DELAY); // attente message dans la file
setCursor(0,0); print_int_lcd(t2); // affichage du message reçu
xSemaphoreGive(sem); // ouverture du sémaphore pour débloquer la tâche 3
}
}

void tacheTrois(void *pvParameters ) { // troisième tâche


int t = 100; // compteur local affiché
while (0==0) {
xSemaphoreTake(sem, portMAX_DELAY); // attente sur sémaphore (ouvert par T2)
setCursor(0,1); print_int_lcd(t); // affichage du compteur local
t++; // incrémentation du compteur local affiché
}
}