Telechargé par Thorsten Mons

poly RTOS 2Ainfo 2018

publicité
3A INSTRUMENTATION AVANCEE
SYSTEMES TEMPS REEL
2018
SYSTEMES TEMPS REEL
CONTACTS
Établissement
ENSICAEN
6 boulevard Maréchal Juin
CS 45053
14050 CAEN cedex 04
Référents
Hugo Descoubes
hugo.descoubes@ensicaen.fr
+33 (0)2 31 45 27 61
+33 (0)6 22 07 21 51
https://fr.linkedin.com/in/hugo-descoubes-823a4268
Philippe Lefebvre
philippe.lefebvre@ensicaen.fr
+33 (0)2 31 45 27 58
SYSTEMES TEMPS REEL
RESSOURCES
Les différentes ressources numériques sont accessibles sur la
plateforme pédagogique de l'ENSICAEN (aucune authentification
requise)
 Lien direct : https://foad.ensicaen.fr/course/view.php?id=115
 Lien indirect : http://foad.ensicaen.fr/
 Formation Classique
 Spécialité Informatique
 2ième année
 Systèmes Temps Réel
 Ne pas oublier de s'inscrire au cours avant tout dépôt !
SYSTEMES TEMPS REEL
Sommaire
0. OUTILS DE DÉVELOPPEMENTS
1. COURS FREERTOS
2. TP TEMPS RÉEL
SYSTEMES TEMPS REEL
OUTILS DE DEVELOPPEMENT
Installer les outils de développement et venir en séance avec vos machines
personnelles (IDE MPLABX et toolchain C XC32). Les TP peuvent être réalisés
indifféremment sur plateforme Windows ou GNU\Linux.
 IDE MPLAB X (Environnement de Développement Intégré):
http://www.microchip.com/mplabx
 Toolchain C XC32 (anciennes versions, installer XC32 v1.21 – Free mode) :
http://www.microchip.com/development-tools/downloads-archive
 Toolchain C XC32 (Free mode) :
http://www.microchip.com/xc32
La trame de TP n'utilisant comme interface que le module UART 1 du PIC32 en
mode transmission, il est alors possible de valider la compilation et le fonctionnement
de vos programmes depuis chez vous en mode Simulateur. Voici la procédure :
 Ouvrir les propriétés du projet : fenêtre Project > clic droit sur le nom du projet
> Properties
 Sélectionner le simulateur : Hardware Tool > Simulator
 Configurer le simulateur : Simulator > Options Categories [UART1 IO Options] >
Enable UART1 IO
 Sauvegarder les propriétés du projet : Apply > OK
 Ouvrir une console de debug : Window > Debugging > Debug Console
 Compiler et exécuter le projet en mode Debug : Debug > Debug Main Project
 Visualiser la console de sortie : sélectionner la nouvelle fenêtre de sortie "UART
