Skip to main content
geeks have feelings

Sensorless Brushless Can’t Even

Charles wrote a practical and less pointlessly technical post about his SimonK experiments, complete with a 250 lb system prototype. These are my hopefully in-phase science notes.

If you’ve spent any time on hobby avionics and mobile robotics in the last few years, you’ve heard of the “SimonK reflash.” This refers to firmware written by the eponymous Simon Kirby based on the work of Bernhard Konze for the hobby-grade ATmega8-based sensorless controllers for brushless motors.

sim-/tgy

Quadrotor folks use controllers flashed with SimonK firmware because it does almost no input filtering, nearly directly converting its input, a remote control (R/C) style pulse signals, into its output, a pulse-width modulation (PWM) duty cycle, scaled to to drive a brushless motor. Mobile robotics folks, including those in my combat circles, use SimonK’s effective sensorless startup routines and reversible operation to run traction systems. They speak reverently of the Simon’s amazing powers over aircraft electronic speed controllers (ESCs).

The most bizarre part of this is that the controllers these people are using and hacking are available from the likes of Hong Kong-based HobbyKing for $5.

HobbyKing store screenshot

Five friggin’ dollars.

I live down the street from $4 toast. So the idea that for 20% more than a slice of flame-kissed bran smeared with Fresh Meadows Farm mulberry compote, one can spin three-phase motors sounded serious skeptic alarms in my head.

McDonald's McDouble

By the way, a McDouble is $1. Not that it should be, but it is. Bullied farmers, countless suffering animals, and underpaid service workers made it possible.

I bought six units of these (ESCs, not McDoubles) with the intention of figuring out what they’re really worth. Questions I want to answer start with,

  1. Can this controller spin a motor?
  2. Can I reconfigure SimonK for this controller so that when reflashed, it still spins a motor?
  3. Do the cool SimonK features that combat robot folks use work with it?

For brevity (lol.), I’m going to refer to this family of ATmega8A-based sensorless brushless controllers with virtual neutral (explained later) as CCCs: cheap Chinese controllers.

To start, I’m not critically examining CCCs’ or this particular controller’s design and construction. I first want to see it work as rumored. Not to mention that for $5, we know that I’ve bought crappy hardware. It doesn’t do me any good to point out the turd nuggets until later, except as prep work and due diligence.

Can this controller spin a motor? §

Yes.

This on its own is astonishing. The global manufacturing economy has cut out enough middlepeople to offer unit quantity computerized power electronics through retail outfits taking credit card payments in US Dollars, shipped to your door. Take a breath.

SimonK functional test §

ESC heat shrink removal

OK, I said I wasn’t gonna delve into the details, but the reflashing requires access to the microcontroller, so I do at least need to take it apart.

Multistar 20A 6S Slim top

Multistar 20A 6S Slim bottom

Yup, that’s a motor controller.

Flashing the firmware §

“Reflashing” refers to programming the device-internal flash memory in the Atmel ATmega8A microcontroller that runs the motor controller with different firmware.

Confession: I’ve never actually programmed an AVR (the Atmel 8-bit microcontroller class of which the ATmega8A is a member) over its in-system programming (ISP) interface before. I’ve “uploaded” a few Arduino “sketches,” but that’s sending the code over a serial link to the Arduino’s already-running AVR, which programs the code to itself.

Arduino upload sketch

In hobbyist argot, the program the microcontroller is running to do this self-programming is called a bootloader, which confusingly is a definition contrary to the use of the word outside of internal-flash system-on-chip (SoC) devices*.

AVR ISP connections

The alternative is to connect an ISP device that externally drives the programming process. I want to program with an ISP programmer because I don’t know if the ESCs have a bootloader that I could rely on. External programming allows you to recover from mistakes (bootloaders can and do erase themselves) and is robust over different microcontroller models.

