Skip to content

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_pct about 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_STALL 43 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_pct of 50.

Changed

  • SSTEP_FAULT_OVERCURRENT comes together with SSTEP_FAULT_STALL when the trip finds the stall detector counting: a light rotor reaches the trip before stall_time_ms, and the position is lost. An application that compares faults with 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_BYTES on 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 of sstep_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.Z Git tag. sstep.cmake now 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 reports 0.0.0 (unknown). CITATION.cff no 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 above stall_sync_pct of Ke·ω; at rest and at low speed it checks that the rotor does not turn faster than the command by more than stall_speed, with the observer offset (resistance, dead time) learned while the rotor follows. An up/down counter raises the new fault SSTEP_FAULT_STALL (0x0020U) after stall_time_ms, with the usual fault behaviour (outputs off, SSTEP_STATE_FAULT, on_fault(), cleared by sstep_clear_faults()).
  • Configuration (struct sstep_ctrl_cfg): stall_time_ms (0 = off, the default), stall_filter_us, stall_speed, stall_sync_pct. sstep_init() returns SSTEP_ERR_CONFIG for stall detection with voltage drive or without ke_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.png for the website and the PDF cover, hero-light.png for the README).

Changed

  • struct sstep grows by 96 B (432 B fixed point, 456 B float on Cortex-M4): SSTEP_CONFIG_BYTES 152 / 176 B, SSTEP_STATE_BYTES 280 / 304 B. struct sstep_status grows from 32 B to 48 B.
  • Flash on Cortex-M4 (-Os): 9040 B fixed point, 9718 B float. Worst-case stack of sstep_fast_loop() 216 B fixed point (128 B float), of sstep_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_STALL 48 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, optional on_fault, on_event), raw ADC sample, status in engineering units, status codes, fault flags, driver states. pwm_freq_hz and dead_time_ns are reserved for later versions.
  • Opaque driver instance: struct sstep is uint64_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 in src/.
  • 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_DONE and SSTEP_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() returns SSTEP_ERR_STATE for 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) or SSTEP_CFG_MATH_BACKEND.
  • Build-time options (inc/sstep_config.h, overridable in sstep_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-analyze and codeql-analyze targets.
  • 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 of src/ 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 and arm-none-eabi-gcc for Cortex-M4); clean under AddressSanitizer and UndefinedBehaviorSanitizer.
  • Cortex-M4 (GCC 15.2, -Os): 6980 B flash fixed point, 7446 B float; worst-case stack of sstep_fast_loop() 184 B / 128 B; .data and .bss empty.
  • Sine table error (default 8 bits): 1.0 LSB of Q15 (fixed point), 4.8·10^-6 (float).