Sensorless Brushless Can’t Even

Charles wrote a prac­ti­cal and less point­lessly tech­ni­cal post about his SimonK exper­i­ments, complete with a 250 lb system proto­type. These are my hope­fully in-phase science notes.

If you’ve spent any time on hobby avion­ics and mobile robot­ics in the last few years, you’ve heard of the “SimonK reflash.” This refers to firmware writ­ten by the epony­mous Simon Kirby based on the work of Bern­hard Konze for the hobby-grade ATmega8-based sensor­less controllers for brush­less motors.

sim-/tgy
Star

Quadro­tor folks use controllers flashed with SimonK firmware because it does almost no input filter­ing, nearly directly convert­ing its input, a remote control (R/C) style pulse signals, into its output, a pulse-width modu­la­tion (PWM) duty cycle, scaled to to drive a brush­less motor. Mobile robot­ics folks, includ­ing those in my combat circles, use SimonK’s effec­tive sensor­less startup routines and reversible oper­a­tion to run trac­tion systems. They speak rever­ently of the Simon’s amaz­ing powers over aircraft elec­tronic speed controllers (ESCs).

The most bizarre part of this is that the controllers these people are using and hack­ing are avail­able from the likes of Hong Kong-based HobbyK­ing for $5.

HobbyKing store screenshot

Five frig­gin’ 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 Mead­ows Farm mulberry compote, one can spin three-phase motors sounded seri­ous skep­tic alarms in my head.

McDonald's McDouble

By the way, a McDou­ble is $1. Not that it should be, but it is. Bullied farm­ers, count­less suffer­ing animals, and under­paid service work­ers made it possi­ble.

I bought six units of these (ESCs, not McDou­bles) with the inten­tion of figur­ing out what they’re really worth. Ques­tions I want to answer start with,

  1. Can this controller spin a motor?
  2. Can I recon­fig­ure 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 sensor­less brush­less controllers with virtual neutral (explained later) as CCCs: cheap Chinese controllers.

To start, I’m not crit­i­cally exam­in­ing CCCs’ or this partic­u­lar controller’s design and construc­tion. I first want to see it work as rumored. Not to mention that for $5, we know that I’ve bought shitty hard­ware. It doesn’t do me any good to point out the turd nuggets until later, except as prep work and due dili­gence.

Can this controller spin a motor?

Yes.

This on its own is aston­ish­ing. The global manu­fac­tur­ing econ­omy has cut out enough middle­men to offer unit quan­tity comput­er­ized power elec­tron­ics 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 reflash­ing requires access to the micro­con­troller, 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

“Reflash­ing” refers to program­ming the device-inter­nal flash memory in the Atmel ATmega8A micro­con­troller that runs the motor controller with differ­ent firmware.

Confes­sion: I’ve never actu­ally programmed an AVR (the Atmel 8-bit micro­con­troller class of which the ATmega8A is a member) over its in-system program­ming (ISP) inter­face before. I’ve “uploaded” a few Arduino “sketches,” but that’s send­ing the code over a serial link to the Arduino’s already-running AVR, which programs the code to itself.

Arduino upload sketch

In hobby­ist argot, the program the micro­con­troller is running to do this self-program­ming is called a boot­loader, which confus­ingly is a defi­n­i­tion contrary to the use of the word outside of inter­nal-flash system-on-chip (SoC) devices1.

AVR ISP connections

The alter­na­tive is to connect an ISP device that exter­nally drives the program­ming process. I want to program with an ISP program­mer because I don’t know if the ESCs have a boot­loader that I could rely on. Exter­nal program­ming allows you to recover from mistakes (boot­load­ers can and do erase them­selves) and is robust over differ­ent micro­con­troller models.

Inex­plic­a­bly, AVR ISP has two differ­ent stan­dard connec­tors for its phys­i­cal inter­face, a 6- and a 10-pin. You could argue that the pinout on the 10-pin insu­la­tion-displace­ment contact (IDC) connec­tor puts a ground wire between every signal-carry­ing conduc­tor, improv­ing signal integrity, but it’s a several mega­hertz proto­col that doesn’t really need that kind of consid­er­a­tion.

USBasp clone with socket programmer

In my pile of crap, I found a Bus Pirate, Spark­fun Edition that has a 10-pin IDC header not for ISP program­ming and for some reason, a Pololu USB AVR program­mer that has a 6-pin header. I hacked up an adapter and promptly ordered a program­mer with a 10-pin header.