Inexplicably, AVR ISP has two different standard connectors for its physical interface, a 6- and a 10-pin. You could argue that the pinout on the 10-pin insulation-displacement contact (IDC) connector puts a ground wire between every signal-carrying conductor, improving signal integrity, but it’s a several megahertz protocol that doesn’t really need that kind of consideration.

In my pile of crap, I found a Bus Pirate, Sparkfun Edition that has a 10-pin IDC header not for ISP programming and for some reason, a Pololu USB AVR programmer that has a 6-pin header.

Jumpers between 10-pin and 6-pin adapter
Impromptu programmer adapter

I hacked up an adapter and promptly ordered a programmer with a 10-pin header.

USBasp clone with socket programmer

The most notable thing about this setup is what the 10-pin header is attached to. It’s a socket programming fixture for the TQFP32-packaged ATmega8A. It routes the programming signals to spring-loaded claw pins positioned over the correct gull wing leads. This lets you program a chip that’s soldered down, even if its board has no programming header exposed. I suspect it’s the tool used by HobbyKing for production on some its boards that lack test points, although these points are present as a row of solder blobs adjacent to the microcontroller on the 20 A board.

AVRDUDE
AVRDUDE installation and connection test to see if it can read the target ATmega8A’s fuse bits

Other than that, it’s a straightforward invocation of AVRDUDE to flash each controller. On OS X, AVRDUDE is available on Homebrew.

Building the code §

Now the question is how to get and build the SimonK code for this controller. The easiest way to do this is to have bought a controller on this Google spreadsheet to begin with: ESC specs for Simonk / BLHeli FW flash. My controller (product ID 9351000061-0) isn’t on there, so I looked at the closest ones in the HobbyKing Multistar section.

SimonK compatibility spreadsheet

That gives me the choices of kda.hex, kda_nfet.hex, kda_nfet_ni.hex, kda_8khz.hex, and dlu40a.hex, each a board target for which the SimonK code can be configured to build. There’s also a chance that the design is totally different from those entirely, since the form factor did change and thus Charles’s “Law of Chinese Packaging Inertia (‘If the Chinese product looks the same, it probably is the same’)” doesn’t quite apply.

In the Makefile, there’s a suffix rule for .inc to .hex, so I know each of those .hex targets were built from the corresponding .inc (by symlinking ¯\_(ツ)_/¯ the source basename to that of its target…).

Looking at the board, I know it’s all N-channel power transistors (FETs) so kda.hex and kda_8khz.hex are out. dlu40a.inc mentions opto-isolators and has the wrong resistor values (O_POWER and O_GROUND) so its target HEX can be eliminated as well.

That leaves kda_nfet.hex and kda_nfet_ni.hex. Their .inc’s differ only on whether they invert the pulse width input signal. I couldn’t trace any inverting transistor from said input (the orange wire) to the microcontroller so I was leaning 80% towards kda_nfet_ni.hex. Getting that wrong doesn’t blow anything up, but getting the power stage polarity or pinout wrong does, so I double checked the .inc declarations against the board. The resistor divider values were also wrong, but I had a hunch they didn’t matter too much.

SimonK flash make target
Download, compiling, and flashing SimonK firmware

Downloading the code is fairly straightforward, since it’s on GitHub. I also needed to install avra, an AVR assembler.

brew install avra
git clone https://github.com/sim-/tgy.git
cd tgy/
make kda_nfet_ni.hex
avrdude -c usbasp -p m8 -U flash:w:kda_nfet_ni.hex:i

After that, it was just a matter of building the .hex target with make and flashing the file to the controller.

Lego Batman first try

And it worked. First try.

vin7yKi08oM thumbnail

Now, this all seemed “easy” because I yolo’d my controller using an educated guess at a working config. However, you should never take the account of something being easy from an embedded systems engineer at face value, because we’re about as credible on easy things as is an email from Lagos promising rewards for breaking Bernie Madoff out of prison, especially if firmware is involved.

Bikini Atoll nuclear test
don't screw up

