️ Improve Homing / Probing Current (#26714)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
sargonphin 2024-08-16 01:30:55 +02:00 committed by GitHub
parent 23d9020a65
commit 2aa2e5487a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 691 additions and 228 deletions

View file

@ -2969,7 +2969,7 @@
#if AXIS_IS_TMC_CONFIG(X) #if AXIS_IS_TMC_CONFIG(X)
#define X_CURRENT 800 // (mA) RMS current. Multiply by 1.414 for peak current. #define X_CURRENT 800 // (mA) RMS current. Multiply by 1.414 for peak current.
#define X_CURRENT_HOME X_CURRENT // (mA) RMS current for sensorless homing #define X_CURRENT_HOME X_CURRENT // (mA) RMS current for homing. (Typically lower than *_CURRENT.)
#define X_MICROSTEPS 16 // 0..256 #define X_MICROSTEPS 16 // 0..256
#define X_RSENSE 0.11 #define X_RSENSE 0.11
#define X_CHAIN_POS -1 // -1..0: Not chained. 1: MCU MOSI connected. 2: Next in chain, ... #define X_CHAIN_POS -1 // -1..0: Not chained. 1: MCU MOSI connected. 2: Next in chain, ...
@ -3179,6 +3179,13 @@
//#define E7_HOLD_MULTIPLIER 0.5 //#define E7_HOLD_MULTIPLIER 0.5
#endif #endif
/**
* Use the homing current for all probing. (e.g., Current may be reduced to the
* point where a collision makes the motor skip instead of damaging the bed,
* though this is unlikely to save delicate probes from being damaged.
*/
//#define PROBING_USE_CURRENT_HOME
// @section tmc/spi // @section tmc/spi
/** /**

View file

@ -28,6 +28,10 @@
#include "../../module/planner.h" #include "../../module/planner.h"
#include "../../module/stepper.h" // for various #include "../../module/stepper.h" // for various
#if HAS_HOMING_CURRENT
#include "../../module/motion.h" // for set/restore_homing_current
#endif
#if HAS_MULTI_HOTEND #if HAS_MULTI_HOTEND
#include "../../module/tool_change.h" #include "../../module/tool_change.h"
#endif #endif
@ -77,6 +81,14 @@
const float minfr = _MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)), const float minfr = _MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)),
fr_mm_s = HYPOT(minfr, minfr); fr_mm_s = HYPOT(minfr, minfr);
// Set homing current to X and Y axis if defined
#if HAS_CURRENT_HOME(X)
set_homing_current(X_AXIS);
#endif
#if HAS_CURRENT_HOME(Y) && NONE(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
set_homing_current(Y_AXIS);
#endif
#if ENABLED(SENSORLESS_HOMING) #if ENABLED(SENSORLESS_HOMING)
sensorless_t stealth_states { sensorless_t stealth_states {
NUM_AXIS_LIST( NUM_AXIS_LIST(
@ -95,6 +107,13 @@
current_position.set(0.0, 0.0); current_position.set(0.0, 0.0);
#if HAS_CURRENT_HOME(X)
restore_homing_current(X_AXIS);
#endif
#if HAS_CURRENT_HOME(Y) && NONE(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
restore_homing_current(Y_AXIS);
#endif
#if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT) #if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)
TERN_(X_SENSORLESS, tmc_disable_stallguard(stepperX, stealth_states.x)); TERN_(X_SENSORLESS, tmc_disable_stallguard(stepperX, stealth_states.x));
TERN_(X2_SENSORLESS, tmc_disable_stallguard(stepperX2, stealth_states.x2)); TERN_(X2_SENSORLESS, tmc_disable_stallguard(stepperX2, stealth_states.x2));
@ -254,73 +273,6 @@ void GcodeSuite::G28() {
// Reset to the XY plane // Reset to the XY plane
TERN_(CNC_WORKSPACE_PLANES, workspace_plane = PLANE_XY); TERN_(CNC_WORKSPACE_PLANES, workspace_plane = PLANE_XY);
#define _OR_HAS_CURR_HOME(N) HAS_CURRENT_HOME(N) ||
#if MAIN_AXIS_MAP(_OR_HAS_CURR_HOME) MAP(_OR_HAS_CURR_HOME, X2, Y2, Z2, Z3, Z4) 0
#define HAS_HOMING_CURRENT 1
#endif
#if HAS_HOMING_CURRENT
#if ENABLED(DEBUG_LEVELING_FEATURE)
auto debug_current = [](FSTR_P const s, const int16_t a, const int16_t b) {
if (DEBUGGING(LEVELING)) { DEBUG_ECHOLN(s, F(" current: "), a, F(" -> "), b); }
};
#else
#define debug_current(...)
#endif
#define _SAVE_SET_CURRENT(A) \
const int16_t saved_current_##A = stepper##A.getMilliamps(); \
stepper##A.rms_current(A##_CURRENT_HOME); \
debug_current(F(STR_##A), saved_current_##A, A##_CURRENT_HOME)
#if HAS_CURRENT_HOME(X)
_SAVE_SET_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(X2)
_SAVE_SET_CURRENT(X2);
#endif
#if HAS_CURRENT_HOME(Y)
_SAVE_SET_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Y2)
_SAVE_SET_CURRENT(Y2);
#endif
#if HAS_CURRENT_HOME(Z)
_SAVE_SET_CURRENT(Z);
#endif
#if HAS_CURRENT_HOME(Z2)
_SAVE_SET_CURRENT(Z2);
#endif
#if HAS_CURRENT_HOME(Z3)
_SAVE_SET_CURRENT(Z3);
#endif
#if HAS_CURRENT_HOME(Z4)
_SAVE_SET_CURRENT(Z4);
#endif
#if HAS_CURRENT_HOME(I)
_SAVE_SET_CURRENT(I);
#endif
#if HAS_CURRENT_HOME(J)
_SAVE_SET_CURRENT(J);
#endif
#if HAS_CURRENT_HOME(K)
_SAVE_SET_CURRENT(K);
#endif
#if HAS_CURRENT_HOME(U)
_SAVE_SET_CURRENT(U);
#endif
#if HAS_CURRENT_HOME(V)
_SAVE_SET_CURRENT(V);
#endif
#if HAS_CURRENT_HOME(W)
_SAVE_SET_CURRENT(W);
#endif
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif
#endif // HAS_HOMING_CURRENT
#if ENABLED(IMPROVE_HOMING_RELIABILITY) #if ENABLED(IMPROVE_HOMING_RELIABILITY)
motion_state_t saved_motion_state = begin_slow_homing(); motion_state_t saved_motion_state = begin_slow_homing();
#endif #endif
@ -572,55 +524,6 @@ void GcodeSuite::G28() {
// Clear endstop state for polled stallGuard endstops // Clear endstop state for polled stallGuard endstops
TERN_(SPI_ENDSTOPS, endstops.clear_endstop_state()); TERN_(SPI_ENDSTOPS, endstops.clear_endstop_state());
#if HAS_HOMING_CURRENT
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Restore driver current...");
#if HAS_CURRENT_HOME(X)
stepperX.rms_current(saved_current_X);
#endif
#if HAS_CURRENT_HOME(X2)
stepperX2.rms_current(saved_current_X2);
#endif
#if HAS_CURRENT_HOME(Y)
stepperY.rms_current(saved_current_Y);
#endif
#if HAS_CURRENT_HOME(Y2)
stepperY2.rms_current(saved_current_Y2);
#endif
#if HAS_CURRENT_HOME(Z)
stepperZ.rms_current(saved_current_Z);
#endif
#if HAS_CURRENT_HOME(Z2)
stepperZ2.rms_current(saved_current_Z2);
#endif
#if HAS_CURRENT_HOME(Z3)
stepperZ3.rms_current(saved_current_Z3);
#endif
#if HAS_CURRENT_HOME(Z4)
stepperZ4.rms_current(saved_current_Z4);
#endif
#if HAS_CURRENT_HOME(I)
stepperI.rms_current(saved_current_I);
#endif
#if HAS_CURRENT_HOME(J)
stepperJ.rms_current(saved_current_J);
#endif
#if HAS_CURRENT_HOME(K)
stepperK.rms_current(saved_current_K);
#endif
#if HAS_CURRENT_HOME(U)
stepperU.rms_current(saved_current_U);
#endif
#if HAS_CURRENT_HOME(V)
stepperV.rms_current(saved_current_V);
#endif
#if HAS_CURRENT_HOME(W)
stepperW.rms_current(saved_current_W);
#endif
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif
#endif // HAS_HOMING_CURRENT
// Move to a height where we can use the full xy-area // Move to a height where we can use the full xy-area
TERN_(DELTA_HOME_TO_SAFE_ZONE, do_blocking_move_to_z(delta_clip_start_height)); TERN_(DELTA_HOME_TO_SAFE_ZONE, do_blocking_move_to_z(delta_clip_start_height));

View file

@ -63,9 +63,9 @@ float lcd_probe_pt(const xy_pos_t &xy);
void ac_home() { void ac_home() {
endstops.enable(true); endstops.enable(true);
TERN_(SENSORLESS_HOMING, endstops.set_z_sensorless_current(true)); TERN_(IMPROVE_HOMING_RELIABILITY, planner.enable_stall_prevention(true));
home_delta(); home_delta();
TERN_(SENSORLESS_HOMING, endstops.set_z_sensorless_current(false)); TERN_(IMPROVE_HOMING_RELIABILITY, planner.enable_stall_prevention(false));
endstops.not_homing(); endstops.not_homing();
} }

View file

@ -97,6 +97,7 @@
#if !HAS_BED_PROBE #if !HAS_BED_PROBE
#undef BABYSTEP_ZPROBE_OFFSET #undef BABYSTEP_ZPROBE_OFFSET
#undef PROBING_USE_CURRENT_HOME
#endif #endif
#if !HAS_STOWABLE_PROBE #if !HAS_STOWABLE_PROBE
#undef PROBE_DEPLOY_STOW_MENU #undef PROBE_DEPLOY_STOW_MENU

View file

@ -30,3 +30,13 @@
#ifdef GITHUB_ACTIONS #ifdef GITHUB_ACTIONS
// Extras for CI testing // Extras for CI testing
#endif #endif
// If an axis's Homing Current differs from standard current...
#define HAS_CURRENT_HOME(N) (N##_CURRENT_HOME > 0 && N##_CURRENT_HOME != N##_CURRENT)
// Does any axis have homing current?
#define _OR_HAS_CURR_HOME(N) HAS_CURRENT_HOME(N) ||
#if MAIN_AXIS_MAP(_OR_HAS_CURR_HOME) MAP(_OR_HAS_CURR_HOME, X2, Y2, Z2, Z3, Z4) 0
#define HAS_HOMING_CURRENT 1
#endif
#undef _OR_HAS_CURR_HOME

View file

@ -237,9 +237,9 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
// Serial DMA is only available for some STM32 MCUs and HC32 // Serial DMA is only available for some STM32 MCUs and HC32
#if ENABLED(SERIAL_DMA) #if ENABLED(SERIAL_DMA)
#if defined(ARDUINO_ARCH_HC32) #ifdef ARDUINO_ARCH_HC32
// checks for HC32 are located in HAL/HC32/inc/SanityCheck.h // checks for HC32 are located in HAL/HC32/inc/SanityCheck.h
#elif !HAL_STM32 || NONE(STM32F0xx, STM32F1xx, STM32F2xx, STM32F4xx, STM32F7xx) #elif DISABLED(HAL_STM32) || NONE(STM32F0xx, STM32F1xx, STM32F2xx, STM32F4xx, STM32F7xx)
#error "SERIAL_DMA is only available for some STM32 MCUs and requires HAL/STM32." #error "SERIAL_DMA is only available for some STM32 MCUs and requires HAL/STM32."
#elif !defined(HAL_UART_MODULE_ENABLED) || defined(HAL_UART_MODULE_ONLY) #elif !defined(HAL_UART_MODULE_ENABLED) || defined(HAL_UART_MODULE_ONLY)
#error "SERIAL_DMA requires STM32 platform HAL UART (without HAL_UART_MODULE_ONLY)." #error "SERIAL_DMA requires STM32 platform HAL UART (without HAL_UART_MODULE_ONLY)."
@ -1726,6 +1726,28 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#endif #endif
#endif #endif
/**
* Assert that the homing current must not be greater than the base current.
* Current may be reduced "to prevent damage" but in fact it's typically reduced to prevent spurious
* DIAG triggering from fast high torque moves with large jerk values which are more prone to cause binding.
*/
#define _BAD_HOME_CURRENT(N) (N##_CURRENT_HOME > N##_CURRENT) ||
#if MAIN_AXIS_MAP(_BAD_HOME_CURRENT) MAP(_BAD_HOME_CURRENT, X2, Y2, Z2, Z3, Z4) 0
#ifndef ALLOW_HIGHER_CURRENT_HOME
#error "*_CURRENT_HOME should be <= *_CURRENT. Define ALLOW_HIGHER_CURRENT_HOME in your configuration to continue anyway."
#else
#define HIGHER_CURRENT_HOME_WARNING 1
#endif
#endif
#undef _BAD_HOME_CURRENT
#if ENABLED(PROBING_USE_CURRENT_HOME)
#if (defined(Z_CURRENT_HOME) && !HAS_CURRENT_HOME(Z)) || (defined(Z2_CURRENT_HOME) && !HAS_CURRENT_HOME(Z2)) \
|| (defined(Z3_CURRENT_HOME) && !HAS_CURRENT_HOME(Z3)) || (defined(Z4_CURRENT_HOME) && !HAS_CURRENT_HOME(Z4))
#error "PROBING_USE_CURRENT_HOME requires a Z_CURRENT_HOME value that differs from Z_CURRENT."
#endif
#endif
/** /**
* Make sure Z_SAFE_HOMING point is reachable * Make sure Z_SAFE_HOMING point is reachable
*/ */

View file

@ -715,7 +715,33 @@
#endif #endif
#if ENABLED(QUICK_HOME) && (X_SPI_SENSORLESS || Y_SPI_SENSORLESS) #if ENABLED(QUICK_HOME) && (X_SPI_SENSORLESS || Y_SPI_SENSORLESS)
#warning "SPI_ENDSTOPS may be unreliable with QUICK_HOME. Adjust back-offs for better results." #warning "SPI_ENDSTOPS may be unreliable with QUICK_HOME. Adjust SENSORLESS_BACKOFF_MM for better results."
#endif
#if HIGHER_CURRENT_HOME_WARNING
#warning "High homing currents can lead to damage if a sensor fails or is set up incorrectly."
#endif
#if USE_SENSORLESS
#if defined(X_CURRENT_HOME) && !HAS_CURRENT_HOME(X)
#warning "It's recommended to set X_CURRENT_HOME lower than X_CURRENT with SENSORLESS_HOMING."
#elif defined(X2_CURRENT_HOME) && !HAS_CURRENT_HOME(X2)
#warning "It's recommended to set X2_CURRENT_HOME lower than X2_CURRENT with SENSORLESS_HOMING."
#endif
#if defined(Y_CURRENT_HOME) && !HAS_CURRENT_HOME(Y)
#warning "It's recommended to set Y_CURRENT_HOME lower than Y_CURRENT with SENSORLESS_HOMING."
#elif defined(Y2_CURRENT_HOME) && !HAS_CURRENT_HOME(Y2)
#warning "It's recommended to set Y2_CURRENT_HOME lower than Y2_CURRENT with SENSORLESS_HOMING."
#endif
#if defined(Z_CURRENT_HOME) && !HAS_CURRENT_HOME(Z)
#warning "It's recommended to set Z_CURRENT_HOME lower than Z_CURRENT with SENSORLESS_HOMING."
#elif defined(Z2_CURRENT_HOME) && !HAS_CURRENT_HOME(Z2)
#warning "It's recommended to set Z2_CURRENT_HOME lower than Z2_CURRENT with SENSORLESS_HOMING."
#elif defined(Z3_CURRENT_HOME) && !HAS_CURRENT_HOME(Z3)
#warning "It's recommended to set Z3_CURRENT_HOME lower than Z3_CURRENT with SENSORLESS_HOMING."
#elif defined(Z4_CURRENT_HOME) && !HAS_CURRENT_HOME(Z4)
#warning "It's recommended to set Z4_CURRENT_HOME lower than Z4_CURRENT with SENSORLESS_HOMING."
#endif
#endif #endif
#if CANNOT_EMBED_CONFIGURATION #if CANNOT_EMBED_CONFIGURATION

View file

@ -241,12 +241,18 @@ void home_delta() {
#endif #endif
#endif #endif
// Set homing current for all motors
TERN_(HAS_HOMING_CURRENT, set_homing_current(Z_AXIS));
// Move all carriages together linearly until an endstop is hit. // Move all carriages together linearly until an endstop is hit.
current_position.z = DIFF_TERN(HAS_BED_PROBE, delta_height + 10, probe.offset.z); current_position.z = DIFF_TERN(HAS_BED_PROBE, delta_height + 10, probe.offset.z);
line_to_current_position(homing_feedrate(Z_AXIS)); line_to_current_position(homing_feedrate(Z_AXIS));
planner.synchronize(); planner.synchronize();
TERN_(HAS_DELTA_SENSORLESS_PROBING, endstops.report_states()); TERN_(HAS_DELTA_SENSORLESS_PROBING, endstops.report_states());
// Restore the homing current for all motors
TERN_(HAS_HOMING_CURRENT, restore_homing_current(Z_AXIS));
// Re-enable stealthChop if used. Disable diag1 pin on driver. // Re-enable stealthChop if used. Disable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT) #if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)
TERN_(X_SENSORLESS, end_sensorless_homing_per_axis(X_AXIS, stealth_states_x)); TERN_(X_SENSORLESS, end_sensorless_homing_per_axis(X_AXIS, stealth_states_x));

View file

@ -1370,94 +1370,3 @@ void Endstops::update() {
} }
#endif // PINS_DEBUGGING #endif // PINS_DEBUGGING
#if USE_SENSORLESS
/**
* Change TMC driver currents to N##_CURRENT_HOME, saving the current configuration of each.
*/
void Endstops::set_z_sensorless_current(const bool onoff) {
#if ENABLED(DELTA) && HAS_CURRENT_HOME(X)
#define HAS_DELTA_X_CURRENT 1
#endif
#if ENABLED(DELTA) && HAS_CURRENT_HOME(Y)
#define HAS_DELTA_Y_CURRENT 1
#endif
#if HAS_DELTA_X_CURRENT || HAS_DELTA_Y_CURRENT || HAS_CURRENT_HOME(Z) || HAS_CURRENT_HOME(Z2) || HAS_CURRENT_HOME(Z3) || HAS_CURRENT_HOME(Z4)
#if HAS_DELTA_X_CURRENT
static int16_t saved_current_X;
#endif
#if HAS_DELTA_Y_CURRENT
static int16_t saved_current_Y;
#endif
#if HAS_CURRENT_HOME(Z)
static int16_t saved_current_Z;
#endif
#if HAS_CURRENT_HOME(Z2)
static int16_t saved_current_Z2;
#endif
#if HAS_CURRENT_HOME(Z3)
static int16_t saved_current_Z3;
#endif
#if HAS_CURRENT_HOME(Z4)
static int16_t saved_current_Z4;
#endif
#if ENABLED(DEBUG_LEVELING_FEATURE)
auto debug_current = [](FSTR_P const s, const int16_t a, const int16_t b) {
if (DEBUGGING(LEVELING)) { DEBUG_ECHOLN(s, F(" current: "), a, F(" -> "), b); }
};
#else
#define debug_current(...)
#endif
#define _SAVE_SET_CURRENT(A) \
saved_current_##A = stepper##A.getMilliamps(); \
stepper##A.rms_current(A##_CURRENT_HOME); \
debug_current(F(STR_##A), saved_current_##A, A##_CURRENT_HOME)
#define _RESTORE_CURRENT(A) \
stepper##A.rms_current(saved_current_##A); \
debug_current(F(STR_##A), saved_current_##A, A##_CURRENT_HOME)
if (onoff) {
TERN_(HAS_DELTA_X_CURRENT, _SAVE_SET_CURRENT(X));
TERN_(HAS_DELTA_Y_CURRENT, _SAVE_SET_CURRENT(Y));
#if HAS_CURRENT_HOME(Z)
_SAVE_SET_CURRENT(Z);
#endif
#if HAS_CURRENT_HOME(Z2)
_SAVE_SET_CURRENT(Z2);
#endif
#if HAS_CURRENT_HOME(Z3)
_SAVE_SET_CURRENT(Z3);
#endif
#if HAS_CURRENT_HOME(Z4)
_SAVE_SET_CURRENT(Z4);
#endif
}
else {
TERN_(HAS_DELTA_X_CURRENT, _RESTORE_CURRENT(X));
TERN_(HAS_DELTA_Y_CURRENT, _RESTORE_CURRENT(Y));
#if HAS_CURRENT_HOME(Z)
_RESTORE_CURRENT(Z);
#endif
#if HAS_CURRENT_HOME(Z2)
_RESTORE_CURRENT(Z2);
#endif
#if HAS_CURRENT_HOME(Z3)
_RESTORE_CURRENT(Z3);
#endif
#if HAS_CURRENT_HOME(Z4)
_RESTORE_CURRENT(Z4);
#endif
}
TERN_(IMPROVE_HOMING_RELIABILITY, planner.enable_stall_prevention(onoff));
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif
#endif
}
#endif // USE_SENSORLESS

View file

@ -37,8 +37,6 @@
#define _ESN_ITEM(K,A,M) ES_ITEM(K,ES_ENUM(A,M)) #define _ESN_ITEM(K,A,M) ES_ITEM(K,ES_ENUM(A,M))
#define ES_MINMAX(A) ES_ITEM(HAS_##A##_MIN_STATE, ES_ENUM(A,MIN)) ES_ITEM(HAS_##A##_MAX_STATE, ES_ENUM(A,MAX)) #define ES_MINMAX(A) ES_ITEM(HAS_##A##_MIN_STATE, ES_ENUM(A,MIN)) ES_ITEM(HAS_##A##_MAX_STATE, ES_ENUM(A,MAX))
#define HAS_CURRENT_HOME(N) ((N##_CURRENT_HOME > 0) && (N##_CURRENT_HOME != N##_CURRENT))
/** /**
* Basic Endstop Flag Bits: * Basic Endstop Flag Bits:
* - Each axis with an endstop gets a flag for its homing direction. * - Each axis with an endstop gets a flag for its homing direction.
@ -288,11 +286,6 @@ class Endstops {
static void clear_endstop_state(); static void clear_endstop_state();
static bool tmc_spi_homing_check(); static bool tmc_spi_homing_check();
#endif #endif
public:
// Basic functions for Sensorless Homing
#if USE_SENSORLESS
static void set_z_sensorless_current(const bool onoff);
#endif
}; };
extern Endstops endstops; extern Endstops endstops;

View file

@ -253,6 +253,560 @@ void report_current_position_projected() {
stepper.report_a_position(planner.position); stepper.report_a_position(planner.position);
} }
#if HAS_HOMING_CURRENT
#if ENABLED(DEBUG_LEVELING_FEATURE)
auto debug_current = [](FSTR_P const s, const int16_t a, const int16_t b) {
if (DEBUGGING(LEVELING)) { DEBUG_ECHOLN(s, F(" current: "), a, F(" -> "), b); }
};
#else
#define debug_current(...)
#endif
#if HAS_CURRENT_HOME(X)
int16_t saved_current_X;
#endif
#if HAS_CURRENT_HOME(Y)
int16_t saved_current_Y;
#endif
#if HAS_CURRENT_HOME(Z)
int16_t saved_current_Z;
#endif
#if HAS_CURRENT_HOME(X2)
int16_t saved_current_X2;
#endif
#if HAS_CURRENT_HOME(Y2)
int16_t saved_current_Y2;
#endif
#if HAS_CURRENT_HOME(Z2)
int16_t saved_current_Z2;
#endif
#if HAS_CURRENT_HOME(Z3)
int16_t saved_current_Z3;
#endif
#if HAS_CURRENT_HOME(Z4)
int16_t saved_current_Z4;
#endif
#if HAS_CURRENT_HOME(I)
int16_t saved_current_I;
#endif
#if HAS_CURRENT_HOME(J)
int16_t saved_current_J;
#endif
#if HAS_CURRENT_HOME(K)
int16_t saved_current_K;
#endif
#if HAS_CURRENT_HOME(U)
int16_t saved_current_U;
#endif
#if HAS_CURRENT_HOME(V)
int16_t saved_current_V;
#endif
#if HAS_CURRENT_HOME(W)
int16_t saved_current_W;
#endif
/**
* Set motors to their homing / probing currents.
* Currents are saved first so they can be restored afterward.
*/
void set_homing_current(const AxisEnum axis) {
// Saves the running current of the motor at the moment the function is called and sets current to CURRENT_HOME
#define _SAVE_SET_CURRENT(A) \
saved_current_##A = stepper##A.getMilliamps(); \
stepper##A.rms_current(A##_CURRENT_HOME); \
debug_current(F(STR_##A), saved_current_##A, A##_CURRENT_HOME)
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Setting homing driver current");
#if ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
// CORE and Markforged kinematics
switch (axis) {
default: break;
case X_AXIS: case Y_AXIS:
#if HAS_CURRENT_HOME(X)
_SAVE_SET_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(X2)
_SAVE_SET_CURRENT(X2);
#endif
#if HAS_CURRENT_HOME(Y)
_SAVE_SET_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Y2)
_SAVE_SET_CURRENT(Y2);
#endif
break;
case Z_AXIS:
#if HAS_CURRENT_HOME(Z)
_SAVE_SET_CURRENT(Z);
#endif
#if HAS_CURRENT_HOME(Z2)
_SAVE_SET_CURRENT(Z2);
#endif
#if HAS_CURRENT_HOME(Z3)
_SAVE_SET_CURRENT(Z3);
#endif
#if HAS_CURRENT_HOME(Z4)
_SAVE_SET_CURRENT(Z4);
#endif
break;
}
#elif CORE_IS_XZ
// CORE XZ / ZX
switch (axis) {
default: break;
case X_AXIS: case Z_AXIS:
#if HAS_CURRENT_HOME(X)
_SAVE_SET_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(Z)
_SAVE_SET_CURRENT(Z);
#endif
break;
case Y_AXIS:
#if HAS_CURRENT_HOME(Y)
_SAVE_SET_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Y2)
_SAVE_SET_CURRENT(Y2);
#endif
break;
}
#elif CORE_IS_YZ
// CORE YZ / ZY
switch (axis) {
default: break;
case X_AXIS:
#if HAS_CURRENT_HOME(X)
_SAVE_SET_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(X2)
_SAVE_SET_CURRENT(X2);
#endif
break;
case Y_AXIS: case Z_AXIS:
#if HAS_CURRENT_HOME(Y)
_SAVE_SET_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Z)
_SAVE_SET_CURRENT(Z);
#endif
break;
}
#elif IS_SCARA
// SCARA kinematics
switch (axis) {
default: break;
#if HAS_CURRENT_HOME(X)
case A_AXIS: _SAVE_SET_CURRENT(X); break;
#endif
#if HAS_CURRENT_HOME(Y)
case B_AXIS: _SAVE_SET_CURRENT(Y); break;
#endif
#if HAS_CURRENT_HOME(Z)
case C_AXIS: _SAVE_SET_CURRENT(Z); break;
#endif
}
#elif ANY(AXEL_TPARA, DELTA)
// TPARA or DELTA kinematics.
// Z_AXIS is a special mode to apply homing current to all axes.
#if HAS_CURRENT_HOME(X)
if (axis == A_AXIS || axis == Z_AXIS) _SAVE_SET_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(Y)
if (axis == B_AXIS || axis == Z_AXIS) _SAVE_SET_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Z)
if (axis == C_AXIS) _SAVE_SET_CURRENT(Z);
#endif
#elif ANY(POLARGRAPH, POLAR)
// POLAR kinematics
switch (axis) {
default: break;
#if HAS_CURRENT_HOME(X)
case A_AXIS: _SAVE_SET_CURRENT(X); break;
#endif
#if HAS_CURRENT_HOME(Y)
case B_AXIS: _SAVE_SET_CURRENT(Y); break;
#endif
#if HAS_CURRENT_HOME(Z)
case C_AXIS: _SAVE_SET_CURRENT(Z); break;
#endif
}
#elif defined(ARTICULATED_ROBOT_ARM)
// Articulated Robot Arm
// Useful?
switch (axis) {
default: break;
#if HAS_CURRENT_HOME(X)
case A_AXIS: _SAVE_SET_CURRENT(X); break;
#endif
#if HAS_CURRENT_HOME(Y)
case B_AXIS: _SAVE_SET_CURRENT(Y); break;
#endif
#if HAS_CURRENT_HOME(Z)
case C_AXIS: _SAVE_SET_CURRENT(Z); break;
#endif
}
#elif defined(FOAMCUTTER_XYUV)
// Foam cutter
switch (axis) {
default: break;
case X_AXIS: case I_AXIS:
#if HAS_CURRENT_HOME(X)
_SAVE_SET_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(I)
_SAVE_SET_CURRENT(I);
#endif
break;
case Y_AXIS: case J_AXIS:
#if HAS_CURRENT_HOME(Y)
_SAVE_SET_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(J)
_SAVE_SET_CURRENT(J);
#endif
break;
case Z_AXIS:
#if HAS_CURRENT_HOME(Z)
_SAVE_SET_CURRENT(Z);
#endif
break;
}
#else
// Cartesian kinematics
switch (axis) {
default: break;
case X_AXIS:
#if HAS_CURRENT_HOME(X)
_SAVE_SET_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(X2)
_SAVE_SET_CURRENT(X2);
#endif
break;
case Y_AXIS:
#if HAS_CURRENT_HOME(Y)
_SAVE_SET_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Y2)
_SAVE_SET_CURRENT(Y2);
#endif
break;
case Z_AXIS:
#if HAS_CURRENT_HOME(Z)
_SAVE_SET_CURRENT(Z);
#endif
#if HAS_CURRENT_HOME(Z2)
_SAVE_SET_CURRENT(Z2);
#endif
#if HAS_CURRENT_HOME(Z3)
_SAVE_SET_CURRENT(Z3);
#endif
#if HAS_CURRENT_HOME(Z4)
_SAVE_SET_CURRENT(Z4);
#endif
break;
}
#endif // kinematics
switch (axis) {
default: break;
#if HAS_CURRENT_HOME(I) && DISABLED(FOAMCUTTER_XYUV)
case I_AXIS: _SAVE_SET_CURRENT(I); break;
#endif
#if HAS_CURRENT_HOME(J) && DISABLED(FOAMCUTTER_XYUV)
case J_AXIS: _SAVE_SET_CURRENT(J); break;
#endif
#if HAS_CURRENT_HOME(K)
case K_AXIS: _SAVE_SET_CURRENT(K); break;
#endif
#if HAS_CURRENT_HOME(U)
case U_AXIS: _SAVE_SET_CURRENT(U); break;
#endif
#if HAS_CURRENT_HOME(V)
case V_AXIS: _SAVE_SET_CURRENT(V); break;
#endif
#if HAS_CURRENT_HOME(W)
case W_AXIS: _SAVE_SET_CURRENT(W); break;
#endif
}
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif
} // set_homing_current()
/**
* Restore motors to their previously-stored currents.
* Always call set_homing_current() first!
*/
void restore_homing_current(const AxisEnum axis) {
// Restore the saved current
#define _RESTORE_CURRENT(A) \
stepper##A.rms_current(saved_current_##A); \
debug_current(F(STR_##A), A##_CURRENT_HOME, saved_current_##A)
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Restore driver current");
#if ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
// CORE and Markforged kinematics
switch (axis) {
default: break;
case X_AXIS: case Y_AXIS:
#if HAS_CURRENT_HOME(X)
_RESTORE_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(Y)
_RESTORE_CURRENT(Y);
#endif
break;
case Z_AXIS:
#if HAS_CURRENT_HOME(Z)
_RESTORE_CURRENT(Z);
#endif
#if HAS_CURRENT_HOME(Z2)
_RESTORE_CURRENT(Z2);
#endif
#if HAS_CURRENT_HOME(Z3)
_RESTORE_CURRENT(Z3);
#endif
#if HAS_CURRENT_HOME(Z4)
_RESTORE_CURRENT(Z4);
#endif
break;
}
#elif CORE_IS_XZ
// CORE XZ / ZX
switch (axis) {
default: break;
case X_AXIS: case Z_AXIS:
#if HAS_CURRENT_HOME(X)
_RESTORE_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(Z)
_RESTORE_CURRENT(Z);
#endif
break;
case Y_AXIS:
#if HAS_CURRENT_HOME(Y)
_RESTORE_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Y2)
_RESTORE_CURRENT(Y2);
#endif
break;
}
#elif CORE_IS_YZ
// CORE YZ / ZY
switch (axis) {
default: break;
case X_AXIS:
#if HAS_CURRENT_HOME(X)
_RESTORE_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(X2)
_RESTORE_CURRENT(X2);
#endif
break;
case Y_AXIS: case Z_AXIS:
#if HAS_CURRENT_HOME(Y)
_RESTORE_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Z)
_RESTORE_CURRENT(Z);
#endif
break;
}
#elif IS_SCARA // Unsupported for now?
// SCARA kinematics
switch (axis) {
default: break;
#if HAS_CURRENT_HOME(X)
case A_AXIS: _RESTORE_CURRENT(X); break;
#endif
#if HAS_CURRENT_HOME(Y)
case B_AXIS: _RESTORE_CURRENT(Y); break;
#endif
#if HAS_CURRENT_HOME(Z)
case C_AXIS: _RESTORE_CURRENT(Z); break;
#endif
}
#elif ANY(AXEL_TPARA, DELTA)
// TPARA or DELTA kinematics
// Z_AXIS is a special mode to set homing current to all axes
#if HAS_CURRENT_HOME(X)
if (axis == A_AXIS || axis == Z_AXIS) _RESTORE_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(Y)
if (axis == B_AXIS || axis == Z_AXIS) _RESTORE_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Z)
if (axis == C_AXIS) _RESTORE_CURRENT(Z);
#endif
#elif ANY(POLARGRAPH, POLAR)
// POLAR kinematics
switch (axis) {
default: break;
#if HAS_CURRENT_HOME(X)
case A_AXIS: _RESTORE_CURRENT(X); break;
#endif
#if HAS_CURRENT_HOME(Y)
case B_AXIS: _RESTORE_CURRENT(Y); break;
#endif
#if HAS_CURRENT_HOME(Z)
case C_AXIS: _RESTORE_CURRENT(Z); break;
#endif
}
#elif ENABLED(ARTICULATED_ROBOT_ARM)
// Articulated Robot Arm
// Useful?
switch (axis) {
default: break;
#if HAS_CURRENT_HOME(X)
case A_AXIS: _RESTORE_CURRENT(X); break;
#endif
#if HAS_CURRENT_HOME(Y)
case B_AXIS: _RESTORE_CURRENT(Y); break;
#endif
#if HAS_CURRENT_HOME(Z)
case C_AXIS: _RESTORE_CURRENT(Z); break;
#endif
}
#elif ENABLED(FOAMCUTTER_XYUV)
// Foam cutter
switch (axis) {
default: break;
case X_AXIS: case I_AXIS:
#if HAS_CURRENT_HOME(X)
_RESTORE_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(I)
_RESTORE_CURRENT(I);
#endif
break;
case Y_AXIS: case J_AXIS:
#if HAS_CURRENT_HOME(Y)
_RESTORE_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(J)
_RESTORE_CURRENT(J);
#endif
break;
case Z_AXIS:
#if HAS_CURRENT_HOME(Z)
_RESTORE_CURRENT(Z);
#endif
break;
}
#else
// Cartesian kinematics
switch (axis) {
default: break;
case X_AXIS:
#if HAS_CURRENT_HOME(X)
_RESTORE_CURRENT(X);
#endif
#if HAS_CURRENT_HOME(X2)
_RESTORE_CURRENT(X2);
#endif
break;
case Y_AXIS:
#if HAS_CURRENT_HOME(Y)
_RESTORE_CURRENT(Y);
#endif
#if HAS_CURRENT_HOME(Y2)
_RESTORE_CURRENT(Y2);
#endif
break;
case Z_AXIS:
#if HAS_CURRENT_HOME(Z)
_RESTORE_CURRENT(Z);
#endif
#if HAS_CURRENT_HOME(Z2)
_RESTORE_CURRENT(Z2);
#endif
#if HAS_CURRENT_HOME(Z3)
_RESTORE_CURRENT(Z3);
#endif
#if HAS_CURRENT_HOME(Z4)
_RESTORE_CURRENT(Z4);
#endif
break;
}
#endif // kinematics
switch (axis) {
default: break;
#if HAS_CURRENT_HOME(I) && DISABLED(FOAMCUTTER_XYUV)
case I_AXIS: _RESTORE_CURRENT(I); break;
#endif
#if HAS_CURRENT_HOME(J) && DISABLED(FOAMCUTTER_XYUV)
case J_AXIS: _RESTORE_CURRENT(J); break;
#endif
#if HAS_CURRENT_HOME(K)
case K_AXIS: _RESTORE_CURRENT(K); break;
#endif
#if HAS_CURRENT_HOME(U)
case U_AXIS: _RESTORE_CURRENT(U); break;
#endif
#if HAS_CURRENT_HOME(V)
case V_AXIS: _RESTORE_CURRENT(V); break;
#endif
#if HAS_CURRENT_HOME(W)
case W_AXIS: _RESTORE_CURRENT(W); break;
#endif
}
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif
} // restore_homing_current()
#endif // HAS_HOMING_CURRENT
#if ENABLED(AUTO_REPORT_POSITION) #if ENABLED(AUTO_REPORT_POSITION)
AutoReporter<PositionReport> position_auto_reporter; AutoReporter<PositionReport> position_auto_reporter;
#endif #endif
@ -2140,6 +2694,11 @@ void prepare_line_to_destination() {
} }
#endif #endif
//
// Set a new current for the homed axis motor(s)
//
TERN_(HAS_HOMING_CURRENT, set_homing_current(axis));
// //
// Back away to prevent an early sensorless trigger // Back away to prevent an early sensorless trigger
// //
@ -2428,6 +2987,11 @@ void prepare_line_to_destination() {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< homeaxis(", C(AXIS_CHAR(axis)), ")"); if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< homeaxis(", C(AXIS_CHAR(axis)), ")");
//
// Restore axis motor(s) current after homing
//
TERN_(HAS_HOMING_CURRENT, restore_homing_current(axis));
} // homeaxis() } // homeaxis()
#endif // HAS_ENDSTOPS #endif // HAS_ENDSTOPS

