CHAPITRE 5:
COMMUNICATION ET
SYNCHRONISATION DES
TÂCHES
OS Embarqués et Temps Réel Dr. Manel FOURATI 1
Plan du chapitre:
1. Introduction
2. Communication et Synchronisation inter-tâches
3. Gestion des flags: Thread flags
4. Gestion des flags: Event flags
5. Section critique
6. Les Mutex
7. Sémaphores
8. Message queue
OS Embarqués et Temps Réel Dr. Manel FOURATI 2
▪ La programmation d’une application temps réel fait souvent intervenir plusieurs
tâches indépendantes.
▪ Il se pose alors un certain nombre de problèmes. Afin de les surmonter, on doit
utiliser les deux principes suivants :
o La communication : échange de données entre tâches.
o La synchronisation : relation d'ordre entre les processus (tâches) avec
l'évolution d'autre tâches.
OS Embarqués et Temps Réel Dr. Manel FOURATI 3
▪ Plusieurs tâches peuvent s’exécuter de façon concurrente dans une
application temps réel :
o ces tâches ne sont pas indépendantes,
o besoin d'échanger des données,
o besoin de synchroniser les moments ou les données sont échangées.
OS Embarqués et Temps Réel Dr. Manel FOURATI 4
▪ La communication entre les threads permet de rendre une application utile.
▪ CMSIS RTOS définie des mécanismes de communication différents afin de relier les
threads entre eux.
o Thread flags: communication et synchronisation entre tâches
o Event flags: communication et synchronisation par évènement
o Mutex (exclusion mutuelle) : accès concurrent à des ressources communes
(critiques).
o Sémaphores: mettre en œuvre pour des problèmes de synchronisation complexes.
o Message queue: assurer une communication asynchrone entre les parties de
l’application: file d’attente de messages.
OS Embarqués et Temps Réel Dr. Manel FOURATI 5
▪ L’objectif principal de ces mécanismes est la synchronisation de l’activité de plusieurs
threads qui vise à:
o Bloquer l'exécution de certaines tâches à des points précis de leur flux d'exécution.
o Les processus se rejoignent à des étapes relais données, tel que prévu par le
programmeur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 6
▪ Thread flags est un mécanisme utilisé pour la synchronisation des threads.
▪ Chaque thread possède (par défaut) 32 bits pré-alloué: ces bits sont alloués
automatiquement au moment de la création du thread.
▪ Ils représentent 31 threads flags ( drapeaux de thread) pour chaque thread.
▪ Tfi représente l’indicateur du thread i.
▪ Les threads flags sont stockés dans le bloc de contrôle de thread.
OS Embarqués et Temps Réel Dr. Manel FOURATI 7
Principe:
▪ Il est possible d’arrêter l’exécution d’un thread jusqu’à ce qu’un
indicateur de thread particulier ou un groupe d’indicateurs de thread soit
défini par un autre thread du système:
o Un thread peut activer un thread flag (TF) d’un autre thread.
o Un thread peut attendre que ses TF soient définis par des
threads/interruptions.
OS Embarqués et Temps Réel Dr. Manel FOURATI 8
int32_t osThredFlagsSet (osThreadId_t thread_id, int32_t flags) ;
▪ La fonction osThreadFlagsSet active les threads flags pour un thread spécifié par le
paramètre thread_id.
▪ Le thread renvoie les drapeaux (flags) stockés dans le bloc de contrôle de thread, ou un
code d’erreur.
▪ Le thread cible en attente de la définition d’un flag reprendra à partir de l’état BLOQUÉ.
▪ int32_t flags: chaque bit dans flags définit le TF correspondant
Exemple: flags=0x8002 => TF #15 and TF #1
OS Embarqués et Temps Réel Dr. Manel FOURATI 9
▪ Les valeurs renvoyées en cas d’erreur:
o osFlagsErrorUnknown : erreur non spécifiée.
o osFlagsErrorParameter : le paramètre thread_id n’est pas un thread valide.
o osFlagsErrorResource : le thread est dans un état non valide.
OS Embarqués et Temps Réel Dr. Manel FOURATI 10
▪ Exemple:
void ledOn(void constant *argument) {
for (;;) {
LED_On(0);
osThreadFlagsSet(tid_ledOff, 0x0001);
osDelay(2000);
LED_Off(0);
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 11
uint32_t osThreadFlagsWait (uint32_t flags, uint32_t options, uint32_t timeout)
▪ La fonction osThreadFlagsWait suspend l’exécution du thread en cours d’exécution
jusqu’à ce que l’un ou les threads flags spécifiés avec flags soient définis (activés).
▪ Si les threads flags ne sont pas définis, le thread est mis dans l’état BLOCKED.
▪ Le paramètre options spécifie la condition d’attente. Option peut prendre les valeurs
suivantes:
o osFlagsWaitAny: Attendre n’importe quel flags (par défaut).
o osFlagsWaitAll= Attendre tous les flags.
OS Embarqués et Temps Réel Dr. Manel FOURATI 12
▪ Le paramètre Timeout représente un nombre de ticks représentant le temps d’attente.
▪ Timeout = 0 (vérification et retour), osWaitForever, or time T
▪ osWaitForever: Cette fonction est souvent utilisée pour mettre en attente un thread ou une
tâche indéfiniment, c'est-à-dire pour bloquer le thread jusqu'à ce qu'un événement
spécifique se produise ou jusqu'à ce qu'il soit explicitement réveillé par une autre partie du
programme.
OS Embarqués et Temps Réel Dr. Manel FOURATI 13
▪ La fonction osThreadFlagsWait renvoie les 32 bits valeur du TF avant l’effacement, ou un
code d’erreur:
o osFlagsErrorTimeout: Les TF sont définis avant l'expiration du délai timeout
o osFlagsErrorResource: si les TFs sont non définis et le timeout = 0
o osFlagsErrorUnknown: erreur non spécifiée (non appelée à partir du contexte correct)
OS Embarqués et Temps Réel Dr. Manel FOURATI 14
▪ Exemple:
// Thread Flag Example –Thread3 must wait for signals from both Thread1 and Thread2
#include "cmsis_os2.h"
osThreadId_t tid1;//three threads
osThreadId_t tid2;
osThreadId_t tid3;
void thread1 (void *argument) {
while (1) {
osThreadFlagsSet(tid3, 0x0001); /* signal thread 3 */
….
void thread2 (void *argument) {
while (1) {
osThreadFlagsSet(tid3, 0x0002); /* signal thread 3 */
….
void thread3 (void *argument) {
uint32_t flags;
while (1) {
//wait for signals from both thread1 and thread2
flags = osThreadFlagsWait(0x0003, osFlagsWaitAll, osWaitForever);
OS Embarqués et Temps Réel Dr. Manel FOURATI 15
uint32_t osThreadFlagsClear (uint32_t flags)
▪ La fonction osThreadFlagsClear efface les indicateurs spécifiés pour le thread en cours
d’exécution.
▪ Flags: Spécifie les indicateurs du thread qui doivent être effacés.
▪ Les fonctions d’erreur qui peuvent être renvoyées :
o osFlagsErrorUnknown : erreur non spécifiée, c’est-à-dire non appelée à partir d’un
contexte de threads en cours d’exécution.
o osFlagsErrorParameter : le paramètre flags a le bit le plus élevé défini.
o osFlagsErrorISR : la fonction osThreadFlagsClear ne peut pas être appelée à partir
des routines de service d’interruption.
OS Embarqués et Temps Réel Dr. Manel FOURATI 16
uint32_t osThreadFlagsGet ()
▪ La fonction osThreadFlagsGet renvoie les flags actuellement définis pour le thread en
cours d’exécution.
▪ La fonction renvoie zéro en cas où elle est appelée sans thread actif.
OS Embarqués et Temps Réel Dr. Manel FOURATI 17
Ecrire un programme CMSIS RTX permettant de définir quatre threads: thread1, thread2,
thread3 et thread4. L’objectif est d’utiliser "thread flags" pour synchroniser les quatre tâches.
Le processus d’exécution est le suivant:
• Le thread1 clignote la LED0 1 fois puis active le thread flag (Ox0001) du thread2,
• Le thread2 clignote la LED1 1 fois puis active le thread flag (Ox0001) du thread1,
• Le thread1 clignote la LED0 1 fois puis active le thread flag (Ox0001) du thread3 et le
thread flag (Ox0002) du thread4,
• On refait le traitement.
OS Embarqués et Temps Réel Dr. Manel FOURATI 18
#include <RTE_Components.h> __NO_RETURN void thread1 (void *argument){(void)argument;
#include <cmsis_os2.h> LED_Initialize();for (;;){
#include <stm32f4xx.h> LED_On(0);
#include <Board_LED.h> osDelay(1000U);
#include <rtx_os.h> osThreadFlagsSet(id2,0x0001);
void thread1 (void *argument); LED_Off(0);
void thread2 (void *argument); osDelay(1000U);
void thread3 (void *argument); osThreadFlagsWait(0x0001, osFlagsWaitAny, osWaitForever); LED_On(0);
void thread4 (void *argument); osDelay(1000U);
static osThreadId_t id1, id2,id3, id4; osThreadFlagsSet(id3,0x0001);
osThreadFlagsSet(id4,0x0002);
LED_Off(0);
osDelay(1000U);}}
OS Embarqués et Temps Réel Dr. Manel FOURATI 19
__NO_RETURN void thread2 (void *argument){
(void)argument; LED_Initialize(); __NO_RETURN void thread4 (void *argument){
for (;;) { (void)argument;
osThreadFlagsWait(0x0001, osFlagsWaitAny, osWaitForever); LED_Initialize();
LED_On(1); for (;;) {
osDelay(1000U); osThreadFlagsWait(0x0002, osFlagsWaitAny, osWaitForever);
osThreadFlagsSet(id1,0x0001); LED_On(3);
LED_Off(1); osDelay(1000U);
osDelay(3000U); LED_Off(3);
}} osDelay(3000U);
__NO_RETURN void thread3 (void *argument){ }}
(void)argument; int main(void){
LED_Initialize(); osKernelInitialize();
for (;;) { id1 = osThreadNew(thread1, NULL,NULL);
osThreadFlagsWait(0x0001, osFlagsWaitAny, osWaitForever); id2=osThreadNew(thread2,NULL,NULL);
LED_On(2); id3=osThreadNew(thread3,NULL,NULL);
osDelay(1000U); id4=osThreadNew(thread4,NULL,NULL);
LED_Off(2); osDelay(3000U);} } osKernelStart();}
OS Embarqués et Temps Réel Dr. Manel FOURATI 20
▪ Event flags est un mécanisme de synchronisation des threads qui se base sur des
indicateurs d’événement.
▪ Les fonctions de gestion des Event flags permettent de contrôler ou d’attendre les
indicateurs d’événement(Event flags).
▪ Chaque signal comporte jusqu’à 31 drapeaux d’événement.
▪ Event flags fonctionne de la même manière que thread flags, mais:
o n'appartient à aucun thread.
o doit être créé.
OS Embarqués et Temps Réel Dr. Manel FOURATI 21
▪ Un thread peut:
o attendre que les Event flags soient définis (à l’aide de osEventFlagsWait). À
l’aide de cette fonction, il passe à l’état BLOQUÉ.
o définir un ou plusieurs indicateurs Event flags dans n’importe quel autre thread
donné (à l’aide de osEventFlagsSet).
o effacer ses propres signaux ou les signaux d’autres threads (à l’aide
de osEventFlagsClear).
▪ Lorsqu’un thread se réveille et reprend l’exécution, ses indicateurs de signal sont
automatiquement effacés (sauf si l’option d’indicateurs
d’événement osFlagsNoClear est spécifiée).
OS Embarqués et Temps Réel Dr. Manel FOURATI 22
▪ struct osEventFlagsAttr_t: Les attributs permettant de configurer un ensemble de
Event flags:
o const char *name: Nom du event flags
o uint32_t attr_bits: bits d’attribut
o void *cb_mem: Mémoire pour bloc de contrôle
o uint32_t cb_size: taille de la mémoire fournie pour le bloc de contrôle
OS Embarqués et Temps Réel Dr. Manel FOURATI 23
osEventFlagsId_t osEventFlagsNew (const osEventFlagsAttr_t * attr)
▪ La fonction osEventFlagsNew permet de créer un nouvel objet Event Flags pour
transmettre des événements entre les threads.
▪ Le paramètre attr définit les attributs des indicateurs d’événement.
▪ Les attributs par défaut seront utilisés s’ils sont définis sur NULL, c’est-à-dire que
l’allocation de mémoire du noyau est utilisée pour le bloc de contrôle des événements.
OS Embarqués et Temps Réel Dr. Manel FOURATI 24
▪ Exemple:
#include "cmsis_os2.h"
osEventFlagsId_t evt_id;
int Init_Events (void) {
evt_id = osEventFlagsNew(NULL);
if (evt_id == NULL) {
; // Event Flags object not created, handle failure
return(-1);
}
return(0);
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 25
uint32_t osEventFlagsSet(osEventFlagsId_t ef_id, uint32_t flags)
▪ La fonction osEventFlagsSet définit les Flags spécifiés par le paramètre flags dans un
objet d’Event Flags spécifié par le paramètre ef_id.
▪ Les threads ayant la priorité la plus élevée en attente de l’ensemble d’indicateurs
seront notifiés pour reprendre à partir de l’état BLOQUÉ.
▪ ef_id: Identifiant de Event Flags obtenu par osEventFlagsNew.
▪ Flags: Spécifie les flags qui doivent être définis.
▪ La fonction renvoie les indicateurs d’événement stockés dans le bloc de contrôle
d’événement ou un code d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 26
▪ Les fonctions d’erreur qui peuvent être renvoyées :
o osFlagsErrorUnknown : erreur non spécifiée.
o osFlagsErrorParameter : le paramètre ef_id n’identifie pas un objet d’indicateur
d’événement valide ou flags dont le bit le plus élevé est défini.
o osFlagsErrorResource : l’objet d’indicateur d’événement est dans un état non valide.
OS Embarqués et Temps Réel Dr. Manel FOURATI 27
▪ Exemple:
#include "cmsis_os2.h"
osEventFlagsId_t evt_id;
void Thread_EventSender (void *argument) {
while (1) {
osEventFlagsSet(evt_id, 0x00000001U);
osThreadYield();
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 28
uint32_t osEventFlagsWait(osEventFlagsId_t ef_id, uint32_t flags, uint32_t options,
uint32_t timeout )
▪ La fonction osEventFlagsWait suspend l’exécution du thread en cours d’exécution jusqu’à
ce que les Flags spécifiés par les indicateurs de paramètre dans l’objet d’événement
spécifié par le paramètre ef_id soient définis.
▪ Lorsque les Event Flags sont déjà définis, la fonction retourne instantanément. Dans le cas
contraire, le thread est mis dans l’état BLOCKED.
OS Embarqués et Temps Réel Dr. Manel FOURATI 29
▪ Le paramètre options spécifie la condition d’attente :
o osFlagsWaitAny: Attendez n’importe quel indicateur.
o osFlagsWaitAll: Attendez tous les flags.
o osFlagsNoClear: N’effacez pas les flags qui ont été spécifiés pour attendre.
▪ Le paramètre timeout spécifie le temps d’attente du système pour les Event Flags et le
thread est mis dans l’état BLOQUÉ.
OS Embarqués et Temps Réel Dr. Manel FOURATI 30
▪ Le paramètre timeout peut avoir les valeurs suivantes :
o 0: la fonction retourne instantanément.
o osWaitForever: la fonction attendra un temps infini jusqu’à ce que les indicateurs
d’événement deviennent disponibles.
o Toutes les autres valeurs spécifient un temps dans les ticks du noyau pour un délai
d’attente.
OS Embarqués et Temps Réel Dr. Manel FOURATI 31
▪ La fonction renvoie les Event Flags avant l’effacement ou un code d’erreur.
▪ Les valeurs renvoyées en cas d’erreur:
o osFlagsErrorUnknown : erreur non spécifiée.
o osFlagsErrorTimeout : les flags attendus n’ont pas été définis dans le temps imparti.
o osFlagsErrorResource : les indicateurs attendus n’ont pas été définis lorsqu’aucun délai
d’expiration n’a été spécifié.
o osFlagsErrorParameter : le paramètre ef_id n’identifie pas un objet d’indicateur
d’événement valide ou flags dont le bit le plus élevé est défini.
OS Embarqués et Temps Réel Dr. Manel FOURATI 32
▪ Exemple:
#include "cmsis_os2.h"
osEventFlagsId_t evt_id;
void Thread_EventReceiver (void *argument) {
uint32_t flags;
while (1) {
flags = osEventFlagsWait(evt_id, 0x00000001U, osFlagsWaitAny,
osWaitForever);
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 33
uint32_t osEventFlagsClear(osEventFlagsId_t ef_id, uint32_t flags)
▪ La fonction osEventFlagsClear efface les Event Flags spécifiés en paramètre pour un
objet Event Flags spécifié par le paramètre ef_id.
▪ La fonction renvoie les Event Flags avant l’effacement ou un code d’erreur.
▪ En cas d’erreur, les valeurs renvoyées :
o osFlagsErrorUnknown : erreur non spécifiée.
o osFlagsErrorParameter : le paramètre ef_id n’identifie pas un objet Event Flags
valide ou flags dont le bit le plus élevé est défini.
o osFlagsErrorResource : l’objet d’indicateur d’événement est dans un état non
valide.
OS Embarqués et Temps Réel Dr. Manel FOURATI 34
uint32_t osEventFlagsGet(osEventFlagsId_t ef_id )
▪ La fonction osEventFlagsGet renvoie:
o les indicateurs d’événement( Event Flags) actuellement définis dans un objet
d’indicateur d’événement spécifié par le paramètre ef_id.
o 0 en cas d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 35
osStatus_t osEventFlagsDelete(osEventFlagsId_t ef_id)
▪ La fonction osEventFlagsDelete supprime l’objet event flags spécifié par le paramètre
ef_id et libère la mémoire interne obtenue pour la gestion des indicateurs d’événement.
▪ Après cet appel, le ef_id n’est plus valide et ne peut plus être utilisé.
▪ Cela peut poser un problème des threads qui attendent les indicateurs de cet objet
d’événement.
▪ Le ef_id peut être créé à nouveau à l’aide de la fonction osEventFlagsNew.
OS Embarqués et Temps Réel Dr. Manel FOURATI 36
▪ osStatus_t : code d’état qui indique l’état d’exécution de la fonction.
▪ Valeurs de retour possibles osStatus_t :
o osOK : l’objet d’indicateurs d’événement spécifié a été supprimé.
o osErrorISR : osEventFlagsDelete ne peut pas être appelé à partir des routines de
service d’interruption.
o osErrorParameter : le paramètre ef_id est NULL ou non valide.
o osErrorResource : l’objet d’indicateurs d’événement est dans un état non valide.
OS Embarqués et Temps Réel Dr. Manel FOURATI 37
const char * osEventFlagsGetName( osEventFlagsId_t ef_id)
▪ La fonction osEventFlagsGetName renvoie:
o le pointeur vers la chaîne de nom de l’objet event flags identifié par le paramètre
ef_id
o NULL en cas d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 38
void thread1 (void *argument){
for (;;){
osEventFlagsSet(fl1, 0x0001);
osEventFlagsWait(fl2 ,0x0002, osFlagsWaitAny,osWaitForever);} }
void thread2 (void *argument) {
{osEventFlagsWait(fl1, 0x0001, osFlagsWaitAny, osWaitForever);
osEventFlagsSet(fl2, 0x0002); } }
Int main(void) {osKernelInitialize();
id1=osThreadNew(thread1,NULL,NULL);
id2=osThreadNew(thread2, NULL,NULL);
fl1= osEventFlagsNew(NULL);
fl2= osEventFlagsNew(NULL);
osKernelStart();}
OS Embarqués et Temps Réel Dr. Manel FOURATI 39
Ecrire un programme CMSIS RTX permettant de définir trois threads: thread1, thread2 et thread3.
L’objectif est d’utiliser "Event flags" pour synchroniser les trois tâches.
Le processus d’exécution est le suivant:
• La LED1 et La LED2 s’allument et après une durée de 1000 ticks la LED2 s’éteint.
• Lorsque la LED2 est éteinte, la LED3 s’allume (sachant que la LED1 est encore allumée).
• par la suite la LED1 s’éteint et la LED2 s’allume (sachant que la LED3 est encore allumée).
• LED2 et LED3 s’éteint.
• On refait le traitement.
OS Embarqués et Temps Réel Dr. Manel FOURATI 40
#include <RTE_Components.h> __NO_RETURN void led_Thread1 (void *argument) { (void)argument;
#include <cmsis_os2.h> for (;;) {
#include <stm32f4xx.h> osEventFlagsWait (EventFlag_LED,0x01,osFlagsWaitAny,osWaitForever);
#include <Board_LED.h> LED_On(1);
#include <rtx_os.h> osEventFlagsWait(EventFlag_LED,0x01,osFlagsWaitAny,osWaitForever);
void led_Thread1 (void *argument) LED_Off(1); }}
;void led_Thread2 (void *argument);
void led_Thread3 (void *argument); static const osThreadAttr_t ThreadAttr_LED2 =
void app_main (void *argument); {.name = "LED2", .priority = osPriorityNormal,};
static osEventFlagsId_t __NO_RETURN void led_Thread2 (void *argument) {
EventFlag_LED; (void)argument;
static osThreadId_t T_led_ID1; for (;;) {
static osThreadId_t T_led_ID2; osEventFlagsSet (EventFlag_LED,0x01);
static osThreadId_t T_led_ID3; LED_On(2);
static const osThreadAttr_t osDelay(1000);
ThreadAttr_LED1 = { .name = LED_Off(2);
"LED1", .priority = osEventFlagsSet (EventFlag_LED,0x01);
osPriorityAboveNormal,}; osDelay(1000); }}
OS Embarqués et Temps Réel Dr. Manel FOURATI 41
static const osThreadAttr_t ThreadAttr_LED3 =
{.name = "LED3", .priority = osPriorityAboveNormal,};
__NO_RETURN void led_Thread3 (void *argument) { int main (void) {
(void)argument; osKernelInitialize();
for (;;) { LED_Initialize();
osEventFlagsWait EventFlag_LED = osEventFlagsNew(&EventFlagAttr_LED);
(EventFlag_LED,0x01,osFlagsWaitAny,osWaitForever); T_led_ID1 = osThreadNew(led_Thread1,
LED_On(3); NULL,&ThreadAttr_LED1); T_led_ID2 =
osEventFlagsWait osThreadNew(led_Thread2, NULL,&ThreadAttr_LED2);
(EventFlag_LED,0x01,osFlagsWaitAny,osWaitForever); T_led_ID3 = osThreadNew(led_Thread3,
LED_Off(3); }} NULL,&ThreadAttr_LED3);
(osKernelGetState() == osKernelReady) { osKernelStart();
static const osThreadAttr_t ThreadAttr_main = } while(1);}
{.name = "main", .priority = osPriorityNormal,};
static const osEventFlagsAttr_t EventFlagAttr_LED = {
.name = "LED_Events",};
OS Embarqués et Temps Réel Dr. Manel FOURATI 42
▪ Les problèmes de synchronisation se ramènent au problème de l’accès concurrent à
une variable partagée.
▪ Solution conceptuelle naturelle à la résolution des problèmes d’accès concurrent:
o interdire la modification de données partagées à plus qu’un processus à la fois.
o Définir un mécanisme sur des portions spécifiques du code appelé section critique.
OS Embarqués et Temps Réel Dr. Manel FOURATI 43
▪ Quatre conditions peuvent formaliser le comportement des sections critiques:
o Deux processus ne peuvent être simultanément dans la même section critique: maximum
qu'un thread à la fois.
o Aucune hypothèse n’est faite sur les vitesses relatives des processus.
o Aucun processus suspendu en dehors d’une section critique ne peux bloquer les autres.
o Aucun processus ne doit attendre trop longtemps avant d’entrer en section critique.
➔La première condition est suffisante pour éviter les conflits d’accès.
➔Ne garantit pas le bon fonctionnement du système pour l’égalité d’accès à la section
critique.
➔Solution: les mécanismes de communication et de synchronisation.
OS Embarqués et Temps Réel Dr. Manel FOURATI 44
▪ Si un thread B veut rentrer dans une section critique, et qu'un autre thread A est déjà dans la
section critique, on doit faire attendre le thread B jusqu'à ce que le thread A sorte de la section
critique.
OS Embarqués et Temps Réel Dr. Manel FOURATI 45
▪ L’exclusion mutuelle (Mutex): est utilisée pour la gestion des ressources partagées.
▪ De nombreuses ressources peuvent être utilisées de manière répétée, mais uniquement par un
thread à la fois.
▪ Les mutex sont utilisés pour protéger l’accès à une ressource partagée.
▪ Un mutex est créé puis passé entre les threads (ils peuvent acquérir et libérer le mutex).
OS Embarqués et Temps Réel Dr. Manel FOURATI 46
▪ Un mutex est une version spéciale d’un sémaphore (Le sémaphore est un conteneur pour des
jetons).
▪ Un mutex ne peut en porter qu’un seul jeton qui représente la ressource partagée.
▪ Un jeton mutex est binaire: il est soit disponible, soit bloqué par un thread propriétaire.
OS Embarqués et Temps Réel Dr. Manel FOURATI 47
▪ RTX5 fournit plusieurs paramètres pour configurer les fonctions de gestion de Mutex.
▪ RTX_Config.h : Configuration de Mutex:
o #define OS_MUTEX_OBJ_MEM: Active l’allocation de mémoire spécifique à
l’objet.
o #define OS_MUTEX_NUM: Définit le nombre maximal d’objets pouvant être
actifs en même temps.
OS Embarqués et Temps Réel Dr. Manel FOURATI 48
▪ struct osMutexAttr_t Spécifie les attributs suivants:
o const char *name: Nom du event flags
o uint32_t attr_bits: bits d’attribut. Les masques de bits suivants peuvent être utilisés pour
définir les options : osMutexRecursive, osMutexPrioInherit et osMutexRobust.
o void *cb_mem: Mémoire pour bloc de contrôle
o uint32_t cb_size: taille de la mémoire fournie pour le bloc de contrôle.
OS Embarqués et Temps Réel Dr. Manel FOURATI 49
osMutexId_t osMutexNew(const osMutexAttr_t * attr)
▪ La fonction osMutexNew permet la création et l’initialisation d’un nouvel objet mutex.
▪ Elle renvoie:
o le pointeur vers l’identificateur d’objet mutex
o NULL en cas d’erreur.
▪ Le paramètre attr définit les attributs de l’objet mutex (osMutexAttr_t).
▪ Les attributs par défaut seront utilisés s’ils sont définis sur NULL.
OS Embarqués et Temps Réel Dr. Manel FOURATI 50
▪ Exemple:
#include "cmsis_os2.h"
osMutexId_t mutex_id;
void CreateMutex (void) {
mutex_id = osMutexNew(NULL);
if (mutex_id != NULL) {
// Mutex object created
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 51
const char * osMutexGetName(osMutexId_t mutex_id)
▪ La fonction osMutexGetName renvoie:
o le pointeur vers la chaîne de nom du mutex identifié par le paramètre mutex_id.
o NULL en cas d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 52
osStatus_t osMutexAcquire(osMutexId_t mutex_id, uint32_t timeout)
▪ La fonction osMutexAcquire attend qu’un objet mutex spécifié par le paramètre mutex_id soit
disponible.
▪ Si aucun autre thread n’a obtenu le mutex, la fonction retourne instantanément et bloque l’objet
mutex.
▪ Le paramètre timeout spécifie le temps d’attente du système pour acquérir le mutex.
▪ Pendant que le système attend, le thread qui appelle cette fonction est mis dans l’état BLOQUÉ.
OS Embarqués et Temps Réel Dr. Manel FOURATI 53
▪ Le paramètre timeout peut avoir les valeurs suivantes :
o 0: la fonction retourne instantanément
o osWaitForever, la fonction attendra un temps infini jusqu’à ce que le mutex devient disponible.
o Toutes les autres valeurs spécifient un temps dans les ticks du noyau pour un délai d’attente.
▪ Valeurs de retour possibles osStatus_t :
o osOK : le mutex a été obtenu.
o osErrorTimeout : le mutex n’a pas pu être obtenu dans le temps imparti.
o osErrorResource : le mutex n’a pas pu être obtenu lorsqu’aucun délai d’expiration n’a été spécifié.
o osErrorParameter : le paramètre mutex_id est NULL ou non valide.
o osErrorISR : ne peut pas être appelé à partir des routines de service d’interruption.
OS Embarqués et Temps Réel Dr. Manel FOURATI 54
▪ Exemple:
#include "cmsis_os2.h"
void WaitMutex (void) {
osMutexId_t mutex_id;
osStatus_t status;
mutex_id = osMutexNew(NULL);
if (mutex_id != NULL) {
status = osMutexAcquire(mutex_id, 1U);
if (status != osOK) {
// handle failure code
}
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 55
osStatus_t osMutexRelease(osMutexId_t mutex_id)
▪ La fonction osMutexRelease libère un mutex spécifié par le paramètre mutex_id.
▪ Les autres threads qui attendent actuellement ce mutex seront mis dans l’état READY.
▪ Valeurs de retour possibles osStatus_t :
o osOK : le mutex a été correctement relâché.
o osErrorResource : le mutex n’a pas pu être libéré (le mutex n’a pas été acquis ou le thread en
cours d’exécution n’est pas le propriétaire).
o osErrorParameter : le paramètre mutex_id est NULL ou non valide.
o osErrorISR : osMutexRelease ne peut pas être appelé à partir des routines de service
d’interruption.
OS Embarqués et Temps Réel Dr. Manel FOURATI 56
▪ Exemple:
#include "cmsis_os2.h"
osMutexId_t mutex_id; // Mutex id populated by the function osMutexNew()
void ReleaseMutex (osMutexId_t mutex_id) {
osStatus_t status;
if (mutex_id != NULL) {
status = osMutexRelease(mutex_id);
if (status != osOK) {
// handle failure code
}
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 57
osThreadId_t osMutexGetOwner(osMutexId_t mutex_id)
▪ La fonction osMutexGetOwner renvoie:
o ID du thread qui a acquis un mutex spécifié par le paramètre mutex_id.
o NULL, en cas d’erreur ou si le mutex n’est bloqué par aucun thread.
OS Embarqués et Temps Réel Dr. Manel FOURATI 58
osStatus_t osMutexDelete(osMutexId_t mutex_id)
▪ La fonction osMutexDelete supprime un objet mutex spécifié par le paramètre mutex_id.
▪ Il libère la mémoire interne obtenue pour la gestion des mutex.
▪ Le mutex peut être recréé à l’aide de la fonction osMutexNew.
▪ Valeurs de retour possibles osStatus_t :
o osOK : l’objet mutex a été supprimé.
o osErrorParameter : le paramètre mutex_id est NULL ou non valide.
o osErrorResource : le mutex est dans un état non valide.
o osErrorISR : osMutexDelete ne peut pas être appelé à partir des routines de service
d’interruption.
OS Embarqués et Temps Réel Dr. Manel FOURATI 59
▪ Exemple:
#include "cmsis_os2.h"
osMutexId_t mutex_id; // Mutex id populated by the function osMutexNew()
void DeleteMutex (osMutexId_t mutex_id) {
osStatus_t status;
if (mutex_id != NULL) {
status = osMutexDelete(mutex_id);
if (status != osOK) {
// handle failure code
}
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 60
Ecrire un programme CMSIS RTX permettant de définir deux threads: thread1 et thread2. L’objectif
est d’utiliser le "Mutex" pour la gestion d’une ressource partagée.
Le programme affiche le résultat suivant:
OS Embarqués et Temps Réel Dr. Manel FOURATI 61
__NO_RETURN void uart_Thread1 (void *argument) {
#include <RTE_Components.h>
(void) argument;uint32_t i;
#include <cmsis_os2.h> for (;;) {osMutexAcquire(uartMutex, osWaitForever);
#include <stm32f4xx.h> for( i=0;i<10;i++) {
#include <stdio.h> printf("1");}
printf("\n");
void uart_Thread1 (void *argument);
osMutexRelease(uartMutex); }}
void uart_Thread2 (void *argument);
__NO_RETURN void uart_Thread2 (void *argument) {
void app_main (void *argument); (void) argument;uint32_t i;
static osThreadId_t T_uart1, T_uart2; for(;;){
osMutexAcquire(uartMutex, osWaitForever);
static osMutexId_t uartMutex;
for( i=0;i<10;i++) {printf("2");}
printf("\n"); osMutexRelease(uartMutex); }}
OS Embarqués et Temps Réel Dr. Manel FOURATI 62
void app_main (void *argument) {
(void) argument;
uartMutex = osMutexNew(NULL);
T_uart1 = osThreadNew(uart_Thread1, NULL, NULL);
T_uart2 = osThreadNew(uart_Thread2, NULL, NULL);}
int main (void) {
osKernelInitialize();
osThreadNew(app_main, NULL, NULL);
if (osKernelGetState() == osKernelReady) {
osKernelStart();
} while(1);}
OS Embarqués et Temps Réel Dr. Manel FOURATI 63
Ecrire un programme CMSIS RTX permettant de définir trois threads: thread1, thread2 et thread3.
L’objectif est d’utiliser le "Mutex" pour gérer l'accès à deux ressources partagées (Ressource A et
Ressource B sont deux sections critiques) entre les trois threads. Chaque thread essaiera d'accéder aux
ressources dans l’ ordre suivant:
▪ Thread 1 tente d'accéder à la Ressource A, puis à la Ressource B.
▪ Thread 2 tente d'accéder à la Ressource B, puis à la Ressource A.
▪ Thread 3 tente d'accéder à la Ressource A, puis à la Ressource B.
OS Embarqués et Temps Réel Dr. Manel FOURATI 64
__NO_RETURN void Thread1 (void *argument) {
#include <RTE_Components.h> (void) argument; for (;;) {
#include <cmsis_os2.h> osMutexAcquire(MutexA, osWaitForever);
osMutexAcquire(MutexB, osWaitForever);
#include <stm32f4xx.h>
printf("Thread 1 is running - A then B\n");
//#include <Board_LED.h>
osMutexRelease(MutexA);
#include <stdio.h> osMutexRelease(MutexB); osDelay(1000);}}
void Thread1 (void *argument); __NO_RETURN void Thread2 (void *argument) {
void Thread2 (void *argument); (void) argument; for(;;){
osMutexAcquire(MutexB, osWaitForever);
void Thread3 (void *argument);
osMutexAcquire(MutexA, osWaitForever);
static osThreadId_t id1, id2, id3; printf("Thread 2 is running - B then A\n");
static osMutexId_t MutexA, MutexB; osMutexRelease(MutexB); osMutexRelease(MutexA);
osDelay(1000);}}
OS Embarqués et Temps Réel Dr. Manel FOURATI 65
__NO_RETURN void Thread3 (void *argument) { int main (void) {
(void) argument; for(;;){ osKernelInitialize();
osMutexAcquire(MutexA, osWaitForever); MutexA = osMutexNew(NULL);
osMutexAcquire(MutexB, osWaitForever); MutexB = osMutexNew(NULL);
printf("Thread 3 is running - A then B\n"); id1 = osThreadNew(Thread1, NULL, NULL);
osMutexRelease(MutexA); id2 = osThreadNew(Thread2, NULL, NULL);
osMutexRelease(MutexB); id3 = osThreadNew(Thread3, NULL, NULL);
osDelay(1000);}} osKernelStart();
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 66
▪ La structure osMutexAttr_t spécifie les bits d’attribut pour la fonction osMutexNew avec
l’attribut attr_bits.
▪ La valeur par défaut de attr_bits est 0 qui spécifie :
o Mutex non récursif : un thread ne peut pas consommer le mutex plusieurs fois.
o La priorité d’un thread propriétaire n’est pas modifiée.
o Le mutex n’est pas automatiquement libéré : l’objet mutex doit toujours être est
automatiquement libéré lorsque le thread propriétaire est terminé.
▪ Les masques de bits suivants peuvent être utilisés pour définir les options :
o osMutexRecursive
o osMutexPrioInherit
o osMutexRobust
OS Embarqués et Temps Réel Dr. Manel FOURATI 67
▪ Le même thread peut consommer un mutex plusieurs fois sans le libérer.
▪ Chaque fois que le thread propriétaire acquiert le mutex, le nombre de verrous est incrémenté.
▪ Le mutex doit également être relâché plusieurs fois jusqu’à ce que le nombre de verrous
atteigne zéro.
▪ Lorsque le mutex atteint zéro, il est libéré et peut être acquis par d’autres threads.
OS Embarqués et Temps Réel Dr. Manel FOURATI 68
#include "cmsis_os2.h"
osMutexId_t mutex_id;
const osMutexAttr_t Thread_Mutex_attr = {
"myThreadMutex", // human readable mutex name
osMutexRecursive, // attr_bits
NULL, // memory for control block
0U // size for control block
};
void UseMutexRecursively(int count) {
osStatus_t result = osMutexAcquire(mutex_id, osWaitForever);
if (result == osOK) {
if (count < 10) {
UseMutexRecursively(count + 1);
}
osMutexRelease(mutex_id);
}}
OS Embarqués et Temps Réel Dr. Manel FOURATI 69
▪ Un mutex utilisant le protocole d’héritage de priorité transfère une priorité de threads en attente
au propriétaire actuel du mutex si la priorité du thread propriétaire est inférieure. Cela permet
de s’assurer qu’un thread de faible priorité ne bloque pas un thread de haute priorité.
▪ Dans le cas contraire, un thread de faible priorité peut contenir un mutex, mais aucun temps
d’exécution n’est accordé en raison d’un autre thread de priorité moyenne. Sans héritage de
priorité, le thread de haute priorité en attente du mutex serait bloqué par le thread de priorité
moyenne, appelé inversion de priorité.
OS Embarqués et Temps Réel Dr. Manel FOURATI 70
#include "cmsis_os2.h" void MidPrioThread(void *argument) {
osMutexId_t mutex_id; osDelay(1000U);
const osMutexAttr_t Thread_Mutex_attr = { while(1) {
"myThreadMutex", // human readable mutex // do non blocking stuff
name }
osMutexPrioInherit, // attr_bits }
NULL, // memory for control block void LowPrioThread(void *argument) {
0U // size for control block while(1) {
}; osMutexAcquire(mutex_id,osWaitForever);
void HighPrioThread(void *argument) { osDelay(5000U); // block mutex for 5s
osDelay(1000U); osMutexRelease(mutex_id);
while(1) { osDelay(5000U); // sleep for 5s
osMutexAcquire(mutex_id, osWaitForever); }
osMutexRelease(mutex_id); }
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 71
▪ Les mutex robustes sont automatiquement libérés si le thread propriétaire est terminé (par
osThreadExit ou osThreadTerminate).
▪ Les mutex non robustes ne sont pas libérés et l’utilisateur doit s’assurer de la libération
manuelle des mutex.
#include "cmsis_os2.h"
osMutexId_t mutex_id;
const osMutexAttr_t Thread_Mutex_attr = {
"myThreadMutex", // human readable mutex name
osMutexRobust, // attr_bits
NULL, // memory for control block
0U // size for control block
};
void Thread(void *argument) {
osMutexAcquire(mutex_id, osWaitForever);
osThreadExit();
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 72
#include "cmsis_os2.h"
osMutexId_t mutex_id;
const osMutexAttr_t Thread_Mutex_attr = {
"myThreadMutex", // human readable mutex name
osMutexRecursive | osMutexPrioInherit, // attr_bits
NULL, // memory for control block
0U // size for control block
};
void CreateMutex (void) {
mutex_id = osMutexNew(&Thread_Mutex_attr);
if (mutex_id != NULL) {
// Mutex object created
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 73
▪ Le sémaphore est un mécanisme de synchronisation de l’activité entre deux threads ou plus.
▪ Un sémaphore est un conteneur qui contient un certain nombre de jetons.
▪ Processus de traitement:
o Le sémaphore doit être créé et initialisé avec un nombre initial de jetons.
o Un thread en cours d’exécution, reçoi un appel RTOS pour acquérir un jeton de sémaphore.
o Si le sémaphore contient un ou plusieurs jetons, le thread continuera à s’exécuter et le nombre
de jetons dans le sémaphore sera décrémenté d’une unité.
o S’il n’y a actuellement aucun jeton dans le sémaphore, le thread sera placé dans un état
d’attente jusqu’à ce qu’un jeton soit disponible.
o À tout moment de son exécution, un thread peut ajouter un jeton au sémaphore, ce qui entraîne
une augmentation de son nombre de jetons.
OS Embarqués et Temps Réel Dr. Manel FOURATI 74
▪ Exemple: le sémaphore est initialisé avec un seul jeton.
▪ Deux threads s’exécutent et à un certain temps ils tenteront d’acquérir un jeton auprès du
sémaphore.
▪ Le premier thread acquerra le jeton du sémaphore et poursuivra l’exécution.
▪ Le deuxième thread tente d’acquérir un jeton, mais comme le sémaphore est vide, il arrêtera
l’exécution et sera placé dans un état d’attente jusqu’à ce qu’un jeton de sémaphore soit
disponible.
▪ Le premier thread peut libérer un jeton vers le sémaphore.
▪ Le deuxième thread qui est en attente acquiert le jeton et quitte l’état d’attente pour l’état prêt.
OS Embarqués et Temps Réel Dr. Manel FOURATI 75
▪ Les sémaphores sont similaires aux mutex.
▪ Un Mutex n’autorise qu’un seul thread à accéder à une ressource partagée à la fois, un sémaphore
peut être utilisé pour permettre à un nombre fixe de threads d’accéder à une ressource partagée.
OS Embarqués et Temps Réel Dr. Manel FOURATI 76
▪ Le principe est le suivant:
o Un objet sémaphore doit être initialisé au nombre maximum de jetons disponibles (paramètre
de la fonction osSemaphoreNew).
o Si un jeton sémaphore est obtenu avec osSemaphoreAcquire, le nombre de jetons
sémaphores est décrémenté.
o Si le nombre de sémaphores est égal à 0, il n’est plus possible d’obtenir de jetons de
sémaphore.
o Le thread qui tente d’obtenir le jeton de sémaphore doit attendre qu’un jeton soit libre.
o Les sémaphores sont libérés avec osSemaphoreRelease qui incrémente le nombre de
sémaphores.
OS Embarqués et Temps Réel Dr. Manel FOURATI 77
osSemaphoreId_t osSemaphoreNew(uint32_t max_count, uint32_t initial_count,
const osSemaphoreAttr_t * attr )
▪ La fonction osSemaphoreNew crée et initialise un objet sémaphore afin de gérer l’accès aux
ressources partagées qui renvoie:
o le pointeur vers l’identificateur d’objet sémaphore
o ou NULL en cas d’erreur.
▪ Le paramètre max_count spécifie le nombre maximal de jetons disponibles : nombre maximum
d’objet sémaphores pouvant être exécuter au même temps.
▪ Une valeur max_count de 1 crée un sémaphore binaire.
▪ Le paramètre initial_count définit le nombre initial de jetons disponibles.
▪ Le paramètre attr spécifie des attributs de sémaphore supplémentaires. Les attributs par défaut
seront utilisés s’ils sont définis sur NULL.
OS Embarqués et Temps Réel Dr. Manel FOURATI 78
void Thread_Semaphore (void *argument) {
#include "cmsis_os2.h" osStatus_t val;
osSemaphoreId_t sid; // semaphore id while (1) {
osThreadId_t tid; // thread id ; // Insert thread code here...
void Thread_Semaphore (void *argument); val = osSemaphoreAcquire(sid, 10U);
int Init_Semaphore (void) { switch (val) {
sid= osSemaphoreNew(2, 2, NULL); case osOK:
if (sid== NULL) {; } ;
tid= osThreadNew(Thread_Semaphore, osSemaphoreRelease(sid);
NULL, NULL); break;
if (tid== NULL) { case osErrorResource:
return(-1); break;
} case osErrorParameter:
return(0); break;
} default:
break;
}
osThreadYield();
}
}
OS Embarqués et Temps Réel Dr. Manel FOURATI 79
const char * osSemaphoreGetName(osSemaphoreId_t semaphore_id )
▪ La fonction osSemaphoreGetName renvoie:
o le pointeur vers la chaîne de nom du sémaphore identifié par le paramètre semaphore_id
o ou NULL en cas d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 80
osStatus_t osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t Timeout )
▪ La fonction osSemaphoreAcquire attend qu’un jeton de l’objet sémaphore spécifié par le
paramètre semaphore_id soit disponible.
▪ Si un jeton est disponible, la fonction retourne instantanément et décrémente le nombre de jetons.
▪ Le paramètre timeout spécifie le temps d’attente du système pour acquérir le jeton.
▪ Pendant que le système attend, le thread qui appelle cette fonction est mis dans l’état BLOQUÉ.
▪ Le paramètre timeout peut avoir les valeurs suivantes :
o 0: la fonction retourne instantanément.
o osWaitForever: la fonction attendra un temps infini jusqu’à ce que le sémaphore devient
disponible.
o Les ticks du noyau pour un délai d’attente.
OS Embarqués et Temps Réel Dr. Manel FOURATI 81
osStatus_t osSemaphoreRelease (osSemaphoreId_t semaphore_id)
▪ La fonction osSemaphoreRelease libère un jeton de l’objet sémaphore spécifié par le paramètre
semaphore_id.
▪ Les jetons ne peuvent être libérés que jusqu’au nombre maximal spécifié au moment de la
création, voir osSemaphoreNew.
▪ Les autres threads qui attendent actuellement un jeton de cet objet sémaphore seront mis dans
l’état READY.
▪ Valeurs de retour possibles osStatus_t :
o osOK : le jeton a été libéré et le nombre incrémenté.
o osErrorResource : le jeton n’a pas pu être libéré (le nombre maximal de jetons a été atteint).
o osErrorParameter : le paramètre semaphore_id est NULL ou non valide.
OS Embarqués et Temps Réel Dr. Manel FOURATI 82
uint32_t osSemaphoreGetCount (osSemaphoreId_t semaphore_id)
▪ La fonction osSemaphoreGetCount renvoie:
o Le nombre de jetons disponibles de l’objet sémaphore spécifié par le paramètre semaphore_id.
o 0 en cas d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 83
osStatus_t osSemaphoreDelete (osSemaphoreId_t semaphore_id)
▪ La fonction osSemaphoreDelete supprime un objet sémaphore spécifié par le paramètre
semaphore_id.
▪ Il libère la mémoire interne obtenue pour la manipulation des sémaphores.
▪ Après cet appel, le semaphore_id n’est plus valide et ne peut plus être utilisé.
▪ Le sémaphore peut être recréé à l’aide de la fonction osSemaphoreNew.
▪ Valeurs de retour possibles osStatus_t :
o osOK : l’objet sémaphore a été supprimé.
o osErrorParameter : le paramètre semaphore_id est NULL ou non valide.
o osErrorResource : le sémaphore est dans un état non valide.
o osErrorISR : osSemaphoreDelete ne peut pas être appelé à partir des routines de service
d’interruption.
OS Embarqués et Temps Réel Dr. Manel FOURATI 84
▪ Soit le code suivant:
static const osThreadAttr_t ThreadAttr_LED2 = { .name =
#include <RTE_Components.h>
"LED2", .priority = osPriorityNormal,};
#include <cmsis_os2.h>
__NO_RETURN void led_Thread2 (void *argument) {
#include <stm32f4xx.h>
(void) argument; for (;;) {
#include <Board_LED.h>
osSemaphoreRelease(sem1);
void led_Thread1 (void *argument);
LED_On(2);
void led_Thread2 (void *argument);
osDelay(500);
void app_main (void *argument);
osSemaphoreRelease(sem1);
static osThreadId_t T_led1, T_led2;
LED_Off(2); osDelay(500); }}
static osSemaphoreId_t sem1;
static const osSemaphoreAttr_t semAttr_SEM1 = { .name =
static const osThreadAttr_t ThreadAttr_LED1 =
"SEM1",};
{.name = "LED1", .priority =
static const osThreadAttr_t ThreadAttr_main = { .name =
osPriorityAboveNormal,};
"main", .priority = osPriorityNormal,};
__NO_RETURN void led_Thread1 (void
void app_main (void *argument) {
*argument) {(void) argument; for (;;)
(void) argument;
{osSemaphoreAcquire(sem1, osWaitForever);
sem1 = osSemaphoreNew(5, 0, &semAttr_SEM1 );
LED_On(1);
T_led1 = osThreadNew(led_Thread1, NULL, &ThreadAttr_LED1);
osSemaphoreAcquire(sem1, osWaitForever);
T_led2 = osThreadNew(led_Thread2, NULL, &ThreadAttr_LED2);}
LED_Off(1); }}
OS Embarqués et Temps Réel Dr. Manel FOURATI 85
int main (void) {LED_Initialize (); osKernelInitialize();
// Initialize
CMSIS-RTOS osThreadNew(app_main, NULL, &ThreadAttr_main);
// Create application main thread if (osKernelGetState() ==
osKernelReady) { osKernelStart();
// Start thread execution } while(1);}
Quel est le résultat affiché?
OS Embarqués et Temps Réel Dr. Manel FOURATI 86
▪ Multiplex
▪ Rendez-Vous
▪ Barrière de sémaphore
OS Embarqués et Temps Réel Dr. Manel FOURATI 87
▪ Un multiplex limite le nombre de threads qui peuvent accéder à une section critique du code.
▪ Pour permettre à plusieurs threads d’exécuter la section critique, initialisez un sémaphore au
nombre maximal de threads autorisés.
▪ Le nombre de jetons dans le sémaphore représente le nombre de threads supplémentaires qui
peuvent entrer.
▪ Si ce nombre est égal à zéro, le prochain thread qui tente d’accéder à la fonction devra attendre
que l’un des autres threads se termine et libère son jeton. Lorsque tous les threads ont quitté le
jeton, le numéro de jeton est de nouveau à n.
OS Embarqués et Temps Réel Dr. Manel FOURATI 88
Ecrire un programme CMSIS RTX permettant de définir un seul thread « multiplex_Thread ».
L’objectif est d’utiliser la section critique de deux threads au même temps en appliquant le "
sémaphore Multiplex" pour la gestion d’une ressource partagée.
Le programme affiche le résultat suivant:
o LED 0 et LED1 s’exécute en clignotement simultané avec LED2 et LED3.
OS Embarqués et Temps Réel Dr. Manel FOURATI 89
#include <RTE_Components.h>
#include <cmsis_os2.h>
#include <stm32f4xx.h> int main (void) { osKernelInitialize ();
#include <Board_LED.h> LED_Initialize();
void multiplex_Thread (void *argument); semMultiplex = osSemaphoreNew(4, 2, &semAttr_MULTIPLEX );
static osSemaphoreId_t semMultiplex;
static osThreadId_t T_mux1, T_mux2, T_mux3, T_mux1 = osThreadNew(multiplex_Thread,(void *)0,NULL);
T_mux4; T_mux2 = osThreadNew(multiplex_Thread,(void *)1,NULL);
T_mux3 = osThreadNew(multiplex_Thread,(void*)2,NULL);
__NO_RETURN void multiplex_Thread (void T_mux4 = osThreadNew(multiplex_Thread,(void*)3,NULL);
*argument) { osKernelStart (); // start thread execution }
for (;;) {
osSemaphoreAcquire(semMultiplex,osWaitForever);
LED_On((uint32_t)argument);
osDelay(100);
LED_Off((uint32_t)argument);
osDelay(100);
osSemaphoreRelease(semMultiplex);}}
OS Embarqués et Temps Réel Dr. Manel FOURATI 90
▪ Une forme plus généralisée de signalisation sémaphore est le rendez-vous.
▪ Un rendez-vous permet de s’assurer que deux threads atteignent un certain point d’exécution. Ni
l’un ni l’autre ne peuvent continuer tant qu’ils n’ont pas atteint le point de rendez-vous.
OS Embarqués et Temps Réel Dr. Manel FOURATI 91
osSemaphoreId_t arrived1, arrived2;
static const osSemaphoreAttr_t semAttr_Arrived1= {.name = "Arr1",};
static const osSemaphoreAttr_t semAttr_Arrived2 ={.name = "Arr2",};
void thread1(void) {
arrived1 = osSemaphoreNew(2, 0,&semAttr_Arrived1);
arrived2 = osSemaphoreNew(2, 0,&semAttr_Arrived2);
while (1) {
FuncA1();
osSemaphoreRelease(arrived1); ▪ FuncA2() ne peux s’exécuter que
osSemaphoreAcquire(arrived2, osWaitForever);
FuncA2();
lorsque FuncB1() termine son
} exécution
}
void thread2(void) { ▪ FuncB2() ne peux s’exécuter que
while (1) {
FuncB1(); lorsque FuncA1() termine son
os_sem_Release(arrived2);
os_sem_Acquire(arrived1, osWaitForever); exécution
FuncB2();
}}
OS Embarqués et Temps Réel Dr. Manel FOURATI 92
#include <RTE_Components.h>
static const osThreadAttr_t ThreadAttr_LED2 = {.name = "LED2",};
#include <cmsis_os2.h>
__NO_RETURN void led_Thread2 (void *argument) {(void)
#include <stm32f4xx.h>
argument; for (;;) {
#include <Board_LED.h>
LED_Off(2); osDelay(500);
void led_Thread1 (void *argument);
osSemaphoreRelease(semArrived1);
void led_Thread2 (void *argument);
osSemaphoreAcquire(semArrived2,osWaitForever);
void app_main (void *argument);
LED_On(2); osDelay(100);
static osThreadId_t T_led_Thread1, T_led_Thread2;
}}
static osSemaphoreId_t semArrived1,semArrived2;
static const osSemaphoreAttr_t semAttr_SEM1 = { .name =
static const osThreadAttr_t ThreadAttr_LED1 = {.name
"SEM1",};static const osSemaphoreAttr_t semAttr_SEM2 = {
= "LED1",};
.name = "SEM2",};
__NO_RETURN void led_Thread1 (void *argument) {
static const osThreadAttr_t ThreadAttr_main = { .name = "main",};
(void) argument; for (;;){
void app_main (void *argument) {(void) argument; semArrived1 =
LED_Off(1); osDelay(100);
osSemaphoreNew(1,0,&semAttr_SEM1);
osSemaphoreRelease(semArrived2);
semArrived2 = osSemaphoreNew(1,0,&semAttr_SEM2);
osSemaphoreAcquire(semArrived1,osWaitForever);
T_led_Thread1 = osThreadNew(led_Thread1, NULL,
LED_On(1); osDelay(500);
&ThreadAttr_LED1); T_led_Thread2 =
}}
osThreadNew(led_Thread2, NULL, &ThreadAttr_LED2);}
OS Embarqués et Temps Réel Dr. Manel FOURATI 93
int main (void) { LED_Initialize (); osKernelInitialize();
osThreadNew(app_main, NULL, &ThreadAttr_main
if (osKernelGetState() == osKernelReady) { osKernelStart();
} while(1);}}
OS Embarqués et Temps Réel Dr. Manel FOURATI 94
Résultat du code:
o LED 1 et LED2 s’exécute en clignotant avec différence au délai d’attente (100 et 500).
OS Embarqués et Temps Réel Dr. Manel FOURATI 95
▪ Bien qu’un rendez-vous soit très utile pour synchroniser l’exécution du code, il ne fonctionne que
pour deux fonctions.
▪ Une barrière est une forme plus généralisée de rendez-vous qui permet de synchroniser plusieurs
threads.
OS Embarqués et Temps Réel Dr. Manel FOURATI 96
#include <RTE_Components.h>
#include <cmsis_os2.h>
#include <stm32f4xx.h>
#include <Board_LED.h>
void app_main (void *argument);void threadBaseCode (void *argument);
static osThreadId_t tsk_0,tsk_1,tsk_2,tsk_3,tsk_4;
static osSemaphoreId_t Turnstile_In,Turnstile_Out,Mutex;
static const osSemaphoreAttr_t semAttr_SEM_In = {.name =
"Turnstile_In",};
static const osSemaphoreAttr_t semAttr_SEM_Out = {.name =
"Turnstile_Out",};
static const osSemaphoreAttr_t semAttr_Mutex = {.name = "Mutex",};
static const osThreadAttr_t ThreadAttr_main = { .name = "main",};
static const osThreadAttr_t ThreadAttr_tsk_0 = { .name = "Task_0",};
static const osThreadAttr_t ThreadAttr_tsk_1 = { .name = "Task_1",};
static const osThreadAttr_t ThreadAttr_tsk_2 = { .name = "Task_2",};
static const osThreadAttr_t ThreadAttr_tsk_3 = { .name = "Task_3",};
static const osThreadAttr_t ThreadAttr_tsk_4 = { .name = "Task_4",};
static unsigned int count = 0;
OS Embarqués et Temps Réel Dr. Manel FOURATI 97
__NO_RETURN void threadBaseCode (void *argument) osSemaphoreAcquire(Turnstile_In,osWaitForever);
{uint32_t LED_data; osSemaphoreRelease(Turnstile_In);
LED_data = (uint32_t) argument; //--Entry Turnstile- LED_On(LED_data);
osDelay(10*LED_data); //Critical Section
while(1) osDelay(200);
{//-----------------------------------Entry Turnstile----------------- LED_Off(LED_data);
osSemaphoreAcquire(Mutex,osWaitForever); //Allow osDelay(200);
one task at a time to access the first turnstile //----------------------------------Exit Turnstile------------------
count = count+1; // Increment count osSemaphoreAcquire(Mutex,osWaitForever);
if( count == 5) count = count - 1;
//When last section of code reaches this point run his code if(count ==0){
{osSemaphoreAcquire (Turnstile_Out,osWaitForever); osSemaphoreAcquire(Turnstile_In,osWaitForever);
//Lock the second turnstile osSemaphoreRelease(Turnstile_Out);
osSemaphoreRelease(Turnstile_In); }
//Unlock the first turnstile osSemaphoreRelease(Mutex);
} osSemaphoreAcquire(Turnstile_Out,osWaitForever);
osSemaphoreRelease(Mutex); //Turnstile Gate
osSemaphoreRelease(Turnstile_Out);//-----------------------
----------Exit Turnstile-------------------------------------
}}
OS Embarqués et Temps Réel Dr. Manel FOURATI 98
/*---------------------------------------------------------------------------- *
Main: Initialize and start RTX Kernel *-------------------------------------------
--------------------------------*/void app_main (void *argument) {
(void) argument;
Turnstile_In = osSemaphoreNew(5, 0,&semAttr_SEM_In);
Turnstile_Out = osSemaphoreNew(5, 1,&semAttr_SEM_Out);
Mutex = osSemaphoreNew(1, 1,&semAttr_Mutex);
tsk_0 = osThreadNew(threadBaseCode,(uint32_t *)0,&ThreadAttr_tsk_0);
tsk_1 = osThreadNew(threadBaseCode,(uint32_t *)1,&ThreadAttr_tsk_1);
tsk_2 = osThreadNew(threadBaseCode,(uint32_t *)2,&ThreadAttr_tsk_2);
tsk_3 = osThreadNew(threadBaseCode,(uint32_t *)3,&ThreadAttr_tsk_3);
tsk_4 = osThreadNew(threadBaseCode,(uint32_t *)4,&ThreadAttr_tsk_4);
}
int main (void) { SystemCoreClockUpdate(); LED_Initialize ();
osKernelInitialize();
osThreadNew(app_main, NULL, &ThreadAttr_main); // Create
application main thread if (osKernelGetState() == osKernelReady) {
osKernelStart();
} while(1);}
OS Embarqués et Temps Réel Dr. Manel FOURATI 99
▪ La communication entre les threads se base aussi sur la transmission de messages.
▪ Un thread envoie des données explicitement, tandis qu’un autre thread les reçoit.
▪ Dans CMSIS-RTOS, ce mécanisme est appelé file d’attente de messages (message
queue).
▪ Les données sont transmises d’un thread à un autre dans le cadre d’une opération de
type FIFO.
OS Embarqués et Temps Réel Dr. Manel FOURATI 100
▪ La file d’attente de messages permet de : contrôler, envoyer, recevoir ou attendre des
messages.
▪ Les données à transmettre peuvent être de type entier ou pointeur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 101
osMessageQueueId_t osMessageQueueNew(uint32_t msg_count, uint32_t msg_size,
const osMessageQueueAttr_t * attr )
▪ La fonction osMessageQueueNew permet de créer et initialiser un objet de file d’attente
de messages.
▪ La fonction renvoie un identificateur d’objet de file d’attente de messages ou NULL en
cas d’erreur.
▪ msg_count: Nombre maximal de messages dans la file d’attente.
▪ msg_size: Taille maximale des messages en octets.
▪ Attr:attributs de file d’attente de messages ; NULL : valeurs par défaut.
OS Embarqués et Temps Réel Dr. Manel FOURATI 102
const char * osMessageQueueGetName (osMessageQueueId_t mq_id)
▪ La fonction osMessageQueueGetName renvoie le pointeur vers la chaîne de nom de
la file d’attente de messages identifiée par le paramètre mq_id ou NULL en cas
d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 103
osStatus_t osMessageQueuePut (osMessageQueueId_t mq_id, const void * msg_ptr,
uint8_t msg_prio, uint32_t Timeout)
▪ La fonction bloquante osMessageQueuePut ajoute le message pointé par msg_ptr
dans la file d’attente de messages spécifiée par le paramètre mq_id.
▪ Le paramètre msg_prio est utilisé pour trier les messages en fonction de leur priorité
(des nombres plus élevés indiquent une priorité plus élevée) lors de l’insertion.
▪ Le paramètre timeout spécifie le temps d’attente du système pour placer le message
dans la file d’attente.
OS Embarqués et Temps Réel Dr. Manel FOURATI 104
▪ Pendant que le système attend, le thread qui appelle cette fonction est mis dans l’état
BLOQUÉ.
▪ Le paramètre timeout peut avoir les valeurs suivantes :
o 0: la fonction retourne instantanément.
o osWaitForever: la fonction attendra un temps infini jusqu’à ce que le message soit
livré.
o Ticks: délai d’attente.
OS Embarqués et Temps Réel Dr. Manel FOURATI 105
▪ Valeurs de retour possibles osStatus_t :
o osOK : le message a été placé dans la file d’attente.
o osErrorTimeout : le message n’a pas pu être mis dans la file d’attente dans le
temps imparti.
o osErrorResource : pas assez d’espace dans la file d’attente.
o osErrorParameter : le paramètre mq_id est NULL ou non valide, délai d’expiration
différent de zéro spécifié dans un ISR.
OS Embarqués et Temps Réel Dr. Manel FOURATI 106
osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id,void * msg_ptr,
uint8_t * msg_prio, uint32_t timeout )
▪ La fonction osMessageQueueGet récupère un message de la file d’attente de
messages spécifiée par le paramètre mq_id et l’enregistre dans la mémoire tampon
pointée par le paramètre msg_ptr.
▪ La priorité du message est stockée dans le paramètre msg_prio.
▪ Le paramètre timeout spécifie le temps d’attente du système pour récupérer le message
dans la file d’attente. Pendant que le système attend, le thread qui appelle cette
fonction est mis dans l’état BLOQUÉ.
OS Embarqués et Temps Réel Dr. Manel FOURATI 107
▪ mq_id: ID de file d’attente de messages obtenu par osMessageQueueNew.
▪ msg_ptr: pointeur vers la mémoire tampon pour que le message soit obtenu à partir
d’une file d’attente.
▪ msg_prio: pointeur vers la mémoire tampon pour la priorité du message ou la valeur
NULL.
▪ Timeout: Valeur du délai d’attente ou 0 en cas d’absence de délai d’attente.
OS Embarqués et Temps Réel Dr. Manel FOURATI 108
uint32_t osMessageQueueGetCapacity (osMessageQueueId_t mq_id )
▪ La fonction osMessageQueueGetCapacity renvoie le nombre maximal de messages
dans l’objet de file d’attente de messages spécifié par le paramètre mq_id ou 0 en cas
d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 109
uint32_t osMessageQueueGetMsgSize (osMessageQueueId_t mq_id )
▪ La fonction osMessageQueueGetMsgSize renvoie la taille maximale du message en
octets pour l’objet de file d’attente de messages spécifié par le paramètre mq_id ou 0
en cas d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 110
uint32_t osMessageQueueGetCount(osMessageQueueId_t mq_id)
▪ La fonction osMessageQueueGetCount renvoie le nombre de messages en file
d’attente dans l’objet de file d’attente de messages spécifié par le paramètre mq_id ou
0 en cas d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 111
uint32_t osMessageQueueGetSpace(osMessageQueueId_t mq_id)
▪ La fonction osMessageQueueGetSpace renvoie le nombre d’emplacements
disponibles pour les messages dans l’objet file d’attente de messages spécifié par le
paramètre mq_id ou 0 en cas d’erreur.
OS Embarqués et Temps Réel Dr. Manel FOURATI 112
osStatus_t osMessageQueueReset(osMessageQueueId_t mq_id)
▪ La fonction osMessageQueueReset réinitialise la file d’attente de messages spécifiée
par le paramètre mq_id.
▪ Valeurs de retour possibles osStatus_t :
o osOK : la file d’attente des messages a été réinitialisée.
o osErrorParameter : le paramètre mq_id est NULL ou non valide.
o osErrorResource : la file d’attente de messages est dans un état non valide.
o osErrorISR : osMessageQueueReset ne peut pas être appelé à partir des routines de
service d’interruption.
OS Embarqués et Temps Réel Dr. Manel FOURATI 113
osStatus_t osMessageQueueDelete(osMessageQueueId_t mq_id )
▪ La fonction osMessageQueueDelete supprime un objet de file d’attente de messages
spécifié par le paramètre mq_id.
▪ Il libère la mémoire interne obtenue pour la gestion des files d’attente de messages.
▪ La file d’attente de messages peut être créée à nouveau à l’aide de la fonction
osMessageQueueNew.
OS Embarqués et Temps Réel Dr. Manel FOURATI 114
▪ Valeurs de retour possibles osStatus_t :
o osOK : l’objet de la file d’attente des messages a été supprimé.
o osErrorParameter : le paramètre mq_id est NULL ou non valide.
o osErrorResource : la file d’attente de messages est dans un état non valide.
o osErrorISR : osMessageQueueDelete ne peut pas être appelé à partir des routines
de service d’interruption.
OS Embarqués et Temps Réel Dr. Manel FOURATI 115
#include <RTE_Components.h>
#include <cmsis_os2.h> static const osThreadAttr_t ThreadAttr_LED1 = {.name =
#include <stm32f4xx.h> "LED1",};
#include <Board_LED.h> __NO_RETURN void led_Thread1 (void *argument)
void led_Thread1 (void *argument); {(void) argument; message_t dataOut;
uint8_t priority;
void led_Thread2 (void *argument); for (;;)
void app_main (void *argument); {osMessageQueueGet(Q_LED,&dataOut,&priority,osWaitForever);
static osThreadId_t T_led_Thread1,T_led_Thread2; LED_On([Link]);
static osMessageQueueId_t Q_LED; osDelay(1000);
typedef struct { uint32_t duration; uint32_t ledNumber; LED_Off([Link]);}}
uint8_t priority;}message_t;
OS Embarqués et Temps Réel Dr. Manel FOURATI 116
static const osThreadAttr_t ThreadAttr_LED2 = {
.name = "LED2",};
__NO_RETURN void led_Thread2 (void osMessageQueuePut(Q_LED,&dataIn,osPriorityAboveNormal,osWaitFor
*argument) {(void) argument; ever);
osDelay(1000);
message_t dataIn; [Link] =1;
[Link] = 1000; osMessageQueuePut(Q_LED,&dataIn,osPriorityNormal,osWaitForever);
osDelay(1000);
[Link] =3; [Link] =2;
[Link] = osPriorityNormal; osMessageQueuePut(Q_LED,&dataIn,osPriorityNormal,osWaitForever);
osDelay(1000);
for (;;) [Link] =1;
{osMessageQueuePut(Q_LED,&dataIn,osPriority osDelay(1000); }}
Normal,osWaitForever); osDelay(1000);
[Link] =0;
OS Embarqués et Temps Réel Dr. Manel FOURATI 117
static const osMessageQueueAttr_t queueAttr_Q_LED = {
.name = "LED_QUEUE",};
static const osThreadAttr_t ThreadAttr_main = { .name = "main",};
void app_main (void *argument) {
(void) argument;
LED_Initialize ();
Q_LED = osMessageQueueNew(16,sizeof(message_t),&queueAttr_Q_LED );
T_led_Thread1 = osThreadNew(led_Thread1, NULL, &ThreadAttr_LED1);
T_led_Thread2 = osThreadNew(led_Thread2, NULL, &ThreadAttr_LED2);}
int main (void) { SystemCoreClockUpdate(); LED_Initialize (); osKernelInitialize();
osThreadNew(app_main, NULL, &ThreadAttr_main);
if (osKernelGetState() == osKernelReady) { osKernelStart(); } while(1);}
OS Embarqués et Temps Réel Dr. Manel FOURATI 118