Lampda - Partie 5

Créer un produit embarqué industriel

Partie 5 - Conception logicielle

Index :

Conception software

La programmation va être responsable pour la gestion de tout le comportement de la lampe.

L’implémentation ne sera que survolée ici, la documentation étant disponible en ligne sur le git.

Le tout sera codé en C++ 17, avec une partie en C++11 pour le core FreeRTOS.

Objectifs

  • Base temps réel
  • Robustesse aux erreurs fatales, qui nécessiterait un démontage pour être réparée
  • Possibilité de mettre à jour le système sans démontage, via USB
  • Sécurité des batteries (Pas de risque d’emballement thermique)
  • Sécurité d’utilisation : pas de risque d’autodestruction à cause de mauvaises manipulations
  • Démarrage rapide depuis le mode veille
  • indication de toute erreur et alerte avec l’indicateur coloré
  • Enregistrement de paramètres et variables utilisateur
  • S’assurer que les commandes boutons soient toujours prises en compte correctement (Minimiser la frustration utilisateur)
  • Commandes intuitives

Bootloader

Le bootloader est le logiciel de base du microcontrôleur, qui sera le premier élément à être activé, et déterminera tout le comportement de base. C’est également la partie responsable des mises à jour par exemple.

En conséquence, cette partie doit être extrêmement robuste.

J’ai utilisé comme base un bootloader réalisé par Adafruit, qui permet de flasher le circuit via USB (méthode DFU). Ce bootloader contrôle aussi le comportement du port de reset, qui est l’ultime sécurité en cas de crash sévère du système, avant de devoir reprogrammer le bootloader. Cinq contacts rapides sur le port de reset enlèvent totalement le programme de la mémoire et force le mode DFU.

La mise à jour par DFU est capable de mettre à jour le programme principal, mais aussi le bootloader lui-même. Cette opération est extrêmement risquée, car toute mauvaise manipulation casserait (le terme consacré est “brickerait”) le bootloader lui-même, obligeant à démonter la lampe pour réparer le bootloader.

Des améliorations possibles du bootloader peuvent permettre des mises à jour sans connexion physique (OTA), ou des sécurités pour valider un nouveau programme, au coût du double de la mémoire, ce qui n’est pas désirable ici.

Gestion des machines à états

Le fonctionnement global de la lampe est articulé autour de machines à états. Voici le diagramme haut niveau de ces machines :

Note: les transitions vers Erreur ne peuvent êtres récupèrées qu’aprés un redémarrage de la lampe.

Détail des modes :

Mode Caractéristiques Changement de Mode
Allumé utilisation standard, batterie se vide, les LEDS sont allumées, l’utilisateur contrôle le comportement du système avec le bouton Appui bouton ou alerte.
Charge LED éteintes, les batteries chargent, les batteries sont équilibrées, l’utilisateur peut allumer le système avec le bouton. Débranchement du câble de charge ou appui bouton ou alerte.
Batterie externe LED éteintes, les batteries se déchargent, l’utilisateur peut allumer le système avec le bouton. Débranchement du câble de charge ou appui bouton ou alerte.
Éteint LED éteintes, indicateur éteint, aucun composant actif Branchement du câble de charge ou appui bouton.
Erreur Se déclenche en cas de bug grave ou d’alerte hardware. Bloque toute action utilisateur à part éteindre la lampe Appui bouton.

Dans tout mode :

  • L’indicateur affiche des alertes utiles a l’utilisateur ou au programmeur
  • La lampe répond aux commandes de terminal série
  • Les capteurs sont surveillés pour que le système ne dépasse jamais ses conditions de fonctionnement.
  • Un watchdog bas niveau redémarrera le système s’il n’est pas notifié fréquemment par le programme.

Animations utilisateur

Design d’un système de pages et de modes génériques, ainsi qu’un manager générique, qui laisse assez de liberté à l’utilisateur pour programmer beaucoup d’animations différentes. Sans entrer dans les détails, c’est un système fortement templatisé pour produire du code à la compilation, et économiser des ressources en RAM et cycle CPU.

