A Strange Software Bug in BLDC Control: Watch those “Write Only” Registers

It was one of the strangest bugs. The system appeared to be working as expected, but the telemetry data was suggesting otherwise. The demand input that determines the speed at which the motor should spin was reporting 85%, but thankfully the motor wasn’t spinning. It was tempting to just wave a hand and suggest it wasn’t a big deal, or that it was an issue with the telemetry parsing software, but something wasn’t quite right. It was time to dig in, poke around the system, and determine what was causing this bug.

This particular system was using an Allegro Microsystems A4964 brushless DC (BLDC) motor driver chip. I’ve grown particularly fond of this chip because it provides a flexible motor drive solution, and it gets all the control code that can eat up CPU cycles off the microcontroller (MCU) and into a dedicated hardware chip (Figure 1).

Figure 1: The A4964 is useful as it offloads the control of a BLDC motor from the MCU to dedicated hardware. (Image source: Allegro Microsystems)

In this system configuration, I was leveraging the SPI communication interface to set up the 32 on-chip registers that determine how the BLDC motor is driven and controlled.

For the application, the A4964 is configured during setup by an initialization routine by reading a configuration table that contains the desired motor configuration parameters. The pseudocode (just for example) for which is listed below (Listing 1):

Copyfor(uint8_t WriteIndex = 0; WriteIndex < A4964MaxRegister; WriteIndex++)
{
    // Write value stored in A4964Config at index WriteIndex to the chip
}

Listing 1: Shown is the initialization routine that reads the motor configuration parameters. (Code source: Jacob Beningo)

From an initialization standpoint, there’s not much that could be wrong with the code, so I quickly decided to examine the main logic that was regularly being called within the application. This code was a little bit more interesting. The application domain was a radiation-rich environment that can affect the values stored in RAM. The initialization values are written to RAM on the A4964 at startup, so to quickly overcome any bit flips that might occur, the A4964 memory was periodically read: if a mismatch occurred, then the settings were updated. The pseudocode was something like the following (Listing 2):

Copyfor(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: Local radiation could affect the values stored in RAM, so to counter bit flips, the A4964 memory was periodically read, and the settings were updated if a mismatch occurred. (Code source: Jacob Beningo)

Once again, very simple code that doesn’t have a lot that can go wrong with it, and yet, somehow, something was writing an incorrect demand input value to the chip. The value seemed to be transient in that it appeared in some telemetry before reporting the correct value, and then once again providing the incorrect value. Strange!

What made this even more interesting is that there was no configuration value or application value that would write an 85% to the register! So where on Earth was this 85% value coming from? Well, decimal 85 when converted to hexadecimal is 0x55. Were there any places in the codebase where 0x55 occurs? Of course, there is. 0x55 is being used as the dummy write character for read operations on the SPI bus! But how are read operations being converted to write operations?

Well, it turns out that the answer is pretty obvious once we look closer at the A4964 datasheet (Figure 2).

Figure 2 : A snippet from the datasheet shows the problem: Register 30 is Write Only! (Image source: Allegro Microsystems)

Register 30, which manages the demand input (DI) for the motor is a write-only register! Attempting to read from the register has the effect of writing the dummy byte to the register! The simple initialization and chip refresh function was trying to read from the demand input register to verify the settings and inadvertently writing a new demand input. The system continued to work as expected because the reading of the write register always resulted in writing the correct value shortly afterward, but not always fast enough so that the incorrect value did not make it into the system telemetry.

With the A4964, a software developer can’t just write configuration data throughout the entire memory map but instead needs to stop after register 29. The last two addressable registers are special write and read-only registers.

Sometimes, no matter how much effort we put into correctly writing a driver or implementing software, there’s always some gotcha that pops up. Strange bugs are often an opportunity to learn something new about the hardware and often result in new best practices. This strange little bug has resulted in me adding to my list to, “Watch carefully for read/write only registers” and make sure that I interact with them properly. Otherwise, reading that write-only register might result in a catastrophic event occurring within the system.

About this author

Image of Jacob Beningo

Jacob Beningo is an embedded software consultant. He has published more than 200 articles on embedded software development techniques, is a sought-after speaker and technical trainer, and holds three degrees, including a Masters of Engineering from the University of Michigan.

More posts by Jacob Beningo
 TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum