Limited backlash editing with Core kinematics (#17281)

This commit is contained in:
Scott Lahteine 2020-03-27 22:00:27 -05:00 committed by GitHub
parent de648bfdc1
commit 53fe572bbd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 78 deletions

View file

@ -255,28 +255,28 @@ void GcodeSuite::G28() {
#define HAS_HOMING_CURRENT (HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2)) #define HAS_HOMING_CURRENT (HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2))
#if HAS_HOMING_CURRENT #if HAS_HOMING_CURRENT
auto debug_current = [](const char * const s, const int16_t a, const int16_t b){ auto debug_current = [](PGM_P const s, const int16_t a, const int16_t b){
DEBUG_ECHO(s); DEBUG_ECHOLNPAIR(" current: ", a, " -> ", b); serialprintPGM(s); DEBUG_ECHOLNPAIR(" current: ", a, " -> ", b);
}; };
#if HAS_CURRENT_HOME(X) #if HAS_CURRENT_HOME(X)
const int16_t tmc_save_current_X = stepperX.getMilliamps(); const int16_t tmc_save_current_X = stepperX.getMilliamps();
stepperX.rms_current(X_CURRENT_HOME); stepperX.rms_current(X_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current("X", tmc_save_current_X, X_CURRENT_HOME); if (DEBUGGING(LEVELING)) debug_current(PSTR("X"), tmc_save_current_X, X_CURRENT_HOME);
#endif #endif
#if HAS_CURRENT_HOME(X2) #if HAS_CURRENT_HOME(X2)
const int16_t tmc_save_current_X2 = stepperX2.getMilliamps(); const int16_t tmc_save_current_X2 = stepperX2.getMilliamps();
stepperX2.rms_current(X2_CURRENT_HOME); stepperX2.rms_current(X2_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current("X2", tmc_save_current_X2, X2_CURRENT_HOME); if (DEBUGGING(LEVELING)) debug_current(PSTR("X2"), tmc_save_current_X2, X2_CURRENT_HOME);
#endif #endif
#if HAS_CURRENT_HOME(Y) #if HAS_CURRENT_HOME(Y)
const int16_t tmc_save_current_Y = stepperY.getMilliamps(); const int16_t tmc_save_current_Y = stepperY.getMilliamps();
stepperY.rms_current(Y_CURRENT_HOME); stepperY.rms_current(Y_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current("Y", tmc_save_current_Y, Y_CURRENT_HOME); if (DEBUGGING(LEVELING)) debug_current(PSTR("Y"), tmc_save_current_Y, Y_CURRENT_HOME);
#endif #endif
#if HAS_CURRENT_HOME(Y2) #if HAS_CURRENT_HOME(Y2)
const int16_t tmc_save_current_Y2 = stepperY2.getMilliamps(); const int16_t tmc_save_current_Y2 = stepperY2.getMilliamps();
stepperY2.rms_current(Y2_CURRENT_HOME); stepperY2.rms_current(Y2_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current("Y2", tmc_save_current_Y2, Y2_CURRENT_HOME); if (DEBUGGING(LEVELING)) debug_current(PSTR("Y2"), tmc_save_current_Y2, Y2_CURRENT_HOME);
#endif #endif
#endif #endif
@ -345,13 +345,9 @@ void GcodeSuite::G28() {
#endif #endif
// Home Y (before X) // Home Y (before X)
#if ENABLED(HOME_Y_BEFORE_X) if (ENABLED(HOME_Y_BEFORE_X) && (doY || (ENABLED(CODEPENDENT_XY_HOMING) && doX)))
if (doY || (doX && ENABLED(CODEPENDENT_XY_HOMING)))
homeaxis(Y_AXIS); homeaxis(Y_AXIS);
#endif
// Home X // Home X
if (doX || (doY && ENABLED(CODEPENDENT_XY_HOMING) && DISABLED(HOME_Y_BEFORE_X))) { if (doX || (doY && ENABLED(CODEPENDENT_XY_HOMING) && DISABLED(HOME_Y_BEFORE_X))) {

View file

@ -37,6 +37,21 @@
#include "../../module/endstops.h" #include "../../module/endstops.h"
#include "../../feature/bedlevel/bedlevel.h" #include "../../feature/bedlevel/bedlevel.h"
#if !AXIS_CAN_CALIBRATE(X)
#undef CALIBRATION_MEASURE_LEFT
#undef CALIBRATION_MEASURE_RIGHT
#endif
#if !AXIS_CAN_CALIBRATE(Y)
#undef CALIBRATION_MEASURE_FRONT
#undef CALIBRATION_MEASURE_BACK
#endif
#if !AXIS_CAN_CALIBRATE(Z)
#undef CALIBRATION_MEASURE_AT_TOP_EDGES
#endif
/** /**
* G425 backs away from the calibration object by various distances * G425 backs away from the calibration object by various distances
* depending on the confidence level: * depending on the confidence level:
@ -207,42 +222,52 @@ inline float measure(const AxisEnum axis, const int dir, const bool stop_state,
inline void probe_side(measurements_t &m, const float uncertainty, const side_t side, const bool probe_top_at_edge=false) { inline void probe_side(measurements_t &m, const float uncertainty, const side_t side, const bool probe_top_at_edge=false) {
const xyz_float_t dimensions = CALIBRATION_OBJECT_DIMENSIONS; const xyz_float_t dimensions = CALIBRATION_OBJECT_DIMENSIONS;
AxisEnum axis; AxisEnum axis;
float dir; float dir = 1;
park_above_object(m, uncertainty); park_above_object(m, uncertainty);
switch (side) { switch (side) {
#if AXIS_CAN_CALIBRATE(Z)
case TOP: { case TOP: {
const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty); const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
m.obj_center.z = measurement - dimensions.z / 2; m.obj_center.z = measurement - dimensions.z / 2;
m.obj_side[TOP] = measurement; m.obj_side[TOP] = measurement;
return; return;
} }
#endif
#if AXIS_CAN_CALIBRATE(X)
case LEFT: axis = X_AXIS; break;
case RIGHT: axis = X_AXIS; dir = -1; break; case RIGHT: axis = X_AXIS; dir = -1; break;
case FRONT: axis = Y_AXIS; dir = 1; break; #endif
case LEFT: axis = X_AXIS; dir = 1; break; #if AXIS_CAN_CALIBRATE(Y)
case FRONT: axis = Y_AXIS; break;
case BACK: axis = Y_AXIS; dir = -1; break; case BACK: axis = Y_AXIS; dir = -1; break;
#endif
default: return; default: return;
} }
if (probe_top_at_edge) { if (probe_top_at_edge) {
#if AXIS_CAN_CALIBRATE(Z)
// Probe top nearest the side we are probing // Probe top nearest the side we are probing
current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 - m.nozzle_outer_dimension[axis]); current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 - m.nozzle_outer_dimension[axis]);
calibration_move(); calibration_move();
m.obj_side[TOP] = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty); m.obj_side[TOP] = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
m.obj_center.z = m.obj_side[TOP] - dimensions.z / 2; m.obj_center.z = m.obj_side[TOP] - dimensions.z / 2;
#endif
} }
if (AXIS_CAN_CALIBRATE(X) && axis == X_AXIS || AXIS_CAN_CALIBRATE(Y) && axis == Y_AXIS) {
// Move to safe distance to the side of the calibration object // Move to safe distance to the side of the calibration object
current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty); current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty);
calibration_move(); calibration_move();
// Plunge below the side of the calibration object and measure // Plunge below the side of the calibration object and measure
current_position.z = m.obj_side[TOP] - CALIBRATION_NOZZLE_TIP_HEIGHT * 0.7; current_position.z = m.obj_side[TOP] - (CALIBRATION_NOZZLE_TIP_HEIGHT) * 0.7f;
calibration_move(); calibration_move();
const float measurement = measure(axis, dir, true, &m.backlash[side], uncertainty); const float measurement = measure(axis, dir, true, &m.backlash[side], uncertainty);
m.obj_center[axis] = measurement + dir * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2); m.obj_center[axis] = measurement + dir * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2);
m.obj_side[side] = measurement; m.obj_side[side] = measurement;
}
} }
/** /**
@ -252,7 +277,7 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
* uncertainty in - How far away from the calibration object to begin probing * uncertainty in - How far away from the calibration object to begin probing
*/ */
inline void probe_sides(measurements_t &m, const float uncertainty) { inline void probe_sides(measurements_t &m, const float uncertainty) {
#ifdef CALIBRATION_MEASURE_AT_TOP_EDGES #if ENABLED(CALIBRATION_MEASURE_AT_TOP_EDGES)
constexpr bool probe_top_at_edge = true; constexpr bool probe_top_at_edge = true;
#else #else
// Probing at the exact center only works if the center is flat. Probing on a washer // Probing at the exact center only works if the center is flat. Probing on a washer
@ -261,18 +286,18 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
probe_side(m, uncertainty, TOP); probe_side(m, uncertainty, TOP);
#endif #endif
#ifdef CALIBRATION_MEASURE_RIGHT #if ENABLED(CALIBRATION_MEASURE_RIGHT)
probe_side(m, uncertainty, RIGHT, probe_top_at_edge); probe_side(m, uncertainty, RIGHT, probe_top_at_edge);
#endif #endif
#ifdef CALIBRATION_MEASURE_FRONT #if ENABLED(CALIBRATION_MEASURE_FRONT)
probe_side(m, uncertainty, FRONT, probe_top_at_edge); probe_side(m, uncertainty, FRONT, probe_top_at_edge);
#endif #endif
#ifdef CALIBRATION_MEASURE_LEFT #if ENABLED(CALIBRATION_MEASURE_LEFT)
probe_side(m, uncertainty, LEFT, probe_top_at_edge); probe_side(m, uncertainty, LEFT, probe_top_at_edge);
#endif #endif
#ifdef CALIBRATION_MEASURE_BACK #if ENABLED(CALIBRATION_MEASURE_BACK)
probe_side(m, uncertainty, BACK, probe_top_at_edge); probe_side(m, uncertainty, BACK, probe_top_at_edge);
#endif #endif
@ -313,7 +338,9 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
#if ENABLED(CALIBRATION_REPORTING) #if ENABLED(CALIBRATION_REPORTING)
inline void report_measured_faces(const measurements_t &m) { inline void report_measured_faces(const measurements_t &m) {
SERIAL_ECHOLNPGM("Sides:"); SERIAL_ECHOLNPGM("Sides:");
#if AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPAIR(" Top: ", m.obj_side[TOP]); SERIAL_ECHOLNPAIR(" Top: ", m.obj_side[TOP]);
#endif
#if ENABLED(CALIBRATION_MEASURE_LEFT) #if ENABLED(CALIBRATION_MEASURE_LEFT)
SERIAL_ECHOLNPAIR(" Left: ", m.obj_side[LEFT]); SERIAL_ECHOLNPAIR(" Left: ", m.obj_side[LEFT]);
#endif #endif
@ -343,19 +370,25 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
inline void report_measured_backlash(const measurements_t &m) { inline void report_measured_backlash(const measurements_t &m) {
SERIAL_ECHOLNPGM("Backlash:"); SERIAL_ECHOLNPGM("Backlash:");
#if AXIS_CAN_CALIBRATE(X)
#if ENABLED(CALIBRATION_MEASURE_LEFT) #if ENABLED(CALIBRATION_MEASURE_LEFT)
SERIAL_ECHOLNPAIR(" Left: ", m.backlash[LEFT]); SERIAL_ECHOLNPAIR(" Left: ", m.backlash[LEFT]);
#endif #endif
#if ENABLED(CALIBRATION_MEASURE_RIGHT) #if ENABLED(CALIBRATION_MEASURE_RIGHT)
SERIAL_ECHOLNPAIR(" Right: ", m.backlash[RIGHT]); SERIAL_ECHOLNPAIR(" Right: ", m.backlash[RIGHT]);
#endif #endif
#endif
#if AXIS_CAN_CALIBRATE(Y)
#if ENABLED(CALIBRATION_MEASURE_FRONT) #if ENABLED(CALIBRATION_MEASURE_FRONT)
SERIAL_ECHOLNPAIR(" Front: ", m.backlash[FRONT]); SERIAL_ECHOLNPAIR(" Front: ", m.backlash[FRONT]);
#endif #endif
#if ENABLED(CALIBRATION_MEASURE_BACK) #if ENABLED(CALIBRATION_MEASURE_BACK)
SERIAL_ECHOLNPAIR(" Back: ", m.backlash[BACK]); SERIAL_ECHOLNPAIR(" Back: ", m.backlash[BACK]);
#endif #endif
#endif
#if AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPAIR(" Top: ", m.backlash[TOP]); SERIAL_ECHOLNPAIR(" Top: ", m.backlash[TOP]);
#endif
SERIAL_EOL(); SERIAL_EOL();
} }
@ -369,7 +402,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
#if HAS_Y_CENTER #if HAS_Y_CENTER
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.pos_error.y); SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.pos_error.y);
#endif #endif
SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.pos_error.z); if (AXIS_CAN_CALIBRATE(Z)) SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.pos_error.z);
SERIAL_EOL(); SERIAL_EOL();
} }
@ -417,6 +450,7 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
probe_sides(m, uncertainty); probe_sides(m, uncertainty);
#if ENABLED(BACKLASH_GCODE) #if ENABLED(BACKLASH_GCODE)
#if HAS_X_CENTER #if HAS_X_CENTER
backlash.distance_mm.x = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2; backlash.distance_mm.x = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2;
#elif ENABLED(CALIBRATION_MEASURE_LEFT) #elif ENABLED(CALIBRATION_MEASURE_LEFT)
@ -433,18 +467,18 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
backlash.distance_mm.y = m.backlash[BACK]; backlash.distance_mm.y = m.backlash[BACK];
#endif #endif
backlash.distance_mm.z = m.backlash[TOP]; if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP];
#endif #endif
} }
#if ENABLED(BACKLASH_GCODE) #if ENABLED(BACKLASH_GCODE)
// Turn on backlash compensation and move in all // Turn on backlash compensation and move in all
// directions to take up any backlash // allowed directions to take up any backlash
{ {
// New scope for TEMPORARY_BACKLASH_CORRECTION // New scope for TEMPORARY_BACKLASH_CORRECTION
TEMPORARY_BACKLASH_CORRECTION(all_on); TEMPORARY_BACKLASH_CORRECTION(all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f); TEMPORARY_BACKLASH_SMOOTHING(0.0f);
const xyz_float_t move = { 3, 3, 3 }; const xyz_float_t move = { AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3 };
current_position += move; calibration_move(); current_position += move; calibration_move();
current_position -= move; calibration_move(); current_position -= move; calibration_move();
} }
@ -482,26 +516,18 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
// Adjust the hotend offset // Adjust the hotend offset
#if HAS_HOTEND_OFFSET #if HAS_HOTEND_OFFSET
#if HAS_X_CENTER if (ENABLED(HAS_X_CENTER) && AXIS_CAN_CALIBRATE(X)) hotend_offset[extruder].x += m.pos_error.x;
hotend_offset[extruder].x += m.pos_error.x; if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) hotend_offset[extruder].y += m.pos_error.y;
#endif if (AXIS_CAN_CALIBRATE(Z)) hotend_offset[extruder].z += m.pos_error.z;
#if HAS_Y_CENTER
hotend_offset[extruder].y += m.pos_error.y;
#endif
hotend_offset[extruder].z += m.pos_error.z;
normalize_hotend_offsets(); normalize_hotend_offsets();
#endif #endif
// Correct for positional error, so the object // Correct for positional error, so the object
// is at the known actual spot // is at the known actual spot
planner.synchronize(); planner.synchronize();
#if HAS_X_CENTER if (ENABLED(HAS_X_CENTER) && AXIS_CAN_CALIBRATE(X)) update_measurements(m, X_AXIS);
update_measurements(m, X_AXIS); if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) update_measurements(m, Y_AXIS);
#endif if (AXIS_CAN_CALIBRATE(Z)) update_measurements(m, Z_AXIS);
#if HAS_Y_CENTER
update_measurements(m, Y_AXIS);
#endif
update_measurements(m, Z_AXIS);
sync_plan_position(); sync_plan_position();
} }

