TMC SPI Endstops and Improved Sensorless Homing (#14044)
This commit is contained in:
parent
d493cafc4a
commit
d4974ea719
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -140,6 +140,9 @@ class TMCMarlin : public TMC, public TMCStorage<AXIS_LETTER, DRIVER_ID> {
|
||||
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<class TMC, char AXIS_LETTER, char DRIVER_ID, AxisEnum AXIS_ID>
|
||||
bool TMCMarlin<TMC, AXIS_LETTER, DRIVER_ID, AXIS_ID>::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();
|
||||
|
@ -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);
|
||||
sensorless_t stealth_states {
|
||||
tmc_enable_stallguard(stepperX)
|
||||
, tmc_enable_stallguard(stepperY)
|
||||
, false
|
||||
, false
|
||||
#if AXIS_HAS_STALLGUARD(X2)
|
||||
stealth_states.x2 = tmc_enable_stallguard(stepperX2);
|
||||
|| tmc_enable_stallguard(stepperX2)
|
||||
#endif
|
||||
, false
|
||||
#if AXIS_HAS_STALLGUARD(Y2)
|
||||
stealth_states.y2 = tmc_enable_stallguard(stepperY2);
|
||||
|| 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();
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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], ")");
|
||||
|
Loading…
Reference in New Issue
Block a user