diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 31d3b8364b..84fbd6f8ed 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -2826,8 +2826,8 @@ * * Adds support for commands: * S000 : Report State and Position while moving. - * P000 : Instant Pause / Hold while moving. - * R000 : Resume from Pause / Hold. + * P000 : Instant Pause / Hold while moving. Enable SOFT_FEED_HOLD for soft deceleration. + * R000 : Resume from Pause / Hold. Enable SOFT_FEED_HOLD for soft acceleration. * * - During Hold all Emergency Parser commands are available, as usual. * - Enable NANODLP_Z_SYNC and NANODLP_ALL_AXIS for move command end-state reports. @@ -4457,15 +4457,38 @@ #endif /** - * Instant freeze / unfreeze functionality - * Potentially useful for rapid stop that allows being resumed. Halts stepper movement. - * Note this does NOT pause spindles, lasers, fans, heaters or any other auxiliary device. - * @section interface + * Freeze / Unfreeze + * + * Pause / Hold that keeps power available and does not stop the spindle can be initiated by + * the FREEZE_PIN. Halts instantly (default) or performs a soft feed hold that decelerates and + * halts movement at FREEZE_JERK (requires SOFT_FEED_HOLD). + * Motion can be resumed by using the FREEZE_PIN. + * + * NOTE: Controls Laser PWM but does NOT pause Spindle, Fans, Heaters or other devices. + * @section freeze */ //#define FREEZE_FEATURE #if ENABLED(FREEZE_FEATURE) - //#define FREEZE_PIN 41 // Override the default (KILL) pin here - #define FREEZE_STATE LOW // State of pin indicating freeze + //#define FREEZE_PIN -1 // Override the default (KILL) pin here + #define FREEZE_STATE LOW // State of pin indicating freeze +#endif + +#if ANY(FREEZE_FEATURE, REALTIME_REPORTING_COMMANDS) + /** + * Command P000 (REALTIME_REPORTING_COMMANDS and EMERGENCY_PARSER) or + * FREEZE_PIN (FREEZE_FEATURE) initiates a soft feed hold that keeps + * power available and does not stop the spindle. + * + * The soft feed hold decelerates and halts movement at FREEZE_JERK. + * Motion can be resumed with command R000 (requires REALTIME_REPORTING_COMMANDS) or + * by using the FREEZE_PIN (requires FREEZE_FEATURE). + * + * NOTE: Affects Laser PWM but DOES NOT pause Spindle, Fans, Heaters or other devices. + */ + //#define SOFT_FEED_HOLD + #if ENABLED(SOFT_FEED_HOLD) + #define FREEZE_JERK 2 // (mm/s) Completely halt when motion has decelerated below this value + #endif #endif /** diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 6d672e3b8c..94d0bce188 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -260,6 +260,10 @@ #include "feature/rs485.h" #endif +#if ENABLED(SOFT_FEED_HOLD) + #include "feature/e_parser.h" +#endif + /** * Spin in place here while keeping temperature processing alive */ @@ -514,8 +518,14 @@ void Marlin::manage_inactivity(const bool no_stepper_sleep/*=false*/) { } #endif - #if ENABLED(FREEZE_FEATURE) - stepper.frozen = READ(FREEZE_PIN) == FREEZE_STATE; + // Handle the FREEZE button + #if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD) + stepper.set_frozen_triggered( + TERN0(FREEZE_FEATURE, READ(FREEZE_PIN) == FREEZE_STATE) + #if ALL(SOFT_FEED_HOLD, REALTIME_REPORTING_COMMANDS) + || realtime_ramping_pause_flag + #endif + ); #endif #if HAS_HOME @@ -1221,7 +1231,7 @@ void setup() { #endif #endif - #if ENABLED(FREEZE_FEATURE) + #if ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) SETUP_LOG("FREEZE_PIN"); #if FREEZE_STATE SET_INPUT_PULLDOWN(FREEZE_PIN); diff --git a/Marlin/src/feature/e_parser.cpp b/Marlin/src/feature/e_parser.cpp index 28a903719c..3744870164 100644 --- a/Marlin/src/feature/e_parser.cpp +++ b/Marlin/src/feature/e_parser.cpp @@ -60,6 +60,10 @@ EmergencyParser emergency_parser; void quickresume_stepper(); #endif +#if ENABLED(SOFT_FEED_HOLD) + bool realtime_ramping_pause_flag = false; +#endif + void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { auto uppercase = [](char c) { return TERN0(GCODE_CASE_INSENSITIVE, WITHIN(c, 'a', 'z')) ? c + 'A' - 'a' : c; @@ -223,8 +227,8 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { #endif #if ENABLED(REALTIME_REPORTING_COMMANDS) case EP_GRBL_STATUS: report_current_position_moving(); break; - case EP_GRBL_PAUSE: quickpause_stepper(); break; - case EP_GRBL_RESUME: quickresume_stepper(); break; + case EP_GRBL_PAUSE: TERN(SOFT_FEED_HOLD, realtime_ramping_pause_flag = true, quickpause_stepper()); break; + case EP_GRBL_RESUME: TERN(SOFT_FEED_HOLD, realtime_ramping_pause_flag = false, quickresume_stepper()); break; #endif #if ENABLED(SOFT_RESET_VIA_SERIAL) case EP_KILL: hal.reboot(); break; diff --git a/Marlin/src/feature/e_parser.h b/Marlin/src/feature/e_parser.h index 9f74a38116..a582ec6c3d 100644 --- a/Marlin/src/feature/e_parser.h +++ b/Marlin/src/feature/e_parser.h @@ -93,3 +93,7 @@ private: }; extern EmergencyParser emergency_parser; + +#if ENABLED(SOFT_FEED_HOLD) + extern bool realtime_ramping_pause_flag; +#endif diff --git a/Marlin/src/inc/Conditionals-5-post.h b/Marlin/src/inc/Conditionals-5-post.h index b9eeb3d85a..55cd16c8d7 100644 --- a/Marlin/src/inc/Conditionals-5-post.h +++ b/Marlin/src/inc/Conditionals-5-post.h @@ -3047,9 +3047,10 @@ #endif // User Interface -#if ENABLED(FREEZE_FEATURE) && !PIN_EXISTS(FREEZE) && PIN_EXISTS(KILL) +#if ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) && !PIN_EXISTS(FREEZE) && PIN_EXISTS(KILL) #define FREEZE_PIN KILL_PIN -#elif PIN_EXISTS(KILL) && TERN1(FREEZE_FEATURE, KILL_PIN != FREEZE_PIN) + #define FREEZE_STOLE_KILL_PIN_WARNING 1 +#elif PIN_EXISTS(KILL) && TERN1(HAS_FREEZE_PIN, KILL_PIN != FREEZE_PIN) #define HAS_KILL 1 #endif #if PIN_EXISTS(HOME) diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 5856723c77..3155127c58 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -579,8 +579,12 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L /** * Instant Freeze */ -#if ENABLED(FREEZE_FEATURE) && !(PIN_EXISTS(FREEZE) && defined(FREEZE_STATE)) - #error "FREEZE_FEATURE requires both FREEZE_PIN and FREEZE_STATE." +#if ENABLED(SOFT_FEED_HOLD) && !defined(FREEZE_JERK) + #error "SOFT_FEED_HOLD requires FREEZE_JERK." +#elif ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) && !(defined(FREEZE_PIN) && defined(FREEZE_STATE)) + #error "FREEZE_FEATURE requires FREEZE_PIN and FREEZE_STATE." +#elif ENABLED(NO_FREEZE_PIN) && !(defined(REALTIME_REPORTING_COMMANDS)) + #error "NO_FREEZE_PIN requires REALTIME_REPORTING_COMMANDS." #endif /** @@ -4501,8 +4505,8 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive." #error "SMOOTH_LIN_ADVANCE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." #elif ENABLED(MIXING_EXTRUDER) #error "MIXING_EXTRUDER is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." - #elif ENABLED(FREEZE_FEATURE) - #error "FREEZE_FEATURE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." + #elif ENABLED(SOFT_FEED_HOLD) + #error "SOFT_FEED_HOLD is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." #elif ENABLED(DIRECT_STEPPING) #error "DIRECT_STEPPING is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." #elif ENABLED(DIFFERENTIAL_EXTRUDER) diff --git a/Marlin/src/inc/Warnings.cpp b/Marlin/src/inc/Warnings.cpp index 24101168f6..6a097777b0 100644 --- a/Marlin/src/inc/Warnings.cpp +++ b/Marlin/src/inc/Warnings.cpp @@ -1005,3 +1005,10 @@ #elif !RECOMMEND_REINIT_NOISY_LCD && ENABLED(REINIT_NOISY_LCD) #warning "REINIT_NOISY_LCD is probably not required with your LCD controller model." #endif + +/** + * FREEZE_FEATURE may override the KILL_PIN + */ +#if FREEZE_STOLE_KILL_PIN_WARNING + #warning "FREEZE_FEATURE uses KILL_PIN replacing the KILL button. Define a separate FREEZE_PIN if you don't want this behavior." +#endif diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 414dfc48b1..6da11fd8c4 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -2428,6 +2428,9 @@ bool Planner::_populate_block( block->acceleration_steps_per_s2 = accel; #if DISABLED(S_CURVE_ACCELERATION) block->acceleration_rate = uint32_t(accel * (float(_BV32(24)) / (STEPPER_TIMER_RATE))); + #elif ENABLED(SOFT_FEED_HOLD) + // No need to waste time calculating the linear acceleration rate until the freeze_pin is triggered, leave this 0 + block->acceleration_rate = 0; #endif #endif block->acceleration = accel / steps_per_mm; diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index 71ba8e18f9..36b94da77d 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -254,7 +254,8 @@ typedef struct PlannerBlock { #if ENABLED(S_CURVE_ACCELERATION) uint32_t acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used deceleration_time_inverse; - #elif HAS_STANDARD_MOTION + #endif + #if ENABLED(HAS_STANDARD_MOTION) && (DISABLED(S_CURVE_ACCELERATION) || ENABLED(FREEZE_FEATURE)) uint32_t acceleration_rate; // Acceleration rate in (2^24 steps)/timer_ticks*s #endif diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index c8b2f150d1..cfb2617024 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -199,8 +199,14 @@ uint32_t Stepper::acceleration_time, Stepper::deceleration_time; constexpr uint8_t Stepper::oversampling_factor; // = 0 #endif -#if ENABLED(FREEZE_FEATURE) - bool Stepper::frozen; // = false +#if ANY(SOFT_FEED_HOLD, FREEZE_FEATURE) + frozen_state_t Stepper::frozen_state; // Frozen flags +#endif +#if ENABLED(SOFT_FEED_HOLD) + uint32_t Stepper::frozen_time; // How much time has passed since frozen_state was triggered? + #if ENABLED(LASER_FEATURE) + uint8_t frozen_last_laser_power; // Saved laser power prior to halting motion + #endif #endif // Delta error variables for the Bresenham line tracer @@ -1846,7 +1852,11 @@ void Stepper::isr() { if (!current_block || step_events_completed >= step_event_count) return; // Skipping step processing causes motion to freeze - if (TERN0(FREEZE_FEATURE, frozen)) return; + #if ENABLED(SOFT_FEED_HOLD) + if (frozen_state.triggered && frozen_state.solid) return; + #elif ENABLED(FREEZE_FEATURE) + if (frozen_state.state == 0) return; + #endif // Count of pending loops and events for this iteration const uint32_t pending_events = step_event_count - step_events_completed; @@ -2418,6 +2428,10 @@ void Stepper::isr() { // If no queued movements, just wait 1ms for the next block hal_timer_t interval = (STEPPER_TIMER_RATE) / 1000UL; + // Frozen solid?? Exit and do not fetch blocks. + if (TERN0(SOFT_FEED_HOLD, frozen_state.triggered && frozen_state.solid)) + return interval; + // If there is a current block if (current_block) { // If current block is finished, reset pointer and finalize state @@ -2460,11 +2474,16 @@ void Stepper::isr() { // acc_step_rate is in steps/second + // Modify acc_step_rate if the machine is freezing + TERN_(SOFT_FEED_HOLD, check_frozen_time(acc_step_rate)); + // step_rate to timer interval and steps per stepper isr interval = calc_multistep_timer_interval(acc_step_rate << oversampling_factor); acceleration_time += interval; deceleration_time = 0; // Reset since we're doing acceleration first. + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_ACCELERATION, interval)); + // Apply Nonlinear Extrusion, if enabled calc_nonlinear_e(acc_step_rate << oversampling_factor); @@ -2526,10 +2545,14 @@ void Stepper::isr() { #endif + TERN_(SOFT_FEED_HOLD, check_frozen_time(step_rate)); + // step_rate to timer interval and steps per stepper isr interval = calc_multistep_timer_interval(step_rate << oversampling_factor); deceleration_time += interval; + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_DECELERATION, interval)); + // Apply Nonlinear Extrusion, if enabled calc_nonlinear_e(step_rate << oversampling_factor); @@ -2576,21 +2599,25 @@ void Stepper::isr() { else { // Must be in cruise phase otherwise // Calculate the ticks_nominal for this nominal speed, if not done yet - if (ticks_nominal == 0) { + if (ticks_nominal == 0 || TERN0(SOFT_FEED_HOLD, frozen_time)) { + uint32_t step_rate = current_block->nominal_rate; + + TERN_(SOFT_FEED_HOLD, check_frozen_time(step_rate)); + // step_rate to timer interval and loops for the nominal speed - ticks_nominal = calc_multistep_timer_interval(current_block->nominal_rate << oversampling_factor); + ticks_nominal = calc_multistep_timer_interval(step_rate << oversampling_factor); deceleration_time = ticks_nominal / 2; // Prepare for deceleration - IF_DISABLED(S_CURVE_ACCELERATION, acc_step_rate = current_block->nominal_rate); + IF_DISABLED(S_CURVE_ACCELERATION, acc_step_rate = step_rate); TERN_(SMOOTH_LIN_ADVANCE, curr_step_rate = current_block->nominal_rate); // Apply Nonlinear Extrusion, if enabled - calc_nonlinear_e(current_block->nominal_rate << oversampling_factor); + calc_nonlinear_e(step_rate << oversampling_factor); #if HAS_ROUGH_LIN_ADVANCE if (la_active) - la_interval = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling); + la_interval = calc_timer_interval(step_rate >> current_block->la_scaling); #endif // Adjust Laser Power - Cruise @@ -2610,6 +2637,8 @@ void Stepper::isr() { // The timer interval is just the nominal value for the nominal speed interval = ticks_nominal; + + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_CRUISE, interval)); } } @@ -2631,9 +2660,12 @@ void Stepper::isr() { #endif } else { // !current_block + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_STATIONARY, interval)); + #if ENABLED(LASER_FEATURE) + // If no movement in dynamic mode turn Laser off if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC) - cutter.apply_power(0); // No movement in dynamic mode so turn Laser off + cutter.apply_power(0); #endif } @@ -2916,13 +2948,22 @@ void Stepper::isr() { } #endif + uint32_t initial_rate = current_block->initial_rate; + + #if ENABLED(SOFT_FEED_HOLD) + if (frozen_time) check_frozen_time(initial_rate); + #endif + // Calculate the initial timer interval - interval = calc_multistep_timer_interval(current_block->initial_rate << oversampling_factor); + interval = calc_multistep_timer_interval(initial_rate << oversampling_factor); + + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_ACCELERATION, interval)); + // Initialize ac/deceleration time as if half the time passed. acceleration_time = deceleration_time = interval / 2; // Apply Nonlinear Extrusion, if enabled - calc_nonlinear_e(current_block->initial_rate << oversampling_factor); + calc_nonlinear_e(initial_rate << oversampling_factor); #if ENABLED(LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE) @@ -2930,7 +2971,7 @@ void Stepper::isr() { #else if (la_active) { const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0; - la_interval = calc_timer_interval((current_block->initial_rate + la_step_rate) >> current_block->la_scaling); + la_interval = calc_timer_interval((initial_rate + la_step_rate) >> current_block->la_scaling); } #endif #endif @@ -3876,3 +3917,105 @@ void Stepper::report_positions() { } #endif // BABYSTEPPING + +#if ENABLED(SOFT_FEED_HOLD) + + void Stepper::set_frozen_solid(const bool state) { + if (state == frozen_state.solid) return; + + frozen_state.solid = true; + + #if ENABLED(LASER_FEATURE) + if (state) { + frozen_last_laser_power = cutter.last_power_applied; + cutter.apply_power(0); // No movement in dynamic mode so turn Laser off + } + else + cutter.apply_power(frozen_last_laser_power); // Restore frozen laser power + #endif + + #if ENABLED(REALTIME_REPORTING_COMMANDS) + set_and_report_grblstate(state ? M_HOLD : M_RUNNING); + #endif + } + + void Stepper::check_frozen_time(uint32_t &step_rate) { + // If frozen_time is 0 there is no need to modify the current step_rate + if (!frozen_time) return; + + #if ENABLED(S_CURVE_ACCELERATION) + // If the machine is configured to use S_CURVE_ACCELERATION standard ramp acceleration + // rate will not have been calculated at this point + if (!current_block->acceleration_rate) + current_block->acceleration_rate = uint32_t(current_block->acceleration_steps_per_s2 * (float(1UL << 24) / (STEPPER_TIMER_RATE))); + #endif + + const uint32_t freeze_rate = STEP_MULTIPLY(frozen_time, current_block->acceleration_rate); + const uint32_t min_step_rate = current_block->steps_per_mm * (FREEZE_JERK); + + if (step_rate > freeze_rate) + step_rate -= freeze_rate; + else + step_rate = 0; + + if (step_rate <= min_step_rate) { + set_frozen_solid(true); + step_rate = min_step_rate; + } + } + + void Stepper::check_frozen_state(const FreezePhase phase, const uint32_t interval) { + switch (phase) { + case FREEZE_STATIONARY: + // If triggered while stationary immediately set solid flag + if (frozen_state.triggered) { + frozen_time = 0; + set_frozen_solid(true); + } + else + set_frozen_solid(false); + break; + + case FREEZE_ACCELERATION: + // If frozen state is activated during the acceleration phase of a block we need to double our decceleration efforts + if (frozen_state.triggered) { + if (!frozen_state.solid) frozen_time += interval * 2; + } + else + set_frozen_solid(false); + break; + + case FREEZE_DECELERATION: + // If frozen state is deactivated during the deceleration phase we need to double our acceleration efforts + if (!frozen_state.triggered) { + if (frozen_time) { + if (frozen_time > interval * 2) + frozen_time -= interval * 2; + else + frozen_time = 0; + } + set_frozen_solid(false); + } + break; + + case FREEZE_CRUISE: + // During cruise stage acceleration/deceleration take place at regular rate + if (frozen_state.triggered) { + if (!frozen_state.solid) frozen_time += interval; + } + else { + if (frozen_time) { + if (frozen_time > interval) + frozen_time -= interval; + else { + frozen_time = 0; + ticks_nominal = 0; // Reset ticks_nominal to allow for recalculation of interval at nominal_rate + } + } + set_frozen_solid(false); + } + break; + } + } + +#endif // SOFT_FEED_HOLD diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 6e6761f842..ec567245f0 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -318,6 +318,26 @@ constexpr ena_mask_t enable_overlap[] = { #endif // NONLINEAR_EXTRUSION +#if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD) + + typedef union { + uint8_t state; + struct { bool triggered:1, solid:1; }; + } frozen_state_t; + + enum FrozenState { FROZEN_TRIGGERED, FROZEN_SOLID }; + + #if ENABLED(SOFT_FEED_HOLD) + enum FreezePhase : uint8_t { + FREEZE_STATIONARY, + FREEZE_ACCELERATION, + FREEZE_DECELERATION, + FREEZE_CRUISE + }; + #endif + +#endif + // // Stepper class definition // @@ -367,8 +387,12 @@ class Stepper { static constexpr uint8_t last_moved_extruder = 0; #endif - #if ENABLED(FREEZE_FEATURE) - static bool frozen; // Set this flag to instantly freeze motion + #if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD) + static frozen_state_t frozen_state; // Frozen flags + static void set_frozen_triggered(const bool state) { frozen_state.triggered = state; } + #if ENABLED(SOFT_FEED_HOLD) + static bool is_frozen_triggered() { return frozen_state.triggered; } + #endif #endif #if ENABLED(NONLINEAR_EXTRUSION) @@ -800,6 +824,15 @@ class Stepper { static void ftMotion_stepper(); #endif + #if ENABLED(SOFT_FEED_HOLD) + static uint32_t frozen_time; // How much time passed since frozen_state was triggered? + #if ENABLED(LASER_FEATURE) + static uint8_t frozen_last_laser_power; // Saved laser power prior to halting motion + #endif + static void check_frozen_state(const FreezePhase type, const uint32_t interval); + static void check_frozen_time(uint32_t &step_rate); + static void set_frozen_solid(const bool state); + #endif }; extern Stepper stepper; diff --git a/buildroot/tests/DUE b/buildroot/tests/DUE index d5fcf2e4cb..9b8be3fbf9 100755 --- a/buildroot/tests/DUE +++ b/buildroot/tests/DUE @@ -14,8 +14,9 @@ opt_set MOTHERBOARD BOARD_RAMPS4DUE_EFB \ E0_AUTO_FAN_PIN 8 FANMUX0_PIN 53 EXTRUDER_AUTO_FAN_SPEED 100 \ TEMP_SENSOR_CHAMBER 3 TEMP_CHAMBER_PIN 6 HEATER_CHAMBER_PIN 45 \ BACKLASH_MEASUREMENT_FEEDRATE 600 \ - TRAMMING_POINT_XY '{{20,20},{20,20},{20,20},{20,20},{20,20}}' TRAMMING_POINT_NAME_5 '"Point 5"' -opt_enable S_CURVE_ACCELERATION EEPROM_SETTINGS GCODE_MACROS GCODE_MACROS_IN_EEPROM \ + TRAMMING_POINT_XY '{{20,20},{20,20},{20,20},{20,20},{20,20}}' TRAMMING_POINT_NAME_5 '"Point 5"' \ + FREEZE_PIN 17 +opt_enable S_CURVE_ACCELERATION FREEZE_FEATURE SOFT_FEED_HOLD EEPROM_SETTINGS GCODE_MACROS GCODE_MACROS_IN_EEPROM \ FIX_MOUNTED_PROBE Z_SAFE_HOMING CODEPENDENT_XY_HOMING \ ASSISTED_TRAMMING REPORT_TRAMMING_MM ASSISTED_TRAMMING_WAIT_POSITION \ EEPROM_SETTINGS SDSUPPORT BINARY_FILE_TRANSFER \