Vous êtes sur la page 1sur 11

Université de Cergy-Pontoise

TP de prise en main de uC/OS-II Le noyau temps réel

Informatique Embarquée

1. Introduction

a. Le noyau temps réel uC/OS-II

uC/OS (ou microC/OS) est un système d’exploitation temps réel multi-tâches préemptif. Développé par le Canadien Jean J. Labrosse, uC/OS est un exécutif temps réel destiné à des environnements de très petite taille construits autour de Microcontrôleurs. Il est maintenant disponible sur un grand nombre de processeurs et peut intégrer des protocoles standards comme TCP/IP (µC/IP) pour assurer une connectivité IP sur une liaison série par PPP. Il est utilisable gratuitement pour l'enseignement. Il est aujourd’hui développé et maintenu par la société Micrium :

http://www.ucos-ii.com/ uC/OS est développé en C, sauf en ce qui concerne les portions de code qui sont cible- dépendant (portage), comme l’implantation des opérations de changement de contexte qui sont écrites en assembleur. uC/OS fait partie des nombreux systèmes d’exploitation temps réel aujourd’hui disponibles sur le marché : Adeos, ChorusOS, eCos, ITRON, LynxOS, MicroC/OS-II, Nucleus, OS-9, OSE, OSEK/VDX, pSOS, QNX, RSX-11, RT-11, RTOS-UH, SCIOPTA, VRTX, VxWorks, Windows CE, RTLinux, RTAI

b. Objectif de la séance

i.

Objectif

L’objectif de la séance de TP est la prise en main des commandes de base (API 1 ) d’un RTOS et de comprendre le principe fondamental de fonctionnement de ce type de système. uCOS étant un exemple représentatif du fonctionnement d’un RTOS, le travail réalisé sous cet environnement serait facilement instanciable avec un autre système d’exploitation. Cette introduction au développement d’applications embarquées temps réel commence par une utilisation de l’OS sur PC mais sera ensuite suivi d’une utilisation sur une carte de développement embarquée. La compréhension des mécanismes de base est donc primordiale pour la suite des TPs. A ce titre, vous devrez à l’issue des séances de TP réaliser une soutenance sur les manipulations, développements et tests que vous aurez réalisés.

ii.

Soutenance

10 minutes de soutenance seront allouées par personne plus 5 minutes de questions. Lors de cette soutenance, il vous sera demandé une ou deux démonstrations sur les 7 exercices réalisés. Vous devrez pour chacune expliquer le code de l’exercice. Des

1 Application Protocole Interface

Université de Cergy-Pontoise

questions sont pour cela insérées dans ce sujet pour vous guider dans la compréhension du système uC/OS. Cette soutenance devra rendre compte de la particularité du développement dans un cadre temps réel d’une part et embarqué d’autre part. Par ailleurs en tant que développeur d’applications temps réel au sein de l’équipe de développement pédagogique du Département Sciences Informatiques, vous pourrez si nécessaire rendre compte des problèmes techniques rencontrés, des limites et contraintes de l’environnement de développement actuel, et des éventuels Buggs rencontrés à faire corriger par l’équipe de développement des Outils d’exploitation.

c. Environnement de développement

Vous allez au cours de ces séances prendre connaissance de 2 environnements de développement selon que l’application que l’on cherche à implanter sera destinée à une exécution sur PC ou à une exécution embarquée. L’environnement de développement sur PC est directement lié au langage de programmation utilisé pour développer des applications sous uCOS : le langage C. Vous pourrez donc utiliser pour le développement des programmes l’éditeur de votre choix. Le compilateur C est quand à lui soumis à quelques contraintes. En effet, les librairies de fonctions uC/OS-II disponibles à l’Université ont été développées et compilées avec BorlandC/C++ version 4.51 pour Windows. Ce compilateur payant n’est pas disponible à l’Université. Aussi l’OS a-t-il été porté pour une version antérieure du compilateur Borland, la version TCPP1.01. L’environnement minimal de développement est l’utilisation d’un logiciel d’édition de texte pour la programmation et le lancement des commandes de compilation par commandes DOS. Vous pouvez selon les logiciels de votre PC utiliser un environnement de développement paramétré pour faire appel aux commandes du compilateur (make, cc, asm…).

Le compilateur est installé dans le répértoire c:\TOOLS\TCPP101\

Si ce n’est pas le cas, décompresser l’archive présente sur

http://www-etis.ensea.fr/Members/bmiramond/Cours/M1/UEF5.html

à cet emplacement.

Vos répertoires de travail seront situés dans c:\SOFTWARE\UCOS-II\. Ce répertoire sera désigné dans la suite par $WORK_DIR.

