File sstep_bridge.c¶
File List > src > sstep_bridge.c
Go to the documentation of this file
/*******************************************************************************
* Stepper-C: Stepper Motor Control Implementation
*
* Project: Stepper-C - Hardware-Agnostic Stepper Motor Control Library
* Description: Averaged power stage: phase voltage request to leg compare values, both topologies.
*
* Author: Anton Vynohradov
* Email: avynohradov@systemfromscratch.com
*
* License: Apache License 2.0
*
* Copyright (c) 2026 Anton Vynohradov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
/* ==============================================================================
* INCLUDES
* =============================================================================*/
#include "sstep_internal.h"
/* ==============================================================================
* DEFINES & MACROS
* =============================================================================*/
#define ACC_HALF SSTEP_ACC_HALF
/* ==============================================================================
* PRIVATE FUNCTION DECLARATIONS
* =============================================================================*/
static sstep_acc_t acc_max(sstep_acc_t a, sstep_acc_t b);
#if (SSTEP_CFG_TOPOLOGY_SHARED_LEG == 1)
static sstep_acc_t acc_min(sstep_acc_t a, sstep_acc_t b);
#endif
static sstep_acc_t scale_to_unit(sstep_acc_t x, sstep_acc_t m);
#if (SSTEP_CFG_TOPOLOGY_FULL_BRIDGE == 1)
static sstep_acc_t span_full(sstep_acc_t a, sstep_acc_t b);
static void duties_full(sstep_acc_t a, sstep_acc_t b, uint16_t period,
struct sstep_bridge_out* out);
#endif
#if (SSTEP_CFG_TOPOLOGY_SHARED_LEG == 1)
static sstep_acc_t span_shared(sstep_acc_t a, sstep_acc_t b);
static void duties_shared(sstep_acc_t a, sstep_acc_t b, uint16_t period,
struct sstep_bridge_out* out);
#endif
/* ==============================================================================
* PRIVATE FUNCTION IMPLEMENTATIONS
* =============================================================================*/
static sstep_acc_t acc_max(sstep_acc_t a, sstep_acc_t b)
{
return (a > b) ? a : b;
}
#if (SSTEP_CFG_TOPOLOGY_SHARED_LEG == 1)
static sstep_acc_t acc_min(sstep_acc_t a, sstep_acc_t b)
{
return (a < b) ? a : b;
}
#endif
static sstep_acc_t scale_to_unit(sstep_acc_t x, sstep_acc_t m)
{
sstep_acc_t q = sstep_acc_from_real(sstep_div_unit(sstep_acc_abs(x), m));
return (x < SSTEP_ACC_ZERO) ? -q : q;
}
#if (SSTEP_CFG_TOPOLOGY_FULL_BRIDGE == 1)
static sstep_acc_t span_full(sstep_acc_t a, sstep_acc_t b)
{
return acc_max(sstep_acc_abs(a), sstep_acc_abs(b));
}
static void duties_full(sstep_acc_t a, sstep_acc_t b, uint16_t period, struct sstep_bridge_out* out)
{
sstep_acc_t ha = sstep_acc_halve(a);
sstep_acc_t hb = sstep_acc_halve(b);
out->compare[0] = sstep_acc_to_ticks(ACC_HALF + ha, period);
out->compare[1] = sstep_acc_to_ticks(ACC_HALF - ha, period);
out->compare[2] = sstep_acc_to_ticks(ACC_HALF + hb, period);
out->compare[3] = sstep_acc_to_ticks(ACC_HALF - hb, period);
}
#endif
#if (SSTEP_CFG_TOPOLOGY_SHARED_LEG == 1)
static sstep_acc_t span_shared(sstep_acc_t a, sstep_acc_t b)
{
return acc_max(acc_max(a, b), SSTEP_ACC_ZERO) - acc_min(acc_min(a, b), SSTEP_ACC_ZERO);
}
static void duties_shared(sstep_acc_t a, sstep_acc_t b, uint16_t period,
struct sstep_bridge_out* out)
{
sstep_acc_t hi = acc_max(acc_max(a, b), SSTEP_ACC_ZERO);
sstep_acc_t lo = acc_min(acc_min(a, b), SSTEP_ACC_ZERO);
sstep_acc_t w = ACC_HALF - sstep_acc_halve(hi + lo);
out->compare[0] = sstep_acc_to_ticks(a + w, period);
out->compare[1] = sstep_acc_to_ticks(b + w, period);
out->compare[2] = sstep_acc_to_ticks(w, period);
out->compare[3] = 0U;
}
#endif
/* ==============================================================================
* PUBLIC FUNCTION IMPLEMENTATIONS (library internal)
* =============================================================================*/
void sstep_bridge_map(enum sstep_topology topology, sstep_acc_t va, sstep_acc_t vb,
const struct sstep_gain* inv_vbus, sstep_acc_t vbus, uint16_t period,
struct sstep_bridge_out* out)
{
sstep_acc_t x = va;
sstep_acc_t y = vb;
sstep_acc_t m0 = acc_max(sstep_acc_abs(va), sstep_acc_abs(vb));
sstep_acc_t a;
sstep_acc_t b;
sstep_acc_t m;
/* Power-of-two pre-scale keeps the direction exactly and the request below 4 pu,
the input range of the multiplication by V_base / Vbus (Vbus <= 2 pu, so a
pre-scaled request is always limited below). Bounded: fixed point at most 14
halvings (2^31 / 2^17); float at most 18 (the PI output is below 2^20 pu). */
while (m0 > SSTEP_ACC_IN_MAX)
{
x = sstep_acc_halve(x);
y = sstep_acc_halve(y);
m0 = sstep_acc_halve(m0);
}
/* relative to the bus voltage: 1.0 = Vbus */
a = sstep_gain_mul_acc(inv_vbus, x);
b = sstep_gain_mul_acc(inv_vbus, y);
#if (SSTEP_CFG_TOPOLOGY_FULL_BRIDGE == 1) && (SSTEP_CFG_TOPOLOGY_SHARED_LEG == 1)
m = (topology == SSTEP_TOPOLOGY_FULL_BRIDGE) ? span_full(a, b) : span_shared(a, b);
#elif (SSTEP_CFG_TOPOLOGY_FULL_BRIDGE == 1)
(void)topology;
m = span_full(a, b);
#else
(void)topology;
m = span_shared(a, b);
#endif
out->limited = (m > SSTEP_ACC_ONE);
if (out->limited)
{
a = scale_to_unit(a, m);
b = scale_to_unit(b, m);
/* applied voltage in pu of V_base: a * Vbus */
out->v[0] = sstep_acc_scale(vbus, sstep_acc_to_real(a));
out->v[1] = sstep_acc_scale(vbus, sstep_acc_to_real(b));
}
else
{
out->v[0] = va;
out->v[1] = vb;
}
#if (SSTEP_CFG_TOPOLOGY_FULL_BRIDGE == 1) && (SSTEP_CFG_TOPOLOGY_SHARED_LEG == 1)
if (topology == SSTEP_TOPOLOGY_FULL_BRIDGE)
{
duties_full(a, b, period, out);
}
else
{
duties_shared(a, b, period, out);
}
#elif (SSTEP_CFG_TOPOLOGY_FULL_BRIDGE == 1)
duties_full(a, b, period, out);
#else
duties_shared(a, b, period, out);
#endif
}