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 [email protected] +33 (0)2 31 45 27 61 +33 (0)6 22 07 21 51 https://fr.linkedin.com/in/hugo-descoubes-823a4268 Philippe Lefebvre [email protected] +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