From e63e7ef4c7ecc7a3e84acd89e7439f0cb89f04f3 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Fri, 26 Dec 2025 11:06:07 +0100 Subject: [PATCH 1/4] FTM, fix TMC2208 bug on E axis --- Marlin/src/module/stepper.cpp | 97 +++++++++++++++++++++++++++-------- Marlin/src/module/stepper.h | 6 +++ 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 2a10c83800..ebbeaf9132 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -671,6 +671,7 @@ void Stepper::disable_all_steppers() { #if ENABLED(FT_MOTION) // We'll compare the updated DIR bits to the last set state static AxisBits last_set_direction; + xyze_int_t Stepper::pending_hysteresis_steps ={0}; // Accumulated steps offset due to hysteresis delays #endif // Set a single axis direction based on the last set flags. @@ -1809,6 +1810,48 @@ void Stepper::isr() { #define ISR_MULTI_STEPS 1 #endif +/** + * ======================================== + * Hysteresis Macros for TMC Driver Safety + * ======================================== + * When input shaping is active, direction changes can occur rapidly. + * This hysteresis prevents TMC2208/TMC2225/TMC5160 shutdown bugs (#16076) + * by increasing the step detection threshold, giving the driver time to + * stabilize phase currents. + */ + +// Define hysteresis values based on driver type (64 units = ~0.5 step) +#if AXIS_DRIVER_TYPE_X(TMC2208) || AXIS_DRIVER_TYPE_X(TMC2208_STANDALONE) || \ + AXIS_DRIVER_TYPE_X(TMC5160) || AXIS_DRIVER_TYPE_X(TMC5160_STANDALONE) + #define HYSTERESIS_X 64 +#else + #define HYSTERESIS_X 0 +#endif + +#if AXIS_DRIVER_TYPE_Y(TMC2208) || AXIS_DRIVER_TYPE_Y(TMC2208_STANDALONE) || \ + AXIS_DRIVER_TYPE_Y(TMC5160) || AXIS_DRIVER_TYPE_Y(TMC5160_STANDALONE) + #define HYSTERESIS_Y 64 +#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 + +#if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || \ + HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE) + #define HYSTERESIS_E 64 +#else + #define HYSTERESIS_E 0 +#endif + +#define _HYSTERESIS(AXIS) HYSTERESIS_##AXIS +#define HYSTERESIS(AXIS) _HYSTERESIS(AXIS) + #if HAS_STANDARD_MOTION /** * This phase of the ISR should ONLY create the pulses for the steppers. @@ -1883,27 +1926,7 @@ void Stepper::isr() { // the TMC2208 / TMC2225 shutdown bug (#16076), add a half step hysteresis // in each direction. This results in the position being off by half an // average half step during travel but correct at the end of each segment. - #if AXIS_DRIVER_TYPE_X(TMC2208) || AXIS_DRIVER_TYPE_X(TMC2208_STANDALONE) || \ - AXIS_DRIVER_TYPE_X(TMC5160) || AXIS_DRIVER_TYPE_X(TMC5160_STANDALONE) - #define HYSTERESIS_X 64 - #else - #define HYSTERESIS_X 0 - #endif - #if AXIS_DRIVER_TYPE_Y(TMC2208) || AXIS_DRIVER_TYPE_Y(TMC2208_STANDALONE) || \ - AXIS_DRIVER_TYPE_Y(TMC5160) || AXIS_DRIVER_TYPE_Y(TMC5160_STANDALONE) - #define HYSTERESIS_Y 64 - #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) - + #define PULSE_PREP_SHAPING(AXIS, DELTA_ERROR, DIVIDEND) do{ \ int16_t de = DELTA_ERROR + (DIVIDEND); \ const bool step_fwd = de >= (64 + HYSTERESIS(AXIS)), \ @@ -3603,6 +3626,38 @@ void Stepper::report_positions() { AxisBits &step_bits = ftMotion.stepping.step_bits; // Aliases for prettier code AxisBits &dir_bits = ftMotion.stepping.dir_bits; + /** + * For axes with TMC drivers vulnerable to shutdown (TMC2208/2225/5160), + * apply hysteresis to direction changes, just like PULSE_PREP_SHAPING + * in the standard motion system. + */ + + #if (HYSTERESIS_E > 0) + /** + * When a direction change is detected on E axis: + * 1. Update last_direction_bits + * 2. Accumulate step counter for the new direction + * 3. Apply WAIT_BEFORE only when accumulated steps >= HYSTERESIS threshold + * 4. Single-direction blocks pass through without suppression + */ + + if (step_bits.E && (last_direction_bits.E != dir_bits.E)) { + // Direction change detected - UPDATE immediately to avoid re-accumulation + last_direction_bits.E = dir_bits.E; + + if (++pending_hysteresis_steps.E >= HYSTERESIS_E) { + // Threshold reached - apply direction change with TMC protection + { USING_TIMED_PULSE(); START_TIMED_PULSE(); AWAIT_LOW_PULSE(); } + DIR_WAIT_BEFORE(); + pending_hysteresis_steps.E = 0; + } else { + step_bits.E = 0; + } + } else { + pending_hysteresis_steps.E = 0; + } + #endif + USING_TIMED_PULSE(); /** diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 6e6761f842..66d4e11080 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -406,6 +406,12 @@ class Stepper { static AxisBits axis_did_move; // Last Movement in the given direction is not null, as computed when the last movement was fetched from planner #endif + // FT_MOTION Hysteresis tracking for direction changes + #if ENABLED(FT_MOTION) && (HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || \ + HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE)) + static xyze_int_t pending_hysteresis_steps; // Accumulated steps offset due to hysteresis delays + #endif + static bool abort_current_block; // Signals to the stepper that current block should be aborted // Motor locking for independent movement of multi-stepper axes From e9444239eefd19a871fd0bdf6f25df976bf61b96 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Fri, 26 Dec 2025 11:53:11 +0100 Subject: [PATCH 2/4] Fix missing preprocessor conditional --- Marlin/src/module/stepper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index ebbeaf9132..ac33fe1287 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -671,7 +671,9 @@ void Stepper::disable_all_steppers() { #if ENABLED(FT_MOTION) // We'll compare the updated DIR bits to the last set state static AxisBits last_set_direction; - xyze_int_t Stepper::pending_hysteresis_steps ={0}; // Accumulated steps offset due to hysteresis delays + #if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE) + xyze_int_t Stepper::pending_hysteresis_steps ={0}; // Accumulated steps offset due to hysteresis delays + #endif #endif // Set a single axis direction based on the last set flags. From a4c3e81530915c9964eb254f18455f0f5cdf5065 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Fri, 26 Dec 2025 14:01:26 +0100 Subject: [PATCH 3/4] Refine --- Marlin/src/inc/Conditionals-4-adv.h | 3 +++ Marlin/src/module/stepper.cpp | 7 +++---- Marlin/src/module/stepper.h | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h index 75c4efa743..e61549b396 100644 --- a/Marlin/src/inc/Conditionals-4-adv.h +++ b/Marlin/src/inc/Conditionals-4-adv.h @@ -359,6 +359,9 @@ #if ANY(FTM_SHAPER_EI, FTM_SHAPER_2HEI, FTM_SHAPER_3HEI) #define HAS_FTM_EI_SHAPING 1 #endif + #if (HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE)) + #define FTM_E_DRIVER_HYSTERESIS 1 + #endif #endif // Standard Motion diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index ac33fe1287..19dcb0bd80 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -671,7 +671,7 @@ void Stepper::disable_all_steppers() { #if ENABLED(FT_MOTION) // We'll compare the updated DIR bits to the last set state static AxisBits last_set_direction; - #if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE) + #if FTM_E_DRIVER_HYSTERESIS xyze_int_t Stepper::pending_hysteresis_steps ={0}; // Accumulated steps offset due to hysteresis delays #endif #endif @@ -1844,8 +1844,7 @@ void Stepper::isr() { #define HYSTERESIS_Z 0 #endif -#if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || \ - HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE) +#if FTM_E_DRIVER_HYSTERESIS #define HYSTERESIS_E 64 #else #define HYSTERESIS_E 0 @@ -3634,7 +3633,7 @@ void Stepper::report_positions() { * in the standard motion system. */ - #if (HYSTERESIS_E > 0) + #if FTM_E_DRIVER_HYSTERESIS /** * When a direction change is detected on E axis: * 1. Update last_direction_bits diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 66d4e11080..3333029d4b 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -407,8 +407,7 @@ class Stepper { #endif // FT_MOTION Hysteresis tracking for direction changes - #if ENABLED(FT_MOTION) && (HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || \ - HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE)) + #if FTM_E_DRIVER_HYSTERESIS static xyze_int_t pending_hysteresis_steps; // Accumulated steps offset due to hysteresis delays #endif From bc05d629121003d9caddb8a88f0f490bde694a11 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Sat, 27 Dec 2025 16:36:50 +0100 Subject: [PATCH 4/4] Apply to X,Y,Z and E axis --- Marlin/src/core/drivers.h | 3 +++ Marlin/src/inc/Conditionals-4-adv.h | 17 +++++++++++--- Marlin/src/module/stepper.cpp | 35 ++++++++++++++--------------- Marlin/src/module/stepper.h | 2 +- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/Marlin/src/core/drivers.h b/Marlin/src/core/drivers.h index 80980380a5..fec4494e32 100644 --- a/Marlin/src/core/drivers.h +++ b/Marlin/src/core/drivers.h @@ -67,6 +67,9 @@ #define AXIS_DRIVER_TYPE_Z2(T) (NUM_Z_STEPPERS >= 2 && _AXIS_DRIVER_TYPE(Z2,T)) #define AXIS_DRIVER_TYPE_Z3(T) (NUM_Z_STEPPERS >= 3 && _AXIS_DRIVER_TYPE(Z3,T)) #define AXIS_DRIVER_TYPE_Z4(T) (NUM_Z_STEPPERS >= 4 && _AXIS_DRIVER_TYPE(Z4,T)) +#define AXIS_DRIVER_TYPE_LOGICAL_X(T) (AXIS_DRIVER_TYPE(X,T) || (HAS_X2_STEPPER && _AXIS_DRIVER_TYPE(X2,T))) +#define AXIS_DRIVER_TYPE_LOGICAL_Y(T) (AXIS_DRIVER_TYPE(Y,T) || (HAS_Y2_STEPPER && _AXIS_DRIVER_TYPE(Y2,T))) +#define AXIS_DRIVER_TYPE_LOGICAL_Z(T) (AXIS_DRIVER_TYPE(Z,T) || (NUM_Z_STEPPERS >= 2 && _AXIS_DRIVER_TYPE(Z2,T)) || (NUM_Z_STEPPERS >= 3 && _AXIS_DRIVER_TYPE(Z3,T)) || (NUM_Z_STEPPERS >= 4 && _AXIS_DRIVER_TYPE(Z4,T))) #define AXIS_DRIVER_TYPE_E(N,T) (E_STEPPERS > N && _AXIS_DRIVER_TYPE(E##N,T)) #define AXIS_DRIVER_TYPE_E0(T) AXIS_DRIVER_TYPE_E(0,T) diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h index e61549b396..704537d384 100644 --- a/Marlin/src/inc/Conditionals-4-adv.h +++ b/Marlin/src/inc/Conditionals-4-adv.h @@ -359,9 +359,6 @@ #if ANY(FTM_SHAPER_EI, FTM_SHAPER_2HEI, FTM_SHAPER_3HEI) #define HAS_FTM_EI_SHAPING 1 #endif - #if (HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE)) - #define FTM_E_DRIVER_HYSTERESIS 1 - #endif #endif // Standard Motion @@ -1394,6 +1391,20 @@ #endif #endif +// Axis hysteresis support for TMC2208, TMC5160 +#if (AXIS_DRIVER_TYPE_LOGICAL_X(TMC2208) || AXIS_DRIVER_TYPE_LOGICAL_X(TMC2208_STANDALONE) || AXIS_DRIVER_TYPE_LOGICAL_X(TMC5160) || AXIS_DRIVER_TYPE_LOGICAL_X(TMC5160_STANDALONE)) + #define X_DRIVER_HYSTERESIS 1 +#endif +#if (AXIS_DRIVER_TYPE_LOGICAL_Y(TMC2208) || AXIS_DRIVER_TYPE_LOGICAL_Y(TMC2208_STANDALONE) || AXIS_DRIVER_TYPE_LOGICAL_Y(TMC5160) || AXIS_DRIVER_TYPE_LOGICAL_Y(TMC5160_STANDALONE)) + #define Y_DRIVER_HYSTERESIS 1 +#endif +#if (AXIS_DRIVER_TYPE_LOGICAL_Z(TMC2208) || AXIS_DRIVER_TYPE_LOGICAL_Z(TMC2208_STANDALONE) || AXIS_DRIVER_TYPE_LOGICAL_Z(TMC5160) || AXIS_DRIVER_TYPE_LOGICAL_Z(TMC5160_STANDALONE)) + #define Z_DRIVER_HYSTERESIS 1 +#endif +#if (HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) || HAS_E_DRIVER(TMC5160) || HAS_E_DRIVER(TMC5160_STANDALONE)) + #define E_DRIVER_HYSTERESIS 1 +#endif + #if AXIS_IS_TMC(X) #define X_IS_TRINAMIC 1 #endif diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 19dcb0bd80..8cd4246615 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -671,7 +671,7 @@ void Stepper::disable_all_steppers() { #if ENABLED(FT_MOTION) // We'll compare the updated DIR bits to the last set state static AxisBits last_set_direction; - #if FTM_E_DRIVER_HYSTERESIS + #if ANY(X_DRIVER_HYSTERESIS, Y_DRIVER_HYSTERESIS,Z_DRIVER_HYSTERESIS,E_DRIVER_HYSTERESIS) xyze_int_t Stepper::pending_hysteresis_steps ={0}; // Accumulated steps offset due to hysteresis delays #endif #endif @@ -3633,30 +3633,29 @@ void Stepper::report_positions() { * in the standard motion system. */ - #if FTM_E_DRIVER_HYSTERESIS + #if ANY(X_DRIVER_HYSTERESIS, Y_DRIVER_HYSTERESIS,Z_DRIVER_HYSTERESIS,E_DRIVER_HYSTERESIS) /** - * When a direction change is detected on E axis: + * When a direction change is detected on any axis: * 1. Update last_direction_bits * 2. Accumulate step counter for the new direction * 3. Apply WAIT_BEFORE only when accumulated steps >= HYSTERESIS threshold * 4. Single-direction blocks pass through without suppression */ - if (step_bits.E && (last_direction_bits.E != dir_bits.E)) { - // Direction change detected - UPDATE immediately to avoid re-accumulation - last_direction_bits.E = dir_bits.E; - - if (++pending_hysteresis_steps.E >= HYSTERESIS_E) { - // Threshold reached - apply direction change with TMC protection - { USING_TIMED_PULSE(); START_TIMED_PULSE(); AWAIT_LOW_PULSE(); } - DIR_WAIT_BEFORE(); - pending_hysteresis_steps.E = 0; - } else { - step_bits.E = 0; - } - } else { - pending_hysteresis_steps.E = 0; - } + #define _AXES_HYSTERESIS(A) if(HYSTERESIS_##A) { \ + if (step_bits.A && (last_direction_bits.A != dir_bits.A)) { \ + last_direction_bits.A = dir_bits.A; \ + if (++pending_hysteresis_steps.A >= HYSTERESIS_##A) { \ + { USING_TIMED_PULSE(); START_TIMED_PULSE(); AWAIT_LOW_PULSE(); } \ + DIR_WAIT_BEFORE(); \ + pending_hysteresis_steps.A = 0; } \ + else { \ + step_bits.A = 0; } \ + } else \ + pending_hysteresis_steps.A = 0; \ + } + + LOGICAL_AXIS_MAP(_AXES_HYSTERESIS); #endif USING_TIMED_PULSE(); diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 3333029d4b..c57aa5a02d 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -407,7 +407,7 @@ class Stepper { #endif // FT_MOTION Hysteresis tracking for direction changes - #if FTM_E_DRIVER_HYSTERESIS + #if ANY(X_DRIVER_HYSTERESIS, Y_DRIVER_HYSTERESIS,Z_DRIVER_HYSTERESIS,E_DRIVER_HYSTERESIS) static xyze_int_t pending_hysteresis_steps; // Accumulated steps offset due to hysteresis delays #endif