View file

@ -632,3 +632,8 @@ void home_if_needed(const bool keeplev=false);
sensorless_t start_sensorless_homing_per_axis(const AxisEnum axis); sensorless_t start_sensorless_homing_per_axis(const AxisEnum axis);
void end_sensorless_homing_per_axis(const AxisEnum axis, sensorless_t enable_stealth); void end_sensorless_homing_per_axis(const AxisEnum axis, sensorless_t enable_stealth);
#endif #endif
#if HAS_HOMING_CURRENT
void set_homing_current(const AxisEnum axis);
void restore_homing_current(const AxisEnum axis);
#endif

View file

@ -627,7 +627,8 @@ bool Probe::probe_down_to_z(const_float_t z, const_feedRate_t fr_mm_s) {
#endif #endif
#endif #endif
} }
endstops.set_z_sensorless_current(true); // The "homing" current also applies to probing TERN_(IMPROVE_HOMING_RELIABILITY, planner.enable_stall_prevention(true));
endstops.enable(true); endstops.enable(true);
#endif // SENSORLESS_PROBING #endif // SENSORLESS_PROBING
@ -671,7 +672,7 @@ bool Probe::probe_down_to_z(const_float_t z, const_feedRate_t fr_mm_s) {
#endif #endif
#endif #endif
} }
endstops.set_z_sensorless_current(false); TERN_(IMPROVE_HOMING_RELIABILITY, planner.enable_stall_prevention(false));
#endif // SENSORLESS_PROBING #endif // SENSORLESS_PROBING
#if ENABLED(BLTOUCH) #if ENABLED(BLTOUCH)
@ -983,14 +984,20 @@ float Probe::probe_at_point(const_float_t rx, const_float_t ry, const ProbePtRai
// Move the probe to the starting XYZ // Move the probe to the starting XYZ
do_blocking_move_to(npos, feedRate_t(XY_PROBE_FEEDRATE_MM_S)); do_blocking_move_to(npos, feedRate_t(XY_PROBE_FEEDRATE_MM_S));
// Change Z motor current to homing current
TERN_(PROBING_USE_CURRENT_HOME, set_homing_current(Z_AXIS));
float measured_z;
#if ENABLED(BD_SENSOR) #if ENABLED(BD_SENSOR)
safe_delay(4); safe_delay(4);
return current_position.z - bdl.read(); // Difference between Z-home-relative Z and sensor reading
measured_z = current_position.z - bdl.read(); // Difference between Z-home-relative Z and sensor reading
#else // !BD_SENSOR #else // !BD_SENSOR
float measured_z = deploy() ? NAN : run_z_probe(sanity_check, z_min_point, z_clearance) + offset.z; measured_z = deploy() ? NAN : run_z_probe(sanity_check, z_min_point, z_clearance) + offset.z;
// Deploy succeeded and a successful measurement was done. // Deploy succeeded and a successful measurement was done.
// Raise and/or stow the probe depending on 'raise_after' and settings. // Raise and/or stow the probe depending on 'raise_after' and settings.
@ -1028,9 +1035,12 @@ float Probe::probe_at_point(const_float_t rx, const_float_t ry, const ProbePtRai
SERIAL_ECHOLNPGM("Bed X: ", LOGICAL_X_POSITION(rx), " Y: ", LOGICAL_Y_POSITION(ry), " Z: ", measured_z); SERIAL_ECHOLNPGM("Bed X: ", LOGICAL_X_POSITION(rx), " Y: ", LOGICAL_Y_POSITION(ry), " Z: ", measured_z);
} }
return measured_z;
#endif // !BD_SENSOR #endif // !BD_SENSOR
// Restore the Z homing current
TERN_(PROBING_USE_CURRENT_HOME, restore_homing_current(Z_AXIS));
return measured_z;
} }
#if HAS_Z_SERVO_PROBE #if HAS_Z_SERVO_PROBE