The most notable thing about this setup is what the 10-pin header is attached to. It’s a socket program­ming fixture for the TQFP32-pack­aged ATmega8A. It routes the program­ming signals to spring-loaded claw pins posi­tioned over the correct gull wing leads. This lets you program a chip that’s soldered down, even if its board has no program­ming header exposed. I suspect it’s the tool used by HobbyK­ing for produc­tion on some its boards that lack test points, although these points are present as a row of solder blobs adja­cent to the micro­con­troller on the 20 A board.

AVRDUDE
AVRDUDE instal­la­tion and connec­tion test to see if it can read the target ATmega8A’s fuse bits

Other than that, it’s a straight­for­ward invo­ca­tion of AVRDUDE to flash each controller. On OS X, AVRDUDE is avail­able on Home­brew.

Building the code

Now the ques­tion is how to get and build the SimonK code for this controller. The easi­est way to do this is to have bought a controller on this Google spread­sheet to begin with: ESC specs for Simonk / BLHeli FW flash. My controller (prod­uct ID 9351000061-0) isn’t on there, so I looked at the clos­est ones in the HobbyK­ing Multi­star 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 config­ured to build. There’s also a chance that the design is totally differ­ent from those entirely, since the form factor did change and thus Charles’s “Law of Chinese Pack­ag­ing Iner­tia (‘If the Chinese prod­uct looks the same, it prob­a­bly is the same’)” doesn’t quite apply.

In the Make­file, there’s a suffix rule for .inc to .hex, so I know each of those .hex targets were built from the corre­spond­ing .inc (by symlink­ing ¯\_(ツ)_/¯ the source base­name to that of its target…).

Look­ing at the board, I know it’s all N-chan­nel power tran­sis­tors (FETs) so kda.hex and kda_8khz.hex are out. dlu40a.inc mentions opto-isola­tors and has the wrong resis­tor values (O_POWER and O_GROUND) so its target HEX can be elim­i­nated 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 invert­ing tran­sis­tor from said input (the orange wire) to the micro­con­troller so I was lean­ing 80% towards kda_nfet_ni.hex. Getting that wrong doesn’t blow anything up, but getting the power stage polar­ity or pinout wrong does, so I double checked the .inc decla­ra­tions against the board. The resis­tor divider values were also wrong, but I had a hunch they didn’t matter too much.

SimonK flash make target
Down­load, compil­ing, and flash­ing SimonK firmware

Down­load­ing the code is fairly straight­for­ward, since it’s on GitHub. I also needed to install avra, an AVR assem­bler.

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 build­ing the .hex target with make and flash­ing the file to the controller.

Lego Batman first try

And it worked. First try.

Now, this all seemed “easy” because I yolo’d my controller using an educated guess at a work­ing config. However, you should never take the account of some­thing being easy from an embed­ded systems engi­neer at face value, because we’re about as cred­i­ble on easy things as is an email from Lagos promis­ing rewards for break­ing Bernie Madoff out of prison, espe­cially if firmware is involved.

Bikini Atoll nuclear test

More impor­tantly, you shouldn’t be yolo’ing non-$5 hard­ware. The remain­ing 20% outcome of my 80% confi­dence in kda_nfet_ni.hex was that the controller liter­ally catches fire. There’s a real discov­ery process to finding—or writing—a board config (the .inc file) appro­pri­ate to your controller before you flash it. It’s a combi­na­tion of trac­ing connec­tions on its printed circuit board (PCB) for micro­con­troller pin assign­ments and analyz­ing polar­i­ties used by the stock firmware with a multi­me­ter or oscil­lo­scope. Charles’s writeup has an exam­ple of this and the SimonK README describes the full setup process.

Customizing options

Since the setup went so smoothly, I jumped directly into config­ur­ing SimonK for “robot drive.” These are control para­digms that set controllers used for mobile robot trac­tion oper­a­tion apart from say, R/C airplane propeller drive.

I deduced what these options mean through a combi­na­tion of read­ing the code (it’s 4000 lines of AVR assem­bler, by the way), guess­ing “how I’d imple­ment it,” and through empir­i­cal test­ing with a radio/servo tester and an oscil­lo­scope.

Regenerative braking (COMP_PWM)

