All projects

2025

Power Electronics · FOC · Motor Control · Altium

Closed-Loop BLDC Driver

Designed a 24 V field-oriented BLDC driver board and control loop holding ±0.3° position error under a 0.4 Nm step load.

Closed-Loop BLDC Driver hardware

01

Approach & key decisions

  • Topology

    Three-phase inverter with DRV8353 gate drivers into 60 V/100 A NexFETs, switching at 20 kHz. Low-side shunts on all three phases give full current reconstruction rather than the single-shunt estimate most hobby boards use.

  • Sensing and control

    AS5047P magnetic encoder over SPI at 14-bit resolution feeds the FOC transform. Cascaded loops run current at 20 kHz, velocity at 2 kHz, and position at 1 kHz, all in fixed-point math on an STM32G474.

  • Layout discipline

    4-layer stackup with an unbroken inner ground plane, gate loops under 8 mm, and the shunt sense pairs routed as differential Kelvin connections away from the switch node. This is the difference between a loop that converges and one that oscillates.

System spec

MCU
STM32G474 (170 MHz, hardware FPU)
Gate driver
TI DRV8353RS
Encoder
AS5047P, 14-bit, SPI @ 10 MHz
Board
4-layer, 2 oz outer copper, 62 × 62 mm
Control
FOC, SVPWM, anti-windup PI cascade
Tools
Altium, MATLAB, Rigol DS1054Z, thermal camera

02

Build gallery

Bench setup with the motor on a torque arm. Load steps applied by hanging calibrated weights on the pulley.
Bench setup with the motor on a torque arm. Load steps applied by hanging calibrated weights on the pulley.
Rev B power stage. Copper pours on both outer layers act as the heatsink; no fan needed to 12 A continuous.
Rev B power stage. Copper pours on both outer layers act as the heatsink; no fan needed to 12 A continuous.
Phase current at 8 A commanded. Ripple came down to 6% after retuning the current-loop bandwidth to 1.8 kHz.
Phase current at 8 A commanded. Ripple came down to 6% after retuning the current-loop bandwidth to 1.8 kHz.

03

Debugging timeline

  1. Rev A, day 1

    Both high-side FETs on phase A failed short within two seconds of first spin-up.

    Shoot-through from an inadequate dead-time setting. Raised hardware dead-time to 300 ns, added a bench current limit, and never again brought up a power stage without a series bulb.

  2. Rev A, week 2

    Current loop oscillated at roughly 4 kHz under any load.

    The shunt sense traces ran alongside a switch node and picked up switching noise. Rerouted as a Kelvin differential pair on Rev B and added an RC filter matched to the amp bandwidth.

  3. Rev B, week 1

    Encoder reported random 180° jumps above 1500 rpm.

    SPI reads landed inside the PWM switching edge. Moved the read to a timer-triggered event synchronized with the PWM center, which eliminated the fault entirely.

  4. Rev B, week 3

    Position drifted several degrees after long holds against load.

    Integrator windup in the velocity PI. Added conditional-integration anti-windup and clamped the current command, dropping steady-state error to ±0.3°.

04

Results & final demo

  • ±0.3° steady-state position error under a 0.4 Nm step disturbance.
  • 12 A continuous phase current with no forced air, case temperature settling at 61 °C.
  • Smooth 15 rpm crawl with no audible cogging, where the reference hobby ESC stalled below 90 rpm.

05

The problem

A robotics teammate needed a joint actuator that could hold position under sudden load rather than just spin at a commanded speed. Off-the-shelf hobby ESCs run open-loop trapezoidal commutation, which cogs at low speed and cannot report position, so I built a driver around field-oriented control instead.

Next project