View file

@ -229,10 +229,16 @@ float segments_per_second = DEFAULT_SEGMENTS_PER_SECOND;
// Move all carriages together linearly until an endstop is hit. // Move all carriages together linearly until an endstop is hit.
//do_blocking_move_to_xy_z(pos, mlz, homing_feedrate(Z_AXIS)); //do_blocking_move_to_xy_z(pos, mlz, homing_feedrate(Z_AXIS));
// Set the homing current for all motors
TERN_(HAS_HOMING_CURRENT, set_homing_current(Z_AXIS));
current_position.set(0, 0, max_length(Z_AXIS)); current_position.set(0, 0, max_length(Z_AXIS));
line_to_current_position(homing_feedrate(Z_AXIS)); line_to_current_position(homing_feedrate(Z_AXIS));
planner.synchronize(); planner.synchronize();
// Restore the homing current for all motors
TERN_(HAS_HOMING_CURRENT, restore_homing_current(Z_AXIS));
// Re-enable stealthChop if used. Disable diag1 pin on driver. // Re-enable stealthChop if used. Disable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING) #if ENABLED(SENSORLESS_HOMING)
TERN_(X_SENSORLESS, end_sensorless_homing_per_axis(X_AXIS, stealth_states_x)); TERN_(X_SENSORLESS, end_sensorless_homing_per_axis(X_AXIS, stealth_states_x));

