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 :

  1. 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).

  2. 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.

  3. 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.

  4. 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.

  5. Remettre le cap à zéro par un appui long (plus d’une seconde) sur SW2.

  6. Rendre compte au terminal série, une fois par seconde, d’une ligne d’état complète, envoyée par DMA.

  7. 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

  1. 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.

  2. Le code, commenté, dans un projet CubeIDE propre.

  3. 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).

  4. 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

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

Éléments de correction : une architecture possible

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

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 Remettre la carte en état RatBot.