More importantly, you shouldn’t be yolo’ing non-$5 hardware. The remaining 20% outcome of my 80% confidence in kda_nfet_ni.hex was that the controller literally catches fire. There’s a real discovery process to finding—or writing—a board config (the .inc file) appropriate to your controller before you flash it. It’s a combination of tracing connections on its printed circuit board (PCB) for microcontroller pin assignments and analyzing polarities used by the stock firmware with a multimeter or oscilloscope. Charles’s writeup has an example of this and the SimonK README describes the full setup process.

Customizing options §

Since the setup went so smoothly, I jumped directly into configuring SimonK for “robot drive.” These are control paradigms that set controllers used for mobile robot traction operation apart from say, R/C airplane propeller drive.

I deduced what these options mean through a combination of reading the code (it’s 4000 lines of AVR assembler, by the way), guessing “how I’d implement it,” and through empirical testing with a radio/servo tester and an oscilloscope.

Regenerative braking (COMP_PWM) §

Brushless inverter passive diode
Non-regenerative default PWM with passive diode freewheeling. Base image from Atmel Application Note AVR444

With SimonK in its default configuration, the action of the three-phase inverter in the controller is alternating between exciting the in-phase coils (one high-side leg on and one low-side on) and diode freewheeling (only one high side on). The fraction of the time spent exciting the coils is then the fraction of the bus voltage that is applied to the motor; the motor will accelerate until its induced back-EMF (BEMF) reaches this fraction.

However, neither of those states allows the motor to return energy to the bus, unless its BEMF (which is linear to its speed) exceeds the bus voltage plus the FETs’ body diode forward voltage. In the first video above, you can see that reducing the throttle causes the motors to coast and slowly reduce its speed until it matches the applied (fractional) voltage.

Brushless inverter synchronous rectification
Regenerative PWM with high-side synchronous rectification/active freewheeling; note the V phase high-side switch turning on synchronously with the low-side switch turning off. Base image from Atmel Application Note AVR444

Regenerative braking is a result of using different pulse-width modulation (PWM) schemes that allow the motor to flow current back into the controller’s bus during at least part of the cycle. SimonK’s optional PWM scheme alternates between exciting the in-phase coils (one high-side leg on and one low-side on) and “active” freewheeling (two high-side legs on). The active freewheeling both brakes the motor (applies torque in the opposite direction of motion) and allows the driven phase windings act as a boost converter.

EUbHGjDRloQ thumbnail

Now you can see that the motor’s speed closely tracks the applied throttle. This is because when the BEMF of the motor exceeds the PWM duty cycle × bus voltage, the motor returns current to the bus. This is the reverse of exciting the motor, so current is allowed to flow in both directions and will want to flow until the motor’s BEMF matches its applied voltage.

This is called COMP_PWM in the code because the phase that switches between high-side conducting and low-side conducting is toggling between its complementary FETs.

Brushless inverter locked antiphase
Regenerative PWM with locked antiphase; not supported by SimonK. Base image from Atmel Application Note AVR444

This is slightly misleading because other PWM schemes like locked antiphase or space vector modulation (SVM) also use complementary PWM.

RLC series schematic

An electrical engineer might consider that motor speed changes don’t happen instantaneously, but instead acts analogously to a series RLC (resistor-inductor-capacitor) circuit. The motor speeds up or slows down (increasing and decreasing BEMF) like the charging and discharging of the capacitor (C). The inductance (L) of the phase windings offers “inertia” against current (I) changes. The stiff voltage source is the battery with bus capacitance, and its voltage (V) varies with PWM duty cycle. And to damp the current sloshing in and out of the capacitor/motor speed/induced BEMF is the resistance (R) provided by the motor windings, inverter rDS (on), and to some degree, the bus capacitors’ equivalent series resistance (ESR).

You might also consider that this is very little damping, so regenerative braking results in massive current spikes, and you’d be right. Without some limit to how quickly the PWM duty cycle slews or better yet, feedback control against sensed current, complementary PWM destroys motor controllers.

