Y.BENABEN - Stage IUT GEII Bordeaux 1
Pour expliquer brièvement le rôle d'un système multitâche, je vais présenter le logiciel µC/OS-II (version 68HC11) conçu et mis au point par Labrosse. Le logiciel µC/os-II est un noyau temps réel qui permet d'effectuer une exécution de plusieurs tâches sur un microcontrôleur 68HC11 ou un microprocesseur (680x0 par exemple). On peut trouver sur le site Internet de µC/OS II ( http://www.ucos-ii.com/ports.htm ) les différentes versions de µC/OS II portées sur des systèmes différents (Motorola famille 680x0, 68HC11/16, Power PC 860, Intel 80x86, Philips XA...). Ce noyau multitâche est fourni gratuitement sur le site Internet et un manuel écrit par l'auteur peut être commandé à l'adresse précédente.
Le multitâche / monoprocesseur permet d'effectuer à l'échelle humaine plusieurs tâches simultanément. En effet l'utilisateur peut alors diviser son projet en plusieurs tâches indépendantes. Au niveau du processeur, une seule tâche est effectuée à la fois mais le multitâche permet d'éliminer les temps machines inutilisés (boucle d'attente...).
Figure 7 les différents états des tâches
( source : http://www.on-time.com/index.html
)
Le fonctionnement du noyau temps réel sur le 68HC11 est le suivant :
lors de l'initialisation
du programme µC/OS-II, les différents programmes de l'utilisateur sont considérés
comme des tâches qui sont toutes créées pendant cette période d'initialisation.
Le programmeur doit alors spécifier le point d'entrée de la tâche, l'emplacement
des données pour cette tâche, l'adresse haute de la pile de la tâche et le degré
de priorité de la tâche. Ainsi la tâche de plus haut degré de priorité et prête
est exécutée par le microcontrôleur ( on dit que le noyau est préemptif
). La figure 7 montre les différents états des tâches lors du fonctionnement
du noyau multitâche.
Remarque : dans le noyau µC/OS,
l'utilisateur dispose de 64 tâches (56 réellement disponibles) où chaque degré
de priorité correspond à une tâche, cest-à-dire que 2 tâches ne peuvent
pas avoir le même degré de priorité ( pas de "round-robin" ou "méthode
du tourniquet").
Les caractéristiques essentielles du noyau temps réel µC/OS sont les suivantes :
création
et gestion de 63 tâches maximum.
création
et gestion de sémaphores binaires et comptés.
fonction
d'attente de tâche.
changement
de priorité des tâches.
effacement
de tâches.
suspension
et continuation de tâches.
envoi de
messages depuis une routine d'interruption (ISR) ou d'une tâche vers une autre
tâche.
Les
tâches peuvent ainsi communiquer avec dautres tâches (grâce aux sémaphores,
boîtes aux lettres, files dattentes
) ou bien avec des périphériques grâce
aux ISR ( Interrupt Service Routine) dont la liste complète est disponible dans la
note d'application AN711 de chez Philips.
La notion de temps réel correspond à la façon dont les tâches sont exécutées dans le temps : le temps d'exécution des tâches étant déterminant pour la commutation des tâches, le noyau temps réel exécute en premier les tâches dont le temps d'exécution est critique.
Le fonctionnement du noyau préemptif est illustré à la figure suivante :
Figure 8 illustration dun noyau préemptif.
( source : note d'application Philips
AN711 )
Comme
nous le voyons, une tâche de faible priorité est exécutée en (1) alors quune
interruption asynchrone intervient en (2). Le microprocesseur saute donc au vecteur
d'interruption exécutant alors une tâche prête de degré plus important que la tâche
précédente (3). A la fin de la routine d'interruption, l'ISR (Interrupt Service Routine)
invoque un service du noyau (4) qui décide de retourner à la tâche de plus haute
priorité et non à la tâche précédente de plus basse priorité. La tâche de plus haut
degré s'exécute donc (5) de manière normale à moins qu'une nouvelle interruption
n'arrive. A la fin de la tâche de haute priorité, le noyau reprend l'exécution de la
tâche de plus faible priorité qui est prête depuis son interruption (6). Comme on peut
le voir, le noyau garantit que les tâches dont le temps d'exécution est critique sont
effectuées en premier.
Un noyau temps réel assure
principalement 2 fonctions : l'ordonnancement et le changement de contexte.
L'ordonnancement permet de déterminer quelle tâche prête a le plus haut degré de
priorité. Quand une tâche de plus haute priorité doit être exécutée, le noyau doit
sauvegarder toutes les informations nécessaires de la tâche en cours d'exécution afin
de permettre une éventuelle continuation. Les informations sauvegardées (appelées
contexte de la tâche) sont généralement les registres (A, B, X, Y, CCR pour le HC11),
la pile et le pointeur de pile de la tâche qui va être suspendue. Lors de l'exécution
d'une nouvelle tâche à un plus haut degré de priorité, le contexte de la nouvelle
tâche est chargé puis l'exécution reprend là où elle s'était interrompue.
La création d'une tâche se fait grâce à la fonction suivante :
OSTaskCreate(AppTask1, (void *)0, (void *)&AppTask1Stk[255], 10);
Le premier paramètre est le point d'entrée du programme utilisateur (nom de l'étiquette), le second paramètre indique l'adresse des données, le troisième paramètre indique l'adresse du haut de la pile de la tâche et le dernier paramètre indique le degré de priorité de la tâche (ici 10). Pour plus de précision, on pourra consulter le fichier OS_CPU_C.C (contenu dans c68hc11.zip), le fichier 68hc11.c (contenu dans 68hc11_1.zip) ou bien dans le manuel adéquat de µC/OS.
Le corps de la tâche est constitué de la manière suivante :
une zone d'initialisation
de la tâche.
une zone
permettant d'initialiser les variables du programme utilisateur.
une zone
constituée d'une boucle infinie où l'utilisateur place le code de son programme.
une instruction
OSTimeDly(x) permettant de céder x coups d'horloge aux autres tâches.
remarque:
cette instruction est obligatoire dans le contenu du programme de la tâche afin
de basculer vers d'autres tâches.
Les fonctions de base du noyau temps réel employées dans le programme test.c (archive test.zip) sont les suivantes :
OSinit();
initialisation globale du noyau
OSTaskcreate
(pointeur sur le programme utilisateur, pointeur données, adresse haut de la
pile, priorité); permet de définir une tâche. Le pointeur "données"
permet de faire passer des données à une tâche lors de son initialisation. La
priorité est définie telle que plus le nombre est faible, plus le degré de priorité
est élevé.
OSStart();
permet de créer au moins une tâche en appelant OSTaskCreate();
D'après la remarque précédente, le fonctionnement correct de µC/OS exige qu'une tâche doive obligatoirement appeler une fonction (un service du noyau) afin de :
faire attendre
la tâche d'un délai de n coups d'horloge (OSTimeDly).
attendre
un sémaphore.
attendre
un message d'une autre tâche ou d'une ISR (Interrupt Service Routine : il peut
s'agir d'une fonction du noyau qui permet de modifier le statut d'une tâche
comme le degré de priorité d'une tâche).
suspendre
l'exécution de cette tâche.
Les fonctions du noyau commencent toutes par les lettres « OS » (Operating System), les lettres suivantes OS définissent les familles de fonctions de µC/OS II. La liste des fonctions de µC/OS II est la suivante :
initialisation
(OSinit, OSStart).
la gestion
des tâches (OSTaskCreate, OSTaskDel, OSTaskDelReq, OSTaskChangePrio, OSTaskSuspend,
OSTaskResume, OSSchedlock, OSSchedUnlock).
la gestion
du temps (OSTimeDly, OSTimeDlyResume, OSTimeSet, OSTimeGet).
la gestion
des sémaphores (OSSemCreate, OSSemAccept, OSSemPost, OSSemPend, OSSemInit).
la gestion
des boîtes aux lettres (OSMboxcreate, OSMboxAccept, OSMboxPost, OSMboxPend).
la gestion
des files d'attente (OSQCreate, OSQAccept, OSQPost, OSQPend).
la gestion
d'interruption (OSIntEnter, OSIntExit).
Les explications complètes, les exemples et la syntaxe des
primitives figurent dans l'ouvrage "µC/OS-II book The real Time Kernel" de J. Labrosse. C'est
l'ouvrage de référence !!!
La
syntaxe succinte des primitives est aussi donnée dans la note d'application AN711 de chez
Philips (http://www.ucos-ii.com/files/an711.pdf).
Le fichier PostScript ucosman.ps (ftp://ftp.cygnus.com/pub/embedded/ucos/ucos-arm/)
est aussi utile mais il faut faire attention car quelques primitives ont été modifiées
pour des besoins spécifiques (voir sémaphores).
Voici un bref descriptif des moyens quoffre µC/OS pour la communication entre tâches.
Un sémaphore est un objet qui permet de résoudre les problèmes de synchronisation entre plusieurs tâches concurrentes.
Un sémaphore est constitué d'un compteur à valeur entière (valeur du sémaphore) et d'une file d'attente. Sur le compteur sont définies 2 opérations qui sont Prendre (P) et Vendre (V). Une valeur positive du sémaphore désigne le nombre d'accès disponibles à un instant donné ; une valeur négative représente par la valeur absolue le nombre de processus en attente de libération de la ressource. En effet, avant d'accéder à une donnée, un processus doit utiliser l'opération P. Si la valeur du sémaphore est 0, alors le processus doit attendre que cette valeur devienne supérieure à 0. Si la valeur est supérieure à 0, alors l'opération P décrémente la valeur du sémaphore et le processus continue son exécution. Après l'accès aux données, le processus doit appeler l'opération V qui incrémente le sémaphore autorisant ainsi aux autres processus l'accès aux données.
Les sémaphores
permettent donc soit de synchroniser des processus (échanges de données), soit
d'exclure des processus concurrents (accès à une même variable en même temps).
Un exemple dutilisation de sémaphores est disponible ici test2.c
(archive test.zip).
Une boîte à lettre est une zone d'échange entre 2 processus et se compose de 2 files d'attente : une file d'attente de messages et une file d'attente de tâches. Les boîtes aux lettres permettent de synchroniser la communication entre des tâches asynchrones.
Une file d'attente constitue une suite de processus en attente d'un évènement, d'un sémaphore On trouve alors des files d'attente d'évènements (les processus attendent un certain type d'évènement), des files d'attente sur sémaphore (les processus sont gérés par ordre d'arrivée : une opération Prendre non passante amène la tâche à la fin de la file et pour chaque opération Vendre c'est la première tâche en attente qui est activée) et enfin des files d'attente sur boîtes aux lettres.
Le noyau temps réel µC/OS est écrit en langage C (COSMIC SOFTWARE 4.5). Seuls les portages spécifiques au microcontrôleur (ici le 68HC11F1) sont écrits en langage d'assemblage. Le travail a consisté en premier lieu à adapter le noyau µC/OS au 68HC11Ax et Ex. Les lignes à modifier sont dans les fichiers start.s et build.bat.
Le fichier start.s contient des initialisations propres au 68HC11F1 telles que :
les bits
de protection de la EEPROM (4 bits de poids fort du registre CONFIG).
l'activation
de la logique de décodage (rattachée au PORTG) des boîtiers de RAM et de ROM
externes (pas besoin de 74HC138).
le pointeur
de pile et linitialisation des 1024 octets de RAM.
Les modifications à apporter ont consisté à enlever la configuration des bits de
protection de la EEPROM et de la logique de décodage interne, l'initialisation des 256
octets de RAM interne et le pointeur de pile en $0FF.
Remarque: le mode étendu de la
carte mère permet de prolonger la mémoire RAM interne jusqu'à l'adresse $0FFF
soit 4Ko de RAM disponible. Il faudra donc prendre soin de ne pas faire "interférer"
la pile du moniteur BUFFALO et celle de µC/OS.
Le fichier build.bat doit être configuré pour appeler le
fichier io.h (et non pas iof1.h) qui est situé dans le répertoire de Cosmic Software (ex
: C:\logiciel\68HC11\cx32\H6811\IO.H ).
Remarque importante : lors de la compilation, l'archive de µC/OS
II (c68hc11c.zip) ne contient pas les fichiers os_core.c, os_mbox.c... nécessaires
lors de la compilation de Ucos_II.c mais le fichier objet est heureusement fourni
sous le nom de Ucos_ii.o situé dans le répertoire UCOS-II/M68hc11/Cosmic/obj.
Il faut donc "commenter" les lignes suivantes du fichier build.bat
:
rem DEL ..\OBJ\uCOS_II.O
rem C:\logiciel\68HC11\cx32\CX6811
-cl..\LST -co..\OBJ -e -gv -l -ocv -v +nowiden +sprec +debug uCOS_II.C
La mise en place du noyau temps réel pour 68HC11Ax ou Ex s'est faite grâce à l'émulateur CT68HC11. Le programme µC/OS sera donc installé dans la mémoire interne de 64Ko de l'émulateur ou bien en RAM externe après avoir programmé correctement l'EPROM aux adresses $7FE8 (avec $206A) et $7FF6 (avec $2036) ainsi que le vecteur de RESET (pour plus de précisions se reporter au fichier vectors.c (dans l'archive test.zip). µC/OS-II est fourni avec un programme test.c (archive test.zip) qui permet de créer 3 tâches : une tâche de démarrage (AppStartTask) qui va créer 2 autres tâches (task#1 et task#2) lesquelles vont être mises en compétition avec des degrés de priorité différentes (Noter que la tâche de démarrage a le plus haut degré de priorité).
La figure
suivante illustre simplement lordonnancement des 3 tâches. Lordonnanceur peut
continuer ou suspendre une tâche grâce aux signaux « continue » et « suspend »
représentés ici par des flèches en pointillés. Lorsquune tâche est suspendue,
la suivante et continuée. Le schéma montre comment sont ordonnancées les tâches mais
ne précise pas comment fonctionne la communication entre les tâches ou avec les
périphériques.
Figure 9- lordonnancement des tâches.
( source : A premptive kernel )
Le programme mis
au point pour la carte mère est basé sur l'exemple test.c
fourni dans l'archive c68hc11.zip. Le programme crée une première tâche qui
consiste à faire un écho sur la liaison série RS232 configurée à 9600bps, puis
une deuxième tâche qui consiste à exécuter un chenillard 4/5 LEDs (PA7 est configurée
par PACTL et PA3 est condamnée si on utilise le moniteur BUFFALO) enfin une
troisième tâche qui recopie les interrupteurs du PORTE sur le PORTA.
Le fichier test.lnk
permet de configurer la mise en mémoire du noyau µC/OS :
+seg .bsct -b 0x0000 -m256 #zéro page
+seg .bss -b 0x7000 -m8192 #adresse de la pile
+seg .text -b 0x2000 -m32726 -n .text #adresse début du programme
+seg .const -a .text #début des données
../OBJ/START.O # routine de démarrage : initialisation de la RAM,
../OBJ/OS_CPU_A.O #du pointeur de pile...
../OBJ/OS_CPU_C.O
../OBJ/TEST.O
../OBJ/uCOS_II.O #fichier fourni avec l'archive c68hc11.zip à ne pas recompiler
../OBJ/hc11lib.o #bibliothèque de fonctions externes.
C:/logiciel/68HC11/cx32/LIB/LIBF.H11 #bibliothèques Cosmic software
C:/logiciel/68HC11/cx32/LIB/LIBI.H11
C:/logiciel/68HC11/cx32/LIB/LIBM.H11
#+seg .const -b 0xFFD6 -m42 #table des vecteurs d'interruption.
#../OBJ/VECTORS.O
Les 2 dernières lignes ne sont à commenter que si µC/OS marche avec le moniteur BUFFALO en EPROM. En effet, les vecteurs d'interruptions doivent être programmés dans l'EPROM . Si les lignes ne sont pas commentées, l'émulateur ou BUFFALO renverra un message d'erreur d'écriture.
La mise en place du programme multitâche se fait grâce à la commande LOAD T du moniteur BUFFALO après avoir converti le fichier.hex en .S19.
L'exécution du programme se fait par la commande G 2000. On observe alors le
fonctionnement correct des tâches. L'utilisation de la fonction OSTimeDly permet donc de
faire des temporisations dont l'unité est le tick, laissant ainsi ces cycles
d'attente à d'autres tâches.
Un deuxième programme a permis de mettre en évidence le fonctionnement des sémaphores avec µC/OS II. Le programme test2.c permet de synchroniser 3 tâches. La tâche #1 attend le sémaphore sem1 et lorsque celui - ci est validé, la tâche allume la LED PA5. De même pour la tâche #2 qui attend le sémaphore 2 (sem2) et allume la LED PA6. La tâche 3 attend le sémaphore 3 (sem3) et allume la LED PA7. La tâche principale (celle qui a la plus forte priorité) permet de valider les 3 sémaphores en « même temps » après avoir exécuté une temporisation ( fonction OSTimeDLy).
Il est intéressant de se reporter au document ucosman.ps qui explique le fonctionnement des sémaphores. Lors de la création dun sémaphore (initialisation), on spécifie la valeur du sémaphore qui est nulle dans lexemple test2.c (archive test.zip).
En effet une tâche peut être mise en attente avec la fonction OSSemPend qui décrémente la valeur du sémaphore si elle était nulle. Lorsque le sémaphore est négatif, la fonction OSSEmPend attend alors que la valeur du sémaphore redevienne nulle (elle attend lincrémentation du sémaphore). Ceci est effectué par la tâche principale grâce à la fonction OSSemPost qui incrémente le sémaphore si sa valeur était nulle et donne la main à la tâche de plus haute priorité qui soit en attente de sémaphore.
Par la suite, les tâches poursuivent leur exécution normale. Pour réutiliser les sémaphores, il faut les réinitialiser grâce à la fonction OSSemInit().
Le noyau
µC/OS II permet donc de développer des applications 68HC11 multitâches en langage C de
Cosmic Software. Les fonctions de communications entre tâches et les ISR permettent de
concevoir des programmes performants. Le portage du noyau µC/OS II a divers processeurs
et microcontrôleurs le rend populaire pour le développement dapplications
multitâches temps réel.