Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input shaping buffer size calculation improvements #25035

Merged
merged 9 commits into from
Dec 10, 2022
27 changes: 14 additions & 13 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1062,14 +1062,13 @@
*
* Zero Vibration (ZV) Input Shaping for X and/or Y movements.
*
* This option uses a lot of SRAM for the step buffer, which is related to the
* largest step rate possible for the shaped axes. If the build fails due to
* low SRAM the buffer size may be reduced by setting smaller values for
* DEFAULT_AXIS_STEPS_PER_UNIT and/or DEFAULT_MAX_FEEDRATE. Disabling
* ADAPTIVE_STEP_SMOOTHING and reducing the step rate for non-shaped axes may
* also reduce the buffer sizes. Runtime editing of max feedrate (M203) or
* resonant frequency (M593) may result in input shaping losing effectiveness
* during high speed movements to prevent buffer overruns.
* This option uses a lot of SRAM for the step buffer. The buffer size is
* calculated automatically from SHAPING_FREQ_[XY], DEFAULT_AXIS_STEPS_PER_UNIT,
* DEFAULT_MAX_FEEDRATE and ADAPTIVE_STEP_SMOOTHING. The default calculation can
* be overridden by setting SHAPING_MIN_FREQ and/or SHAPING_MAX_FEEDRATE.
* The higher the frequency and the lower the feedrate, the smaller the buffer.
* If the buffer is too small at runtime, input shaping will have reduced
* effectiveness during high speed movements.
*
* Tune with M593 D<factor> F<frequency>:
*
Expand All @@ -1083,14 +1082,16 @@
//#define INPUT_SHAPING_Y
#if EITHER(INPUT_SHAPING_X, INPUT_SHAPING_Y)
#if ENABLED(INPUT_SHAPING_X)
#define SHAPING_FREQ_X 40 // (Hz) The default dominant resonant frequency on the X axis.
#define SHAPING_ZETA_X 0.15f // Damping ratio of the X axis (range: 0.0 = no damping to 1.0 = critical damping).
#define SHAPING_FREQ_X 40 // (Hz) The default dominant resonant frequency on the X axis.
#define SHAPING_ZETA_X 0.15f // Damping ratio of the X axis (range: 0.0 = no damping to 1.0 = critical damping).
#endif
#if ENABLED(INPUT_SHAPING_Y)
#define SHAPING_FREQ_Y 40 // (Hz) The default dominant resonant frequency on the Y axis.
#define SHAPING_ZETA_Y 0.15f // Damping ratio of the Y axis (range: 0.0 = no damping to 1.0 = critical damping).
#define SHAPING_FREQ_Y 40 // (Hz) The default dominant resonant frequency on the Y axis.
#define SHAPING_ZETA_Y 0.15f // Damping ratio of the Y axis (range: 0.0 = no damping to 1.0 = critical damping).
#endif
//#define SHAPING_MENU // Add a menu to the LCD to set shaping parameters.
//#define SHAPING_MIN_FREQ 20 // By default the minimum of the shaping frequencies. Override to affect SRAM usage.
//#define SHAPING_MAX_STEPRATE 10000 // By default the maximum total step rate of the shaped axes. Override to affect SRAM usage.
//#define SHAPING_MENU // Add a menu to the LCD to set shaping parameters.
#endif

#define AXIS_RELATIVE_MODES { false, false, false, false }
Expand Down
17 changes: 12 additions & 5 deletions Marlin/src/inc/SanityCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -4272,17 +4272,24 @@ static_assert(_PLUS_TEST(4), "HOMING_FEEDRATE_MM_M values must be positive.");

