✨ FTMotion Z shaping, axis sync, axis smoothing (#28055)
This commit is contained in:
+44
-30
@@ -1143,49 +1143,73 @@
|
||||
|
||||
/**
|
||||
* Fixed-time-based Motion Control -- BETA FEATURE
|
||||
* Enable/disable and set parameters with G-code M493.
|
||||
* Enable/disable and set parameters with G-code M493 and M494.
|
||||
* See ft_types.h for named values used by FTM options.
|
||||
*/
|
||||
//#define FT_MOTION
|
||||
#if ENABLED(FT_MOTION)
|
||||
//#define FTM_IS_DEFAULT_MOTION // Use FT Motion as the factory default?
|
||||
//#define FTM_IS_DEFAULT_MOTION // Use FT Motion as the factory default?
|
||||
//#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 and M494 parameters
|
||||
|
||||
#define FTM_DEFAULT_DYNFREQ_MODE dynFreqMode_DISABLED // Default mode of dynamic frequency calculation. (DISABLED, Z_BASED, MASS_BASED)
|
||||
|
||||
#define FTM_LINEAR_ADV_DEFAULT_ENA false // Default linear advance enable (true) or disable (false)
|
||||
#define FTM_LINEAR_ADV_DEFAULT_K 0.0f // Default linear advance gain. (Acceleration-based scaling factor.)
|
||||
#define FTM_LINEAR_ADV_DEFAULT_ENA false // Default linear advance enable (true) or disable (false)
|
||||
#define FTM_LINEAR_ADV_DEFAULT_K 0.0f // Default linear advance gain. (Acceleration-based scaling factor.)
|
||||
|
||||
#define FTM_DEFAULT_SHAPER_X ftMotionShaper_NONE // Default shaper mode on X axis (NONE, ZV, ZVD, ZVDD, ZVDDD, EI, 2HEI, 3HEI, MZV)
|
||||
#define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers
|
||||
#define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis
|
||||
#define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis
|
||||
#define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers
|
||||
#define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis
|
||||
#define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis
|
||||
|
||||
#define FTM_DEFAULT_SHAPER_Y ftMotionShaper_NONE // Default shaper mode on Y axis
|
||||
#define FTM_SHAPING_DEFAULT_FREQ_Y 37.0f // (Hz) Default peak frequency used by input shapers
|
||||
#define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis
|
||||
#define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis
|
||||
#define FTM_SHAPING_DEFAULT_FREQ_Y 37.0f // (Hz) Default peak frequency used by input shapers
|
||||
#define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis
|
||||
#define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis
|
||||
|
||||
//#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 parameters
|
||||
//#define FTM_SHAPER_Z // Include Z shaping support
|
||||
#define FTM_DEFAULT_SHAPER_Z ftMotionShaper_NONE // Default shaper mode on Z axis
|
||||
#define FTM_SHAPING_DEFAULT_FREQ_Z 21.0f // (Hz) Default peak frequency used by input shapers
|
||||
#define FTM_SHAPING_ZETA_Z 0.03f // Zeta used by input shapers for Z axis
|
||||
#define FTM_SHAPING_V_TOL_Z 0.05f // Vibration tolerance used by EI input shapers for Z axis
|
||||
|
||||
//#define FTM_SHAPER_E // Include E shaping support
|
||||
// Required to synchronise extruder with XYZ (better quality)
|
||||
#define FTM_DEFAULT_SHAPER_E ftMotionShaper_NONE // Default shaper mode on Extruder axis
|
||||
#define FTM_SHAPING_DEFAULT_FREQ_E 21.0f // (Hz) Default peak frequency used by input shapers
|
||||
#define FTM_SHAPING_ZETA_E 0.03f // Zeta used by input shapers for E axis
|
||||
#define FTM_SHAPING_V_TOL_E 0.05f // Vibration tolerance used by EI input shapers for E axis
|
||||
|
||||
//#define FTM_SMOOTHING // Smoothing can reduce artifacts and make steppers quieter
|
||||
// on sharp corners, but too much will round corners.
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
#define FTM_MAX_SMOOTHING_TIME 0.10f // Maximum smoothing time (seconds), higher consumes more RAM.
|
||||
// Increase smoothing time to reduce jerky motion, ghosting and noises.
|
||||
#define FTM_SMOOTHING_TIME_X 0.00f // (s) Smoothing time for X axis. Zero means disabled.
|
||||
#define FTM_SMOOTHING_TIME_Y 0.00f // (s) Smoothing time for Y axis
|
||||
#define FTM_SMOOTHING_TIME_Z 0.00f // (s) Smoothing time for Z axis
|
||||
#define FTM_SMOOTHING_TIME_E 0.02f // (s) Smoothing time for E axis. Prevents noise/skipping from LA by
|
||||
// smoothing acceleration peaks, which may also smooth curved surfaces.
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Advanced configuration
|
||||
*/
|
||||
#define FTM_UNIFIED_BWS // DON'T DISABLE unless you use Ulendo FBS (not implemented)
|
||||
#define FTM_UNIFIED_BWS // DON'T DISABLE unless you use Ulendo FBS (not implemented)
|
||||
#if ENABLED(FTM_UNIFIED_BWS)
|
||||
#define FTM_BW_SIZE 100 // Unified Window and Batch size with a ratio of 2
|
||||
#define FTM_BW_SIZE 100 // Unified Window and Batch size with a ratio of 2
|
||||
#else
|
||||
#define FTM_WINDOW_SIZE 200 // Custom Window size for trajectory generation needed by Ulendo FBS
|
||||
#define FTM_BATCH_SIZE 100 // Custom Batch size for trajectory generation needed by Ulendo FBS
|
||||
#define FTM_WINDOW_SIZE 200 // Custom Window size for trajectory generation needed by Ulendo FBS
|
||||
#define FTM_BATCH_SIZE 100 // Custom Batch size for trajectory generation needed by Ulendo FBS
|
||||
#endif
|
||||
|
||||
#define FTM_FS 1000 // (Hz) Frequency for trajectory generation. (Reciprocal of FTM_TS)
|
||||
#define FTM_TS 0.001f // (s) Time step for trajectory generation. (Reciprocal of FTM_FS)
|
||||
#define FTM_FS 1000 // (Hz) Frequency for trajectory generation. (Reciprocal of FTM_TS)
|
||||
|
||||
#if DISABLED(COREXY)
|
||||
#define FTM_STEPPER_FS 20000 // (Hz) Frequency for stepper I/O update
|
||||
#define FTM_STEPPER_FS 20000 // (Hz) Frequency for stepper I/O update
|
||||
|
||||
// Use this to adjust the time required to consume the command buffer.
|
||||
// Try increasing this value if stepper motion is choppy.
|
||||
#define FTM_STEPPERCMD_BUFF_SIZE 3000 // Size of the stepper command buffers
|
||||
#define FTM_STEPPERCMD_BUFF_SIZE 3000 // Size of the stepper command buffers
|
||||
|
||||
#else
|
||||
// CoreXY motion needs a larger buffer size. These values are based on our testing.
|
||||
@@ -1193,17 +1217,7 @@
|
||||
#define FTM_STEPPERCMD_BUFF_SIZE 6000
|
||||
#endif
|
||||
|
||||
#define FTM_STEPS_PER_UNIT_TIME (FTM_STEPPER_FS / FTM_FS) // Interpolated stepper commands per unit time
|
||||
#define FTM_MIN_TICKS ((STEPPER_TIMER_RATE) / (FTM_STEPPER_FS)) // Minimum stepper ticks between steps
|
||||
|
||||
#define FTM_MIN_SHAPE_FREQ 10 // Minimum shaping frequency
|
||||
#define FTM_RATIO (FTM_FS / FTM_MIN_SHAPE_FREQ) // Factor for use in FTM_ZMAX. DON'T CHANGE.
|
||||
#define FTM_ZMAX (FTM_RATIO * 2) // Maximum delays for shaping functions (even numbers only!)
|
||||
// Calculate as:
|
||||
// ZV : FTM_RATIO / 2
|
||||
// ZVD, MZV : FTM_RATIO
|
||||
// 2HEI : FTM_RATIO * 3 / 2
|
||||
// 3HEI : FTM_RATIO * 2
|
||||
#define FTM_MIN_SHAPE_FREQ 10 // (Hz) Minimum shaping frequency, lower consumes more RAM
|
||||
#endif // FT_MOTION
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,6 @@
|
||||
|
||||
// LPC1768 boards seem to lose steps when saving to EEPROM during print (issue #20785)
|
||||
// TODO: Which other boards are incompatible?
|
||||
#if defined(MCU_LPC1768) && ENABLED(FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0
|
||||
#if ALL(MCU_LPC1768, FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0
|
||||
#define PRINTCOUNTER_SYNC
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,6 @@
|
||||
#endif
|
||||
|
||||
// Some STM32F4 boards may lose steps when saving to EEPROM during print (PR #17946)
|
||||
#if defined(STM32F4xx) && ENABLED(FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0
|
||||
#if ALL(STM32F4xx, FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0
|
||||
#define PRINTCOUNTER_SYNC
|
||||
#endif
|
||||
|
||||
@@ -92,7 +92,7 @@ void install_min_serial() {
|
||||
HAL_min_serial_out = &TX;
|
||||
}
|
||||
|
||||
#if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them
|
||||
#if NONE(DYNAMIC_VECTORTABLE, STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them
|
||||
extern "C" {
|
||||
__attribute__((naked)) void JumpHandler_ASM() {
|
||||
__asm__ __volatile__ (
|
||||
|
||||
@@ -358,6 +358,21 @@
|
||||
#define STR_Z2 STR_C "2"
|
||||
#define STR_Z3 STR_C "3"
|
||||
#define STR_Z4 STR_C "4"
|
||||
#if CORE_IS_XY || CORE_IS_XZ
|
||||
#define STEPPER_A_NAME 'A'
|
||||
#else
|
||||
#define STEPPER_A_NAME 'X'
|
||||
#endif
|
||||
#if CORE_IS_XY || CORE_IS_YZ
|
||||
#define STEPPER_B_NAME 'B'
|
||||
#else
|
||||
#define STEPPER_B_NAME 'Y'
|
||||
#endif
|
||||
#if CORE_IS_XZ || CORE_IS_YZ
|
||||
#define STEPPER_C_NAME 'C'
|
||||
#else
|
||||
#define STEPPER_C_NAME 'Z'
|
||||
#endif
|
||||
|
||||
//
|
||||
// Endstop Names used by Endstops::report_states
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
#include "../../../module/ft_motion.h"
|
||||
#include "../../../module/stepper.h"
|
||||
|
||||
void say_shaper_type(const AxisEnum a) {
|
||||
SERIAL_ECHOPGM(" axis ");
|
||||
void say_shaper_type(const AxisEnum a, bool &sep, const char axis_name) {
|
||||
if (sep) SERIAL_ECHOPGM(" ; ");
|
||||
SERIAL_CHAR(axis_name, '=');
|
||||
switch (ftMotion.cfg.shaper[a]) {
|
||||
default: break;
|
||||
case ftMotionShaper_ZV: SERIAL_ECHOPGM("ZV"); break;
|
||||
@@ -41,46 +42,36 @@ void say_shaper_type(const AxisEnum a) {
|
||||
case ftMotionShaper_3HEI: SERIAL_ECHOPGM("3 Hump EI"); break;
|
||||
case ftMotionShaper_MZV: SERIAL_ECHOPGM("MZV"); break;
|
||||
}
|
||||
SERIAL_ECHOPGM(" shaping");
|
||||
sep = true;
|
||||
}
|
||||
|
||||
#if CORE_IS_XY || CORE_IS_XZ
|
||||
#define AXIS_0_NAME "A"
|
||||
#else
|
||||
#define AXIS_0_NAME "X"
|
||||
#endif
|
||||
#if CORE_IS_XY || CORE_IS_YZ
|
||||
#define AXIS_1_NAME "B"
|
||||
#else
|
||||
#define AXIS_1_NAME "Y"
|
||||
#endif
|
||||
|
||||
void say_shaping() {
|
||||
// FT Enabled
|
||||
SERIAL_ECHO_TERNARY(ftMotion.cfg.active, "Fixed-Time Motion ", "en", "dis", "abled");
|
||||
|
||||
// FT Shaping
|
||||
bool sep = false;
|
||||
SERIAL_ECHOPGM(" (");
|
||||
#if HAS_X_AXIS
|
||||
if (AXIS_IS_SHAPING(X)) {
|
||||
SERIAL_ECHOPGM(" with " AXIS_0_NAME);
|
||||
say_shaper_type(X_AXIS);
|
||||
}
|
||||
if (AXIS_IS_SHAPING(X)) say_shaper_type(X_AXIS, sep, STEPPER_A_NAME);
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
if (AXIS_IS_SHAPING(Y)) {
|
||||
SERIAL_ECHOPGM(" and with " AXIS_1_NAME);
|
||||
say_shaper_type(Y_AXIS);
|
||||
}
|
||||
if (AXIS_IS_SHAPING(Y)) say_shaper_type(Y_AXIS, sep, STEPPER_B_NAME);
|
||||
#endif
|
||||
|
||||
SERIAL_ECHOLNPGM(".");
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
if (AXIS_IS_SHAPING(Z)) say_shaper_type(Z_AXIS, sep, STEPPER_C_NAME);
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
if (AXIS_IS_SHAPING(E)) say_shaper_type(E_AXIS, sep, 'E');
|
||||
#endif
|
||||
SERIAL_ECHOLNPGM(")");
|
||||
|
||||
const bool z_based = TERN0(HAS_DYNAMIC_FREQ_MM, ftMotion.cfg.dynFreqMode == dynFreqMode_Z_BASED),
|
||||
g_based = TERN0(HAS_DYNAMIC_FREQ_G, ftMotion.cfg.dynFreqMode == dynFreqMode_MASS_BASED),
|
||||
dynamic = z_based || g_based;
|
||||
|
||||
// FT Dynamic Frequency Mode
|
||||
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y)) {
|
||||
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y) || AXIS_IS_SHAPING(Z) || AXIS_IS_SHAPING(E)) {
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
SERIAL_ECHOPGM("Dynamic Frequency Mode ");
|
||||
switch (ftMotion.cfg.dynFreqMode) {
|
||||
@@ -97,7 +88,8 @@ void say_shaping() {
|
||||
#endif
|
||||
|
||||
#if HAS_X_AXIS
|
||||
SERIAL_ECHO_TERNARY(dynamic, AXIS_0_NAME " ", "base dynamic", "static", " shaper frequency: ");
|
||||
SERIAL_CHAR(STEPPER_A_NAME);
|
||||
SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: ");
|
||||
SERIAL_ECHO(p_float_t(ftMotion.cfg.baseFreq.x, 2), F("Hz"));
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(ftMotion.cfg.dynFreqK.x, 2), F("Hz/"), z_based ? F("mm") : F("g"));
|
||||
@@ -106,13 +98,24 @@ void say_shaping() {
|
||||
#endif
|
||||
|
||||
#if HAS_Y_AXIS
|
||||
SERIAL_ECHO_TERNARY(dynamic, AXIS_1_NAME " ", "base dynamic", "static", " shaper frequency: ");
|
||||
SERIAL_CHAR(STEPPER_B_NAME);
|
||||
SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: ");
|
||||
SERIAL_ECHO(p_float_t(ftMotion.cfg.baseFreq.y, 2), F(" Hz"));
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(ftMotion.cfg.dynFreqK.y, 2), F("Hz/"), z_based ? F("mm") : F("g"));
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
SERIAL_CHAR(STEPPER_C_NAME);
|
||||
SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: ");
|
||||
SERIAL_ECHO(p_float_t(ftMotion.cfg.baseFreq.z, 2), F(" Hz"));
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(ftMotion.cfg.dynFreqK.z, 2), F("Hz/"), z_based ? F("mm") : F("g"));
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
@@ -129,26 +132,44 @@ void GcodeSuite::M493_report(const bool forReplay/*=true*/) {
|
||||
|
||||
report_heading_etc(forReplay, F(STR_FT_MOTION));
|
||||
const ft_config_t &c = ftMotion.cfg;
|
||||
SERIAL_ECHOPGM(" M493 S", c.active);
|
||||
#if HAS_X_AXIS
|
||||
SERIAL_ECHOPGM(" A", c.baseFreq.x);
|
||||
#if HAS_Y_AXIS
|
||||
SERIAL_ECHOPGM(" B", c.baseFreq.y);
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
SERIAL_ECHOPGM(" D", c.dynFreqMode);
|
||||
SERIAL_ECHOLNPGM(
|
||||
" M493 S", c.active
|
||||
#if HAS_X_AXIS
|
||||
SERIAL_ECHOPGM(" F", c.dynFreqK.x);
|
||||
, " A", c.baseFreq.x
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
, " B", c.baseFreq.y
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
, " C", c.baseFreq.z
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
, " E", c.baseFreq.e
|
||||
#endif
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
, " D", c.dynFreqMode
|
||||
#if HAS_X_AXIS
|
||||
, " F", c.dynFreqK.x
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
SERIAL_ECHOPGM(" H", c.dynFreqK.y);
|
||||
, " H", c.dynFreqK.y
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
, " L", c.dynFreqK.z
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
, " O", c.dynFreqK.e
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_EXTRUDERS
|
||||
SERIAL_ECHOPGM(" P", c.linearAdvEna, " K", c.linearAdvK);
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
|
||||
, " G", c.axis_sync_enabled
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
, " P", c.linearAdvEna, " K", c.linearAdvK
|
||||
#endif
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,8 +179,8 @@ void GcodeSuite::M493_report(const bool forReplay/*=true*/) {
|
||||
* 0: Fixed-Time Motion OFF (Standard Motion)
|
||||
* 1: Fixed-Time Motion ON
|
||||
*
|
||||
* X/Y<mode> Set the vibration compensator [input shaper] mode for X / Y axis.
|
||||
* Users / slicers must remember to set the mode for both axes!
|
||||
* X/Y/Z/E<mode> Set the vibration compensator [input shaper] mode for an axis.
|
||||
* Users / slicers must remember to set the mode for all relevant axes!
|
||||
* 0: NONE : No input shaper
|
||||
* 1: ZV : Zero Vibration
|
||||
* 2: ZVD : Zero Vibration and Derivative
|
||||
@@ -174,20 +195,35 @@ void GcodeSuite::M493_report(const bool forReplay/*=true*/) {
|
||||
*
|
||||
* K<gain> Set Linear Advance gain
|
||||
*
|
||||
* G<bool> Enable (1) or Disable (0) axis synchronization.
|
||||
*
|
||||
* D<mode> Set Dynamic Frequency mode
|
||||
* 0: DISABLED
|
||||
* 1: Z-based (Requires a Z axis)
|
||||
* 2: Mass-based (Requires X and E axes)
|
||||
*
|
||||
* A<Hz> Set static/base frequency for the X axis
|
||||
* F<Hz> Set frequency scaling for the X axis
|
||||
* I 0.0 Set damping ratio for the X axis
|
||||
* Q 0.00 Set the vibration tolerance for the X axis
|
||||
* A<Hz> Set X static/base frequency
|
||||
* F<Hz> Set X frequency scaling
|
||||
* I<flt> Set X damping ratio
|
||||
* Q<flt> Set X vibration tolerance
|
||||
*
|
||||
* B<Hz> Set Y static/base frequency
|
||||
* H<Hz> Set Y frequency scaling
|
||||
* J<flt> Set Y damping ratio
|
||||
* R<flt> Set Y vibration tolerance
|
||||
*
|
||||
* With FTM_SHAPING_Z:
|
||||
* C<Hz> Set Z static/base frequency
|
||||
* L<Hz> Set Z frequency scaling
|
||||
* O<flt> Set Z damping ratio
|
||||
* M<flt> Set Z vibration tolerance
|
||||
*
|
||||
* With FTM_SHAPING_E:
|
||||
* W<Hz> Set E static/base frequency
|
||||
* O<Hz> Set E frequency scaling
|
||||
* U<flt> Set E damping ratio
|
||||
* V<flt> Set E vibration tolerance
|
||||
*
|
||||
* B<Hz> Set static/base frequency for the Y axis
|
||||
* H<Hz> Set frequency scaling for the Y axis
|
||||
* J 0.0 Set damping ratio for the Y axis
|
||||
* R 0.00 Set the vibration tolerance for the Y axis
|
||||
*/
|
||||
void GcodeSuite::M493() {
|
||||
struct { bool update:1, report:1; } flag = { false };
|
||||
@@ -205,7 +241,8 @@ void GcodeSuite::M493() {
|
||||
}
|
||||
}
|
||||
|
||||
#if HAS_X_AXIS
|
||||
#if ANY(HAS_X_AXIS, HAS_Y_AXIS, FTM_SHAPER_Z, FTM_SHAPER_E)
|
||||
|
||||
auto set_shaper = [&](const AxisEnum axis, const char c) {
|
||||
const ftMotionShaper_t newsh = (ftMotionShaper_t)parser.value_byte();
|
||||
if (newsh != ftMotion.cfg.shaper[axis]) {
|
||||
@@ -228,13 +265,20 @@ void GcodeSuite::M493() {
|
||||
return false;
|
||||
};
|
||||
|
||||
if (parser.seenval('X') && set_shaper(X_AXIS, 'X')) return; // Parse 'X' mode parameter
|
||||
|
||||
#if HAS_X_AXIS
|
||||
if (parser.seenval('X') && set_shaper(X_AXIS, 'X')) return; // Parse 'X' mode parameter
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
if (parser.seenval('Y') && set_shaper(Y_AXIS, 'Y')) return; // Parse 'Y' mode parameter
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
if (parser.seenval('Z') && set_shaper(Z_AXIS, 'Z')) return; // Parse 'Z' mode parameter
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
if (parser.seenval('E') && set_shaper(E_AXIS, 'E')) return; // Parse 'E' mode parameter
|
||||
#endif
|
||||
|
||||
#endif // HAS_X_AXIS
|
||||
#endif // HAS_X_AXIS || HAS_Y_AXIS || FTM_SHAPER_Z || FTM_SHAPER_E
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
|
||||
@@ -259,11 +303,20 @@ void GcodeSuite::M493() {
|
||||
|
||||
#endif // HAS_EXTRUDERS
|
||||
|
||||
// Parse '?' axis synchronization parameter.
|
||||
if (parser.seen('?')) {
|
||||
const bool enabled = parser.value_bool();
|
||||
if (enabled != ftMotion.cfg.axis_sync_enabled) {
|
||||
ftMotion.cfg.axis_sync_enabled = enabled;
|
||||
flag.report = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
|
||||
// Dynamic frequency mode parameter.
|
||||
if (parser.seenval('D')) {
|
||||
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y)) {
|
||||
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y) || AXIS_IS_SHAPING(Z) || AXIS_IS_SHAPING(E)) {
|
||||
const dynFreqMode_t val = dynFreqMode_t(parser.value_byte());
|
||||
switch (val) {
|
||||
#if HAS_DYNAMIC_FREQ_MM
|
||||
@@ -295,7 +348,7 @@ void GcodeSuite::M493() {
|
||||
|
||||
#if HAS_X_AXIS
|
||||
|
||||
// Parse frequency parameter (X axis).
|
||||
// Parse X frequency parameter
|
||||
if (parser.seenval('A')) {
|
||||
if (AXIS_IS_SHAPING(X)) {
|
||||
const float val = parser.value_float();
|
||||
@@ -305,59 +358,59 @@ void GcodeSuite::M493() {
|
||||
flag.update = flag.report = true;
|
||||
}
|
||||
else // Frequency out of range.
|
||||
SERIAL_ECHOLNPGM("Invalid [", C('A'), "] frequency value.");
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_A_NAME), " [", C('A'), "] frequency value.");
|
||||
}
|
||||
else // Mode doesn't use frequency.
|
||||
SERIAL_ECHOLNPGM("Wrong mode for [", C('A'), "] frequency.");
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " [", C('A'), "] frequency.");
|
||||
}
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
// Parse frequency scaling parameter (X axis).
|
||||
// Parse X frequency scaling parameter
|
||||
if (parser.seenval('F')) {
|
||||
if (modeUsesDynFreq) {
|
||||
ftMotion.cfg.dynFreqK.x = parser.value_float();
|
||||
flag.report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Wrong mode for [", C('F'), "] frequency scaling.");
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " [", C('F'), "] frequency scaling.");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Parse zeta parameter (X axis).
|
||||
// Parse X zeta parameter
|
||||
if (parser.seenval('I')) {
|
||||
const float val = parser.value_float();
|
||||
if (AXIS_IS_SHAPING(X)) {
|
||||
if (WITHIN(val, 0.01f, 1.0f)) {
|
||||
ftMotion.cfg.zeta[0] = val;
|
||||
ftMotion.cfg.zeta.x = val;
|
||||
flag.update = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Invalid X zeta [", C('I'), "] value."); // Zeta out of range.
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_A_NAME), " zeta [", C('I'), "] value."); // Zeta out of range
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Wrong mode for zeta parameter.");
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " zeta parameter.");
|
||||
}
|
||||
|
||||
// Parse vtol parameter (X axis).
|
||||
// Parse X vtol parameter
|
||||
if (parser.seenval('Q')) {
|
||||
const float val = parser.value_float();
|
||||
if (AXIS_IS_EISHAPING(X)) {
|
||||
if (WITHIN(val, 0.00f, 1.0f)) {
|
||||
ftMotion.cfg.vtol[0] = val;
|
||||
ftMotion.cfg.vtol.x = val;
|
||||
flag.update = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Invalid X vtol [", C('Q'), "] value."); // VTol out of range.
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_A_NAME), " vtol [", C('Q'), "] value."); // VTol out of range.
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Wrong mode for vtol parameter.");
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " vtol parameter.");
|
||||
}
|
||||
|
||||
#endif // HAS_X_AXIS
|
||||
|
||||
#if HAS_Y_AXIS
|
||||
|
||||
// Parse frequency parameter (Y axis).
|
||||
// Parse Y frequency parameter
|
||||
if (parser.seenval('B')) {
|
||||
if (AXIS_IS_SHAPING(Y)) {
|
||||
const float val = parser.value_float();
|
||||
@@ -366,56 +419,178 @@ void GcodeSuite::M493() {
|
||||
flag.update = flag.report = true;
|
||||
}
|
||||
else // Frequency out of range.
|
||||
SERIAL_ECHOLNPGM("Invalid frequency [", C('B'), "] value.");
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_B_NAME), " frequency [", C('B'), "] value.");
|
||||
}
|
||||
else // Mode doesn't use frequency.
|
||||
SERIAL_ECHOLNPGM("Wrong mode for [", C('B'), "] frequency.");
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " [", C('B'), "] frequency.");
|
||||
}
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
// Parse frequency scaling parameter (Y axis).
|
||||
// Parse Y frequency scaling parameter
|
||||
if (parser.seenval('H')) {
|
||||
if (modeUsesDynFreq) {
|
||||
ftMotion.cfg.dynFreqK.y = parser.value_float();
|
||||
flag.report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Wrong mode for [", C('H'), "] frequency scaling.");
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " [", C('H'), "] frequency scaling.");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Parse zeta parameter (Y axis).
|
||||
// Parse Y zeta parameter
|
||||
if (parser.seenval('J')) {
|
||||
const float val = parser.value_float();
|
||||
if (AXIS_IS_SHAPING(Y)) {
|
||||
if (WITHIN(val, 0.01f, 1.0f)) {
|
||||
ftMotion.cfg.zeta[1] = val;
|
||||
ftMotion.cfg.zeta.y = val;
|
||||
flag.update = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Invalid Y zeta [", C('J'), "] value."); // Zeta Out of range
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_B_NAME), " zeta [", C('J'), "] value."); // Zeta out of range
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Wrong mode for zeta parameter.");
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " zeta parameter.");
|
||||
}
|
||||
|
||||
// Parse vtol parameter (Y axis).
|
||||
// Parse Y vtol parameter
|
||||
if (parser.seenval('R')) {
|
||||
const float val = parser.value_float();
|
||||
if (AXIS_IS_EISHAPING(Y)) {
|
||||
if (WITHIN(val, 0.00f, 1.0f)) {
|
||||
ftMotion.cfg.vtol[1] = val;
|
||||
ftMotion.cfg.vtol.y = val;
|
||||
flag.update = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Invalid Y vtol [", C('R'), "] value."); // VTol out of range.
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_B_NAME), " vtol [", C('R'), "] value."); // VTol out of range.
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("Wrong mode for vtol parameter.");
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " vtol parameter.");
|
||||
}
|
||||
|
||||
#endif // HAS_Y_AXIS
|
||||
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
|
||||
// Parse Z frequency parameter
|
||||
if (parser.seenval('C')) {
|
||||
if (AXIS_IS_SHAPING(Z)) {
|
||||
const float val = parser.value_float();
|
||||
if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) {
|
||||
ftMotion.cfg.baseFreq.z = val;
|
||||
flag.update = flag.report = true;
|
||||
}
|
||||
else // Frequency out of range.
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_C_NAME), " frequency [", C('C'), "] value.");
|
||||
}
|
||||
else // Mode doesn't use frequency.
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " [", C('C'), "] frequency.");
|
||||
}
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
// Parse Z frequency scaling parameter
|
||||
if (parser.seenval('L')) {
|
||||
if (modeUsesDynFreq) {
|
||||
ftMotion.cfg.dynFreqK.z = parser.value_float();
|
||||
flag.report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " [", C('L'), "] frequency scaling.");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Parse Z zeta parameter
|
||||
if (parser.seenval('O')) {
|
||||
const float val = parser.value_float();
|
||||
if (AXIS_IS_SHAPING(Z)) {
|
||||
if (WITHIN(val, 0.01f, 1.0f)) {
|
||||
ftMotion.cfg.zeta.z = val;
|
||||
flag.update = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_C_NAME), " zeta [", C('O'), "] value."); // Zeta out of range
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " zeta parameter.");
|
||||
}
|
||||
|
||||
// Parse Z vtol parameter
|
||||
if (parser.seenval('M')) {
|
||||
const float val = parser.value_float();
|
||||
if (AXIS_IS_EISHAPING(Z)) {
|
||||
if (WITHIN(val, 0.00f, 1.0f)) {
|
||||
ftMotion.cfg.vtol.z = val;
|
||||
flag.update = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_C_NAME), " vtol [", C('M'), "] value."); // VTol out of range.
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " vtol parameter.");
|
||||
}
|
||||
|
||||
#endif // FTM_SHAPER_Z
|
||||
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
|
||||
// Parse E frequency parameter
|
||||
if (parser.seenval('W')) {
|
||||
if (AXIS_IS_SHAPING(E)) {
|
||||
const float val = parser.value_float();
|
||||
if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) {
|
||||
ftMotion.cfg.baseFreq.e = val;
|
||||
flag.update = flag.report = true;
|
||||
}
|
||||
else // Frequency out of range.
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C('E'), " frequency [", C('W'), "] value.");
|
||||
}
|
||||
else // Mode doesn't use frequency.
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " [", C('W'), "] frequency.");
|
||||
}
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
// Parse E frequency scaling parameter
|
||||
if (parser.seenval('O')) {
|
||||
if (modeUsesDynFreq) {
|
||||
ftMotion.cfg.dynFreqK.e = parser.value_float();
|
||||
flag.report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " [", C('O'), "] frequency scaling.");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Parse E zeta parameter
|
||||
if (parser.seenval('U')) {
|
||||
const float val = parser.value_float();
|
||||
if (AXIS_IS_SHAPING(E)) {
|
||||
if (WITHIN(val, 0.01f, 1.0f)) {
|
||||
ftMotion.cfg.zeta.e = val;
|
||||
flag.update = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C('E'), " zeta [", C('U'), "] value."); // Zeta out of range
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " zeta parameter.");
|
||||
}
|
||||
|
||||
// Parse E vtol parameter
|
||||
if (parser.seenval('V')) {
|
||||
const float val = parser.value_float();
|
||||
if (AXIS_IS_EISHAPING(E)) {
|
||||
if (WITHIN(val, 0.00f, 1.0f)) {
|
||||
ftMotion.cfg.vtol.e = val;
|
||||
flag.update = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C('E'), " vtol [", C('V'), "] value."); // VTol out of range.
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " vtol parameter.");
|
||||
}
|
||||
|
||||
#endif // FTM_SHAPER_E
|
||||
|
||||
if (flag.update) ftMotion.update_shaping_params();
|
||||
|
||||
if (flag.report) say_shaping();
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "../../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
|
||||
#include "../../gcode.h"
|
||||
#include "../../../module/ft_motion.h"
|
||||
#include "../../../module/stepper.h"
|
||||
|
||||
void say_smoothing() {
|
||||
#if HAS_X_AXIS
|
||||
SERIAL_ECHOLN(F(" "), C('X'), F(" smoothing time: "), p_float_t(ftMotion.cfg.smoothingTime.x, 3), C('s'));
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
SERIAL_ECHOLN(F(" "), C('Y'), F(" smoothing time: "), p_float_t(ftMotion.cfg.smoothingTime.y, 3), C('s'));
|
||||
#endif
|
||||
#if HAS_Z_AXIS
|
||||
SERIAL_ECHOLN(F(" "), C('Z'), F(" smoothing time: "), p_float_t(ftMotion.cfg.smoothingTime.z, 3), C('s'));
|
||||
#endif
|
||||
#if HAS_EXTRUDERS
|
||||
SERIAL_ECHOLN(F(" "), C('E'), F(" smoothing time: "), p_float_t(ftMotion.cfg.smoothingTime.e, 3), C('s'));
|
||||
#endif
|
||||
}
|
||||
|
||||
void GcodeSuite::M494_report(const bool forReplay/*=true*/) {
|
||||
TERN_(MARLIN_SMALL_BUILD, return);
|
||||
|
||||
report_heading_etc(forReplay, F("FTM Smoothing"));
|
||||
const ft_config_t &c = ftMotion.cfg;
|
||||
SERIAL_ECHOLN(F(" M494")
|
||||
#if HAS_X_AXIS
|
||||
, F(" X"), c.smoothingTime.x
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
, F(" Y"), c.smoothingTime.y
|
||||
#endif
|
||||
#if HAS_Z_AXIS
|
||||
, F(" Z"), c.smoothingTime.z
|
||||
#endif
|
||||
#if HAS_EXTRUDERS
|
||||
, F(" E"), c.smoothingTime.e
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* M494: Set Fixed-time Motion Control Smoothing parameters
|
||||
*
|
||||
* Parameters:
|
||||
* X<time> Set smoothing time for the X axis
|
||||
* Y<time> Set smoothing time for the Y axis
|
||||
* Z<time> Set smoothing time for the Z axis
|
||||
* E<time> Set smoothing time for the E axis
|
||||
*/
|
||||
void GcodeSuite::M494() {
|
||||
bool report = !parser.seen_any();
|
||||
|
||||
#if HAS_X_AXIS
|
||||
// Parse X axis smoothing time parameter.
|
||||
if (parser.seenval('X')) {
|
||||
const float val = parser.value_float();
|
||||
if (WITHIN(val, 0.0f, FTM_MAX_SMOOTHING_TIME)) {
|
||||
ftMotion.set_smoothing_time(X_AXIS, val);
|
||||
report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_A_NAME), " smoothing time [", C('X'), "] value.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_Y_AXIS
|
||||
// Parse Y axis smoothing time parameter.
|
||||
if (parser.seenval('Y')) {
|
||||
const float val = parser.value_float();
|
||||
if (WITHIN(val, 0.0f, FTM_MAX_SMOOTHING_TIME)) {
|
||||
ftMotion.set_smoothing_time(Y_AXIS, val);
|
||||
report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_B_NAME), " smoothing time [", C('Y'), "] value.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_Z_AXIS
|
||||
// Parse Z axis smoothing time parameter.
|
||||
if (parser.seenval('Z')) {
|
||||
const float val = parser.value_float();
|
||||
if (WITHIN(val, 0.0f, FTM_MAX_SMOOTHING_TIME)) {
|
||||
ftMotion.set_smoothing_time(Z_AXIS, val);
|
||||
report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_C_NAME), " smoothing time [", C('Z'), "] value.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
// Parse E axis smoothing time parameter.
|
||||
if (parser.seenval('E')) {
|
||||
const float val = parser.value_float();
|
||||
if (WITHIN(val, 0.0f, FTM_MAX_SMOOTHING_TIME)) {
|
||||
ftMotion.set_smoothing_time(E_AXIS, val);
|
||||
report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C('E'), " smoothing time [", C('E'), "] value.");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (report) say_smoothing();
|
||||
}
|
||||
|
||||
#endif // FTM_SMOOTHING
|
||||
@@ -922,6 +922,9 @@ void GcodeSuite::process_parsed_command(bool no_ok/*=false*/) {
|
||||
|
||||
#if ENABLED(FT_MOTION)
|
||||
case 493: M493(); break; // M493: Fixed-Time Motion control
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
case 494: M494(); break; // M494: Fixed-Time Motion extras
|
||||
#endif
|
||||
#endif
|
||||
|
||||
case 500: M500(); break; // M500: Store settings in EEPROM
|
||||
|
||||
@@ -1105,6 +1105,10 @@ private:
|
||||
#if ENABLED(FT_MOTION)
|
||||
static void M493();
|
||||
static void M493_report(const bool forReplay=true);
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
static void M494();
|
||||
static void M494_report(const bool forReplay=true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void M500();
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
*/
|
||||
#if EXTRUDERS
|
||||
#define HAS_EXTRUDERS 1
|
||||
#define HAS_E_AXIS 1
|
||||
#if EXTRUDERS > 1
|
||||
#define HAS_MULTI_EXTRUDER 1
|
||||
#endif
|
||||
|
||||
@@ -1522,6 +1522,16 @@
|
||||
#if ENABLED(FT_MOTION)
|
||||
#if HAS_X_AXIS
|
||||
#define HAS_FTM_SHAPING 1
|
||||
#define FTM_SHAPER_X
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
#define FTM_SHAPER_Y
|
||||
#endif
|
||||
#if !HAS_Z_AXIS
|
||||
#undef FTM_SHAPER_Z
|
||||
#endif
|
||||
#if !HAS_EXTRUDERS
|
||||
#undef FTM_SHAPER_E
|
||||
#endif
|
||||
#if ENABLED(FTM_UNIFIED_BWS)
|
||||
#define FTM_WINDOW_SIZE FTM_BW_SIZE
|
||||
|
||||
@@ -3630,3 +3630,19 @@
|
||||
#if ALL(SPI_FLASH, HAS_MEDIA, MARLIN_DEV_MODE)
|
||||
#define SPI_FLASH_BACKUP 1
|
||||
#endif
|
||||
|
||||
// Fixed-Time Motion
|
||||
#if ENABLED(FT_MOTION)
|
||||
#define FTM_TS (1.0f / FTM_FS) // (s) Time step for trajectory generation. (Reciprocal of FTM_FS)
|
||||
#define FTM_STEPS_PER_UNIT_TIME (FTM_STEPPER_FS / FTM_FS) // Interpolated stepper commands per unit time
|
||||
#define FTM_MIN_TICKS ((STEPPER_TIMER_RATE) / (FTM_STEPPER_FS)) // Minimum stepper ticks between steps
|
||||
#define FTM_RATIO (FTM_FS / FTM_MIN_SHAPE_FREQ) // Factor for use in FTM_ZMAX. DON'T CHANGE.
|
||||
#define FTM_SMOOTH_MAX_I uint32_t(TERN0(FTM_SMOOTHING, CEIL(FTM_FS * FTM_MAX_SMOOTHING_TIME))) // Max delays for smoothing
|
||||
#define FTM_ZMAX (FTM_RATIO * 2 + FTM_SMOOTH_MAX_I) // Maximum delays for shaping functions (even numbers only!)
|
||||
// Calculate as:
|
||||
// ZV : FTM_RATIO / 2
|
||||
// ZVD, MZV : FTM_RATIO
|
||||
// 2HEI : FTM_RATIO * 3 / 2
|
||||
// 3HEI : FTM_RATIO * 2
|
||||
#define FTM_SMOOTHING_ORDER 5 // 3 to 5 is closest to gaussian
|
||||
#endif
|
||||
|
||||
@@ -4594,6 +4594,12 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
|
||||
#if HAS_DYNAMIC_FREQ_G
|
||||
static_assert(FTM_DEFAULT_DYNFREQ_MODE != dynFreqMode_MASS_BASED, "dynFreqMode_MASS_BASED requires an X axis and an extruder.");
|
||||
#endif
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
static_assert(FTM_SMOOTHING_TIME_X <= FTM_MAX_SMOOTHING_TIME, "FTM_SMOOTHING_TIME_X must be <= FTM_MAX_SMOOTHING_TIME.");
|
||||
static_assert(FTM_SMOOTHING_TIME_Y <= FTM_MAX_SMOOTHING_TIME, "FTM_SMOOTHING_TIME_Y must be <= FTM_MAX_SMOOTHING_TIME.");
|
||||
static_assert(FTM_SMOOTHING_TIME_Z <= FTM_MAX_SMOOTHING_TIME, "FTM_SMOOTHING_TIME_Z must be <= FTM_MAX_SMOOTHING_TIME.");
|
||||
static_assert(FTM_SMOOTHING_TIME_E <= FTM_MAX_SMOOTHING_TIME, "FTM_SMOOTHING_TIME_E must be <= FTM_MAX_SMOOTHING_TIME.");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Multi-Stepping Limit
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <U8glib-HAL.h>
|
||||
|
||||
#if defined(__AVR__) && ENABLED(NOT_EXTENDED_ISO10646_1_5X7)
|
||||
#if ALL(__AVR__, NOT_EXTENDED_ISO10646_1_5X7)
|
||||
// reduced font (only symbols 1 - 127) - saves about 1278 bytes of FLASH
|
||||
|
||||
/**
|
||||
|
||||
@@ -3593,7 +3593,7 @@ void drawTuneMenu() {
|
||||
}
|
||||
|
||||
#if ENABLED(ADAPTIVE_STEP_SMOOTHING_TOGGLE)
|
||||
void setAdaptiveStepSmoothing() {
|
||||
void toggleAdaptiveStepSmoothing() {
|
||||
toggleCheckboxLine(stepper.adaptive_step_smoothing_enabled);
|
||||
}
|
||||
#endif
|
||||
@@ -3706,7 +3706,7 @@ void drawMotionMenu() {
|
||||
MENU_ITEM(ICON_InputShaping, MSG_INPUT_SHAPING, onDrawSubMenu, drawInputShaping_menu);
|
||||
#endif
|
||||
#if ENABLED(ADAPTIVE_STEP_SMOOTHING_TOGGLE)
|
||||
EDIT_ITEM(ICON_UBLActive, MSG_STEP_SMOOTHING, onDrawChkbMenu, setAdaptiveStepSmoothing, &stepper.adaptive_step_smoothing_enabled);
|
||||
EDIT_ITEM(ICON_UBLActive, MSG_STEP_SMOOTHING, onDrawChkbMenu, toggleAdaptiveStepSmoothing, &stepper.adaptive_step_smoothing_enabled);
|
||||
#endif
|
||||
EDIT_ITEM(ICON_Speed, MSG_SPEED, onDrawSpeedItem, setSpeed, &feedrate_percentage);
|
||||
EDIT_ITEM(ICON_Flow, MSG_FLOW, onDrawPIntMenu, setFlow, &planner.flow_percentage[0]);
|
||||
|
||||
@@ -936,6 +936,7 @@ namespace LanguageNarrow_en {
|
||||
LSTR MSG_FTM_MZV = _UxGT("MZV");
|
||||
//LSTR MSG_FTM_ULENDO_FBS = _UxGT("Ulendo FBS");
|
||||
//LSTR MSG_FTM_DISCTF = _UxGT("DISCTF");
|
||||
LSTR MSG_FTM_AXIS_SYNC = _UxGT("Axis Sync");
|
||||
LSTR MSG_FTM_DYN_MODE = _UxGT("DF Mode: $");
|
||||
LSTR MSG_FTM_Z_BASED = _UxGT("Z-based");
|
||||
LSTR MSG_FTM_MASS_BASED = _UxGT("Mass-based");
|
||||
@@ -943,6 +944,7 @@ namespace LanguageNarrow_en {
|
||||
LSTR MSG_FTM_DFREQ_K_N = _UxGT("@ Dyn. Freq.");
|
||||
LSTR MSG_FTM_ZETA_N = _UxGT("@ Damping");
|
||||
LSTR MSG_FTM_VTOL_N = _UxGT("@ Vib. Level");
|
||||
LSTR MSG_FTM_SMOOTH_TIME_N = _UxGT("@ Smoothing Time");
|
||||
|
||||
LSTR MSG_LEVEL_X_AXIS = _UxGT("Level X Axis");
|
||||
LSTR MSG_AUTO_CALIBRATE = _UxGT("Auto Calibrate");
|
||||
|
||||
@@ -338,47 +338,33 @@ void menu_move() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void ftm_menu_set_shaper(const AxisEnum axis, const ftMotionShaper_t s) {
|
||||
ftMotion.cfg.shaper[axis] = s;
|
||||
void ftm_menu_set_shaper(ftMotionShaper_t &outShaper, const ftMotionShaper_t s) {
|
||||
outShaper = s;
|
||||
ftMotion.update_shaping_params();
|
||||
ui.go_back();
|
||||
}
|
||||
|
||||
inline void menu_ftm_shaper_x() {
|
||||
const ftMotionShaper_t shaper = ftMotion.cfg.shaper.x;
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION);
|
||||
#define MENU_FTM_SHAPER(A) \
|
||||
inline void menu_ftm_shaper_##A() { \
|
||||
const ftMotionShaper_t shaper = ftMotion.cfg.shaper.A; \
|
||||
START_MENU(); \
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION); \
|
||||
if (shaper != ftMotionShaper_NONE) ACTION_ITEM(MSG_LCD_OFF, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_NONE ); }); \
|
||||
if (shaper != ftMotionShaper_ZV) ACTION_ITEM(MSG_FTM_ZV, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_ZV ); }); \
|
||||
if (shaper != ftMotionShaper_ZVD) ACTION_ITEM(MSG_FTM_ZVD, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_ZVD ); }); \
|
||||
if (shaper != ftMotionShaper_ZVDD) ACTION_ITEM(MSG_FTM_ZVDD, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_ZVDD ); }); \
|
||||
if (shaper != ftMotionShaper_ZVDDD) ACTION_ITEM(MSG_FTM_ZVDDD,[]{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_ZVDDD ); }); \
|
||||
if (shaper != ftMotionShaper_EI) ACTION_ITEM(MSG_FTM_EI, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_EI ); }); \
|
||||
if (shaper != ftMotionShaper_2HEI) ACTION_ITEM(MSG_FTM_2HEI, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_2HEI ); }); \
|
||||
if (shaper != ftMotionShaper_3HEI) ACTION_ITEM(MSG_FTM_3HEI, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_3HEI ); }); \
|
||||
if (shaper != ftMotionShaper_MZV) ACTION_ITEM(MSG_FTM_MZV, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_MZV ); }); \
|
||||
END_MENU(); \
|
||||
}
|
||||
|
||||
if (shaper != ftMotionShaper_NONE) ACTION_ITEM(MSG_LCD_OFF, []{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_NONE); });
|
||||
if (shaper != ftMotionShaper_ZV) ACTION_ITEM(MSG_FTM_ZV, []{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_ZV); });
|
||||
if (shaper != ftMotionShaper_ZVD) ACTION_ITEM(MSG_FTM_ZVD, []{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_ZVD); });
|
||||
if (shaper != ftMotionShaper_ZVDD) ACTION_ITEM(MSG_FTM_ZVDD, []{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_ZVDD); });
|
||||
if (shaper != ftMotionShaper_ZVDDD) ACTION_ITEM(MSG_FTM_ZVDDD,[]{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_ZVDDD); });
|
||||
if (shaper != ftMotionShaper_EI) ACTION_ITEM(MSG_FTM_EI, []{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_EI); });
|
||||
if (shaper != ftMotionShaper_2HEI) ACTION_ITEM(MSG_FTM_2HEI, []{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_2HEI); });
|
||||
if (shaper != ftMotionShaper_3HEI) ACTION_ITEM(MSG_FTM_3HEI, []{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_3HEI); });
|
||||
if (shaper != ftMotionShaper_MZV) ACTION_ITEM(MSG_FTM_MZV, []{ ftm_menu_set_shaper(X_AXIS, ftMotionShaper_MZV); });
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
|
||||
inline void menu_ftm_shaper_y() {
|
||||
const ftMotionShaper_t shaper = ftMotion.cfg.shaper.y;
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION);
|
||||
|
||||
if (shaper != ftMotionShaper_NONE) ACTION_ITEM(MSG_LCD_OFF, []{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_NONE); });
|
||||
if (shaper != ftMotionShaper_ZV) ACTION_ITEM(MSG_FTM_ZV, []{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_ZV); });
|
||||
if (shaper != ftMotionShaper_ZVD) ACTION_ITEM(MSG_FTM_ZVD, []{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_ZVD); });
|
||||
if (shaper != ftMotionShaper_ZVDD) ACTION_ITEM(MSG_FTM_ZVDD, []{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_ZVDD); });
|
||||
if (shaper != ftMotionShaper_ZVDDD) ACTION_ITEM(MSG_FTM_ZVDDD,[]{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_ZVDDD); });
|
||||
if (shaper != ftMotionShaper_EI) ACTION_ITEM(MSG_FTM_EI, []{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_EI); });
|
||||
if (shaper != ftMotionShaper_2HEI) ACTION_ITEM(MSG_FTM_2HEI, []{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_2HEI); });
|
||||
if (shaper != ftMotionShaper_3HEI) ACTION_ITEM(MSG_FTM_3HEI, []{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_3HEI); });
|
||||
if (shaper != ftMotionShaper_MZV) ACTION_ITEM(MSG_FTM_MZV, []{ ftm_menu_set_shaper(Y_AXIS, ftMotionShaper_MZV); });
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
MENU_FTM_SHAPER(X);
|
||||
MENU_FTM_SHAPER(Y);
|
||||
TERN_(FTM_SHAPER_Z, MENU_FTM_SHAPER(Z));
|
||||
TERN_(FTM_SHAPER_E, MENU_FTM_SHAPER(E));
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
|
||||
@@ -454,26 +440,19 @@ void menu_move() {
|
||||
|
||||
// Show only when FT Motion is active (or optionally always show)
|
||||
if (c.active || ENABLED(FT_MOTION_NO_MENU_TOGGLE)) {
|
||||
#if HAS_X_AXIS
|
||||
SUBMENU_N_S(X_AXIS, _shaper_name(X_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_x);
|
||||
|
||||
if (AXIS_IS_SHAPING(X)) {
|
||||
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_BASE_FREQ_N, &c.baseFreq.x, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params);
|
||||
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_ZETA_N, &c.zeta.x, 0.0f, 1.0f, ftMotion.update_shaping_params);
|
||||
if (AXIS_IS_EISHAPING(X))
|
||||
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_VTOL_N, &c.vtol.x, 0.0f, 1.0f, ftMotion.update_shaping_params);
|
||||
#define SHAPER_MENU_ITEM(A) \
|
||||
SUBMENU_N_S(_AXIS(A), _shaper_name(_AXIS(A)), MSG_FTM_CMPN_MODE, menu_ftm_shaper_##A); \
|
||||
if (AXIS_IS_SHAPING(A)) { \
|
||||
EDIT_ITEM_FAST_N(float42_52, _AXIS(A), MSG_FTM_BASE_FREQ_N, &c.baseFreq.A, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params); \
|
||||
EDIT_ITEM_FAST_N(float42_52, _AXIS(A), MSG_FTM_ZETA_N, &c.zeta.A, 0.0f, 1.0f, ftMotion.update_shaping_params); \
|
||||
if (AXIS_IS_EISHAPING(A)) \
|
||||
EDIT_ITEM_FAST_N(float42_52, _AXIS(A), MSG_FTM_VTOL_N, &c.vtol.A, 0.0f, 1.0f, ftMotion.update_shaping_params); \
|
||||
}
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
SUBMENU_N_S(Y_AXIS, _shaper_name(Y_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_y);
|
||||
|
||||
if (AXIS_IS_SHAPING(Y)) {
|
||||
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_BASE_FREQ_N, &c.baseFreq.y, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params);
|
||||
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_ZETA_N, &c.zeta.y, 0.0f, 1.0f, ftMotion.update_shaping_params);
|
||||
if (AXIS_IS_EISHAPING(Y))
|
||||
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_VTOL_N, &c.vtol.y, 0.0f, 1.0f, ftMotion.update_shaping_params);
|
||||
}
|
||||
#endif
|
||||
TERN_(HAS_X_AXIS, SHAPER_MENU_ITEM(X));
|
||||
TERN_(HAS_Y_AXIS, SHAPER_MENU_ITEM(Y));
|
||||
TERN_(FTM_SHAPER_Z, SHAPER_MENU_ITEM(Z));
|
||||
TERN_(FTM_SHAPER_E, SHAPER_MENU_ITEM(E));
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
SUBMENU_S(_dmode(), MSG_FTM_DYN_MODE, menu_ftm_dyn_mode);
|
||||
@@ -484,6 +463,12 @@ void menu_move() {
|
||||
#if HAS_Y_AXIS
|
||||
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_DFREQ_K_N, &c.dynFreqK.y, 0.0f, 20.0f);
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
EDIT_ITEM_FAST_N(float42_52, Z_AXIS, MSG_FTM_DFREQ_K_N, &c.dynFreqK.z, 0.0f, 20.0f);
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
EDIT_ITEM_FAST_N(float42_52, E_AXIS, MSG_FTM_DFREQ_K_N, &c.dynFreqK.e, 0.0f, 20.0f);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -492,9 +477,30 @@ void menu_move() {
|
||||
if (c.linearAdvEna || ENABLED(FT_MOTION_NO_MENU_TOGGLE))
|
||||
EDIT_ITEM(float42_52, MSG_ADVANCE_K, &c.linearAdvK, 0.0f, 10.0f);
|
||||
#endif
|
||||
|
||||
EDIT_ITEM(bool, MSG_FTM_AXIS_SYNC, &c.axis_sync_enabled);
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
#if HAS_X_AXIS
|
||||
editable.decimal = c.smoothingTime.x;
|
||||
EDIT_ITEM_FAST_N(float43, X_AXIS, MSG_FTM_SMOOTH_TIME_N, &editable.decimal, 0.0f, FTM_MAX_SMOOTHING_TIME, []{ ftMotion.set_smoothing_time(X_AXIS, editable.decimal); });
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
editable.decimal = c.smoothingTime.y;
|
||||
EDIT_ITEM_FAST_N(float43, Y_AXIS, MSG_FTM_SMOOTH_TIME_N, &editable.decimal, 0.0f, FTM_MAX_SMOOTHING_TIME, []{ ftMotion.set_smoothing_time(Y_AXIS, editable.decimal); });
|
||||
#endif
|
||||
#if HAS_Z_AXIS
|
||||
editable.decimal = c.smoothingTime.z;
|
||||
EDIT_ITEM_FAST_N(float43, Z_AXIS, MSG_FTM_SMOOTH_TIME_N, &editable.decimal, 0.0f, FTM_MAX_SMOOTHING_TIME, []{ ftMotion.set_smoothing_time(Z_AXIS, editable.decimal); });
|
||||
#endif
|
||||
#if HAS_EXTRUDERS
|
||||
editable.decimal = c.smoothingTime.e;
|
||||
EDIT_ITEM_FAST_N(float43, E_AXIS, MSG_FTM_SMOOTH_TIME_N, &editable.decimal, 0.0f, FTM_MAX_SMOOTHING_TIME, []{ ftMotion.set_smoothing_time(E_AXIS, editable.decimal); });
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
END_MENU();
|
||||
}
|
||||
} // menu_ft_motion
|
||||
|
||||
void menu_tune_ft_motion() {
|
||||
// Define stuff ahead of the menu loop
|
||||
@@ -536,10 +542,16 @@ void menu_move() {
|
||||
BACK_ITEM(MSG_TUNE);
|
||||
|
||||
#if HAS_X_AXIS
|
||||
SUBMENU_N_S(X_AXIS, _shaper_name(X_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_x);
|
||||
SUBMENU_N_S(X_AXIS, _shaper_name(X_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_X);
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
SUBMENU_N_S(Y_AXIS, _shaper_name(Y_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_y);
|
||||
SUBMENU_N_S(Y_AXIS, _shaper_name(Y_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_Y);
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
SUBMENU_N_S(Z_AXIS, _shaper_name(Z_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_Z);
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
SUBMENU_N_S(E_AXIS, _shaper_name(E_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_E);
|
||||
#endif
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
SUBMENU_S(_dmode(), MSG_FTM_DYN_MODE, menu_ftm_dyn_mode);
|
||||
@@ -551,7 +563,7 @@ void menu_move() {
|
||||
#endif
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
} // menu_tune_ft_motion
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
@@ -612,6 +624,6 @@ void menu_motion() {
|
||||
GCODES_ITEM(MSG_DISABLE_STEPPERS, F("M84"));
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
} // menu_motion
|
||||
|
||||
#endif // HAS_MARLINUI_MENU
|
||||
|
||||
+150
-38
@@ -106,6 +106,29 @@ uint32_t FTMotion::interpIdx = 0; // Index of current data point b
|
||||
#if HAS_Y_AXIS
|
||||
, y:{ false, { 0.0f }, { 0.0f }, { 0 }, 0 }
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
, z:{ false, { 0.0f }, { 0.0f }, { 0 }, 0 }
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
, e:{ false, { 0.0f }, { 0.0f }, { 0 }, 0 }
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
FTMotion::smoothing_t FTMotion::smoothing = {
|
||||
#if HAS_X_AXIS
|
||||
x:{ { 0.0f }, 0.0f, 0 }, // smoothing_pass[], alpha, delay_samples
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
y:{ { 0.0f }, 0.0f, 0 },
|
||||
#endif
|
||||
#if HAS_Z_AXIS
|
||||
z:{ { 0.0f }, 0.0f, 0 },
|
||||
#endif
|
||||
#if HAS_EXTRUDERS
|
||||
e:{ { 0.0f }, 0.0f, 0 }
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -306,14 +329,16 @@ void FTMotion::loop() {
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ZERO(Ai);
|
||||
case ftMotionShaper_NONE:
|
||||
max_i = 0;
|
||||
Ai[0] = 1.0f; // No echoes so the whole impulse is applied in the first tap
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Refresh the indices used by shaping functions.
|
||||
// Ai[] must be precomputed (if zeta or vtol change, call set_axis_shaping_A first)
|
||||
void FTMotion::AxisShaping::set_axis_shaping_N(const ftMotionShaper_t shaper, const float f, const float zeta) {
|
||||
// Note that protections are omitted for DBZ and for index exceeding array length.
|
||||
const float df = sqrt ( 1.f - sq(zeta) );
|
||||
@@ -343,27 +368,73 @@ void FTMotion::loop() {
|
||||
Ni[1] = round((0.375f / f / df) * (FTM_FS));
|
||||
Ni[2] = Ni[1] + Ni[1];
|
||||
break;
|
||||
default: ZERO(Ni);
|
||||
case ftMotionShaper_NONE:
|
||||
// No echoes.
|
||||
// max_i is set to 0 by set_axis_shaping_A, so delay centroid (Ni[0]) will also correctly be 0
|
||||
break;
|
||||
}
|
||||
|
||||
// Group delay in samples (i.e., Axis delay caused by shaping): sum(Ai * Ni[i]).
|
||||
// Skipping i=0 since the uncompensated delay of the first impulse is always zero, so Ai[0] * Ni[0] == 0
|
||||
float centroid = 0.0f;
|
||||
for (uint8_t i = 1; i <= max_i; ++i) centroid -= Ai[i] * Ni[i];
|
||||
|
||||
Ni[0] = round(centroid);
|
||||
|
||||
// The resulting echo index can be negative, this is ok because it will be offset
|
||||
// by the max delay of all axes before it is used.
|
||||
for (uint8_t i = 1; i <= max_i; ++i) Ni[i] += Ni[0];
|
||||
}
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
// Set smoothing time and recalculate alpha and delay.
|
||||
void FTMotion::AxisSmoothing::set_smoothing_time(const float s_time) {
|
||||
if (s_time > 0.001f) {
|
||||
alpha = 1.0f - expf(-(FTM_TS) * (FTM_SMOOTHING_ORDER) / s_time );
|
||||
delay_samples = s_time * FTM_FS;
|
||||
}
|
||||
else {
|
||||
alpha = 0.0f;
|
||||
delay_samples = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void FTMotion::update_shaping_params() {
|
||||
#if HAS_X_AXIS
|
||||
if ((shaping.x.ena = AXIS_IS_SHAPING(X))) {
|
||||
shaping.x.set_axis_shaping_A(cfg.shaper.x, cfg.zeta.x, cfg.vtol.x);
|
||||
shaping.x.set_axis_shaping_N(cfg.shaper.x, cfg.baseFreq.x, cfg.zeta.x);
|
||||
}
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
if ((shaping.y.ena = AXIS_IS_SHAPING(Y))) {
|
||||
shaping.y.set_axis_shaping_A(cfg.shaper.y, cfg.zeta.y, cfg.vtol.y);
|
||||
shaping.y.set_axis_shaping_N(cfg.shaper.y, cfg.baseFreq.y, cfg.zeta.y);
|
||||
}
|
||||
#endif
|
||||
#define UPDATE_SHAPER(A) \
|
||||
shaping.A.ena = ftMotion.cfg.shaper.A != ftMotionShaper_NONE; \
|
||||
shaping.A.set_axis_shaping_A(cfg.shaper.A, cfg.zeta.A, cfg.vtol.A); \
|
||||
shaping.A.set_axis_shaping_N(cfg.shaper.A, cfg.baseFreq.A, cfg.zeta.A);
|
||||
|
||||
TERN_(HAS_X_AXIS, UPDATE_SHAPER(x));
|
||||
TERN_(HAS_Y_AXIS, UPDATE_SHAPER(y));
|
||||
TERN_(FTM_SHAPER_Z, UPDATE_SHAPER(z));
|
||||
TERN_(FTM_SHAPER_E, UPDATE_SHAPER(e));
|
||||
}
|
||||
|
||||
#endif // HAS_FTM_SHAPING
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
|
||||
void FTMotion::update_smoothing_params() {
|
||||
TERN_(HAS_X_AXIS, smoothing.x.set_smoothing_time(cfg.smoothingTime.x));
|
||||
TERN_(HAS_Y_AXIS, smoothing.y.set_smoothing_time(cfg.smoothingTime.y));
|
||||
TERN_(HAS_Z_AXIS, smoothing.z.set_smoothing_time(cfg.smoothingTime.z));
|
||||
TERN_(HAS_EXTRUDERS, smoothing.e.set_smoothing_time(cfg.smoothingTime.e));
|
||||
}
|
||||
|
||||
void FTMotion::set_smoothing_time(uint8_t axis, const float s_time) {
|
||||
switch (axis) {
|
||||
TERN_(HAS_X_AXIS, case X_AXIS: cfg.smoothingTime.x = s_time; break;)
|
||||
TERN_(HAS_Y_AXIS, case Y_AXIS: cfg.smoothingTime.y = s_time; break;)
|
||||
TERN_(HAS_Z_AXIS, case Z_AXIS: cfg.smoothingTime.z = s_time; break;)
|
||||
TERN_(HAS_EXTRUDERS, case E_AXIS: cfg.smoothingTime.e = s_time; break;)
|
||||
}
|
||||
update_smoothing_params();
|
||||
}
|
||||
|
||||
#endif // FTM_SMOOTHING
|
||||
|
||||
// Reset all trajectory processing variables.
|
||||
void FTMotion::reset() {
|
||||
|
||||
@@ -383,8 +454,10 @@ void FTMotion::reset() {
|
||||
interpIdx = 0;
|
||||
|
||||
#if HAS_FTM_SHAPING
|
||||
TERN_(HAS_X_AXIS, ZERO(shaping.x.d_zi));
|
||||
TERN_(HAS_Y_AXIS, ZERO(shaping.y.d_zi));
|
||||
TERN_(HAS_X_AXIS, ZERO(shaping.x.d_zi));
|
||||
TERN_(HAS_Y_AXIS, ZERO(shaping.y.d_zi));
|
||||
TERN_(FTM_SHAPER_Z, ZERO(shaping.z.d_zi));
|
||||
TERN_(FTM_SHAPER_E, ZERO(shaping.e.d_zi));
|
||||
shaping.zi_idx = 0;
|
||||
#endif
|
||||
|
||||
@@ -443,6 +516,7 @@ int32_t FTMotion::stepperCmdBuffItems() {
|
||||
// Initializes storage variables before startup.
|
||||
void FTMotion::init() {
|
||||
update_shaping_params();
|
||||
TERN_(FTM_SMOOTHING, update_smoothing_params());
|
||||
reset(); // Precautionary.
|
||||
}
|
||||
|
||||
@@ -627,30 +701,68 @@ void FTMotion::generateTrajectoryPointsFromBlock() {
|
||||
|
||||
default: break;
|
||||
}
|
||||
uint32_t max_total_delay = 0;
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
#define SMOOTHEN(A) /* Approximate gaussian smoothing via chained EMAs */ \
|
||||
if (smoothing.A.alpha > 0.0f) { \
|
||||
float smooth_val = traj.A[traj_idx_set]; \
|
||||
for (uint8_t _i = 0; _i < FTM_SMOOTHING_ORDER; ++_i) { \
|
||||
smoothing.A.smoothing_pass[_i] += (smooth_val - smoothing.A.smoothing_pass[_i]) * smoothing.A.alpha; \
|
||||
smooth_val = smoothing.A.smoothing_pass[_i]; \
|
||||
} \
|
||||
traj.A[traj_idx_set] = smooth_val; \
|
||||
}
|
||||
|
||||
TERN_(HAS_X_AXIS, SMOOTHEN(x));
|
||||
TERN_(HAS_Y_AXIS, SMOOTHEN(y));
|
||||
TERN_(HAS_Z_AXIS, SMOOTHEN(z));
|
||||
TERN_(HAS_EXTRUDERS, SMOOTHEN(e));
|
||||
|
||||
max_total_delay += _MAX(
|
||||
TERN0(HAS_X_AXIS, smoothing.x.delay_samples),
|
||||
TERN0(HAS_Y_AXIS, smoothing.y.delay_samples),
|
||||
TERN0(HAS_Z_AXIS, smoothing.z.delay_samples),
|
||||
TERN0(HAS_EXTRUDERS, smoothing.e.delay_samples)
|
||||
);
|
||||
|
||||
#endif // FTM_SMOOTHING
|
||||
|
||||
// Apply shaping if active on each axis
|
||||
#if HAS_FTM_SHAPING
|
||||
#if HAS_X_AXIS
|
||||
if (shaping.x.ena) {
|
||||
shaping.x.d_zi[shaping.zi_idx] = traj.x[traj_idx_set];
|
||||
traj.x[traj_idx_set] *= shaping.x.Ai[0];
|
||||
for (uint32_t i = 1U; i <= shaping.x.max_i; i++) {
|
||||
const uint32_t udiffx = shaping.zi_idx - shaping.x.Ni[i];
|
||||
traj.x[traj_idx_set] += shaping.x.Ai[i] * shaping.x.d_zi[shaping.x.Ni[i] > shaping.zi_idx ? (FTM_ZMAX) + udiffx : udiffx];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_Y_AXIS
|
||||
if (shaping.y.ena) {
|
||||
shaping.y.d_zi[shaping.zi_idx] = traj.y[traj_idx_set];
|
||||
traj.y[traj_idx_set] *= shaping.y.Ai[0];
|
||||
for (uint32_t i = 1U; i <= shaping.y.max_i; i++) {
|
||||
const uint32_t udiffy = shaping.zi_idx - shaping.y.Ni[i];
|
||||
traj.y[traj_idx_set] += shaping.y.Ai[i] * shaping.y.d_zi[shaping.y.Ni[i] > shaping.zi_idx ? (FTM_ZMAX) + udiffy : udiffy];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ftMotion.cfg.axis_sync_enabled) {
|
||||
max_total_delay -= _MIN(
|
||||
TERN0(HAS_X_AXIS, shaping.x.Ni[0]),
|
||||
TERN0(HAS_Y_AXIS, shaping.y.Ni[0]),
|
||||
TERN0(FTM_SHAPER_Z, shaping.z.Ni[0]),
|
||||
TERN0(FTM_SHAPER_E, shaping.e.Ni[0])
|
||||
);
|
||||
}
|
||||
|
||||
// Apply shaping if active on each axis
|
||||
#define SHAPE(A) \
|
||||
do { \
|
||||
const uint32_t group_delay = ftMotion.cfg.axis_sync_enabled \
|
||||
? max_total_delay - TERN0(FTM_SMOOTHING, smoothing.A.delay_samples) \
|
||||
: -shaping.A.Ni[0]; \
|
||||
/* α=1−exp(−(dt / (τ / order))) */ \
|
||||
shaping.A.d_zi[shaping.zi_idx] = traj.A[traj_idx_set]; \
|
||||
traj.A[traj_idx_set] = 0; \
|
||||
for (uint32_t i = 0; i <= shaping.A.max_i; i++) { \
|
||||
/* echo_delay is always positive since Ni[i] = echo_relative_delay - group_delay + max_total_delay */ \
|
||||
/* where echo_relative_delay > 0 and group_delay ≤ max_total_delay */ \
|
||||
const uint32_t echo_delay = group_delay + shaping.A.Ni[i]; \
|
||||
int32_t udiff = shaping.zi_idx - echo_delay; \
|
||||
if (udiff < 0) udiff += FTM_ZMAX; \
|
||||
traj.A[traj_idx_set] += shaping.A.Ai[i] * shaping.A.d_zi[udiff]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
TERN_(HAS_X_AXIS, SHAPE(x));
|
||||
TERN_(HAS_Y_AXIS, SHAPE(y));
|
||||
TERN_(FTM_SHAPER_Z, SHAPE(z));
|
||||
TERN_(FTM_SHAPER_E, SHAPE(e));
|
||||
|
||||
if (++shaping.zi_idx == (FTM_ZMAX)) shaping.zi_idx = 0;
|
||||
|
||||
#endif // HAS_FTM_SHAPING
|
||||
|
||||
@@ -39,16 +39,21 @@
|
||||
|
||||
typedef struct FTConfig {
|
||||
bool active = ENABLED(FTM_IS_DEFAULT_MOTION); // Active (else standard motion)
|
||||
bool axis_sync_enabled = true; // Axis synchronization enabled
|
||||
|
||||
#if HAS_FTM_SHAPING
|
||||
ft_shaped_shaper_t shaper = // Shaper type
|
||||
{ SHAPED_ELEM(FTM_DEFAULT_SHAPER_X, FTM_DEFAULT_SHAPER_Y) };
|
||||
{ SHAPED_ELEM(FTM_DEFAULT_SHAPER_X, FTM_DEFAULT_SHAPER_Y, FTM_DEFAULT_SHAPER_Z, FTM_DEFAULT_SHAPER_E) };
|
||||
ft_shaped_float_t baseFreq = // Base frequency. [Hz]
|
||||
{ SHAPED_ELEM(FTM_SHAPING_DEFAULT_FREQ_X, FTM_SHAPING_DEFAULT_FREQ_Y) };
|
||||
{ SHAPED_ELEM(FTM_SHAPING_DEFAULT_FREQ_X, FTM_SHAPING_DEFAULT_FREQ_Y, FTM_SHAPING_DEFAULT_FREQ_Z, FTM_SHAPING_DEFAULT_FREQ_E) };
|
||||
ft_shaped_float_t zeta = // Damping factor
|
||||
{ SHAPED_ELEM(FTM_SHAPING_ZETA_X, FTM_SHAPING_ZETA_Y) };
|
||||
{ SHAPED_ELEM(FTM_SHAPING_ZETA_X, FTM_SHAPING_ZETA_Y, FTM_SHAPING_ZETA_Z, FTM_SHAPING_ZETA_E) };
|
||||
ft_shaped_float_t vtol = // Vibration Level
|
||||
{ SHAPED_ELEM(FTM_SHAPING_V_TOL_X, FTM_SHAPING_V_TOL_Y) };
|
||||
{ SHAPED_ELEM(FTM_SHAPING_V_TOL_X, FTM_SHAPING_V_TOL_Y, FTM_SHAPING_V_TOL_Z, FTM_SHAPING_V_TOL_E) };
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
ft_smoothed_float_t smoothingTime; // Smoothing time. [s]
|
||||
#endif
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
dynFreqMode_t dynFreqMode = FTM_DEFAULT_DYNFREQ_MODE; // Dynamic frequency mode configuration.
|
||||
@@ -79,30 +84,37 @@ class FTMotion {
|
||||
|
||||
#if HAS_FTM_SHAPING
|
||||
|
||||
#if HAS_X_AXIS
|
||||
cfg.shaper.x = FTM_DEFAULT_SHAPER_X;
|
||||
cfg.baseFreq.x = FTM_SHAPING_DEFAULT_FREQ_X;
|
||||
cfg.zeta.x = FTM_SHAPING_ZETA_X;
|
||||
cfg.vtol.x = FTM_SHAPING_V_TOL_X;
|
||||
#endif
|
||||
#define SET_CFG_DEFAULTS(A) \
|
||||
cfg.shaper.A = FTM_DEFAULT_SHAPER_##A; \
|
||||
cfg.baseFreq.A = FTM_SHAPING_DEFAULT_FREQ_##A; \
|
||||
cfg.zeta.A = FTM_SHAPING_ZETA_##A; \
|
||||
cfg.vtol.A = FTM_SHAPING_V_TOL_##A;
|
||||
|
||||
#if HAS_Y_AXIS
|
||||
cfg.shaper.y = FTM_DEFAULT_SHAPER_Y;
|
||||
cfg.baseFreq.y = FTM_SHAPING_DEFAULT_FREQ_Y;
|
||||
cfg.zeta.y = FTM_SHAPING_ZETA_Y;
|
||||
cfg.vtol.y = FTM_SHAPING_V_TOL_Y;
|
||||
#endif
|
||||
TERN_(HAS_X_AXIS, SET_CFG_DEFAULTS(X));
|
||||
TERN_(HAS_Y_AXIS, SET_CFG_DEFAULTS(Y));
|
||||
TERN_(FTM_SHAPER_Z, SET_CFG_DEFAULTS(Z));
|
||||
TERN_(FTM_SHAPER_E, SET_CFG_DEFAULTS(E));
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
cfg.dynFreqMode = FTM_DEFAULT_DYNFREQ_MODE;
|
||||
TERN_(HAS_X_AXIS, cfg.dynFreqK.x = 0.0f);
|
||||
TERN_(HAS_Y_AXIS, cfg.dynFreqK.y = 0.0f);
|
||||
//ZERO(cfg.dynFreqK);
|
||||
TERN_(HAS_X_AXIS, cfg.dynFreqK.x = 0.0f);
|
||||
TERN_(HAS_Y_AXIS, cfg.dynFreqK.y = 0.0f);
|
||||
TERN_(FTM_SHAPER_Z, cfg.dynFreqK.z = 0.0f);
|
||||
TERN_(FTM_SHAPER_E, cfg.dynFreqK.e = 0.0f);
|
||||
#endif
|
||||
|
||||
update_shaping_params();
|
||||
|
||||
#endif // HAS_FTM_SHAPING
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
TERN_(HAS_X_AXIS, set_smoothing_time(X_AXIS, FTM_SMOOTHING_TIME_X));
|
||||
TERN_(HAS_Y_AXIS, set_smoothing_time(Y_AXIS, FTM_SMOOTHING_TIME_Y));
|
||||
TERN_(HAS_Z_AXIS, set_smoothing_time(Z_AXIS, FTM_SMOOTHING_TIME_Z));
|
||||
TERN_(HAS_EXTRUDERS, set_smoothing_time(E_AXIS, FTM_SMOOTHING_TIME_E));
|
||||
#endif
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
cfg.linearAdvEna = FTM_LINEAR_ADV_DEFAULT_ENA;
|
||||
cfg.linearAdvK = FTM_LINEAR_ADV_DEFAULT_K;
|
||||
@@ -129,6 +141,13 @@ class FTMotion {
|
||||
static void update_shaping_params(void);
|
||||
#endif
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
// Refresh alpha and delay samples used by smoothing functions.
|
||||
static void update_smoothing_params();
|
||||
// Setters for smoothingTime that update alpha and delay
|
||||
static void set_smoothing_time(uint8_t axis, const float s_time);
|
||||
#endif
|
||||
|
||||
static void reset(); // Reset all states of the fixed time conversion to defaults.
|
||||
|
||||
FORCE_INLINE static bool axis_is_moving(const AxisEnum axis) {
|
||||
@@ -179,15 +198,14 @@ class FTMotion {
|
||||
static bool use_advance_lead;
|
||||
#endif
|
||||
|
||||
// Shaping variables.
|
||||
#if HAS_FTM_SHAPING
|
||||
|
||||
// Shaping data
|
||||
typedef struct AxisShaping {
|
||||
bool ena = false; // Enabled indication.
|
||||
float d_zi[FTM_ZMAX] = { 0.0f }; // Data point delay vector.
|
||||
float Ai[5]; // Shaping gain vector.
|
||||
uint32_t Ni[5]; // Shaping time index vector.
|
||||
uint32_t max_i; // Vector length for the selected shaper.
|
||||
bool ena = false; // Enabled indication
|
||||
float d_zi[FTM_ZMAX] = { 0.0f }; // Data point delay vector
|
||||
float Ai[5]; // Shaping gain vector
|
||||
int32_t Ni[5]; // Shaping time index vector
|
||||
uint32_t max_i; // Vector length for the selected shaper
|
||||
|
||||
void set_axis_shaping_N(const ftMotionShaper_t shaper, const float f, const float zeta); // Sets the gains used by shaping functions.
|
||||
void set_axis_shaping_A(const ftMotionShaper_t shaper, const float zeta, const float vtol); // Sets the indices used by shaping functions.
|
||||
@@ -196,18 +214,35 @@ class FTMotion {
|
||||
|
||||
typedef struct Shaping {
|
||||
uint32_t zi_idx; // Index of storage in the data point delay vectors.
|
||||
#if HAS_X_AXIS
|
||||
axis_shaping_t x;
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
axis_shaping_t y;
|
||||
#endif
|
||||
OPTCODE(HAS_X_AXIS, axis_shaping_t x)
|
||||
OPTCODE(HAS_Y_AXIS, axis_shaping_t y)
|
||||
OPTCODE(FTM_SHAPER_Z, axis_shaping_t z)
|
||||
OPTCODE(FTM_SHAPER_E, axis_shaping_t e)
|
||||
} shaping_t;
|
||||
|
||||
static shaping_t shaping; // Shaping data
|
||||
|
||||
#endif // HAS_FTM_SHAPING
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
// Smoothing data for each axis
|
||||
typedef struct AxisSmoothing {
|
||||
float smoothing_pass[FTM_SMOOTHING_ORDER] = { 0.0f }; // Last value of each of the exponential smoothing passes
|
||||
float alpha = 0.0f; // Pre-calculated alpha for smoothing.
|
||||
uint32_t delay_samples = 0; // Pre-calculated delay in samples for smoothing.
|
||||
void set_smoothing_time(const float s_time); // Set smoothing time, recalculate alpha and delay.
|
||||
} axis_smoothing_t;
|
||||
|
||||
// Smoothing data for XYZE axes
|
||||
typedef struct Smoothing {
|
||||
OPTCODE(HAS_X_AXIS, axis_smoothing_t x)
|
||||
OPTCODE(HAS_Y_AXIS, axis_smoothing_t y)
|
||||
OPTCODE(HAS_Z_AXIS, axis_smoothing_t z)
|
||||
OPTCODE(HAS_EXTRUDERS, axis_smoothing_t e)
|
||||
} smoothing_t;
|
||||
static smoothing_t smoothing; // Smoothing data
|
||||
#endif
|
||||
|
||||
// Linear advance variables.
|
||||
#if HAS_EXTRUDERS
|
||||
static float prev_traj_e;
|
||||
@@ -221,7 +256,14 @@ class FTMotion {
|
||||
static void generateTrajectoryPointsFromBlock();
|
||||
static void generateStepsFromTrajectory(const uint32_t idx);
|
||||
|
||||
FORCE_INLINE static int32_t num_samples_shaper_settle() { return ( shaping.x.ena || shaping.y.ena ) ? FTM_ZMAX : 0; }
|
||||
FORCE_INLINE static int32_t num_samples_shaper_settle() {
|
||||
return (
|
||||
TERN0(HAS_X_AXIS, shaping.x.ena)
|
||||
|| TERN0(HAS_Y_AXIS, shaping.y.ena)
|
||||
|| TERN0(FTM_SHAPER_Z, shaping.z.ena)
|
||||
|| TERN0(FTM_SHAPER_E, shaping.e.ena)
|
||||
) ? FTM_ZMAX : 0;
|
||||
}
|
||||
|
||||
}; // class FTMotion
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ enum dynFreqMode_t : uint8_t {
|
||||
dynFreqMode_MASS_BASED = 2
|
||||
};
|
||||
|
||||
#define AXIS_IS_SHAPING(A) (ftMotion.cfg.shaper[_AXIS(A)] != ftMotionShaper_NONE)
|
||||
#define AXIS_IS_EISHAPING(A) WITHIN(ftMotion.cfg.shaper[_AXIS(A)], ftMotionShaper_EI, ftMotionShaper_3HEI)
|
||||
#define AXIS_IS_SHAPING(A) TERN0(FTM_SHAPER_##A, (ftMotion.cfg.shaper.A != ftMotionShaper_NONE))
|
||||
#define AXIS_IS_EISHAPING(A) TERN0(FTM_SHAPER_##A, WITHIN(ftMotion.cfg.shaper.A, ftMotionShaper_EI, ftMotionShaper_3HEI))
|
||||
|
||||
typedef struct XYZEarray<float, FTM_WINDOW_SIZE> xyze_trajectory_t;
|
||||
typedef struct XYZEarray<float, FTM_BATCH_SIZE> xyze_trajectoryMod_t;
|
||||
@@ -58,26 +58,67 @@ enum {
|
||||
FT_BIT_COUNT
|
||||
};
|
||||
|
||||
typedef bits_t(FT_BIT_COUNT) ft_command_t;
|
||||
|
||||
#if HAS_FTM_SHAPING
|
||||
#define NUM_AXES_SHAPED TERN(HAS_Y_AXIS, 2, 1)
|
||||
#define SHAPED_ELEM(A, B) A OPTARG(HAS_Y_AXIS, B)
|
||||
#define NUM_AXES_SHAPED COUNT_ENABLED(HAS_X_AXIS, HAS_Y_AXIS, FTM_SHAPER_Z, FTM_SHAPER_E)
|
||||
#define SHAPED_ELEM(A,B,C,D) A OPTARG(HAS_Y_AXIS, B) OPTARG(FTM_SHAPER_Z, C) OPTARG(FTM_SHAPER_E, D)
|
||||
#else
|
||||
#define NUM_AXES_SHAPED 0
|
||||
#define SHAPED_ELEM(A, B)
|
||||
#define SHAPED_ELEM(A,B,C,D)
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
struct FTShapedAxes {
|
||||
union {
|
||||
struct { T SHAPED_ELEM(X, Y); };
|
||||
struct { T SHAPED_ELEM(x, y); };
|
||||
struct { T SHAPED_ELEM(X, Y, Z, E); };
|
||||
struct { T SHAPED_ELEM(x, y, z, e); };
|
||||
T val[NUM_AXES_SHAPED];
|
||||
};
|
||||
T& operator[](int i) { return val[i]; }
|
||||
T& operator[](const int axis) {
|
||||
return val[axis_to_index(axis)];
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int axis_to_index(int axis) {
|
||||
int idx = 0;
|
||||
#if HAS_X_AXIS
|
||||
if (axis == X_AXIS) return idx;
|
||||
idx++;
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
if (axis == Y_AXIS) return idx;
|
||||
idx++;
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_Z)
|
||||
if (axis == Z_AXIS) return idx;
|
||||
idx++;
|
||||
#endif
|
||||
#if ENABLED(FTM_SHAPER_E)
|
||||
if (axis == E_AXIS) return idx;
|
||||
idx++;
|
||||
#endif
|
||||
return -1; // Invalid axis
|
||||
}
|
||||
};
|
||||
|
||||
typedef FTShapedAxes<float> ft_shaped_float_t;
|
||||
typedef FTShapedAxes<ftMotionShaper_t> ft_shaped_shaper_t;
|
||||
typedef FTShapedAxes<dynFreqMode_t> ft_shaped_dfm_t;
|
||||
|
||||
typedef bits_t(FT_BIT_COUNT) ft_command_t;
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
typedef struct FTSmoothedAxes {
|
||||
#if HAS_X_AXIS
|
||||
float x;
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
float y;
|
||||
#endif
|
||||
#if HAS_Z_AXIS
|
||||
float z;
|
||||
#endif
|
||||
#if HAS_EXTRUDERS
|
||||
float e;
|
||||
#endif
|
||||
} ft_smoothed_float_t;
|
||||
#endif
|
||||
|
||||
@@ -71,7 +71,7 @@ opt_set MOTHERBOARD BOARD_RAMBO EXTRUDERS 0 TEMP_SENSOR_BED 1 TEMP_SENSOR_PROBE
|
||||
DEFAULT_MAX_ACCELERATION '{ 3000, 3000, 100 }' \
|
||||
MANUAL_FEEDRATE '{ 50*60, 50*60, 4*60 }' \
|
||||
AXIS_RELATIVE_MODES '{ false, false, false }'
|
||||
opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER FIX_MOUNTED_PROBE Z_SAFE_HOMING FT_MOTION
|
||||
opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER FIX_MOUNTED_PROBE Z_SAFE_HOMING FT_MOTION FTM_SMOOTHING
|
||||
exec_test $1 $2 "Rambo with ZERO EXTRUDERS, heated bed, FT_MOTION" "$3"
|
||||
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user