Étrange bogue logiciel dans la commande BLDC : attention aux registres « en écriture seule »
C'était l'un des bogues les plus étranges. Le système semblait fonctionner comme prévu, mais les données télémétriques suggéraient le contraire. L'entrée de demande qui détermine la vitesse à laquelle le moteur doit tourner indiquait 85 %, mais heureusement, le moteur ne tournait pas. Il était tentant de balayer le problème d'un revers de main en prétendant que ce n'était pas grave ou que c'était une erreur du logiciel d'analyse de la télémétrie, mais quelque chose clochait. Le moment était venu de fouiller dans le système pour déterminer la cause de ce bogue.
Ce système utilisait une puce de variateur moteur CC sans balais (BLDC) A4964 d'Allegro Microsystems. Je me suis particulièrement attaché à cette puce, car elle offre une solution flexible de commande du moteur et retire du microcontrôleur (MCU) tout le code de commande qui peut consommer des cycles de processeur pour le mettre sur une puce matérielle dédiée (Figure 1).
Figure 1 : La puce A4964 est utile, car elle transfère la commande d'un moteur BLDC du microcontrôleur vers du matériel dédié. (Source de l'image : Allegro Microsystems)
Dans cette configuration système, j'utilisais l'interface de communication SPI pour configurer les 32 registres intégrés qui déterminent la manière dont le moteur BLDC est commandé et contrôlé.
Pour l'application, la puce A4964 est configurée pendant l'installation par une routine d'initialisation via la lecture d'un tableau de configuration qui contient les paramètres souhaités de configuration du moteur. Le pseudocode (à titre d'exemple) de cette procédure est présenté ci-dessous (Liste 1) :
Copierfor(uint8_t WriteIndex = 0; WriteIndex < A4964MaxRegister; WriteIndex++)
{
// Write value stored in A4964Config at index WriteIndex to the chip
}
Liste 1 : Routine d'initialisation qui lit les paramètres de configuration du moteur. (Source du code : Jacob Beningo)
Du point de vue de l'initialisation, il ne peut pas y avoir grand-chose à redire quant au code. J'ai donc rapidement décidé d'examiner la logique principale qui était utilisée de façon régulière dans l'application. Ce code-ci était un peu plus intéressant. Le domaine d'application était un environnement riche en rayonnements susceptible d'altérer les valeurs stockées dans la RAM. Les valeurs d'initialisation sont écrites dans la RAM de l'A4964 au démarrage. Aussi, pour surmonter rapidement les éventuelles inversions de bits, la mémoire de l'A4964 était lue périodiquement : en cas d'incohérence, les paramètres étaient alors mis à jour. Le pseudocode ressemblait à ce qui suit (Liste 2) :
Copierfor(uint8_t Index = 0; Index < A4964MaxRegister; Index++)
{
// Read the A4964 configuration register at location Index
// If read value does not match expected value, write configuration value
}
Liste 2 : Les rayonnements locaux étaient susceptibles d'altérer les valeurs stockées dans la RAM. Aussi, pour contrer les inversions de bits, la mémoire de l'A4964 était lue périodiquement et les paramètres étaient mis à jour en cas d'incohérence. (Source du code : Jacob Beningo)
Là encore, le code était très simple et peu susceptible de présenter des problèmes. Pourtant, quelque chose écrivait une valeur d'entrée de demande incorrecte sur la puce. La valeur semblait transitoire dans la mesure où elle apparaissait dans certaines données télémétriques avant d'indiquer la valeur correcte, puis d'indiquer à nouveau la valeur incorrecte. Étrange !
Ce qui rendait la question encore plus intéressante, c'est qu'il n'y avait aucune valeur de configuration ou d'application susceptible d'écrire 85 % dans le registre. D'où pouvaient bien venir ces 85 % ? Il se trouve que la valeur 85 en décimal convertie en hexadécimal correspond à 0x55. Est-ce que cette valeur de 0x55 apparaissant quelque part dans le code base ? Bien sûr : 0x55 est utilisé comme caractère d'écriture factice pour les opérations de lecture sur le bus SPI. Mais comment les opérations de lecture sont-elles converties en opérations d'écriture ?
Il s'avère que la réponse est assez évidente si l'on regarde de plus près la fiche technique de l'A4964 (Figure 2).
Figure 2 : Un extrait de la fiche technique montre le problème. Le registre 30 est en écriture seule ! (Source de l'image : Allegro Microsystems)
Le registre 30, qui gère l'entrée de demande (DI) pour le moteur, est un registre en écriture seule. Toute tentative de lecture du registre a pour effet d'écrire l'octet factice dans le registre ! La fonction d'initialisation simple et d'actualisation de la puce tentait de lire le registre d'entrée de demande pour vérifier les paramètres et écrivait par inadvertance une nouvelle entrée de demande. Le système continuait de fonctionner comme prévu, car la lecture du registre en écriture entraînait toujours l'écriture de la valeur correcte peu de temps après, mais pas toujours assez rapidement pour que la valeur incorrecte n'entre pas dans la télémétrie du système.
Avec l'A4964, un développeur logiciel ne peut pas simplement écrire des données de configuration dans toute la carte mémoire, mais doit s'arrêter après le registre 29. Les deux derniers registres adressables sont des registres spéciaux en écriture seule et en lecture seule.
Parfois, quels que soient les efforts déployés pour écrire un pilote ou implémenter un logiciel correctement, il arrive toujours qu'un problème surgisse. Les bogues étranges sont souvent l'occasion d'apprendre de nouvelles choses sur le matériel et d'acquérir de nouvelles bonnes pratiques. Cet étrange petit bogue m'a conduit à prendre en compte la nécessité de faire très attention aux registres en lecture/écriture seule, et m'a permis de veiller à interagir correctement avec eux. Dans le cas contraire, la lecture d'un registre en écriture seule risquerait de provoquer un événement désastreux dans le système.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum

