Ein seltsamer Software-Bug in der BLDC-Steuerung: Achten Sie auf die „Write Only“-Register
Es war einer der seltsamsten Fehler. Das System schien wie erwartet zu funktionieren, aber die Telemetriedaten ließen etwas anderes vermuten. Der Bedarfseingang, der die Drehzahl bestimmt, mit der sich der Motor drehen soll, meldete 85 %, aber zum Glück drehte sich der Motor nicht. Es war verlockend, einfach abzuwinken und zu sagen, dass es keine große Sache war oder dass es ein Problem mit der Software zur Auswertung der Telemetrie war, aber irgendetwas stimmte nicht ganz. Es war an der Zeit, im System herumzustochern und herauszufinden, was diesen Fehler verursachte.
In diesem speziellen System wurde der Treiber-Chip A4964 von Allegro Microsystems für bürstenlose Gleichstrommotoren (BLDC) verwendet. Dieser Chip ist mir besonders ans Herz gewachsen, weil er eine flexible Motorantriebslösung bietet und den gesamten Steuercode, der CPU-Zyklen fressen kann, vom Mikrocontroller (MCU) in einen speziellen Hardware-Chip verlagert (Abbildung 1).
Abbildung 1: Der A4964 ist nützlich, da er die Steuerung eines BLDC-Motors von der MCU auf dedizierte Hardware auslagert. (Bildquelle: Allegro Microsystems)
In dieser Systemkonfiguration habe ich die SPI-Kommunikationsschnittstelle genutzt, um die 32 On-Chip-Register einzurichten, die bestimmen, wie der BLDC-Motor angetrieben und gesteuert wird.
Für die Anwendung wird der A4964 während der Inbetriebnahme durch eine Initialisierungsroutine konfiguriert, indem eine Konfigurationstabelle gelesen wird, die die gewünschten Motor-Konfigurationsparameter enthält. Der Pseudocode (nur als Beispiel) dafür ist unten aufgeführt (Listing 1):
Kopierenfor(uint8_t WriteIndex = 0; WriteIndex < A4964MaxRegister; WriteIndex++)
{
// Write value stored in A4964Config at index WriteIndex to the chip
}
Listing 1: Abgebildet ist die Initialisierungsroutine, die die Motor-Konfigurationsparameter einliest. (Code-Quelle: Jacob Beningo)
Vom Standpunkt der Initialisierung aus gesehen, gibt es nicht viel, was mit dem Code falsch sein könnte, also beschloss ich schnell, die Hauptlogik zu untersuchen, die regelmäßig innerhalb der Anwendung aufgerufen wurde. Dieser Code war ein wenig interessanter. Der Anwendungsbereich war eine strahlungsreiche Umgebung, die die im RAM gespeicherten Werte beeinflussen kann. Die Initialisierungswerte werden beim Start in den RAM-Speicher des A4964 geschrieben. Um eventuell auftretende Bit-Flips schnell zu beheben, wurde der Speicher des A4964 periodisch ausgelesen: Wenn eine Fehlanpassung auftrat, wurden die Einstellungen aktualisiert. Der Pseudocode sah etwa wie folgt aus (Listing 2):
Kopierenfor(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
}
Listing 2: Lokale Strahlung könnte die im RAM gespeicherten Werte beeinflussen. Um Bit-Flips entgegenzuwirken, wurde der Speicher des A4964 periodisch ausgelesen und die Einstellungen bei einer Fehlanpassung aktualisiert. (Code-Quelle: Jacob Beningo)
Wieder einmal ein sehr einfacher Code, bei dem nicht viel schiefgehen kann, und doch wurde irgendwie ein falscher Nachfrage-Eingangswert an den Chip geschrieben. Der Wert schien vorübergehend zu sein, da er in einigen Telemetriedaten auftauchte, bevor er den korrekten Wert meldete, und dann wieder den falschen Wert lieferte. Seltsam!
Was dies noch interessanter machte, war, dass es keinen Konfigurationswert oder Anwendungswert gab, der eine 85% in das Register schreiben würde! Woher kam also dieser „85%“-Wert? Nun, dezimal 85, wenn in hexadezimal umgewandelt, ist 0x55. Gibt es Stellen in der Codebasis, an denen 0x55 vorkommt? Natürlich gibt es das. 0x55 wird als Dummy-Schreibzeichen für Leseoperationen auf dem SPI-Bus verwendet! Aber wie werden Lesevorgänge in Schreibvorgänge umgewandelt?
Nun, es stellt sich heraus, dass die Antwort ziemlich offensichtlich ist, wenn wir uns das Datenblatt des A4964 genauer ansehen (Abbildung 2).
Abbildung 2 : Ein Ausschnitt aus dem Datenblatt zeigt das Problem: Register 30 ist schreibgeschützt! (Bildquelle: Allegro Microsystems)
Das Register 30, das den Bedarfseingang (DI) für den Motor verwaltet, ist ein schreibgeschütztes Register! Der Versuch, aus dem Register zu lesen, hat zur Folge, dass das Dummy-Byte in das Register geschrieben wird! Die einfache Initialisierungs- und Chip-Refresh-Funktion hat versucht, aus dem Bedarfseingangsregister zu lesen, um die Einstellungen zu überprüfen, und dabei versehentlich einen neuen Bedarfseingang geschrieben. Das System funktionierte weiterhin wie erwartet, da das Lesen des Schreibregisters immer dazu führte, dass kurz darauf der richtige Wert geschrieben wurde, aber nicht immer schnell genug, damit der falsche Wert nicht in die Systemtelemetrie gelangte.
Beim A4964 kann ein Softwareentwickler die Konfigurationsdaten nicht einfach über die gesamte Memory Map (Speicherzuordnung) schreiben, sondern muss nach Register 29 aufhören. Die letzten beiden adressierbaren Register sind spezielle Schreib- und Nur-Lese-Register.
Egal, wie viel Mühe wir uns geben, einen Treiber korrekt zu schreiben oder eine Software zu implementieren, manchmal gibt es halt ein Problem. Seltsame Fehler sind oft eine Gelegenheit, etwas Neues über die Hardware zu lernen und führen oft zu neuen Vorgehensweisen. Dieser seltsame kleine Fehler hat dazu geführt, dass ich meiner Liste hinzugefügt habe „sorgfältig auf schreib- und lesegeschützte Register zu achten“ und sicherzustellen, dass ich mit ihnen richtig interagiere. Andernfalls könnte das Lesen dieses schreibgeschützten Registers zu einem katastrophalen Ereignis innerhalb des Systems führen.
Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.
Visit TechForum