d. Les primitives utiles de la librairie uC/OS-II

Vous trouverez en annexe à la fin de ce document les fonctions C principales de la librairie ucos_ii (API).

Note : En cours de TP, vous pouvez demander un livre uC/OS-II disponible dans les armoires de la salle pour le détail de certains appels ou paramètres d’appel à l’API.

2. Exercice 1 : Concurrence de tâches

a. Un modèle de code

Vous trouverez dans le répertoire $WORK_DIR\EX1_X86L\TCPP101\TEST les fichiers nécessaires à la compilation du premier exercice : Test.mak (Makefile), Maketest.bat (script).

Université de Cergy-Pontoise

Pour lancer la compilation, vous devez lancer le script Maketest.bat. L’affichage qui apparaît n’est pas complet, ce sera à vous de compléter le code et de comprendre sa fonctionnalité.

Vous trouverez pour cela dans l’annexe B le code complet de l’application temps réel simple développée avec les fonctions de la librairie ucos_ii. L’objectif de ce premier exercice est tout d’abord de comprendre la structure d’un programme sous cet environnement, puis d’analyser l’utilité des fonctions uCOS. Pour satisfaire au premier objectif, la première étape de ce TP est de recopier le code fourni en annexe dans le fichier test.c présent dans

$WORK_DIR\EX1_X86L\TCPP101\SOURCE

Au cours de l’écriture du code, vous placerez des commentaires indiquant la fonctionnalité des fonctions de l’OS en vous aidant de l’annexe A. D’autres fichiers sont déjà présents dans ce répertoire comme Include.h et Os_cfg.h.

b. Analyse du code

Après avoir correctement compilé le code et réaliser son exécution, vous allez maintenant procéder aux modifications suivantes :

Enlever l’appel à la fonction PCDosSaveReturn() dans la fonction main. Recompiler et exécuter. Que se passe-t-il ? Expliquer pourquoi. En quoi l’utilisation de uCOS sur PC est-elle différente d’une utilisation sur plateforme embarquée ?

3. Exercice 2 : Mesure de taille de pile d’exécution

a. Compréhension de code

Vous allez maintenant dans le répertoire

$WORK_DIR\EX2_x86l\TCPP101\

Vous y retrouvez la même organisation que dans l’exemple précédent. Compiler le code et exécuter l’application. Observer ce qui se passe. Identifier le problème. Vous allez maintenant entrer dans le code (répertoire SOURCE) et écrire la fonction d’affichage TaskStartDispInit() de manière à ce qu’un utilisateur comprenne les données brutes affichées à l’écran. Vous devez pour cela à partir du code déjà présent comprendre la nature des informations affichées et vous inspirer du code de l’exercice 1 pour apporter des indications à l’affichage. Cette application est composée de 9 tâches, que font chacune de ces tâches ?

b. Analyse de comportement

Cet exercice fait appel à la fonction OSTaskStkChk() qui réalise des vérifications et des mesures de taille de la pile d’exécution. L’analyse de pile d’exécution est primordiale en informatique embarqué et en temps réel lorsque l’on ne connaît pas de manière statique et déterministe la place mémoire utilisée par chaque tâche. Dans ce cas, une solution est d’allouer plus de place que nécessaire et de laisser uC/OS- II mesurer la pile réellement utilisée. Pour cela l’application doit être lancée

Université de Cergy-Pontoise

suffisamment longtemps pour être représentative d’une exécution en environnement réel. Votre mesure finale doit pouvoir s’adapter à des variations non prévues de l’environnement. Une marge de 15 à 20 % est donc nécessaire. Dans les applications critiques, une marge de 100% est indispensable. Après mesure, adapter la taille de pile de chaque tâche et vérifier le bon comportement du système. Que se passe-t-il si la taille de pile allouée est insuffisante ? La fonction OSTaskStckChk() rempli l’espace réservé pour la pile de valeurs nulles à

la création de chaque tâche.

La vérification et la mesure de la taille de pile commence au bas de la pile (pointeur Bottom Of Stack : BOS). En avançant dans la pile le pointeur est incrémenté jusqu’à trouver une entrée non nulle. Le compteur donne le nombre d’emplacements utilisés. Cette vérification prends du temps et n’est donc utilisée qu’en cours de développement

et d’analyse de code. Une fois implantée sur cible, ces vérifications sont supprimées.

A vous de mesurer le coût en temps d’exécution de cette vérification en utilisant

successivement les fonctions PC_ElapsedStart() et PC_ElapsedStop() (cf. Annexe).

c. Communications entre tâches