Chaque animation doit implémenter ses fonctions principales (on_enter_mode, on_exit_mode, loop, on_ramp_update, etc) qui lui permettent d’intéragir avec la logique haut niveau et le matériel. Les animations peuvent également acceder aux données microphone et centrale intertielle.

Les animations doivent être non bloquantes, et ont un temps limite de processeur à respecter, au dela duquel une alerte sera levée. Chaque animation à également accès a un petit espace de stockage en flash, qui permet de concerver des données entre les allumages de la lampe.

Les animations sont définies à la compilation, et ne peuvent pas êtres modifiées quand le code est en fonctionnement.

Hardware Abstraction Layer (HAL)

Lors de la création du programme, j’ai passé beaucoup de temps à séparer clairement les composantes spécifiques au microcontrôleur (registres etc) du reste du code afin d’avoir une frontière claire entre le programme et de la plateforme. Cette séparation se présente sous la forme d’une HAL qu’il est possible de remplacer lors de la compilation, dans le but de faire fonctionner le programme sur n’importe quelle autre plateforme.

Cela a permis d’implémenter une simulation de la lampe qui peut fonctionner sur un ordinateur, afin de faciliter le processus de développement, mais aussi d’ajouter des tests unitaires et d’intégration. L’ajout de tests n’est jamais facile pour de l’implémentation microcontrôleur, et est encore en cours, avec le but de couvrir en priorité les parties critiques, et à long terme l’ensemble du programme.

Gestion des entrées sorties

La gestion des entrées et sortie (GPIO) du microcontrôleur est un processus à part des machines à état.

Composant Signal/Pin Type Rôle
Bouton RGB X Entrée (INT) Détection appui
X Sortie (PWM) Contrôle led RGB
USB-PD CH_INT_N Entrée (INT) Interrupt pour communication I2C
USB protection FAULT Entrée (INT) Signal d’erreur sur le port USB ou composant
Bus I2C I2CM_SDA Entrée/sortie Bus I2C
I2CM_CLK Sortie Bus I2C
VBUS gate VBUS_FAULT Entrée Signal d’erreur composant
VBUS_DSCHRG Sortie Contrôle du déchargement du power rail
VBUS_DIR Sortie Contrôle du sens de circulation du courant sur VBUS
VBUS_FRS Sortie Contrôle de l’activation du FastRoleSwap
VBUS_GATE_DRV Sortie Contrôle de l’activation de la gate
Power gate STRIP_OUT_DRV Sortie Contrôle de l’activation de la gate
3.3 V gate V33_EXT_EN Sortie Contrôle de l’activation de la ligne 3.3 V
Chargeur CH_OTG_EN Sortie Contrôle de l’activation du mode OTG
CH_PROC_HOT Entrée Signal d’erreur du chargeur
CH_CHARGE_OK Entrée Signal de validité du voltage en entrée
Équilibreur BLNC_ALERT Entrée Signal d’erreur de l’équilibreur
IMU IMU_INT1 Entrée (INT) Signal événementiel configurable
IMU_INT2 Entrée (INT) Signal événementiel configurable
MEMS MIC_PWR Entrée Alimentation du microphone
I2S_DATA Entrée Bus I2S
I2S_CLK Entrée Bus I2S

Les signaux interrupts ne doivent jamais porter des sections de code “lourdes” et ne se suffisent pas à gérer des actions complexes.

La consommation des GPIO doit être limitée à moins de 15 mA, ce qui suffira pour les éléments qui consomment le plus (Led RGB).

Les GPIO accessibles à l’utilisateur seront également celles responsables du bouton, sa led RGB et pour la lampe colorée, le signal du ruban de Leds.

Sections critiques

Le programme contient plusieurs sections critiques qui prennent toute la priorité sur les autres fonctions. Ces sections sont les suivantes, dans l’ordre de criticité :

  • Gestion du power path : LED et USB partagent une même section de power path et en AUCUN CAS la porte LED et USB ne doivent être ouvertes en même temps.
  • Gestion de la batterie : La batterie doit être surveillée à tout moment, son voltage son courant et sa température sont des caractéristiques critiques de sécurité.
  • Contrôle des capteurs et des limites systèmes : La température interne, les voltages USB et des Leds et la bonne réponse des composants sont contrôlés très régulièrement.
  • Gestion des interruptions du bouton : les commandes utilisateurs sont très importante, car des dysfonctionnements à ce niveau produisent une grande frustration.
  • Affichage des erreurs : Des erreurs identifiées doivent être remontées rapidement à l’utilisateur.