Reversible operation (RC_PULS_REVERSE) §

SimonK has an option to interpret its throttle input as bidirectional, with commanded speeds in opposite directions mirrored across a neutral point between the extremes of the input range.

iV8bKyUxft0 thumbnail

Combined with COMP_PWM, this RC_PULS_REVERSE option allows “four-quadrant” (4Q) control of your brushless motor. This means that either forward or reverse torque can be applied to a motor that is spinning forward or in reverse. As you can imagine, this is critical to brushless motors used for mobile robot traction applications, where differential steering like on skid-steer loaders and tanks is the norm. In this drivetrain scheme, torques are commanded in either direction while at any position or velocity.

I’m pretty impressed by the firmware’s ability to maintain rotor tracking during directional changes§.

Neutral braking (MOTOR_BRAKE) §

While it’s a bit hard to tell that the motor is reversing in the RC_PULS_REVERSE video, it’s easier to tell that when the motor is command to zero speed, it’ll coast until it comes to a stop.

Brushless inverter brake
Brake activation using two high-side switches. Base image from Atmel Application Note AVR444

SimonK (and even most stock airplane ESC firmwares) provides an option to brake the motor by turning on FETs along one side of the inverter. This short circuits the induced current through the inverter. Due to Lenz’s law, the direction of the induced current creates a torque that brakes the motor. Like when driving the motor, the braking can also be PWM’d so it’s only active for some fraction of time (BRAKE_POWER setting).

odm_H0dj0w4 thumbnail

The result is quicker stopping when the motor is commanded to neutral or zero speed. This stopping torque would be more significant when multiplied by gearing as in a traction system.

A lot of additional settings cover braking torque and ramping.

Variable timing advance (MOTOR_ADVANCE, TIMING_OFFSET) §

“Six-step” sensorless controllers like CCCs are so named because they approximate three-phase alternating current (AC) output using six steps of PWM’d direct current (DC) output, with each step representing one-sixth or 60° of a rotation. DC drive takes up only two motor phases, leaving the third undriven and monitored for the motor’s BEMF. The transition between each step is called commutation.

Six step brushless commutation
Six step sensorless commutation; without loss of generality, zero-cross detection timing (in the highlighted step) is shown in detail in following diagram. Base image from Atmel Application Note AVR444

Notice that when a phase is left undriven, the BEMF it shows moves from its previous step’s DC state (+ or −) to its next step’s DC state (− or +). The controller monitors this transition specifically looking for a negative-to-positive or positive-to-negative sign change, or zero-cross, because it occurs at 30° of rotation past the previous commutation.

By the way, the “zero” referred to isn’t the negative bus voltage (i.e. the inverter’s zero), but rather the voltage at the motor’s star—or “neutral”—connection (i.e. the motor’s zero). For simplicity with PWM at 100% duty cycle as shown, the voltage at the motor’s zero is roughly equal to (Vbus+ + Vbus−) ÷ 2, the average of the inverter’s buses. So for these voltage timing diagrams, you should picture it from the motor’s perspective with zero volts floating at half bus voltage.

Assuming the rotor is spinning at constant speed, the zero-cross occurs halfway in time through each step. Given the duration (Tzero-cross in the timing diagram) between the previous commutation and the zero-cross, the firmware extrapolates the time it takes to rotate to the step’s full 60°. It schedules the next commutation for that time, which theoretically is Tzero-cross from when zero-cross happened.

Commutation timing advance diagram
Six-step sensorless zero-cross detection timing. Base image from Atmel Application Note AVR444. Diagram is intended to be generic to all six-step sensorless controllers, so it glosses over some SimonK implementation details like when the timer really resets its counter