Brushless inverter passive diode
Non-regen­er­a­tive default PWM with passive diode free­wheel­ing2

With SimonK in its default config­u­ra­tion, the action of the three-phase inverter in the controller is alter­nat­ing between excit­ing the in-phase coils (one high-side leg on and one low-side on3) and diode free­wheel­ing (only one high side on). The frac­tion of the time spent excit­ing the coils is then the frac­tion of the bus volt­age that is applied to the motor; the motor will accel­er­ate until its induced back-EMF (BEMF) reaches this frac­tion.

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 volt­age plus the FETs’ body diode forward volt­age4. In the first video above, you can see that reduc­ing the throt­tle causes the motors to coast and slowly reduce its speed until it matches the applied (frac­tional) volt­age.

Brushless inverter synchronous rectification
Regen­er­a­tive PWM with high-side synchro­nous rectification/active free­wheel­ing; note the V phase high-side switch turn­ing on synchro­nously with the low-side switch turn­ing off2

Regen­er­a­tive brak­ing is a result of using differ­ent pulse-width modu­la­tion (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 alter­nates between excit­ing the in-phase coils (one high-side leg on and one low-side on) and “active” free­wheel­ing (two high-side legs on). The active free­wheel­ing both brakes the motor (applies torque in the oppo­site direc­tion of motion) and allows the driven phase wind­ings act as a boost converter.

Now you can see that the motor’s speed closely tracks the applied throt­tle. This is because when the BEMF of the motor exceeds the PWM duty cycle × bus volt­age, the motor returns current to the bus. This is the reverse of excit­ing the motor, so current is allowed to flow in both direc­tions and will want to flow until the motor’s BEMF matches its applied volt­age.

This is called COMP_PWM in the code because the phase that switches between high-side conduct­ing and low-side conduct­ing is toggling between its comple­men­tary FETs.

Brushless inverter locked antiphase
Regen­er­a­tive PWM with locked antiphase; not supported by SimonK2

This is slightly mislead­ing because other PWM schemes like locked antiphase or space vector modu­la­tion (SVM) also use comple­men­tary PWM.

RLC series schematic
Series resis­tor-induc­tor-capac­i­tor circuit

An elec­tri­cal engi­neer might consider that motor speed changes don’t happen instan­ta­neously, but instead acts anal­o­gously to a series RLC circuit. The motor speeds up or slows down (increas­ing and decreas­ing BEMF) like the charg­ing and discharg­ing of the capac­i­tor (C). The induc­tance (L) of the phase wind­ings offers “iner­tia” against current (I) changes. The stiff volt­age source is the battery with bus capac­i­tance, and its volt­age (V) varies with PWM duty cycle. And to damp the current slosh­ing in and out of the capacitor/motor speed/induced BEMF is the resis­tance (R) provided by the motor wind­ings, inverter rDS (on), and to some degree, the bus capac­i­tors’ equiv­a­lent series resis­tance (ESR).

You might also consider that this is very little damp­ing, so regen­er­a­tive brak­ing results in massive current spikes, and you’d be right. With­out some limit to how quickly the PWM duty cycle slews or better yet, feed­back control against sensed current, comple­men­tary PWM destroys motor controllers.

Reversible operation (RC_PULS_REVERSE)

SimonK has an option to inter­pret its throt­tle input as bidi­rec­tional, with commanded speeds in oppo­site direc­tions mirrored across a neutral point between the extremes of the input range.

Combined with COMP_PWM, this RC_PULS_REVERSE option allows “four-quad­rant” (4Q) control of your brush­less motor. This means that either forward or reverse torque can be applied to a motor that is spin­ning forward or in reverse. As you can imag­ine, this is crit­i­cal to brush­less motors used for mobile robot trac­tion appli­ca­tions, where differ­en­tial steer­ing like on skid-steer load­ers and tanks is the norm. In this drive­train scheme, torques are commanded in either direc­tion while at any posi­tion or veloc­ity.

I’m pretty impressed by the firmware’s abil­ity to main­tain rotor track­ing during direc­tional changes5.

Neutral braking (MOTOR_BRAKE)

While it’s a bit hard to tell that the motor is revers­ing 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 acti­va­tion using two high-side switches2

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

The result is quicker stop­ping when the motor is commanded to neutral or zero speed. This stop­ping torque would be more signif­i­cant when multi­plied by gear­ing as in a trac­tion system.

A lot of addi­tional settings cover brak­ing torque and ramp­ing.

Variable timing advance (MOTOR_ADVANCE, TIMING_OFFSET)

“Six-step” sensor­less controllers like CCCs are so named because they approx­i­mate three-phase AC output using six steps of PWM’d direct current (DC) output, with each step repre­sent­ing one-sixth or 60° of a rota­tion. DC drive takes up only two motor phases, leav­ing the third undriven and moni­tored for the motor’s BEMF. The tran­si­tion between each step is called commu­ta­tion.

Six step brushless commutation
Six step sensor­less commu­ta­tion; with­out loss of gener­al­ity, zero-cross detec­tion timing (in the high­lighted step) is shown in detail in follow­ing diagram2

Notice that when a phase is left undriven, the BEMF it shows moves from its previ­ous step’s DC state (+ or -) to its next step’s DC state (- or +). The controller moni­tors this tran­si­tion specif­i­cally look­ing for a nega­tive-to-posi­tive or posi­tive-to-nega­tive sign change, or zero-cross, because it occurs at 30° of rota­tion past the previ­ous commu­ta­tion.

By the way, the “zero” referred to isn’t the nega­tive bus volt­age (i.e. the inverter’s zero), but rather the volt­age at the motor’s star—or “neutral”—connection (i.e. the motor’s zero). For simplic­ity with PWM at 100% duty cycle as shown, the volt­age at the motor’s zero is roughly equal to (Vbus+ + Vbus-) ÷ 2, the aver­age of the inverter’s buses. So for these volt­age timing diagrams, you should picture it from the motor’s perspec­tive with zero volts float­ing at half bus volt­age.

Assum­ing the rotor is spin­ning at constant speed, the zero-cross occurs halfway in time through each step. Given the dura­tion (Tzero-cross in the timing diagram) between the previ­ous commu­ta­tion and the zero-cross, the firmware extrap­o­lates the time it takes to rotate to the step’s full 60°. It sched­ules the next commu­ta­tion for that time, which theo­ret­i­cally is Tzero-cross from when zero-cross happened.

Commutation timing advance diagram
Six-step sensor­less zero-cross detec­tion timing26

However, “commu­ta­tion” from the point of view of the motor controller is simply the volt­age applied to the motor. It doesn’t reflect the current running through the motor, which can be delayed signif­i­cantly by the phase wind­ings’ induc­tance. So like most sensor­less firmwares, SimonK provides a timing advance option. It sched­ules commu­ta­tion events to occur ahead of reach­ing the 60° rota­tion mark, all the way up to advanc­ing to the next step imme­di­ately after detect­ing zero-cross at the 30° rota­tion mark7. The MOTOR_ADVANCE option expresses the number of degrees by which to advance (i.e. to subtract from the ideal 60° step dura­tion), corre­spond­ing to the θadvance para­me­ter in the timing diagram.

More­over, there’s yet another option, TIMING_OFFSET, that expresses the microsec­onds by which to advance the next commu­ta­tion. As you might imag­ine, a fixed amount of advance time repre­sents a greater frac­tion of a cycle as speed increases (and cycle times go down), creat­ing a greater effec­tive phase advance. This setting corre­sponds to the Toffset para­me­ter in the timing diagram.

Commutation advance

So, this option in effect creates vari­able timing that commands a more aggres­sive timing with increas­ing speed. There’s even a fan-made calcu­la­tor to compute how much timing advance you get at differ­ent 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-throt­tle current draw.

Other tweaks

SimonK has a huge number of config­u­ra­tion options, with­out much orga­ni­za­tion between board config­u­ra­tion, controller func­tion­al­ity, and user pref­er­ence customiza­tion.

  • Input range: STOP_RC_PULS and FULL_RC_PULS repre­sent the range of pulse widths to use as throt­tle input. With RC_PULS_REVERSE, they are actu­ally max speeds at oppo­site polar­i­ties, with a zero speed input at their aver­age. SimonK firmware can also be “cali­brated” to store these endpoints in non-volatile memory (the AVR’s EEPROM). Also of note are limits for too-short and too-long pulses that should be rejected: MIN_RC_PULS and MAX_RC_PULS. Pulse widths that fall outside of the [STOP_RC_PULS, FULL_RC_PULS] range but fall within [MIN_RC_PULS, MAX_RC_PULS] are clamped to valid power values. For the tests above, I set my limits to [1100 µs, 1900 µs].
  • Dead­band: with RC_PULS_REVERSE, the controller needs a region in the center of the input range that repre­sents a “zero speed” command. Due to timing vari­a­tions in R/C systems (whose trans­mit­ters also have non-negli­gi­ble slop in their control sticks) and even micro­con­trollers, it’s diffi­cult send a pulse that is guar­an­teed to be received as a certain width by the ESC. I set my RCP_DEADBAND to be 20 µs on either side of the center of my input range (1500 µs).
  • PWM frequency: the PWM gener­a­tion in the SimonK code is done by load­ing the dura­tion of on and off inter­vals into a timer and wait­ing for its over­flow inter­rupt, so the result­ing frequency isn’t exact (and prob­a­bly has some jitter). However, it’s roughly a touch less than F_CPU / POWER_RANGE. My 16 MHz board with a POWER_RANGE of 856 was then running at some­thing like 18 kHz, which is corrob­o­rated by oscil­lo­scope shots.
  • Mini­mum duty cycle: once the motor is running, MIN_DUTY is the mini­mum time in each PWM cycle that the inverter is excit­ing the active phases. This is limited by how quickly the low-side FETs can turn on then off (i.e. apply torque) and by how little aver­age volt­age will spin the motor quickly enough to gener­ate a usable BEMF signal. With the aggres­sive default timing advance of 18° and a default mini­mum duty cycle of 6.5%, the mini­mum no-load speed of this ESC/motor combi­na­tion was unrea­son­ably high. More­over, for human control it’s kind of unex­pected that the effec­tive commanded throt­tle should jump from 0 to 6.5% at a few percent stick posi­tion.

There are more options related to motor start behav­ior (on its own a broad topic) and I want to explore them in greater depth before offer­ing remarks.

What’s with the assembly code?

These days it’s pretty rare that a widely used open source project is writ­ten in some computer’s assem­bly language. The code is basi­cally unread­able to even most electrical/software engi­neers. Writ­ing assem­bly requires a lot of tedious busy work like regis­ter manage­ment and call vari­able pass­ing, so a lot of func­tion­al­ity is hidden behind verbose “boil­er­plate” code for say, subtract­ing 24-bit numbers.

On the other hand, given that the code is wholly opti­mized for a single (very limited) micro­con­troller running on a narrow family of ESC boards, assem­bly makes sense. Really, the tiny code space avail­able on the micro­con­troller and the verbosity of AVR’s RISC instruc­tion set assem­bly puts a soft limit on how complex the code can get. To me, that’s a bless­ing. As a result, the complex­ity of func­tion­al­ity in the firmware is fairly manage­able, even if writ­ing and debug­ging new features might be diffi­cult with­out a lot of under­damped head-desk inter­ac­tion.

Next: How does this even work?

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

The most press­ing ques­tion for me is, how do CCCs spin motors with such mini­mal micro­con­troller hard­ware? How does it, running SimonK or not, perform all the sensor­less motor control tasks like detect BEMF zero-cross­ing and switch shoot-through unpro­tected invert­ers with­out explod­ing them?

CCC phases with virtual

SCIENCE TO BE CONTINUED.

  1. Every­where else, it means a program that reads and loads the code to boot, not a program that writes soft­ware to stor­age. []
  2. Base image from Atmel Appli­ca­tion Note AVR444. [] [] [] [] [] []
  3. Remem­ber, one phase is left undriven, with its high- and low-side legs both off. Also, the oppos­ing high- and low-side legs in each phase can’t both be on or the bus would short through them. []
  4. This only occurs when the motor spins faster than the bus volt­age can drive it, so it is not a partic­u­larly useful mode of oper­a­tion. []
  5. i.e. When speed is close to zero and BEMF is low in ampli­tude to being unde­tectable. []
  6. Diagram is intended to be generic to all six-step sensor­less controllers, so it glosses over some SimonK imple­men­ta­tion details like when the timer really resets its counter. []
  7. You might note that since “commu­ta­tion timing” is a delay from the 30° point, zero-cross detec­tion-based controllers can’t advance timing more aggres­sively than (60° – 30°) = 30° ahead of normal. This limi­ta­tion is not usually a prob­lem. []