So previously I had briefly mentioned a scooter built out of a brushless chainsaw; remarkably enough, it worked well enough so that we (Peter and I) decided to write new firmware for the chainsaw to make it behave more like a vehicle and less like a power tool.
The first step was to trace out the schematic for the board. After a couple hours of rooting around the board we came up with this:
The PNP transistors are BC856B'; the NPN transistors are BC846's. The MOSFETS are Alpha Omega AOT470's - pretty generic 100V 10mOhm FETs with 130nC of gate charge; not high performance by any means but they work.
Next we needed to set up the PWMs and interrupts to get the motor turning. The following snippet of code initializes the timers to do block commutation with high-side PWM:
This configures the high side PWM's to be enabled, and leaves the low side pins free for GPIO use.
The actual commutation is done by the following function, which is called every time the hall sensors change:
htable and ltable are lookup tables for the states of the high and low side FETs in each phase.
This was enough to get the motor turning; the next step was to read the current shunt and add some overcurrent detection. Unfortunately, the current surge during motor startup in open-loop mode was enough to trip even a 300A current limit.
We added a current loop, which ran in a main() to avoid conflicting with commutation:
After dealing with a bunch of minor-but-deadly bugs with the current loop (and in the process killing a couple dozen transistors), the motor seemed to more or less turn smoothly.
Next we added stall protection - the firmware would compute the speed of the motor and limit the duty cycle to 10% if the speed was too low. This inevitably resulted in a few more dead transistors due to integral windup, but worked pretty effectively after the bugs were fixed, giving just enough startup torque to move the scooter without pushing dangerous amounts of phase current.
At this point we were a couple weekends and ~50 dead MOSFETs (and about a hundred little BJT's) into the project and it was getting to be pretty late. We figured that before we plugged it into the battery we should investigate the startup transient of the micrcontroller. This resulted in the removal of a bug that caused a brief shoot-through event on startup (the timer duty cycles and complementary modes were not being initialized before the PWM channels were enabled) and the loss of another bridge.
We couldn't figure out what was up with the damaged bridge - no amount of replacing shorted MOSFETs could restore it to functionality. Eventually we replaced all of the transistors, and it worked again. Lesson learned: zombie-FETs are a thing.
Get the code here. To flash it, solder a 4-pin header to the "P0" pads on the board and connect a STM8 programmer, notch side in, to the header. You'll also need to configure the option bytes to set the correct alternate functions for the timer pins, which is pretty trivial in ST Visual Programmer. Code tested to work in IAR studio, definitely will not compile in other environments without some tweaking.
The default settings (constants.h) set the max current limit to ~25A (controller gets real hot above that) and UVLO to 20V (gate drives brown out below that).