View file

@ -55,9 +55,10 @@ opt_set MOTHERBOARD BOARD_COHESION3D_REMIX \
NOZZLE_TO_PROBE_OFFSET '{ 0, 0, 0, 0 }' \ NOZZLE_TO_PROBE_OFFSET '{ 0, 0, 0, 0 }' \
I_MIN_PIN P1_25 \ I_MIN_PIN P1_25 \
X_CURRENT_HOME 750 Y_CURRENT_HOME 750 Z_CURRENT_HOME 750 X_CURRENT_HOME 750 Y_CURRENT_HOME 750 Z_CURRENT_HOME 750
opt_enable AUTO_BED_LEVELING_BILINEAR EEPROM_SETTINGS EEPROM_CHITCHAT MECHANICAL_GANTRY_CALIBRATION \ opt_enable EEPROM_SETTINGS EEPROM_CHITCHAT MECHANICAL_GANTRY_CALIBRATION \
TMC_USE_SW_SPI MONITOR_DRIVER_STATUS STEALTHCHOP_XY STEALTHCHOP_Z HYBRID_THRESHOLD \ TMC_USE_SW_SPI MONITOR_DRIVER_STATUS STEALTHCHOP_XY STEALTHCHOP_Z HYBRID_THRESHOLD \
SENSORLESS_PROBING SENSORLESS_HOMING Z_SAFE_HOMING X_STALL_SENSITIVITY Y_STALL_SENSITIVITY Z_STALL_SENSITIVITY TMC_DEBUG \ SENSORLESS_HOMING Z_SAFE_HOMING X_STALL_SENSITIVITY Y_STALL_SENSITIVITY Z_STALL_SENSITIVITY TMC_DEBUG \
AUTO_BED_LEVELING_BILINEAR SENSORLESS_PROBING PROBING_USE_CURRENT_HOME \
AXIS4_ROTATES I_MIN_POS I_MAX_POS I_HOME_DIR I_ENABLE_ON INVERT_I_DIR \ AXIS4_ROTATES I_MIN_POS I_MAX_POS I_HOME_DIR I_ENABLE_ON INVERT_I_DIR \
EXPERIMENTAL_I2CBUS EXPERIMENTAL_I2CBUS
opt_disable PSU_CONTROL Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN opt_disable PSU_CONTROL Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN