Changelog¶
All notable changes to this project are documented in this file. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.2.0 - 2026-11-06¶
Fixed¶
- Stall detection missed a slipping rotor at low speed. The back-EMF vector of a slipping
rotor turns in the commanded frame and stayed above
stall_sync_pctabout half of the time, so the up/down counter did not accumulate: a heavy axis (load inertia ×100) lost hundreds of full steps without a fault. A reversed back-EMF now keeps the condition true for half of the trip time:SSTEP_FAULT_STALL43 to 60 ms after the load angle passes 90°. - False stall under a heavy load. The inductance error adds to \(\cos\delta\) (it does not
scale it, as the documentation said), so a synchronous rotor at 72 % of the pull-out torque
with \(R\) +30 %, \(L\) −20 % tripped. The condition in motion now also requires the d axis
inside the window of a rotor that does not turn. The same window removes the false trip at
the voltage limit: 11 rev/s at 24 V now runs with the example
stall_sync_pctof 50.
Changed¶
SSTEP_FAULT_OVERCURRENTcomes together withSSTEP_FAULT_STALLwhen the trip finds the stall detector counting: a light rotor reaches the trip beforestall_time_ms, and the position is lost. An application that comparesfaultswith a single flag must test the bits. Without stall detection nothing changes.- A load angle swinging repeatedly beyond about 120° near the voltage limit is reported as a stall even before a step is lost (documented in Limits).
SSTEP_STATE_BYTESon 64-bit hosts: 288 B fixed point, 312 B float (Cortex-M4 unchanged: 280 B, 304 B). Flash on Cortex-M4 (-Os): 9216 B fixed point, 9894 B float. Worst-case stack ofsstep_slow_loop()152 B fixed point (float 140 B); the slow loop executes about 145 more instructions per period with stall detection on (-Os); the fast loop is unchanged.- The version comes only from the
vX.Y.ZGit tag.sstep.cmakenow applies it to the applications that include it, and the headers of the release package carry it. A header taken from the repository and built without CMake reports0.0.0(unknown).CITATION.cffno longer states a version or a release date.
1.1.0 - 2026-11-01¶
Sensorless stall detection. Compatible with 1.0.0: the new configuration fields are zero in existing initializers, which keeps stall detection off and the behaviour unchanged; rebuild the application with the new headers (the instance size changed).
Added¶
- Stall detection (
src/sstep_stall.c): a back-EMF observer in the fast loop (e = v − R·i − L·di/dt from the applied voltage, the measured currents and the commanded angle, filtered in the commanded current frame) and a decision in the slow loop. In motion it checks that the back-EMF along the commanded motion stays abovestall_sync_pctof Ke·ω; at rest and at low speed it checks that the rotor does not turn faster than the command by more thanstall_speed, with the observer offset (resistance, dead time) learned while the rotor follows. An up/down counter raises the new faultSSTEP_FAULT_STALL(0x0020U) afterstall_time_ms, with the usual fault behaviour (outputs off,SSTEP_STATE_FAULT,on_fault(), cleared bysstep_clear_faults()). - Configuration (
struct sstep_ctrl_cfg):stall_time_ms(0 = off, the default),stall_filter_us,stall_speed,stall_sync_pct.sstep_init()returnsSSTEP_ERR_CONFIGfor stall detection with voltage drive or withoutke_uv_s_per_rad. - Status (
struct sstep_status):emf_d_mv,emf_q_mv,rotor_velocity,load_angle_cdeg,stall_level_pct. - Documentation: the stall detection section of Motor Control (principle, the exact
equations for a model, design decisions, parameter choice, measured results, cost,
limits), configuration, usage, integration and API updates. New hero illustrations
(
docs/assets/images/hero-dark.pngfor the website and the PDF cover,hero-light.pngfor the README).
Changed¶
struct sstepgrows by 96 B (432 B fixed point, 456 B float on Cortex-M4):SSTEP_CONFIG_BYTES152 / 176 B,SSTEP_STATE_BYTES280 / 304 B.struct sstep_statusgrows from 32 B to 48 B.- Flash on Cortex-M4 (
-Os): 9040 B fixed point, 9718 B float. Worst-case stack ofsstep_fast_loop()216 B fixed point (128 B float), ofsstep_get_status()92 B. - With stall detection on, the fast loop executes about 240 more instructions per
period (
-O2, both backends; 475 fixed point and 213 float with-Os).
Quality¶
- 93 GoogleTest tests (fixed point) and 92 (float), 100 % line, branch and function
coverage of
src/in both backends. The motor model of the tests gained a detent torque, a dead-time voltage error, a configurable current quantization and an imposed rotor motion for synthetic tests. - Stall detection on the motor model: the reference case (17HS19-2004S1, three-leg stage,
24 V, 0.12 N·m held at 1.5 A but not at the 0.5 A hold current) reports
SSTEP_FAULT_STALL48 ms after the load angle passes 90°; a hard stop at 3 rev/s after 50 ms. No fault through enable, run → hold, moves, reversal and runs from 0.125 to 11 rev/s, also with R +30 %, L −20 %, 70 mV dead time per leg, 29 mA current quantization and detent torque (largest detector level 32 %). - MISRA C:2012: 0 violations, no new deviation.
1.0.0 - 2026-10-31¶
First release: closed-loop microstepping current control for two-phase hybrid stepper motors in portable C11.
Added¶
- Public API (
sstep.h, 15 functions):sstep_init(),sstep_enable(),sstep_disable(),sstep_fast_loop(),sstep_slow_loop(),sstep_move_to(),sstep_move_by(),sstep_run(),sstep_stop(),sstep_set_position(),sstep_set_current(),sstep_set_motion_limits(),sstep_report_fault(),sstep_clear_faults(),sstep_get_status(). - Types (
inc/sstep_ctrl.h,inc/sstep_defs.h): configuration in integer engineering units (motor, power stage and measurement, control and motion), hardware callbacks given at initialization (pwm_write,output_enable, optionalon_fault,on_event), raw ADC sample, status in engineering units, status codes, fault flags, driver states.pwm_freq_hzanddead_time_nsare reserved for later versions. - Opaque driver instance:
struct sstepisuint64_t opaque[SSTEP_INSTANCE_WORDS], allocated by the application (336 B fixed point, 360 B float on Cortex-M4); the driver state and every numeric type stay private insrc/. - Current control: PI controller per phase with gains derived from R, L and the
bandwidth (
Kp = L wc,Ki = R wc), back-EMF feed-forward, back-calculation anti-windup, bus voltage compensation; open-loop voltage drive for boards without current sensing. - Microstepping: 1 to 256 microsteps per full step, 32-bit electrical angle,
quarter-wave sine table with linear interpolation (generated by
tools/sstep_gen_sin_table.py), angle interpolated at the fast-loop rate. - Motion planner: trapezoidal moves without overshoot in integer arithmetic
(positions in 1/65536 microstep, 64 bits), separate acceleration and deceleration,
smooth retargeting, constant-velocity runs, stop, homing with
sstep_set_position(), hold current after a standstill delay,SSTEP_EVENT_MOVE_DONEandSSTEP_EVENT_HOLD_CURRENT. - Power stages: two H-bridges (4 legs) and three half-bridges with a shared leg (3 legs); the voltage request is limited to the topology with its direction kept.
- Protection: overcurrent, overvoltage and undervoltage trips in the fast loop,
callback errors and external faults switch the outputs off in the same period.
Every function except
sstep_init()returnsSSTEP_ERR_STATEfor an instance that was not initialized successfully, without touching the hardware. - Numeric backends: fixed point (Q15/Q31, integer only) and single-precision float,
selected with
SSTEP_MATH_BACKEND(CMake) orSSTEP_CFG_MATH_BACKEND. - Build-time options (
inc/sstep_config.h, overridable insstep_user_config.h): topologies, largest microstep division, sine table size, argument checks of the fast loop. - Build: CMake library target in
sstep.cmake, presets for Debug, Release and the unit tests of both backends;misra-analyze,cppcheck-analyzeandcodeql-analyzetargets. - Documentation: website and PDF manual: introduction, getting started, architecture, usage, extending, configuration with the measured stack and memory footprint, motor control theory with measured results, integration guide, API reference, MISRA compliance.
Quality¶
- 78 GoogleTest tests (fixed point) and 77 (float): numeric backend, power stage,
motion planner, API and closed loop on a motor model (
tests/unit/sstep_test_plant.hpp); 100 % line, branch and function coverage ofsrc/in both backends. - Closed loop on the motor model (17HS19-2004S1, 24 V, 20 kHz, 1 kHz bandwidth), matching the Scilab prototype: current step overshoot 1.11 %, settling 0.40 ms; 8 rev/s current 1.862 A with two H-bridges and 1.557 A with a shared leg; load angle at 0.25 N·m 37.21°.
- MISRA C:2012: 0 violations (Cppcheck 2.13 and 2.16), one documented deviation (DEV-01, Rule 11.3, opaque instance).
- No warnings with
-std=c11 -Wall -Wextra -Wpedantic -Wconversion(GCC on x86-64 andarm-none-eabi-gccfor Cortex-M4); clean under AddressSanitizer and UndefinedBehaviorSanitizer. - Cortex-M4 (GCC 15.2,
-Os): 6980 B flash fixed point, 7446 B float; worst-case stack ofsstep_fast_loop()184 B / 128 B;.dataand.bssempty. - Sine table error (default 8 bits): 1.0 LSB of Q15 (fixed point), 4.8·10^-6 (float).