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 75c4efa743..704537d384 100644 --- a/Marlin/src/inc/Conditionals-4-adv.h +++ b/Marlin/src/inc/Conditionals-4-adv.h @@ -1391,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 2a10c83800..8cd4246615 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -671,6 +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; + #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 // Set a single axis direction based on the last set flags. @@ -1809,6 +1812,47 @@ 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 FTM_E_DRIVER_HYSTERESIS + #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 +1927,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 +3627,37 @@ 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 ANY(X_DRIVER_HYSTERESIS, Y_DRIVER_HYSTERESIS,Z_DRIVER_HYSTERESIS,E_DRIVER_HYSTERESIS) + /** + * 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 + */ + + #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 6e6761f842..c57aa5a02d 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -406,6 +406,11 @@ 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 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 + static bool abort_current_block; // Signals to the stepper that current block should be aborted // Motor locking for independent movement of multi-stepper axes