Skip to content

Control & Motor Modules

SyntropicOS provides integer-only control algorithms, closed-loop PID controllers, trapezoidal motion profiling, and Field-Oriented Control (FOC) for BLDC/PMSM motors.


Technical Specifications

Feature Specification
Math Format Fixed-point Q16.16 (q16_t) or normalized integer scaling.
FPU Requirement None. Runs on ARM Cortex-M0/M3/M4, RISC-V, and 8-bit MCUs.
Safety Features Anti-windup clamping, derivative filtering, stall detection, & limit events.

1. Closed-Loop PID Controller (control/syn_pid.h)

The syn_pid module provides an integer-only PID controller with anti-windup clamping and derivative-term filtering.

Control Loop Diagram

flowchart LR
    Setpoint["Target Setpoint"] --> ErrorCalc["Error = Setpoint - Feedback"]
    Feedback --> ErrorCalc
    ErrorCalc --> PID["SYN_PID (Kp, Ki, Kd)"]
    PID --> AntiWindup["Anti-Windup Clamp (Min/Max Out)"]
    AntiWindup --> Actuator["PWM / Motor Drive Output"]

Complete Code Example (Integer PID Temperature / Speed Control)

#include <syntropic/control/syn_pid.h>

static SYN_PID pid;

void control_init(void) {
    // Configure PID gains: Kp = 2.5, Ki = 0.5, Kd = 0.1, Output Range: 0 to 100% PWM
    SYN_PID_Config cfg = {
        .Kp = Q16_FROM_FLOAT(2.5f),
        .Ki = Q16_FROM_FLOAT(0.5f),
        .Kd = Q16_FROM_FLOAT(0.1f),
        .out_min = 0,
        .out_max = 1000, // 0 to 100.0% PWM
        .integral_min = -500,
        .integral_max = 500
    };
    syn_pid_init(&pid, &cfg);
}

uint16_t control_step(int32_t setpoint, int32_t current_reading) {
    // Compute control output step
    int32_t output = syn_pid_update(&pid, setpoint, current_reading);
    return (uint16_t)output;
}

2. Stepper Motor Control (motor/syn_stepper.h)

Provides Step/Direction motor driving with trapezoidal velocity acceleration profiles.

#include <syntropic/motor/syn_stepper.h>

static SYN_Stepper stepper;

void stepper_init(void) {
    // Initialize pin 4 (step), pin 5 (dir), max_speed = 1000 steps/s, accel = 500 steps/s²
    syn_stepper_init(&stepper, 4, 5, 1000, 500);
}

void move_relative(int32_t steps) {
    syn_stepper_move(&stepper, steps);
}

void main_timer_isr(void) {
    // Serves step generation (call at fixed interrupt interval e.g. 10 kHz)
    syn_stepper_update(&stepper);
}

3. Field-Oriented Control (FOC) (motor/syn_foc.h)

Provides Clarke/Park transforms, Space Vector PWM (SVPWM), and Sliding Mode Observers (SMO) for sensorless BLDC/PMSM motor control in pure Q16.16 fixed-point math.

#include <syntropic/motor/syn_foc.h>
#include <syntropic/motor/syn_foc_observer.h>

static SYN_FOCObserver observer;

void foc_setup(void) {
    SYN_FOCObserverConfig cfg = {
        .R = Q16_FROM_FLOAT(0.5f),    // 0.5 Ohm phase resistance
        .L = Q16_FROM_FLOAT(0.001f),  // 1 mH phase inductance
        .G = Q16_FROM_INT(10),        // Sliding mode observer gain
        .dt = Q16_FROM_FLOAT(0.0001f),// 10 kHz sampling period (100 µs)
        .Kp_pll = Q16_FROM_INT(150),
        .Ki_pll = Q16_FROM_INT(6000)
    };
    syn_foc_observer_init(&observer, &cfg);
}