uCOS permet évidemment de faire communiquer des tâches entre elles, par exemple en envoyant des messages par l’intermédiaire d’une boîte aux lettres (mailbox). Nous allons ici faire communiquer les tâches 4 et 5 : T4 envoie un message à T5 et T5 répondra à T4 en lui envoyant un Ack(nowledge). Pour cela nous avons besoin de créer 2 mailboxes qui permettent de contenir un (et un seul) pointeur sur une donnée. Cette donnée est spécifique à l’application et doit être définie en commun par l’expéditeur et le destinataire. Les étapes pour faire communiquer T4 et T5 sont simples :

Déclarer 2 mailboxes en variables globales. Ce sont des pointeurs sur des OS_EVENT

Créer les 2 mailboxes par la fonction OSMboxCreate (void *) qui retourne un pointeur sur les structures mailbox

Dans les fonctions T4 et T5 utiliser les fonctions d’envoie OSMboxPost() et d’attente OSMboxPend() pour que T4 envoie un caract ère qui change à chaque Ack reçu de ‘A’ à ‘Z’ de mani ère circulaire. T5 affichera ce caractère.

4. Exercice 3 : Analyse de temps d’exécution

a. Mesure de temps d’exécution

Les sources de l’exercice 3 sont dans $WORK_DIR\EX3_x86L\TCPP101\SOURCE. L’objectif de cet exercice est inverse à celui de l’exercice précédent. La fonction d’affichage TaskStartDispInit() est écrite. Elle indique différentes

mesures pour les tâches de l’application. Combien de tâches composent l’application. Quelles sont les mesures que doit afficher l’exécution. Votre objectif est d’utiliser les commandes uCOS pour donner ces mesures à l’affichage de données actuellement constantes.

Université de Cergy-Pontoise

uCOS est un OS ouvert au sens ou le code source de l’OS est fourni et modifiable par les utilisateurs. Nous allons ici utiliser cette propriété pour réaliser nos mesures en ajoutant cette fonctionnalité par l’intermédiaire des fonctions appelées Hook. 9 fonctions Hook existent. Nous aurons pour notre cas uniquement besoin de modifier la fonction OSTaskSwHook. Celle-ci est appellé par l’OS au moment de chaque changement de contexte. Au moment de son appel, le pointeur global OSTCBCur pointe sur le TCB de la tâche en cours d’exécution, alors que OSTCBHighRdy pointe sur le TCB de la nouvelle tâche plus prioritaire. Nous n’utiliserons ici que le premier pointeur. Le champ OSTCBExtPtr de la structure TCB contient la structure de donnée qui lui est passée en paramètre à la création par la fonction OSTaskCreateExt(). C’est cette structure de donnée que nous allons utiliser pour mémoriser les informations dynamiques sur chaque tâche. Le principe est donc le suivant

Déclaration d’une structure de données TASK_USER_DATA mémorisant les informations mesurées (déjà écrite en tête de fichier). Mettre des commentaires pour chaque champ de la structure.

Passage de cette structure de données en 8 e paramètre de la fonction OSTaskCreateExt() jusqu’à présent laissé à un pointeur vide.

Mise à jour des champs de cette structure par la fonction OSTaskSwHook à chaque changement de contexte.

Le temps d’exécution de chaque tâche est obtenu en lancant la fonction PC_ElapsedStart() à la fin de la fonction OSTaskSwHook() et en récupérant un INT16U en valeur de retour de la fonction PC_ElapsedStop(). Réaliser ces modifications. Quelle est la tâche la plus longue. La priorité des tâches apparente est-elle conforme à celle donnée à leur création ? Que fait la tâche T1 par rapport aux autres tâches ? Pourquoi utilise-t-elle un autre mécanisme de communication que celui de la boîte aux lettres utilisé précédemment ?

5. Exercice 4 : Emulation d’un jeu d’instructions à virgule flottante

a. Architecture cible

Rechercher sur le web l’architecture du processeur de votre machine. Donner le schéma en bloque du processeur. Dispose-t-elle d’une unité de calcul en virgule flottante ? Comment réalise-t-on des calculs en virgule flottante dans le cas ou le processeur n’en dispose pas.

Université de Cergy-Pontoise

6. Conclusion

Déduiser de cette expérience un patron (modèle) général d’écriture d’applications sous uCOS. Ce patron doit être indépendant de l’application visée et doit ensuite pouvoir être rempli de manière à répondre aux besoins de l’application.

Lancer à nouveau un de vos exécutables. Au cours de son exécution et sans l’interrompre, lancer également le gestionnaire de tâches de Windows. Quelle est l’utilisation du processeur ? Quelle l’utilisation CPU occupé par le code uCOS ? Que déduisez-vous de ces mesures du fonctionnement de uC/OS-II sous un environnement PC.

