Smooth, quiet and exact, on a small MCU
A stepper driver chip hides the current control in silicon. When you build the power stage yourself (an MCU with gate drivers, a three-leg ESC board, a custom H-bridge) the firmware has to regulate two phase currents at the PWM rate, keep the position exact over millions of microsteps, stay inside the voltage the bridge can apply and switch off within one period on a fault, often on a core without an FPU.
Currents that follow the angle at 20 kHz
One PI controller per phase with gains derived from R, L and one bandwidth value, back-EMF feed-forward and anti-windup. On the motor model a 2 A step settles in 0.40 ms with 1.1 % overshoot.
Tight embedded budgets
432 B of RAM per motor, 216 B worst-case stack in the PWM interrupt, 8.8 KiB of flash on Cortex-M4. A fixed-point backend for cores without FPU; no allocation, no globals, no OS.
Code you can certify
Zero MISRA C:2012 violations, a documented deviation register, 100 % line and branch coverage in both numeric backends, bounded execution time and static analysis in CI.
One library from motion command to PWM
15 public functions, five modules (control, stall detection, motion, power stage, numeric backend), two power stage topologies and two numeric backends, verified in closed loop on a motor model.
Current control and microstepping
PI per phase with feed-forward, sine/cosine references from a 32-bit electrical angle, 1 to 256 microsteps.
Motion planner
Trapezoidal moves without overshoot, constant-velocity runs, smooth stop and retargeting, exact integer positions.
Any power stage
Two H-bridges or three half-bridges with a shared leg; the voltage limit keeps the direction; the bus voltage is compensated.
Protection and stall detection
Overcurrent, overvoltage and undervoltage trips switch the outputs off in the same period; a back-EMF observer reports a stalled or slipping rotor without a sensor.
Complete documentation
Guides, the control theory with measured results, the integration guide and a full API reference, also as a PDF.
Quality gates in CI
Unit and closed-loop tests in both backends with a 100 % coverage gate, MISRA C:2012 gate, Cppcheck and CodeQL.
Quick start
A motor running in one file¶
Two callbacks connect the library to the power stage: one writes the PWM compare values, one switches the gate drivers. The PWM/ADC interrupt passes the raw ADC readings to the fast loop; the main loop runs the slow loop once per millisecond and gives the commands.
#include "sstep.h"
static struct sstep motor;
static int pwm_write(void* context, const uint16_t compare[SSTEP_LEGS_MAX])
{
(void)context;
board_pwm_set(compare);
return 0;
}
static int output_enable(void* context, bool enable)
{
(void)context;
board_gates_enable(enable);
return 0;
}
/* 17HS19-2004S1 on a B-G431B-ESC1: 24 V, 20 kHz PWM, 16 microsteps */
static const struct sstep_config motor_config = {
.motor = {.r_mohm = 1600U, .l_uh = 3000U, .i_rated_ma = 2000U,
.ke_uv_s_per_rad = 208600U, .full_steps_per_rev = 200U},
.stage = {.topology = SSTEP_TOPOLOGY_SHARED_LEG, .vbus_nominal_mv = 24000U,
.vbus_min_mv = 10000U, .vbus_max_mv = 28000U, .i_full_scale_ma = 4000U,
.i_trip_ma = 3500U, .pwm_period_ticks = 4250U, .pwm_freq_hz = 20000U,
.adc_i_zero = 2048U, .adc_i_span = 2047, .adc_vbus_mv_full = 34320U,
.adc_full = 4095U},
.ctrl = {.drive_mode = SSTEP_DRIVE_CURRENT, .fast_loop_hz = 20000U,
.slow_loop_hz = 1000U, .microsteps = 16U, .current_bw_hz = 1000U,
.i_run_ma = 1500U, .i_hold_ma = 500U, .hold_delay_ms = 500U,
.v_max = 16000U, .accel = 64000U},
.hal = {.pwm_write = pwm_write, .output_enable = output_enable},
};
/* PWM/ADC interrupt, 20 kHz */
void ADC1_2_IRQHandler(void);
void ADC1_2_IRQHandler(void)
{
const struct sstep_sample sample = {board_adc_phase_a(), board_adc_phase_b(),
board_adc_vbus()};
(void)sstep_fast_loop(&motor, &sample);
}
int main(void)
{
board_init();
if ((sstep_init(&motor, &motor_config) != SSTEP_OK) || (sstep_enable(&motor) != SSTEP_OK))
{
for (;;)
{
}
}
(void)sstep_move_to(&motor, 3200); /* one revolution */
for (;;)
{
if (board_tick_1ms())
{
(void)sstep_slow_loop(&motor);
}
}
}
The board_… functions stand for your timer, ADC and GPIO code; the ADC scaling values
above must be measured on your board (Configuration).
Find your way¶
| If you want to… | Read |
|---|---|
| Understand what the library does and does not do | Introduction |
| Build it, run the tests, add it to your project | Getting Started |
| Understand the loops, states and memory | How It Works |
| Configure a motor and command motion | Using the Library |
| Understand the current control, microstepping and limits | Motor Control |
| Connect it to timers, ADC and interrupts | Integration Guide |
| Look up a function | API Reference |
| Read everything offline or print it | PDF user manual |
SystemFromScratch®
Part of an open-source embedded portfolio
Stepper-C is developed by Anton Vynohradov under SystemFromScratch® — embedded software and hardware built from the first schematic to certification.
SystemFromScratch® is a registered trademark. STM32 is a registered trademark of STMicroelectronics International N.V.; Stepper-C is not affiliated with or endorsed by STMicroelectronics.