void foc_10khz_isr(q16_t v_alpha, q16_t v_beta, q16_t i_alpha, q16_t i_beta) {
    // 1. Update Sliding Mode Observer with phase voltages and currents
    syn_foc_observer_update(&observer, v_alpha, v_beta, i_alpha, i_beta);

    // 2. Read estimated rotor angle theta_e [0, 2pi) and speed omega_e
    q16_t est_angle = syn_foc_observer_get_angle(&observer);
    q16_t est_speed = syn_foc_observer_get_speed(&observer);
}

4. G-Code & NIST RS-274 Motion Interpreter (motor/syn_gcode.h)

Zero-heap streaming ASCII G-code parser and multi-axis trajectory coordinator: - Parser: Streaming word extraction (G, M, X, Y, Z, I, J, K, F, S, P, T). - Modal Engine: G0/G1/G2/G3/G4, G17/G18/G19 planes, G20/G21 units, G90/G91 distance modes, G92 offsets, M3/M4/M5 spindle control, M7/M8/M9 coolant. - Interpolator Integration: Automatic trajectory planning via SYN_Interpolator with S-curve/trapezoidal velocity profiles.

#include <syntropic/motor/syn_gcode.h>
#include <syntropic/motor/syn_interpolator.h>

static SYN_Interpolator g_interp;
static SYN_GCode_Controller g_gcode_ctrl;

void gcode_setup(void) {
    SYN_GCode_Config cfg = {
        .interpolator = &g_interp,
        .default_feedrate = 200.0f,
        .max_acceleration = 1000.0f,
        .max_jerk = 5000.0f,
        .step_resolution = 0.001f,
    };
    syn_gcode_init(&g_gcode_ctrl, &cfg);

    /* Stream G-code blocks */
    syn_gcode_execute_line(&g_gcode_ctrl, "G21 G90 G0 X0 Y0 Z5");
    syn_gcode_execute_line(&g_gcode_ctrl, "G1 Z-1 F100");
    syn_gcode_execute_line(&g_gcode_ctrl, "G2 X10 Y10 I5 J0 F300");
}

5. Multi-Axis Robot Kinematics (motor/syn_kinematics.h)

Zero-heap integer Q16.16 multi-axis robot kinematics engine: - Transforms & Conventions: Standard and Modified Denavit-Hartenberg (DH) 4x4 matrix transforms. - Forward Kinematics: Arbitrary N-DOF serial manipulator forward kinematics with 3D position and roll/pitch/yaw Euler orientations. - Differential Kinematics: Exact 6xN Geometric Jacobian computation for velocity mapping and singularity analysis. - Standard Robot Models: - 3-DOF Planar Articulated Arm (FK & analytical IK with Elbow-Up / Elbow-Down selection). - 4-DOF SCARA Robot (FK & analytical IK with tool pitch/yaw). - 6-DOF PUMA Manipulator with Spherical Wrist (FK & Pieper analytical decoupling IK). - 3-Axis Delta Parallel Robot (FK via 3-sphere intersection & analytical IK).

#include <syntropic/motor/syn_kinematics.h>

void scara_pick_and_place_demo(void) {
    SYN_Kinematics_SCARAConfig cfg = {
        .l1 = Q16_FROM_INT(200),     /* 200 mm inner arm */
        .l2 = Q16_FROM_INT(150),     /* 150 mm outer arm */
        .d_max = Q16_FROM_INT(100),  /* 100 mm Z stroke */
        .z_home = Q16_FROM_INT(100), /* 100 mm home height */
    };

    /* Define target 6D pose */
    SYN_Pose6D target = {
        .position = {.x = Q16_FROM_INT(250), .y = Q16_FROM_INT(100), .z = Q16_FROM_INT(20)},
        .orientation = {.roll = 0, .pitch = 0, .yaw = Q16_PI_4}
    };

    q16_t q1 = 0, q2 = 0, d3 = 0, q4 = 0;
    if (syn_kinematics_scara_ik(&cfg, &target, SYN_ARM_ELBOW_UP, &q1, &q2, &d3, &q4) == SYN_OK) {
        /* Drive joint actuators to solved angles */
    }
}