View file

@ -47,7 +47,7 @@ void GcodeSuite::M425() {
bool noArgs = true; bool noArgs = true;
LOOP_XYZ(a) { LOOP_XYZ(a) {
if (parser.seen(XYZ_CHAR(a))) { if (CAN_CALIBRATE(a) && parser.seen(XYZ_CHAR(a))) {
planner.synchronize(); planner.synchronize();
backlash.distance_mm[a] = parser.has_value() ? parser.value_linear_units() : backlash.get_measurement(AxisEnum(a)); backlash.distance_mm[a] = parser.has_value() ? parser.value_linear_units() : backlash.get_measurement(AxisEnum(a));
noArgs = false; noArgs = false;
@ -74,7 +74,7 @@ void GcodeSuite::M425() {
SERIAL_ECHOLNPGM("active:"); SERIAL_ECHOLNPGM("active:");
SERIAL_ECHOLNPAIR(" Correction Amount/Fade-out: F", backlash.get_correction(), " (F1.0 = full, F0.0 = none)"); SERIAL_ECHOLNPAIR(" Correction Amount/Fade-out: F", backlash.get_correction(), " (F1.0 = full, F0.0 = none)");
SERIAL_ECHOPGM(" Backlash Distance (mm): "); SERIAL_ECHOPGM(" Backlash Distance (mm): ");
LOOP_XYZ(a) { LOOP_XYZ(a) if (CAN_CALIBRATE(a)) {
SERIAL_CHAR(' ', XYZ_CHAR(a)); SERIAL_CHAR(' ', XYZ_CHAR(a));
SERIAL_ECHO(backlash.distance_mm[a]); SERIAL_ECHO(backlash.distance_mm[a]);
SERIAL_EOL(); SERIAL_EOL();
@ -87,7 +87,7 @@ void GcodeSuite::M425() {
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
SERIAL_ECHOPGM(" Average measured backlash (mm):"); SERIAL_ECHOPGM(" Average measured backlash (mm):");
if (backlash.has_any_measurement()) { if (backlash.has_any_measurement()) {
LOOP_XYZ(a) if (backlash.has_measurement(AxisEnum(a))) { LOOP_XYZ(a) if (CAN_CALIBRATE(a) && backlash.has_measurement(AxisEnum(a))) {
SERIAL_CHAR(' ', XYZ_CHAR(a)); SERIAL_CHAR(' ', XYZ_CHAR(a));
SERIAL_ECHO(backlash.get_measurement(AxisEnum(a))); SERIAL_ECHO(backlash.get_measurement(AxisEnum(a)));
} }

View file

@ -139,6 +139,19 @@
#define CORESIGN(n) (ANY(COREYX, COREZX, COREZY) ? (-(n)) : (n)) #define CORESIGN(n) (ANY(COREYX, COREZX, COREZY) ? (-(n)) : (n))
#endif #endif
// Calibration codes only for non-core axes
#if EITHER(BACKLASH_GCODE, CALIBRATION_GCODE)
#if IS_CORE
#define X_AXIS_INDEX 0
#define Y_AXIS_INDEX 1
#define Z_AXIS_INDEX 2
#define CAN_CALIBRATE(A,B) (A##_AXIS_INDEX == B##_INDEX)
#else
#define CAN_CALIBRATE(...) 1
#endif
#endif
#define AXIS_CAN_CALIBRATE(A) CAN_CALIBRATE(A,NORMAL_AXIS)
/** /**
* No adjustable bed on non-cartesians * No adjustable bed on non-cartesians
*/ */

View file

@ -35,11 +35,12 @@
#include "Conditionals_post.h" #include "Conditionals_post.h"
#include HAL_PATH(../HAL, inc/Conditionals_post.h) #include HAL_PATH(../HAL, inc/Conditionals_post.h)
#include "../core/types.h" // Ahead of sanity-checks
#include "SanityCheck.h" #include "SanityCheck.h"
#include HAL_PATH(../HAL, inc/SanityCheck.h) #include HAL_PATH(../HAL, inc/SanityCheck.h)
// Include all core headers // Include all core headers
#include "../core/types.h"
#include "../core/language.h" #include "../core/language.h"
#include "../core/utility.h" #include "../core/utility.h"
#include "../core/serial.h" #include "../core/serial.h"

View file

@ -39,9 +39,9 @@ void menu_backlash() {
EDIT_ITEM_FAST(percent, MSG_BACKLASH_CORRECTION, &backlash.correction, all_off, all_on); EDIT_ITEM_FAST(percent, MSG_BACKLASH_CORRECTION, &backlash.correction, all_off, all_on);
#define EDIT_BACKLASH_DISTANCE(N) EDIT_ITEM_FAST(float43, MSG_BACKLASH_##N, &backlash.distance_mm[_AXIS(N)], 0.0f, 9.9f); #define EDIT_BACKLASH_DISTANCE(N) EDIT_ITEM_FAST(float43, MSG_BACKLASH_##N, &backlash.distance_mm[_AXIS(N)], 0.0f, 9.9f);
EDIT_BACKLASH_DISTANCE(A); if (AXIS_CAN_CALIBRATE(A)) EDIT_BACKLASH_DISTANCE(A);
EDIT_BACKLASH_DISTANCE(B); if (AXIS_CAN_CALIBRATE(B)) EDIT_BACKLASH_DISTANCE(B);
EDIT_BACKLASH_DISTANCE(C); if (AXIS_CAN_CALIBRATE(C)) EDIT_BACKLASH_DISTANCE(C);
#ifdef BACKLASH_SMOOTHING_MM #ifdef BACKLASH_SMOOTHING_MM
EDIT_ITEM_FAST(float43, MSG_BACKLASH_SMOOTHING, &backlash.smoothing_mm, 0.0f, 9.9f); EDIT_ITEM_FAST(float43, MSG_BACKLASH_SMOOTHING, &backlash.smoothing_mm, 0.0f, 9.9f);