From d4974ea719ceba2d76a678f77942d0b92e8fecb8 Mon Sep 17 00:00:00 2001 From: teemuatlut Date: Mon, 5 Aug 2019 06:22:58 +0300 Subject: [PATCH] TMC SPI Endstops and Improved Sensorless Homing (#14044) --- Marlin/Configuration_adv.h | 24 ++++++++---- Marlin/src/Marlin.cpp | 7 ++++ Marlin/src/feature/tmc_util.h | 53 ++++++++++++++++++++++++- Marlin/src/gcode/calibrate/G28.cpp | 54 +++++++++++++++++++++----- Marlin/src/inc/Conditionals_post.h | 5 +++ Marlin/src/module/delta.cpp | 9 +++-- Marlin/src/module/endstops.cpp | 62 +++++++++++++++++++++++++++--- Marlin/src/module/endstops.h | 12 ++++++ Marlin/src/module/motion.cpp | 54 +++++++++++++++++++++++++- 9 files changed, 251 insertions(+), 29 deletions(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 634ebffdba..23ce491218 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -1851,17 +1851,25 @@ #define E5_HYBRID_THRESHOLD 30 /** + * Use StallGuard2 to home / probe X, Y, Z. + * * TMC2130, TMC2160, TMC2209, TMC2660, TMC5130, and TMC5160 only - * Use StallGuard2 to sense an obstacle and trigger an endstop. * Connect the stepper driver's DIAG1 pin to the X/Y endstop pin. * X, Y, and Z homing will always be done in spreadCycle mode. * - * X/Y/Z_STALL_SENSITIVITY is used for tuning the trigger sensitivity. - * Higher values make the system LESS sensitive. - * Lower value make the system MORE sensitive. - * Too low values can lead to false positives, while too high values will collide the axis without triggering. - * It is advised to set X/Y/Z_HOME_BUMP_MM to 0. - * M914 X/Y/Z to live tune the setting + * X/Y/Z_STALL_SENSITIVITY is used to tune the trigger sensitivity. + * Use M914 X Y Z to live-adjust the sensitivity. + * Higher: LESS sensitive. (Too high => failure to trigger) + * Lower: MORE sensitive. (Too low => false positives) + * + * It is recommended to set [XYZ]_HOME_BUMP_MM to 0. + * + * SPI_ENDSTOPS *** Beta feature! *** TMC2130 Only *** + * Poll the driver through SPI to determine load when homing. + * Removes the need for a wire from DIAG1 to an endstop pin. + * + * IMPROVE_HOMING_RELIABILITY tunes acceleration and jerk when + * homing and adds a guard period for endstop triggering. */ //#define SENSORLESS_HOMING // StallGuard capable drivers only @@ -1878,6 +1886,8 @@ #define X_STALL_SENSITIVITY 8 #define Y_STALL_SENSITIVITY 8 //#define Z_STALL_SENSITIVITY 8 + //#define SPI_ENDSTOPS // TMC2130 only + //#define IMPROVE_HOMING_RELIABILITY #endif /** diff --git a/Marlin/src/Marlin.cpp b/Marlin/src/Marlin.cpp index d1f5fe53b6..8e2aa56a8f 100644 --- a/Marlin/src/Marlin.cpp +++ b/Marlin/src/Marlin.cpp @@ -659,6 +659,13 @@ void idle( bool no_stepper_sleep/*=false*/ #endif ) { + + #if ENABLED(SPI_ENDSTOPS) + if (endstops.tmc_spi_homing.any && ELAPSED(millis(), sg_guard_period)) + for (uint8_t i = 4; i--;) // Read SGT 4 times per idle loop + if (endstops.tmc_spi_homing_check()) break; + #endif + #if ENABLED(MAX7219_DEBUG) max7219.idle_tasks(); #endif diff --git a/Marlin/src/feature/tmc_util.h b/Marlin/src/feature/tmc_util.h index a3eb49a1e0..b5b45089b9 100644 --- a/Marlin/src/feature/tmc_util.h +++ b/Marlin/src/feature/tmc_util.h @@ -140,6 +140,9 @@ class TMCMarlin : public TMC, public TMCStorage { this->stored.homing_thrs = sgt_val; #endif } + #if ENABLED(SPI_ENDSTOPS) + bool test_stall_status(); + #endif #endif #if HAS_LCD_MENU @@ -355,9 +358,22 @@ void test_tmc_connection(const bool test_x, const bool test_y, const bool test_z * Defined here because of limitations with templates and headers. */ #if USE_SENSORLESS + // Track enabled status of stealthChop and only re-enable where applicable struct sensorless_t { bool x, y, z, x2, y2, z2, z3; }; + #if ENABLED(IMPROVE_HOMING_RELIABILITY) + extern millis_t sg_guard_period; + constexpr uint16_t default_sg_guard_duration = 400; + + struct slow_homing_t { + struct { uint32_t x, y; } acceleration; + #if HAS_CLASSIC_JERK + struct { float x, y; } jerk; + #endif + }; + #endif + bool tmc_enable_stallguard(TMC2130Stepper &st); void tmc_disable_stallguard(TMC2130Stepper &st, const bool restore_stealth); @@ -366,7 +382,42 @@ void test_tmc_connection(const bool test_x, const bool test_y, const bool test_z bool tmc_enable_stallguard(TMC2660Stepper); void tmc_disable_stallguard(TMC2660Stepper, const bool); -#endif + + #if ENABLED(SPI_ENDSTOPS) + + template + bool TMCMarlin::test_stall_status() { + uint16_t sg_result = 0; + + this->switchCSpin(LOW); + + if (this->TMC_SW_SPI != nullptr) { + this->TMC_SW_SPI->transfer(TMC2130_n::DRV_STATUS_t::address); + this->TMC_SW_SPI->transfer16(0); + // We only care about the last 10 bits + sg_result = this->TMC_SW_SPI->transfer(0); + sg_result <<= 8; + sg_result |= this->TMC_SW_SPI->transfer(0); + } + else { + SPI.beginTransaction(SPISettings(16000000/8, MSBFIRST, SPI_MODE3)); + // Read DRV_STATUS + SPI.transfer(TMC2130_n::DRV_STATUS_t::address); + SPI.transfer16(0); + // We only care about the last 10 bits + sg_result = SPI.transfer(0); + sg_result <<= 8; + sg_result |= SPI.transfer(0); + SPI.endTransaction(); + } + this->switchCSpin(HIGH); + + return (sg_result & 0x3FF) == 0; + } + + #endif // SPI_ENDSTOPS + +#endif // USE_SENSORLESS #if TMC_HAS_SPI void tmc_init_cs_pins(); diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp index 039bd3d1a3..d936809051 100644 --- a/Marlin/src/gcode/calibrate/G28.cpp +++ b/Marlin/src/gcode/calibrate/G28.cpp @@ -78,15 +78,19 @@ fr_mm_s = _MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)) * SQRT(sq(mlratio) + 1.0); #if ENABLED(SENSORLESS_HOMING) - sensorless_t stealth_states { false }; - stealth_states.x = tmc_enable_stallguard(stepperX); - stealth_states.y = tmc_enable_stallguard(stepperY); - #if AXIS_HAS_STALLGUARD(X2) - stealth_states.x2 = tmc_enable_stallguard(stepperX2); - #endif - #if AXIS_HAS_STALLGUARD(Y2) - stealth_states.y2 = tmc_enable_stallguard(stepperY2); - #endif + sensorless_t stealth_states { + tmc_enable_stallguard(stepperX) + , tmc_enable_stallguard(stepperY) + , false + , false + #if AXIS_HAS_STALLGUARD(X2) + || tmc_enable_stallguard(stepperX2) + #endif + , false + #if AXIS_HAS_STALLGUARD(Y2) + || tmc_enable_stallguard(stepperY2) + #endif + }; #endif do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * home_dir(Y_AXIS), fr_mm_s); @@ -229,6 +233,22 @@ void GcodeSuite::G28(const bool always_home_all) { workspace_plane = PLANE_XY; #endif + #if ENABLED(IMPROVE_HOMING_RELIABILITY) + slow_homing_t slow_homing { 0 }; + slow_homing.acceleration.x = planner.settings.max_acceleration_mm_per_s2[X_AXIS]; + slow_homing.acceleration.y = planner.settings.max_acceleration_mm_per_s2[Y_AXIS]; + planner.settings.max_acceleration_mm_per_s2[X_AXIS] = 100; + planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = 100; + #if HAS_CLASSIC_JERK + slow_homing.jerk.x = planner.max_jerk[X_AXIS]; + slow_homing.jerk.y = planner.max_jerk[Y_AXIS]; + planner.max_jerk[X_AXIS] = 0; + planner.max_jerk[Y_AXIS] = 0; + #endif + + planner.reset_acceleration_rates(); + #endif + // Always home with tool 0 active #if HOTENDS > 1 #if DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE) @@ -393,6 +413,11 @@ void GcodeSuite::G28(const bool always_home_all) { endstops.not_homing(); + // Clear endstop state for polled stallGuard endstops + #if ENABLED(SPI_ENDSTOPS) + endstops.clear_endstop_state(); + #endif + #if BOTH(DELTA, DELTA_HOME_TO_SAFE_ZONE) // move to a height where we can use the full xy-area do_blocking_move_to_z(delta_clip_start_height); @@ -414,6 +439,17 @@ void GcodeSuite::G28(const bool always_home_all) { tool_change(old_tool_index, NO_FETCH); #endif + #if ENABLED(IMPROVE_HOMING_RELIABILITY) + planner.settings.max_acceleration_mm_per_s2[X_AXIS] = slow_homing.acceleration.x; + planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = slow_homing.acceleration.y; + #if HAS_CLASSIC_JERK + planner.max_jerk[X_AXIS] = slow_homing.jerk.x; + planner.max_jerk[Y_AXIS] = slow_homing.jerk.y; + #endif + + planner.reset_acceleration_rates(); + #endif + ui.refresh(); report_current_position(); diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h index b9216ddd0e..c6a2efbd59 100644 --- a/Marlin/src/inc/Conditionals_post.h +++ b/Marlin/src/inc/Conditionals_post.h @@ -920,6 +920,11 @@ #define X_SENSORLESS (AXIS_HAS_STALLGUARD(X) && defined(X_STALL_SENSITIVITY)) #define Y_SENSORLESS (AXIS_HAS_STALLGUARD(Y) && defined(Y_STALL_SENSITIVITY)) #define Z_SENSORLESS (AXIS_HAS_STALLGUARD(Z) && defined(Z_STALL_SENSITIVITY)) + #if ENABLED(SPI_ENDSTOPS) + #define X_SPI_SENSORLESS X_SENSORLESS + #define Y_SPI_SENSORLESS Y_SENSORLESS + #define Z_SPI_SENSORLESS Z_SENSORLESS + #endif #endif // Endstops and bed probe diff --git a/Marlin/src/module/delta.cpp b/Marlin/src/module/delta.cpp index 51f20b2519..88485605b9 100644 --- a/Marlin/src/module/delta.cpp +++ b/Marlin/src/module/delta.cpp @@ -227,10 +227,11 @@ void home_delta() { // Disable stealthChop if used. Enable diag1 pin on driver. #if ENABLED(SENSORLESS_HOMING) - sensorless_t stealth_states { false }; - stealth_states.x = tmc_enable_stallguard(stepperX); - stealth_states.y = tmc_enable_stallguard(stepperY); - stealth_states.z = tmc_enable_stallguard(stepperZ); + sensorless_t stealth_states { + tmc_enable_stallguard(stepperX), + tmc_enable_stallguard(stepperY), + tmc_enable_stallguard(stepperZ) + }; #endif // Move all carriages together linearly until an endstop is hit. diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 9aaab38dd2..b1a4f9e57f 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -76,6 +76,13 @@ Endstops::esbits_t Endstops::live_state = 0; float Endstops::z3_endstop_adj; #endif +#if ENABLED(SPI_ENDSTOPS) + Endstops::tmc_spi_homing_t Endstops::tmc_spi_homing; // = 0 +#endif +#if ENABLED(IMPROVE_HOMING_RELIABILITY) + millis_t sg_guard_period; // = 0 +#endif + /** * Class and Instance Methods */ @@ -699,7 +706,7 @@ void Endstops::update() { // Now, we must signal, after validation, if an endstop limit is pressed or not if (stepper.axis_is_moving(X_AXIS)) { if (stepper.motor_direction(X_AXIS_HEAD)) { // -direction - #if HAS_X_MIN + #if HAS_X_MIN || (X_SPI_SENSORLESS && X_HOME_DIR < 0) #if ENABLED(X_DUAL_ENDSTOPS) PROCESS_DUAL_ENDSTOP(X, X2, MIN); #else @@ -708,7 +715,7 @@ void Endstops::update() { #endif } else { // +direction - #if HAS_X_MAX + #if HAS_X_MAX || (X_SPI_SENSORLESS && X_HOME_DIR > 0) #if ENABLED(X_DUAL_ENDSTOPS) PROCESS_DUAL_ENDSTOP(X, X2, MAX); #else @@ -720,7 +727,7 @@ void Endstops::update() { if (stepper.axis_is_moving(Y_AXIS)) { if (stepper.motor_direction(Y_AXIS_HEAD)) { // -direction - #if HAS_Y_MIN + #if HAS_Y_MIN || (Y_SPI_SENSORLESS && Y_HOME_DIR < 0) #if ENABLED(Y_DUAL_ENDSTOPS) PROCESS_DUAL_ENDSTOP(Y, Y2, MIN); #else @@ -729,7 +736,7 @@ void Endstops::update() { #endif } else { // +direction - #if HAS_Y_MAX + #if HAS_Y_MAX || (Y_SPI_SENSORLESS && Y_HOME_DIR > 0) #if ENABLED(Y_DUAL_ENDSTOPS) PROCESS_DUAL_ENDSTOP(Y, Y2, MAX); #else @@ -741,7 +748,7 @@ void Endstops::update() { if (stepper.axis_is_moving(Z_AXIS)) { if (stepper.motor_direction(Z_AXIS_HEAD)) { // Z -direction. Gantry down, bed up. - #if HAS_Z_MIN + #if HAS_Z_MIN || (Z_SPI_SENSORLESS && Z_HOME_DIR < 0) #if ENABLED(Z_TRIPLE_ENDSTOPS) PROCESS_TRIPLE_ENDSTOP(Z, Z2, Z3, MIN); #elif ENABLED(Z_DUAL_ENDSTOPS) @@ -763,7 +770,7 @@ void Endstops::update() { #endif } else { // Z +direction. Gantry up, bed down. - #if HAS_Z_MAX + #if HAS_Z_MAX || (Z_SPI_SENSORLESS && Z_HOME_DIR > 0) #if ENABLED(Z_TRIPLE_ENDSTOPS) PROCESS_TRIPLE_ENDSTOP(Z, Z2, Z3, MAX); #elif ENABLED(Z_DUAL_ENDSTOPS) @@ -778,6 +785,49 @@ void Endstops::update() { } } // Endstops::update() +#if ENABLED(SPI_ENDSTOPS) + + #define X_STOP (X_HOME_DIR < 0 ? X_MIN : X_MAX) + #define Y_STOP (Y_HOME_DIR < 0 ? Y_MIN : Y_MAX) + #define Z_STOP (Z_HOME_DIR < 0 ? Z_MIN : Z_MAX) + + bool Endstops::tmc_spi_homing_check() { + bool hit = false; + #if X_SPI_SENSORLESS + if (tmc_spi_homing.x && stepperX.test_stall_status()) { + SBI(live_state, X_STOP); + hit = true; + } + #endif + #if Y_SPI_SENSORLESS + if (tmc_spi_homing.y && stepperY.test_stall_status()) { + SBI(live_state, Y_STOP); + hit = true; + } + #endif + #if Z_SPI_SENSORLESS + if (tmc_spi_homing.z && stepperZ.test_stall_status()) { + SBI(live_state, Z_STOP); + hit = true; + } + #endif + return hit; + } + + void Endstops::clear_endstop_state() { + #if X_SPI_SENSORLESS + CBI(live_state, X_STOP); + #endif + #if Y_SPI_SENSORLESS + CBI(live_state, Y_STOP); + #endif + #if Z_SPI_SENSORLESS + CBI(live_state, Z_STOP); + #endif + } + +#endif // SPI_ENDSTOPS + #if ENABLED(PINS_DEBUGGING) bool Endstops::monitor_flag = false; diff --git a/Marlin/src/module/endstops.h b/Marlin/src/module/endstops.h index 604befdbb9..43893e3b8a 100644 --- a/Marlin/src/module/endstops.h +++ b/Marlin/src/module/endstops.h @@ -161,6 +161,18 @@ class Endstops { static void monitor(); static void run_monitor(); #endif + + #if ENABLED(SPI_ENDSTOPS) + typedef struct { + union { + bool any; + struct { bool x:1, y:1, z:1; }; + }; + } tmc_spi_homing_t; + static tmc_spi_homing_t tmc_spi_homing; + static void clear_endstop_state(); + static bool tmc_spi_homing_check(); + #endif }; extern Endstops endstops; diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 0216a658c5..85d9362ef8 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1121,6 +1121,26 @@ float get_homing_bump_feedrate(const AxisEnum axis) { break; #endif } + + #if ENABLED(SPI_ENDSTOPS) + switch (axis) { + #if X_SPI_SENSORLESS + case X_AXIS: endstops.tmc_spi_homing.x = true; break; + #endif + #if Y_SPI_SENSORLESS + case Y_AXIS: endstops.tmc_spi_homing.y = true; break; + #endif + #if Z_SPI_SENSORLESS + case Z_AXIS: endstops.tmc_spi_homing.z = true; break; + #endif + default: break; + } + #endif + + #if ENABLED(IMPROVE_HOMING_RELIABILITY) + sg_guard_period = millis() + default_sg_guard_duration; + #endif + return stealth_states; } @@ -1170,6 +1190,21 @@ float get_homing_bump_feedrate(const AxisEnum axis) { break; #endif } + + #if ENABLED(SPI_ENDSTOPS) + switch (axis) { + #if X_SPI_SENSORLESS + case X_AXIS: endstops.tmc_spi_homing.x = false; break; + #endif + #if Y_SPI_SENSORLESS + case Y_AXIS: endstops.tmc_spi_homing.y = false; break; + #endif + #if Z_SPI_SENSORLESS + case Z_AXIS: endstops.tmc_spi_homing.z = false; break; + #endif + default: break; + } + #endif } #endif // SENSORLESS_HOMING @@ -1383,9 +1418,24 @@ void homeaxis(const AxisEnum axis) { // Only Z homing (with probe) is permitted if (axis != Z_AXIS) { BUZZ(100, 880); return; } #else - #define CAN_HOME(A) \ + #define _CAN_HOME(A) \ (axis == _AXIS(A) && ((A##_MIN_PIN > -1 && A##_HOME_DIR < 0) || (A##_MAX_PIN > -1 && A##_HOME_DIR > 0))) - if (!CAN_HOME(X) && !CAN_HOME(Y) && !CAN_HOME(Z)) return; + #if X_SPI_SENSORLESS + #define CAN_HOME_X true + #else + #define CAN_HOME_X _CAN_HOME(X) + #endif + #if Y_SPI_SENSORLESS + #define CAN_HOME_Y true + #else + #define CAN_HOME_Y _CAN_HOME(Y) + #endif + #if Z_SPI_SENSORLESS + #define CAN_HOME_Z true + #else + #define CAN_HOME_Z _CAN_HOME(Z) + #endif + if (!CAN_HOME_X && !CAN_HOME_Y && !CAN_HOME_Z) return; #endif if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(">>> homeaxis(", axis_codes[axis], ")");