Skip to content

StallGuard and homing

How the TMC5160 measures motor load without a sensor, how to tune the stall threshold, and how the driver uses it to find the ends of travel and home the axis.

StallGuard2

StallGuard2 compares the back-EMF to the commanded current. SG_RESULT in DRV_STATUS is 0 to 1023: high with a light load, falling as the load rises, and 0 (or close to it) at a stall. The chip raises the stall flag when the value reaches 0 for the current threshold SGT.

Rule Why
SpreadCycle only StallGuard2 does not work in StealthChop. Disable StealthChop, or move above TPWMTHRS
Above TCOOLTHRS The stall output and stop-on-stall are only active above this speed; below it the value is unreliable
Moderate speed At very low speed the back-EMF is too small; at very high speed the motor runs out of voltage
A fixed current SG_RESULT depends on the current; tune at the current you home with

SGT runs from -64 to 63. Higher values make detection less sensitive; start at 0 and raise it when stalls are reported without a load, lower it when real stalls are missed. The filter (enable_filter, sfilt) averages four full steps for a steadier value at a quarter of the update rate.

driver.motorControl.SetStealthChopEnabled(false);       // StallGuard2 needs SpreadCycle

tmc51x0::StallGuardConfig sg{};
sg.threshold = 4;                                       // SGT, -64..63: higher = less sensitive
sg.enable_filter = true;
sg.min_velocity = 0.5f;                                 // TCOOLTHRS: ignore stalls below this speed
sg.velocity_unit = Unit::RevPerSec;
sg.stop_on_stall = true;                                // sg_stop in SW_MODE
if (!driver.stallGuard.ConfigureStallGuard(sg)) {
  return false;
}
return driver.stallGuard.IsStallDetected().ValueOr(false);

stallGuard.GetStallGuardResult() reads SG_RESULT; IsStallDetected() reads the latched stall event and ClearStallFlag() clears it. With stop_on_stall the ramp generator stops the motor itself (SW_MODE.sg_stop). The datasheet forbids combining it with soft stop (SetSoftStop(true), deceleration on a switch event); the driver returns INVALID_STATE if you try.

Tune the threshold

The driver can search for SGT by running the motor at a target speed and testing thresholds:

tmc51x0::StallGuardTuningResult result{};
auto r = driver.tuning.AutoTuneStallGuard(60.0f, result,          // target speed, in RPM below
                                          -20, 20,                 // SGT search range
                                          2.0f,                    // acceleration, rev/s²
                                          30.0f, 90.0f,            // speeds the SGT must also work at
                                          Unit::RPM, Unit::RevPerSec,
                                          0.3f);                   // 30 % current while tuning
if (r && result.tuning_success) {
  tmc51x0::StallGuardConfig sg{};
  sg.threshold = result.optimal_sgt;
  driver.stallGuard.ConfigureStallGuard(sg);
}
Call What it does
tuning.TuneStallGuard() Finds the best SGT at the target speed, then checks it at the minimum and maximum speeds you give
tuning.AutoTuneStallGuard() The same with the motor current reduced (30 % by default) for the duration, which makes stalls easier to detect and gentler on the mechanics

The result reports optimal_sgt, whether it was found (tuning_success) and, when the minimum or maximum speed does not work with it, the speeds that do. Run the tuning with the motor free to turn: it does not need a hard stop. The stallguard_tuning example runs it on hardware.

Find bounds and home

homing.FindBounds() drives towards both ends of travel, records where it stopped, backs off and places the home position:

using Homing = Driver::Homing;

Homing::BoundsOptions opt{};
opt.speed_unit = Unit::RPM;
opt.position_unit = Unit::Deg;
opt.search_speed = 30.0f;          // RPM
opt.search_span = 720.0f;          // at most two turns each way
opt.backoff_distance = 5.0f;       // degrees off the hard stop
opt.timeout_ms = 10000;

Homing::HomeConfig home{};
home.mode = Homing::HomePlacement::AtCenter;   // XACTUAL = 0 in the middle of the travel

auto bounds = driver.homing.FindBounds(Homing::BoundsMethod::StallGuard, opt, home);
if (bounds && bounds.Value().bounded) {
  std::printf("travel %.1f .. %.1f deg\n", bounds.Value().min_bound, bounds.Value().max_bound);
}
BoundsMethod Detects an end by Needs
StallGuard A stall against a hard stop A tuned SGT; the routine switches to SpreadCycle and reduces the current (current_reduction_factor, 0.3 by default)
Switch The left or right reference switch Switches on REFL / REFR, configured with switches
Encoder The encoder no longer following the motor An ABN encoder configured with encoder
HomePlacement Position 0 after homing
None Unchanged
AtMin, AtMax At the lower or upper end
AtCenter In the middle of the travel
AtOffsetFromMin offset (in offset_unit) above the lower end

BoundsOptions sets the units, search speed, the most distance to try in each direction (search_span), backoff_distance, a timeout per direction, and optional acceleration and StallGuard overrides. The routines save StealthChop, currents and StallGuard settings first and restore them afterwards. Every routine takes an optional cancel callback (bool (*)()), polled while the motor moves.

To home in one direction only, use PerformSensorlessHoming(), PerformSwitchHoming() or PerformEncoderIndexHoming(); each returns the final position in microsteps.

Homing moves into hard stops

Sensorless homing deliberately stalls the motor. Use a reduced current and a low search speed, and check that your mechanics take the impact. Set search_span so a missed stall cannot drive the axis far past its travel.

Next: Multi-chip.