[STM32] Real Time Operating System (RTOS)

[STM32] Real Time Operating System (RTOS)

Le microcontrôleur STM32F411 fait tourner le Système d’Exploitation Temps Réel FreeRTOS.

Définition

RTOS_tree.svg

Un système d’exploitation (OS) permet entre autres :

  • Le découpage en tâches d’une application
  • L’allocation d’un temps processeur à ces différentes tâches.

Le Multitâche peut être :

  • PREEMPTIF : chaque tâche reçoit régulièrement un intervalle de temps processeur en fonction d’un algorithme d’ORDONNANCEMENT
  • COOPERATIF : On écrit dans le programme d’une tâche à quel moment il faut donner la main à une autre tâche.

Un Système d’exploitation TEMPS REEL (RTOS) n’est pas plus rapide qu’un OS généraliste (plutôt le contraire), mais son utilisation garantit l’exécution d’une tâche ‘à temps’. Cela convient particulièrement à tout système DETERMINISTE (tout doit être prévu dans le code) pour lequel on veut respecter une certaine période d’échantillonage.
A l’inverse un OS généraliste réarrangera l’exécution d’une application en fonction des ressources pour apporter le maximum de confort à l’utilisateur.

Nous utilisons pour notre application FREERTOS avec un multitâche coopératif.
Cet RTOS répond à la norme POSIX, autrement dit les primitives utilisées se retrouvent dans tout RTOS y compris le leader du marché Windriver VxWorks.

Le choix d’un RTOS peut être soumis à l’exigence d’une certification du genre aéronoautique (système CRITIQUE)


Illustration d’un multitâche coopératif

Toute tâche créée devient READY.
La plus prioritaire est alors exécutée par le processeur et devient RUNNING.
Ci cette dernière est interrompue, elle devient soit BLOCKED (attente d’un objet synchronisant) ou SUSPENDED (mise en état d’attente par temporisation ou sleeping).

Le premier exemple ci dessous avec les tâches A et B illustre l’utilisation de la primitive vTaskDelay().

  • A la création des tâches, A et B sont READY, A plus prioritaire devient RUNNING.
  • L’appel de vTaskDelay la met dans un état SUSPENDED, au profit de B qui s’exécute alors.
  • Lorsque A se réveille, elle redevient RUNNING au détriment de B. Il y a alors PREEMPTION.

Le problème dans ce premier exemple réside dans le fait qu’on ne saura pas combien de tours de boucle sont réalisés dans B.

Pour préciser à quel endroit du code on veut interrompre ou relancer une tâche on utilise des SEMAPHORES (exemple Tâches C et D).

Cliquer sur la figure ci-dessous:

L’exemple précédent ne permet pas de garantir une certaine période d’échantillonage ; tout s’exécute au rythme d’exécution des instructions (donc très rapidement au regard d’une période d’échantillonage pour un contrôle moteur).

L’exemple ci-dessous fait intervenir un deuxième objet synchronisant : la boîte à messages (QUEUE).
On fait l’hypothèse que le temps d’exécution des instructions est négligeable au regard de la période d’échantillonage.

Il s’agit d’un sémaphore amélioré permettant en plus de transmettre une information d’une tâche à une autre, sans s’encombrer de l’utilisation d’une variable globale avec accès nécessairement contrôlé par un Mutex.

  • E et F sont créées.
  • E plus prioritaire s’exécute puis est mise en sommeil au profit de F.
  • F se met en attente d’un message.
  • E se réveille. Au moment où elle à besoin de F, elle poste un message dans la boîte.
  • F redevient READY mais ne peut pas s’exécuter tant que E est running.
  • La prise d’un sémaphore vide met E en état d’attente d’un objet synchronisant.
  • F s’exécute alors jusqu’à ce qu’elle décide de libérer E en postant un jeton.
  • E libérée préempte F.

Ce mécanisme est répété toutes les périodes spécifiées dans vTaskDelay.

Cliquer sur la figure ci-dessous:

L’ensemble des primitives proposées par FREERTOS sont décrites à l’adresse suivante : http://www.freertos.org/
A noter que la compréhension des exemples précédents est essentielle pour la suite.


Vérification de la période d’échantillonnage

L’ajout de tâches dans notre programme ne doit pas remettre en cause la mise à jour du rapport cyclique des moteurs tous les Te.

Afin de vérifier cette période, on peut tout simplement mettre à 1 puis à 0 une sortie GPIO en début et en fin de tâche :

main.c
static void task_E( void *pvParameters )
{
	for (;;)
	{
	    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, 1);
		...
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, 0);
		vTaskDelay(SAMPLING_PERIOD_ms);
	}
}

static void task_F(void *pvParameters)
{
	for (;;)
	{
		...	
	}
}

La Broche PB4 est accessible sur le connecteur arduino :

PB4.jpg


Utilisation d’un Timer

On peut également utiliser un périphérique timer pour imposer une période d’échantillonage ( cf application Commande Vectorielle PMSM )

Attention, la gestion des jetons de semaphores depuis une routine d’interruption nécessite des primitives spécifiques :

main.c
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	static BaseType_t xHigherPriorityTaskWoken;

	if (htim->Instance==TIM3)	// Fast Task
	{
		xSemaphoreGiveFromISR( xSemaphore_Fast,&xHigherPriorityTaskWoken );
		portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
	}
}