// Check requirements for Input Shaping
#if HAS_SHAPING && defined(__AVR__)
#ifdef SHAPING_MIN_FREQ
static_assert((SHAPING_MIN_FREQ) > 0, "SHAPING_MIN_FREQ must be > 0.");
#else
TERN_(INPUT_SHAPING_X, static_assert((SHAPING_FREQ_X) > 0, "SHAPING_FREQ_X must be > 0 or SHAPING_MIN_FREQ must be set."));
TERN_(INPUT_SHAPING_Y, static_assert((SHAPING_FREQ_Y) > 0, "SHAPING_FREQ_Y must be > 0 or SHAPING_MIN_FREQ must be set."));
#endif
#if ENABLED(INPUT_SHAPING_X)
#if F_CPU > 16000000
static_assert((SHAPING_FREQ_X) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_X is below the minimum (20) for AVR 20MHz.");
static_assert((SHAPING_FREQ_X) == 0 || (SHAPING_FREQ_X) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_X is below the minimum (20) for AVR 20MHz.");
#else
static_assert((SHAPING_FREQ_X) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_X is below the minimum (16) for AVR 16MHz.");
static_assert((SHAPING_FREQ_X) == 0 || (SHAPING_FREQ_X) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_X is below the minimum (16) for AVR 16MHz.");
#endif
#elif ENABLED(INPUT_SHAPING_Y)
#endif
#if ENABLED(INPUT_SHAPING_Y)
#if F_CPU > 16000000
static_assert((SHAPING_FREQ_Y) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Y is below the minimum (20) for AVR 20MHz.");
static_assert((SHAPING_FREQ_Y) == 0 || (SHAPING_FREQ_Y) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Y is below the minimum (20) for AVR 20MHz.");
#else
static_assert((SHAPING_FREQ_Y) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Y is below the minimum (16) for AVR 16MHz.");
static_assert((SHAPING_FREQ_Y) == 0 || (SHAPING_FREQ_Y) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Y is below the minimum (16) for AVR 16MHz.");
#endif
#endif
#endif
Expand Down
2 changes: 0 additions & 2 deletions Marlin/src/module/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,6 @@ typedef struct { bool NUM_AXIS_LIST(X:1, Y:1, Z:1, I:1, J:1, K:1, U:1, V:1,

// Defaults for reset / fill in on load
static const uint32_t _DMA[] PROGMEM = DEFAULT_MAX_ACCELERATION;
static const float _DASU[] PROGMEM = DEFAULT_AXIS_STEPS_PER_UNIT;
static const feedRate_t _DMF[] PROGMEM = DEFAULT_MAX_FEEDRATE;

/**
* Current EEPROM Layout
Expand Down
60 changes: 27 additions & 33 deletions Marlin/src/module/stepper.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,49 +326,43 @@ constexpr ena_mask_t enable_overlap[] = {
#endif
};

constexpr float _DASU[] = DEFAULT_AXIS_STEPS_PER_UNIT;
constexpr feedRate_t _DMF[] = DEFAULT_MAX_FEEDRATE;

//static_assert(!any_enable_overlap(), "There is some overlap.");

#if HAS_SHAPING

// These constexpr are used to calculate the shaping queue buffer sizes
constexpr xyze_float_t max_feedrate = DEFAULT_MAX_FEEDRATE;
constexpr xyze_float_t steps_per_unit = DEFAULT_AXIS_STEPS_PER_UNIT;
// MIN_STEP_ISR_FREQUENCY is known at compile time on AVRs and any reduction in SRAM is welcome
#ifdef __AVR__
constexpr float max_isr_rate = _MAX(
LOGICAL_AXIS_LIST(
max_feedrate.e * steps_per_unit.e,
max_feedrate.x * steps_per_unit.x,
max_feedrate.y * steps_per_unit.y,
max_feedrate.z * steps_per_unit.z,
max_feedrate.i * steps_per_unit.i,
max_feedrate.j * steps_per_unit.j,
max_feedrate.k * steps_per_unit.k,
max_feedrate.u * steps_per_unit.u,
max_feedrate.v * steps_per_unit.v,
max_feedrate.w * steps_per_unit.w
)
OPTARG(ADAPTIVE_STEP_SMOOTHING, MIN_STEP_ISR_FREQUENCY)
);
constexpr float max_step_rate = _MIN(max_isr_rate,
TERN0(INPUT_SHAPING_X, max_feedrate.x * steps_per_unit.x) +
TERN0(INPUT_SHAPING_Y, max_feedrate.y * steps_per_unit.y)
);
#ifdef SHAPING_MAX_STEPRATE
constexpr float max_step_rate = SHAPING_MAX_STEPRATE;
#else
constexpr float max_step_rate = TERN0(INPUT_SHAPING_X, max_feedrate.x * steps_per_unit.x) +
TERN0(INPUT_SHAPING_Y, max_feedrate.y * steps_per_unit.y);
constexpr float max_shaped_rate = TERN0(INPUT_SHAPING_X, _DMF[X_AXIS] * _DASU[X_AXIS]) +
TERN0(INPUT_SHAPING_Y, _DMF[Y_AXIS] * _DASU[Y_AXIS]);
#if defined(__AVR__) || !defined(ADAPTIVE_STEP_SMOOTHING)
// MIN_STEP_ISR_FREQUENCY is known at compile time on AVRs and any reduction in SRAM is welcome
template<int INDEX=DISTINCT_AXES> constexpr float max_isr_rate() {
thinkyhead marked this conversation as resolved.
Show resolved Hide resolved
return _MAX(_DMF[INDEX - 1] * _DASU[INDEX - 1], max_isr_rate<INDEX - 1>());
}
template<> constexpr float max_isr_rate<0>() {
return TERN0(ADAPTIVE_STEP_SMOOTHING, MIN_STEP_ISR_FREQUENCY);
}
constexpr float max_step_rate = _MIN(max_isr_rate(), max_shaped_rate);
#else
constexpr float max_step_rate = max_shaped_rate;
#endif
#endif
constexpr uint16_t shaping_echoes = max_step_rate / _MIN(0x7FFFFFFFL OPTARG(INPUT_SHAPING_X, SHAPING_FREQ_X) OPTARG(INPUT_SHAPING_Y, SHAPING_FREQ_Y)) / 2 + 3;

#ifndef SHAPING_MIN_FREQ
#define SHAPING_MIN_FREQ _MIN(0x7FFFFFFFL OPTARG(INPUT_SHAPING_X, SHAPING_FREQ_X) OPTARG(INPUT_SHAPING_Y, SHAPING_FREQ_Y))
#endif
constexpr uint16_t shaping_min_freq = SHAPING_MIN_FREQ,
shaping_echoes = max_step_rate / shaping_min_freq / 2 + 3;

typedef IF<ENABLED(__AVR__), uint16_t, uint32_t>::type shaping_time_t;
enum shaping_echo_t { ECHO_NONE = 0, ECHO_FWD = 1, ECHO_BWD = 2 };
struct shaping_echo_axis_t {
#if ENABLED(INPUT_SHAPING_X)
shaping_echo_t x:2;
#endif
#if ENABLED(INPUT_SHAPING_Y)
shaping_echo_t y:2;
#endif
TERN_(INPUT_SHAPING_X, shaping_echo_t x:2);
TERN_(INPUT_SHAPING_Y, shaping_echo_t y:2);
};

class ShapingQueue {
Expand Down