However, “commutation” from the point of view of the motor controller is simply the voltage applied to the motor. It doesn’t reflect the current running through the motor, which can be delayed significantly by the phase windings’ inductance. So like most sensorless firmwares, SimonK provides a timing advance option. It schedules commutation events to occur ahead of reaching the 60° rotation mark, all the way up to advancing to the next step immediately after detecting zero-cross at the 30° rotation mark. The MOTOR_ADVANCE option expresses the number of degrees by which to advance (i.e. to subtract from the ideal 60° step duration), corresponding to the θadvance parameter in the timing diagram.

Moreover, there’s yet another option, TIMING_OFFSET, that expresses the microseconds by which to advance the next commutation. As you might imagine, a fixed amount of advance time represents a greater fraction of a cycle as speed increases (and cycle times go down), creating a greater effective phase advance. This setting corresponds to the Toffset parameter in the timing diagram.

Commutation advance

So, this option in effect creates variable timing that commands a more aggressive timing with increasing speed. There’s even a fan-made calculator to compute how much timing advance you get at different speeds. In my videos, I was running 2° of MOTOR_ADVANCE and 37 μs of TIMING_OFFSET that I found to hit a happy local minima for low-throttle current draw.

Other tweaks §

SimonK has a huge number of configuration options, without much organization between board configuration, controller functionality, and user preference customization.

There are more options related to motor start behavior (on its own a broad topic) and I want to explore them in greater depth before offering remarks.

What’s with the assembly code? §

These days it’s pretty rare that a widely used open source project is written in some computer’s assembly language. The code is basically unreadable to even most electrical/software engineers. Writing assembly requires a lot of tedious busy work like register management and call variable passing, so a lot of functionality is hidden behind verbose “boilerplate” code for say, subtracting 24-bit numbers.

On the other hand, given that the code is wholly optimized for a single (very limited) microcontroller running on a narrow family of ESC boards, assembly makes sense. Really, the tiny code space available on the microcontroller and the verbosity of AVR’s RISC instruction set assembly puts a soft limit on how complex the code can get. To me, that’s a blessing. As a result, the complexity of functionality in the firmware is fairly manageable, even if writing and debugging new features might be difficult without a lot of underdamped head-desk interaction.

Next: How does this even work? §

Now that I’ve looked whether the firmware/controller combination works, I wanted to dig into how it works.

The most pressing question for me is, how do CCCs spin motors with such minimal microcontroller hardware? How does it, running SimonK or not, perform all the sensorless motor control tasks like detect BEMF zero-crossing and switch shoot-through unprotected inverters without exploding them?

CCC phases with virtual

SCIENCE TO BE CONTINUED.

Every Post by Year

  1. 2023
    1. Ducati Timing Belt Replacement
    2. C++ Corrections
  2. 2016
    1. Liftlord
    2. Sensorless Brushless Can’t Even
  3. 2015
    1. Big Data: Test & Refresh
  4. 2014
    1. The Orange Involute
    2. Big Data EVT
  5. 2013
    1. Integer Arithmetic Continued
    2. Real Talk: Integer Arithmetic
    3. Why Microsoft’s 3D Printing Rocks
    4. Flapjack Stator Thoughts
    5. Delicious Axial Flux Flapjack
  6. 2012
    1. How to teach how to PCB?
    2. Fixed-point atan2
    3. It Was Never About the Mileage
    4. Trayrace
    5. BabyCorntrolling
    6. Conkers
    7. BabyCorntroller
    8. Templated numerical integrators in C++
  7. 2011
    1. Bringing up Corntroller
    2. Assembly-izing Tassel
    3. Corn-Troller: Tassel
    4. 5 V to 3.3 V with Preferred Resistors
  8. 2010
    1. HÄRDBÖRD: Interesting Bits
    2. HÄRDBÖRD: Hardcore Electric Longboard
    3. Mistakes to Make on a Raytracer
    4. US International Dvorak
  9. 2009
    1. Raxo
    2. Better Spheres, Fewer Triangles
    3. Donald Knuth Finally Sells Out
    4. Harpy – Sumo Bots 2009