INPUT_SHAPING_Z (#27073)

This commit is contained in:
Jonathan Brazier 2024-05-20 06:03:03 +01:00 committed by GitHub
parent 6c018eb770
commit 6b6865d068
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 250 additions and 141 deletions

View File

@ -1202,7 +1202,8 @@
*/
//#define INPUT_SHAPING_X
//#define INPUT_SHAPING_Y
#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y)
//#define INPUT_SHAPING_Z
#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y, INPUT_SHAPING_Z)
#if ENABLED(INPUT_SHAPING_X)
#define SHAPING_FREQ_X 40.0 // (Hz) The default dominant resonant frequency on the X axis.
#define SHAPING_ZETA_X 0.15 // Damping ratio of the X axis (range: 0.0 = no damping to 1.0 = critical damping).
@ -1211,6 +1212,10 @@
#define SHAPING_FREQ_Y 40.0 // (Hz) The default dominant resonant frequency on the Y axis.
#define SHAPING_ZETA_Y 0.15 // Damping ratio of the Y axis (range: 0.0 = no damping to 1.0 = critical damping).
#endif
#if ENABLED(INPUT_SHAPING_Z)
#define SHAPING_FREQ_Z 40.0 // (Hz) The default dominant resonant frequency on the Z axis.
#define SHAPING_ZETA_Z 0.15 // Damping ratio of the Z axis (range: 0.0 = no damping to 1.0 = critical damping).
#endif
//#define SHAPING_MIN_FREQ 20.0 // (Hz) 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.

View File

@ -44,6 +44,15 @@ void GcodeSuite::M593_report(const bool forReplay/*=true*/) {
" D", stepper.get_shaping_damping_ratio(Y_AXIS)
);
#endif
#if ENABLED(INPUT_SHAPING_Z)
#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y)
report_echo_start(forReplay);
#endif
SERIAL_ECHOLNPGM(" M593 Z"
" F", stepper.get_shaping_frequency(Z_AXIS),
" D", stepper.get_shaping_damping_ratio(Z_AXIS)
);
#endif
}
/**
@ -59,14 +68,17 @@ void GcodeSuite::M593() {
const bool seen_X = TERN0(INPUT_SHAPING_X, parser.seen_test('X')),
seen_Y = TERN0(INPUT_SHAPING_Y, parser.seen_test('Y')),
for_X = seen_X || TERN0(INPUT_SHAPING_X, (!seen_X && !seen_Y)),
for_Y = seen_Y || TERN0(INPUT_SHAPING_Y, (!seen_X && !seen_Y));
seen_Z = TERN0(INPUT_SHAPING_Z, parser.seen_test('Z')),
for_X = seen_X || TERN0(INPUT_SHAPING_X, (!seen_X && !seen_Y && !seen_Z)),
for_Y = seen_Y || TERN0(INPUT_SHAPING_Y, (!seen_X && !seen_Y && !seen_Z)),
for_Z = seen_Z || TERN0(INPUT_SHAPING_Z, (!seen_X && !seen_Y && !seen_Z));
if (parser.seen('D')) {
const float zeta = parser.value_float();
if (WITHIN(zeta, 0, 1)) {
if (for_X) stepper.set_shaping_damping_ratio(X_AXIS, zeta);
if (for_Y) stepper.set_shaping_damping_ratio(Y_AXIS, zeta);
if (for_Z) stepper.set_shaping_damping_ratio(Z_AXIS, zeta);
}
else
SERIAL_ECHO_MSG("?Zeta (D) value out of range (0-1)");
@ -78,6 +90,7 @@ void GcodeSuite::M593() {
if (freq == 0.0f || freq > min_freq) {
if (for_X) stepper.set_shaping_frequency(X_AXIS, freq);
if (for_Y) stepper.set_shaping_frequency(Y_AXIS, freq);
if (for_Z) stepper.set_shaping_frequency(Z_AXIS, freq);
}
else
SERIAL_ECHOLNPGM(GCODE_ERR_MSG("Frequency (F) must be greater than ", min_freq, " or 0 to disable"));

View File

@ -112,8 +112,8 @@
#undef DISABLE_IDLE_X
#undef INPUT_SHAPING_X
#undef SAFE_BED_LEVELING_START_X
#undef SHAPING_BUFFER_X
#undef SHAPING_FREQ_X
#undef SHAPING_ZETA_X
#undef STEALTHCHOP_X
#endif
@ -128,8 +128,8 @@
#undef INPUT_SHAPING_Y
#undef QUICK_HOME
#undef SAFE_BED_LEVELING_START_Y
#undef SHAPING_BUFFER_Y
#undef SHAPING_FREQ_Y
#undef SHAPING_ZETA_Y
#undef STEALTHCHOP_Y
#undef STEP_STATE_Y
#endif
@ -142,8 +142,11 @@
#undef ENABLE_LEVELING_FADE_HEIGHT
#undef HOME_Z_FIRST
#undef HOMING_Z_WITH_PROBE
#undef INPUT_SHAPING_Z
#undef NUM_Z_STEPPERS
#undef SAFE_BED_LEVELING_START_Z
#undef SHAPING_FREQ_Z
#undef SHAPING_ZETA_Z
#undef STEALTHCHOP_Z
#undef STEP_STATE_Z
#undef Z_IDLE_HEIGHT
@ -1338,7 +1341,7 @@
#endif
// Input shaping
#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y)
#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y, INPUT_SHAPING_Z)
#define HAS_ZV_SHAPING 1
#endif

View File

@ -4208,7 +4208,12 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
*/
#if HAS_ZV_SHAPING
#if ENABLED(DELTA)
#error "Input Shaping is not compatible with DELTA kinematics."
#if !ALL(INPUT_SHAPING_X, INPUT_SHAPING_Y, INPUT_SHAPING_Z)
#error "INPUT_SHAPING_X, INPUT_SHAPING_Y and INPUT_SHAPING_Z must all be enabled for DELTA."
#else
static_assert(SHAPING_FREQ_X == SHAPING_FREQ_Y && SHAPING_FREQ_Y == SHAPING_FREQ_Z, "SHAPING_FREQ_X, SHAPING_FREQ_Y and SHAPING_FREQ_Z must be the same for DELTA.");
static_assert(SHAPING_ZETA_X == SHAPING_ZETA_Y && SHAPING_ZETA_Y == SHAPING_ZETA_Z, "SHAPING_ZETA_X, SHAPING_ZETA_Y and SHAPING_ZETA_Z must be the same for DELTA.");
#endif
#elif ENABLED(SCARA)
#error "Input Shaping is not compatible with SCARA kinematics."
#elif ENABLED(TPARA)
@ -4220,9 +4225,19 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
#elif ENABLED(DIRECT_STEPPING)
#error "Input Shaping is not compatible with DIRECT_STEPPING."
#elif ALL(INPUT_SHAPING_X, CORE_IS_XZ)
#error "INPUT_SHAPING_X is not supported with COREXZ."
#if !ALL(INPUT_SHAPING_X, INPUT_SHAPING_Z)
#error "INPUT_SHAPING_X and INPUT_SHAPING_Z must both be enabled for COREXZ."
#else
static_assert(SHAPING_FREQ_X == SHAPING_FREQ_Z, "SHAPING_FREQ_X and SHAPING_FREQ_Z must be the same for COREXZ.");
static_assert(SHAPING_ZETA_X == SHAPING_ZETA_Z, "SHAPING_ZETA_X and SHAPING_ZETA_Z must be the same for COREXZ.");
#endif
#elif ALL(INPUT_SHAPING_Y, CORE_IS_YZ)
#error "INPUT_SHAPING_Y is not supported with COREYZ."
#if !ALL(INPUT_SHAPING_Y, INPUT_SHAPING_Z)
#error "INPUT_SHAPING_Y and INPUT_SHAPING_Z must both be enabled for COREYZ."
#else
static_assert(SHAPING_FREQ_Y == SHAPING_FREQ_Z, "SHAPING_FREQ_Y and SHAPING_FREQ_Z must be the same for COREYZ.");
static_assert(SHAPING_ZETA_Y == SHAPING_ZETA_Z, "SHAPING_ZETA_Y and SHAPING_ZETA_Z must be the same for COREYZ.");
#endif
#elif ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
#if !ALL(INPUT_SHAPING_X, INPUT_SHAPING_Y)
#error "INPUT_SHAPING_X and INPUT_SHAPING_Y must both be enabled for COREXY, COREYX, or MARKFORGED_*."
@ -4237,6 +4252,7 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
#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."));
TERN_(INPUT_SHAPING_Z, static_assert((SHAPING_FREQ_Z) > 0, "SHAPING_FREQ_Z must be > 0 or SHAPING_MIN_FREQ must be set."));
#endif
#ifdef __AVR__
#if ENABLED(INPUT_SHAPING_X)
@ -4253,6 +4269,13 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
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
#if ENABLED(INPUT_SHAPING_Z)
#if F_CPU > 16000000
static_assert((SHAPING_FREQ_Z) == 0 || (SHAPING_FREQ_Z) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Z is below the minimum (20) for AVR 20MHz.");
#else
static_assert((SHAPING_FREQ_Z) == 0 || (SHAPING_FREQ_Z) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Z is below the minimum (16) for AVR 16MHz.");
#endif
#endif
#endif
#endif

View File

@ -3478,6 +3478,13 @@ void drawTuneMenu() {
void setShapingYZeta() { hmiValue.axis = Y_AXIS; setFloatOnClick(0, 1, 2, stepper.get_shaping_damping_ratio(Y_AXIS), applyShapingZeta); }
#endif
#if ENABLED(INPUT_SHAPING_Z)
void onDrawShapingZFreq(MenuItem* menuitem, int8_t line) { onDrawFloatMenu(menuitem, line, 2, stepper.get_shaping_frequency(Z_AXIS)); }
void onDrawShapingZZeta(MenuItem* menuitem, int8_t line) { onDrawFloatMenu(menuitem, line, 2, stepper.get_shaping_damping_ratio(Z_AXIS)); }
void setShapingZFreq() { hmiValue.axis = Z_AXIS; setFloatOnClick(0, 200, 2, stepper.get_shaping_frequency(Z_AXIS), applyShapingFreq); }
void setShapingZZeta() { hmiValue.axis = Z_AXIS; setFloatOnClick(0, 1, 2, stepper.get_shaping_damping_ratio(Z_AXIS), applyShapingZeta); }
#endif
void drawInputShaping_menu() {
checkkey = ID_Menu;
if (SET_MENU(inputShapingMenu, MSG_INPUT_SHAPING, 5)) {
@ -3490,6 +3497,10 @@ void drawTuneMenu() {
MENU_ITEM(ICON_ShapingY, MSG_SHAPING_B_FREQ, onDrawShapingYFreq, setShapingYFreq);
MENU_ITEM(ICON_ShapingY, MSG_SHAPING_B_ZETA, onDrawShapingYZeta, setShapingYZeta);
#endif
#if ENABLED(INPUT_SHAPING_Z)
MENU_ITEM(ICON_ShapingZ, MSG_SHAPING_C_FREQ, onDrawShapingZFreq, setShapingZFreq);
MENU_ITEM(ICON_ShapingZ, MSG_SHAPING_C_ZETA, onDrawShapingZZeta, setShapingZZeta);
#endif
}
updateMenu(inputShapingMenu);
}

View File

@ -559,28 +559,20 @@ void menu_backlash();
BACK_ITEM(MSG_ADVANCED_SETTINGS);
// M593 F Frequency and D Damping ratio
#if ENABLED(INPUT_SHAPING_X)
editable.decimal = stepper.get_shaping_frequency(X_AXIS);
if (editable.decimal) {
ACTION_ITEM_N(X_AXIS, MSG_SHAPING_DISABLE, []{ stepper.set_shaping_frequency(X_AXIS, 0.0f); ui.refresh(); });
EDIT_ITEM_FAST_N(float41, X_AXIS, MSG_SHAPING_FREQ, &editable.decimal, min_frequency, 200.0f, []{ stepper.set_shaping_frequency(X_AXIS, editable.decimal); });
editable.decimal = stepper.get_shaping_damping_ratio(X_AXIS);
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_SHAPING_ZETA, &editable.decimal, 0.0f, 1.0f, []{ stepper.set_shaping_damping_ratio(X_AXIS, editable.decimal); });
}
else
ACTION_ITEM_N(X_AXIS, MSG_SHAPING_ENABLE, []{ stepper.set_shaping_frequency(X_AXIS, (SHAPING_FREQ_X) ?: (SHAPING_MIN_FREQ)); ui.refresh(); });
#endif
#if ENABLED(INPUT_SHAPING_Y)
editable.decimal = stepper.get_shaping_frequency(Y_AXIS);
if (editable.decimal) {
ACTION_ITEM_N(Y_AXIS, MSG_SHAPING_DISABLE, []{ stepper.set_shaping_frequency(Y_AXIS, 0.0f); ui.refresh(); });
EDIT_ITEM_FAST_N(float41, Y_AXIS, MSG_SHAPING_FREQ, &editable.decimal, min_frequency, 200.0f, []{ stepper.set_shaping_frequency(Y_AXIS, editable.decimal); });
editable.decimal = stepper.get_shaping_damping_ratio(Y_AXIS);
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_SHAPING_ZETA, &editable.decimal, 0.0f, 1.0f, []{ stepper.set_shaping_damping_ratio(Y_AXIS, editable.decimal); });
}
else
ACTION_ITEM_N(Y_AXIS, MSG_SHAPING_ENABLE, []{ stepper.set_shaping_frequency(Y_AXIS, (SHAPING_FREQ_Y) ?: (SHAPING_MIN_FREQ)); ui.refresh(); });
#endif
#define SHAPING_MENU_FOR_AXIS(AXIS) \
editable.decimal = stepper.get_shaping_frequency(AXIS); \
if (editable.decimal) { \
ACTION_ITEM_N(AXIS, MSG_SHAPING_DISABLE, []{ stepper.set_shaping_frequency(AXIS, 0.0f); ui.refresh(); }); \
EDIT_ITEM_FAST_N(float41, AXIS, MSG_SHAPING_FREQ, &editable.decimal, min_frequency, 200.0f, []{ stepper.set_shaping_frequency(AXIS, editable.decimal); }); \
editable.decimal = stepper.get_shaping_damping_ratio(AXIS); \
EDIT_ITEM_FAST_N(float42_52, AXIS, MSG_SHAPING_ZETA, &editable.decimal, 0.0f, 1.0f, []{ stepper.set_shaping_damping_ratio(AXIS, editable.decimal); }); \
} \
else \
ACTION_ITEM_N(AXIS, MSG_SHAPING_ENABLE, []{ stepper.set_shaping_frequency(AXIS, (SHAPING_FREQ_X) ?: (SHAPING_MIN_FREQ)); ui.refresh(); });
TERN_(INPUT_SHAPING_X, SHAPING_MENU_FOR_AXIS(X_AXIS))
TERN_(INPUT_SHAPING_Y, SHAPING_MENU_FOR_AXIS(Y_AXIS))
TERN_(INPUT_SHAPING_Z, SHAPING_MENU_FOR_AXIS(Z_AXIS))
END_MENU();
}

View File

@ -634,6 +634,10 @@ typedef struct SettingsDataStruct {
float shaping_y_frequency, // M593 Y F
shaping_y_zeta; // M593 Y D
#endif
#if ENABLED(INPUT_SHAPING_Z)
float shaping_z_frequency, // M593 Z F
shaping_z_zeta; // M593 Z D
#endif
//
// HOTEND_IDLE_TIMEOUT
@ -1731,6 +1735,10 @@ void MarlinSettings::postprocess() {
EEPROM_WRITE(stepper.get_shaping_frequency(Y_AXIS));
EEPROM_WRITE(stepper.get_shaping_damping_ratio(Y_AXIS));
#endif
#if ENABLED(INPUT_SHAPING_Z)
EEPROM_WRITE(stepper.get_shaping_frequency(Z_AXIS));
EEPROM_WRITE(stepper.get_shaping_damping_ratio(Z_AXIS));
#endif
#endif
//
@ -2813,22 +2821,33 @@ void MarlinSettings::postprocess() {
//
#if ENABLED(INPUT_SHAPING_X)
{
float _data[2];
struct { float freq, damp; } _data;
EEPROM_READ(_data);
if (!validating) {
stepper.set_shaping_frequency(X_AXIS, _data[0]);
stepper.set_shaping_damping_ratio(X_AXIS, _data[1]);
stepper.set_shaping_frequency(X_AXIS, _data.freq);
stepper.set_shaping_damping_ratio(X_AXIS, _data.damp);
}
}
#endif
#if ENABLED(INPUT_SHAPING_Y)
{
float _data[2];
struct { float freq, damp; } _data;
EEPROM_READ(_data);
if (!validating) {
stepper.set_shaping_frequency(Y_AXIS, _data[0]);
stepper.set_shaping_damping_ratio(Y_AXIS, _data[1]);
stepper.set_shaping_frequency(Y_AXIS, _data.freq);
stepper.set_shaping_damping_ratio(Y_AXIS, _data.damp);
}
}
#endif
#if ENABLED(INPUT_SHAPING_Z)
{
struct { float freq, damp; } _data;
EEPROM_READ(_data);
if (!validating) {
stepper.set_shaping_frequency(Z_AXIS, _data.freq);
stepper.set_shaping_damping_ratio(Z_AXIS, _data.damp);
}
}
#endif
@ -3665,6 +3684,10 @@ void MarlinSettings::reset() {
stepper.set_shaping_frequency(Y_AXIS, SHAPING_FREQ_Y);
stepper.set_shaping_damping_ratio(Y_AXIS, SHAPING_ZETA_Y);
#endif
#if ENABLED(INPUT_SHAPING_Z)
stepper.set_shaping_frequency(Z_AXIS, SHAPING_FREQ_Z);
stepper.set_shaping_damping_ratio(Z_AXIS, SHAPING_ZETA_Z);
#endif
#endif
//

View File

@ -281,20 +281,16 @@ uint32_t Stepper::advance_divisor = 0,
shaping_echo_axis_t ShapingQueue::echo_axes[shaping_echoes];
uint16_t ShapingQueue::tail = 0;
#if ENABLED(INPUT_SHAPING_X)
shaping_time_t ShapingQueue::delay_x;
shaping_time_t ShapingQueue::peek_x_val = shaping_time_t(-1);
uint16_t ShapingQueue::head_x = 0;
uint16_t ShapingQueue::_free_count_x = shaping_echoes - 1;
ShapeParams Stepper::shaping_x;
#endif
#if ENABLED(INPUT_SHAPING_Y)
shaping_time_t ShapingQueue::delay_y;
shaping_time_t ShapingQueue::peek_y_val = shaping_time_t(-1);
uint16_t ShapingQueue::head_y = 0;
uint16_t ShapingQueue::_free_count_y = shaping_echoes - 1;
ShapeParams Stepper::shaping_y;
#endif
#define SHAPING_VAR_DEFS(AXIS) \
shaping_time_t ShapingQueue::delay_##AXIS; \
shaping_time_t ShapingQueue::_peek_##AXIS = shaping_time_t(-1); \
uint16_t ShapingQueue::head_##AXIS = 0; \
uint16_t ShapingQueue::_free_count_##AXIS = shaping_echoes - 1; \
ShapeParams Stepper::shaping_##AXIS;
TERN_(INPUT_SHAPING_X, SHAPING_VAR_DEFS(x))
TERN_(INPUT_SHAPING_Y, SHAPING_VAR_DEFS(y))
TERN_(INPUT_SHAPING_Z, SHAPING_VAR_DEFS(z))
#endif
#if ENABLED(BABYSTEPPING)
@ -1610,6 +1606,7 @@ void Stepper::isr() {
interval = _MIN(nextMainISR, uint32_t(HAL_TIMER_TYPE_MAX)); // Time until the next Pulse / Block phase
TERN_(INPUT_SHAPING_X, NOMORE(interval, ShapingQueue::peek_x())); // Time until next input shaping echo for X
TERN_(INPUT_SHAPING_Y, NOMORE(interval, ShapingQueue::peek_y())); // Time until next input shaping echo for Y
TERN_(INPUT_SHAPING_Z, NOMORE(interval, ShapingQueue::peek_z())); // Time until next input shaping echo for Z
TERN_(LIN_ADVANCE, NOMORE(interval, nextAdvanceISR)); // Come back early for Linear Advance?
TERN_(BABYSTEPPING, NOMORE(interval, nextBabystepISR)); // Come back early for Babystepping?
@ -1754,6 +1751,10 @@ void Stepper::pulse_phase_isr() {
shaping_y.delta_error = 0;
shaping_y.last_block_end_pos = count_position.y;
#endif
#if ENABLED(INPUT_SHAPING_Z)
shaping_z.delta_error = 0;
shaping_z.last_block_end_pos = count_position.z;
#endif
#endif
}
}
@ -1813,6 +1814,12 @@ void Stepper::pulse_phase_isr() {
#else
#define HYSTERESIS_Y 0
#endif
#if AXIS_DRIVER_TYPE_Z(TMC2208) || AXIS_DRIVER_TYPE_Z(TMC2208_STANDALONE) || \
AXIS_DRIVER_TYPE_Z(TMC5160) || AXIS_DRIVER_TYPE_Z(TMC5160_STANDALONE)
#define HYSTERESIS_Z 64
#else
#define HYSTERESIS_Z 0
#endif
#define _HYSTERESIS(AXIS) HYSTERESIS_##AXIS
#define HYSTERESIS(AXIS) _HYSTERESIS(AXIS)
@ -2005,9 +2012,10 @@ void Stepper::pulse_phase_isr() {
#if HAS_ZV_SHAPING
// record an echo if a step is needed in the primary bresenham
const bool x_step = TERN0(INPUT_SHAPING_X, step_needed.x && shaping_x.enabled),
y_step = TERN0(INPUT_SHAPING_Y, step_needed.y && shaping_y.enabled);
if (x_step || y_step)
ShapingQueue::enqueue(x_step, TERN0(INPUT_SHAPING_X, shaping_x.forward), y_step, TERN0(INPUT_SHAPING_Y, shaping_y.forward));
y_step = TERN0(INPUT_SHAPING_Y, step_needed.y && shaping_y.enabled),
z_step = TERN0(INPUT_SHAPING_Z, step_needed.z && shaping_z.enabled);
if (x_step || y_step || z_step)
ShapingQueue::enqueue(x_step, TERN0(INPUT_SHAPING_X, shaping_x.forward), y_step, TERN0(INPUT_SHAPING_Y, shaping_y.forward), z_step, TERN0(INPUT_SHAPING_Z, shaping_z.forward));
// do the first part of the secondary bresenham
#if ENABLED(INPUT_SHAPING_X)
@ -2018,6 +2026,10 @@ void Stepper::pulse_phase_isr() {
if (y_step)
PULSE_PREP_SHAPING(Y, shaping_y.delta_error, shaping_y.forward ? shaping_y.factor1 : -shaping_y.factor1);
#endif
#if ENABLED(INPUT_SHAPING_Z)
if (z_step)
PULSE_PREP_SHAPING(Z, shaping_z.delta_error, shaping_z.forward ? shaping_z.factor1 : -shaping_z.factor1);
#endif
#endif
}
@ -2124,6 +2136,7 @@ void Stepper::pulse_phase_isr() {
// Clear the echoes that are ready to process. If the buffers are too full and risk overflow, also apply echoes early.
TERN_(INPUT_SHAPING_X, step_needed.x = !ShapingQueue::peek_x() || ShapingQueue::free_count_x() < steps_per_isr);
TERN_(INPUT_SHAPING_Y, step_needed.y = !ShapingQueue::peek_y() || ShapingQueue::free_count_y() < steps_per_isr);
TERN_(INPUT_SHAPING_Z, step_needed.z = !ShapingQueue::peek_z() || ShapingQueue::free_count_z() < steps_per_isr);
if (bool(step_needed)) while (true) {
#if ENABLED(INPUT_SHAPING_X)
@ -2142,6 +2155,14 @@ void Stepper::pulse_phase_isr() {
}
#endif
#if ENABLED(INPUT_SHAPING_Z)
if (step_needed.z) {
const bool forward = ShapingQueue::dequeue_z();
PULSE_PREP_SHAPING(Z, shaping_z.delta_error, (forward ? shaping_z.factor2 : -shaping_z.factor2));
PULSE_START(Z);
}
#endif
TERN_(I2S_STEPPER_STREAM, i2s_push_sample());
USING_TIMED_PULSE();
@ -2156,10 +2177,14 @@ void Stepper::pulse_phase_isr() {
#if ENABLED(INPUT_SHAPING_Y)
PULSE_STOP(Y);
#endif
#if ENABLED(INPUT_SHAPING_Z)
PULSE_STOP(Z);
#endif
}
TERN_(INPUT_SHAPING_X, step_needed.x = !ShapingQueue::peek_x() || ShapingQueue::free_count_x() < steps_per_isr);
TERN_(INPUT_SHAPING_Y, step_needed.y = !ShapingQueue::peek_y() || ShapingQueue::free_count_y() < steps_per_isr);
TERN_(INPUT_SHAPING_Z, step_needed.z = !ShapingQueue::peek_z() || ShapingQueue::free_count_z() < steps_per_isr);
if (!bool(step_needed)) break;
@ -2708,7 +2733,7 @@ hal_timer_t Stepper::block_phase_isr() {
}
#endif
// Y follows the same logic as X (but the comments aren't repeated)
// Y and Z follow the same logic as X (but the comments aren't repeated)
#if ENABLED(INPUT_SHAPING_Y)
if (shaping_y.enabled) {
const int64_t steps = current_block->direction_bits.y ? int64_t(current_block->steps.y) : -int64_t(current_block->steps.y);
@ -2718,6 +2743,15 @@ hal_timer_t Stepper::block_phase_isr() {
}
#endif
#if ENABLED(INPUT_SHAPING_Z)
if (shaping_z.enabled) {
const int64_t steps = current_block->direction_bits.z ? int64_t(current_block->steps.z) : -int64_t(current_block->steps.z);
shaping_z.last_block_end_pos += steps;
shaping_z.forward = current_block->direction_bits.z;
if (!ShapingQueue::empty_z()) current_block->direction_bits.z = last_direction_bits.z;
}
#endif
// No step events completed so far
step_events_completed = 0;
@ -3220,12 +3254,14 @@ void Stepper::init() {
hal.isr_off();
TERN_(INPUT_SHAPING_X, if (axis == X_AXIS) { shaping_x.factor2 = factor2; shaping_x.factor1 = 128 - factor2; shaping_x.zeta = zeta; })
TERN_(INPUT_SHAPING_Y, if (axis == Y_AXIS) { shaping_y.factor2 = factor2; shaping_y.factor1 = 128 - factor2; shaping_y.zeta = zeta; })
TERN_(INPUT_SHAPING_Z, if (axis == Z_AXIS) { shaping_z.factor2 = factor2; shaping_z.factor1 = 128 - factor2; shaping_z.zeta = zeta; })
if (was_on) hal.isr_on();
}
float Stepper::get_shaping_damping_ratio(const AxisEnum axis) {
TERN_(INPUT_SHAPING_X, if (axis == X_AXIS) return shaping_x.zeta);
TERN_(INPUT_SHAPING_Y, if (axis == Y_AXIS) return shaping_y.zeta);
TERN_(INPUT_SHAPING_Z, if (axis == Z_AXIS) return shaping_z.zeta);
return -1;
}
@ -3237,24 +3273,18 @@ void Stepper::init() {
hal.isr_off();
const shaping_time_t delay = freq ? float(uint32_t(STEPPER_TIMER_RATE) / 2) / freq : shaping_time_t(-1);
#if ENABLED(INPUT_SHAPING_X)
if (axis == X_AXIS) {
ShapingQueue::set_delay(X_AXIS, delay);
shaping_x.frequency = freq;
shaping_x.enabled = !!freq;
shaping_x.delta_error = 0;
shaping_x.last_block_end_pos = count_position.x;
#define SHAPING_SET_FREQ_FOR_AXIS(AXISN, AXISL) \
if (axis == AXISN) { \
ShapingQueue::set_delay(AXISN, delay); \
shaping_##AXISL.frequency = freq; \
shaping_##AXISL.enabled = !!freq; \
shaping_##AXISL.delta_error = 0; \
shaping_##AXISL.last_block_end_pos = count_position.AXISL; \
}
#endif
#if ENABLED(INPUT_SHAPING_Y)
if (axis == Y_AXIS) {
ShapingQueue::set_delay(Y_AXIS, delay);
shaping_y.frequency = freq;
shaping_y.enabled = !!freq;
shaping_y.delta_error = 0;
shaping_y.last_block_end_pos = count_position.y;
}
#endif
TERN_(INPUT_SHAPING_X, SHAPING_SET_FREQ_FOR_AXIS(X_AXIS, x))
TERN_(INPUT_SHAPING_Y, SHAPING_SET_FREQ_FOR_AXIS(Y_AXIS, y))
TERN_(INPUT_SHAPING_Z, SHAPING_SET_FREQ_FOR_AXIS(Z_AXIS, z))
if (was_on) hal.isr_on();
}
@ -3262,6 +3292,7 @@ void Stepper::init() {
float Stepper::get_shaping_frequency(const AxisEnum axis) {
TERN_(INPUT_SHAPING_X, if (axis == X_AXIS) return shaping_x.frequency);
TERN_(INPUT_SHAPING_Y, if (axis == Y_AXIS) return shaping_y.frequency);
TERN_(INPUT_SHAPING_Z, if (axis == Z_AXIS) return shaping_z.frequency);
return -1;
}
@ -3283,6 +3314,9 @@ void Stepper::_set_position(const abce_long_t &spos) {
#if ENABLED(INPUT_SHAPING_Y)
const int32_t y_shaping_delta = count_position.y - shaping_y.last_block_end_pos;
#endif
#if ENABLED(INPUT_SHAPING_Z)
const int32_t z_shaping_delta = count_position.z - shaping_z.last_block_end_pos;
#endif
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
// Core equations follow the form of the dA and dB equations at https://www.corexy.com/theory.html
@ -3323,6 +3357,12 @@ void Stepper::_set_position(const abce_long_t &spos) {
shaping_y.last_block_end_pos = spos.y;
}
#endif
#if ENABLED(INPUT_SHAPING_Z)
if (shaping_z.enabled) {
count_position.z += z_shaping_delta;
shaping_z.last_block_end_pos = spos.z;
}
#endif
}
/**
@ -3364,6 +3404,7 @@ void Stepper::set_axis_position(const AxisEnum a, const int32_t &v) {
count_position[a] = v;
TERN_(INPUT_SHAPING_X, if (a == X_AXIS) shaping_x.last_block_end_pos = v);
TERN_(INPUT_SHAPING_Y, if (a == Y_AXIS) shaping_y.last_block_end_pos = v);
TERN_(INPUT_SHAPING_Z, if (a == Z_AXIS) shaping_z.last_block_end_pos = v);
#ifdef __AVR__
// Reenable Stepper ISR

View File

@ -143,7 +143,8 @@ constexpr ena_mask_t enable_overlap[] = {
constexpr float _ISDASU[] = DEFAULT_AXIS_STEPS_PER_UNIT;
constexpr feedRate_t _ISDMF[] = DEFAULT_MAX_FEEDRATE;
constexpr float max_shaped_rate = TERN0(INPUT_SHAPING_X, _ISDMF[X_AXIS] * _ISDASU[X_AXIS]) +
TERN0(INPUT_SHAPING_Y, _ISDMF[Y_AXIS] * _ISDASU[Y_AXIS]);
TERN0(INPUT_SHAPING_Y, _ISDMF[Y_AXIS] * _ISDASU[Y_AXIS]) +
TERN0(INPUT_SHAPING_Z, _ISDMF[Z_AXIS] * _ISDASU[Z_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<unsigned int INDEX=DISTINCT_AXES> constexpr float max_isr_rate() {
@ -159,7 +160,7 @@ constexpr ena_mask_t enable_overlap[] = {
#endif
#ifndef SHAPING_MIN_FREQ
#define SHAPING_MIN_FREQ _MIN(__FLT_MAX__ OPTARG(INPUT_SHAPING_X, SHAPING_FREQ_X) OPTARG(INPUT_SHAPING_Y, SHAPING_FREQ_Y))
#define SHAPING_MIN_FREQ _MIN(__FLT_MAX__ OPTARG(INPUT_SHAPING_X, SHAPING_FREQ_X) OPTARG(INPUT_SHAPING_Y, SHAPING_FREQ_Y) OPTARG(INPUT_SHAPING_Z, SHAPING_FREQ_Z))
#endif
constexpr float shaping_min_freq = SHAPING_MIN_FREQ;
constexpr uint16_t shaping_echoes = FLOOR(max_step_rate / shaping_min_freq / 2) + 3;
@ -169,6 +170,7 @@ constexpr ena_mask_t enable_overlap[] = {
struct shaping_echo_axis_t {
TERN_(INPUT_SHAPING_X, shaping_echo_t x:2);
TERN_(INPUT_SHAPING_Y, shaping_echo_t y:2);
TERN_(INPUT_SHAPING_Z, shaping_echo_t z:2);
};
class ShapingQueue {
@ -178,96 +180,89 @@ constexpr ena_mask_t enable_overlap[] = {
static shaping_echo_axis_t echo_axes[shaping_echoes];
static uint16_t tail;
#if ENABLED(INPUT_SHAPING_X)
static shaping_time_t delay_x; // = shaping_time_t(-1) to disable queueing
static shaping_time_t peek_x_val;
static uint16_t head_x;
static uint16_t _free_count_x;
#endif
#if ENABLED(INPUT_SHAPING_Y)
static shaping_time_t delay_y; // = shaping_time_t(-1) to disable queueing
static shaping_time_t peek_y_val;
static uint16_t head_y;
static uint16_t _free_count_y;
#endif
#define SHAPING_QUEUE_AXIS_VARS(AXIS) \
static shaping_time_t delay_##AXIS; /* = shaping_time_t(-1) to disable queueing*/ \
static shaping_time_t _peek_##AXIS; \
static uint16_t head_##AXIS; \
static uint16_t _free_count_##AXIS;
TERN_(INPUT_SHAPING_X, SHAPING_QUEUE_AXIS_VARS(x))
TERN_(INPUT_SHAPING_Y, SHAPING_QUEUE_AXIS_VARS(y))
TERN_(INPUT_SHAPING_Z, SHAPING_QUEUE_AXIS_VARS(z))
public:
static void decrement_delays(const shaping_time_t interval) {
now += interval;
TERN_(INPUT_SHAPING_X, if (peek_x_val != shaping_time_t(-1)) peek_x_val -= interval);
TERN_(INPUT_SHAPING_Y, if (peek_y_val != shaping_time_t(-1)) peek_y_val -= interval);
TERN_(INPUT_SHAPING_X, if (_peek_x != shaping_time_t(-1)) _peek_x -= interval);
TERN_(INPUT_SHAPING_Y, if (_peek_y != shaping_time_t(-1)) _peek_y -= interval);
TERN_(INPUT_SHAPING_Z, if (_peek_z != shaping_time_t(-1)) _peek_z -= interval);
}
static void set_delay(const AxisEnum axis, const shaping_time_t delay) {
TERN_(INPUT_SHAPING_X, if (axis == X_AXIS) delay_x = delay);
TERN_(INPUT_SHAPING_Y, if (axis == Y_AXIS) delay_y = delay);
TERN_(INPUT_SHAPING_Z, if (axis == Z_AXIS) delay_z = delay);
}
static void enqueue(const bool x_step, const bool x_forward, const bool y_step, const bool y_forward) {
#if ENABLED(INPUT_SHAPING_X)
if (x_step) {
if (head_x == tail) peek_x_val = delay_x;
echo_axes[tail].x = x_forward ? ECHO_FWD : ECHO_BWD;
_free_count_x--;
static void enqueue(const bool x_step, const bool x_forward, const bool y_step, const bool y_forward, const bool z_step, const bool z_forward) {
#define SHAPING_QUEUE_ENQUEUE(AXIS) \
if (AXIS##_step) { \
if (head_##AXIS == tail) _peek_##AXIS = delay_##AXIS; \
echo_axes[tail].AXIS = AXIS##_forward ? ECHO_FWD : ECHO_BWD; \
_free_count_##AXIS--; \
} \
else { \
echo_axes[tail].AXIS = ECHO_NONE; \
if (head_##AXIS != tail) \
_free_count_##AXIS--; \
else if (++head_##AXIS == shaping_echoes) \
head_##AXIS = 0; \
}
else {
echo_axes[tail].x = ECHO_NONE;
if (head_x != tail)
_free_count_x--;
else if (++head_x == shaping_echoes)
head_x = 0;
}
#endif
#if ENABLED(INPUT_SHAPING_Y)
if (y_step) {
if (head_y == tail) peek_y_val = delay_y;
echo_axes[tail].y = y_forward ? ECHO_FWD : ECHO_BWD;
_free_count_y--;
}
else {
echo_axes[tail].y = ECHO_NONE;
if (head_y != tail)
_free_count_y--;
else if (++head_y == shaping_echoes)
head_y = 0;
}
#endif
TERN_(INPUT_SHAPING_X, SHAPING_QUEUE_ENQUEUE(x))
TERN_(INPUT_SHAPING_Y, SHAPING_QUEUE_ENQUEUE(y))
TERN_(INPUT_SHAPING_Z, SHAPING_QUEUE_ENQUEUE(z))
times[tail] = now;
if (++tail == shaping_echoes) tail = 0;
}
#if ENABLED(INPUT_SHAPING_X)
static shaping_time_t peek_x() { return peek_x_val; }
static bool dequeue_x() {
bool forward = echo_axes[head_x].x == ECHO_FWD;
do {
_free_count_x++;
if (++head_x == shaping_echoes) head_x = 0;
} while (head_x != tail && echo_axes[head_x].x == ECHO_NONE);
peek_x_val = head_x == tail ? shaping_time_t(-1) : times[head_x] + delay_x - now;
#define SHAPING_QUEUE_DEQUEUE(AXIS) \
bool forward = echo_axes[head_##AXIS].AXIS == ECHO_FWD; \
do { \
_free_count_##AXIS++; \
if (++head_##AXIS == shaping_echoes) head_##AXIS = 0; \
} while (head_##AXIS != tail && echo_axes[head_##AXIS].AXIS == ECHO_NONE); \
_peek_##AXIS = head_##AXIS == tail ? shaping_time_t(-1) : times[head_##AXIS] + delay_##AXIS - now; \
return forward;
}
#if ENABLED(INPUT_SHAPING_X)
static shaping_time_t peek_x() { return _peek_x; }
static bool dequeue_x() { SHAPING_QUEUE_DEQUEUE(x) }
static bool empty_x() { return head_x == tail; }
static uint16_t free_count_x() { return _free_count_x; }
#endif
#if ENABLED(INPUT_SHAPING_Y)
static shaping_time_t peek_y() { return peek_y_val; }
static bool dequeue_y() {
bool forward = echo_axes[head_y].y == ECHO_FWD;
do {
_free_count_y++;
if (++head_y == shaping_echoes) head_y = 0;
} while (head_y != tail && echo_axes[head_y].y == ECHO_NONE);
peek_y_val = head_y == tail ? shaping_time_t(-1) : times[head_y] + delay_y - now;
return forward;
}
static shaping_time_t peek_y() { return _peek_y; }
static bool dequeue_y() { SHAPING_QUEUE_DEQUEUE(y) }
static bool empty_y() { return head_y == tail; }
static uint16_t free_count_y() { return _free_count_y; }
#endif
#if ENABLED(INPUT_SHAPING_Z)
static shaping_time_t peek_z() { return _peek_z; }
static bool dequeue_z() { SHAPING_QUEUE_DEQUEUE(z) }
static bool empty_z() { return head_z == tail; }
static uint16_t free_count_z() { return _free_count_z; }
#endif
static void purge() {
const auto st = shaping_time_t(-1);
#if ENABLED(INPUT_SHAPING_X)
head_x = tail; _free_count_x = shaping_echoes - 1; peek_x_val = st;
head_x = tail; _free_count_x = shaping_echoes - 1; _peek_x = st;
#endif
#if ENABLED(INPUT_SHAPING_Y)
head_y = tail; _free_count_y = shaping_echoes - 1; peek_y_val = st;
head_y = tail; _free_count_y = shaping_echoes - 1; _peek_y = st;
#endif
#if ENABLED(INPUT_SHAPING_Z)
head_z = tail; _free_count_z = shaping_echoes - 1; _peek_z = st;
#endif
}
};
@ -420,6 +415,9 @@ class Stepper {
#if ENABLED(INPUT_SHAPING_Y)
static ShapeParams shaping_y;
#endif
#if ENABLED(INPUT_SHAPING_Z)
static ShapeParams shaping_z;
#endif
#endif
#if ENABLED(LIN_ADVANCE)
@ -517,7 +515,7 @@ class Stepper {
const bool was_on = hal.isr_state();
hal.isr_off();
const bool result = TERN0(INPUT_SHAPING_X, !ShapingQueue::empty_x()) || TERN0(INPUT_SHAPING_Y, !ShapingQueue::empty_y());
const bool result = TERN0(INPUT_SHAPING_X, !ShapingQueue::empty_x()) || TERN0(INPUT_SHAPING_Y, !ShapingQueue::empty_y()) || TERN0(INPUT_SHAPING_Z, !ShapingQueue::empty_z());
if (was_on) hal.isr_on();

View File

@ -188,7 +188,7 @@
#define ISR_LOOP_CYCLES(R) ((ISR_LOOP_BASE_CYCLES + MIN_ISR_LOOP_CYCLES + MIN_STEPPER_PULSE_CYCLES) * ((1UL << R) - 1) + _MAX(MIN_ISR_LOOP_CYCLES, MIN_STEPPER_PULSE_CYCLES))
// Model input shaping as an extra loop call
#define ISR_SHAPING_LOOP_CYCLES(R) (TERN0(HAS_ZV_SHAPING, (ISR_LOOP_BASE_CYCLES + TERN0(INPUT_SHAPING_X, ISR_X_STEPPER_CYCLES) + TERN0(INPUT_SHAPING_Y, ISR_Y_STEPPER_CYCLES)) << R))
#define ISR_SHAPING_LOOP_CYCLES(R) (TERN0(HAS_ZV_SHAPING, (ISR_LOOP_BASE_CYCLES + TERN0(INPUT_SHAPING_X, ISR_X_STEPPER_CYCLES) + TERN0(INPUT_SHAPING_Y, ISR_Y_STEPPER_CYCLES) + TERN0(INPUT_SHAPING_Z, ISR_Z_STEPPER_CYCLES)) << R))
// If linear advance is enabled, then it is handled separately
#if ENABLED(LIN_ADVANCE)

View File

@ -96,7 +96,7 @@ opt_set MOTHERBOARD BOARD_AZTEEG_X3_PRO MIXING_STEPPERS 5 LCD_LANGUAGE ru \
opt_enable MIXING_EXTRUDER GRADIENT_MIX GRADIENT_VTOOL CR10_STOCKDISPLAY \
USE_CONTROLLER_FAN CONTROLLER_FAN_EDITABLE CONTROLLER_FAN_IGNORE_Z \
XY_AFTER_HOMING EVENT_GCODE_AFTER_HOMING \
FILAMENT_RUNOUT_SENSOR ADVANCED_PAUSE_FEATURE NOZZLE_PARK_FEATURE INPUT_SHAPING_X INPUT_SHAPING_Y
FILAMENT_RUNOUT_SENSOR ADVANCED_PAUSE_FEATURE NOZZLE_PARK_FEATURE INPUT_SHAPING_X INPUT_SHAPING_Y INPUT_SHAPING_Z
opt_disable DISABLE_OTHER_EXTRUDERS
exec_test $1 $2 "Azteeg X3 | Mixing Extruder (x5) | Gradient Mix | Input Shaping | Russian" "$3"