TP 12 — Mini-projet : le tableau de bord ======================================== Objectifs --------- Réunir les outils de cette partie dans un programme complet et organisé : des tâches aux rôles clairs, des échanges par files et par état partagé, une surveillance par chien de garde, et une mesure de la charge. Durée indicative : trois séances de 3 h, en binôme. Prérequis : les TP 1 à 11 de cette partie. Le cahier des charges --------------------- La carte devient le **tableau de bord** d'un robot. Elle doit : #. **Mesurer** la température (TMP102, 1 Hz) et le cap (gyroscope, 50 Hz, zéro mesuré au démarrage), et la **tension de la batterie** (ADC, TP 6 de la partie 1, 2 Hz, moyenne glissante). #. **Afficher** sur l'écran, à 10 images par seconde, trois pages au choix : - *mesures* : température, tension, cap avec sa boussole ; - *système* : charge du processeur, marge de pile de chaque tâche, nombre d'erreurs I²C ; - *alarmes* : la liste des alarmes en cours. **SW1** passe à la page suivante. #. **Refroidir** : le ventilateur (TP 4 de la partie 1) démarre au-dessus de 35 °C et s'arrête en dessous de 32 °C. #. **Signaler** par le voyant RGB (TP 2 de la partie 1) : vert si tout va bien, orange si la batterie est faible (moins de 10,5 V), rouge clignotant en cas d'alarme de température ou de batterie très faible (moins de 10 V). Le buzzer bipe une fois à chaque **nouvelle** alarme. #. **Remettre le cap à zéro** par un appui long (plus d'une seconde) sur **SW2**. #. **Rendre compte** au terminal série, une fois par seconde, d'une ligne d'état complète, envoyée **par DMA**. #. **Se surveiller** : le chien de garde redémarre la carte si l'une des tâches de mesure ou d'affichage cesse de donner signe de vie ; au redémarrage suivant, l'écran affiche « WDG » pendant 3 s. Contraintes ----------- - Une tâche **par rôle** (mesure, affichage, actionneurs, journal, surveillance), avec une priorité **justifiée**. - Chaque périphérique a **un seul** propriétaire, ou une protection explicite. - **Aucun** ``HAL_Delay`` dans les tâches, **aucune** variable partagée sans protection, **aucune** attente active. - Les **événements** (appuis, alarmes) passent par des files ; les **états** (dernières mesures) par une structure protégée. - La charge du processeur reste **sous 30 %**, et chaque tâche garde au moins **25 %** de sa pile libre. Ce qu'il faut rendre -------------------- #. Un **schéma** de l'architecture : les tâches, leurs priorités et leurs périodes, les files, les mutex, les sémaphores, et qui possède quel périphérique. #. Le **code**, commenté, dans un projet CubeIDE propre. #. Un **tableau de mesures** : charge du processeur par tâche, marge de pile par tâche, période réelle de la tâche gyroscope (moyenne et écart maximal). #. Une **démonstration** de cinq minutes, qui montre en particulier le redémarrage par chien de garde : un appui sur SW4 fige volontairement une tâche de mesure. Grille indicative ----------------- .. list-table:: :header-rows: 1 :widths: 60 15 * - Critère - Points * - Les fonctions 1 à 7 marchent - 7 * - Architecture : rôles, priorités et échanges justifiés - 4 * - Respect des contraintes - 3 * - Mesures : charge, piles, régularité - 3 * - Qualité du code et des commentaires - 2 * - Démonstration - 1 .. dropdown:: Éléments de correction : une architecture possible :icon: light-bulb :color: info Il n'y a pas **une** solution, mais toute bonne solution répond aux mêmes questions : qui fait quoi, à quel rythme, avec quelle urgence, et qui possède quoi. En voici une, qui respecte le cahier des charges. **Les tâches** .. list-table:: :header-rows: 1 :widths: 16 16 12 56 * - Tâche - Priorité - Période - Rôle et ressources possédées * - ``imuTask`` - AboveNormal - 20 ms - gyroscope (I2C3), intégration du cap ; la plus régulière, donc la plus prioritaire * - ``buttonTask`` - AboveNormal - 10 ms - SW1 à SW4, anti-rebond, appui long sur SW2 ; envoie des **événements** dans ``eventQueue`` * - ``sensorTask`` - Normal - 500 ms - TMP102 (I2C2) à 1 Hz, batterie (ADC2) à 2 Hz, moyenne glissante ; détecte les franchissements de seuils (hystérésis) et envoie les alarmes * - ``actuatorTask`` - Normal - événements - ventilateur (TIM12), voyant RGB (TIM1 + PWM logiciel TIM7), buzzer ; réagit aux alarmes, clignote avec le délai de sa file * - ``displayTask`` - BelowNormal - 100 ms - écran (SPI3, DMA, double tampon), pages ; reçoit SW1 par file * - ``logTask`` - BelowNormal - 1 s - ligne d'état sur UART5 par DMA * - ``watchdogTask`` - Low - 500 ms - IWDG1 ; relance seulement si ``imuTask``, ``sensorTask`` et ``displayTask`` ont donné signe de vie **Les échanges** - **État** : une structure ``g_state`` (température, tension, pourcentage, cap, alarmes, erreurs I²C, charge, marges de pile), protégée par **un mutex** ; chaque tâche y écrit ses champs, l'affichage et le journal en font une **copie**. - **Événements** : ``eventQueue`` (boutons → affichage et cap), ``alarmQueue`` (capteurs → actionneurs). Une alarme n'est envoyée qu'au **franchissement** d'un seuil. - **Fins de transfert** : un sémaphore par DMA (SPI3, UART5), levé par le rappel de fin, jamais de travail dans les interruptions. **Les erreurs qui coûtent des points** - un ``HAL_Delay`` oublié dans une initialisation appelée depuis une tâche (``screen_init``) ; - ``printf`` appelé depuis plusieurs tâches : soit une seule tâche écrit, soit un mutex sur la liaison série ; - le chien de garde relancé par une seule tâche, sans vérifier les autres (TP 5) ; - un tampon DMA en DTCM, ou sans nettoyage de cache (TP 6) ; - le cap remis à zéro par l'appui long **dans** l'interruption, ou par écriture directe de ``g_state`` sans le mutex : c'est ``imuTask`` qui possède le cap, l'appui long doit lui envoyer un événement ; - la mesure de charge sur le tableau **cumulé** plutôt que sur une fenêtre (TP 9) ; - des priorités justifiées par l'importance (« l'écran, c'est ce qu'on voit ») plutôt que par l'**urgence**. **Le message « WDG »** : au démarrage, lire ``RCC_FLAG_IWDG1RST`` **avant** d'effacer les drapeaux, et le transmettre à ``displayTask`` (un champ de l'état, ou un événement au démarrage). Pour aller plus loin -------------------- - **Enregistrer** : gardez les 100 dernières alarmes, horodatées par la RTC (TP 9 de la partie 1), dans la flash SPI. - **Piloter un servomoteur** (TP 10 de la partie 1) : il pointe toujours vers le nord de départ, quel que soit le cap. - **Passer à ROS** : c'est l'objet de la partie 3. Après le TP ----------- Pour rendre la carte au robot, voir :ref:`etat-ratbot`.