Académique Documents
Professionnel Documents
Culture Documents
2ème année
Spécialité Électronique et Physique Appliquée
Majeure SATE
Responsable de Cours :
Hugo Descoubes, hugo.descoubes@ensicaen.fr - 02 31 45 27 61
Équipe pédagogique :
Gabriel Frey, freyg.it@gmail.com – 06 80 95 61 56
Basile Dufay, basile.dufay@unicaen.fr - 02 31 45 26 91
2015-2016
Systèmes Temps Réel
PLAN
• COURS
• TRAVAUX PRATIQUES
• ANNEXES
Les documents présents dans ce document sont librement téléchargeables sur la plateforme
d'enseignement de l'ENSICAEN via un accès anonyme. Utiliser l'onglet de recherche en utilisant les
mots clés ''système temps réel'' :
http://foad.ensicaen.fr/
Ce document est protégé par une licence Creative Common, néanmoins les différents supports
restent librement réutilisables à des fins pédagogiques. Merci cependant de me prévenir par mail
(hugo.descoubes@ensicaen.fr) et de citer le nom de l'ENSICAEN dans vos documents de cours.
Systèmes Temps Réel
INTRODUCTION FREERTOS
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
SOMMAIRE
1. PREAMBULE
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
4. SEMAPHORE
-1-
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
-2-
Systèmes Temps Réel
Exécutif temps réel 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 ci-
dessous, 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/).
-3-
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
En 2014, 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 :
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) :
-4-
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
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 :
-5-
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
2.1. Tâche
// 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.
-6-
Systèmes Temps Réel
Exécutif temps réel 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
● ...
-7-
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
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 :
/**
* @fn main
* @brief main entry point
*/
int main(void){
// Configurations matérielles
UserInit();
● 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.
-8-
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
/**
* @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();
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.
-9-
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
/**
* @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();
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.
- 10 -
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
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()) :
● ''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).
● 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.
- 11 -
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
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.
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.
- 12 -
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
3. QUEUE DE MESSAGES
Une queue de messages (messages queue ou queue) ou boîte aux lettres (mailboxe) 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 :
- 13 -
Systèmes Temps Réel
Exécutif temps réel 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 :
...
- 14 -
Systèmes Temps Réel
Exécutif temps réel 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
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).
- 15 -
Systèmes Temps Réel
Exécutif temps réel 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).
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
- 16 -
Systèmes Temps Réel
Exécutif temps réel 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é.
- 17 -
Systèmes Temps Réel
Exécutif temps réel FreeRTOS
- 18 -
Systèmes Temps Réel
TRAVAUX PRATIQUES
Systèmes Temps Réel
SOMMAIRE
Attention, des travaux préparatoires seront à préparer avant l'arrivée en séance !
4.1. Présentation
4.2. Spécifications
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
7.3. Présentation
7.4. Spécifications
Systèmes Temps Réel
Travaux Pratiques
SOMMAIRE
4.1. Présentation
4.2. Spécifications
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
7.3. Présentation
7.4. Spécifications
-1-
Systèmes Temps Réel
Travaux Pratiques
-2-
Systèmes Temps Réel
Travaux Pratiques
1. MODE COOPERATIF
Travail préparatoire
• 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) ?
-3-
Systèmes Temps Réel
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.
• 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-
Systèmes Temps Réel
Travaux Pratiques
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ésent 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-
Systèmes Temps Réel
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");
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é dans la tâche 3. Du coup, trois
questions se soulèvent :
• 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-
Systèmes Temps Réel
Travaux Pratiques
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 !
http://www.youtube.com/watch?feature=player_embedded&v=WTNc1PwoMG4
-7-
Systèmes Temps Réel
Travaux Pratiques
• Quel code s'exécute lorsque nous nous trouvons dans aucune des 3 tâches ?
-8-
Systèmes Temps Réel
Travaux Pratiques
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().
• 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");
}
• 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-
Systèmes Temps Réel
Travaux Pratiques
- 10 -
Systèmes Temps Réel
Travaux Pratiques
• 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) ?
• 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 ?
• 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 -
Systèmes Temps Réel
Travaux Pratiques
• 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.
- 12 -
Systèmes Temps Réel
Travaux Pratiques
• Compléter le chronogramme suivant et préciser l'état pris par chaque tâche (R = ready et B =
blocked). Attention aux pièges :
- 13 -
Systèmes Temps Réel
Travaux Pratiques
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 réalisé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;
}
- 14 -
Systèmes Temps Réel
Travaux Pratiques
http://www.freertos.org/Stacks-and-stack-overflow-checking.html
/**
* @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
}
/**
* @fn void growStack( void )
* @brief current stack allocation until overflow
*/
void growStack( void ) {
vTaskDelay( 1 );
return growStack();
}
- 15 -
Systèmes Temps Réel
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 :
- 16 -
Systèmes Temps Réel
Travaux Pratiques
void prodTask1( void *pvParam ){ void prodTask2( void *pvParam ){ void consTask( void *pvParam ){
int x=1; int x=2; int y;
char sBuffer[20];
while(1){ while(1){
if (xQueueSend(xQueue, \ if (xQueueSend(xQueue, \ while(1){
&x, 0) == pdTRUE ){ &x, 0) == pdTRUE ){ xQueueReceive(xQueue, &y, \
x += 2; x += 2; portMAX_DELAY );
vTaskDelay( 1 ); vTaskDelay( 1 ); sprintf(sBuffer, ''%d'', y);
} } uartPuts(sBuffer) ;
} } }
} } }
- 17 -
Systèmes Temps Réel
Travaux Pratiques
void task1( void *pvParam ){ void task2( void *pvParam ){ void task3( void *pvParam ){
} } }
} } }
- 18 -
Systèmes Temps Réel
Travaux Pratiques
• 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, une périodique 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 ne fera
qu'envoyer une chaîne de caractères à l'ordinateur puis se bloquer.
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.
- 19 -
Systèmes Temps Réel
Travaux Pratiques
// ...
uartPutS(''\r\ntask2, current tick value '');
// print current tick value ...
uartPutS(''\r\n'');
Nous venons ici d'illustrer deux concepts important 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.
- 20 -
Systèmes Temps Réel
Travaux Pratiques
3.2. Timeout
• 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 :
uartPutS(''\r\ntask2, timeout'');
• 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 ?
- 21 -
Systèmes Temps Réel
Travaux Pratiques
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.
- 22 -
Systèmes Temps Réel
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.
• 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.
• 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.
- 23 -
Systèmes Temps Réel
Travaux Pratiques
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 un 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 :
• 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);
- 24 -
Systèmes Temps Réel
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.
http://www.freertos.org/tutorial/index.html
- 25 -
Systèmes Temps Réel
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.
- 26 -
Systèmes Temps Réel
Travaux Pratiques
• 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.
- 27 -
Systèmes Temps Réel
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 :
• L'application devra réaliser des acquisitions toutes les 20ms et devra toujours garder
en mémoire les 100 dernières mesures 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
- 28 -
Systèmes Temps Réel
Travaux Pratiques
- 29 -
Systèmes Temps Réel
Travaux Pratiques
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.
• 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.
- 30 -
Systèmes Temps Réel
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.
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 :
- 31 -
Systèmes Temps Réel
Travaux Pratiques
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.
• 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
- 32 -
Systèmes Temps Réel
Travaux Pratiques
Observons un exemple de machine d'état présentant la connexion entre un client (client TCP
par exemple) et un serveur.
- 33 -
Systèmes Temps Réel
Travaux Pratiques
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.
• 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.
- 34 -
Systèmes Temps Réel
Travaux Pratiques
• Lister ci-dessous les appels de fonctions nécessaires à une bonne configuration et utilisation
de la pile :
- 35 -
Systèmes Temps Réel
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 :
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.
- 36 -
Systèmes Temps Réel
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é.
• 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 !
- 37 -
Systèmes Temps Réel
Travaux Pratiques
• 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.
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 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.
- 38 -
Systèmes Temps Réel
Travaux Pratiques
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).
- 39 -
Systèmes Temps Réel
Travaux Pratiques
• 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.
- 40 -
Systèmes Temps Réel
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
- 41 -
Systèmes Temps Réel
Travaux Pratiques
• 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 :
- 42 -
Systèmes Temps Réel
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.
- 43 -
Systèmes Temps Réel
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 :
• 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 :
- 44 -
Systèmes Temps Réel
ANNEXES
Systèmes Temps Réel
SOMMAIRE
3. INTRODUCTION FREERTOS
4. PORTAGE FREERTOS
5. FICHIER FreeRTOSConfig.h
REGLES DE CODAGE
GLOSSAIRE
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
1. CREATION DE PROJET MPLABX IDE • Sélectionner l'architecture et le MCU cible. Cette étape est à adapter en fonction de vos
projets. Dans l'exemple ci-dessous il s'agit de la famille PIC32 sur MCU PIC32MX795F512L :
• Créer un répertoire au nom de votre projet et importer vos fichiers sources et fichiers d'en-
tête. Dans les captures d'écrans suivantes, le répertoire du projet se nomme example ainsi
que le fichier source principal. Ne pas hésiter à créer une arborescence plus riche en fonction
de la complexité de votre projet. Par exemple : example/h, example/src et example/pjct
• Connecter la sonde de programmation à l'ordinateur de développement puis ouvrir MPLABX : • Sélectionner la sonde de programmation à utiliser :
• Créer un nouveau projet : File -> New Project -> Next • Sélectionner la chaîne de compilation à utiliser. Cette étape est à adapter en fonction des
outils installées sur la machine de développement. Il faut toujours essayer d'attaquer de
nouveaux projets avec les dernières versions proposées tout en gardant les anciennes pour
des problèmes de compatibilité pour vos anciens projets :
-1- -2-
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
• Donner un nom au projet tout en étant prudent à la localité du répertoire de travail. Sur les • Si le projet ne possède pas de dépendances avec des fichiers d'en-tête, il est alors possible de
machine de TP, vous avez la possibilité de travailler sur le répertoire Z: (lecteur réseau le compiler dès maintenant.
accessible de n'importe quel poste) ou dans le répertoire C:/…/Documents localement
présent sur la machine.
• En cas de dépendances avec des headers, ne pas oublier de configurer les chemins ou path de
la chaîne de compilation :
-3- -4-
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
Nous allons travailler sous MPLABX qui est l'environnement de développement ou IDE
(Integrated Development Environment) libre proposé par Microchip pour toute sa gamme de MCU's.
Comme tout IDE, MPLABX doit être associé à une chaîne de compilation ou toolchain. Par exemple
C18 ou XC18 pour la famille PIC18 et C32 ou XC32 pour la gamme PIC32. Tous les outils logiciel
proposés par Microchip sont gratuits et librement téléchargeables sur le site du fondeur. Seules
certaines versions des chaînes de compilation proposées sont payantes et ouvrent la porte à de
meilleures performances d'optimisation à la compilation.
http://www.microchip.com/
Vous pouvez d'ailleurs développer et pré-compiler vos sources chez vous du moment que
vous restez au stade de la compilation ou de la simulation. Si vous le souhaitez, les TP peuvent
également être réalisés sur votre machine personnelle du moment que l'installation des outils ait
été faite avant l'arrivée en séance et que votre machine offre des performances suffisantes.
-5- -6-
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
Pour assurer une portabilité du noyau, les développeurs de FreeRTOS ont utilisé des
définitions de type. Par exemple sur XC18 (sur PIC18) un int est un entier sur 16bits, sous XC32
(sur PIC32) il s'agit d'un entier sur 32bits. Sous XC32, les types utilisés par FreeRTOS sont les
• États possibles pour une tâche sous FreeRTOS : suivants :
• Préfixes de type :
• c: char
• s: short
• l: long
• x: portBASE_TYPE
• p: pointeur
• v: void (fonction ne retournant aucun paramètre)
• u: unsigned
• uc : unsigned char
• pc : pointeur sur un char ...
• État Suspendu ou Suspended state :
• Préfixes de localité :
Cet état est propre à FreeRTOS. Une tâche dans cet état n'est plus vue du noyau est ne
fait plus partie des tâches gérées par l’ordonnanceur. Cet état est donc à manier avec grande Afin de localiser rapidement le fichier de définition d'une variable, d'une macro ou
précaution sachant qu'il reste de plus relativement peu utilisé. Les seules fonctions d'une fonction un préfixe suit celui du type (cf. ci-dessus). Prenons quelques exemples :
permettant de mettre une tâche dans cet état sont les suivantes :
• vTaskPrioritySet() retourne un void et est définie dans task.c
• vTaskSuspend() • xQueueReceive() retourne un portBASE_TYPE et est définie dans Queue.c
• vTaskSuspendAll() • vSemaphoreCreateBinary() retourne un void et est définie dans semphr.h
• vTaskResume()
• vTaskResumeAll() Découvrons les principaux préfixes (macros et fonctions) :
• vTaskResumeFromISR()
• config : FreeRTOSConfig.h
• task ou Task : task.h ou task.c
• pd : projdefs.h ...
-7- -8-
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
4. PORTAGE FREERTOS • Copier le système de fichiers (après avoir fait du vide) dans votre répertoire de travail. Cette
étape n'est pas obligatoire mais offre l'avantage d'assurer la portabilité de votre projet vers
une autre machine.
Nous allons maintenant présenter une méthodologie de portage de FreeRTOS vers votre
application. Nous verrons que cette méthode n'est pas unique et que certaines étapes peuvent
différées en fonction des habitudes de chaque développeur.
www.freertos.org
http://sourceforge.net/projects/freertos/files/FreeRTOS/
• Voici ci-dessous le système de fichiers du noyau. Les répertoires marqués d'une croix rouge ne
sont pas utiles à la bonne compilation d'un projet.
• Après avoir créé un projet sous MPLABX, nous allons y-intégrer les sources du noyau. Créer
également des sous-répertoires dans votre arborescence du projet afin de faciliter
d'éventuelles futures recherches.
-9- - 10 -
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
• Arborescence du projet sous MPLABX. Remarque, nous travaillerons avec le fichier heap_1.c 5. FICHIER FreeRTOSConfig.h
pour la stratégie de gestion du tas :
Ce fichier comportant que des macros est le seul physiquement sorti du système de fichiers de
FreeRTOS. Il est essentiel et assure la configuration du noyau avant compilation. Pour bien
comprendre son rôle, illustrons deux cas de figure :
• toutes (ou presque) les macros sont à ''1'' : Si toutes les macros sont forcées à 1 (ou valeur
propre à la macro) nous pouvons alors utiliser toutes l'API et fonctionnalités proposées par
FreeRTOS. Cependant, la taille du code sera maximale. En effet toutes les fonctions auront été
compilées, linkées et seront embarquées dans la mémoire programme du processeur, qu'on
les utilise ou pas.
• Toutes les macros sont à ''0'' : Nous en pourrons utiliser que les principales fonctionnalités de
FreeRTOS. Les services système (et donc les macros associées) devront être rajoutées au fur et
à mesure des besoins afin de ne pas gaspiller inutilement les ressources mémoire du
processeur.
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#define configUSE_PREEMPTION 0
#define configTICK_RATE_HZ ( ( portTickType ) 1000 )
#define configPERIPHERAL_CLOCK_HZ (80000000UL)
#define configCPU_CLOCK_HZ (80000000UL)
#define configUSE_16_BIT_TICKS 0
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configUSE_COUNTING_SEMAPHORES 0
#define configUSE_MUTEXES 0
• Spécifier à la chaîne de compilation les chemins ou path vers les fichiers d'en-tête de
FreeRTOS. Attention, cette étape est à adapter en fonction de l'arborescence de votre projet. #define configMINIMAL_STACK_SIZE ( 240 )
Se rendre dans File >> Project Properties : #define configISR_STACK_SIZE ( 400 )
#define configKERNEL_INTERRUPT_PRIORITY 0x01
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 0x04
/* Co-routine definitions. *
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES (2)
/* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */
#define INCLUDE_uxTaskPriorityGet 0
#define INCLUDE_vTaskDelete 0
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 0
#define INCLUDE_vTaskDelayUntil 0
#define INCLUDE_vTaskDelay 0
#define INCLUDE_uxTaskGetStackHighWaterMark 0
#endif /* FREERTOS_CONFIG_H */
- 11 - - 12 -
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
REGLES DE CODAGE ● Structures de contrôle : règles spécifiant l'indentation ainsi que les espaces à respecter
pour l'écriture de structures de contrôle.
do {
Ce document à pour objectif de fixer un cadre et des règles de codage en langage C pour les if (a == b) {
enseignements de Systèmes Embarqués à l'ENSICAEN. Cette démarche reflète les contraintes que ...
vous aurez d'ici quelques années à suivre dans le monde de l'entreprise. La plupart des entreprises } else if (a > b) {
travaillant dans le domaine du développement logiciel, dont l'embarqué fait parti, imposent à leurs ...
} else {
développeurs des règles semblables à celles-ci (beaucoup plus exhaustives en général). L'objectif
...
étant de standardiser, clarifier, cadrer et faciliter le partage de codes sources au sein d'une équipe }
voir de l'entreprise. Les règles de codage imposées sont inspirées du ''Linux Kernel Coding Style'' et
du ''GNU Coding Standard''. Voici le sommaire de ce document : /* single statement */
if (condition)
action1();
1. Indentation
else
2. Dénomination action2();
3. Commentaire
4. Divers } while (condition);
INDENTATION
DENOMINATION
● Tabulation : 8 caractères (à régler dans les préférences de l'éditeur de texte). ● Fonctions : Toujours débuter par une minuscule. Pour donner un nom complexe,
utiliser comme séparateur une Majuscule ou un ''_''.
switch (data) {
case 'A': ● Variables locales : Toujours débuter par une minuscule. Les variables servant de
case 'B':
/* comment */ compteur de boucle peuvent avoir un nom sans sens précis, par exemple ''i''. Sinon,
data <<= 20; toujours donner à une variable un nom court permettant d'identifier clairement son
break; rôle. Déclarez vos variables locales en début de fonction (portabilité de code).
default:
break;
● Variables globales : Toujours débuter par une minuscule. Toujours donner à une
}
variable globale un nom permettant d'identifier clairement son rôle (séparateurs ''_''
ou Majuscules). Bien évidemment, éviter tant que faire se peut les variables globales
● Nombres de niveaux d'indentation : Éviter plus de 3 niveaux d'indentation imbriqués. (ressources partagées).
Facilite la lisibilité du code.
int tempProcessA, temp_process_B;
● Nombres de colonnes par page : 80 caractères/colonnes par page (à régler dans les
préférences de l'éditeur de texte). void lm4567_codec_ init (void)
{
temp_process_B++;
● procédures : cas spécial pour le fonctions, ouvrir l’accolade au début de la ligne …
suivante }
int function (int x, int y) ● Définition de type : Toujours débuter par une Majuscule. Toujours définir un nom
{
int z ; permettant d'identifier clairement le type des variables déclarés.
...
HandleSpiModule a; // Bien !
return 0;
Hmod a; // Pas Bien !
}
- 13 - - 14 -
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
● Macros : Toujours en Majuscule. Utiliser comme séparateur un ''_'' pour les noms ● Quelques tags supplémentaires (cf. www.doxygen.org ):
complexes. Pour les macros fonctions, appliquer les même règles de dénomination que
pour les fonctions. /**
* @example example insertion
#define UART_BAUDRATE 0xFFFF * @warning warning insertion
#define mul (x, y) x*y * @li bullet point insertion
* @mainpage main page comments insertion
* @image picture insertion
COMMENTAIRES * @include code insertion
*/
● Toujours penser à expliquer ce que votre code fait et non comment il le fait !
DIVERS
● C99 style : Ne pas utiliser ''//'' (problème de portabilité)
● C89 style : Toujours utiliser ''/* ... */'' (solution portable) ● Valeur de retour : Essayer de faire en sorte que la valeur de retour d'une fonction soit
représentative de son bon traitement. Par exemple, retourne zéro (ou pointeur nul) en
● Balise pour la documentation de code (exemple de Doxygen) : Insérer dans vos cas d'échec et différent de zéro en cas de succès. Les résultats des procédures seront
cartouches quelques tags standards, par exemple ceux de Doxygen. Attention, seuls les retournés par pointeur via les paramètres d'entrée.
fichiers d'en-tête doivent contenir des balises pour la génération de documentation.
Doxygen permet la génération automatique de documentation vers différents formats
(HTML, PDF, CHM ...). Ne pas utiliser de caractères accentués dans vos commentaires.
/**
* @file example.h
* @brief demo header file
* @author
*/
/**
* @struct Str_data_buffer
* @brief Objet latch converted data from temperature sensor
*/
typedef struct {
int buffSize
int* allocCnvTab
} Str_data_buffer;
/**
* @brief read data codec LM4567 controller
* @param resetVal imput value
* @param cnvResult converted value
* @return null if data conversion error
*/
Handle_spi_module lm4567_codec_read (char resetVal, float cnvResult);
- 15 - - 16 -
Systèmes Temps Réel Systèmes Temps Réel
Annexes Annexes
A G
- 17 - - 18 -
Systèmes Temps Réel
Annexes
• PC : Program Counter
• PC : Personal Computer
• PIC18 : Famille MCU 8bits Microchip
• PLD : Programmable Logic Device
• POSIX : Portable Operating System Interface, héritage d'UNIX (norme IEEE 1003)
• PPC : Power PC
• TI : Texas Instruments
• TNS : Traitement Numérique du Signal
• TSC : Time Stamp Counter
• TTM : Time To Market
- 19 -