7. Annexe A : Fonctions uC/OS-II

PC_VectSet(()

Fixe le vecteur d'interruption: premier paramètre

PC_SetTickRate()

est le numéro d'interruption, second paramètre est le pointeur sur le handler d'interruption. Modifie l'unité de mesure temporel de l'OS

OSMboxPend

(standard: 18.20648Hz) Suspend une tâche en attente de message (dans

OSMboxPost()

une boîte aux lettres) tant qu'elle ne l'a pas reçu. Envoie un message

OSInit()

Doit être appelé avant toute autre fonction de

OSSemCreate()

l’OS. Elle créée 2 tâches : 1 Tâche Idle et 1 Tâche statistique Crée un sémaphore et retourne un pointeur sur

OSTaskCreate()

une structure de type OS_EVENT Création de tâche

OSStart()

Arguments : pointeur sur le code de la tâche, pointeur sur les données d’initialisation, pointeur sur la pile utilisée, taille de la pile, priorité de la tâche Lance l’OS : création des structures de données,

OSStatInit()

lancement du scheduler Initialise les statistiques relatives à uC/OS-II.

PC_DispChar()

Affiche un caractère quelque part dans une

OSTimeDly

fenêtre DOS (position en premier et second argument, caractère à afficher en troisième et couleurs en dernier) retarde une tâche de N unités de mesure temporel (cf PC_SetTickRate()), N étant passé

Université de Cergy-Pontoise

OSTimeDlyHMSM(int,int,int,int)

OSSemPend(*OS_EVENT, INT16U, INT8U)

OSemPost(*OS_EVENT)

OSTimeDly(int)

PC_DispClrScr()

PC_DispStr()

PC_DOSSaveReturn()

PC_DOSReturn

OSTaskCreateExt(*OS_STK,* struct, *OS_STK,int,int,*OS_STK,sk_size)

en argument. même fonctionnalité que OSTimeDly mais en heures/minutes/secondes/millisecondes Prise de semaphore : premier paramètre est un pointeur sur l’évènement, 2 e paramètre est un timeout, 3 e paramètre est un code d'erreur :

OS_NO_ERR si le sémaphore est dispo OS_TIMEOUT si le sémaphore n'est pas relâché avant le timeout spécifié OS_ERR_EVENT_TYP si le premier argument ne pointe pas vers un sémaphore OS_ERR_PEVENT_NULL si le premier argument est NULL. Relâche le sémaphore La tâche s’endort (état WAITING) pendant le

temps donné en paramètre. (Une valeur de 1 signifie 1 cycle d’horloge système) Fonction de réinitialisation de l’écran (DOS) Affiche une chaîne de caractère quelque part à l’écran Sauvegarde l'environnement DOS avant le retour en mode console Permet de revenir en mode DOS (utiliser au préalable PC_DOSSaveReturn() pour sauvegarder les registres importants du processeur pour permettre ce retour à DOS). Version étendue de OSTaskCreateExt() prenant en plus les arguments suivants:

- id de la tâche

- l'adresse du bas de la pile

- la taille de la pile alloué pour la tâche (en nombre d'éléments: INT8U, INT16U par exemple suivant le type que prend OS_STK)

- un pointeur vers une structure de données (peut contenir le temps que met une tâche pour s'exécuter, le contenu de registres virgule flottante )

- une option de partage entre tâches :

OS_TASK_OPT_STK_CHK spécifie quel contrôle de pile est permis pour la tâche OS_TASK_OPT_STK_CLR spécifie quelle pile doit être réinitialisée OS_TASK_OPT_SAVE_FP spécifie quels registres flottant sont sauvés.

Cette fonction renvoie:

- soit OS_NO_ERR en cas de succès

- soit OS_PRIO_EXIST si la priorité demandée

Université de Cergy-Pontoise

existe déjà

 

OS_NO_MORE_TCB si mucos n'a plus de OS_TCB à affecter.

-

OS_ENTER_CRITICAL()

Rem: certains indices de priorité sont réservés à certains fonctions système mucos: 0,1,2,3, OS_LOWEST_PRIO-3, OS_LOWEST_PRIO-2 active et désactive les interruptions du

premier argument: indice de priorité de la tâche

OSMboxCreate()

processeur. crée une boîte à lettres

PC_GetKey()

Renvoie vrai ou faux en fonction de la touche

PC_ElapsedInit()

tapée sur le clavier et celle spécifiée en argument de cette fonction. Initialise le processus de mesure de temps

PC_ElapsedStart()

Démarrage de la mesure de temps d’exécution

PC_ElapsedStop()

d’une portion de code Fin de la mesure de temps

OSTaskStkChk()

Renvoie des informations relatives à la pile

d'exécution:

second argument: pointeur sur la donnée de type OS_STK_DATA contenant les champs suivants:

- INT32U OSFree (nb d'octets dispos sur la pile)

- INT32U OSUsed (nb d’octets utilisés par la

OSQPend

pile) Suspend une tâche en attente de message (d'une

OSQPost

file d’attente) tant qu'elle ne l'a pas reçu. Envoie un message à une autre tâche au moyen

PC_GetDateTime()

d'une file d’attente Donne l'heure et la date du PC

Université de Cergy-Pontoise

8. Annexe B : Code de l’exercice 1

/*

****************************************************************************************

*

uC/OS-II

*

*****************************************************************************************

*/ #include "includes.h"

/*

****************************************************************************************

*

******************************************************************************************

CONSTANTS

*/

#define

TASK_STK_SIZE

512

/* Size of each task's stacks (# of WORDs)

*/

#define

N_TASKS

10

/* Number of identical tasks

*/

/*

******************************************************************************************

*

******************************************************************************************

VARIABLES

*/

OS_STK

TaskStk[N_TASKS][TASK_STK_SIZE];

/* Tasks stacks

*/

OS_STK

TaskStartStk[TASK_STK_SIZE];

char

TaskData[N_TASKS];

/* Parameters to pass to each task

*/

OS_EVENT

*RandomSem;

/*

******************************************************************************************

*

******************************************************************************************

*/

FUNCTION PROTOTYPES

void

Task(void *data);

/* Function prototypes of tasks

*/

void

TaskStart(void *data);

/* Function prototypes of Startup task

*/

static void TaskStartCreateTasks(void); static void TaskStartDispInit(void);

static void TaskStartDisp(void); /*

******************************************************************************************

*

******************************************************************************************

*/

void main (void)

MAIN

{

PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK);

/*

*/

OSInit();

/* */

PC_DOSSaveReturn(); PC_VectSet(uCOS, OSCtxSw);

/* */

/* */

RandomSem

= OSSemCreate(1);

/* */

OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0);

OSStart();

/* */

Université de Cergy-Pontoise

}

/*

******************************************************************************************

*

******************************************************************************************

*/ void TaskStart (void *pdata)

{

#if OS_CRITICAL_METHOD == 3

OS_CPU_SR cpu_sr; #endif

STARTUP TASK

/* Allocate storage for CPU status register */

char

s[100];

INT16S

key;

pdata = pdata;

/* Prevent compiler warning

 

*/

TaskStartDispInit();

/* Initialize the display

*/

OS_ENTER_CRITICAL(); PC_VectSet(0x08, OSTickISR); PC_SetTickRate(OS_TICKS_PER_SEC); OS_EXIT_CRITICAL();

/* */

/*

*/

OSStatInit();

/* Initialize uC/OS-II's statistics

*/

TaskStartCreateTasks();

/*

*/

for (;;) { TaskStartDisp();

/* Update the display

*/

if (PC_GetKey(&key) == TRUE) {

/* See if key has been pressed

*/

if (key == 0x1B) {

/* Yes, see if it's the ESCAPE key

*/

PC_DOSReturn();

/*

*/

}

}

OSCtxSwCtr = 0; OSTimeDlyHMSM(0, 0, 1, 0);

/* Clear context switch counter /* */

*/

}

}

/*

***************************************************************************************

*

*****************************************************************************************

*/ static void TaskStartCreateTasks (void)

{

CREATE TASKS

INT8U i;

for (i = 0; i < N_TASKS; i++) { TaskData[i] = '0' + i;

/* Create N_TASKS identical tasks /* Each task will display its own letter

*/

*/

OSTaskCreate(Task, (void *)&TaskData[i], &TaskStk[i][TASK_STK_SIZE - 1], i + 1);

Université de Cergy-Pontoise

}

}

/*

******************************************************************************************

*

******************************************************************************************

*/ void Task (void *pdata)

TASKS

{

 

INT8U x;

INT8U y;

INT8U err;

for (;;) { OSSemPend(RandomSem, 0, &err);

/* Acquire semaphore to perform random numbers

*/

x = random(80);

/* Find X position where task number will appear

*/

y = random(16);

/* Find Y position where task number will appear

*/

OSSemPost(RandomSem);

/* Release semaphore

*/

 

/* Display the task number on the screen

*/

 

PC_DispChar(x, y + 5, *(char *)pdata, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);

OSTimeDly(1);

/* Delay 1 clock tick

*/

}

}