Gestion des modes de sécurité

Il faut prévoir en avance de phase tous les cas d’échec du système, et comment en récupérer, et pouvoir informer l’utilisateur de tout problème.

Choix d’un système logique

  • Les erreurs sont affichées avec des clignotements de couleur
  • Les informations sont affichées avec des animations “breeze”, plus douces
  • Chaque erreur utilise des couleurs bien différentes faciles à reconnaitre (désolé si vous êtes daltoniens, je n’ai pas trouvé mieux). Certains ont des fréquences plus ou moins rapides pour faire passer un message sur l’urgence.
  • Alertes graves sont plus rapides que les alertes classiques.

La lampe ne doit pas pouvoir être utilisée lors d’alertes graves

  • le système s’éteint tout seul et bloque des fonctionnalités en fonction de la nature de l’alerte.

Si la batterie tombe sous les 4V nécessaires à produire la tension de 3.3 V pour alimenter le système, ou si cette alimentation est défaillante

Comment le savoir ? Sans cette alimentation, le microcontrôleur ne démarrera jamais.

  • Utilisation du VBUS pour alimenter le système sans batterie Si l’USB est branché, on peut utiliser le fil VBUS comme alimentation de secours : en surdimensionnant le signal 5V, on peut fournir au convertisseur 3.3 V une tension d’entrée suffisante pour démarrer le système et permettre le rechargement des batteries même si elles tombent à 0V.

Si un composant arrête de fonctionner, le microcontrôleur doit pouvoir le signaler

  • Ajout d’un contrôle des signaux de statut de tous les composants. Et vérification des statuts dans le code

Si un des batteries se retrouve isolé du reste (bande de nickel qui saute, fil ou connecteur endommagé, etc), les batteries ne peuvent pas être utilisées

  • Ajout d’un contrôle software de l’état des batteries et de l’équilibreur, qui détecte les problèmes grâces a la tension de chaque fil d’équilibrage (une connexion rompue fait sauter la tension d’un fil hors de la zone de voltages autorisée pour un accu)

En cas de situations d’utilisation hors norme, bloquer l’utilisation

  • Contrôle de consommation des batteries à tout moment
  • Contrôle de température en plusieurs points de la carte
  • Détection de courts-circuits sur VBUS et l’USB en général
  • Détection de surtensions sur VBUS

Contrôle des paramètres physiques attendus par rapports aux réels

  • Vérification de la puissance coté USB/LED par rapport a la puissance mesurée coté batterie, elles doivent être proches
  • Limitation de la conso de la board via le PDO sélectionnée USB PD
  • Limitation de l’utilisation de puissance sur VBUS en mode OTG, via le PDO sélectionné par le consommateur.
  • Ajout de TestPoints sur le circuit pour mesurer manuellement les endroits critiques.

En cas de crash logiciel, le système doit pouvoir se remettre en état tout seul, ou au minimum empêcher une mauvaise utilisation

  • Ajout d’un watchdog logiciel, qui reset le programme et la mémoire si aucun signal de vie n’est envoyé au bout d’une période de 10 secondes.
  • Idéalement, il aurait également fallu un watchdog hardware qui reset la board entière en cas d’absence de signal. Ça aurait été possible avec l’équilibreur, mais j’ai manqué de temps.
  • En cas de crash absolu (mémoire corrompue ou autre), GPIO de reset directement sur la board. Suffit de la connecter rapidement 5 fois et le bootloader efface toute la mémoire et le programme, et affiche la board comme un périphérique de stockage de masse.
  • Si même cela échoue, un port hardware est disponible sur la board pour reflasher le bootloader.
Tags: Electronic Design 

Suggestions de lecture :