1 Output" et Hop c'est magique (normalement) !
SYSTEMES TEMPS REEL
FREERTOS
SYSTEMES TEMPS REEL
FreeRTOS
SOMMAIRE
1. PREAMBULE
2. SYSTEME D'EXPLOITATION TEMPS REEL
2.1. Tâche
2.2. Gestion mémoire
2.3. Gestion du tas
2.4. Création de tâche
2.5. Mode préemptif
3. QUEUE DE MESSAGES
3.1. API de FreeRTOS
3.2. Timeout
4. SEMAPHORE
4.1. Sémaphore binaire
4.2. MUTEX
4.3. Sémaphore à compteur
1
SYSTEMES TEMPS REEL
FreeRTOS
1. PREAMBULE
Durant la lecture de cette partie, nous allons nous intéresser à l'exécutif temps réel ou RTOS
(Real Time Operating System) FreeRTOS. La traduction littérale en Français de RTOS est Système
d'Exploitation Temps Réel. Il serait néanmoins plus rigoureux d'appeler ce type d'outil scheduler ou
ordonnanceur, plus que système d'exploitation, à l'image par exemple de systèmes comme GNU/Linux,
Android, Windows, MacOS. Néanmoins, l'acronyme OS est un abus de langage fréquemment utilisé
dans le monde de l'embarqué pour parler des systèmes d'exploitation temps réel.
Un scheduler ou ordonnanceur nous propose des services logiciel (tâches, queues de
messages, sémaphores ...). Bien maîtrisé, il s'agit d'une aide précieuse durant les phases de
développement d'un projet (évolutivité, modélisation, gestion optimale des ressources CPU ...).
Néanmoins, encore beaucoup de systèmes autour de nous fonctionnent sans OS (cf. sondage cidessous, UBM Tech). La question d'utiliser ou pas un ordonnanceur dépend bien sûr de l'application.
Dès que les spécifications fonctionnelles d'une application mettent en avant un certain nombre
(difficile à estimer) de traitements pouvant potentiellement s'exécuter en parallèle, la question
d'utiliser un RTOS se pose. Pour des applications communicantes (Bluetooth, WIFI, réseaux de
terrains ...), l'utilisation d'un OS peut s'avérer très utile.
Il existe un grand nombre de RTOS, libres, open Sources et propriétaires (RTX, MicroC/OS III,
FreeRTOS, VxWorks...) possédant des jeux d'avantages et d'inconvénients différents afin de s'adapter
au très grand nombre de problématiques du marché. Nous avons de notre côté à l'école porté notre
attention sur FreeRTOS, un petit scheduler temps réel offrant un certain nombre d'avantages. La
documentation de FreeRTOS est directement accessible en ligne depuis le site internet
(http://www.freertos.org/).
2
SYSTEMES TEMPS REEL
FreeRTOS
En 2017, FreeRTOS est l'un des RTOS leader sur le marché de l'embarqué. FreeRTOS est libre de
droit d'utilisation et open source (sous licence). Il est officiellement supporté par 34 architectures (TI,
Microchip, Atmel, NXP, Intel ...) et 18 chaînes de compilation. Il a par exemple été téléchargé plus de
100000 fois l'année passée, cela implique une communauté assez riche d'utilisateurs. Il existe de plus
en tout 4 variantes de FreeRTOS :
●
FreeRTOS : cf. ci-dessus
●
FreeRTOS + Trace : idem FreeRTOS avec des outils propriétaires permettant
d'instrumenter le code. Par exemple des outils graphique de trace.
●
OpenRTOS : version commerciale sous licence de FreeRTOS
●
SafeRTOS : version certifié SIL3 TUV
De plus en 2016, FreeRTOS est le RTOS actuellement le plus utilisé et celui le plus regardé pour
démarrer de nouveaux projets. Observons le marché en 2015 des OS et RTOS pour l'embarqué
(www.eetimes.com) :
3
SYSTEMES TEMPS REEL
FreeRTOS
2. SYSTEME D'EXPLOITATION TEMPS REEL
FreeRTOS, comme n'importe quel autre noyau, est un outil purement logiciel, ce n'est qu'un
système de fichiers. FreeRTOS nous propose une API de programmation pour gérer un environnement
multitâches. La notion de tâche sera présentée par la suite. Vous trouverez ci-dessous le systèmes de
fichiers du noyau :
4
SYSTEMES TEMPS REEL
FreeRTOS
2.1. Tâche
Dans un environnement multitâches, l'application est découpée en plusieurs tâches
correspondant à des actions à effectuer. Le rôle de l'ordonnanceur est alors de gérer cet environnent
sachant que potentiellement, plusieurs d'entre-elles chercheront à s'exécuter en même temps.
N'oublions pas qu'un PIC32 ne possède qu'un seul CPU, l'ordonnanceur ne pourra donc donner la main
qu'à une seule tâche à la fois. Vous constaterez qu'à première vue, une tâche ressemble beaucoup à
une simple fonction. Nous verrons par la suite qu'une tâche est bien plus que ça. Observons un
exemple de tâche sous FreeRTOS :
void Task1( void *pvParameters ){
}
// Faire toujours
for( ;; ){
// code utilisateur
}
Une tâche est le plus souvent implémentée sous forme d'une boucle infinie. De plus,
l'ordonnanceur a la capacité de pouvoir faire passer une tâche dans différents états :
●
Running (en cours) : il s'agit de la tâche en cours d'exécution par le CPU. Une seule tâche peutêtre dans cet état.
●
Ready (prêt) : les tâches dans cet état sont prêtes à être exécutées. Il suffit que la tâche à l'état
en cours redonne la main à l'ordonnanceur ou que celui-ci la reprenne et selon le contexte
d'exécution (priorité supérieure, round-robin ...) une nouvelle tâche s'exécutera.
●
Blocked (bloqué) : les tâches dans cet état sont en attentes d'un événement pour se réveiller
(queue de messages, sémaphores, timeout ...). Une fois l'événement arrivé, la tâche concernée
repasse alors à l'état prêt.
●
Suspended (suspendu) : Cet état, peu utilisé, est propre à FreeRTOS et est à manier avec
précaution. Une tâche dans cet état n'est plus vue de l'ordonnanceur.
5
SYSTEMES TEMPS REEL
FreeRTOS
États d'une tâche sous FreeRTOS :
Une différence très importante entre une simple fonction et une tâche est qu'à chaque tâche
est associée un TCB (Task Control Block). Un TCB est une structure de données décrivant une tâche
(descripteur de tâche). L'ordonnanceur utilise ensuite les TCB pour le management de son
environnement multitâches. Sous FreeRTOS, un TCB comporte par exemple :
●
la priorité de la tâche
●
le ou les événements qu'elle attend
●
le pointeur de base de la pile associée à la tâche
●
le pointeur de sommet de pile
●
...
6
SYSTEMES TEMPS REEL
FreeRTOS
2.2. Gestion mémoire
Ce sont les TCB que l'ordonnanceur analyse afin de savoir à quelle tâche prendre ou donner du
temps CPU. Depuis la première année, tous les programmes que vous avez développés sur MCU
n'étaient constitués que d'un main() voir d'ISR's, aucun OS n'était embarqué. Effectuons quelques
rapides rappels sur le fonctionnement du main() et notamment les mécanismes de gestion mémoire
réalisés conjointement par la chaîne de compilation et le processeur.
●
Sans noyau, si une application n'est constituée que d'un main(), la mémoire des données peutêtre découpée en deux grandes zones. La zone où se trouvent les variables statiques (variables
globales, locales static ...) et celle nommée pile ou stack où sont notamment gérées les
variables locales (dynamiques). Selon l'application, un tas peut également être utilisé. Toutes
les variables locales du main() ou des fonctions appelées depuis le main() sont allouées
dynamiquement dans la pile du main() ou pile système. Il en est de même des variables locales,
paramètres de fonction et des sauvegardes de contexte des fonctions d'interruption. Prenons
un exemple de mapping mémoire pour une application sans OS et sans Tas système :
unsigned char stringBuffer[100] = "FreeRTOS test !";
/**
* @fn main
* @brief main entry point
*/
int main(void){
// Configurations matérielles
UserInit();
// envoi d'une chaîne de caractères via UART
UARTputs(stringBuffer);
}
// On reste bloqué ici ... pour le moment !
while(1);
●
Sous FreeRTOS, le noyau utilise une zone de taille configurable nommée Tas ou Heap.
Attention, en fonction de la stratégie de gestion du tas choisie (fichiers heap_1.c, heap_2.c,
heap_3.c ou heap_4.c) le tas peut-être le tas système ou une simple très large variable globale.
Chaque tâche possède sa propre pile dans le Tas du kernel. Chaque tâche possède donc un
environnement d'exécution indépendant.
7
SYSTEMES TEMPS REEL
FreeRTOS
void Task1( void *pvParameters );
void Task2( void *pvParameters );
/**
* @fn main
* @brief main entry point
*/
int main(void){
// Création de 2 tâches
xTaskCreate( Task1, "Task 1", configMINIMAL_STACK_SIZE, ...);
xTaskCreate( Task2, "Task 2", configMINIMAL_STACK_SIZE, ...);
// Démarrage ordonnanceur
vTaskStartScheduler();
}
while(1);
// ... Nous ne reviendrons jamais ici !
void Task1( void *pvParameters ){
// Faire toujours
for( ;; ){ ... }
}
void Task2( void *pvParameters ){
// Faire toujours
for( ;; ){ ... }
}
2.3. Gestion du Tas
Dans le cas de FreeRTOS, le Tas ou heap n'est qu'un tableau déclaré en variable globale
(stratégies heap_1.c et heap_2.c). En tant que développeur, vous n'aurez pas directement accès à ces
fonctions d'allocation. Durant la création d'une tâche, le noyau génère un espace pour la TCB et la pile
de celle-ci dans le Tas.
Attention, en fonction de l'application et du MCU il faut être très prudent à l'espace alloué au
Tas et aux piles de chaque tâche. Par exemple, si la taille d'une pile est trop faible, en cas de
débordement de pile (stack overflow) nous pouvons écraser le contenu de la TCB de la tâche suivante
dans le Tas ... et donc faire tomber l'application. FreeRTOS propose 3 principales techniques pour la
gestion du Tas. Il suffit pour cela d'inclure à votre projet l'un des trois fichiers sources ci-dessous :
●
●
●
heap1.c : seules des allocations dynamiques sont possibles (exemple, créations de tâches).
Nous utiliserons ce fichier durant la trame de TP.
heap2.c : allocations et désallocations dynamiques sont possibles (exemple, créations et
suppressions de tâches). Soyez très prudent avec la désallocation de ressources et la gestion
par le kernel d'une mémoire fragmentée.
heap3.c : idem heap2.c, mais utilise les API standards malloc() et free() proposées par la chaîne
de compilation.
8
SYSTEMES TEMPS REEL
FreeRTOS
Exemple de mapping mémoire après la création de 2 tâches sous FreeRTOS :
void Task1( void *pvParameters );
void Task2( void *pvParameters );
/**
* @fn main
* @brief main entry point
*/
int main(void){
// Création de 2 tâches
xTaskCreate( Task1, "Task 1", configMINIMAL_STACK_SIZE, ...);
xTaskCreate( Task2, "Task 2", configMINIMAL_STACK_SIZE, ...);
// Démarrage ordonnanceur
vTaskStartScheduler();
}
// Nous ne reviendrons jamais ici !
while(1);
void Task1( void *pvParameters ){
// Faire toujours
for( ;; ){
// code utilisateur
}
}
void Task2( void *pvParameters ){
// Faire toujours
for( ;; ){
// code utilisateur
}
}
Vous constaterez que nous n'avons créé que deux tâches alors que le noyau en a créé trois. En
effet juste avant de démarrer l'ordonnanceur, le noyau crée une ultime tâche nommée tâche Idle qui
est la tâche de plus basse priorité de l'application. Lorsque aucune tâche ne tourne (tâches bloquées),
c'est en fait la tâche Idle qui est en cours d'exécution. Nous ne reviendrons donc jamais dans le main().
Nous verrons dans la trame de TP qu'il est d'ailleurs possible de détourner la tâche Idle. Pour
information, certains noyau considèrent le main() comme une tâche, ce n'est pas le cas de FreeRTOS.
9
SYSTEMES TEMPS REEL
FreeRTOS
2.4. Création de tâche
Nous avons précédemment vu qu'à la création d'une tâche, le noyau alloue un espace pour la
TCB et la pile dans le Tas. Découvrons maintenant le rôle des paramètres passés à l'API de création de
tâche (xTaskCreate()) :
void Task1( void *pvParameters );
const char *pvParametersTask1 = "Passage d'un pointeur sur une chaîne de caractères !";
/**
* @fn main
* @brief main entry point
*/
int main(void){
}
// Création d'une tâche
xTaskCreate( Task1,
"Tache 1",
configMINIMAL_STACK_SIZE,
pvParametersTask1,
tskIDLE_PRIORITY + 1,
NULL);
...
// Pointeur sur la fonction implémentant la tâche
// Chaîne de caractères associée à la tâche (debug).
// Taille de la pile associée à la tâche
// Paramètre passé à la tâche
// Priorité associée à la tâche
// "handle" pour la gestion de la tâche
●
Task1 : pointeur sur la fonction implémentant le tâche
●
''Tache 1'' : chaîne de caractères associée à la tâche (sauvée dans la TCB). Utilisé par des outils
de trace ou pour de l'instrumentation de code (par exemple, stack overflow).
●
configMINIMAL_STACK_SIZE: taille de la pile associée à la tâche. Cette macro est définie dans le
fichier FreeRTOSConfig.h. Il s'agit par défaut de la taille de la pile pour la tâche Idle. Attention,
cette taille est donnée en ''Words'' et non en octets. La taille d'un Word dépend de
l'architecture matérielle utilisée. Dans notre cas un Word = 32bits (les PIC32MX sont des MCU's
32bits).
●
pvParametersTask1: possibilité de passer un paramètre à la tâche.
●
tskIDLE_PRIORITY + 1 : priorité de la tâche. tskIDLE_PRIORITY ou ''0'' est la priorité minimale qui
correspond à la priorité de la tâche Idle. La priorité maximale vaut configMAX_PRIORITIES
définie dans FreeRTOSConfig.h.
●
NULL : il s'agit d'un outil de préemption permettant de passer la tâche en cours de création à
l'état suspendu. A manipuler avec précaution.
10
SYSTEMES TEMPS REEL
FreeRTOS
2.5. Mode préemptif
En mode coopératif, chaque tâche doit explicitement permettre à une autre tâche de
s'exécuter. Ce mode de fonctionnement était encore très rencontré notamment jusqu'à MS-DOS et
Mac OS 9 qui étaient tout deux des OS coopératifs. Cependant ce mode peut amener de gros
problèmes de robustesse. Par exemple, imaginons que nous restons bloqué dans une tâche (bug). Les
autres tâches ne pourront donc jamais prendre la main ... nous venons de faire tomber l'application.
En mode préemptif, l'OS prend périodiquement la main et force un réordonnancement. La
référence de temps utilisée est nommée tick (timer clock). Ce mode de fonctionnement est plus
robuste, les principaux OS sur ordinateur sont préemptif (Windows 8, Mac OS X, Linux ...). Exemple
d'exécution en mode préemptif sous FreeRTOS :
Dans notre cas, FreeRTOS configure et utilise un timer du processeur. Ce timer interrompt
périodiquement le programme en cours d'exécution en envoyant une demande d'interruption au CPU.
Une demande d'interruption ou IRQ peut arriver n'importe quand. Ce qui signifie que, sauf dans
certains cas (par exemple sections critiques ...), n'importe quelle tâche peut être interrompue à
n'importe quel moment pour donner la main à l'ordonnanceur. Quelque soit la priorité de la tâche en
cours d'exécution.
11
SYSTEMES TEMPS REEL
FreeRTOS
3. QUEUE DE MESSAGES
Une queue de messages (messages queue ou queue) ou boîte aux lettres (mailbox) est un outil
logiciel asynchrone permettant des communications voir synchronisations entre tâches. Il lui est
associé deux files d'attentes, une préservant l'ordre d'arrivée des messages et une seconde préservant
l'ordre d'arrivée des tâches. Les deux appellations sont très rencontrées. A titre indicatif, DSP/BIOS ou
SYS/BIOS le RTOS embarqué sur le DSP TMS320C6xxx de Texas Instruments étudié en deuxième année
parle de ''Mailbox'' contrairement à FreeRTOS qui parle de queue. Nous parlerons donc de préférence
de queues de messages durant cet enseignement.
Une queue de messages (ou file d'attente) contient un nombre fini d'éléments dont la taille est
configurable. Elle est de façon générale régit par le principe de fonctionnement d'une FIFO (First In
First Out). Le premier entré dans la file sera le premier à en sortir. Nous constaterons que FreeRTOS
propose si nécessaire des alternatives à ce fonctionnement. De plus, sachez que FreeRTOS créé les
queues de messages et alloue les ressources mémoire nécessaires dans le Tas.
Une queue de messages peut avoir plusieurs écrivains et plusieurs lecteurs. Cependant, de
façon générale, elles sont utilisées avec des écrivains multiples et un seul lecteur. Les écrivains sont les
tâches pouvant écrire dans une queue de messages. Les lecteurs sont donc celles susceptibles de la
lire. Un élément lu est retiré de la file d'attente. Si un lecteur cherche à lire une queue de messages
vide, il se fait bloquer jusqu'à l'arrivée d'un nouveau message. Pour des tâches de même priorité, ce
sera la première bloquée qui sera la première réveillée (FIFO). FreeRTOS étant un exécutif temps réel,
si une tâche de plus haute priorité se fait bloquer, elle passe en tête de file. Exemple de scénario :
3.1. API de FreeRTOS
Les deux principales fonctions proposées par FreeRTOS sont :
•
•
xQueueSend() ou xQueueSendToBack() : envoi d'une donnée vers une queue de messages à la
suite des éléments déjà présents. Cette fonction n'est bloquante que si la queue de messages
est pleine.
XQueueReceive() : Récupère la donnée en tête d'une queue de messages. L'élément lu est retiré
de la file d'attente. Le second élément passe alors en tête de file. Cette fonction n'est
bloquante que si la queue de messages est vide.
12
SYSTEMES TEMPS REEL
FreeRTOS
Illustrons maintenant ces concepts. Dans l'exemple ci-dessous le noyau travaille en mode
coopératif. Ce scénario présente une application avec deux écrivains et un lecteur :
...
13
SYSTEMES TEMPS REEL
FreeRTOS
FreeRTOS propose également des fonctions permettant de rendre prioritaire des données
envoyées à une queue de messages. Par exemple xQueueSendToFront() écrit une donnée en tête de
file d'attente.
3.2. Timeout
Certaines fonctions pour la gestion de queue de messages et de sémaphores utilisent un
Timeout ou temps mort. Lorsqu'une tâche est bloquée après l'appel de l'une de ces fonctions, celle-ci
se réveillera (passage à l'état prêt) automatiquement après un laps de temps nommé Timeout, même
si l'événement attendu n'est pas arrivé. L'utilisation du Timeout permet de garantir la robustesse d'un
code en forçant par exemple le réveil d'une tâche en attente d'un événement devant être envoyé par
une tâche boguée. Prenons l'exemple de l'API suivante :
portBASE_TYPE xQueueReceive(
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait
);
Le paramètre xTicksToWait permet de fixer le Timeout. Sous FreeRTOS, le Timeout est donné
en ticks. Si nous ne souhaitons pas utiliser cette fonctionnalité (Timeout infini), il suffit de passer en
paramètre la macro portMAX_DELAY (définie dans portmacro.h).
14
SYSTEMES TEMPS REEL
FreeRTOS
4. SEMAPHORES
Un Sémaphore est un outil logiciel couramment (mais pas seulement !) utilisé pour la
protection de ressources partagées (variables, périphériques, espaces mémoires ...). Le principe de
fonctionnement des sémaphores est très proche de celui des queues de messages. La preuve en est,
sous FreeRTOS vous ne trouverez aucun fichier source propre aux sémaphores. Les API pour la gestion
des sémaphores ne sont que des macros appelant des fonctions propres aux queues de messages
(queue.c).
4.1. Sémaphore binaire
Un sémaphore binaire peut-être vu comme une variable booléenne associée à une file
d'attente préservant l'ordre d'arrivée des tâches (cf. queues de messages). Un sémaphore binaire Pris
(Take) par une tâche ne peut plus l'être par une autre, ni même par elle même tant qu'il n'est pas
explicitement Vendu (Give). Une tâche cherchant à prendre un sémaphore binaire déjà pris se verra
bloquée (blocked) jusqu'à ce que la ressource soit relâchée. Prenons un exemple de scénario :
...etc
15
SYSTEMES TEMPS REEL
FreeRTOS
Le scénario précédent reflète par exemple une application où 3 tâches de même priorité
cherchent simultanément à envoyer des données via un UART. La ressource partagée est alors l'UART
qui est protégé par un sémaphore binaire. Durant ce laps de temps nous nous trouvons dans une
section critique qui peut cependant être préemptée par le noyau. Les différentes tâches utiliseront
donc l'UART à tour de rôle (exclusion mutuelle) .
4.2. MUTEX
Mutex signifie MUTual EXclusion (ou exclusion mutuelle), il s'agit du concept très rapidement
présenté durant le scénario présenté ci-dessus. Une exclusion mutuelle traduit la notion de protection
de ressources partagées. Sous FreeRTOS, elle peut notamment être réalisée à partir d'un sémaphore
binaire (inversion de priorité) ou d'un mutex (héritage de priorité). Pour FreeRTOS, le principe de
fonctionnement de ces deux outils est exactement le même à ceci près que le Mutex effectue un
héritage de priorité. Illustrons le concept d'héritage de priorité :
●
Héritage de priorité : La tâche Task Low2 vient de prendre une ressource (Mutex) et elle ne la
rendra qu'une fois avoir fini ce pourquoi elle l'a pris. Cependant la tâche Task Low1 est
également prête (état ready), l'ordonnanceur applique donc le round-robin et partage le temps
CPU entre les deux tâches de même priorité.
Imaginons maintenant que la tâche Task High (de priorité supérieure) cherche
également à prendre la ressource (Mutex). Ceci est impossible, le MUTEX ayant déjà été pris et
elle se fait donc bloquer, c'est ce que l'on appel l'inversion de priorité. Une tâche de haute
priorité se fait bloquer par une tâche de plus basse priorité, le système de priorité choisi par le
développeur est inversé. Cependant avec l'héritage de priorité, la tâche Task Low2 hérite
temporairement de la priorité de Task High et pourra donc potentiellement finir de s'exécuter
plus rapidement que sans héritage (plus de round-robin avec la tâche de même priorité). De
façon général, dans un système temps réel une tâche ne doit jamais (ou le moins longtemps
possible !) rester bloquée par une tâche de moindre priorité.
16
SYSTEMES TEMPS REEL
FreeRTOS
4.3. Sémaphore à compteur
Le principe de fonctionnement d'un sémaphore à compteur est identique à celui d'un
sémaphore binaire sauf que la ressource protégée peut maintenant être prise (Take) à plusieurs
reprise. Elle peut être prise soit par la même tâche, soit par une nouvelle. Exemple de sémaphore à 4
jetons :
17
SYSTEMES TEMPS REEL
TRAVAUX PRATIQUES
SYSTEMES TEMPS REEL
Travaux Pratiques
SOMMAIRE
1. MODE COOPERATIF - séance n°1
1.1. Interface de communication
1.2. Mode coopératif
1.3. État bloqué
1.4. Tâche idle
2. MODE PREEMPTIF ET GESTION MEMOIRE - séance n°2
2.1. Mode préemptif
2.2. Gestion mémoire
3. QUEUE DE MESSAGE ET SEMAPHORE - séance n°3
3.1. Queue de Message
3.2. Timeout
3.3. Section critique
3.4. Sémaphore
3.5. Bibliothèque UART avec appels système
4. PROJET - séance n°4 et plus
4.1. Présentation
4.2. Spécifications
APPLICATION RESEAU - facultatif
5. PILE RESEAU MICROCHIP
5.1. Présentation
5.1.a. Pile Réseau Microchip
5.1.b. Berkeley sockets
5.2. Serveur TCP
5.3. Services TCP/IP
5.3.a Serveur UDP
5.3.b Serveur HTTP
6. PROJET RTOS ET PILE RESEAU
7. PORTAGE PILE RESEAU MICROCHIP
7.3. Présentation
7.4. Spécifications
1
SYSTEMES TEMPS REEL
Travaux Pratiques
2
SYSTEMES TEMPS REEL
Travaux Pratiques
1. MODE COOPERATIF
Travail préparatoire
•
1. (0,5pt) Quels états peu prendre une tâche sous FreeRTOS ?
•
2. (1,5pts) Ces états sont-ils les mêmes quelque soit l'OS ou le RTOS utilisé ? Donner un
exemple pour un autre RTOS.
•
3. (0,5pt) Que se passe-t-il sous FreeRTOS lorsqu'aucune tâche, précédemment créée via
xTaskCreate(), ne s'exécute (état running) ?
•
4. (0,5pt) Qu'est-ce qu'un TCB ?
•
5. (1pt) Que trouve-t-on en général dans un TCB (prendre l'exemple de FreeRTOS) ?
3
SYSTEMES TEMPS REEL
Travaux Pratiques
1. MODE COOPERATIF
Travail en séance
1.1. Interface de communication
Avant de commencer à découvrir FreeRTOS, un micro-noyau temps réel libre et open source,
nous allons devoir mettre en place une interface de communication avec l'ordinateur de
développement (ordinateur host). Même à notre époque, l'une des premières interfaces mise en œuvre
lors de développement sur MCU est une communication série asynchrone via UART. La rapidité de mise
en œuvre, la simplicité du protocole et sa robustesse en font sa force. Cet exercice ayant déjà été
réalisé en première année à l'école sur architecture PIC18 de Microchip, le programme de
configuration et de gestion de l'UART sur architecture PIC32 de Microchip vous est donné. Il vous est
seulement demandé de l'éditer, d'en comprendre le fonctionnement tout en réalisant quelques tests
après compilation.
•
Créer un projet uart dans le répertoire rtos/peripherals/uart/explorer16/ ou
rtos/peripherals/uart/pic32maxiweb/ en fonction de la plateforme de développement utilisée.
Ce projet doit inclure les fichiers sources uart.c, main.c présents dans ./src/ et le fichier d'entête uart.h présent dans ./h/
•
Ouvrir un terminal asynchrone de communication côté ordinateur (TeraTerm, PuTTY, minicom,
kermit …) et s'assurer de sa bonne configuration afin d'analyser les données envoyées depuis la
maquette. Prenons l'exemple de TeraTerm :
•
Compiler et interpréter le fonctionnement du programme. Ne pas hésiter à modifier le fichier
main.c afin de bien comprendre le fonctionnement de l'API de gestion de l'UART.
•
Pour information, lors de développement sur processeurs pour l'embarqué, nous utilisons des
convertisseurs USB-serial afin de communiquer avec l'ordinateur de développement. Ces
interfaces nécessitent l'installation de drivers sous Windows et sont nativement reconnus sous
Linux en apparaissant à la racine dans le répertoire /dev sous le nom ttyXXXX (teletypewriter
terminal) :
4
SYSTEMES TEMPS REEL
Travaux Pratiques
1.2. mode coopératif
Nous allons maintenant découvrir pas à pas les principaux services proposés par FreeRTOS.
Dans un premier temps, intéressons-nous au mode coopératif, très peu utilisé pour des problèmes de
robustesse et d'égalité de partage de temps CPU, problèmes que nous illustrerons dès le premier
exercice. En mode coopératif, chaque tâche doit explicitement permettre à une autre tâche de
s'exécuter en effectuant un appel système, sans quoi, aucune autre tâche ne pourra prendre la main.
Nous allons dans un premier temps créer 3 tâches de même priorité. Chaque tâche enverra une chaîne
de caractères à l'ordinateur par liaison série.
•
Créer un projet cooperative dans le répertoire rtos/cooperative/pjct. Ce projet doit inclure les
fichiers sources uart.c, utask.c (user tasks), ktrap.c (kernel trap), main.c présents dans
rtos/cooperative/src et les fichiers d'en-tête uart.h, utask.h, FreeRTOSConfig.h présents dans
rtos/cooperative/h
•
Inclure à votre projet les sources de FreeRTOS sans oublier de configurer vos chaînes de
compilation C et ASM avec les chemins vers les différents répertoires contenant des fichiers
d'en-tête. Ne pas hésiter à s'aider des documents d'annexe.
•
La documentation du noyau est accessible en ligne sur le site officiel de la société proposant
FreeRTOS :
http://www.freertos.org/
5
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Créer 3 tâches de priorité 1. Les fonctions implémentant les tâches se nommeront task1(),
task2() et task3(). Chaque tâche ne fera qu'envoyer une chaîne de caractères à l'ordinateur puis
attendre un délai de quelques centaines de millisecondes via une temporisation logicielle avant
de répéter l'opération. Prenons l'exemple de la tâche 1 :
uartPutS("\r\ntask1\r\n");
// wait few 100ms
int i;
for (i=0; i<4000000; i++);
•
Compléter, compiler et interpréter le fonctionnement du programme en visualisant les données
reçues côté ordinateur.
Pour ce premier exercice, l'analyse du fonctionnement du programme est donnée. Ce travail
sera bien entendu à votre charge pour la totalité des exercices qui suivent :
Les traits pleins apparaissant sur les chronogrammes représentent le code en cours d'exécution
par le CPU. Nous constatons que nous sommes bloqués dans la tâche 3. Du coup, trois questions se
soulèvent :
•
Pourquoi sommes-nous bloqués ? tout simplement car en mode coopératif, la tâche doit
appeler elle même l'ordonnanceur ou une fonction système réalisant un appel du
scheduler.
•
Pourquoi dans la tâche 3 ? Avec FreeRTOS, au démarrage si plusieurs tâches de même
priorité sont susceptibles de prendre la main, c'est la dernière créée qui sera la
première à démarrer. Cette politique est différente en fonction de l'OS rencontré.
•
Dans quel état se trouvent les tâches 1 et 2 ? elles se trouvent à l'état prêt (ready). Elles
sont toutes les deux prêtes à prendre la main si la tâche 3 le leur donne.
6
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Nous allons maintenant forcer des commutations de contexte en appelant l'ordonnanceur.
Dans chaque tâche, à la suite de la temporisation logicielle, appelez la fonction suivante :
taskYIELD();
Vous constaterez que le noyau donne la main à chaque tâche à tour de rôle. Beaucoup d'OS
temps réel légers travaillent ainsi, il s'agit de la technique dîtes du round-robin qui suit le principe de
fonctionnement d'un tourniquet. Chaque tâches de même priorité prêtes ou en court d'exécution
prendra la main à tour de rôle. Le principal avantage de cette technique est de ne nécessiter qu'une
intelligence très réduite au niveau du code du kernel.
Nous venons d'illustrer le principe de la coopération entre tâches ainsi que le principal
problème amené si une boucle infinie (bug) intervient dans le code d'une tâche. Les tâches bloquées
ou prêtes ne peuvent plus prendre la main et votre application tombe !
•
Pour information, durant la totalité de la trame de TP, nous étudierons le fonctionnement de
nos programmes à l'aide de chronogrammes comme ci-dessus. Sachez néanmoins qu'il existe
des outils dédiés, souvent propriétaires et donc payant permettant ce type d'analyses. Prenez
quelques minutes pour visualiser la vidéo présentant les outils de trace proposés par FreeRTOS
(outils payant en fonctions des services demandés) :
http://www.youtube.com/watch?feature=player_embedded&v=WTNc1PwoMG4
7
SYSTEMES TEMPS REEL
Travaux Pratiques
1.3. État bloqué
Intéressons-nous à la fonction bloquante vTaskDelay(). Afin de pouvoir utiliser cette fonction,
ne pas oublier de mettre à 1 la macro INCLUDE_vTaskDelay présente dans le fichier d'en-tête
FreeRTOSConfig.h.
•
Compléter le programme précédent en remplaçant dans la tâche 1 la temporisation logicielle
et taskYIELD() par :
uartPutS("\r\ntask1 delay 3s\r\n");
// task blocked during 3000 ticks
vTaskDelay(3000);
•
Compléter le chronogramme ci-dessous en étant prudent aux quelques pièges pouvant
apparaître. Pour information, 3000 signifie 3000 ticks donc 3 secondes dans notre cas (1 tick =
1ms cf. FreeRTOSConfig.h). Préciser à chaque fois les appels des fonctions taskYIELD() et
vTaskDelay(3000) ainsi que l'état pris par chaque tâche (R = ready et B = blocked) :
•
Remplacer maintenant les temporisations logicielles et taskYIELD() par vTaskDelay(3000) dans
chaque tâche. Compléter le chronogramme suivant et préciser l'état pris par chaque tâche (R =
ready et B = blocked) :
•
Quel code s'exécute lorsque nous nous trouvons dans aucune des 3 tâches ?
8
SYSTEMES TEMPS REEL
Travaux Pratiques
1.4. Tâche idle
Intéressons-nous à la tâche Idle et découvrons comment la détourner afin d'y insérer du code
utilisateur. Par défaut en mode coopératif la tâche Idle ne fait que forcer des commutations de
contexte en appelant la fonction taskYIELD().
•
Mettre à 1 la macro configUSE_IDLE_HOOK présente dans le fichier d'en-tête
FreeRTOSConfig.h.
•
Créer alors une fonction (et non une tâche !) nommée vApplicationIdleHook(). Attention ce
nom est imposé par le système. Cette fonction ne fera qu'envoyer une chaîne de caractères :
/**
* @fn void vApplicationIdleHook( void )
* @brief function called by idle task
*/
void vApplicationIdleHook( void ){
uartPutS("i");
}
•
Tester votre code et compléter le chronogramme ci-dessous :
•
Proposer des cas d'applications et exemples d'utilisation de la tâche Idle. Ne pas hésiter à
s'aider du web et d'exemples de détournement de la tâche Idle sur d'autres noyaux temps réel.
9
SYSTEMES TEMPS REEL
Travaux Pratiques
10
SYSTEMES TEMPS REEL
Travaux Pratiques
2. MODE PREEMPTIF ET GESTION MEMOIRE
Travail préparatoire
•
1. (0,5pt) Quels sont les inconvénients et avantages d'un OS coopératif (aidez-vous du Web) ?
•
2. (0,5pt) Quels sont les inconvénients et avantages d'un OS préemptif (aidez-vous du Web) ?
•
3. (1pt) Que trouve-t-on sur le Tas ?
•
4. (1,5pt) Les stratégies de gestion du tas par FreeRTOS sont implémentées dans les fichiers
heap_1.c, heap_2.c, heap_3.c et heap_4.c présents dans le répertoire
/rtos/FreeRTOS/Source/portable/MemMang de notre arborescence de TP.
•
•
Quelles sont les différences entre les stratégies utilisant heap_1.c ou heap_2.c ?
Quelles sont les différences entre les stratégies utilisant heap_2.c ou heap_3.c ?
•
5. (1pt) Qu'est-ce qu'une pile ou stack et que trouve-t-on sur la pile ?
•
6. (1,5pt) Qu'elle est la taille par défaut de la pile de la tâche Idle dans le cadre de notre trame
de TP ? Expliquez votre démarche pour répondre à cette question.
11
SYSTEMES TEMPS REEL
Travaux Pratiques
2. MODE PREEMPTIF ET GESTION MEMOIRE
Travail en séance
2.1. mode préemptif
A partir de maintenant et jusqu'à la fin de la trame de TP nous travaillerons exclusivement en
mode préemptif (macro configUSE_PREMPTION à 1 dans le fichier FreeRTOSConfig.h). En mode
préemptif, le scheduler prend périodiquement la main, interrompant ainsi une tâche en cours
d'exécution, puis force un ré-ordonnancement. Cette périodicité se nomme tick (timer clock) et est
configurable sous FreeRTOS à travers la macro configTICK_RATE_HZ présente dans FreeRTOSConfig.h.
•
Créer un projet preemptive dans le répertoire rtos/preemptive/pjct. Ce projet doit inclure les
fichiers sources uart.c, utask.c (user tasks), ktrap.c (kernel trap), main.c présents dans
rtos/preemptive/src et les fichiers d'en-tête uart.h, utask.h, FreeRTOSConfig.h présent dans
rtos/preemptive/h
•
Inclure à votre projet les sources de FreeRTOS sans oublier de configurer vos chaînes de
compilation C et ASM avec les chemins vers les différents répertoires contenant des fichiers
d'en-tête. Ne pas hésiter à s'aider des documents d'annexe.
•
Créer 3 tâches, une de priorité 2 et deux de priorité 1. Les fonctions implémentant les tâches
se nommeront respectivement task1(), task2() et task3(). Chaque tâche appellera une fonction
bloquante puis ne fera qu'envoyer une chaîne de caractères à l'ordinateur.
•
Prenons l'exemple des tâche 2 et 3 (exemple de la tâche 2) :
// task blocked during 1000 ticks
uartPutS("\r\ntask2\r\n");
vTaskDelay(1000);
•
La tâche 1 sera bloquée quant-à elle durant 3000 ticks :
// task blocked during 3000 ticks
uartPutS("\r\ntask1\r\n");
vTaskDelay(3000);
12
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Compléter, compiler et interpréter le fonctionnement du programme en visualisant les données
reçues côté ordinateur. Le comportement du programme peut sembler étrange dans un
premier temps, mais s'explique bien entendu très bien !
•
Compléter le chronogramme suivant et préciser l'état pris par chaque tâche (R = ready et B =
blocked). Attention aux pièges :
•
Dans notre cas, la tâche 1 est-elle périodique ?
•
De quoi dépend la périodicité d'une tâche appelant la fonction vTaskDelay()?
•
FreeRTOS propose la fonction xTaskGetTickCount() permettant de récupérer la valeur courante
du tick. Récupérer à chaque réveil de la tâche 1 cette valeur, puis l'envoyer à l'ordinateur
(s'aider de la fonction standard sprintf) :
uartPutS("\r\ntask1 prio, current tick value ");
// get and send current tick value
…
uartPutS("\r\n");
•
Utiliser maintenant la fonction vTaskDelayUntil() puis interpréter le résultat obtenu.
13
SYSTEMES TEMPS REEL
Travaux Pratiques
2.2. Gestion mémoire
La partie qui suit est extrêmement importante et sujette à énormément de bugs et mauvais
développement en milieu industriel, notamment lorsque nous travaillons sur de petits exécutifs temps
réel comme FreeRTOS. Problématique différente sur OS évolué (GNU\Linux, Android …) et processeur
avec MMU (Memory Managment Unit). Sur RTOS, le développeur doit avoir une très bonne gestion et
maîtrise des ressources mémoire utilisées par la chaîne de compilation et le système. Prenons un petit
programme d'exemple et observons le mapping mémoire de données du processeur.
int gbl;
/**
* @fn int main(void)
*/
void main(void){
int lclMain;
xTaskCreate(task, "task", 100, NULL, 1, NULL);
vTaskStartScheduler();
}
/**
* @fn void task(void *pvParameters)
*/
void task(void *pvParameters){
int lclTask;
}
•
Représenter ci-contre un découpage du mapping mémoire en
faisant apparaître : tas de FreeRTOS, pile fonction main, pile de
la tâche, TCB de la tâche
•
Que trouve-t-on dans le reste de la mémoire (hors zones
spécifiées dans la question précédente) ?
•
Où si situe physiquement la chaîne de caractères ''task'' ?
Mettre à jour le schéma ci-contre.
•
Où si situe physiquement la variable globale gbl ? Mettre à jour
le schéma ci-contre.
•
Où si situe physiquement la variable locale lclMain ? Mettre à
jour le schéma ci-contre.
•
Où si situe physiquement la variable locale lclTask ? Mettre à
jour le schéma ci-contre.
14
SYSTEMES TEMPS REEL
Travaux Pratiques
FreeRTOS propose une API de programmation permettant de détecter certaines exceptions du
noyau et d'appeler des fonctions de callback en cas d’occurrence. Le kernel permet notamment de
détecter certains stack overflow (débordement de pile) et heap overflow. Pour information, les stack
overflow font partis des bugs les plus répandus durant des développement sur STR et peuvent être
délicats à mettre au jour dans certains cas. Il vous est très très très fortement conseillé d'implémenter
ce type de détection durant vos phases de développement. Malheureusement, lorsque vous vous
trouvez dans l'une de ces fonctions de callback, il est déjà trop tard !
•
Nous allons maintenant mettre en place les mécanismes de détection de débordement de pile.
Forcer à 1 ou 2 la macro configCHECK_FOR_STACK_OVERFLOW présente dans le fichier
FreeRTOSConfig.h. En fonction de la valeur choisie différentes stratégies de détection de stack
overflow seront appliquées par le kernel :
http://www.freertos.org/Stacks-and-stack-overflow-checking.html
•
Dé-commenter la fonction vApplicationStackOverflowHook présente dans le fichier ktrap.c
(kernel trap).
/**
* @fn void vApplicationStackOverflowHook ...
* @brief kernel hook here if stack overflow detection
*/
void vApplicationStackOverflowHook( xTaskHandle xTask, signed char *pcTaskName ){
uartPutS("\r\nerror stack overflow by ");
uartPutS(pcTaskName);
uartPutS(" \r\n");
while(1); // it's a trap
}
•
Éditer la fonction suivante et l'appeler dans la tâche 1. Interpréter et illustrer le comportement
du programme sur le schéma ci-contre :
/**
* @fn void growStack( void )
* @brief current stack allocation until overflow
*/
void growStack( void ) {
vTaskDelay( 1 );
return growStack();
}
•
Effectuer le même exercice avec la fonction suivante. Interpréter et
illustrer le comportement du programme sur le schéma ci-contre :
void growStack( void ) {
return growStack();
}
15
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Nous allons maintenant mettre en place les mécanismes de détection de débordement de tas.
Forcer à 1 la macro configUSE_MALLOC_FAILED_HOOK présente dans le fichier
FreeRTOSConfig.h. Dé-commenter la fonction vApplicationMallocFailedHook dans le fichier
ktrap.c (kernel trap).
/**
* @fn vApplicationMallocFailedHook()
* @brief kernel hook here if malloc allocation failed detection
*/
void vApplicationMallocFailedHook( void ){
uartPutS("\r\nerror malloc failed \r\n");
// it's a trap
while(1);
}
•
Éditer le code suivant dans la tâche 1. Nous allons créer une tâche depuis
la tâche 1 dont la pile dépasse la taille du tas. Interpréter et illustrer le
comportement du programme sur le schéma ci-contre :
// force heap allocation failed
uartPutS("\r\ntask creation with a too large stack\r\n");
xTaskCreate(task1, "mfailed",
configTOTAL_HEAP_SIZE,
NULL,
tskIDLE_PRIORITY + 2,
NULL);
16
SYSTEMES TEMPS REEL
Travaux Pratiques
17
SYSTEMES TEMPS REEL
Travaux Pratiques
3. QUEUE DE MESSAGE ET SEMAPHORE
Travail préparatoire
•
1. (2pts) Exemple de communication entre 3 tâches via queue de messages :
void prodTask1( void *pvParam ){
int x=1;
void prodTask2( void *pvParam ){
int x=2;
while(1){
if (xQueueSend(xQueue, \
&x, 0) == pdTRUE ){
x += 2;
vTaskDelay( 1 );
}
}
while(1){
if (xQueueSend(xQueue, \
&x, 0) == pdTRUE ){
x += 2;
vTaskDelay( 1 );
}
}
}
}
void consTask( void *pvParam ){
int y;
char sBuffer[20];
while(1){
xQueueReceive(xQueue, &y, \
portMAX_DELAY );
sprintf(sBuffer, ''%d'', y);
uartPuts(sBuffer) ;
}
}
L'application présentée ci-dessus met en œuvre 2 tâches producteur de priorité 1
(prodTask1() et prodTask2()) et une tâche consommateur de priorité 2 (consTask()) sachant que
le kernel fonctionne en mode préemptif. Représenter la séquence d'exécution des tâches à
partir du démarrage de l'application. Notez bien sur le chronogramme les instants clés ainsi que
les appels de fonction associés (R = ready et B = blocked) :
•
2. (0,5pt) Qu'observerait-on au niveau d'un terminal asynchrone côté ordinateur ?
18
SYSTEMES TEMPS REEL
Travaux Pratiques
•
3. (1,5pt) En utilisant 3 sémaphores binaires, proposez une solution permettant de chaîner
l'exécution de 3 tâches en respectant l'ordre suivant : task3, task1, task2, task3, task1, task2,
task3 ...
void task1( void *pvParam ){
void task2( void *pvParam ){
while(1){
}
}
void task3( void *pvParam ){
while(1){
}
}
while(1){
}
}
•
4. (0,5pt) Comment faire pour chaîner 6 tâches ?
•
5. (1,5pt) Compléter le chronogramme ci-dessous de la totalité des prises et ventes de
sémaphores sachant qu'avant le démarrage de l'ordonnanceur le sémaphore 3 (S3) a été vendu
une fois (Give) et les deux autres ont déjà été pris (Take). Ne pas oublier de représenter les
états de chaque tâche (R = ready et B = blocked) :
19
SYSTEMES TEMPS REEL
Travaux Pratiques
3. QUEUE DE MESSAGE ET SEMAPHORE
Travail en séance
3.1. Queue de message
Intéressons-nous maintenant aux outils de communication, synchronisation et protection
proposés par FreeRTOS. Dans cette partie nous allons nous attarder sur les files d'attente (ou queue de
message ou boîte aux lettres ou mail boxes selon le système utilisé) ainsi que sur les sémaphores, qui
ne sont qu'un dérivé sans message des files d'attente. D'ailleurs, sous FreeRTOS il n'existe aucun fichier
source propre aux sémaphores. Les sémaphores sont implémentés et utilisent les mêmes sources que
les files d'attente, seul un fichier d'en-tête existe permettant de wrapper l'API de gestion des
sémaphores vers celle pour le queue (simple skin).
•
Créer un projet queue dans le répertoire rtos/queue/pjct. Ce projet doit inclure les fichiers
sources uart.c, utask.c (user tasks), ktrap.c (kernel trap), main.c présents dans rtos/queue/src et
les fichiers d'en-tête uart.h, utask.h, FreeRTOSConfig.h présent dans rtos/queue/h
•
Inclure à votre projet les sources de FreeRTOS sans oublier de configurer vos chaînes de
compilation C et ASM avec les chemins vers les différents répertoires contenant des fichiers
d'en-tête. Ne pas hésiter à s'aider des documents d'annexe.
•
Créer 3 tâches, la première, périodique de priorité 2 et les deux autres de priorité 1. Les
fonctions implémentant les tâches se nommeront respectivement task1(), task2() et task3().
Chaque tâche ne fera qu'envoyer une chaîne de caractères à l'ordinateur puis se bloquer.
•
La tâche 3 ne fera qu'envoyer un chaîne de caractères côté ordinateur puis se bloquera
durant 1s :
// task blocked during 1000 ticks
vTaskDelay(1000);
uartPutS("\r\ntask3\r\n");
•
La tâche 1 devra être périodique avec un périodicité de 5s. Elle devra récupérer la valeur
courante du tick (sans l'envoyer à l'ordinateur) puis la postera dans une queue de
message pour la tâche 2. La fonction d'écriture dans la file d'attente ne devra pas être
bloquante.
// block periodically current task during 5000 ticks
vTaskDelayUntil( … );
uartPutS("\r\ntask1 prio, message queue post for task2\r\n");
// post current tick value by message queue
// ...
20
SYSTEMES TEMPS REEL
Travaux Pratiques
•
La tâche 2 devra être synchronisée avec la tâche 1 et récupérera la valeur courante du
tick avant de la renvoyer à l'ordinateur via liaison série. Le reste du temps, cette fonction
devra rester bloqué.
// ...
uartPutS(''\r\ntask2, current tick value '');
// print current tick value ...
uartPutS(''\r\n'');
•
Ne pas oublier de créer un queue de message. A vous de fixer la taille de la file d'attente
ainsi que la taille de chaque élément.
•
Compléter, compiler et interpréter le fonctionnement du programme en visualisant les données
reçues côté ordinateur.
•
Compléter le chronogramme ci-dessous (R = ready et B = blocked). Attention aux pièges :
•
Quel est la période d'exécution de la tâche 2 ?
Nous venons ici d'illustrer deux concepts importants dans le domaine des systèmes d'exploitation,
la synchronisation et la communication. La synchronisation permet notamment de synchroniser
l'exécution d'une tâche (par exemple implémentant un traitement long) suite à l'exécution d'une autre
tâche ou d'une ISR (traitement toujours court). La communication inter-tâche consiste quant-à-elle en
la capacité d’effectuer des échanges sécurisés d'informations entre différentes tâches de l'application.
Notion de partage d'information dans des cas d'usage (use case) avec plusieurs écrivains et/ou
plusieurs lecteurs. Un troisième concept important, est l'exclusion mutuelle.
21
SYSTEMES TEMPS REEL
Travaux Pratiques
3.2. Timeout
Certaines fonctions pour la gestion de queue de messages ou de sémaphores utilisent un
Timeout. La notion de timeout ne s'applique qu'à des appels système bloquant. Lorsqu'une tâche est
bloquée, celle-ci se réveillera (passage à l'état prêt) automatiquement après un laps de temps nommé
Timeout, même si l'événement attendu n'est pas arrivé (libération de sémaphore, écriture dans une
file d'attente ...).
•
Forcer le réveil de la tâche 2 toutes les secondes en utilisant le Timeout associé à la fonction
xQueueReceive(). Après avoir testé la nature du réveil de la tâche, envoyer l'une des deux
chaînes de caractères suivantes :
•
Réveil par lecture du message présent dans la queue :
uartPutS(''\r\ntask2, current tick value '');
// print current tick value ...
uartPutS(''\r\n'');
•
Réveil par timeout :
uartPutS(''\r\ntask2, timeout'');
•
Que se passe-t-il si nous forçons à 0 le Timeout d'une fonction bloquante ?
•
Que se passe-t-il si nous forçons à portMAX_DELAY le Timeout d'une fonction bloquante (ainsi
que la macro INCLUDE_vTaskSuspend à 1) ? À quelle valeur théorique de timeout correspond
cet argument ?
•
Peut-on trouver un timeout sur une fonction système non bloquante ?
22
SYSTEMES TEMPS REEL
Travaux Pratiques
3.3. Section Critique
Une section critique est une région de code pour laquelle nous devons garantir sa bonne
exécution et l'intégrité des données à sa sortie. Il s'agira le plus souvent de protéger une ressource
partagée (variable globale, accès à un périphérique …). Une section critique peut-être protégée par
différents outils système :
•
•
•
Sémaphores
Mutex (mutual exclusion)
fonctions dédiées le plus souvent par masquage d'interruption
Vous avez normalement dû constater que les tâches 2 et 3 étant de même priorité, elles se
partagent à tour de rôle l'accès à l'UART. Nous allons donc protéger l'accès à ce périphérique. Cela
signifie qu'une tâche ayant pris cette ressource matérielle la gardera jusqu'à-ce qu'elle ait fini le
traitement en cours.
•
Pour les tâches 2 et 3, placer la fonction d'envoi de données à l'ordinateur dans une section
critique. Utiliser pour cela les macros taskENTER_CRITICAL() et taskEXIT_CRITICAL().
•
Compléter le chronogramme suivant et préciser l'état pris par chaque tâche (R = ready et B =
blocked). Attention aux pièges :
•
Cette implémentation des sections critiques par FreeRTOS est assez dangereuse, notamment
dans l'exemple actuellement présenté, vu que les Ticks (générés par timer matériel) ne sont
plus vus de l'ordonnanceur pendant la durée d'exécution de la section (section longue en
temps d'exécution). Quel problème cela peut-il poser ?
Nous découvrirons par la suite une solution à base de sémaphores plus douce et surtout
interruptible par le kernel. Attention, une section critique doit-être la plus courte possible par principe.
Ne pas oublier qu'elle peut potentiellement être partagée avec d'autres tâches voir ISR's. Les solutions
utilisant le principe de masquage d'interruption sont donc à utiliser pour des sections de code très
courtes en temps d'exécution.
23
SYSTEMES TEMPS REEL
Travaux Pratiques
3.4. Sémaphore
Durant l'exercice précédent vous avez été amené à manipuler des sections critiques en utilisant
les fonctions taskENTER_CRITICAL() et taskEXIT_CRITICAL(). Cependant sous FreeRTOS, ces deux
fonctions sont à manier avec précaution car une région critique ainsi créée ne peut plus être
préemptée par le noyau, ni par les interruptions matérielles en dessous d'un certain niveau de priorité
système (section non interruptible). Ceci peut donc devenir très dangereux en cas de mauvaise
programmation et en fonction de la criticité de l'application. A l'aide de sémaphores, nous allons créer
des sections critiques pouvant être préempté par le système et également interrompues par les
périphériques matériels.
Dans l'exercice qui suit, nous allons réécrire en partie la bibliothèque C de gestion de l'UART et
donc modifier le fichier source uart.c ainsi que le fichier d'en-tête uart.h.
•
retirer les sections critiques précédemment insérées
•
copier les fichiers uart.c et uart.h dans le répertoire du projet actuel et renommer
respectivement les fichiers kuart.c et kuart.h (kernel uart). Modifier dans un premier temps les
sources voire la configuration de la chaîne de compilation afin d'assurer une bonne compilation
du projet modifié.
•
Créer un sémaphore binaire en choisissant un nom adapté au travail en cours puis modifier le
code source de la fonction uartPutS afin de s'assurer qu'une seule tâche à la fois puisse prendre
en main l'UART en transmission.
•
Interpréter le fonctionnement du programme puis compléter le chronogramme suivant en
précisant l'état pris par chaque tâche (R = ready et B = blocked) :
•
Sous FreeRTOS, qu'elle différence existe-t-il entre un sémaphore binaire et un mutex ?
•
Dans notre cas, est-il plus intéressant d'utiliser un sémaphore binaire ou un mutex ? Justifier
votre réponse puis modifier si nécessaire le programme.
24
SYSTEMES TEMPS REEL
Travaux Pratiques
3.5. Bibliothèque UART avec appels système
Nous allons, dans cet ultime exercice, modifier plus profondément les sources de la librairie de
gestion du module UART. Le but étant d'obtenir une bibliothèque optimisée pour travailler avec
FreeRTOS (pas en vitesse d'exécution mais en robustesse et efficacité).
A titre indicatif, beaucoup d'applications font cohabiter bibliothèque réseau (ou stack réseau) et
système d'exploitation. Microchip propose par exemple une librairie réseau libre et open source
indépendante de tout OS, qui n'est donc pas optimisée pour travailler avec notre kernel. FreeRTOS
propose en revanche une librairie réseau implémentant des appels système qui est donc optimisée
pour cohabiter avec le noyau, néanmoins cette stack est un outil propriétaire. Observons rapidement
le coût de certains de ces services en 2014 :
•
Supprimer dans les fichiers /peripherals/kuart/<board-selection>/src/kuart.c et
/peripherals/kuart/<board-selection>/h/kuart.h toute référence à l'utilisation du buffer circulaire
permettant l'échange d'information entre l'ISR de réception de l'UART et la fonction uartGetC.
•
Modifier l'ISR ainsi que la fonction uartGetC en synchronisant par queue de message les réveils
de la fonction d'interruption avec l'appel de la fonction uartGetC. Chaque caractère reçu sera
posté dans la file d'attente et la fonction de réception de caractères implémentera donc un
appel système bloquant en vidant cette queue de message.
•
Une fois ce travail réalisé, modifier le code de la tâche 3 de façon à réceptionner puis renvoyer
des chaînes de caractères envoyées depuis l'ordinateur. S'assurer du bon fonctionnement du
programme. Ultime test, envoyer un fichier texte depuis l'ordinateur et s'assurer de sa bonne
réception et renvoi par l'application embarquée. Le fichier est présent dans le répertoire
rtos/queue/rxfile.txt. Sous TeraTerm, aller dans Fichier → Envoyer un fichier...
// received string
uartGetS(strTmp);
// echo of received string
uartPutS("\r\ntask3, received message : ");
uartPutS(strTmp);
uartPutS("\r\n");
25
SYSTEMES TEMPS REEL
Travaux Pratiques
26
SYSTEMES TEMPS REEL
Travaux Pratiques
4. PROJET
Travail préparatoire
•
1. (0,5pt) Sachant que le capteur de température utilisé est un TC1047, si en sortie du capteur
je mesure 0,85V, quelle température fait-il approximativement au niveau du boîtier ?
•
2. (0,5pt) Sachant que la plage d'acquisition analogique d'entrée de l'ADC 10bits dans le cadre
de notre application est comprise entre Vref+ - Vref- = VCC - 0 = 3,3V, quelle serait la valeur
numérique précédemment obtenue après conversion ?
•
3. (3pts) Comme dans la plupart des projets industriels utilisant des systèmes temps réel, les
premières ébauches de l'environnement multitâches de l'application sont réalisées par
l'architecte système logiciel sur un bout de papier.
En utilisant le formalisme imposé ci-dessous, représenter graphiquement
l'environnement logiciel de votre application ainsi que les différents mécanismes de
synchronisation, communication et protection mis en œuvre.
A titre indicatif, vous pouvez trouver un exemple de réalisation de projet industriel et
d'environnement multitâches proposé par FreeRTOS à partir de leur solution (Real Time Application
Design Tutorial). Le projet est présenté sous plusieurs angles, solution pleinement préemptive,
optimisée au regard de l'usage de la RAM ...
http://www.freertos.org/tutorial/index.html
27
SYSTEMES TEMPS REEL
Travaux Pratiques
4. PROJET
Travail en séance
4.1. Présentation
Il vous est demandé durant ce projet de mettre en place une plateforme d'acquisition de
température communicante avec un ordinateur muni d'un terminal asynchrone de communication. Le
capteur de température instrumenté est un TC1047 présent sur la maquette de développement
explorer 16.
•
Capteur de température
Le capteur de température TC1047 est physiquement connecté à la broche AN4 du MCU qui est
elle même mappée vers une entrée de l'ADC interne du processeur. Nous nous trouvons actuellement
dans un enseignement de découverte et mise œuvre de système temps réel, le travail n'est donc pas
de passer du temps sur la configuration et la gestion de périphérique interne. La gestion du module
ADC sur MCU Microchip a d'ailleurs déjà été vue en première année à l'école. Vous trouverez donc une
librairie C permettant par défaut de lire les valeurs converties présentent sur la broche AN4 du
microcontrôleur. Cette librairie se trouve dans le répertoire rtos/peripherals/adc/<board-selection>/,
charge à vous de la prendre en main.
Si vous le souhaitez durant vos phases de développement, un potentiomètre est également
connecté à la broche AN2 du MCU. Par simple modification des sources de la librairie ADC (cf. librairie)
vous pouvez également effectuer des acquisitions sur cette broche.
28
SYSTEMES TEMPS REEL
Travaux Pratiques
Observons maintenant la réponse tension/température du capteur TC1047. Le schéma
présenté ci-dessous est directement issu de la documentation technique du capteur.
•
Analog to Digital Converter
Le convertisseur analogique numérique de donnée interne aux PIC32MX de Microchip est un
ADC 10bits. Cela implique une plage numérique de conversion comprise entre 0 et 1023 (format entier
non signé). La plage analogique de conversion a quant-à-elle été fixée après configuration entre 0 et
3,3V.
•
Reset logiciel
Afin d'effectuer un reset logiciel du MCU, il vous suffit d'appeler la fonction standard SoftReset()
proposée avec la chaîne de compilation.
•
Light Emitting Diode
La maquette de développement explorer 16 propose 8 LED's utilisateur. Utiliser la LED de
gauche physiquement reliée à la broche RA7 du MCU pour répondre au cahier des charges.
29
SYSTEMES TEMPS REEL
Travaux Pratiques
4.2. Spécifications
L'application devra être multitâches et réalisera les traitements suivants. Création du projet
dans le répertoire rtos/tempserver :
•
une LED servira d'interface utilisateur. Si la température courante est supérieure à 38°C,
la LED restera allumée. Si la température est inférieure, la LED devra clignoter avec une
périodicité de 0,5s.
•
L'application devra réaliser des acquisitions toutes les 20ms et devra toujours garder en
mémoire les 100 dernières mesures converties.
•
L'application devra proposer un interpréteur de commande (console) accessible de tout
ordinateur muni d'un terminal asynchrone (TeraTerm, PuTTY, GTKTerm, minicom,
kermit ...). Cet interpréteur sera relativement simple et ne proposera à l'opérateur qu'un
jeu de 6 commandes :
•
read : retourne la dernière valeur convertie
•
dump : retourne les 100 dernières valeurs converties
•
stream : retourne toutes les 20ms la dernière valeur convertie en effaçant côté
terminal la précédente valeur affichée. Il suffira d'appuyer sur la touche
<Entrée> côté ordinateur pour quitter le mode stream
•
reset : force un reset logiciel de l'application
•
baudrate : modifie le débit de la liaison série pour la communication avec
l'ordinateur
•
help : retourne le menu des commandes supportées par l'application
Au démarrage de l'application, le programme devra proposer l'interface cidessous. L'application sera alors en attente de saisie d'une commande par l'opérateur.
30
SYSTEMES TEMPS REEL
Travaux Pratiques
Exemple d'échanges avec l'application :
•
A partir de maintenant, débrouillez-vous et bon courage !
31
SYSTEMES TEMPS REEL
Travaux Pratiques
32
SYSTEMES TEMPS REEL
Travaux Pratiques
5. PILE RESEAU MICROCHIP - facultatif
5.1. Présentation
Dans cette ultime partie facultative (uniquement pour les curieux et votre CV), nous allons nous
intéresser aux outils et piles réseaux proposées pour le monde des systèmes embarqués. Plusieurs
types de solutions logicielles sont proposés à notre époque sur le marché (fin 2013).
•
Fondeurs (NXP, STmicro ...) ne développant pas de solutions logicielles et passant de des
sociétés tierces (Keil, IAR …). Ces sociétés assurent un support technique sur leurs outils de
développement et bibliothèques fournies. Ces outils sont souvent très onéreux.
•
Fondeurs (Microchip, Texas Instruments, Stmicro, NXP … ) développant et proposant des
solutions logicielles à des prix plus ou moins attractifs (IDE, pile réseaux, pile USB, pile
graphique ...).
•
Solutions libres, le plus souvent open sources, pouvant être multiplateformes. Le support se fait
en ligne par forum interposés, le plus souvent par la communauté d'utilisateurs voir par les
équipes de développeurs et mainteneurs.
Prenons l'exemple de deux sociétés de développement de suites logicielles très rencontrées
dans le domaine de l'embarqué, Keil et IAR (développement sur MCU's, SoC's, DSP's … notamment sur
architectures ARM). Illustrons les services proposés par Keil, notamment le contenu de la librairie
réseau (TCP/IP Networking Suite) :
33
SYSTEMES TEMPS REEL
Travaux Pratiques
A titre indicatif, une grande partie des services proposés sont payants et très coûteux. Une
version complète, au tarif éducation, n'incluant pas les sources des librairies (sources payants) mais
seulement les binaires, avoisine un coût de ~4000-5000€ par poste. Compter ~12000€ afin d'obtenir
une bibliothèque réseau avec fichiers sources. Des tarifs légèrement plus attractifs sont néanmoins
possibles via des licences à jeton.
5.1.a Pile réseau Microchip
Nous allons quant à nous utiliser les outils Microchip (IDE, chaîne de compilation, pile
réseau ...), développés par Microchip, outils gratuits, libres (sous quelques conditions) et open sources.
Il faut savoir que cette stratégie reste très peu rencontrée dans le domaine de l'embarqué, même si de
plus en plus de fondeurs franchissent le pas. Observons par exemple les services proposés par la
bibliothèque réseau Microchip :
Une bibliothèque ou stack réseau propose au développeur une API de fonctions implémentant
les différents services de chaque couche réseau. Cette pile réseau est supportée par grand nombre de
processeurs Microchip (PIC18, PIC24, dsPIC, PIC32) et l'empreinte mémoire de la pile complète
avoisine sur PIC32 les 90Ko, contre 15Ko pour la couche TCP et couches inférieures. Néanmoins, les
couches basses de la pile nécessitent une communication entre matériel et couches logicielles
supérieures (drivers). La stack Microchip ne propose que le support de quelques contrôleurs PHY
externes (implémentent la couche physique) ou contrôleurs Ethernet MAC/PHY externes
(implémentent les couches MAC et physique).
34
SYSTEMES TEMPS REEL
Travaux Pratiques
Prenons l'exemple de notre maquette de développement (PIC32 MAXI WEB de Olimex) :
5.1.b Berkeley sockets
Berkeley sockets ou BSD sockets est une API de programmation C (supportée par d'autres
langages) implémentant des sockets internet et sockets UNIX. De même, cette API très standard reste
très proche de l'API POSIX. Il faut savoir que Microchip propose une API propriétaire (syntaxe et
fonctionnement propre à Microchip), mais supporte également l'API BSD. Il s'agit en fait d'une
redirection d'une API BSD vers leur librairie. Observerons le descriptif succinct des fonctions BSD
proposées :
•
accept : accepte les requêtes de connexion et les place en file d'attente pour une socket en
cours d'écoute.
•
Bind : assigne un numéro de port client ou serveur à une socket
•
closesocket : ferme une socket existante
•
connect : permet à un client de se connecter à un serveur en mode TCP
•
gethostname : retourne le nom du host au système
•
listen : écoute une socket ciblée
•
recv : réception de données ayant été mises en file d'attente pour une socket
•
recvfrom : réception des données ayant été mises en file d'attente pour une socket
•
send : envoie de données vers une socket actuellement connectée
•
socket : création d'une nouvelle socket BSD
35
SYSTEMES TEMPS REEL
Travaux Pratiques
Observons un exemple de machine d'état présentant la connexion entre un client (client TCP
par exemple) et un serveur.
36
SYSTEMES TEMPS REEL
Travaux Pratiques
5.2. Serveur TCP
Nous allons maintenant embarqué, dans notre MCU, une partie des services proposés par la
stack réseau de Microchip et mettre en œuvre un serveur TCP. Le client se trouvera quant-à-lui côté
ordinateur. Les premières phases de validation se feront via Telnet, afin de s'assurer du bon
fonctionnement de notre serveur.
•
Observons rapidement l'arborescence du projet :
•
Créer un projet tcp dans le répertoire network/tcp/pjct. Ce projet doit inclure les fichiers
sources uart.c, main.c, tcp.c. Concernant l'UART, utiliser les fichiers présents dans
../peripherals/uart/<board-selection>/src et le fichier d'en-tête uart.h présent dans
../peripherals/uart/<board-selection>/h
•
Ajouter maintenant à votre projet les sources strictement nécessaires de la pile réseau
Microchip. Pour réaliser cet exercice, loin d'être trivial, s'aider de la documentation technique
de la pile (network/Microchip/Help/TCPIP Stack Help) dans la rubrique Using the stack –
Required files.
37
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Lister ci-dessous le système de fichiers minimal permettant une première implémentation et
compilation de la pile réseau de Microchip :
•
Compléter maintenant le fichier main.c puis s'assurer de la bonne compilation du projet.
S'aider de la documentation technique de la pile (TCPIP Stack Help) dans la rubrique Using the
stack – Main File.
•
Lister ci-dessous les appels de fonctions nécessaires à une bonne configuration et utilisation de
la pile :
38
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Spécifier le travail réalisé par chacune de ces fonctions. Ne pas hésiter à ouvrir les sources de
chaque fonction afin d'en démystifier leur travail !
•
Compiler votre projet et appeler cycliquement la pile. Néanmoins, elle n'implémente pour le
moment aucun service. Nous allons rajouter les services TCP, ICMP (afin de pouvoir réaliser des
ping depuis l'ordinateur) ainsi que l'utilisation de l'API BSD. Pour ce faire, à chaque famille de
contrôleurs réseau supporté par la librairie correspond un fichier d'en-tête de configuration
utilisé pour savoir quels services rajouter à la compilation. Celui nous concernant est
network/Microchip/Configs/TCPIP ETH795.h. Étudions son contenu :
/* Application Level Module Selection
* Uncomment or comment the following lines to enable or
* disabled the following high-level application modules.
*/
//#define STACK_USE_UART
// Application demo using UART for IP address ...
//#define STACK_USE_UART2TCP_BRIDGE
// UART to TCP Bridge application example
//#define STACK_USE_IP_GLEANING
//#define STACK_USE_ICMP_SERVER
// Ping query and response capability
//#define STACK_USE_ICMP_CLIENT
// Ping transmission capability
//#define STACK_USE_HTTP2_SERVER
// New HTTP server with POST, Cookies, Authenticati...
//#define STACK_USE_SSL_SERVER
// SSL server socket support (Requires SW300052)
//#define STACK_USE_SSL_CLIENT
// SSL client socket support (Requires SW300052)
//#define STACK_USE_AUTO_IP
// Dynamic link-layer IP address automatic configur...
//#define STACK_USE_DHCP_CLIENT
// Dynamic Host Configuration Protocol client ...
//#define STACK_USE_DHCP_SERVER
// Single host DHCP server
//#define STACK_USE_FTP_SERVER
// File Transfer Protocol (old)
//#define STACK_USE_SMTP_CLIENT
// Simple Mail Transfer Protocol for sending email
//#define STACK_USE_SNMP_SERVER
// Simple Network Management Protocol ...
//#define STACK_USE_SNMPV3_SERVER
// Simple Network Management Protocol v3 Agent
//#define STACK_USE_TFTP_CLIENT
// Trivial File Transfer Protocol client
//#define STACK_USE_GENERIC_TCP_CLIENT_EXAMPLE
// HTTP Client example in GenericTCPClient.c
//#define STACK_USE_GENERIC_TCP_SERVER_EXAMPLE
// ToUpper server example in
//#define STACK_USE_TELNET_SERVER
// Telnet server
//#define STACK_USE_ANNOUNCE
// Microchip Embedded Ethernet Device ...
//#define STACK_USE_DNS
// Domain Name Service Client for resolving
hostna...
//#define STACK_USE_DNS_SERVER
// Domain Name Service Server for redirection
to...
//#define STACK_USE_NBNS
// NetBIOS Name Service Server for repsonding to …
//#define STACK_USE_REBOOT_SERVER
// Module for resetting this PIC remotely. Primarily...
//#define STACK_USE_SNTP_CLIENT
// Simple Network Time Protocol for obtaining ...
//#define STACK_USE_UDP_PERFORMANCE_TEST // Module for testing UDP TX performance...
//#define STACK_USE_TCP_PERFORMANCE_TEST
// Module for testing TCP TX performance...
//#define STACK_USE_DYNAMICDNS_CLIENT
// Dynamic DNS client updater module
//#define STACK_USE_BERKELEY_API
// Berekely Sockets APIs are available
//#define STACK_USE_ZEROCONF_LINK_LOCAL
// Zeroconf IPv4 Link-Local Addressing
//#define STACK_USE_ZEROCONF_MDNS_SD
// Zeroconf mDNS and mDNS service discovery
En dé-commentant les macros correspondants aux services désirés et en incluant les sources
associés au projet, nous pouvons ainsi rajouter à la compilation les services souhaités. Dé-commenter
les services en fonction de nos besoins (ICMP, TCP et BSD API). Vous constaterez à la compilation que
l'utilisation de l'API BSD nécessite l'ajout d'un fichier source supplémentaire.
39
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Nous sommes maintenant prêt à développer un serveur TCP. Pour ce faire, rien de compliquer,
nous allons utiliser les fichiers d'exemples fournis par Microchip. Compléter le fichier source
tcp.c présent dans notre projet en vous aidant du fichier source de démonstration fourni avec la
stack (network/Microchip/Demo App). Prendre celui qui semble le plus proche du cahier des
charges puis analyser le code ainsi importé.
•
Représenter sous forme graphique (rectangle et flèches) le fonctionnement de la machine
d'état implémentée par le serveur TCP BSD du programme précédent :
•
Quel traitement réalise ce programme ?
•
Avant de tester votre application, réaliser un ping sur l'adresse du serveur et s'assurer de la
bonne réponse du système.
•
Pour tester votre serveur, votre allons établir une communication depuis Telnet. Ouvrir un
client Telnet sur votre machine puis établir une communication avec le serveur embarqué.
•
Modifier le code du serveur afin qu'il réalise un echo vers l'UART des caractères reçus. Voilà,
vous venez d'implémenter un bridge TCP/UART !
40
SYSTEMES TEMPS REEL
Travaux Pratiques
5.3. Services TCP/IP
5.3.a Serveur UDP
•
Sans pour autant le tester, expliquer rigoureusement ci-dessous quelle aurait été votre
démarche afin d'implémenter un serveur UDP utilisant l'API BSD.
5.3.b Serveur HTTP
Un serveur web est une application dont le service de base est de distribuer des fichiers écrits
en HTML. On se propose d'écrire un programme affichant des relevés de températures. Lorsqu'un
utilisateur tape ceci dans la barre d'URL de son navigateur :
http://machine.nomDeDomaine:1999/repertoire/fichier.html
Le navigateur essaye de se connecter à la machine ''machine.nomDedomaine'' et à un
programme écoutant le port 1999. Ensuite, il envoie une série de chaînes de caractères dont la
première est :
''GET /repertoire/fichier.html HTTP/1.1\n\n''
Le serveur renvoie alors une entête HTTP qui est une chaîne de caractères qui précise entre
autre la version du protocole HTTP, un code d’erreur (200 : pas d’erreur) et le type de document
transporté. Par exemple :
''HTTP/1.1 200 OK \n content-type: text/html \n\n"
Ensuite, le serveur envoie le fichier d'un seul bloc, puis ferme la connexion. Si le fichier est
absent, il renvoie la chaîne suivante à la place et ferme la connexion. Le port par défaut des serveurs
web est 80 et non pas 1999.
"http/1.1 404 ERROR\n\n"
•
Écrire un programme qui écoute le port 80 et envoie sur l'UART la première ligne qu'il reçoit.
•
Se connecter au précédent programme via un navigateur web (modifier les paramètres du
proxy : connexion direct à internet). Écrire un programme qui renvoie une page web statique,
quelque soit la requête du navigateur. Attention à bien transmettre l'entête HTTP avant le code
HTML.
41
SYSTEMES TEMPS REEL
Travaux Pratiques
6. PROJET RTOS ET PILE RESEAU
Voilà, nous arrivons à la fin de cet exercice complémentaire. Dernier travail à réaliser, intégrer
au projet réalisé sous FreeRTOS (serveur de température via UART) un serveur TCP. Comme pour le
projet précédent, l'application devra proposer non seulement un shell vers l'UART mais également un
shell TCP sur le port 5151.
Attention, après intégration à votre projet de FreeRTOS et de la pile réseau de Microchip, vous
allez rencontré un problème à la compilation. En effet, la stack et le RTOS utilisent le même timer afin
d'implémenter leurs références internes de comptage (Timer1). Utiliser le fichier TickTimer2.c présent
dans /network/Microchip/TCPIP Stack au lieu de Tick.c. Ce fichier à été modifié par nos soins et ne fait
que reconfigurer le timer 2 ainsi que l'ISR associée au lieu du Timer1 (niveau première année).
42
SYSTEMES TEMPS REEL
Travaux Pratiques
7. PORTAGE PILE RESEAU MICROCHIP
Intéressons-nous à la méthodologie de portage de la pile réseau proposée par Microchip. Cette
méthodologie peut bien entendu être étendue à d'autres stack proposées par Microchip voir d'autres
fondeurs.
•
Télécharger la pile réseau sur le site officiel de Microchip (travail déjà réalisé sur les postes
école). Si vous attaquez un nouveau projet, toujours utiliser (si possible) la dernière version
disponible, le plus souvent plus robuste. Garder toujours les anciennes versions pour soucis de
compatibilité avec vos anciens projets :
www.microchip.com/tcpip
Vous constaterez que Microchip concentre toutes ses librairies évoluées (stack
graphique, USB, SD card …) dans un même outils, appelé MAL (Microchip Application Libraries,
www.microchip.com/mal). A l'installation, vous pouvez choisir de n'installer que la pile réseau.
•
Voici le contenu de l'arborescence de fichiers, si vous ne téléchargez que la pile réseau.
L'installation se fait par défaut à la racine du disque c:\ sous Windows. Ne pas hésiter à aller
observer le contenu de ce système de fichiers sur votre machine de développement :
43
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Si seule la pile réseau nous intéresse, vous pouvons choisir de n'importer vers vos projets que
les sources et fichiers d'en-tête nécessaires. Il faut néanmoins garder l'arborescence imposée
par Microchip par soucis de dépendance des fichiers d'en-tête. Ne pas oublier de rapatrier
également le contenu du répertoire C:\microchip_solutions_v2013-06-15\TCPIP\Demo
App\Configs contenant des headers propres aux contrôleurs externes PHY ou MAC/PHY
supportés par la stack. Observons l'arborescence minimale pour réaliser du développement sur
la pile réseau Microchip :
•
Après importation de la pile vers votre répertoire de projet, nous sommes maintenant prêt à le
créer. Ouvrir MPLABX, créer un nouveau projet et ajouter les fichiers sources strictement
nécessaires à une utilisation minimale de la pile. Pour réaliser cet exercice, loin d'être trivial,
s'aider de la documentation technique de la pile (Help\TCPIP Stack Help) dans la rubrique Using
the stack – Required files.
•
•
•
•
•
•
•
•
main.c
APR.c
Delay.c
Physical layer files ? (vu juste après)
Helper.c
IP.c
StackTsk.c
Tick.c
44
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Observons une capture d'écran de MPLABX à ce niveau là du projet :
•
La partie qui suit peut en revanche être délicate en fonction du contrôleur matériel externe
PHY ou MAC/PHY que vous souhaitez interfacer (composant entre MCU et connecteur RJ45).
•
Si votre contrôleur est supporté par la pile réseau (ENC28J60, ENCX24J600, ETH97J60,
SMSC8700 ...),nous n'avons qu'à rajouter les fichiers sources et headers propres au
contrôleur
•
Si votre contrôleur n'est pas supporté par la pile réseau et n'est pas compatible broche à
broche et registre à registre avec un des contrôleurs supportés, nous avons à
développer nous même les drivers. Ce travail peut prendre plusieurs jours de
développement. Toujours s'inspirer de drivers proches de celui en cours de
développement. Ne jamais réinventer la poudre.
•
Si votre contrôleur n'est pas supporté par la pile réseau mais est compatible broche à
broche et registre à registre avec un des contrôleurs supportés (notre cas sur la
maquette PIC32 MAXI WEB), nous n'avons qu'à rajouter les fichiers sources et headers
propres au contrôleur déjà supporté.
Par exemple, le contrôleur PHY présent sur la maquette est un KS8721B et est
compatible (broche/broche et registre/registre) avec le SMSC8700 qui lui est supporté.
En ajoutant la macro CFG_INCLUDE_PIC32_ETH_SK_ETH795 à la configuration de votre
projet, vous pouvez pré-configurer, avant compilation, l'inclusion de tous les fichiers
d'en-tête nécessaires :
45
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Inclure les fichiers sources propres au contrôleur utilisé. Dans notre cas, nous utiliserons le
contrôleur MAC interne aux PIC32 et un contrôleur PHY externe. Observons le projet après
inclusion des sources nécessaires :
•
Ajouter maintenant les fichiers d'en-tête nécessaires à une bonne compilation du projet. Ces
fichiers sont présents dans les répertoires Microchip/Configs, Microchip/Include et
Microchip/Include/TCPIP Stack. Cette étape n'a pour but que de proposer des raccourcis vers les
headers, ne pas oublier donc de configurer la chaîne de compilation.
46
SYSTEMES TEMPS REEL
Travaux Pratiques
•
Configurer maintenant sous L'IDE les chemins vers les tous les fichiers d'en-tête nécessaires au
projet. Cette étape configure les paths/chemins de votre chaîne de compilation et dépend de
l'arborescence et du système de fichier du projet :
•
Voilà, nous sommes enfin prêt pour une première compilation.
•
Afin de commencer à compléter le fichier main.c, s'aider de l'aide fournie avec la stack (TCPIP
Stack Help) via la rubrique Using the stack – Main File :
47
SYSTEMES TEMPS REEL
Travaux Pratiques
48
SYSTEMES TEMPS REEL
ANNEXES
Téléchargement
Random flashcards
amour

4 Cartes mariam kasouh

aaaaaaaaaaaaaaaa

4 Cartes Beniani Ilyes

découpe grammaticale

0 Cartes Beniani Ilyes

Anatomie membre inf

0 Cartes Axelle Bailleau

Fonction exponentielle.

3 Cartes axlb48

Créer des cartes mémoire