update code base to Marlin 2.0.9.2
This commit is contained in:
370
Marlin/src/gcode/calibrate/G28.cpp
Executable file → Normal file
370
Marlin/src/gcode/calibrate/G28.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "../../module/stepper.h"
|
||||
#include "../../module/endstops.h"
|
||||
|
||||
#if HOTENDS > 1
|
||||
#if HAS_MULTI_HOTEND
|
||||
#include "../../module/tool_change.h"
|
||||
#endif
|
||||
|
||||
@@ -45,12 +45,24 @@
|
||||
#include "../../feature/bltouch.h"
|
||||
#endif
|
||||
|
||||
#include "../../lcd/ultralcd.h"
|
||||
#include "../../lcd/marlinui.h"
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../../lcd/extui/ui_api.h"
|
||||
#elif ENABLED(DWIN_CREALITY_LCD)
|
||||
#include "../../lcd/e3v2/creality/dwin.h"
|
||||
#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
|
||||
#include "../../lcd/e3v2/enhanced/dwin.h"
|
||||
#endif
|
||||
|
||||
#if HAS_L64XX // set L6470 absolute position registers to counts
|
||||
#include "../../libs/L64XX/L64XX_Marlin.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(LASER_MOVE_G28_OFF)
|
||||
#include "../../feature/spindle_laser.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../core/debug_out.h"
|
||||
|
||||
@@ -62,7 +74,7 @@
|
||||
current_position.set(0.0, 0.0);
|
||||
sync_plan_position();
|
||||
|
||||
const int x_axis_home_dir = x_home_dir(active_extruder);
|
||||
const int x_axis_home_dir = TOOL_X_HOME_DIR(active_extruder);
|
||||
|
||||
const float mlx = max_length(X_AXIS),
|
||||
mly = max_length(Y_AXIS),
|
||||
@@ -85,13 +97,13 @@
|
||||
};
|
||||
#endif
|
||||
|
||||
do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * home_dir(Y_AXIS), fr_mm_s);
|
||||
do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * Y_HOME_DIR, fr_mm_s);
|
||||
|
||||
endstops.validate_homing_move();
|
||||
|
||||
current_position.set(0.0, 0.0);
|
||||
|
||||
#if ENABLED(SENSORLESS_HOMING)
|
||||
#if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)
|
||||
tmc_disable_stallguard(stepperX, stealth_states.x);
|
||||
tmc_disable_stallguard(stepperY, stealth_states.y);
|
||||
#if AXIS_HAS_STALLGUARD(X2)
|
||||
@@ -108,15 +120,10 @@
|
||||
#if ENABLED(Z_SAFE_HOMING)
|
||||
|
||||
inline void home_z_safely() {
|
||||
DEBUG_SECTION(log_G28, "home_z_safely", DEBUGGING(LEVELING));
|
||||
|
||||
// Disallow Z homing if X or Y are unknown
|
||||
if (!TEST(axis_known_position, X_AXIS) || !TEST(axis_known_position, Y_AXIS)) {
|
||||
LCD_MESSAGEPGM(MSG_ERR_Z_HOMING);
|
||||
SERIAL_ECHO_MSG(STR_ERR_Z_HOMING_SER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("home_z_safely >>>");
|
||||
// Disallow Z homing if X or Y homing is needed
|
||||
if (homing_needed_error(_BV(X_AXIS) | _BV(Y_AXIS))) return;
|
||||
|
||||
sync_plan_position();
|
||||
|
||||
@@ -124,24 +131,26 @@
|
||||
* Move the Z probe (or just the nozzle) to the safe homing point
|
||||
* (Z is already at the right height)
|
||||
*/
|
||||
destination.set(safe_homing_xy, current_position.z);
|
||||
|
||||
#if HOMING_Z_WITH_PROBE
|
||||
destination -= probe.offset_xy;
|
||||
constexpr xy_float_t safe_homing_xy = { Z_SAFE_HOMING_X_POINT, Z_SAFE_HOMING_Y_POINT };
|
||||
#if HAS_HOME_OFFSET
|
||||
xy_float_t okay_homing_xy = safe_homing_xy;
|
||||
okay_homing_xy -= home_offset;
|
||||
#else
|
||||
constexpr xy_float_t okay_homing_xy = safe_homing_xy;
|
||||
#endif
|
||||
|
||||
destination.set(okay_homing_xy, current_position.z);
|
||||
|
||||
TERN_(HOMING_Z_WITH_PROBE, destination -= probe.offset_xy);
|
||||
|
||||
if (position_is_reachable(destination)) {
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("home_z_safely", destination);
|
||||
|
||||
// This causes the carriage on Dual X to unpark
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
active_extruder_parked = false;
|
||||
#endif
|
||||
// Free the active extruder for movement
|
||||
TERN_(DUAL_X_CARRIAGE, idex_set_parked(false));
|
||||
|
||||
#if ENABLED(SENSORLESS_HOMING)
|
||||
safe_delay(500); // Short delay needed to settle
|
||||
#endif
|
||||
TERN_(SENSORLESS_HOMING, safe_delay(500)); // Short delay needed to settle
|
||||
|
||||
do_blocking_move_to_xy(destination);
|
||||
homeaxis(Z_AXIS);
|
||||
@@ -150,34 +159,34 @@
|
||||
LCD_MESSAGEPGM(MSG_ZPROBE_OUT);
|
||||
SERIAL_ECHO_MSG(STR_ZPROBE_OUT_SER);
|
||||
}
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< home_z_safely");
|
||||
}
|
||||
|
||||
#endif // Z_SAFE_HOMING
|
||||
|
||||
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
|
||||
|
||||
slow_homing_t begin_slow_homing() {
|
||||
slow_homing_t slow_homing{0};
|
||||
slow_homing.acceleration.set(planner.settings.max_acceleration_mm_per_s2[X_AXIS],
|
||||
planner.settings.max_acceleration_mm_per_s2[Y_AXIS]);
|
||||
motion_state_t begin_slow_homing() {
|
||||
motion_state_t motion_state{0};
|
||||
motion_state.acceleration.set(planner.settings.max_acceleration_mm_per_s2[X_AXIS],
|
||||
planner.settings.max_acceleration_mm_per_s2[Y_AXIS]
|
||||
OPTARG(DELTA, planner.settings.max_acceleration_mm_per_s2[Z_AXIS])
|
||||
);
|
||||
planner.settings.max_acceleration_mm_per_s2[X_AXIS] = 100;
|
||||
planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = 100;
|
||||
TERN_(DELTA, planner.settings.max_acceleration_mm_per_s2[Z_AXIS] = 100);
|
||||
#if HAS_CLASSIC_JERK
|
||||
slow_homing.jerk_xy = planner.max_jerk;
|
||||
planner.max_jerk.set(0, 0);
|
||||
motion_state.jerk_state = planner.max_jerk;
|
||||
planner.max_jerk.set(0, 0 OPTARG(DELTA, 0));
|
||||
#endif
|
||||
planner.reset_acceleration_rates();
|
||||
return slow_homing;
|
||||
return motion_state;
|
||||
}
|
||||
|
||||
void end_slow_homing(const slow_homing_t &slow_homing) {
|
||||
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 = slow_homing.jerk_xy;
|
||||
#endif
|
||||
void end_slow_homing(const motion_state_t &motion_state) {
|
||||
planner.settings.max_acceleration_mm_per_s2[X_AXIS] = motion_state.acceleration.x;
|
||||
planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = motion_state.acceleration.y;
|
||||
TERN_(DELTA, planner.settings.max_acceleration_mm_per_s2[Z_AXIS] = motion_state.acceleration.z);
|
||||
TERN_(HAS_CLASSIC_JERK, planner.max_jerk = motion_state.jerk_state);
|
||||
planner.reset_acceleration_rates();
|
||||
}
|
||||
|
||||
@@ -191,22 +200,23 @@
|
||||
* None Home to all axes with no parameters.
|
||||
* With QUICK_HOME enabled XY will home together, then Z.
|
||||
*
|
||||
* O Home only if position is unknown
|
||||
*
|
||||
* Rn Raise by n mm/inches before homing
|
||||
* L<bool> Force leveling state ON (if possible) or OFF after homing (Requires RESTORE_LEVELING_AFTER_G28 or ENABLE_LEVELING_AFTER_G28)
|
||||
* O Home only if the position is not known and trusted
|
||||
* R<linear> Raise by n mm/inches before homing
|
||||
*
|
||||
* Cartesian/SCARA parameters
|
||||
*
|
||||
* X Home to the X endstop
|
||||
* Y Home to the Y endstop
|
||||
* Z Home to the Z endstop
|
||||
*
|
||||
*/
|
||||
void GcodeSuite::G28() {
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_ECHOLNPGM(">>> G28");
|
||||
log_machine_info();
|
||||
}
|
||||
DEBUG_SECTION(log_G28, "G28", DEBUGGING(LEVELING));
|
||||
if (DEBUGGING(LEVELING)) log_machine_info();
|
||||
|
||||
TERN_(LASER_MOVE_G28_OFF, cutter.set_inline_enabled(false)); // turn off laser
|
||||
|
||||
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_HOMING));
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
bool IDEX_saved_duplication_state = extruder_duplication_enabled;
|
||||
@@ -214,49 +224,53 @@ void GcodeSuite::G28() {
|
||||
#endif
|
||||
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
if (parser.seen('S')) {
|
||||
LOOP_XYZ(a) set_axis_is_at_home((AxisEnum)a);
|
||||
if (parser.seen_test('S')) {
|
||||
LOOP_LINEAR_AXES(a) set_axis_is_at_home((AxisEnum)a);
|
||||
sync_plan_position();
|
||||
SERIAL_ECHOLNPGM("Simulated Homing");
|
||||
report_current_position();
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G28");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Home (O)nly if position is unknown
|
||||
if (!homing_needed() && parser.boolval('O')) {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> homing not needed, skip\n<<< G28");
|
||||
if (!axes_should_home() && parser.seen_test('O')) {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> homing not needed, skip");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for planner moves to finish!
|
||||
planner.synchronize();
|
||||
TERN_(HAS_DWIN_E3V2_BASIC, DWIN_StartHoming());
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onHomingStart());
|
||||
|
||||
planner.synchronize(); // Wait for planner moves to finish!
|
||||
|
||||
SET_SOFT_ENDSTOP_LOOSE(false); // Reset a leftover 'loose' motion state
|
||||
|
||||
// Disable the leveling matrix before homing
|
||||
#if HAS_LEVELING
|
||||
|
||||
// Cancel the active G29 session
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
g29_in_progress = false;
|
||||
#endif
|
||||
|
||||
#if ENABLED(RESTORE_LEVELING_AFTER_G28)
|
||||
const bool leveling_was_active = planner.leveling_active;
|
||||
#endif
|
||||
set_bed_leveling_enabled(false);
|
||||
#if CAN_SET_LEVELING_AFTER_G28
|
||||
const bool leveling_restore_state = parser.boolval('L', TERN1(RESTORE_LEVELING_AFTER_G28, planner.leveling_active));
|
||||
#endif
|
||||
|
||||
#if ENABLED(CNC_WORKSPACE_PLANES)
|
||||
workspace_plane = PLANE_XY;
|
||||
#endif
|
||||
// Cancel any prior G29 session
|
||||
TERN_(PROBE_MANUALLY, g29_in_progress = false);
|
||||
|
||||
// Disable leveling before homing
|
||||
TERN_(HAS_LEVELING, set_bed_leveling_enabled(false));
|
||||
|
||||
// Reset to the XY plane
|
||||
TERN_(CNC_WORKSPACE_PLANES, workspace_plane = PLANE_XY);
|
||||
|
||||
// Count this command as movement / activity
|
||||
reset_stepper_timeout();
|
||||
|
||||
#define HAS_CURRENT_HOME(N) (defined(N##_CURRENT_HOME) && N##_CURRENT_HOME != N##_CURRENT)
|
||||
#define HAS_HOMING_CURRENT (HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2))
|
||||
#if HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2) || (ENABLED(DELTA) && HAS_CURRENT_HOME(Z))
|
||||
#define HAS_HOMING_CURRENT 1
|
||||
#endif
|
||||
|
||||
#if HAS_HOMING_CURRENT
|
||||
auto debug_current = [](PGM_P const s, const int16_t a, const int16_t b){
|
||||
serialprintPGM(s); DEBUG_ECHOLNPAIR(" current: ", a, " -> ", b);
|
||||
auto debug_current = [](PGM_P const s, const int16_t a, const int16_t b) {
|
||||
DEBUG_ECHOPGM_P(s); DEBUG_ECHOLNPGM(" current: ", a, " -> ", b);
|
||||
};
|
||||
#if HAS_CURRENT_HOME(X)
|
||||
const int16_t tmc_save_current_X = stepperX.getMilliamps();
|
||||
@@ -278,23 +292,30 @@ void GcodeSuite::G28() {
|
||||
stepperY2.rms_current(Y2_CURRENT_HOME);
|
||||
if (DEBUGGING(LEVELING)) debug_current(PSTR("Y2"), tmc_save_current_Y2, Y2_CURRENT_HOME);
|
||||
#endif
|
||||
#if HAS_CURRENT_HOME(Z) && ENABLED(DELTA)
|
||||
const int16_t tmc_save_current_Z = stepperZ.getMilliamps();
|
||||
stepperZ.rms_current(Z_CURRENT_HOME);
|
||||
if (DEBUGGING(LEVELING)) debug_current(PSTR("Z"), tmc_save_current_Z, Z_CURRENT_HOME);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
|
||||
slow_homing_t slow_homing = begin_slow_homing();
|
||||
motion_state_t saved_motion_state = begin_slow_homing();
|
||||
#endif
|
||||
|
||||
// Always home with tool 0 active
|
||||
#if HOTENDS > 1
|
||||
#if HAS_MULTI_HOTEND
|
||||
#if DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE)
|
||||
const uint8_t old_tool_index = active_extruder;
|
||||
#endif
|
||||
// PARKING_EXTRUDER homing requires different handling of movement / solenoid activation, depending on the side of homing
|
||||
#if ENABLED(PARKING_EXTRUDER)
|
||||
const bool pe_final_change_must_unpark = parking_extruder_unpark_after_homing(old_tool_index, X_HOME_DIR + 1 == old_tool_index * 2);
|
||||
#endif
|
||||
tool_change(0, true);
|
||||
#endif
|
||||
|
||||
#if HAS_DUPLICATION_MODE
|
||||
extruder_duplication_enabled = false;
|
||||
#endif
|
||||
TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false));
|
||||
|
||||
remember_feedrate_scaling_off();
|
||||
|
||||
@@ -306,46 +327,60 @@ void GcodeSuite::G28() {
|
||||
|
||||
home_delta();
|
||||
|
||||
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
|
||||
end_slow_homing(slow_homing);
|
||||
TERN_(IMPROVE_HOMING_RELIABILITY, end_slow_homing(saved_motion_state));
|
||||
|
||||
#elif ENABLED(AXEL_TPARA)
|
||||
|
||||
constexpr bool doZ = true; // for NANODLP_Z_SYNC if your DLP is on a TPARA
|
||||
|
||||
home_TPARA();
|
||||
|
||||
#else
|
||||
|
||||
#define _UNSAFE(A) (homeZ && TERN0(Z_SAFE_HOMING, axes_should_home(_BV(A##_AXIS))))
|
||||
|
||||
const bool homeZ = TERN0(HAS_Z_AXIS, parser.seen_test('Z')),
|
||||
LINEAR_AXIS_LIST( // Other axes should be homed before Z safe-homing
|
||||
needX = _UNSAFE(X), needY = _UNSAFE(Y), needZ = false, // UNUSED
|
||||
needI = _UNSAFE(I), needJ = _UNSAFE(J), needK = _UNSAFE(K)
|
||||
),
|
||||
LINEAR_AXIS_LIST( // Home each axis if needed or flagged
|
||||
homeX = needX || parser.seen_test('X'),
|
||||
homeY = needY || parser.seen_test('Y'),
|
||||
homeZZ = homeZ,
|
||||
homeI = needI || parser.seen_test(AXIS4_NAME), homeJ = needJ || parser.seen_test(AXIS5_NAME), homeK = needK || parser.seen_test(AXIS6_NAME),
|
||||
),
|
||||
home_all = LINEAR_AXIS_GANG( // Home-all if all or none are flagged
|
||||
homeX == homeX, && homeY == homeX, && homeZ == homeX,
|
||||
&& homeI == homeX, && homeJ == homeX, && homeK == homeX
|
||||
),
|
||||
LINEAR_AXIS_LIST(
|
||||
doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ,
|
||||
doI = home_all || homeI, doJ = home_all || homeJ, doK = home_all || homeK
|
||||
);
|
||||
|
||||
#if HAS_Z_AXIS
|
||||
UNUSED(needZ); UNUSED(homeZZ);
|
||||
#else
|
||||
constexpr bool doZ = false;
|
||||
#endif
|
||||
|
||||
#else // NOT DELTA
|
||||
TERN_(HOME_Z_FIRST, if (doZ) homeaxis(Z_AXIS));
|
||||
|
||||
const bool homeX = parser.seen('X'), homeY = parser.seen('Y'), homeZ = parser.seen('Z'),
|
||||
home_all = homeX == homeY && homeX == homeZ, // All or None
|
||||
doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ;
|
||||
const float z_homing_height = parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT;
|
||||
|
||||
destination = current_position;
|
||||
|
||||
#if Z_HOME_DIR > 0 // If homing away from BED do Z first
|
||||
|
||||
if (doZ) homeaxis(Z_AXIS);
|
||||
|
||||
#endif
|
||||
|
||||
const float z_homing_height =
|
||||
(DISABLED(UNKNOWN_Z_NO_RAISE) || TEST(axis_known_position, Z_AXIS))
|
||||
? (parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT)
|
||||
: 0;
|
||||
|
||||
if (z_homing_height && (doX || doY)) {
|
||||
if (z_homing_height && (LINEAR_AXIS_GANG(doX, || doY, || TERN0(Z_SAFE_HOMING, doZ), || doI, || doJ, || doK))) {
|
||||
// Raise Z before homing any other axes and z is not already high enough (never lower z)
|
||||
destination.z = z_homing_height + (TEST(axis_known_position, Z_AXIS) ? 0.0f : current_position.z);
|
||||
if (destination.z > current_position.z) {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) to ", destination.z);
|
||||
do_blocking_move_to_z(destination.z);
|
||||
}
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Raise Z (before homing) by ", z_homing_height);
|
||||
do_z_clearance(z_homing_height);
|
||||
TERN_(BLTOUCH, bltouch.init());
|
||||
}
|
||||
|
||||
#if ENABLED(QUICK_HOME)
|
||||
|
||||
if (doX && doY) quick_home_xy();
|
||||
|
||||
#endif
|
||||
// Diagonal move first if both are homing
|
||||
TERN_(QUICK_HOME, if (doX && doY) quick_home_xy());
|
||||
|
||||
// Home Y (before X)
|
||||
if (ENABLED(HOME_Y_BEFORE_X) && (doY || (ENABLED(CODEPENDENT_XY_HOMING) && doX)))
|
||||
if (ENABLED(HOME_Y_BEFORE_X) && (doY || TERN0(CODEPENDENT_XY_HOMING, doX)))
|
||||
homeaxis(Y_AXIS);
|
||||
|
||||
// Home X
|
||||
@@ -358,16 +393,14 @@ void GcodeSuite::G28() {
|
||||
homeaxis(X_AXIS);
|
||||
|
||||
// Remember this extruder's position for later tool change
|
||||
inactive_extruder_x_pos = current_position.x;
|
||||
inactive_extruder_x = current_position.x;
|
||||
|
||||
// Home the 1st (left) extruder
|
||||
active_extruder = 0;
|
||||
homeaxis(X_AXIS);
|
||||
|
||||
// Consider the active extruder to be parked
|
||||
raised_parked_position = current_position;
|
||||
delayed_move_time = 0;
|
||||
active_extruder_parked = true;
|
||||
// Consider the active extruder to be in its "parked" position
|
||||
idex_set_parked();
|
||||
|
||||
#else
|
||||
|
||||
@@ -380,40 +413,34 @@ void GcodeSuite::G28() {
|
||||
if (DISABLED(HOME_Y_BEFORE_X) && doY)
|
||||
homeaxis(Y_AXIS);
|
||||
|
||||
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
|
||||
end_slow_homing(slow_homing);
|
||||
#endif
|
||||
TERN_(IMPROVE_HOMING_RELIABILITY, end_slow_homing(saved_motion_state));
|
||||
|
||||
// Home Z last if homing towards the bed
|
||||
#if Z_HOME_DIR < 0
|
||||
|
||||
#if HAS_Z_AXIS && DISABLED(HOME_Z_FIRST)
|
||||
if (doZ) {
|
||||
#if ENABLED(BLTOUCH)
|
||||
bltouch.init();
|
||||
#endif
|
||||
#if ENABLED(Z_SAFE_HOMING)
|
||||
home_z_safely();
|
||||
#else
|
||||
homeaxis(Z_AXIS);
|
||||
#if EITHER(Z_MULTI_ENDSTOPS, Z_STEPPER_AUTO_ALIGN)
|
||||
stepper.set_all_z_lock(false);
|
||||
stepper.set_separate_multi_axis(false);
|
||||
#endif
|
||||
|
||||
#if HOMING_Z_WITH_PROBE && defined(Z_AFTER_PROBING)
|
||||
#if Z_AFTER_HOMING > Z_AFTER_PROBING
|
||||
do_blocking_move_to_z(Z_AFTER_HOMING);
|
||||
#else
|
||||
probe.move_z_after_probing();
|
||||
#endif
|
||||
#elif defined(Z_AFTER_HOMING)
|
||||
do_blocking_move_to_z(Z_AFTER_HOMING);
|
||||
#endif
|
||||
TERN(Z_SAFE_HOMING, home_z_safely(), homeaxis(Z_AXIS));
|
||||
probe.move_z_after_homing();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // doZ
|
||||
|
||||
#endif // Z_HOME_DIR < 0
|
||||
#if LINEAR_AXES >= 4
|
||||
if (doI) homeaxis(I_AXIS);
|
||||
#endif
|
||||
#if LINEAR_AXES >= 5
|
||||
if (doJ) homeaxis(J_AXIS);
|
||||
#endif
|
||||
#if LINEAR_AXES >= 6
|
||||
if (doK) homeaxis(K_AXIS);
|
||||
#endif
|
||||
|
||||
sync_plan_position();
|
||||
|
||||
#endif // !DELTA (G28)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Preserve DXC mode across a G28 for IDEX printers in DXC_DUPLICATION_MODE.
|
||||
@@ -423,34 +450,28 @@ void GcodeSuite::G28() {
|
||||
*/
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
|
||||
if (dxc_is_duplicating()) {
|
||||
if (idex_is_duplicating()) {
|
||||
|
||||
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
|
||||
slow_homing = begin_slow_homing();
|
||||
#endif
|
||||
TERN_(IMPROVE_HOMING_RELIABILITY, saved_motion_state = begin_slow_homing());
|
||||
|
||||
// Always home the 2nd (right) extruder first
|
||||
active_extruder = 1;
|
||||
homeaxis(X_AXIS);
|
||||
|
||||
// Remember this extruder's position for later tool change
|
||||
inactive_extruder_x_pos = current_position.x;
|
||||
inactive_extruder_x = current_position.x;
|
||||
|
||||
// Home the 1st (left) extruder
|
||||
active_extruder = 0;
|
||||
homeaxis(X_AXIS);
|
||||
|
||||
// Consider the active extruder to be parked
|
||||
raised_parked_position = current_position;
|
||||
delayed_move_time = 0;
|
||||
active_extruder_parked = true;
|
||||
extruder_duplication_enabled = IDEX_saved_duplication_state;
|
||||
dual_x_carriage_mode = IDEX_saved_mode;
|
||||
stepper.set_directions();
|
||||
idex_set_parked();
|
||||
|
||||
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
|
||||
end_slow_homing(slow_homing);
|
||||
#endif
|
||||
dual_x_carriage_mode = IDEX_saved_mode;
|
||||
set_duplication_enabled(IDEX_saved_duplication_state);
|
||||
|
||||
TERN_(IMPROVE_HOMING_RELIABILITY, end_slow_homing(saved_motion_state));
|
||||
}
|
||||
|
||||
#endif // DUAL_X_CARRIAGE
|
||||
@@ -458,24 +479,18 @@ void GcodeSuite::G28() {
|
||||
endstops.not_homing();
|
||||
|
||||
// Clear endstop state for polled stallGuard endstops
|
||||
#if ENABLED(SPI_ENDSTOPS)
|
||||
endstops.clear_endstop_state();
|
||||
#endif
|
||||
TERN_(SPI_ENDSTOPS, endstops.clear_endstop_state());
|
||||
|
||||
#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);
|
||||
#endif
|
||||
// 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));
|
||||
|
||||
#if ENABLED(RESTORE_LEVELING_AFTER_G28)
|
||||
set_bed_leveling_enabled(leveling_was_active);
|
||||
#endif
|
||||
TERN_(CAN_SET_LEVELING_AFTER_G28, if (leveling_restore_state) set_bed_leveling_enabled());
|
||||
|
||||
restore_feedrate_and_scaling();
|
||||
|
||||
// Restore the active tool after homing
|
||||
#if HOTENDS > 1 && (DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE))
|
||||
tool_change(old_tool_index, NONE(PARKING_EXTRUDER, DUAL_X_CARRIAGE)); // Do move if one of these
|
||||
#if HAS_MULTI_HOTEND && (DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE))
|
||||
tool_change(old_tool_index, TERN(PARKING_EXTRUDER, !pe_final_change_must_unpark, DISABLED(DUAL_X_CARRIAGE))); // Do move if one of these
|
||||
#endif
|
||||
|
||||
#if HAS_HOMING_CURRENT
|
||||
@@ -492,26 +507,43 @@ void GcodeSuite::G28() {
|
||||
#if HAS_CURRENT_HOME(Y2)
|
||||
stepperY2.rms_current(tmc_save_current_Y2);
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_CURRENT_HOME(Z) && ENABLED(DELTA)
|
||||
stepperZ.rms_current(tmc_save_current_Z);
|
||||
#endif
|
||||
#if HAS_CURRENT_HOME(I)
|
||||
stepperI.rms_current(tmc_save_current_I);
|
||||
#endif
|
||||
#if HAS_CURRENT_HOME(J)
|
||||
stepperJ.rms_current(tmc_save_current_J);
|
||||
#endif
|
||||
#if HAS_CURRENT_HOME(K)
|
||||
stepperK.rms_current(tmc_save_current_K);
|
||||
#endif
|
||||
#endif // HAS_HOMING_CURRENT
|
||||
|
||||
ui.refresh();
|
||||
|
||||
TERN_(HAS_DWIN_E3V2_BASIC, DWIN_CompletedHoming());
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onHomingComplete());
|
||||
|
||||
report_current_position();
|
||||
|
||||
if (ENABLED(NANODLP_Z_SYNC) && (doZ || ENABLED(NANODLP_ALL_AXIS)))
|
||||
SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP);
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G28");
|
||||
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE));
|
||||
|
||||
#if HAS_L64XX
|
||||
// Set L6470 absolute position registers to counts
|
||||
// constexpr *might* move this to PROGMEM.
|
||||
// If not, this will need a PROGMEM directive and an accessor.
|
||||
#define _EN_ITEM(N) , E_AXIS
|
||||
static constexpr AxisEnum L64XX_axis_xref[MAX_L64XX] = {
|
||||
X_AXIS, Y_AXIS, Z_AXIS,
|
||||
X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS,
|
||||
E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS
|
||||
LINEAR_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS, I_AXIS, J_AXIS, K_AXIS),
|
||||
X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS, Z_AXIS
|
||||
REPEAT(E_STEPPERS, _EN_ITEM)
|
||||
};
|
||||
#undef _EN_ITEM
|
||||
for (uint8_t j = 1; j <= L64XX::chain[0]; j++) {
|
||||
const uint8_t cv = L64XX::chain[j];
|
||||
L64xxManager.set_param((L64XX_axis_t)cv, L6470_ABS_POS, stepper.position(L64XX_axis_xref[cv]));
|
||||
|
152
Marlin/src/gcode/calibrate/G33.cpp
Executable file → Normal file
152
Marlin/src/gcode/calibrate/G33.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -29,13 +29,13 @@
|
||||
#include "../../module/motion.h"
|
||||
#include "../../module/stepper.h"
|
||||
#include "../../module/endstops.h"
|
||||
#include "../../lcd/ultralcd.h"
|
||||
#include "../../lcd/marlinui.h"
|
||||
|
||||
#if HAS_BED_PROBE
|
||||
#include "../../module/probe.h"
|
||||
#endif
|
||||
|
||||
#if HOTENDS > 1
|
||||
#if HAS_MULTI_HOTEND
|
||||
#include "../../module/tool_change.h"
|
||||
#endif
|
||||
|
||||
@@ -63,25 +63,24 @@ enum CalEnum : char { // the 7 main calibration points -
|
||||
#define LOOP_CAL_RAD(VAR) LOOP_CAL_PT(VAR, __A, _7P_STEP)
|
||||
#define LOOP_CAL_ACT(VAR, _4P, _OP) LOOP_CAL_PT(VAR, _OP ? _AB : __A, _4P ? _4P_STEP : _7P_STEP)
|
||||
|
||||
#if HOTENDS > 1
|
||||
#if HAS_MULTI_HOTEND
|
||||
const uint8_t old_tool_index = active_extruder;
|
||||
#define AC_CLEANUP() ac_cleanup(old_tool_index)
|
||||
#else
|
||||
#define AC_CLEANUP() ac_cleanup()
|
||||
#endif
|
||||
|
||||
float lcd_probe_pt(const xy_pos_t &xy);
|
||||
|
||||
float dcr;
|
||||
|
||||
void ac_home() {
|
||||
endstops.enable(true);
|
||||
TERN_(SENSORLESS_HOMING, probe.set_homing_current(true));
|
||||
home_delta();
|
||||
TERN_(SENSORLESS_HOMING, probe.set_homing_current(false));
|
||||
endstops.not_homing();
|
||||
}
|
||||
|
||||
void ac_setup(const bool reset_bed) {
|
||||
#if HOTENDS > 1
|
||||
tool_change(0, true);
|
||||
#endif
|
||||
TERN_(HAS_MULTI_HOTEND, tool_change(0, true));
|
||||
|
||||
planner.synchronize();
|
||||
remember_feedrate_scaling_off();
|
||||
@@ -91,26 +90,16 @@ void ac_setup(const bool reset_bed) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ac_cleanup(
|
||||
#if HOTENDS > 1
|
||||
const uint8_t old_tool_index
|
||||
#endif
|
||||
) {
|
||||
#if ENABLED(DELTA_HOME_TO_SAFE_ZONE)
|
||||
do_blocking_move_to_z(delta_clip_start_height);
|
||||
#endif
|
||||
#if HAS_BED_PROBE
|
||||
probe.stow();
|
||||
#endif
|
||||
void ac_cleanup(TERN_(HAS_MULTI_HOTEND, const uint8_t old_tool_index)) {
|
||||
TERN_(DELTA_HOME_TO_SAFE_ZONE, do_blocking_move_to_z(delta_clip_start_height));
|
||||
TERN_(HAS_BED_PROBE, probe.stow());
|
||||
restore_feedrate_and_scaling();
|
||||
#if HOTENDS > 1
|
||||
tool_change(old_tool_index, true);
|
||||
#endif
|
||||
TERN_(HAS_MULTI_HOTEND, tool_change(old_tool_index, true));
|
||||
}
|
||||
|
||||
void print_signed_float(PGM_P const prefix, const float &f) {
|
||||
void print_signed_float(PGM_P const prefix, const_float_t f) {
|
||||
SERIAL_ECHOPGM(" ");
|
||||
serialprintPGM(prefix);
|
||||
SERIAL_ECHOPGM_P(prefix);
|
||||
SERIAL_CHAR(':');
|
||||
if (f >= 0) SERIAL_CHAR('+');
|
||||
SERIAL_ECHO_F(f, 2);
|
||||
@@ -120,14 +109,14 @@ void print_signed_float(PGM_P const prefix, const float &f) {
|
||||
* - Print the delta settings
|
||||
*/
|
||||
static void print_calibration_settings(const bool end_stops, const bool tower_angles) {
|
||||
SERIAL_ECHOPAIR(".Height:", delta_height);
|
||||
SERIAL_ECHOPGM(".Height:", delta_height);
|
||||
if (end_stops) {
|
||||
print_signed_float(PSTR("Ex"), delta_endstop_adj.a);
|
||||
print_signed_float(PSTR("Ey"), delta_endstop_adj.b);
|
||||
print_signed_float(PSTR("Ez"), delta_endstop_adj.c);
|
||||
}
|
||||
if (end_stops && tower_angles) {
|
||||
SERIAL_ECHOPAIR(" Radius:", delta_radius);
|
||||
SERIAL_ECHOPGM(" Radius:", delta_radius);
|
||||
SERIAL_EOL();
|
||||
SERIAL_CHAR('.');
|
||||
SERIAL_ECHO_SP(13);
|
||||
@@ -138,7 +127,7 @@ static void print_calibration_settings(const bool end_stops, const bool tower_an
|
||||
print_signed_float(PSTR("Tz"), delta_tower_angle_trim.c);
|
||||
}
|
||||
if ((!end_stops && tower_angles) || (end_stops && !tower_angles)) { // XOR
|
||||
SERIAL_ECHOPAIR(" Radius:", delta_radius);
|
||||
SERIAL_ECHOPGM(" Radius:", delta_radius);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
}
|
||||
@@ -188,9 +177,9 @@ static float std_dev_points(float z_pt[NPP + 1], const bool _0p_cal, const bool
|
||||
/**
|
||||
* - Probe a point
|
||||
*/
|
||||
static float calibration_probe(const xy_pos_t &xy, const bool stow) {
|
||||
static float calibration_probe(const xy_pos_t &xy, const bool stow, const bool probe_at_offset) {
|
||||
#if HAS_BED_PROBE
|
||||
return probe.probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, true, false);
|
||||
return probe.probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, true, probe_at_offset);
|
||||
#else
|
||||
UNUSED(stow);
|
||||
return lcd_probe_pt(xy);
|
||||
@@ -200,7 +189,7 @@ static float calibration_probe(const xy_pos_t &xy, const bool stow) {
|
||||
/**
|
||||
* - Probe a grid
|
||||
*/
|
||||
static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_points, const bool towers_set, const bool stow_after_each) {
|
||||
static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_points, const bool towers_set, const bool stow_after_each, const bool probe_at_offset) {
|
||||
const bool _0p_calibration = probe_points == 0,
|
||||
_1p_calibration = probe_points == 1 || probe_points == -1,
|
||||
_4p_calibration = probe_points == 2,
|
||||
@@ -222,11 +211,9 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
|
||||
|
||||
if (!_0p_calibration) {
|
||||
|
||||
const float dcr = delta_calibration_radius();
|
||||
|
||||
if (!_7p_no_intermediates && !_7p_4_intermediates && !_7p_11_intermediates) { // probe the center
|
||||
const xy_pos_t center{0};
|
||||
z_pt[CEN] += calibration_probe(center, stow_after_each);
|
||||
z_pt[CEN] += calibration_probe(center, stow_after_each, probe_at_offset);
|
||||
if (isnan(z_pt[CEN])) return false;
|
||||
}
|
||||
|
||||
@@ -237,7 +224,7 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
|
||||
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
|
||||
r = dcr * 0.1;
|
||||
const xy_pos_t vec = { cos(a), sin(a) };
|
||||
z_pt[CEN] += calibration_probe(vec * r, stow_after_each);
|
||||
z_pt[CEN] += calibration_probe(vec * r, stow_after_each, probe_at_offset);
|
||||
if (isnan(z_pt[CEN])) return false;
|
||||
}
|
||||
z_pt[CEN] /= float(_7p_2_intermediates ? 7 : probe_points);
|
||||
@@ -262,7 +249,7 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
|
||||
r = dcr * (1 - 0.1 * (zig_zag ? offset - circle : circle)),
|
||||
interpol = FMOD(rad, 1);
|
||||
const xy_pos_t vec = { cos(a), sin(a) };
|
||||
const float z_temp = calibration_probe(vec * r, stow_after_each);
|
||||
const float z_temp = calibration_probe(vec * r, stow_after_each, probe_at_offset);
|
||||
if (isnan(z_temp)) return false;
|
||||
// split probe point to neighbouring calibration points
|
||||
z_pt[uint8_t(LROUND(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cos(RADIANS(interpol * 90)));
|
||||
@@ -289,7 +276,6 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
|
||||
static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_at_pt_axis[NPP + 1]) {
|
||||
xyz_pos_t pos{0};
|
||||
|
||||
const float dcr = delta_calibration_radius();
|
||||
LOOP_CAL_ALL(rad) {
|
||||
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
|
||||
r = (rad == CEN ? 0.0f : dcr);
|
||||
@@ -300,7 +286,7 @@ static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_
|
||||
}
|
||||
|
||||
static void forward_kinematics_probe_points(abc_float_t mm_at_pt_axis[NPP + 1], float z_pt[NPP + 1]) {
|
||||
const float r_quot = delta_calibration_radius() / delta_radius;
|
||||
const float r_quot = dcr / delta_radius;
|
||||
|
||||
#define ZPP(N,I,A) (((1.0f + r_quot * (N)) / 3.0f) * mm_at_pt_axis[I].A)
|
||||
#define Z00(I, A) ZPP( 0, I, A)
|
||||
@@ -341,7 +327,7 @@ static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], abc_float_t d
|
||||
}
|
||||
|
||||
static float auto_tune_h() {
|
||||
const float r_quot = delta_calibration_radius() / delta_radius;
|
||||
const float r_quot = dcr / delta_radius;
|
||||
return RECIPROCAL(r_quot / (2.0f / 3.0f)); // (2/3)/CR
|
||||
}
|
||||
|
||||
@@ -362,7 +348,7 @@ static float auto_tune_a() {
|
||||
abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f };
|
||||
|
||||
delta_t.reset();
|
||||
LOOP_XYZ(axis) {
|
||||
LOOP_LINEAR_AXES(axis) {
|
||||
delta_t[axis] = diff;
|
||||
calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t);
|
||||
delta_t[axis] = 0;
|
||||
@@ -386,6 +372,8 @@ static float auto_tune_a() {
|
||||
* P3 Probe all positions: center, towers and opposite towers. Calibrate all.
|
||||
* P4-P10 Probe all positions at different intermediate locations and average them.
|
||||
*
|
||||
* Rn.nn override default calibration Radius
|
||||
*
|
||||
* T Don't calibrate tower angle corrections
|
||||
*
|
||||
* Cn.nn Calibration precision; when omitted calibrates to maximum precision
|
||||
@@ -399,16 +387,46 @@ static float auto_tune_a() {
|
||||
* V3 Report settings and probe results
|
||||
*
|
||||
* E Engage the probe for each point
|
||||
*
|
||||
* O Probe at offset points (this is wrong but it seems to work)
|
||||
*
|
||||
* With SENSORLESS_PROBING:
|
||||
* Use these flags to calibrate stall sensitivity: (e.g., `G33 P1 Y Z` to calibrate X only.)
|
||||
* X Don't activate stallguard on X.
|
||||
* Y Don't activate stallguard on Y.
|
||||
* Z Don't activate stallguard on Z.
|
||||
*/
|
||||
void GcodeSuite::G33() {
|
||||
|
||||
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE));
|
||||
|
||||
const int8_t probe_points = parser.intval('P', DELTA_CALIBRATION_DEFAULT_POINTS);
|
||||
if (!WITHIN(probe_points, 0, 10)) {
|
||||
SERIAL_ECHOLNPGM("?(P)oints implausible (0-10).");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool towers_set = !parser.seen('T');
|
||||
const bool probe_at_offset = TERN0(HAS_PROBE_XY_OFFSET, parser.boolval('O')),
|
||||
towers_set = !parser.seen_test('T');
|
||||
|
||||
float max_dcr = dcr = DELTA_PRINTABLE_RADIUS;
|
||||
#if HAS_PROBE_XY_OFFSET
|
||||
// For offset probes the calibration radius is set to a safe but non-optimal value
|
||||
dcr -= HYPOT(probe.offset_xy.x, probe.offset_xy.y);
|
||||
if (probe_at_offset) {
|
||||
// With probe positions both probe and nozzle need to be within the printable area
|
||||
max_dcr = dcr;
|
||||
}
|
||||
// else with nozzle positions there is a risk of the probe being outside the bed
|
||||
// but as long the nozzle stays within the printable area there is no risk of
|
||||
// the effector crashing into the towers.
|
||||
#endif
|
||||
|
||||
if (parser.seenval('R')) dcr = parser.value_float();
|
||||
if (!WITHIN(dcr, 0, max_dcr)) {
|
||||
SERIAL_ECHOLNPGM("?calibration (R)adius implausible.");
|
||||
return;
|
||||
}
|
||||
|
||||
const float calibration_precision = parser.floatval('C', 0.0f);
|
||||
if (calibration_precision < 0) {
|
||||
@@ -428,7 +446,13 @@ void GcodeSuite::G33() {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool stow_after_each = parser.seen('E');
|
||||
const bool stow_after_each = parser.seen_test('E');
|
||||
|
||||
#if HAS_DELTA_SENSORLESS_PROBING
|
||||
probe.test_sensitivity.x = !parser.seen_test('X');
|
||||
TERN_(HAS_Y_AXIS, probe.test_sensitivity.y = !parser.seen_test('Y'));
|
||||
TERN_(HAS_Z_AXIS, probe.test_sensitivity.z = !parser.seen_test('Z'));
|
||||
#endif
|
||||
|
||||
const bool _0p_calibration = probe_points == 0,
|
||||
_1p_calibration = probe_points == 1 || probe_points == -1,
|
||||
@@ -452,21 +476,9 @@ void GcodeSuite::G33() {
|
||||
|
||||
SERIAL_ECHOLNPGM("G33 Auto Calibrate");
|
||||
|
||||
const float dcr = delta_calibration_radius();
|
||||
|
||||
if (!_1p_calibration && !_0p_calibration) { // test if the outer radius is reachable
|
||||
LOOP_CAL_RAD(axis) {
|
||||
const float a = RADIANS(210 + (360 / NPP) * (axis - 1));
|
||||
if (!position_is_reachable(cos(a) * dcr, sin(a) * dcr)) {
|
||||
SERIAL_ECHOLNPGM("?Bed calibration radius implausible.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Report settings
|
||||
PGM_P checkingac = PSTR("Checking... AC");
|
||||
serialprintPGM(checkingac);
|
||||
PGM_P const checkingac = PSTR("Checking... AC");
|
||||
SERIAL_ECHOPGM_P(checkingac);
|
||||
if (verbose_level == 0) SERIAL_ECHOPGM(" (DRY-RUN)");
|
||||
SERIAL_EOL();
|
||||
ui.set_status_P(checkingac);
|
||||
@@ -486,9 +498,9 @@ void GcodeSuite::G33() {
|
||||
|
||||
// Probe the points
|
||||
zero_std_dev_old = zero_std_dev;
|
||||
if (!probe_calibration_points(z_at_pt, probe_points, towers_set, stow_after_each)) {
|
||||
if (!probe_calibration_points(z_at_pt, probe_points, towers_set, stow_after_each, probe_at_offset)) {
|
||||
SERIAL_ECHOLNPGM("Correct delta settings with M665 and M666");
|
||||
return AC_CLEANUP();
|
||||
return ac_cleanup(TERN_(HAS_MULTI_HOTEND, old_tool_index));
|
||||
}
|
||||
zero_std_dev = std_dev_points(z_at_pt, _0p_calibration, _1p_calibration, _4p_calibration, _4p_opposite_points);
|
||||
|
||||
@@ -525,11 +537,11 @@ void GcodeSuite::G33() {
|
||||
#define Z0(I) ZP(0, I)
|
||||
|
||||
// calculate factors
|
||||
if (_7p_9_center) calibration_radius_factor = 0.9f;
|
||||
if (_7p_9_center) dcr *= 0.9f;
|
||||
h_factor = auto_tune_h();
|
||||
r_factor = auto_tune_r();
|
||||
a_factor = auto_tune_a();
|
||||
calibration_radius_factor = 1.0f;
|
||||
dcr /= 0.9f;
|
||||
|
||||
switch (probe_points) {
|
||||
case 0:
|
||||
@@ -538,7 +550,7 @@ void GcodeSuite::G33() {
|
||||
|
||||
case 1:
|
||||
test_precision = 0.0f; // forced end
|
||||
LOOP_XYZ(axis) e_delta[axis] = +Z4(CEN);
|
||||
LOOP_LINEAR_AXES(axis) e_delta[axis] = +Z4(CEN);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
@@ -586,21 +598,21 @@ void GcodeSuite::G33() {
|
||||
// Normalize angles to least-squares
|
||||
if (_angle_results) {
|
||||
float a_sum = 0.0f;
|
||||
LOOP_XYZ(axis) a_sum += delta_tower_angle_trim[axis];
|
||||
LOOP_XYZ(axis) delta_tower_angle_trim[axis] -= a_sum / 3.0f;
|
||||
LOOP_LINEAR_AXES(axis) a_sum += delta_tower_angle_trim[axis];
|
||||
LOOP_LINEAR_AXES(axis) delta_tower_angle_trim[axis] -= a_sum / 3.0f;
|
||||
}
|
||||
|
||||
// adjust delta_height and endstops by the max amount
|
||||
const float z_temp = _MAX(delta_endstop_adj.a, delta_endstop_adj.b, delta_endstop_adj.c);
|
||||
delta_height -= z_temp;
|
||||
LOOP_XYZ(axis) delta_endstop_adj[axis] -= z_temp;
|
||||
LOOP_LINEAR_AXES(axis) delta_endstop_adj[axis] -= z_temp;
|
||||
}
|
||||
recalc_delta_settings();
|
||||
NOMORE(zero_std_dev_min, zero_std_dev);
|
||||
|
||||
// print report
|
||||
|
||||
if (verbose_level == 3)
|
||||
if (verbose_level == 3 || verbose_level == 0)
|
||||
print_calibration_results(z_at_pt, _tower_results, _opposite_results);
|
||||
|
||||
if (verbose_level != 0) { // !dry run
|
||||
@@ -641,8 +653,8 @@ void GcodeSuite::G33() {
|
||||
}
|
||||
}
|
||||
else { // dry run
|
||||
PGM_P enddryrun = PSTR("End DRY-RUN");
|
||||
serialprintPGM(enddryrun);
|
||||
PGM_P const enddryrun = PSTR("End DRY-RUN");
|
||||
SERIAL_ECHOPGM_P(enddryrun);
|
||||
SERIAL_ECHO_SP(35);
|
||||
SERIAL_ECHOLNPAIR_F("std dev:", zero_std_dev, 3);
|
||||
|
||||
@@ -659,7 +671,9 @@ void GcodeSuite::G33() {
|
||||
}
|
||||
while (((zero_std_dev < test_precision && iterations < 31) || iterations <= force_iterations) && zero_std_dev > calibration_precision);
|
||||
|
||||
AC_CLEANUP();
|
||||
ac_cleanup(TERN_(HAS_MULTI_HOTEND, old_tool_index));
|
||||
|
||||
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE));
|
||||
}
|
||||
|
||||
#endif // DELTA_AUTO_CALIBRATION
|
||||
|
157
Marlin/src/gcode/calibrate/G34.cpp
Normal file
157
Marlin/src/gcode/calibrate/G34.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(MECHANICAL_GANTRY_CALIBRATION)
|
||||
|
||||
#include "../gcode.h"
|
||||
#include "../../module/motion.h"
|
||||
#include "../../module/stepper.h"
|
||||
#include "../../module/endstops.h"
|
||||
|
||||
#if HAS_LEVELING
|
||||
#include "../../feature/bedlevel/bedlevel.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../core/debug_out.h"
|
||||
|
||||
void GcodeSuite::G34() {
|
||||
|
||||
// Home before the alignment procedure
|
||||
home_if_needed();
|
||||
|
||||
TERN_(HAS_LEVELING, TEMPORARY_BED_LEVELING_STATE(false));
|
||||
|
||||
SET_SOFT_ENDSTOP_LOOSE(true);
|
||||
TemporaryGlobalEndstopsState unlock_z(false);
|
||||
|
||||
#ifdef GANTRY_CALIBRATION_COMMANDS_PRE
|
||||
gcode.process_subcommands_now_P(PSTR(GANTRY_CALIBRATION_COMMANDS_PRE));
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Sub Commands Processed");
|
||||
#endif
|
||||
|
||||
#ifdef GANTRY_CALIBRATION_SAFE_POSITION
|
||||
// Move XY to safe position
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Parking XY");
|
||||
const xy_pos_t safe_pos = GANTRY_CALIBRATION_SAFE_POSITION;
|
||||
do_blocking_move_to(safe_pos, MMM_TO_MMS(GANTRY_CALIBRATION_XY_PARK_FEEDRATE));
|
||||
#endif
|
||||
|
||||
const float move_distance = parser.intval('Z', GANTRY_CALIBRATION_EXTRA_HEIGHT),
|
||||
zbase = ENABLED(GANTRY_CALIBRATION_TO_MIN) ? Z_MIN_POS : Z_MAX_POS,
|
||||
zpounce = zbase - move_distance, zgrind = zbase + move_distance;
|
||||
|
||||
// Move Z to pounce position
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Setting Z Pounce");
|
||||
do_blocking_move_to_z(zpounce, homing_feedrate(Z_AXIS));
|
||||
|
||||
// Store current motor settings, then apply reduced value
|
||||
|
||||
#define _REDUCE_CURRENT ANY(HAS_MOTOR_CURRENT_SPI, HAS_MOTOR_CURRENT_PWM, HAS_MOTOR_CURRENT_DAC, HAS_MOTOR_CURRENT_I2C, HAS_TRINAMIC_CONFIG)
|
||||
#if _REDUCE_CURRENT
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Reducing Current");
|
||||
#endif
|
||||
|
||||
#if HAS_MOTOR_CURRENT_SPI
|
||||
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
|
||||
const uint32_t previous_current = stepper.motor_current_setting[Z_AXIS];
|
||||
stepper.set_digipot_current(Z_AXIS, target_current);
|
||||
#elif HAS_MOTOR_CURRENT_PWM
|
||||
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
|
||||
const uint32_t previous_current = stepper.motor_current_setting[Z_AXIS];
|
||||
stepper.set_digipot_current(1, target_current);
|
||||
#elif HAS_MOTOR_CURRENT_DAC
|
||||
const float target_current = parser.floatval('S', GANTRY_CALIBRATION_CURRENT);
|
||||
const float previous_current = dac_amps(Z_AXIS, target_current);
|
||||
stepper_dac.set_current_value(Z_AXIS, target_current);
|
||||
#elif HAS_MOTOR_CURRENT_I2C
|
||||
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
|
||||
previous_current = dac_amps(Z_AXIS);
|
||||
digipot_i2c.set_current(Z_AXIS, target_current)
|
||||
#elif HAS_TRINAMIC_CONFIG
|
||||
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
|
||||
static uint16_t previous_current_arr[NUM_Z_STEPPER_DRIVERS];
|
||||
#if AXIS_IS_TMC(Z)
|
||||
previous_current_arr[0] = stepperZ.getMilliamps();
|
||||
stepperZ.rms_current(target_current);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(Z2)
|
||||
previous_current_arr[1] = stepperZ2.getMilliamps();
|
||||
stepperZ2.rms_current(target_current);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(Z3)
|
||||
previous_current_arr[2] = stepperZ3.getMilliamps();
|
||||
stepperZ3.rms_current(target_current);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(Z4)
|
||||
previous_current_arr[3] = stepperZ4.getMilliamps();
|
||||
stepperZ4.rms_current(target_current);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Do Final Z move to adjust
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Final Z Move");
|
||||
do_blocking_move_to_z(zgrind, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE));
|
||||
|
||||
// Back off end plate, back to normal motion range
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Backoff");
|
||||
do_blocking_move_to_z(zpounce, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE));
|
||||
|
||||
#if _REDUCE_CURRENT
|
||||
// Reset current to original values
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Restore Current");
|
||||
#endif
|
||||
|
||||
#if HAS_MOTOR_CURRENT_SPI
|
||||
stepper.set_digipot_current(Z_AXIS, previous_current);
|
||||
#elif HAS_MOTOR_CURRENT_PWM
|
||||
stepper.set_digipot_current(1, previous_current);
|
||||
#elif HAS_MOTOR_CURRENT_DAC
|
||||
stepper_dac.set_current_value(Z_AXIS, previous_current);
|
||||
#elif HAS_MOTOR_CURRENT_I2C
|
||||
digipot_i2c.set_current(Z_AXIS, previous_current)
|
||||
#elif HAS_TRINAMIC_CONFIG
|
||||
#if AXIS_IS_TMC(Z)
|
||||
stepperZ.rms_current(previous_current_arr[0]);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(Z2)
|
||||
stepperZ2.rms_current(previous_current_arr[1]);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(Z3)
|
||||
stepperZ3.rms_current(previous_current_arr[2]);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(Z4)
|
||||
stepperZ4.rms_current(previous_current_arr[3]);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GANTRY_CALIBRATION_COMMANDS_POST
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Running Post Commands");
|
||||
gcode.process_subcommands_now_P(PSTR(GANTRY_CALIBRATION_COMMANDS_POST));
|
||||
#endif
|
||||
|
||||
SET_SOFT_ENDSTOP_LOOSE(false);
|
||||
}
|
||||
|
||||
#endif // MECHANICAL_GANTRY_CALIBRATION
|
772
Marlin/src/gcode/calibrate/G34_M422.cpp
Executable file → Normal file
772
Marlin/src/gcode/calibrate/G34_M422.cpp
Executable file → Normal file
@@ -16,389 +16,447 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
|
||||
#if EITHER(Z_MULTI_ENDSTOPS, Z_STEPPER_AUTO_ALIGN)
|
||||
|
||||
#include "../../feature/z_stepper_align.h"
|
||||
|
||||
#include "../gcode.h"
|
||||
#include "../../module/planner.h"
|
||||
#include "../../module/stepper.h"
|
||||
#include "../../module/motion.h"
|
||||
#include "../../module/stepper.h"
|
||||
#include "../../module/planner.h"
|
||||
#include "../../module/probe.h"
|
||||
|
||||
#if HOTENDS > 1
|
||||
#include "../../module/tool_change.h"
|
||||
#endif
|
||||
#include "../../lcd/marlinui.h" // for LCD_MESSAGEPGM
|
||||
|
||||
#if HAS_LEVELING
|
||||
#include "../../feature/bedlevel/bedlevel.h"
|
||||
#endif
|
||||
|
||||
#if HAS_MULTI_HOTEND
|
||||
#include "../../module/tool_change.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
#include "../../libs/least_squares_fit.h"
|
||||
#include "../../libs/least_squares_fit.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../core/debug_out.h"
|
||||
|
||||
inline void set_all_z_lock(const bool lock) {
|
||||
stepper.set_z_lock(lock);
|
||||
stepper.set_z2_lock(lock);
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 3
|
||||
stepper.set_z3_lock(lock);
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 4
|
||||
stepper.set_z4_lock(lock);
|
||||
#endif
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 3
|
||||
#define TRIPLE_Z 1
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 4
|
||||
#define QUAD_Z 1
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* G34: Z-Stepper automatic alignment
|
||||
*
|
||||
* I<iterations>
|
||||
* T<accuracy>
|
||||
* A<amplification>
|
||||
* R<recalculate> points based on current probe offsets
|
||||
* Manual stepper lock controls (reset by G28):
|
||||
* L Unlock all steppers
|
||||
* Z<1-4> Z stepper to lock / unlock
|
||||
* S<state> 0=UNLOCKED 1=LOCKED. If omitted, assume LOCKED.
|
||||
*
|
||||
* Examples:
|
||||
* G34 Z1 ; Lock Z1
|
||||
* G34 L Z2 ; Unlock all, then lock Z2
|
||||
* G34 Z2 S0 ; Unlock Z2
|
||||
*
|
||||
* With Z_STEPPER_AUTO_ALIGN:
|
||||
* I<iterations> Number of tests. If omitted, Z_STEPPER_ALIGN_ITERATIONS.
|
||||
* T<accuracy> Target Accuracy factor. If omitted, Z_STEPPER_ALIGN_ACC.
|
||||
* A<amplification> Provide an Amplification value. If omitted, Z_STEPPER_ALIGN_AMP.
|
||||
* R Flag to recalculate points based on current probe offsets
|
||||
*/
|
||||
void GcodeSuite::G34() {
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_ECHOLNPGM(">>> G34");
|
||||
log_machine_info();
|
||||
DEBUG_SECTION(log_G34, "G34", DEBUGGING(LEVELING));
|
||||
if (DEBUGGING(LEVELING)) log_machine_info();
|
||||
|
||||
planner.synchronize(); // Prevent damage
|
||||
|
||||
const bool seenL = parser.seen('L');
|
||||
if (seenL) stepper.set_all_z_lock(false);
|
||||
|
||||
const bool seenZ = parser.seenval('Z');
|
||||
if (seenZ) {
|
||||
const bool state = parser.boolval('S', true);
|
||||
switch (parser.intval('Z')) {
|
||||
case 1: stepper.set_z1_lock(state); break;
|
||||
case 2: stepper.set_z2_lock(state); break;
|
||||
#if TRIPLE_Z
|
||||
case 3: stepper.set_z3_lock(state); break;
|
||||
#if QUAD_Z
|
||||
case 4: stepper.set_z4_lock(state); break;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
do { // break out on error
|
||||
if (seenL || seenZ) {
|
||||
stepper.set_separate_multi_axis(seenZ);
|
||||
return;
|
||||
}
|
||||
|
||||
#if NUM_Z_STEPPER_DRIVERS == 4
|
||||
SERIAL_ECHOLNPGM("Alignment for 4 steppers is Experimental!");
|
||||
#elif NUM_Z_STEPPER_DRIVERS > 4
|
||||
SERIAL_ECHOLNPGM("Alignment not supported for over 4 steppers");
|
||||
break;
|
||||
#endif
|
||||
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
|
||||
do { // break out on error
|
||||
|
||||
const int8_t z_auto_align_iterations = parser.intval('I', Z_STEPPER_ALIGN_ITERATIONS);
|
||||
if (!WITHIN(z_auto_align_iterations, 1, 30)) {
|
||||
SERIAL_ECHOLNPGM("?(I)teration out of bounds (1-30).");
|
||||
break;
|
||||
}
|
||||
const int8_t z_auto_align_iterations = parser.intval('I', Z_STEPPER_ALIGN_ITERATIONS);
|
||||
if (!WITHIN(z_auto_align_iterations, 1, 30)) {
|
||||
SERIAL_ECHOLNPGM("?(I)teration out of bounds (1-30).");
|
||||
break;
|
||||
}
|
||||
|
||||
const float z_auto_align_accuracy = parser.floatval('T', Z_STEPPER_ALIGN_ACC);
|
||||
if (!WITHIN(z_auto_align_accuracy, 0.01f, 1.0f)) {
|
||||
SERIAL_ECHOLNPGM("?(T)arget accuracy out of bounds (0.01-1.0).");
|
||||
break;
|
||||
}
|
||||
const float z_auto_align_accuracy = parser.floatval('T', Z_STEPPER_ALIGN_ACC);
|
||||
if (!WITHIN(z_auto_align_accuracy, 0.01f, 1.0f)) {
|
||||
SERIAL_ECHOLNPGM("?(T)arget accuracy out of bounds (0.01-1.0).");
|
||||
break;
|
||||
}
|
||||
|
||||
const float z_auto_align_amplification =
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
Z_STEPPER_ALIGN_AMP;
|
||||
#else
|
||||
parser.floatval('A', Z_STEPPER_ALIGN_AMP);
|
||||
if (!WITHIN(ABS(z_auto_align_amplification), 0.5f, 2.0f)) {
|
||||
SERIAL_ECHOLNPGM("?(A)mplification out of bounds (0.5-2.0).");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
const float z_auto_align_amplification = TERN(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, Z_STEPPER_ALIGN_AMP, parser.floatval('A', Z_STEPPER_ALIGN_AMP));
|
||||
if (!WITHIN(ABS(z_auto_align_amplification), 0.5f, 2.0f)) {
|
||||
SERIAL_ECHOLNPGM("?(A)mplification out of bounds (0.5-2.0).");
|
||||
break;
|
||||
}
|
||||
|
||||
if (parser.seen('R')) z_stepper_align.reset_to_default();
|
||||
if (parser.seen('R')) z_stepper_align.reset_to_default();
|
||||
|
||||
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
|
||||
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
|
||||
|
||||
// Wait for planner moves to finish!
|
||||
planner.synchronize();
|
||||
|
||||
// Disable the leveling matrix before auto-aligning
|
||||
#if HAS_LEVELING
|
||||
#if ENABLED(RESTORE_LEVELING_AFTER_G34)
|
||||
const bool leveling_was_active = planner.leveling_active;
|
||||
#endif
|
||||
set_bed_leveling_enabled(false);
|
||||
#endif
|
||||
|
||||
#if ENABLED(CNC_WORKSPACE_PLANES)
|
||||
workspace_plane = PLANE_XY;
|
||||
#endif
|
||||
|
||||
// Always home with tool 0 active
|
||||
#if HOTENDS > 1
|
||||
const uint8_t old_tool_index = active_extruder;
|
||||
tool_change(0, true);
|
||||
#endif
|
||||
|
||||
#if HAS_DUPLICATION_MODE
|
||||
extruder_duplication_enabled = false;
|
||||
#endif
|
||||
|
||||
#if BOTH(BLTOUCH, BLTOUCH_HS_MODE)
|
||||
// In BLTOUCH HS mode, the probe travels in a deployed state.
|
||||
// Users of G34 might have a badly misaligned bed, so raise Z by the
|
||||
// length of the deployed pin (BLTOUCH stroke < 7mm)
|
||||
#define Z_BASIC_CLEARANCE Z_CLEARANCE_BETWEEN_PROBES + 7.0f
|
||||
#else
|
||||
#define Z_BASIC_CLEARANCE Z_CLEARANCE_BETWEEN_PROBES
|
||||
#endif
|
||||
|
||||
// Compute a worst-case clearance height to probe from. After the first
|
||||
// iteration this will be re-calculated based on the actual bed position
|
||||
auto magnitude2 = [&](const uint8_t i, const uint8_t j) {
|
||||
const xy_pos_t diff = z_stepper_align.xy[i] - z_stepper_align.xy[j];
|
||||
return HYPOT2(diff.x, diff.y);
|
||||
};
|
||||
float z_probe = Z_BASIC_CLEARANCE + (G34_MAX_GRADE) * 0.01f * SQRT(
|
||||
#if NUM_Z_STEPPER_DRIVERS == 3
|
||||
_MAX(magnitude2(0, 1), magnitude2(1, 2), magnitude2(2, 0))
|
||||
#elif NUM_Z_STEPPER_DRIVERS == 4
|
||||
_MAX(magnitude2(0, 1), magnitude2(1, 2), magnitude2(2, 3),
|
||||
magnitude2(3, 0), magnitude2(0, 2), magnitude2(1, 3))
|
||||
#else
|
||||
magnitude2(0, 1)
|
||||
#endif
|
||||
);
|
||||
|
||||
// Home before the alignment procedure
|
||||
if (!all_axes_known()) home_all_axes();
|
||||
|
||||
// Move the Z coordinate realm towards the positive - dirty trick
|
||||
current_position.z += z_probe * 0.5f;
|
||||
sync_plan_position();
|
||||
// Now, the Z origin lies below the build plate. That allows to probe deeper, before run_z_probe throws an error.
|
||||
// This hack is un-done at the end of G34 - either by re-homing, or by using the probed heights of the last iteration.
|
||||
|
||||
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
float last_z_align_move[NUM_Z_STEPPER_DRIVERS] = ARRAY_N(NUM_Z_STEPPER_DRIVERS, 10000.0f, 10000.0f, 10000.0f, 10000.0f);
|
||||
#else
|
||||
float last_z_align_level_indicator = 10000.0f;
|
||||
#endif
|
||||
float z_measured[NUM_Z_STEPPER_DRIVERS] = { 0 },
|
||||
z_maxdiff = 0.0f,
|
||||
amplification = z_auto_align_amplification;
|
||||
|
||||
// These are needed after the for-loop
|
||||
uint8_t iteration;
|
||||
bool err_break = false;
|
||||
float z_measured_min;
|
||||
|
||||
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
bool adjustment_reverse = false;
|
||||
#endif
|
||||
|
||||
// 'iteration' is declared above and is also used after the for-loop.
|
||||
// *not* the same as LOOP_L_N(iteration, z_auto_align_iterations)
|
||||
for (iteration = 0; iteration < z_auto_align_iterations; ++iteration) {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> probing all positions.");
|
||||
|
||||
SERIAL_ECHOLNPAIR("\nITERATION: ", int(iteration + 1));
|
||||
|
||||
// Initialize minimum value
|
||||
z_measured_min = 100000.0f;
|
||||
float z_measured_max = -100000.0f;
|
||||
|
||||
// Probe all positions (one per Z-Stepper)
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
|
||||
// iteration odd/even --> downward / upward stepper sequence
|
||||
const uint8_t iprobe = (iteration & 1) ? NUM_Z_STEPPER_DRIVERS - 1 - i : i;
|
||||
|
||||
// Safe clearance even on an incline
|
||||
if ((iteration == 0 || i > 0) && z_probe > current_position.z) do_blocking_move_to_z(z_probe);
|
||||
|
||||
if (DEBUGGING(LEVELING))
|
||||
DEBUG_ECHOLNPAIR_P(PSTR("Probing X"), z_stepper_align.xy[iprobe].x, SP_Y_STR, z_stepper_align.xy[iprobe].y);
|
||||
|
||||
// Probe a Z height for each stepper.
|
||||
// Probing sanity check is disabled, as it would trigger even in normal cases because
|
||||
// current_position.z has been manually altered in the "dirty trick" above.
|
||||
const float z_probed_height = probe.probe_at_point(z_stepper_align.xy[iprobe], raise_after, 0, true, false);
|
||||
if (isnan(z_probed_height)) {
|
||||
SERIAL_ECHOLNPGM("Probing failed.");
|
||||
err_break = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Add height to each value, to provide a more useful target height for
|
||||
// the next iteration of probing. This allows adjustments to be made away from the bed.
|
||||
z_measured[iprobe] = z_probed_height + Z_CLEARANCE_BETWEEN_PROBES;
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("> Z", int(iprobe + 1), " measured position is ", z_measured[iprobe]);
|
||||
|
||||
// Remember the minimum measurement to calculate the correction later on
|
||||
z_measured_min = _MIN(z_measured_min, z_measured[iprobe]);
|
||||
z_measured_max = _MAX(z_measured_max, z_measured[iprobe]);
|
||||
} // for (i)
|
||||
|
||||
if (err_break) break;
|
||||
|
||||
// Adapt the next probe clearance height based on the new measurements.
|
||||
// Safe_height = lowest distance to bed (= highest measurement) plus highest measured misalignment.
|
||||
z_maxdiff = z_measured_max - z_measured_min;
|
||||
z_probe = Z_BASIC_CLEARANCE + z_measured_max + z_maxdiff;
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
// Replace the initial values in z_measured with calculated heights at
|
||||
// each stepper position. This allows the adjustment algorithm to be
|
||||
// shared between both possible probing mechanisms.
|
||||
|
||||
// This must be done after the next z_probe height is calculated, so that
|
||||
// the height is calculated from actual print area positions, and not
|
||||
// extrapolated motor movements.
|
||||
|
||||
// Compute the least-squares fit for all probed points.
|
||||
// Calculate the Z position of each stepper and store it in z_measured.
|
||||
// This allows the actual adjustment logic to be shared by both algorithms.
|
||||
linear_fit_data lfd;
|
||||
incremental_LSF_reset(&lfd);
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
|
||||
SERIAL_ECHOLNPAIR("PROBEPT_", int(i), ": ", z_measured[i]);
|
||||
incremental_LSF(&lfd, z_stepper_align.xy[i], z_measured[i]);
|
||||
}
|
||||
finish_incremental_LSF(&lfd);
|
||||
|
||||
z_measured_min = 100000.0f;
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
|
||||
z_measured[i] = -(lfd.A * z_stepper_align.stepper_xy[i].x + lfd.B * z_stepper_align.stepper_xy[i].y + lfd.D);
|
||||
z_measured_min = _MIN(z_measured_min, z_measured[i]);
|
||||
}
|
||||
|
||||
SERIAL_ECHOLNPAIR("CALCULATED STEPPER POSITIONS: Z1=", z_measured[0], " Z2=", z_measured[1], " Z3=", z_measured[2]);
|
||||
#endif
|
||||
|
||||
SERIAL_ECHOLNPAIR("\n"
|
||||
"DIFFERENCE Z1-Z2=", ABS(z_measured[0] - z_measured[1])
|
||||
#if NUM_Z_STEPPER_DRIVERS == 3
|
||||
, " Z2-Z3=", ABS(z_measured[1] - z_measured[2])
|
||||
, " Z3-Z1=", ABS(z_measured[2] - z_measured[0])
|
||||
// Disable the leveling matrix before auto-aligning
|
||||
#if HAS_LEVELING
|
||||
#if ENABLED(RESTORE_LEVELING_AFTER_G34)
|
||||
const bool leveling_was_active = planner.leveling_active;
|
||||
#endif
|
||||
);
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
// Check if the applied corrections go in the correct direction.
|
||||
// Calculate the sum of the absolute deviations from the mean of the probe measurements.
|
||||
// Compare to the last iteration to ensure it's getting better.
|
||||
|
||||
// Calculate mean value as a reference
|
||||
float z_measured_mean = 0.0f;
|
||||
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) z_measured_mean += z_measured[zstepper];
|
||||
z_measured_mean /= NUM_Z_STEPPER_DRIVERS;
|
||||
|
||||
// Calculate the sum of the absolute deviations from the mean value
|
||||
float z_align_level_indicator = 0.0f;
|
||||
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS)
|
||||
z_align_level_indicator += ABS(z_measured[zstepper] - z_measured_mean);
|
||||
|
||||
// If it's getting worse, stop and throw an error
|
||||
if (last_z_align_level_indicator < z_align_level_indicator * 0.7f) {
|
||||
SERIAL_ECHOLNPGM("Decreasing accuracy detected.");
|
||||
err_break = true;
|
||||
break;
|
||||
}
|
||||
|
||||
last_z_align_level_indicator = z_align_level_indicator;
|
||||
set_bed_leveling_enabled(false);
|
||||
#endif
|
||||
|
||||
// The following correction actions are to be enabled for select Z-steppers only
|
||||
stepper.set_separate_multi_axis(true);
|
||||
TERN_(CNC_WORKSPACE_PLANES, workspace_plane = PLANE_XY);
|
||||
|
||||
bool success_break = true;
|
||||
// Correct the individual stepper offsets
|
||||
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) {
|
||||
// Calculate current stepper move
|
||||
float z_align_move = z_measured[zstepper] - z_measured_min;
|
||||
const float z_align_abs = ABS(z_align_move);
|
||||
// Always home with tool 0 active
|
||||
#if HAS_MULTI_HOTEND
|
||||
const uint8_t old_tool_index = active_extruder;
|
||||
tool_change(0, true);
|
||||
#endif
|
||||
|
||||
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
// Optimize one iteration's correction based on the first measurements
|
||||
if (z_align_abs) amplification = (iteration == 1) ? _MIN(last_z_align_move[zstepper] / z_align_abs, 2.0f) : z_auto_align_amplification;
|
||||
TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false));
|
||||
|
||||
// Check for less accuracy compared to last move
|
||||
if (last_z_align_move[zstepper] < z_align_abs * 0.7f) {
|
||||
SERIAL_ECHOLNPGM("Decreasing accuracy detected.");
|
||||
adjustment_reverse = !adjustment_reverse;
|
||||
// In BLTOUCH HS mode, the probe travels in a deployed state.
|
||||
// Users of G34 might have a badly misaligned bed, so raise Z by the
|
||||
// length of the deployed pin (BLTOUCH stroke < 7mm)
|
||||
#define Z_BASIC_CLEARANCE (Z_CLEARANCE_BETWEEN_PROBES + 7.0f * BOTH(BLTOUCH, BLTOUCH_HS_MODE))
|
||||
|
||||
// Compute a worst-case clearance height to probe from. After the first
|
||||
// iteration this will be re-calculated based on the actual bed position
|
||||
auto magnitude2 = [&](const uint8_t i, const uint8_t j) {
|
||||
const xy_pos_t diff = z_stepper_align.xy[i] - z_stepper_align.xy[j];
|
||||
return HYPOT2(diff.x, diff.y);
|
||||
};
|
||||
float z_probe = Z_BASIC_CLEARANCE + (G34_MAX_GRADE) * 0.01f * SQRT(_MAX(0, magnitude2(0, 1)
|
||||
#if TRIPLE_Z
|
||||
, magnitude2(2, 1), magnitude2(2, 0)
|
||||
#if QUAD_Z
|
||||
, magnitude2(3, 2), magnitude2(3, 1), magnitude2(3, 0)
|
||||
#endif
|
||||
#endif
|
||||
));
|
||||
|
||||
// Home before the alignment procedure
|
||||
home_if_needed();
|
||||
|
||||
// Move the Z coordinate realm towards the positive - dirty trick
|
||||
current_position.z += z_probe * 0.5f;
|
||||
sync_plan_position();
|
||||
// Now, the Z origin lies below the build plate. That allows to probe deeper, before run_z_probe throws an error.
|
||||
// This hack is un-done at the end of G34 - either by re-homing, or by using the probed heights of the last iteration.
|
||||
|
||||
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
float last_z_align_move[NUM_Z_STEPPER_DRIVERS] = ARRAY_N_1(NUM_Z_STEPPER_DRIVERS, 10000.0f);
|
||||
#else
|
||||
float last_z_align_level_indicator = 10000.0f;
|
||||
#endif
|
||||
float z_measured[NUM_Z_STEPPER_DRIVERS] = { 0 },
|
||||
z_maxdiff = 0.0f,
|
||||
amplification = z_auto_align_amplification;
|
||||
|
||||
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
bool adjustment_reverse = false;
|
||||
#endif
|
||||
|
||||
#if HAS_STATUS_MESSAGE
|
||||
PGM_P const msg_iteration = GET_TEXT(MSG_ITERATION);
|
||||
const uint8_t iter_str_len = strlen_P(msg_iteration);
|
||||
#endif
|
||||
|
||||
// Final z and iteration values will be used after breaking the loop
|
||||
float z_measured_min;
|
||||
uint8_t iteration = 0;
|
||||
bool err_break = false; // To break out of nested loops
|
||||
while (iteration < z_auto_align_iterations) {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> probing all positions.");
|
||||
|
||||
const int iter = iteration + 1;
|
||||
SERIAL_ECHOLNPGM("\nG34 Iteration: ", iter);
|
||||
#if HAS_STATUS_MESSAGE
|
||||
char str[iter_str_len + 2 + 1];
|
||||
sprintf_P(str, msg_iteration, iter);
|
||||
ui.set_status(str);
|
||||
#endif
|
||||
|
||||
// Initialize minimum value
|
||||
z_measured_min = 100000.0f;
|
||||
float z_measured_max = -100000.0f;
|
||||
|
||||
// Probe all positions (one per Z-Stepper)
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
|
||||
// iteration odd/even --> downward / upward stepper sequence
|
||||
const uint8_t iprobe = (iteration & 1) ? NUM_Z_STEPPER_DRIVERS - 1 - i : i;
|
||||
|
||||
// Safe clearance even on an incline
|
||||
if ((iteration == 0 || i > 0) && z_probe > current_position.z) do_blocking_move_to_z(z_probe);
|
||||
|
||||
if (DEBUGGING(LEVELING))
|
||||
DEBUG_ECHOLNPGM_P(PSTR("Probing X"), z_stepper_align.xy[iprobe].x, SP_Y_STR, z_stepper_align.xy[iprobe].y);
|
||||
|
||||
// Probe a Z height for each stepper.
|
||||
// Probing sanity check is disabled, as it would trigger even in normal cases because
|
||||
// current_position.z has been manually altered in the "dirty trick" above.
|
||||
const float z_probed_height = probe.probe_at_point(z_stepper_align.xy[iprobe], raise_after, 0, true, false);
|
||||
if (isnan(z_probed_height)) {
|
||||
SERIAL_ECHOLNPGM("Probing failed");
|
||||
LCD_MESSAGEPGM(MSG_LCD_PROBING_FAILED);
|
||||
err_break = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Remember the alignment for the next iteration
|
||||
last_z_align_move[zstepper] = z_align_abs;
|
||||
// Add height to each value, to provide a more useful target height for
|
||||
// the next iteration of probing. This allows adjustments to be made away from the bed.
|
||||
z_measured[iprobe] = z_probed_height + Z_CLEARANCE_BETWEEN_PROBES;
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", iprobe + 1, " measured position is ", z_measured[iprobe]);
|
||||
|
||||
// Remember the minimum measurement to calculate the correction later on
|
||||
z_measured_min = _MIN(z_measured_min, z_measured[iprobe]);
|
||||
z_measured_max = _MAX(z_measured_max, z_measured[iprobe]);
|
||||
} // for (i)
|
||||
|
||||
if (err_break) break;
|
||||
|
||||
// Adapt the next probe clearance height based on the new measurements.
|
||||
// Safe_height = lowest distance to bed (= highest measurement) plus highest measured misalignment.
|
||||
z_maxdiff = z_measured_max - z_measured_min;
|
||||
z_probe = Z_BASIC_CLEARANCE + z_measured_max + z_maxdiff;
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
// Replace the initial values in z_measured with calculated heights at
|
||||
// each stepper position. This allows the adjustment algorithm to be
|
||||
// shared between both possible probing mechanisms.
|
||||
|
||||
// This must be done after the next z_probe height is calculated, so that
|
||||
// the height is calculated from actual print area positions, and not
|
||||
// extrapolated motor movements.
|
||||
|
||||
// Compute the least-squares fit for all probed points.
|
||||
// Calculate the Z position of each stepper and store it in z_measured.
|
||||
// This allows the actual adjustment logic to be shared by both algorithms.
|
||||
linear_fit_data lfd;
|
||||
incremental_LSF_reset(&lfd);
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
|
||||
SERIAL_ECHOLNPGM("PROBEPT_", i, ": ", z_measured[i]);
|
||||
incremental_LSF(&lfd, z_stepper_align.xy[i], z_measured[i]);
|
||||
}
|
||||
finish_incremental_LSF(&lfd);
|
||||
|
||||
z_measured_min = 100000.0f;
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
|
||||
z_measured[i] = -(lfd.A * z_stepper_align.stepper_xy[i].x + lfd.B * z_stepper_align.stepper_xy[i].y + lfd.D);
|
||||
z_measured_min = _MIN(z_measured_min, z_measured[i]);
|
||||
}
|
||||
|
||||
SERIAL_ECHOLNPGM(
|
||||
LIST_N(DOUBLE(NUM_Z_STEPPER_DRIVERS),
|
||||
"Calculated Z1=", z_measured[0],
|
||||
" Z2=", z_measured[1],
|
||||
" Z3=", z_measured[2],
|
||||
" Z4=", z_measured[3]
|
||||
)
|
||||
);
|
||||
#endif
|
||||
|
||||
// Stop early if all measured points achieve accuracy target
|
||||
if (z_align_abs > z_auto_align_accuracy) success_break = false;
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("> Z", int(zstepper + 1), " corrected by ", z_align_move);
|
||||
|
||||
// Lock all steppers except one
|
||||
set_all_z_lock(true);
|
||||
switch (zstepper) {
|
||||
case 0: stepper.set_z_lock(false); break;
|
||||
case 1: stepper.set_z2_lock(false); break;
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 3
|
||||
case 2: stepper.set_z3_lock(false); break;
|
||||
SERIAL_ECHOLNPGM("\n"
|
||||
"Z2-Z1=", ABS(z_measured[1] - z_measured[0])
|
||||
#if TRIPLE_Z
|
||||
, " Z3-Z2=", ABS(z_measured[2] - z_measured[1])
|
||||
, " Z3-Z1=", ABS(z_measured[2] - z_measured[0])
|
||||
#if QUAD_Z
|
||||
, " Z4-Z3=", ABS(z_measured[3] - z_measured[2])
|
||||
, " Z4-Z2=", ABS(z_measured[3] - z_measured[1])
|
||||
, " Z4-Z1=", ABS(z_measured[3] - z_measured[0])
|
||||
#endif
|
||||
#endif
|
||||
#if NUM_Z_STEPPER_DRIVERS == 4
|
||||
case 3: stepper.set_z4_lock(false); break;
|
||||
);
|
||||
|
||||
#if HAS_STATUS_MESSAGE
|
||||
char fstr1[10];
|
||||
char msg[6 + (6 + 5) * NUM_Z_STEPPER_DRIVERS + 1]
|
||||
#if TRIPLE_Z
|
||||
, fstr2[10], fstr3[10]
|
||||
#if QUAD_Z
|
||||
, fstr4[10], fstr5[10], fstr6[10]
|
||||
#endif
|
||||
#endif
|
||||
;
|
||||
sprintf_P(msg,
|
||||
PSTR("1:2=%s" TERN_(TRIPLE_Z, " 3-2=%s 3-1=%s") TERN_(QUAD_Z, " 4-3=%s 4-2=%s 4-1=%s")),
|
||||
dtostrf(ABS(z_measured[1] - z_measured[0]), 1, 3, fstr1)
|
||||
OPTARG(TRIPLE_Z,
|
||||
dtostrf(ABS(z_measured[2] - z_measured[1]), 1, 3, fstr2),
|
||||
dtostrf(ABS(z_measured[2] - z_measured[0]), 1, 3, fstr3))
|
||||
OPTARG(QUAD_Z,
|
||||
dtostrf(ABS(z_measured[3] - z_measured[2]), 1, 3, fstr4),
|
||||
dtostrf(ABS(z_measured[3] - z_measured[1]), 1, 3, fstr5),
|
||||
dtostrf(ABS(z_measured[3] - z_measured[0]), 1, 3, fstr6))
|
||||
);
|
||||
ui.set_status(msg);
|
||||
#endif
|
||||
|
||||
auto decreasing_accuracy = [](const_float_t v1, const_float_t v2) {
|
||||
if (v1 < v2 * 0.7f) {
|
||||
SERIAL_ECHOLNPGM("Decreasing Accuracy Detected.");
|
||||
LCD_MESSAGEPGM(MSG_DECREASING_ACCURACY);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
// Check if the applied corrections go in the correct direction.
|
||||
// Calculate the sum of the absolute deviations from the mean of the probe measurements.
|
||||
// Compare to the last iteration to ensure it's getting better.
|
||||
|
||||
// Calculate mean value as a reference
|
||||
float z_measured_mean = 0.0f;
|
||||
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) z_measured_mean += z_measured[zstepper];
|
||||
z_measured_mean /= NUM_Z_STEPPER_DRIVERS;
|
||||
|
||||
// Calculate the sum of the absolute deviations from the mean value
|
||||
float z_align_level_indicator = 0.0f;
|
||||
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS)
|
||||
z_align_level_indicator += ABS(z_measured[zstepper] - z_measured_mean);
|
||||
|
||||
// If it's getting worse, stop and throw an error
|
||||
err_break = decreasing_accuracy(last_z_align_level_indicator, z_align_level_indicator);
|
||||
if (err_break) break;
|
||||
|
||||
last_z_align_level_indicator = z_align_level_indicator;
|
||||
#endif
|
||||
|
||||
// The following correction actions are to be enabled for select Z-steppers only
|
||||
stepper.set_separate_multi_axis(true);
|
||||
|
||||
bool success_break = true;
|
||||
// Correct the individual stepper offsets
|
||||
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) {
|
||||
// Calculate current stepper move
|
||||
float z_align_move = z_measured[zstepper] - z_measured_min;
|
||||
const float z_align_abs = ABS(z_align_move);
|
||||
|
||||
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
// Optimize one iteration's correction based on the first measurements
|
||||
if (z_align_abs) amplification = (iteration == 1) ? _MIN(last_z_align_move[zstepper] / z_align_abs, 2.0f) : z_auto_align_amplification;
|
||||
|
||||
// Check for less accuracy compared to last move
|
||||
if (decreasing_accuracy(last_z_align_move[zstepper], z_align_abs)) {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " last_z_align_move = ", last_z_align_move[zstepper]);
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " z_align_abs = ", z_align_abs);
|
||||
adjustment_reverse = !adjustment_reverse;
|
||||
}
|
||||
|
||||
// Remember the alignment for the next iteration, but only if steppers move,
|
||||
// otherwise it would be just zero (in case this stepper was at z_measured_min already)
|
||||
if (z_align_abs > 0) last_z_align_move[zstepper] = z_align_abs;
|
||||
#endif
|
||||
|
||||
// Stop early if all measured points achieve accuracy target
|
||||
if (z_align_abs > z_auto_align_accuracy) success_break = false;
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " corrected by ", z_align_move);
|
||||
|
||||
// Lock all steppers except one
|
||||
stepper.set_all_z_lock(true, zstepper);
|
||||
|
||||
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
// Decreasing accuracy was detected so move was inverted.
|
||||
// Will match reversed Z steppers on dual steppers. Triple will need more work to map.
|
||||
if (adjustment_reverse) {
|
||||
z_align_move = -z_align_move;
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " correction reversed to ", z_align_move);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Do a move to correct part of the misalignment for the current stepper
|
||||
do_blocking_move_to_z(amplification * z_align_move + current_position.z);
|
||||
} // for (zstepper)
|
||||
|
||||
// Back to normal stepper operations
|
||||
stepper.set_all_z_lock(false);
|
||||
stepper.set_separate_multi_axis(false);
|
||||
|
||||
if (err_break) break;
|
||||
|
||||
if (success_break) {
|
||||
SERIAL_ECHOLNPGM("Target accuracy achieved.");
|
||||
LCD_MESSAGEPGM(MSG_ACCURACY_ACHIEVED);
|
||||
break;
|
||||
}
|
||||
|
||||
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
// Decreasing accuracy was detected so move was inverted.
|
||||
// Will match reversed Z steppers on dual steppers. Triple will need more work to map.
|
||||
if (adjustment_reverse)
|
||||
z_align_move = -z_align_move;
|
||||
#endif
|
||||
iteration++;
|
||||
} // while (iteration < z_auto_align_iterations)
|
||||
|
||||
// Do a move to correct part of the misalignment for the current stepper
|
||||
do_blocking_move_to_z(amplification * z_align_move + current_position.z);
|
||||
} // for (zstepper)
|
||||
if (err_break)
|
||||
SERIAL_ECHOLNPGM("G34 aborted.");
|
||||
else {
|
||||
SERIAL_ECHOLNPGM("Did ", iteration + (iteration != z_auto_align_iterations), " of ", z_auto_align_iterations);
|
||||
SERIAL_ECHOLNPAIR_F("Accuracy: ", z_maxdiff);
|
||||
}
|
||||
|
||||
// Back to normal stepper operations
|
||||
set_all_z_lock(false);
|
||||
stepper.set_separate_multi_axis(false);
|
||||
// Stow the probe because the last call to probe.probe_at_point(...)
|
||||
// leaves the probe deployed when it's successful.
|
||||
IF_DISABLED(TOUCH_MI_PROBE, probe.stow());
|
||||
|
||||
if (err_break) break;
|
||||
#if ENABLED(HOME_AFTER_G34)
|
||||
// After this operation the z position needs correction
|
||||
set_axis_never_homed(Z_AXIS);
|
||||
// Home Z after the alignment procedure
|
||||
process_subcommands_now_P(PSTR("G28Z"));
|
||||
#else
|
||||
// Use the probed height from the last iteration to determine the Z height.
|
||||
// z_measured_min is used, because all steppers are aligned to z_measured_min.
|
||||
// Ideally, this would be equal to the 'z_probe * 0.5f' which was added earlier.
|
||||
current_position.z -= z_measured_min - (float)Z_CLEARANCE_BETWEEN_PROBES;
|
||||
sync_plan_position();
|
||||
#endif
|
||||
|
||||
if (success_break) { SERIAL_ECHOLNPGM("Target accuracy achieved."); break; }
|
||||
// Restore the active tool after homing
|
||||
TERN_(HAS_MULTI_HOTEND, tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER))); // Fetch previous tool for parking extruder
|
||||
|
||||
} // for (iteration)
|
||||
#if BOTH(HAS_LEVELING, RESTORE_LEVELING_AFTER_G34)
|
||||
set_bed_leveling_enabled(leveling_was_active);
|
||||
#endif
|
||||
|
||||
if (err_break)
|
||||
SERIAL_ECHOLNPGM("G34 aborted.");
|
||||
else {
|
||||
SERIAL_ECHOLNPAIR("Did ", int(iteration + (iteration != z_auto_align_iterations)), " of ", int(z_auto_align_iterations));
|
||||
SERIAL_ECHOLNPAIR_F("Accuracy: ", z_maxdiff);
|
||||
}
|
||||
|
||||
// Stow the probe, as the last call to probe.probe_at_point(...) left
|
||||
// the probe deployed if it was successful.
|
||||
probe.stow();
|
||||
|
||||
#if ENABLED(HOME_AFTER_G34)
|
||||
// After this operation the z position needs correction
|
||||
set_axis_not_trusted(Z_AXIS);
|
||||
// Home Z after the alignment procedure
|
||||
process_subcommands_now_P(PSTR("G28Z"));
|
||||
#else
|
||||
// Use the probed height from the last iteration to determine the Z height.
|
||||
// z_measured_min is used, because all steppers are aligned to z_measured_min.
|
||||
// Ideally, this would be equal to the 'z_probe * 0.5f' which was added earlier.
|
||||
current_position.z -= z_measured_min - (float)Z_CLEARANCE_BETWEEN_PROBES;
|
||||
sync_plan_position();
|
||||
#endif
|
||||
|
||||
// Restore the active tool after homing
|
||||
#if HOTENDS > 1
|
||||
tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER)); // Fetch previous tool for parking extruder
|
||||
#endif
|
||||
|
||||
#if HAS_LEVELING && ENABLED(RESTORE_LEVELING_AFTER_G34)
|
||||
set_bed_leveling_enabled(leveling_was_active);
|
||||
#endif
|
||||
|
||||
}while(0);
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G34");
|
||||
}while(0);
|
||||
#endif // Z_STEPPER_AUTO_ALIGN
|
||||
}
|
||||
|
||||
#endif // Z_MULTI_ENDSTOPS || Z_STEPPER_AUTO_ALIGN
|
||||
|
||||
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
|
||||
|
||||
/**
|
||||
* M422: Set a Z-Stepper automatic alignment XY point.
|
||||
* Use repeatedly to set multiple points.
|
||||
@@ -417,49 +475,27 @@ void GcodeSuite::G34() {
|
||||
*/
|
||||
void GcodeSuite::M422() {
|
||||
|
||||
if (!parser.seen_any()) return M422_report();
|
||||
|
||||
if (parser.seen('R')) {
|
||||
z_stepper_align.reset_to_default();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!parser.seen_any()) {
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS)
|
||||
SERIAL_ECHOLNPAIR_P(PSTR("M422 S"), int(i + 1), SP_X_STR, z_stepper_align.xy[i].x, SP_Y_STR, z_stepper_align.xy[i].y);
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS)
|
||||
SERIAL_ECHOLNPAIR_P(PSTR("M422 W"), int(i + 1), SP_X_STR, z_stepper_align.stepper_xy[i].x, SP_Y_STR, z_stepper_align.stepper_xy[i].y);
|
||||
#endif
|
||||
const bool is_probe_point = parser.seen('S');
|
||||
|
||||
if (TERN0(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, is_probe_point && parser.seen('W'))) {
|
||||
SERIAL_ECHOLNPGM("?(S) and (W) may not be combined.");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_probe_point = parser.seen('S');
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
if (is_probe_point && parser.seen('W')) {
|
||||
SERIAL_ECHOLNPGM("?(S) and (W) may not be combined.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
xy_pos_t *pos_dest = (
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
!is_probe_point ? z_stepper_align.stepper_xy :
|
||||
#endif
|
||||
TERN_(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, !is_probe_point ? z_stepper_align.stepper_xy :)
|
||||
z_stepper_align.xy
|
||||
);
|
||||
|
||||
if (!is_probe_point
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
&& !parser.seen('W')
|
||||
#endif
|
||||
) {
|
||||
SERIAL_ECHOLNPGM(
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
"?(S) or (W) is required."
|
||||
#else
|
||||
"?(S) is required."
|
||||
#endif
|
||||
);
|
||||
if (!is_probe_point && TERN1(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, !parser.seen('W'))) {
|
||||
SERIAL_ECHOLNPGM("?(S)" TERN_(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, " or (W)") " is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -468,7 +504,7 @@ void GcodeSuite::M422() {
|
||||
if (is_probe_point) {
|
||||
position_index = parser.intval('S') - 1;
|
||||
if (!WITHIN(position_index, 0, int8_t(NUM_Z_STEPPER_DRIVERS) - 1)) {
|
||||
SERIAL_ECHOLNPGM("?(S) Z-ProbePosition index invalid.");
|
||||
SERIAL_ECHOLNPGM("?(S) Probe-position index invalid.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -476,7 +512,7 @@ void GcodeSuite::M422() {
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
position_index = parser.intval('W') - 1;
|
||||
if (!WITHIN(position_index, 0, NUM_Z_STEPPER_DRIVERS - 1)) {
|
||||
SERIAL_ECHOLNPGM("?(W) Z-Stepper index invalid.");
|
||||
SERIAL_ECHOLNPGM("?(W) Z-stepper index invalid.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -501,4 +537,26 @@ void GcodeSuite::M422() {
|
||||
pos_dest[position_index] = pos;
|
||||
}
|
||||
|
||||
void GcodeSuite::M422_report(const bool forReplay/*=true*/) {
|
||||
report_heading(forReplay, PSTR(STR_Z_AUTO_ALIGN));
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
|
||||
report_echo_start(forReplay);
|
||||
SERIAL_ECHOLNPGM_P(
|
||||
PSTR(" M422 S"), i + 1,
|
||||
SP_X_STR, z_stepper_align.xy[i].x,
|
||||
SP_Y_STR, z_stepper_align.xy[i].y
|
||||
);
|
||||
}
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
|
||||
report_echo_start(forReplay);
|
||||
SERIAL_ECHOLNPGM_P(
|
||||
PSTR(" M422 W"), i + 1,
|
||||
SP_X_STR, z_stepper_align.stepper_xy[i].x,
|
||||
SP_Y_STR, z_stepper_align.stepper_xy[i].y
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // Z_STEPPER_AUTO_ALIGN
|
||||
|
362
Marlin/src/gcode/calibrate/G425.cpp
Executable file → Normal file
362
Marlin/src/gcode/calibrate/G425.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "../../feature/backlash.h"
|
||||
#endif
|
||||
|
||||
#include "../../lcd/ultralcd.h"
|
||||
#include "../../lcd/marlinui.h"
|
||||
#include "../../module/motion.h"
|
||||
#include "../../module/planner.h"
|
||||
#include "../../module/tool_change.h"
|
||||
@@ -51,7 +51,6 @@
|
||||
#undef CALIBRATION_MEASURE_AT_TOP_EDGES
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* G425 backs away from the calibration object by various distances
|
||||
* depending on the confidence level:
|
||||
@@ -74,11 +73,23 @@
|
||||
#if BOTH(CALIBRATION_MEASURE_LEFT, CALIBRATION_MEASURE_RIGHT)
|
||||
#define HAS_X_CENTER 1
|
||||
#endif
|
||||
#if BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK)
|
||||
#if HAS_Y_AXIS && BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK)
|
||||
#define HAS_Y_CENTER 1
|
||||
#endif
|
||||
#if LINEAR_AXES >= 4 && BOTH(CALIBRATION_MEASURE_IMIN, CALIBRATION_MEASURE_IMAX)
|
||||
#define HAS_I_CENTER 1
|
||||
#endif
|
||||
#if LINEAR_AXES >= 5 && BOTH(CALIBRATION_MEASURE_JMIN, CALIBRATION_MEASURE_JMAX)
|
||||
#define HAS_J_CENTER 1
|
||||
#endif
|
||||
#if LINEAR_AXES >= 6 && BOTH(CALIBRATION_MEASURE_KMIN, CALIBRATION_MEASURE_KMAX)
|
||||
#define HAS_K_CENTER 1
|
||||
#endif
|
||||
|
||||
enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES };
|
||||
enum side_t : uint8_t {
|
||||
TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES,
|
||||
LIST_N(DOUBLE(SUB3(LINEAR_AXES)), IMINIMUM, IMAXIMUM, JMINIMUM, JMAXIMUM, KMINIMUM, KMAXIMUM)
|
||||
};
|
||||
|
||||
static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER;
|
||||
static constexpr xyz_float_t dimensions CALIBRATION_OBJECT_DIMENSIONS;
|
||||
@@ -93,8 +104,6 @@ struct measurements_t {
|
||||
xy_float_t nozzle_outer_dimension = nod;
|
||||
};
|
||||
|
||||
#define TEMPORARY_SOFT_ENDSTOP_STATE(enable) REMEMBER(tes, soft_endstops_enabled, enable);
|
||||
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
#define TEMPORARY_BACKLASH_CORRECTION(value) REMEMBER(tbst, backlash.correction, value)
|
||||
#else
|
||||
@@ -108,7 +117,7 @@ struct measurements_t {
|
||||
#endif
|
||||
|
||||
inline void calibration_move() {
|
||||
do_blocking_move_to(current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
|
||||
do_blocking_move_to((xyz_pos_t)current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +136,7 @@ inline void park_above_object(measurements_t &m, const float uncertainty) {
|
||||
calibration_move();
|
||||
}
|
||||
|
||||
#if HOTENDS > 1
|
||||
#if HAS_MULTI_HOTEND
|
||||
inline void set_nozzle(measurements_t &m, const uint8_t extruder) {
|
||||
if (extruder != active_extruder) {
|
||||
park_above_object(m, CALIBRATION_MEASUREMENT_UNKNOWN);
|
||||
@@ -146,14 +155,16 @@ inline void park_above_object(measurements_t &m, const float uncertainty) {
|
||||
|
||||
#endif
|
||||
|
||||
#if !PIN_EXISTS(CALIBRATION)
|
||||
#include "../../module/probe.h"
|
||||
#endif
|
||||
|
||||
inline bool read_calibration_pin() {
|
||||
return (
|
||||
#if PIN_EXISTS(CALIBRATION)
|
||||
READ(CALIBRATION_PIN) != CALIBRATION_PIN_INVERTING
|
||||
#elif HAS_CUSTOM_PROBE_PIN
|
||||
READ(Z_MIN_PROBE_PIN) != Z_MIN_PROBE_ENDSTOP_INVERTING
|
||||
#else
|
||||
READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING
|
||||
PROBE_TRIGGERED()
|
||||
#endif
|
||||
);
|
||||
}
|
||||
@@ -175,7 +186,7 @@ float measuring_movement(const AxisEnum axis, const int dir, const bool stop_sta
|
||||
destination = current_position;
|
||||
for (float travel = 0; travel < limit; travel += step) {
|
||||
destination[axis] += dir * step;
|
||||
do_blocking_move_to(destination, mms);
|
||||
do_blocking_move_to((xyz_pos_t)destination, mms);
|
||||
planner.synchronize();
|
||||
if (read_calibration_pin() == stop_state) break;
|
||||
}
|
||||
@@ -195,18 +206,22 @@ float measuring_movement(const AxisEnum axis, const int dir, const bool stop_sta
|
||||
inline float measure(const AxisEnum axis, const int dir, const bool stop_state, float * const backlash_ptr, const float uncertainty) {
|
||||
const bool fast = uncertainty == CALIBRATION_MEASUREMENT_UNKNOWN;
|
||||
|
||||
// Save position
|
||||
destination = current_position;
|
||||
const float start_pos = destination[axis];
|
||||
// Save the current position of the specified axis
|
||||
const float start_pos = current_position[axis];
|
||||
|
||||
// Take a measurement. Only the specified axis will be affected.
|
||||
const float measured_pos = measuring_movement(axis, dir, stop_state, fast);
|
||||
|
||||
// Measure backlash
|
||||
if (backlash_ptr && !fast) {
|
||||
const float release_pos = measuring_movement(axis, -dir, !stop_state, fast);
|
||||
*backlash_ptr = ABS(release_pos - measured_pos);
|
||||
}
|
||||
// Return to starting position
|
||||
|
||||
// Move back to the starting position
|
||||
destination = current_position;
|
||||
destination[axis] = start_pos;
|
||||
do_blocking_move_to(destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
|
||||
do_blocking_move_to((xyz_pos_t)destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
|
||||
return measured_pos;
|
||||
}
|
||||
|
||||
@@ -227,7 +242,15 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
|
||||
park_above_object(m, uncertainty);
|
||||
|
||||
switch (side) {
|
||||
#if AXIS_CAN_CALIBRATE(Z)
|
||||
#if AXIS_CAN_CALIBRATE(X)
|
||||
case RIGHT: dir = -1;
|
||||
case LEFT: axis = X_AXIS; break;
|
||||
#endif
|
||||
#if LINEAR_AXES >= 2 && AXIS_CAN_CALIBRATE(Y)
|
||||
case BACK: dir = -1;
|
||||
case FRONT: axis = Y_AXIS; break;
|
||||
#endif
|
||||
#if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
|
||||
case TOP: {
|
||||
const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
|
||||
m.obj_center.z = measurement - dimensions.z / 2;
|
||||
@@ -235,13 +258,17 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if AXIS_CAN_CALIBRATE(X)
|
||||
case LEFT: axis = X_AXIS; break;
|
||||
case RIGHT: axis = X_AXIS; dir = -1; break;
|
||||
#if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I)
|
||||
case IMINIMUM: dir = -1;
|
||||
case IMAXIMUM: axis = I_AXIS; break;
|
||||
#endif
|
||||
#if AXIS_CAN_CALIBRATE(Y)
|
||||
case FRONT: axis = Y_AXIS; break;
|
||||
case BACK: axis = Y_AXIS; dir = -1; break;
|
||||
#if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J)
|
||||
case JMINIMUM: dir = -1;
|
||||
case JMAXIMUM: axis = J_AXIS; break;
|
||||
#endif
|
||||
#if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K)
|
||||
case KMINIMUM: dir = -1;
|
||||
case KMAXIMUM: axis = K_AXIS; break;
|
||||
#endif
|
||||
default: return;
|
||||
}
|
||||
@@ -256,7 +283,7 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
|
||||
#endif
|
||||
}
|
||||
|
||||
if (AXIS_CAN_CALIBRATE(X) && axis == X_AXIS || AXIS_CAN_CALIBRATE(Y) && axis == Y_AXIS) {
|
||||
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
|
||||
current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty);
|
||||
calibration_move();
|
||||
@@ -286,72 +313,86 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
|
||||
probe_side(m, uncertainty, TOP);
|
||||
#endif
|
||||
|
||||
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
|
||||
probe_side(m, uncertainty, RIGHT, probe_top_at_edge);
|
||||
#endif
|
||||
|
||||
#if ENABLED(CALIBRATION_MEASURE_FRONT)
|
||||
probe_side(m, uncertainty, FRONT, probe_top_at_edge);
|
||||
#endif
|
||||
|
||||
#if ENABLED(CALIBRATION_MEASURE_LEFT)
|
||||
probe_side(m, uncertainty, LEFT, probe_top_at_edge);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_BACK)
|
||||
probe_side(m, uncertainty, BACK, probe_top_at_edge);
|
||||
#endif
|
||||
TERN_(CALIBRATION_MEASURE_RIGHT, probe_side(m, uncertainty, RIGHT, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_FRONT, probe_side(m, uncertainty, FRONT, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_LEFT, probe_side(m, uncertainty, LEFT, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_BACK, probe_side(m, uncertainty, BACK, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_IMIN, probe_side(m, uncertainty, IMINIMUM, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_IMAX, probe_side(m, uncertainty, IMAXIMUM, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_JMIN, probe_side(m, uncertainty, JMINIMUM, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_JMAX, probe_side(m, uncertainty, JMAXIMUM, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_KMIN, probe_side(m, uncertainty, KMINIMUM, probe_top_at_edge));
|
||||
TERN_(CALIBRATION_MEASURE_KMAX, probe_side(m, uncertainty, KMAXIMUM, probe_top_at_edge));
|
||||
|
||||
// Compute the measured center of the calibration object.
|
||||
#if HAS_X_CENTER
|
||||
m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2;
|
||||
#endif
|
||||
#if HAS_Y_CENTER
|
||||
m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2;
|
||||
#endif
|
||||
TERN_(HAS_X_CENTER, m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2);
|
||||
TERN_(HAS_Y_CENTER, m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2);
|
||||
TERN_(HAS_I_CENTER, m.obj_center.i = (m.obj_side[IMINIMUM] + m.obj_side[IMAXIMUM]) / 2);
|
||||
TERN_(HAS_J_CENTER, m.obj_center.j = (m.obj_side[JMINIMUM] + m.obj_side[JMAXIMUM]) / 2);
|
||||
TERN_(HAS_K_CENTER, m.obj_center.k = (m.obj_side[KMINIMUM] + m.obj_side[KMAXIMUM]) / 2);
|
||||
|
||||
// Compute the outside diameter of the nozzle at the height
|
||||
// at which it makes contact with the calibration object
|
||||
#if HAS_X_CENTER
|
||||
m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x;
|
||||
#endif
|
||||
#if HAS_Y_CENTER
|
||||
m.nozzle_outer_dimension.y = m.obj_side[BACK] - m.obj_side[FRONT] - dimensions.y;
|
||||
#endif
|
||||
TERN_(HAS_X_CENTER, m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x);
|
||||
TERN_(HAS_Y_CENTER, m.nozzle_outer_dimension.y = m.obj_side[BACK] - m.obj_side[FRONT] - dimensions.y);
|
||||
|
||||
park_above_object(m, uncertainty);
|
||||
|
||||
// The difference between the known and the measured location
|
||||
// of the calibration object is the positional error
|
||||
m.pos_error.x = (0
|
||||
#if HAS_X_CENTER
|
||||
+ true_center.x - m.obj_center.x
|
||||
#endif
|
||||
LINEAR_AXIS_CODE(
|
||||
m.pos_error.x = TERN0(HAS_X_CENTER, true_center.x - m.obj_center.x),
|
||||
m.pos_error.y = TERN0(HAS_Y_CENTER, true_center.y - m.obj_center.y),
|
||||
m.pos_error.z = true_center.z - m.obj_center.z,
|
||||
m.pos_error.i = TERN0(HAS_I_CENTER, true_center.i - m.obj_center.i),
|
||||
m.pos_error.j = TERN0(HAS_J_CENTER, true_center.j - m.obj_center.j),
|
||||
m.pos_error.k = TERN0(HAS_K_CENTER, true_center.k - m.obj_center.k)
|
||||
);
|
||||
m.pos_error.y = (0
|
||||
#if HAS_Y_CENTER
|
||||
+ true_center.y - m.obj_center.y
|
||||
#endif
|
||||
);
|
||||
m.pos_error.z = true_center.z - m.obj_center.z;
|
||||
}
|
||||
|
||||
#if ENABLED(CALIBRATION_REPORTING)
|
||||
inline void report_measured_faces(const measurements_t &m) {
|
||||
SERIAL_ECHOLNPGM("Sides:");
|
||||
#if AXIS_CAN_CALIBRATE(Z)
|
||||
SERIAL_ECHOLNPAIR(" Top: ", m.obj_side[TOP]);
|
||||
#if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
|
||||
SERIAL_ECHOLNPGM(" Top: ", m.obj_side[TOP]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_LEFT)
|
||||
SERIAL_ECHOLNPAIR(" Left: ", m.obj_side[LEFT]);
|
||||
SERIAL_ECHOLNPGM(" Left: ", m.obj_side[LEFT]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
|
||||
SERIAL_ECHOLNPAIR(" Right: ", m.obj_side[RIGHT]);
|
||||
SERIAL_ECHOLNPGM(" Right: ", m.obj_side[RIGHT]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_FRONT)
|
||||
SERIAL_ECHOLNPAIR(" Front: ", m.obj_side[FRONT]);
|
||||
#if HAS_Y_AXIS
|
||||
#if ENABLED(CALIBRATION_MEASURE_FRONT)
|
||||
SERIAL_ECHOLNPGM(" Front: ", m.obj_side[FRONT]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_BACK)
|
||||
SERIAL_ECHOLNPGM(" Back: ", m.obj_side[BACK]);
|
||||
#endif
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_BACK)
|
||||
SERIAL_ECHOLNPAIR(" Back: ", m.obj_side[BACK]);
|
||||
#if LINEAR_AXES >= 4
|
||||
#if ENABLED(CALIBRATION_MEASURE_IMIN)
|
||||
SERIAL_ECHOLNPGM(" " STR_I_MIN ": ", m.obj_side[IMINIMUM]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_IMAX)
|
||||
SERIAL_ECHOLNPGM(" " STR_I_MAX ": ", m.obj_side[IMAXIMUM]);
|
||||
#endif
|
||||
#endif
|
||||
#if LINEAR_AXES >= 5
|
||||
#if ENABLED(CALIBRATION_MEASURE_JMIN)
|
||||
SERIAL_ECHOLNPGM(" " STR_J_MIN ": ", m.obj_side[JMINIMUM]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_JMAX)
|
||||
SERIAL_ECHOLNPGM(" " STR_J_MAX ": ", m.obj_side[JMAXIMUM]);
|
||||
#endif
|
||||
#endif
|
||||
#if LINEAR_AXES >= 6
|
||||
#if ENABLED(CALIBRATION_MEASURE_KMIN)
|
||||
SERIAL_ECHOLNPGM(" " STR_K_MIN ": ", m.obj_side[KMINIMUM]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_KMAX)
|
||||
SERIAL_ECHOLNPGM(" " STR_K_MAX ": ", m.obj_side[KMAXIMUM]);
|
||||
#endif
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
}
|
||||
@@ -359,12 +400,21 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
|
||||
inline void report_measured_center(const measurements_t &m) {
|
||||
SERIAL_ECHOLNPGM("Center:");
|
||||
#if HAS_X_CENTER
|
||||
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.obj_center.x);
|
||||
SERIAL_ECHOLNPGM_P(SP_X_STR, m.obj_center.x);
|
||||
#endif
|
||||
#if HAS_Y_CENTER
|
||||
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.obj_center.y);
|
||||
SERIAL_ECHOLNPGM_P(SP_Y_STR, m.obj_center.y);
|
||||
#endif
|
||||
SERIAL_ECHOLNPGM_P(SP_Z_STR, m.obj_center.z);
|
||||
#if HAS_I_CENTER
|
||||
SERIAL_ECHOLNPGM_P(SP_I_STR, m.obj_center.i);
|
||||
#endif
|
||||
#if HAS_J_CENTER
|
||||
SERIAL_ECHOLNPGM_P(SP_J_STR, m.obj_center.j);
|
||||
#endif
|
||||
#if HAS_K_CENTER
|
||||
SERIAL_ECHOLNPGM_P(SP_K_STR, m.obj_center.k);
|
||||
#endif
|
||||
SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.obj_center.z);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
@@ -372,53 +422,85 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
|
||||
SERIAL_ECHOLNPGM("Backlash:");
|
||||
#if AXIS_CAN_CALIBRATE(X)
|
||||
#if ENABLED(CALIBRATION_MEASURE_LEFT)
|
||||
SERIAL_ECHOLNPAIR(" Left: ", m.backlash[LEFT]);
|
||||
SERIAL_ECHOLNPGM(" Left: ", m.backlash[LEFT]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
|
||||
SERIAL_ECHOLNPAIR(" Right: ", m.backlash[RIGHT]);
|
||||
SERIAL_ECHOLNPGM(" Right: ", m.backlash[RIGHT]);
|
||||
#endif
|
||||
#endif
|
||||
#if AXIS_CAN_CALIBRATE(Y)
|
||||
#if HAS_Y_AXIS && AXIS_CAN_CALIBRATE(Y)
|
||||
#if ENABLED(CALIBRATION_MEASURE_FRONT)
|
||||
SERIAL_ECHOLNPAIR(" Front: ", m.backlash[FRONT]);
|
||||
SERIAL_ECHOLNPGM(" Front: ", m.backlash[FRONT]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_BACK)
|
||||
SERIAL_ECHOLNPAIR(" Back: ", m.backlash[BACK]);
|
||||
SERIAL_ECHOLNPGM(" Back: ", m.backlash[BACK]);
|
||||
#endif
|
||||
#endif
|
||||
#if AXIS_CAN_CALIBRATE(Z)
|
||||
SERIAL_ECHOLNPAIR(" Top: ", m.backlash[TOP]);
|
||||
#if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
|
||||
SERIAL_ECHOLNPGM(" Top: ", m.backlash[TOP]);
|
||||
#endif
|
||||
#if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I)
|
||||
#if ENABLED(CALIBRATION_MEASURE_IMIN)
|
||||
SERIAL_ECHOLNPGM(" " STR_I_MIN ": ", m.backlash[IMINIMUM]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_IMAX)
|
||||
SERIAL_ECHOLNPGM(" " STR_I_MAX ": ", m.backlash[IMAXIMUM]);
|
||||
#endif
|
||||
#endif
|
||||
#if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J)
|
||||
#if ENABLED(CALIBRATION_MEASURE_JMIN)
|
||||
SERIAL_ECHOLNPGM(" " STR_J_MIN ": ", m.backlash[JMINIMUM]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_JMAX)
|
||||
SERIAL_ECHOLNPGM(" " STR_J_MAX ": ", m.backlash[JMAXIMUM]);
|
||||
#endif
|
||||
#endif
|
||||
#if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K)
|
||||
#if ENABLED(CALIBRATION_MEASURE_KMIN)
|
||||
SERIAL_ECHOLNPGM(" " STR_K_MIN ": ", m.backlash[KMINIMUM]);
|
||||
#endif
|
||||
#if ENABLED(CALIBRATION_MEASURE_KMAX)
|
||||
SERIAL_ECHOLNPGM(" " STR_K_MAX ": ", m.backlash[KMAXIMUM]);
|
||||
#endif
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
inline void report_measured_positional_error(const measurements_t &m) {
|
||||
SERIAL_CHAR('T');
|
||||
SERIAL_ECHO(int(active_extruder));
|
||||
SERIAL_ECHO(active_extruder);
|
||||
SERIAL_ECHOLNPGM(" Positional Error:");
|
||||
#if HAS_X_CENTER
|
||||
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.pos_error.x);
|
||||
#if HAS_X_CENTER && AXIS_CAN_CALIBRATE(X)
|
||||
SERIAL_ECHOLNPGM_P(SP_X_STR, m.pos_error.x);
|
||||
#endif
|
||||
#if HAS_Y_CENTER
|
||||
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.pos_error.y);
|
||||
#if HAS_Y_CENTER && AXIS_CAN_CALIBRATE(Y)
|
||||
SERIAL_ECHOLNPGM_P(SP_Y_STR, m.pos_error.y);
|
||||
#endif
|
||||
#if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
|
||||
SERIAL_ECHOLNPGM_P(SP_Z_STR, m.pos_error.z);
|
||||
#endif
|
||||
#if HAS_I_CENTER && AXIS_CAN_CALIBRATE(I)
|
||||
SERIAL_ECHOLNPGM_P(SP_I_STR, m.pos_error.i);
|
||||
#endif
|
||||
#if HAS_J_CENTER && AXIS_CAN_CALIBRATE(J)
|
||||
SERIAL_ECHOLNPGM_P(SP_J_STR, m.pos_error.j);
|
||||
#endif
|
||||
#if HAS_K_CENTER && AXIS_CAN_CALIBRATE(K)
|
||||
SERIAL_ECHOLNPGM_P(SP_Z_STR, m.pos_error.z);
|
||||
#endif
|
||||
if (AXIS_CAN_CALIBRATE(Z)) SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.pos_error.z);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
inline void report_measured_nozzle_dimensions(const measurements_t &m) {
|
||||
SERIAL_ECHOLNPGM("Nozzle Tip Outer Dimensions:");
|
||||
#if HAS_X_CENTER || HAS_Y_CENTER
|
||||
#if HAS_X_CENTER
|
||||
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.nozzle_outer_dimension.x);
|
||||
#endif
|
||||
#if HAS_Y_CENTER
|
||||
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.nozzle_outer_dimension.y);
|
||||
#endif
|
||||
#else
|
||||
UNUSED(m);
|
||||
#if HAS_X_CENTER
|
||||
SERIAL_ECHOLNPGM_P(SP_X_STR, m.nozzle_outer_dimension.x);
|
||||
#endif
|
||||
#if HAS_Y_CENTER
|
||||
SERIAL_ECHOLNPGM_P(SP_Y_STR, m.nozzle_outer_dimension.y);
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
UNUSED(m);
|
||||
}
|
||||
|
||||
#if HAS_HOTEND_OFFSET
|
||||
@@ -427,7 +509,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
|
||||
//
|
||||
inline void report_hotend_offsets() {
|
||||
LOOP_S_L_N(e, 1, HOTENDS)
|
||||
SERIAL_ECHOLNPAIR_P(PSTR("T"), int(e), PSTR(" Hotend Offset X"), hotend_offset[e].x, SP_Y_STR, hotend_offset[e].y, SP_Z_STR, hotend_offset[e].z);
|
||||
SERIAL_ECHOLNPGM_P(PSTR("T"), e, PSTR(" Hotend Offset X"), hotend_offset[e].x, SP_Y_STR, hotend_offset[e].y, SP_Z_STR, hotend_offset[e].z);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -467,8 +549,33 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
|
||||
backlash.distance_mm.y = m.backlash[BACK];
|
||||
#endif
|
||||
|
||||
if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP];
|
||||
#endif
|
||||
TERN_(HAS_Z_AXIS, if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP]);
|
||||
|
||||
#if HAS_I_CENTER
|
||||
backlash.distance_mm.i = (m.backlash[IMINIMUM] + m.backlash[IMAXIMUM]) / 2;
|
||||
#elif ENABLED(CALIBRATION_MEASURE_IMIN)
|
||||
backlash.distance_mm.i = m.backlash[IMINIMUM];
|
||||
#elif ENABLED(CALIBRATION_MEASURE_IMAX)
|
||||
backlash.distance_mm.i = m.backlash[IMAXIMUM];
|
||||
#endif
|
||||
|
||||
#if HAS_J_CENTER
|
||||
backlash.distance_mm.j = (m.backlash[JMINIMUM] + m.backlash[JMAXIMUM]) / 2;
|
||||
#elif ENABLED(CALIBRATION_MEASURE_JMIN)
|
||||
backlash.distance_mm.j = m.backlash[JMINIMUM];
|
||||
#elif ENABLED(CALIBRATION_MEASURE_JMAX)
|
||||
backlash.distance_mm.j = m.backlash[JMAXIMUM];
|
||||
#endif
|
||||
|
||||
#if HAS_K_CENTER
|
||||
backlash.distance_mm.k = (m.backlash[KMINIMUM] + m.backlash[KMAXIMUM]) / 2;
|
||||
#elif ENABLED(CALIBRATION_MEASURE_KMIN)
|
||||
backlash.distance_mm.k = m.backlash[KMINIMUM];
|
||||
#elif ENABLED(CALIBRATION_MEASURE_KMAX)
|
||||
backlash.distance_mm.k = m.backlash[KMAXIMUM];
|
||||
#endif
|
||||
|
||||
#endif // BACKLASH_GCODE
|
||||
}
|
||||
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
@@ -478,7 +585,10 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
|
||||
// New scope for TEMPORARY_BACKLASH_CORRECTION
|
||||
TEMPORARY_BACKLASH_CORRECTION(all_on);
|
||||
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
|
||||
const xyz_float_t move = { AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3 };
|
||||
const xyz_float_t move = LINEAR_AXIS_ARRAY(
|
||||
AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3,
|
||||
AXIS_CAN_CALIBRATE(I) * 3, AXIS_CAN_CALIBRATE(J) * 3, AXIS_CAN_CALIBRATE(K) * 3
|
||||
);
|
||||
current_position += move; calibration_move();
|
||||
current_position -= move; calibration_move();
|
||||
}
|
||||
@@ -506,11 +616,7 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
|
||||
TEMPORARY_BACKLASH_CORRECTION(all_on);
|
||||
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
|
||||
|
||||
#if HOTENDS > 1
|
||||
set_nozzle(m, extruder);
|
||||
#else
|
||||
UNUSED(extruder);
|
||||
#endif
|
||||
TERN(HAS_MULTI_HOTEND, set_nozzle(m, extruder), UNUSED(extruder));
|
||||
|
||||
probe_sides(m, uncertainty);
|
||||
|
||||
@@ -529,6 +635,10 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
|
||||
if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) update_measurements(m, Y_AXIS);
|
||||
if (AXIS_CAN_CALIBRATE(Z)) update_measurements(m, Z_AXIS);
|
||||
|
||||
TERN_(HAS_I_CENTER, update_measurements(m, I_AXIS));
|
||||
TERN_(HAS_J_CENTER, update_measurements(m, J_AXIS));
|
||||
TERN_(HAS_K_CENTER, update_measurements(m, K_AXIS));
|
||||
|
||||
sync_plan_position();
|
||||
}
|
||||
|
||||
@@ -545,13 +655,9 @@ inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty)
|
||||
|
||||
HOTEND_LOOP() calibrate_toolhead(m, uncertainty, e);
|
||||
|
||||
#if HAS_HOTEND_OFFSET
|
||||
normalize_hotend_offsets();
|
||||
#endif
|
||||
TERN_(HAS_HOTEND_OFFSET, normalize_hotend_offsets());
|
||||
|
||||
#if HOTENDS > 1
|
||||
set_nozzle(m, 0);
|
||||
#endif
|
||||
TERN_(HAS_MULTI_HOTEND, set_nozzle(m, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -568,9 +674,7 @@ inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty)
|
||||
inline void calibrate_all() {
|
||||
measurements_t m;
|
||||
|
||||
#if HAS_HOTEND_OFFSET
|
||||
reset_hotend_offsets();
|
||||
#endif
|
||||
TERN_(HAS_HOTEND_OFFSET, reset_hotend_offsets());
|
||||
|
||||
TEMPORARY_BACKLASH_CORRECTION(all_on);
|
||||
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
|
||||
@@ -578,12 +682,10 @@ inline void calibrate_all() {
|
||||
// Do a fast and rough calibration of the toolheads
|
||||
calibrate_all_toolheads(m, CALIBRATION_MEASUREMENT_UNKNOWN);
|
||||
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
calibrate_backlash(m, CALIBRATION_MEASUREMENT_UNCERTAIN);
|
||||
#endif
|
||||
TERN_(BACKLASH_GCODE, calibrate_backlash(m, CALIBRATION_MEASUREMENT_UNCERTAIN));
|
||||
|
||||
// Cycle the toolheads so the servos settle into their "natural" positions
|
||||
#if HOTENDS > 1
|
||||
#if HAS_MULTI_HOTEND
|
||||
HOTEND_LOOP() set_nozzle(m, e);
|
||||
#endif
|
||||
|
||||
@@ -605,19 +707,23 @@ inline void calibrate_all() {
|
||||
* no args - Perform entire calibration sequence (backlash + position on all toolheads)
|
||||
*/
|
||||
void GcodeSuite::G425() {
|
||||
TEMPORARY_SOFT_ENDSTOP_STATE(false);
|
||||
TEMPORARY_BED_LEVELING_STATE(false);
|
||||
|
||||
if (axis_unhomed_error()) return;
|
||||
#ifdef CALIBRATION_SCRIPT_PRE
|
||||
GcodeSuite::process_subcommands_now_P(PSTR(CALIBRATION_SCRIPT_PRE));
|
||||
#endif
|
||||
|
||||
if (homing_needed_error()) return;
|
||||
|
||||
TEMPORARY_BED_LEVELING_STATE(false);
|
||||
SET_SOFT_ENDSTOP_LOOSE(true);
|
||||
|
||||
measurements_t m;
|
||||
const float uncertainty = parser.floatval('U', CALIBRATION_MEASUREMENT_UNCERTAIN);
|
||||
|
||||
float uncertainty = parser.seenval('U') ? parser.value_float() : CALIBRATION_MEASUREMENT_UNCERTAIN;
|
||||
|
||||
if (parser.seen('B'))
|
||||
if (parser.seen_test('B'))
|
||||
calibrate_backlash(m, uncertainty);
|
||||
else if (parser.seen('T'))
|
||||
calibrate_toolhead(m, uncertainty, parser.has_value() ? parser.value_int() : active_extruder);
|
||||
else if (parser.seen_test('T'))
|
||||
calibrate_toolhead(m, uncertainty, parser.intval('T', active_extruder));
|
||||
#if ENABLED(CALIBRATION_REPORTING)
|
||||
else if (parser.seen('V')) {
|
||||
probe_sides(m, uncertainty);
|
||||
@@ -635,6 +741,12 @@ void GcodeSuite::G425() {
|
||||
#endif
|
||||
else
|
||||
calibrate_all();
|
||||
|
||||
SET_SOFT_ENDSTOP_LOOSE(false);
|
||||
|
||||
#ifdef CALIBRATION_SCRIPT_POST
|
||||
GcodeSuite::process_subcommands_now_P(PSTR(CALIBRATION_SCRIPT_POST));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // CALIBRATION_GCODE
|
||||
|
176
Marlin/src/gcode/calibrate/G76_M871.cpp → Marlin/src/gcode/calibrate/G76_M192_M871.cpp
Executable file → Normal file
176
Marlin/src/gcode/calibrate/G76_M871.cpp → Marlin/src/gcode/calibrate/G76_M192_M871.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "../../module/temperature.h"
|
||||
#include "../../module/probe.h"
|
||||
#include "../../feature/probe_temp_comp.h"
|
||||
#include "../../lcd/marlinui.h"
|
||||
|
||||
/**
|
||||
* G76: calibrate probe and/or bed temperature offsets
|
||||
@@ -45,8 +46,8 @@
|
||||
* - When calibrating bed, probe temperature is held constant.
|
||||
* Compensation values are deltas to first probe measurement at bed temp. = 60°C.
|
||||
* - The hotend will not be heated at any time.
|
||||
* - On my Prusa MK3S clone I put a piece of paper between the probe and the hotend
|
||||
* so the hotend fan would not cool my probe constantly. Alternativly you could just
|
||||
* - On my Průša MK3S clone I put a piece of paper between the probe and the hotend
|
||||
* so the hotend fan would not cool my probe constantly. Alternatively you could just
|
||||
* make sure the fan is not running while running the calibration process.
|
||||
*
|
||||
* Probe calibration:
|
||||
@@ -80,6 +81,12 @@
|
||||
* - `B` - Run bed temperature calibration.
|
||||
* - `P` - Run probe temperature calibration.
|
||||
*/
|
||||
|
||||
static void say_waiting_for() { SERIAL_ECHOPGM("Waiting for "); }
|
||||
static void say_waiting_for_probe_heating() { say_waiting_for(); SERIAL_ECHOLNPGM("probe heating."); }
|
||||
static void say_successfully_calibrated() { SERIAL_ECHOPGM("Successfully calibrated"); }
|
||||
static void say_failed_to_calibrate() { SERIAL_ECHOPGM("!Failed to calibrate"); }
|
||||
|
||||
void GcodeSuite::G76() {
|
||||
// Check if heated bed is available and z-homing is done with probe
|
||||
#if TEMP_SENSOR_BED == 0 || !(HOMING_Z_WITH_PROBE)
|
||||
@@ -96,20 +103,26 @@ void GcodeSuite::G76() {
|
||||
return (timeout && ELAPSED(ms, timeout));
|
||||
};
|
||||
|
||||
auto wait_for_temps = [&](const float tb, const float tp, millis_t &ntr, const millis_t timeout=0) {
|
||||
SERIAL_ECHOLNPGM("Waiting for bed and probe temperature.");
|
||||
while (fabs(thermalManager.degBed() - tb) > 0.1f || thermalManager.degProbe() > tp)
|
||||
auto wait_for_temps = [&](const celsius_t tb, const celsius_t tp, millis_t &ntr, const millis_t timeout=0) {
|
||||
say_waiting_for(); SERIAL_ECHOLNPGM("bed and probe temperature.");
|
||||
while (thermalManager.wholeDegBed() != tb || thermalManager.wholeDegProbe() > tp)
|
||||
if (report_temps(ntr, timeout)) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
auto g76_probe = [](const xy_pos_t &xypos) {
|
||||
do_blocking_move_to_z(5.0); // Raise nozzle before probing
|
||||
const float measured_z = probe.probe_at_point(xypos, PROBE_PT_NONE, 0, false); // verbose=0, probe_relative=false
|
||||
auto g76_probe = [](const TempSensorID sid, celsius_t &targ, const xy_pos_t &nozpos) {
|
||||
do_z_clearance(5.0); // Raise nozzle before probing
|
||||
const float measured_z = probe.probe_at_point(nozpos, PROBE_PT_STOW, 0, false); // verbose=0, probe_relative=false
|
||||
if (isnan(measured_z))
|
||||
SERIAL_ECHOLNPGM("!Received NAN. Aborting.");
|
||||
else
|
||||
else {
|
||||
SERIAL_ECHOLNPAIR_F("Measured: ", measured_z);
|
||||
if (targ == cali_info_init[sid].start_temp)
|
||||
temp_comp.prepare_new_calibration(measured_z);
|
||||
else
|
||||
temp_comp.push_back_new_measurement(sid, measured_z);
|
||||
targ += cali_info_init[sid].temp_res;
|
||||
}
|
||||
return measured_z;
|
||||
};
|
||||
|
||||
@@ -125,8 +138,9 @@ void GcodeSuite::G76() {
|
||||
// Synchronize with planner
|
||||
planner.synchronize();
|
||||
|
||||
const xyz_pos_t parkpos = { temp_comp.park_point_x, temp_comp.park_point_y, temp_comp.park_point_z };
|
||||
const xy_pos_t ppos = { temp_comp.measure_point_x, temp_comp.measure_point_y };
|
||||
const xyz_pos_t parkpos = temp_comp.park_point,
|
||||
probe_pos_xyz = xyz_pos_t(temp_comp.measure_point) + xyz_pos_t({ 0.0f, 0.0f, PTC_PROBE_HEATING_OFFSET }),
|
||||
noz_pos_xyz = probe_pos_xyz - probe.offset_xy; // Nozzle position based on probe position
|
||||
|
||||
if (do_bed_cal || do_probe_cal) {
|
||||
// Ensure park position is reachable
|
||||
@@ -135,7 +149,7 @@ void GcodeSuite::G76() {
|
||||
SERIAL_ECHOLNPGM("!Park");
|
||||
else {
|
||||
// Ensure probe position is reachable
|
||||
reachable = probe.can_reach(ppos);
|
||||
reachable = probe.can_reach(probe_pos_xyz);
|
||||
if (!reachable) SERIAL_ECHOLNPGM("!Probe");
|
||||
}
|
||||
|
||||
@@ -144,14 +158,11 @@ void GcodeSuite::G76() {
|
||||
return;
|
||||
}
|
||||
|
||||
process_subcommands_now_P(PSTR("G28"));
|
||||
process_subcommands_now_P(G28_STR);
|
||||
}
|
||||
|
||||
remember_feedrate_scaling_off();
|
||||
|
||||
// Nozzle position based on probe position
|
||||
const xy_pos_t noz_pos = ppos - probe.offset_xy;
|
||||
|
||||
/******************************************
|
||||
* Calibrate bed temperature offsets
|
||||
******************************************/
|
||||
@@ -159,63 +170,60 @@ void GcodeSuite::G76() {
|
||||
// Report temperatures every second and handle heating timeouts
|
||||
millis_t next_temp_report = millis() + 1000;
|
||||
|
||||
auto report_targets = [&](const celsius_t tb, const celsius_t tp) {
|
||||
SERIAL_ECHOLNPGM("Target Bed:", tb, " Probe:", tp);
|
||||
};
|
||||
|
||||
if (do_bed_cal) {
|
||||
|
||||
uint16_t target_bed = temp_comp.cali_info_init[TSI_BED].start_temp,
|
||||
target_probe = temp_comp.bed_calib_probe_temp;
|
||||
celsius_t target_bed = cali_info_init[TSI_BED].start_temp,
|
||||
target_probe = temp_comp.bed_calib_probe_temp;
|
||||
|
||||
SERIAL_ECHOLNPGM("Waiting for cooling.");
|
||||
while (thermalManager.degBed() > target_bed || thermalManager.degProbe() > target_probe)
|
||||
say_waiting_for(); SERIAL_ECHOLNPGM(" cooling.");
|
||||
while (thermalManager.wholeDegBed() > target_bed || thermalManager.wholeDegProbe() > target_probe)
|
||||
report_temps(next_temp_report);
|
||||
|
||||
// Disable leveling so it won't mess with us
|
||||
#if HAS_LEVELING
|
||||
set_bed_leveling_enabled(false);
|
||||
#endif
|
||||
TERN_(HAS_LEVELING, set_bed_leveling_enabled(false));
|
||||
|
||||
for (;;) {
|
||||
thermalManager.setTargetBed(target_bed);
|
||||
|
||||
SERIAL_ECHOLNPAIR("Target Bed:", target_bed, " Probe:", target_probe);
|
||||
report_targets(target_bed, target_probe);
|
||||
|
||||
// Park nozzle
|
||||
do_blocking_move_to(parkpos);
|
||||
|
||||
// Wait for heatbed to reach target temp and probe to cool below target temp
|
||||
if (wait_for_temps(target_bed, target_probe, next_temp_report, millis() + 900UL * 1000UL)) {
|
||||
if (wait_for_temps(target_bed, target_probe, next_temp_report, millis() + MIN_TO_MS(15))) {
|
||||
SERIAL_ECHOLNPGM("!Bed heating timeout.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Move the nozzle to the probing point and wait for the probe to reach target temp
|
||||
do_blocking_move_to_xy(noz_pos);
|
||||
SERIAL_ECHOLNPGM("Waiting for probe heating.");
|
||||
while (thermalManager.degProbe() < target_probe)
|
||||
do_blocking_move_to(noz_pos_xyz);
|
||||
say_waiting_for_probe_heating();
|
||||
SERIAL_EOL();
|
||||
while (thermalManager.wholeDegProbe() < target_probe)
|
||||
report_temps(next_temp_report);
|
||||
|
||||
const float measured_z = g76_probe(noz_pos);
|
||||
if (isnan(measured_z)) break;
|
||||
|
||||
if (target_bed == temp_comp.cali_info_init[TSI_BED].start_temp)
|
||||
temp_comp.prepare_new_calibration(measured_z);
|
||||
else
|
||||
temp_comp.push_back_new_measurement(TSI_BED, measured_z);
|
||||
|
||||
target_bed += temp_comp.cali_info_init[TSI_BED].temp_res;
|
||||
if (target_bed > temp_comp.max_bed_temp) break;
|
||||
const float measured_z = g76_probe(TSI_BED, target_bed, noz_pos_xyz);
|
||||
if (isnan(measured_z) || target_bed > (BED_MAX_TARGET)) break;
|
||||
}
|
||||
|
||||
SERIAL_ECHOLNPAIR("Retrieved measurements: ", temp_comp.get_index());
|
||||
if (temp_comp.finish_calibration(TSI_BED))
|
||||
SERIAL_ECHOLNPGM("Successfully calibrated bed.");
|
||||
else
|
||||
SERIAL_ECHOLNPGM("!Failed to calibrate bed. Values reset.");
|
||||
SERIAL_ECHOLNPGM("Retrieved measurements: ", temp_comp.get_index());
|
||||
if (temp_comp.finish_calibration(TSI_BED)) {
|
||||
say_successfully_calibrated();
|
||||
SERIAL_ECHOLNPGM(" bed.");
|
||||
}
|
||||
else {
|
||||
say_failed_to_calibrate();
|
||||
SERIAL_ECHOLNPGM(" bed. Values reset.");
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
thermalManager.setTargetBed(0);
|
||||
#if HAS_LEVELING
|
||||
set_bed_leveling_enabled(true);
|
||||
#endif
|
||||
TERN_(HAS_LEVELING, set_bed_leveling_enabled(true));
|
||||
} // do_bed_cal
|
||||
|
||||
/********************************************
|
||||
@@ -228,26 +236,27 @@ void GcodeSuite::G76() {
|
||||
do_blocking_move_to(parkpos);
|
||||
|
||||
// Initialize temperatures
|
||||
const uint16_t target_bed = temp_comp.probe_calib_bed_temp;
|
||||
const celsius_t target_bed = temp_comp.probe_calib_bed_temp;
|
||||
thermalManager.setTargetBed(target_bed);
|
||||
|
||||
uint16_t target_probe = temp_comp.cali_info_init[TSI_PROBE].start_temp;
|
||||
celsius_t target_probe = cali_info_init[TSI_PROBE].start_temp;
|
||||
|
||||
report_targets(target_bed, target_probe);
|
||||
|
||||
// Wait for heatbed to reach target temp and probe to cool below target temp
|
||||
wait_for_temps(target_bed, target_probe, next_temp_report);
|
||||
|
||||
// Disable leveling so it won't mess with us
|
||||
#if HAS_LEVELING
|
||||
set_bed_leveling_enabled(false);
|
||||
#endif
|
||||
TERN_(HAS_LEVELING, set_bed_leveling_enabled(false));
|
||||
|
||||
bool timeout = false;
|
||||
for (;;) {
|
||||
// Move probe to probing point and wait for it to reach target temperature
|
||||
do_blocking_move_to_xy(noz_pos);
|
||||
do_blocking_move_to(noz_pos_xyz);
|
||||
|
||||
SERIAL_ECHOLNPAIR("Waiting for probe heating. Bed:", target_bed, " Probe:", target_probe);
|
||||
const millis_t probe_timeout_ms = millis() + 900UL * 1000UL;
|
||||
say_waiting_for_probe_heating();
|
||||
SERIAL_ECHOLNPGM(" Bed:", target_bed, " Probe:", target_probe);
|
||||
const millis_t probe_timeout_ms = millis() + SEC_TO_MS(900UL);
|
||||
while (thermalManager.degProbe() < target_probe) {
|
||||
if (report_temps(next_temp_report, probe_timeout_ms)) {
|
||||
SERIAL_ECHOLNPGM("!Probe heating timed out.");
|
||||
@@ -257,30 +266,20 @@ void GcodeSuite::G76() {
|
||||
}
|
||||
if (timeout) break;
|
||||
|
||||
const float measured_z = g76_probe(noz_pos);
|
||||
if (isnan(measured_z)) break;
|
||||
|
||||
if (target_probe == temp_comp.cali_info_init[TSI_PROBE].start_temp)
|
||||
temp_comp.prepare_new_calibration(measured_z);
|
||||
else
|
||||
temp_comp.push_back_new_measurement(TSI_PROBE, measured_z);
|
||||
|
||||
target_probe += temp_comp.cali_info_init[TSI_PROBE].temp_res;
|
||||
if (target_probe > temp_comp.cali_info_init[TSI_PROBE].end_temp) break;
|
||||
const float measured_z = g76_probe(TSI_PROBE, target_probe, noz_pos_xyz);
|
||||
if (isnan(measured_z) || target_probe > cali_info_init[TSI_PROBE].end_temp) break;
|
||||
}
|
||||
|
||||
SERIAL_ECHOLNPAIR("Retrieved measurements: ", temp_comp.get_index());
|
||||
SERIAL_ECHOLNPGM("Retrieved measurements: ", temp_comp.get_index());
|
||||
if (temp_comp.finish_calibration(TSI_PROBE))
|
||||
SERIAL_ECHOPGM("Successfully calibrated");
|
||||
say_successfully_calibrated();
|
||||
else
|
||||
SERIAL_ECHOPGM("!Failed to calibrate");
|
||||
say_failed_to_calibrate();
|
||||
SERIAL_ECHOLNPGM(" probe.");
|
||||
|
||||
// Cleanup
|
||||
thermalManager.setTargetBed(0);
|
||||
#if HAS_LEVELING
|
||||
set_bed_leveling_enabled(true);
|
||||
#endif
|
||||
TERN_(HAS_LEVELING, set_bed_leveling_enabled(true));
|
||||
|
||||
SERIAL_ECHOLNPGM("Final compensation values:");
|
||||
temp_comp.print_offsets();
|
||||
@@ -316,17 +315,17 @@ void GcodeSuite::M871() {
|
||||
}
|
||||
else if (parser.seen("BPE")) {
|
||||
if (!parser.seenval('V')) return;
|
||||
const int16_t val = parser.value_int();
|
||||
const int16_t offset_val = parser.value_int();
|
||||
if (!parser.seenval('I')) return;
|
||||
const int16_t idx = parser.value_int();
|
||||
const TempSensorID mod = (parser.seen('B') ? TSI_BED :
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
parser.seen('E') ? TSI_EXT :
|
||||
#endif
|
||||
TSI_PROBE
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
parser.seen('E') ? TSI_EXT :
|
||||
#endif
|
||||
TSI_PROBE
|
||||
);
|
||||
if (idx > 0 && temp_comp.set_offset(mod, idx - 1, val))
|
||||
SERIAL_ECHOLNPAIR("Set value: ", val);
|
||||
if (idx > 0 && temp_comp.set_offset(mod, idx - 1, offset_val))
|
||||
SERIAL_ECHOLNPGM("Set value: ", offset_val);
|
||||
else
|
||||
SERIAL_ECHOLNPGM("!Invalid index. Failed to set value (note: value at index 0 is constant).");
|
||||
|
||||
@@ -335,4 +334,25 @@ void GcodeSuite::M871() {
|
||||
temp_comp.print_offsets();
|
||||
}
|
||||
|
||||
/**
|
||||
* M192: Wait for probe temperature sensor to reach a target
|
||||
*
|
||||
* Select only one of these flags:
|
||||
* R - Wait for heating or cooling
|
||||
* S - Wait only for heating
|
||||
*/
|
||||
void GcodeSuite::M192() {
|
||||
if (DEBUGGING(DRYRUN)) return;
|
||||
|
||||
const bool no_wait_for_cooling = parser.seenval('S');
|
||||
if (!no_wait_for_cooling && ! parser.seenval('R')) {
|
||||
SERIAL_ERROR_MSG("No target temperature set.");
|
||||
return;
|
||||
}
|
||||
|
||||
const celsius_t target_temp = parser.value_celsius();
|
||||
ui.set_status_P(thermalManager.isProbeBelowTemp(target_temp) ? GET_TEXT(MSG_PROBE_HEATING) : GET_TEXT(MSG_PROBE_COOLING));
|
||||
thermalManager.wait_for_probe(target_temp, no_wait_for_cooling);
|
||||
}
|
||||
|
||||
#endif // PROBE_TEMP_COMPENSATION
|
115
Marlin/src/gcode/calibrate/M100.cpp
Executable file → Normal file
115
Marlin/src/gcode/calibrate/M100.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "../gcode.h"
|
||||
#include "../queue.h"
|
||||
#include "../../libs/hex_print_routines.h"
|
||||
#include "../../libs/hex_print.h"
|
||||
|
||||
#include "../../MarlinCore.h" // for idle()
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
* Also, there are two support functions that can be called from a developer's C code.
|
||||
*
|
||||
* uint16_t check_for_free_memory_corruption(PGM_P const free_memory_start);
|
||||
* void M100_dump_routine(PGM_P const title, const char * const start, const char * const end);
|
||||
* void M100_dump_routine(PGM_P const title, const char * const start, const uintptr_t size);
|
||||
*
|
||||
* Initial version by Roxy-3D
|
||||
*/
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
#define TEST_BYTE ((char) 0xE5)
|
||||
|
||||
#if defined(__AVR__) || IS_32BIT_TEENSY
|
||||
#if EITHER(__AVR__, IS_32BIT_TEENSY)
|
||||
|
||||
extern char __bss_end;
|
||||
char *end_bss = &__bss_end,
|
||||
@@ -116,13 +116,18 @@
|
||||
// Utility functions
|
||||
//
|
||||
|
||||
// Location of a variable on its stack frame. Returns a value above
|
||||
// the stack (once the function returns to the caller).
|
||||
char* top_of_stack() {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wreturn-local-addr"
|
||||
|
||||
// Location of a variable in its stack frame.
|
||||
// The returned address will be above the stack (after it returns).
|
||||
char *top_of_stack() {
|
||||
char x;
|
||||
return &x + 1; // x is pulled on return;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// Count the number of test bytes at the specified location.
|
||||
inline int32_t count_test_bytes(const char * const start_free_memory) {
|
||||
for (uint32_t i = 0; i < 32000; i++)
|
||||
@@ -146,13 +151,13 @@ inline int32_t count_test_bytes(const char * const start_free_memory) {
|
||||
* the block. If so, it may indicate memory corruption due to a bad pointer.
|
||||
* Unexpected bytes are flagged in the right column.
|
||||
*/
|
||||
inline void dump_free_memory(char *start_free_memory, char *end_free_memory) {
|
||||
void dump_free_memory(char *start_free_memory, char *end_free_memory) {
|
||||
//
|
||||
// Start and end the dump on a nice 16 byte boundary
|
||||
// (even though the values are not 16-byte aligned).
|
||||
//
|
||||
start_free_memory = (char*)(ptr_int_t(uint32_t(start_free_memory) & ~0xFUL)); // Align to 16-byte boundary
|
||||
end_free_memory = (char*)(ptr_int_t(uint32_t(end_free_memory) | 0xFUL)); // Align end_free_memory to the 15th byte (at or above end_free_memory)
|
||||
start_free_memory = (char*)(uintptr_t(uint32_t(start_free_memory) & ~0xFUL)); // Align to 16-byte boundary
|
||||
end_free_memory = (char*)(uintptr_t(uint32_t(end_free_memory) | 0xFUL)); // Align end_free_memory to the 15th byte (at or above end_free_memory)
|
||||
|
||||
// Dump command main loop
|
||||
while (start_free_memory < end_free_memory) {
|
||||
@@ -177,42 +182,42 @@ inline int32_t count_test_bytes(const char * const start_free_memory) {
|
||||
}
|
||||
}
|
||||
|
||||
void M100_dump_routine(PGM_P const title, const char * const start, const char * const end) {
|
||||
serialprintPGM(title);
|
||||
SERIAL_EOL();
|
||||
void M100_dump_routine(PGM_P const title, const char * const start, const uintptr_t size) {
|
||||
SERIAL_ECHOLNPGM_P(title);
|
||||
//
|
||||
// Round the start and end locations to produce full lines of output
|
||||
//
|
||||
const char * const end = start + size - 1;
|
||||
dump_free_memory(
|
||||
(char*)(ptr_int_t(uint32_t(start) & ~0xFUL)), // Align to 16-byte boundary
|
||||
(char*)(ptr_int_t(uint32_t(end) | 0xFUL)) // Align end_free_memory to the 15th byte (at or above end_free_memory)
|
||||
(char*)(uintptr_t(uint32_t(start) & ~0xFUL)), // Align to 16-byte boundary
|
||||
(char*)(uintptr_t(uint32_t(end) | 0xFUL)) // Align end_free_memory to the 15th byte (at or above end_free_memory)
|
||||
);
|
||||
}
|
||||
|
||||
#endif // M100_FREE_MEMORY_DUMPER
|
||||
|
||||
inline int check_for_free_memory_corruption(PGM_P const title) {
|
||||
serialprintPGM(title);
|
||||
SERIAL_ECHOPGM_P(title);
|
||||
|
||||
char *start_free_memory = free_memory_start, *end_free_memory = free_memory_end;
|
||||
int n = end_free_memory - start_free_memory;
|
||||
|
||||
SERIAL_ECHOPAIR("\nfmc() n=", n);
|
||||
SERIAL_ECHOPAIR("\nfree_memory_start=", hex_address(free_memory_start));
|
||||
SERIAL_ECHOLNPAIR(" end_free_memory=", hex_address(end_free_memory));
|
||||
SERIAL_ECHOLNPGM("\nfmc() n=", n,
|
||||
"\nfree_memory_start=", hex_address(free_memory_start),
|
||||
" end=", hex_address(end_free_memory));
|
||||
|
||||
if (end_free_memory < start_free_memory) {
|
||||
SERIAL_ECHOPGM(" end_free_memory < Heap ");
|
||||
// SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board
|
||||
// safe_delay(5); // this code can be enabled to pause the display as soon as the
|
||||
// while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch
|
||||
// idle(); // being on pin-63 which is unassigend and available on most controller
|
||||
// safe_delay(20); // boards.
|
||||
// while ( !READ(63))
|
||||
// idle();
|
||||
//SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board
|
||||
//safe_delay(5); // this code can be enabled to pause the display as soon as the
|
||||
//while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch
|
||||
// idle(); // being on pin-63 which is unassigend and available on most controller
|
||||
//safe_delay(20); // boards.
|
||||
//while ( !READ(63))
|
||||
// idle();
|
||||
serial_delay(20);
|
||||
#if ENABLED(M100_FREE_MEMORY_DUMPER)
|
||||
M100_dump_routine(PSTR(" Memory corruption detected with end_free_memory<Heap\n"), (const char*)0x1B80, (const char*)0x21FF);
|
||||
M100_dump_routine(PSTR(" Memory corruption detected with end_free_memory<Heap\n"), (const char*)0x1B80, 0x0680);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -222,17 +227,15 @@ inline int check_for_free_memory_corruption(PGM_P const title) {
|
||||
if (start_free_memory[i] == TEST_BYTE) {
|
||||
int32_t j = count_test_bytes(start_free_memory + i);
|
||||
if (j > 8) {
|
||||
// SERIAL_ECHOPAIR("Found ", j);
|
||||
// SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(start_free_memory + i));
|
||||
//SERIAL_ECHOPGM("Found ", j);
|
||||
//SERIAL_ECHOLNPGM(" bytes free at ", hex_address(start_free_memory + i));
|
||||
i += j;
|
||||
block_cnt++;
|
||||
SERIAL_ECHOPAIR(" (", block_cnt);
|
||||
SERIAL_ECHOPAIR(") found=", j);
|
||||
SERIAL_ECHOLNPGM(" ");
|
||||
SERIAL_ECHOLNPGM(" (", block_cnt, ") found=", j);
|
||||
}
|
||||
}
|
||||
}
|
||||
SERIAL_ECHOPAIR(" block_found=", block_cnt);
|
||||
SERIAL_ECHOPGM(" block_found=", block_cnt);
|
||||
|
||||
if (block_cnt != 1)
|
||||
SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
|
||||
@@ -264,8 +267,7 @@ inline void free_memory_pool_report(char * const start_free_memory, const int32_
|
||||
if (*addr == TEST_BYTE) {
|
||||
const int32_t j = count_test_bytes(addr);
|
||||
if (j > 8) {
|
||||
SERIAL_ECHOPAIR("Found ", j);
|
||||
SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(addr));
|
||||
SERIAL_ECHOLNPGM("Found ", j, " bytes free at ", hex_address(addr));
|
||||
if (j > max_cnt) {
|
||||
max_cnt = j;
|
||||
max_addr = addr;
|
||||
@@ -275,12 +277,11 @@ inline void free_memory_pool_report(char * const start_free_memory, const int32_
|
||||
}
|
||||
}
|
||||
}
|
||||
if (block_cnt > 1) {
|
||||
SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
|
||||
SERIAL_ECHOPAIR("\nLargest free block is ", max_cnt);
|
||||
SERIAL_ECHOLNPAIR(" bytes at ", hex_address(max_addr));
|
||||
}
|
||||
SERIAL_ECHOLNPAIR("check_for_free_memory_corruption() = ", check_for_free_memory_corruption(PSTR("M100 F ")));
|
||||
if (block_cnt > 1) SERIAL_ECHOLNPGM(
|
||||
"\nMemory Corruption detected in free memory area."
|
||||
"\nLargest free block is ", max_cnt, " bytes at ", hex_address(max_addr)
|
||||
);
|
||||
SERIAL_ECHOLNPGM("check_for_free_memory_corruption() = ", check_for_free_memory_corruption(PSTR("M100 F ")));
|
||||
}
|
||||
|
||||
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
|
||||
@@ -289,16 +290,16 @@ inline void free_memory_pool_report(char * const start_free_memory, const int32_
|
||||
* Corrupt <num> locations in the free memory pool and report the corrupt addresses.
|
||||
* This is useful to check the correctness of the M100 D and the M100 F commands.
|
||||
*/
|
||||
inline void corrupt_free_memory(char *start_free_memory, const uint32_t size) {
|
||||
inline void corrupt_free_memory(char *start_free_memory, const uintptr_t size) {
|
||||
start_free_memory += 8;
|
||||
const uint32_t near_top = top_of_stack() - start_free_memory - 250, // -250 to avoid interrupt activity that's altered the stack.
|
||||
j = near_top / (size + 1);
|
||||
|
||||
SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
|
||||
SERIAL_ECHOLNPGM("Corrupting free memory block.");
|
||||
for (uint32_t i = 1; i <= size; i++) {
|
||||
char * const addr = start_free_memory + i * j;
|
||||
*addr = i;
|
||||
SERIAL_ECHOPAIR("\nCorrupting address: ", hex_address(addr));
|
||||
SERIAL_ECHOPGM("\nCorrupting address: ", hex_address(addr));
|
||||
}
|
||||
SERIAL_EOL();
|
||||
}
|
||||
@@ -317,8 +318,8 @@ inline void init_free_memory(char *start_free_memory, int32_t size) {
|
||||
return;
|
||||
}
|
||||
|
||||
start_free_memory += 8; // move a few bytes away from the heap just because we don't want
|
||||
// to be altering memory that close to it.
|
||||
start_free_memory += 8; // move a few bytes away from the heap just because we
|
||||
// don't want to be altering memory that close to it.
|
||||
memset(start_free_memory, TEST_BYTE, size);
|
||||
|
||||
SERIAL_ECHO(size);
|
||||
@@ -326,8 +327,8 @@ inline void init_free_memory(char *start_free_memory, int32_t size) {
|
||||
|
||||
for (int32_t i = 0; i < size; i++) {
|
||||
if (start_free_memory[i] != TEST_BYTE) {
|
||||
SERIAL_ECHOPAIR("? address : ", hex_address(start_free_memory + i));
|
||||
SERIAL_ECHOLNPAIR("=", hex_byte(start_free_memory[i]));
|
||||
SERIAL_ECHOPGM("? address : ", hex_address(start_free_memory + i));
|
||||
SERIAL_ECHOLNPGM("=", hex_byte(start_free_memory[i]));
|
||||
SERIAL_EOL();
|
||||
}
|
||||
}
|
||||
@@ -337,16 +338,16 @@ inline void init_free_memory(char *start_free_memory, int32_t size) {
|
||||
* M100: Free Memory Check
|
||||
*/
|
||||
void GcodeSuite::M100() {
|
||||
|
||||
char *sp = top_of_stack();
|
||||
if (!free_memory_end) free_memory_end = sp - MEMORY_END_CORRECTION;
|
||||
SERIAL_ECHOPAIR("\nbss_end : ", hex_address(end_bss));
|
||||
if (heaplimit) SERIAL_ECHOPAIR("\n__heaplimit : ", hex_address(heaplimit));
|
||||
SERIAL_ECHOPAIR("\nfree_memory_start : ", hex_address(free_memory_start));
|
||||
if (stacklimit) SERIAL_ECHOPAIR("\n__stacklimit : ", hex_address(stacklimit));
|
||||
SERIAL_ECHOPAIR("\nfree_memory_end : ", hex_address(free_memory_end));
|
||||
if (MEMORY_END_CORRECTION) SERIAL_ECHOPAIR("\nMEMORY_END_CORRECTION: ", MEMORY_END_CORRECTION);
|
||||
SERIAL_ECHOLNPAIR("\nStack Pointer : ", hex_address(sp));
|
||||
SERIAL_ECHOPGM("\nbss_end : ", hex_address(end_bss));
|
||||
if (heaplimit) SERIAL_ECHOPGM("\n__heaplimit : ", hex_address(heaplimit));
|
||||
SERIAL_ECHOPGM("\nfree_memory_start : ", hex_address(free_memory_start));
|
||||
if (stacklimit) SERIAL_ECHOPGM("\n__stacklimit : ", hex_address(stacklimit));
|
||||
SERIAL_ECHOPGM("\nfree_memory_end : ", hex_address(free_memory_end));
|
||||
if (MEMORY_END_CORRECTION)
|
||||
SERIAL_ECHOPGM("\nMEMORY_END_CORRECTION : ", MEMORY_END_CORRECTION);
|
||||
SERIAL_ECHOLNPGM("\nStack Pointer : ", hex_address(sp));
|
||||
|
||||
// Always init on the first invocation of M100
|
||||
static bool m100_not_initialized = true;
|
||||
@@ -364,10 +365,8 @@ void GcodeSuite::M100() {
|
||||
return free_memory_pool_report(free_memory_start, free_memory_end - free_memory_start);
|
||||
|
||||
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
|
||||
|
||||
if (parser.seen('C'))
|
||||
return corrupt_free_memory(free_memory_start, parser.value_int());
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
8
Marlin/src/gcode/calibrate/M12.cpp
Executable file → Normal file
8
Marlin/src/gcode/calibrate/M12.cpp
Executable file → Normal file
@@ -16,9 +16,10 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
|
||||
@@ -28,9 +29,12 @@
|
||||
#include "../../feature/closedloop.h"
|
||||
|
||||
void GcodeSuite::M12() {
|
||||
|
||||
planner.synchronize();
|
||||
|
||||
if (parser.seenval('S'))
|
||||
set_closedloop(parser.value_int()); // Force a CLC set
|
||||
closedloop.set(parser.value_int()); // Force a CLC set
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
50
Marlin/src/gcode/calibrate/M425.cpp
Executable file → Normal file
50
Marlin/src/gcode/calibrate/M425.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -46,8 +46,22 @@
|
||||
void GcodeSuite::M425() {
|
||||
bool noArgs = true;
|
||||
|
||||
LOOP_XYZ(a) {
|
||||
if (CAN_CALIBRATE(a) && parser.seen(XYZ_CHAR(a))) {
|
||||
auto axis_can_calibrate = [](const uint8_t a) {
|
||||
switch (a) {
|
||||
default: return false;
|
||||
LINEAR_AXIS_CODE(
|
||||
case X_AXIS: return AXIS_CAN_CALIBRATE(X),
|
||||
case Y_AXIS: return AXIS_CAN_CALIBRATE(Y),
|
||||
case Z_AXIS: return AXIS_CAN_CALIBRATE(Z),
|
||||
case I_AXIS: return AXIS_CAN_CALIBRATE(I),
|
||||
case J_AXIS: return AXIS_CAN_CALIBRATE(J),
|
||||
case K_AXIS: return AXIS_CAN_CALIBRATE(K),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
LOOP_LINEAR_AXES(a) {
|
||||
if (axis_can_calibrate(a) && parser.seen(AXIS_CHAR(a))) {
|
||||
planner.synchronize();
|
||||
backlash.distance_mm[a] = parser.has_value() ? parser.value_linear_units() : backlash.get_measurement(AxisEnum(a));
|
||||
noArgs = false;
|
||||
@@ -72,23 +86,23 @@ void GcodeSuite::M425() {
|
||||
SERIAL_ECHOPGM("Backlash Correction ");
|
||||
if (!backlash.correction) SERIAL_ECHOPGM("in");
|
||||
SERIAL_ECHOLNPGM("active:");
|
||||
SERIAL_ECHOLNPAIR(" Correction Amount/Fade-out: F", backlash.get_correction(), " (F1.0 = full, F0.0 = none)");
|
||||
SERIAL_ECHOLNPGM(" Correction Amount/Fade-out: F", backlash.get_correction(), " (F1.0 = full, F0.0 = none)");
|
||||
SERIAL_ECHOPGM(" Backlash Distance (mm): ");
|
||||
LOOP_XYZ(a) if (CAN_CALIBRATE(a)) {
|
||||
SERIAL_CHAR(' ', XYZ_CHAR(a));
|
||||
LOOP_LINEAR_AXES(a) if (axis_can_calibrate(a)) {
|
||||
SERIAL_CHAR(' ', AXIS_CHAR(a));
|
||||
SERIAL_ECHO(backlash.distance_mm[a]);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
SERIAL_ECHOLNPAIR(" Smoothing (mm): S", backlash.smoothing_mm);
|
||||
SERIAL_ECHOLNPGM(" Smoothing (mm): S", backlash.smoothing_mm);
|
||||
#endif
|
||||
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
SERIAL_ECHOPGM(" Average measured backlash (mm):");
|
||||
if (backlash.has_any_measurement()) {
|
||||
LOOP_XYZ(a) if (CAN_CALIBRATE(a) && backlash.has_measurement(AxisEnum(a))) {
|
||||
SERIAL_CHAR(' ', XYZ_CHAR(a));
|
||||
LOOP_LINEAR_AXES(a) if (axis_can_calibrate(a) && backlash.has_measurement(AxisEnum(a))) {
|
||||
SERIAL_CHAR(' ', AXIS_CHAR(a));
|
||||
SERIAL_ECHO(backlash.get_measurement(AxisEnum(a)));
|
||||
}
|
||||
}
|
||||
@@ -99,4 +113,22 @@ void GcodeSuite::M425() {
|
||||
}
|
||||
}
|
||||
|
||||
void GcodeSuite::M425_report(const bool forReplay/*=true*/) {
|
||||
report_heading_etc(forReplay, PSTR(STR_BACKLASH_COMPENSATION));
|
||||
SERIAL_ECHOLNPGM_P(
|
||||
PSTR(" M425 F"), backlash.get_correction()
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
, PSTR(" S"), LINEAR_UNIT(backlash.smoothing_mm)
|
||||
#endif
|
||||
, LIST_N(DOUBLE(LINEAR_AXES),
|
||||
SP_X_STR, LINEAR_UNIT(backlash.distance_mm.x),
|
||||
SP_Y_STR, LINEAR_UNIT(backlash.distance_mm.y),
|
||||
SP_Z_STR, LINEAR_UNIT(backlash.distance_mm.z),
|
||||
SP_I_STR, LINEAR_UNIT(backlash.distance_mm.i),
|
||||
SP_J_STR, LINEAR_UNIT(backlash.distance_mm.j),
|
||||
SP_K_STR, LINEAR_UNIT(backlash.distance_mm.k)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#endif // BACKLASH_GCODE
|
||||
|
190
Marlin/src/gcode/calibrate/M48.cpp
Executable file → Normal file
190
Marlin/src/gcode/calibrate/M48.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -27,13 +27,10 @@
|
||||
#include "../gcode.h"
|
||||
#include "../../module/motion.h"
|
||||
#include "../../module/probe.h"
|
||||
#include "../../lcd/marlinui.h"
|
||||
|
||||
#include "../../feature/bedlevel/bedlevel.h"
|
||||
|
||||
#if HAS_SPI_LCD
|
||||
#include "../../lcd/ultralcd.h"
|
||||
#endif
|
||||
|
||||
#if HAS_LEVELING
|
||||
#include "../../module/planner.h"
|
||||
#endif
|
||||
@@ -54,11 +51,9 @@
|
||||
* This function requires the machine to be homed before invocation.
|
||||
*/
|
||||
|
||||
extern const char SP_Y_STR[];
|
||||
|
||||
void GcodeSuite::M48() {
|
||||
|
||||
if (axis_unhomed_error()) return;
|
||||
if (homing_needed_error()) return;
|
||||
|
||||
const int8_t verbose_level = parser.byteval('V', 1);
|
||||
if (!WITHIN(verbose_level, 0, 4)) {
|
||||
@@ -77,61 +72,85 @@ void GcodeSuite::M48() {
|
||||
|
||||
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
|
||||
|
||||
xy_float_t next_pos = current_position;
|
||||
|
||||
const xy_pos_t probe_pos = {
|
||||
parser.linearval('X', next_pos.x + probe.offset_xy.x), // If no X use the probe's current X position
|
||||
parser.linearval('Y', next_pos.y + probe.offset_xy.y) // If no Y, ditto
|
||||
// Test at the current position by default, overridden by X and Y
|
||||
const xy_pos_t test_position = {
|
||||
parser.linearval('X', current_position.x + probe.offset_xy.x), // If no X use the probe's current X position
|
||||
parser.linearval('Y', current_position.y + probe.offset_xy.y) // If no Y, ditto
|
||||
};
|
||||
|
||||
if (!probe.can_reach(probe_pos)) {
|
||||
if (!probe.can_reach(test_position)) {
|
||||
ui.set_status_P(GET_TEXT(MSG_M48_OUT_OF_BOUNDS), 99);
|
||||
SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the number of leg moves per test-point
|
||||
bool seen_L = parser.seen('L');
|
||||
uint8_t n_legs = seen_L ? parser.value_byte() : 0;
|
||||
if (n_legs > 15) {
|
||||
SERIAL_ECHOLNPGM("?Number of legs in movement not plausible (0-15).");
|
||||
SERIAL_ECHOLNPGM("?Legs of movement implausible (0-15).");
|
||||
return;
|
||||
}
|
||||
if (n_legs == 1) n_legs = 2;
|
||||
|
||||
// Schizoid motion as an optional stress-test
|
||||
const bool schizoid_flag = parser.boolval('S');
|
||||
if (schizoid_flag && !seen_L) n_legs = 7;
|
||||
|
||||
/**
|
||||
* Now get everything to the specified probe point So we can safely do a
|
||||
* probe to get us close to the bed. If the Z-Axis is far from the bed,
|
||||
* we don't want to use that as a starting point for each probe.
|
||||
*/
|
||||
if (verbose_level > 2)
|
||||
SERIAL_ECHOLNPGM("Positioning the probe...");
|
||||
|
||||
// Disable bed level correction in M48 because we want the raw data when we probe
|
||||
// Always disable Bed Level correction before probing...
|
||||
|
||||
#if HAS_LEVELING
|
||||
const bool was_enabled = planner.leveling_active;
|
||||
set_bed_leveling_enabled(false);
|
||||
#endif
|
||||
|
||||
// Work with reasonable feedrates
|
||||
remember_feedrate_scaling_off();
|
||||
|
||||
float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples];
|
||||
// Working variables
|
||||
float mean = 0.0, // The average of all points so far, used to calculate deviation
|
||||
sigma = 0.0, // Standard deviation of all points so far
|
||||
min = 99999.9, // Smallest value sampled so far
|
||||
max = -99999.9, // Largest value sampled so far
|
||||
sample_set[n_samples]; // Storage for sampled values
|
||||
|
||||
auto dev_report = [](const bool verbose, const_float_t mean, const_float_t sigma, const_float_t min, const_float_t max, const bool final=false) {
|
||||
if (verbose) {
|
||||
SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
|
||||
if (!final) SERIAL_ECHOPAIR_F(" Sigma: ", sigma, 6);
|
||||
SERIAL_ECHOPAIR_F(" Min: ", min, 3);
|
||||
SERIAL_ECHOPAIR_F(" Max: ", max, 3);
|
||||
SERIAL_ECHOPAIR_F(" Range: ", max-min, 3);
|
||||
if (final) SERIAL_EOL();
|
||||
}
|
||||
if (final) {
|
||||
SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
};
|
||||
|
||||
// Move to the first point, deploy, and probe
|
||||
const float t = probe.probe_at_point(probe_pos, raise_after, verbose_level);
|
||||
const float t = probe.probe_at_point(test_position, raise_after, verbose_level);
|
||||
bool probing_good = !isnan(t);
|
||||
|
||||
if (probing_good) {
|
||||
randomSeed(millis());
|
||||
|
||||
float sample_sum = 0.0;
|
||||
|
||||
LOOP_L_N(n, n_samples) {
|
||||
#if HAS_SPI_LCD
|
||||
#if HAS_STATUS_MESSAGE
|
||||
// Display M48 progress in the status bar
|
||||
ui.status_printf_P(0, PSTR(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples));
|
||||
#endif
|
||||
|
||||
// When there are "legs" of movement move around the point before probing
|
||||
if (n_legs) {
|
||||
|
||||
// Pick a random direction, starting angle, and radius
|
||||
const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise
|
||||
float angle = random(0, 360);
|
||||
const float radius = random(
|
||||
@@ -142,97 +161,91 @@ void GcodeSuite::M48() {
|
||||
int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE))
|
||||
#endif
|
||||
);
|
||||
|
||||
if (verbose_level > 3) {
|
||||
SERIAL_ECHOPAIR("Start radius:", radius, " angle:", angle, " dir:");
|
||||
SERIAL_ECHOPGM("Start radius:", radius, " angle:", angle, " dir:");
|
||||
if (dir > 0) SERIAL_CHAR('C');
|
||||
SERIAL_ECHOLNPGM("CW");
|
||||
}
|
||||
|
||||
// Move from leg to leg in rapid succession
|
||||
LOOP_L_N(l, n_legs - 1) {
|
||||
float delta_angle;
|
||||
|
||||
// Move some distance around the perimeter
|
||||
float delta_angle;
|
||||
if (schizoid_flag) {
|
||||
// The points of a 5 point star are 72 degrees apart. We need to
|
||||
// skip a point and go to the next one on the star.
|
||||
// The points of a 5 point star are 72 degrees apart.
|
||||
// Skip a point and go to the next one on the star.
|
||||
delta_angle = dir * 2.0 * 72.0;
|
||||
}
|
||||
else {
|
||||
// If we do this line, we are just trying to move further
|
||||
// around the circle.
|
||||
delta_angle = dir * (float) random(25, 45);
|
||||
// Just move further along the perimeter.
|
||||
delta_angle = dir * (float)random(25, 45);
|
||||
}
|
||||
|
||||
angle += delta_angle;
|
||||
while (angle > 360.0) angle -= 360.0; // We probably do not need to keep the angle between 0 and 2*PI, but the
|
||||
// Arduino documentation says the trig functions should not be given values
|
||||
while (angle < 0.0) angle += 360.0; // outside of this range. It looks like they behave correctly with
|
||||
// numbers outside of the range, but just to be safe we clamp them.
|
||||
|
||||
const xy_pos_t noz_pos = probe_pos - probe.offset_xy;
|
||||
next_pos.set(noz_pos.x + cos(RADIANS(angle)) * radius,
|
||||
noz_pos.y + sin(RADIANS(angle)) * radius);
|
||||
// Trig functions work without clamping, but just to be safe...
|
||||
while (angle > 360.0) angle -= 360.0;
|
||||
while (angle < 0.0) angle += 360.0;
|
||||
|
||||
#if DISABLED(DELTA)
|
||||
LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
|
||||
LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
|
||||
#else
|
||||
// If we have gone out too far, we can do a simple fix and scale the numbers
|
||||
// back in closer to the origin.
|
||||
// Choose the next position as an offset to chosen test position
|
||||
const xy_pos_t noz_pos = test_position - probe.offset_xy;
|
||||
xy_pos_t next_pos = {
|
||||
noz_pos.x + float(cos(RADIANS(angle))) * radius,
|
||||
noz_pos.y + float(sin(RADIANS(angle))) * radius
|
||||
};
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
// If the probe can't reach the point on a round bed...
|
||||
// Simply scale the numbers to bring them closer to origin.
|
||||
while (!probe.can_reach(next_pos)) {
|
||||
next_pos *= 0.8f;
|
||||
if (verbose_level > 3)
|
||||
SERIAL_ECHOLNPAIR_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
|
||||
SERIAL_ECHOLNPGM_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
|
||||
}
|
||||
#elif HAS_ENDSTOPS
|
||||
// For a rectangular bed just keep the probe in bounds
|
||||
LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
|
||||
LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
|
||||
#endif
|
||||
|
||||
if (verbose_level > 3)
|
||||
SERIAL_ECHOLNPAIR_P(PSTR("Going to: X"), next_pos.x, SP_Y_STR, next_pos.y);
|
||||
SERIAL_ECHOLNPGM_P(PSTR("Going to: X"), next_pos.x, SP_Y_STR, next_pos.y);
|
||||
|
||||
do_blocking_move_to_xy(next_pos);
|
||||
} // n_legs loop
|
||||
} // n_legs
|
||||
|
||||
// Probe a single point
|
||||
sample_set[n] = probe.probe_at_point(probe_pos, raise_after, 0);
|
||||
const float pz = probe.probe_at_point(test_position, raise_after, 0);
|
||||
|
||||
// Break the loop if the probe fails
|
||||
probing_good = !isnan(sample_set[n]);
|
||||
probing_good = !isnan(pz);
|
||||
if (!probing_good) break;
|
||||
|
||||
/**
|
||||
* Get the current mean for the data points we have so far
|
||||
*/
|
||||
float sum = 0.0;
|
||||
LOOP_LE_N(j, n) sum += sample_set[j];
|
||||
mean = sum / (n + 1);
|
||||
// Store the new sample
|
||||
sample_set[n] = pz;
|
||||
|
||||
NOMORE(min, sample_set[n]);
|
||||
NOLESS(max, sample_set[n]);
|
||||
// Keep track of the largest and smallest samples
|
||||
NOMORE(min, pz);
|
||||
NOLESS(max, pz);
|
||||
|
||||
/**
|
||||
* Now, use that mean to calculate the standard deviation for the
|
||||
* data points we have so far
|
||||
*/
|
||||
sum = 0.0;
|
||||
LOOP_LE_N(j, n)
|
||||
sum += sq(sample_set[j] - mean);
|
||||
// Get the mean value of all samples thus far
|
||||
sample_sum += pz;
|
||||
mean = sample_sum / (n + 1);
|
||||
|
||||
sigma = SQRT(sum / (n + 1));
|
||||
if (verbose_level > 0) {
|
||||
if (verbose_level > 1) {
|
||||
SERIAL_ECHO(n + 1);
|
||||
SERIAL_ECHOPAIR(" of ", int(n_samples));
|
||||
SERIAL_ECHOPAIR_F(": z: ", sample_set[n], 3);
|
||||
if (verbose_level > 2) {
|
||||
SERIAL_ECHOPAIR_F(" mean: ", mean, 4);
|
||||
SERIAL_ECHOPAIR_F(" sigma: ", sigma, 6);
|
||||
SERIAL_ECHOPAIR_F(" min: ", min, 3);
|
||||
SERIAL_ECHOPAIR_F(" max: ", max, 3);
|
||||
SERIAL_ECHOPAIR_F(" range: ", max-min, 3);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
}
|
||||
// Calculate the standard deviation so far.
|
||||
// The value after the last sample will be the final output.
|
||||
float dev_sum = 0.0;
|
||||
LOOP_LE_N(j, n) dev_sum += sq(sample_set[j] - mean);
|
||||
sigma = SQRT(dev_sum / (n + 1));
|
||||
|
||||
if (verbose_level > 1) {
|
||||
SERIAL_ECHO(n + 1);
|
||||
SERIAL_ECHOPGM(" of ", n_samples);
|
||||
SERIAL_ECHOPAIR_F(": z: ", pz, 3);
|
||||
SERIAL_CHAR(' ');
|
||||
dev_report(verbose_level > 2, mean, sigma, min, max);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
} // n_samples loop
|
||||
@@ -242,18 +255,9 @@ void GcodeSuite::M48() {
|
||||
|
||||
if (probing_good) {
|
||||
SERIAL_ECHOLNPGM("Finished!");
|
||||
dev_report(verbose_level > 0, mean, sigma, min, max, true);
|
||||
|
||||
if (verbose_level > 0) {
|
||||
SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
|
||||
SERIAL_ECHOPAIR_F(" Min: ", min, 3);
|
||||
SERIAL_ECHOPAIR_F(" Max: ", max, 3);
|
||||
SERIAL_ECHOLNPAIR_F(" Range: ", max-min, 3);
|
||||
}
|
||||
|
||||
SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
|
||||
SERIAL_EOL();
|
||||
|
||||
#if HAS_SPI_LCD
|
||||
#if HAS_STATUS_MESSAGE
|
||||
// Display M48 results in the status bar
|
||||
char sigma_str[8];
|
||||
ui.status_printf_P(0, PSTR(S_FMT ": %s"), GET_TEXT(MSG_M48_DEVIATION), dtostrf(sigma, 2, 6, sigma_str));
|
||||
@@ -263,9 +267,7 @@ void GcodeSuite::M48() {
|
||||
restore_feedrate_and_scaling();
|
||||
|
||||
// Re-enable bed level correction if it had been on
|
||||
#if HAS_LEVELING
|
||||
set_bed_leveling_enabled(was_enabled);
|
||||
#endif
|
||||
TERN_(HAS_LEVELING, set_bed_leveling_enabled(was_enabled));
|
||||
|
||||
report_current_position();
|
||||
}
|
||||
|
85
Marlin/src/gcode/calibrate/M665.cpp
Executable file → Normal file
85
Marlin/src/gcode/calibrate/M665.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#if ENABLED(DELTA)
|
||||
|
||||
#include "../../module/delta.h"
|
||||
|
||||
/**
|
||||
* M665: Set delta configurations
|
||||
*
|
||||
@@ -38,20 +39,44 @@
|
||||
* R = delta radius
|
||||
* S = segments per second
|
||||
* X = Alpha (Tower 1) angle trim
|
||||
* Y = Beta (Tower 2) angle trim
|
||||
* Y = Beta (Tower 2) angle trim
|
||||
* Z = Gamma (Tower 3) angle trim
|
||||
* A = Alpha (Tower 1) diagonal rod trim
|
||||
* B = Beta (Tower 2) diagonal rod trim
|
||||
* C = Gamma (Tower 3) diagonal rod trim
|
||||
*/
|
||||
void GcodeSuite::M665() {
|
||||
if (parser.seen('H')) delta_height = parser.value_linear_units();
|
||||
if (parser.seen('L')) delta_diagonal_rod = parser.value_linear_units();
|
||||
if (parser.seen('R')) delta_radius = parser.value_linear_units();
|
||||
if (parser.seen('S')) delta_segments_per_second = parser.value_float();
|
||||
if (parser.seen('X')) delta_tower_angle_trim.a = parser.value_float();
|
||||
if (parser.seen('Y')) delta_tower_angle_trim.b = parser.value_float();
|
||||
if (parser.seen('Z')) delta_tower_angle_trim.c = parser.value_float();
|
||||
if (!parser.seen_any()) return M665_report();
|
||||
|
||||
if (parser.seenval('H')) delta_height = parser.value_linear_units();
|
||||
if (parser.seenval('L')) delta_diagonal_rod = parser.value_linear_units();
|
||||
if (parser.seenval('R')) delta_radius = parser.value_linear_units();
|
||||
if (parser.seenval('S')) segments_per_second = parser.value_float();
|
||||
if (parser.seenval('X')) delta_tower_angle_trim.a = parser.value_float();
|
||||
if (parser.seenval('Y')) delta_tower_angle_trim.b = parser.value_float();
|
||||
if (parser.seenval('Z')) delta_tower_angle_trim.c = parser.value_float();
|
||||
if (parser.seenval('A')) delta_diagonal_rod_trim.a = parser.value_float();
|
||||
if (parser.seenval('B')) delta_diagonal_rod_trim.b = parser.value_float();
|
||||
if (parser.seenval('C')) delta_diagonal_rod_trim.c = parser.value_float();
|
||||
recalc_delta_settings();
|
||||
}
|
||||
|
||||
void GcodeSuite::M665_report(const bool forReplay/*=true*/) {
|
||||
report_heading_etc(forReplay, PSTR(STR_DELTA_SETTINGS));
|
||||
SERIAL_ECHOLNPGM_P(
|
||||
PSTR(" M665 L"), LINEAR_UNIT(delta_diagonal_rod)
|
||||
, PSTR(" R"), LINEAR_UNIT(delta_radius)
|
||||
, PSTR(" H"), LINEAR_UNIT(delta_height)
|
||||
, PSTR(" S"), segments_per_second
|
||||
, SP_X_STR, LINEAR_UNIT(delta_tower_angle_trim.a)
|
||||
, SP_Y_STR, LINEAR_UNIT(delta_tower_angle_trim.b)
|
||||
, SP_Z_STR, LINEAR_UNIT(delta_tower_angle_trim.c)
|
||||
, PSTR(" A"), LINEAR_UNIT(delta_diagonal_rod_trim.a)
|
||||
, PSTR(" B"), LINEAR_UNIT(delta_diagonal_rod_trim.b)
|
||||
, PSTR(" C"), LINEAR_UNIT(delta_diagonal_rod_trim.c)
|
||||
);
|
||||
}
|
||||
|
||||
#elif IS_SCARA
|
||||
|
||||
#include "../../module/scara.h"
|
||||
@@ -62,6 +87,9 @@
|
||||
* Parameters:
|
||||
*
|
||||
* S[segments-per-second] - Segments-per-second
|
||||
*
|
||||
* Without NO_WORKSPACE_OFFSETS:
|
||||
*
|
||||
* P[theta-psi-offset] - Theta-Psi offset, added to the shoulder (A/X) angle
|
||||
* T[theta-offset] - Theta offset, added to the elbow (B/Y) angle
|
||||
* Z[z-offset] - Z offset, added to Z
|
||||
@@ -70,7 +98,9 @@
|
||||
* B, T, and Y are all aliases for the elbow angle
|
||||
*/
|
||||
void GcodeSuite::M665() {
|
||||
if (parser.seenval('S')) delta_segments_per_second = parser.value_float();
|
||||
if (!parser.seen_any()) return M665_report();
|
||||
|
||||
if (parser.seenval('S')) segments_per_second = parser.value_float();
|
||||
|
||||
#if HAS_SCARA_OFFSET
|
||||
|
||||
@@ -101,6 +131,41 @@
|
||||
#endif // HAS_SCARA_OFFSET
|
||||
}
|
||||
|
||||
void GcodeSuite::M665_report(const bool forReplay/*=true*/) {
|
||||
report_heading_etc(forReplay, PSTR(STR_SCARA_SETTINGS " (" STR_S_SEG_PER_SEC TERN_(HAS_SCARA_OFFSET, " " STR_SCARA_P_T_Z) ")"));
|
||||
SERIAL_ECHOLNPGM_P(
|
||||
PSTR(" M665 S"), segments_per_second
|
||||
#if HAS_SCARA_OFFSET
|
||||
, SP_P_STR, scara_home_offset.a
|
||||
, SP_T_STR, scara_home_offset.b
|
||||
, SP_Z_STR, LINEAR_UNIT(scara_home_offset.z)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
#elif ENABLED(POLARGRAPH)
|
||||
|
||||
#include "../../module/polargraph.h"
|
||||
|
||||
/**
|
||||
* M665: Set POLARGRAPH settings
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* S[segments-per-second] - Segments-per-second
|
||||
*/
|
||||
void GcodeSuite::M665() {
|
||||
if (parser.seenval('S'))
|
||||
segments_per_second = parser.value_float();
|
||||
else
|
||||
M665_report();
|
||||
}
|
||||
|
||||
void GcodeSuite::M665_report(const bool forReplay/*=true*/) {
|
||||
report_heading_etc(forReplay, PSTR(STR_POLARGRAPH_SETTINGS " (" STR_S_SEG_PER_SEC ")"));
|
||||
SERIAL_ECHOLNPGM(" M665 S", segments_per_second);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // IS_KINEMATIC
|
||||
|
95
Marlin/src/gcode/calibrate/M666.cpp
Executable file → Normal file
95
Marlin/src/gcode/calibrate/M666.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -27,31 +27,49 @@
|
||||
#include "../gcode.h"
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
|
||||
#include "../../module/delta.h"
|
||||
#include "../../module/motion.h"
|
||||
#else
|
||||
#include "../../module/endstops.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../core/debug_out.h"
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../core/debug_out.h"
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
|
||||
/**
|
||||
* M666: Set delta endstop adjustment
|
||||
*/
|
||||
void GcodeSuite::M666() {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM(">>> M666");
|
||||
LOOP_XYZ(i) {
|
||||
if (parser.seen(XYZ_CHAR(i))) {
|
||||
DEBUG_SECTION(log_M666, "M666", DEBUGGING(LEVELING));
|
||||
bool is_err = false, is_set = false;
|
||||
LOOP_LINEAR_AXES(i) {
|
||||
if (parser.seen(AXIS_CHAR(i))) {
|
||||
is_set = true;
|
||||
const float v = parser.value_linear_units();
|
||||
if (v * Z_HOME_DIR <= 0) delta_endstop_adj[i] = v;
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("delta_endstop_adj[", XYZ_CHAR(i), "] = ", delta_endstop_adj[i]);
|
||||
if (v > 0)
|
||||
is_err = true;
|
||||
else {
|
||||
delta_endstop_adj[i] = v;
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("delta_endstop_adj[", AS_CHAR(AXIS_CHAR(i)), "] = ", v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< M666");
|
||||
if (is_err) SERIAL_ECHOLNPGM("?M666 offsets must be <= 0");
|
||||
if (!is_set) M666_report();
|
||||
}
|
||||
|
||||
#elif HAS_EXTRA_ENDSTOPS
|
||||
void GcodeSuite::M666_report(const bool forReplay/*=true*/) {
|
||||
report_heading_etc(forReplay, PSTR(STR_ENDSTOP_ADJUSTMENT));
|
||||
SERIAL_ECHOLNPGM_P(
|
||||
PSTR(" M666 X"), LINEAR_UNIT(delta_endstop_adj.a)
|
||||
, SP_Y_STR, LINEAR_UNIT(delta_endstop_adj.b)
|
||||
, SP_Z_STR, LINEAR_UNIT(delta_endstop_adj.c)
|
||||
);
|
||||
}
|
||||
|
||||
#include "../../module/endstops.h"
|
||||
#else
|
||||
|
||||
/**
|
||||
* M666: Set Dual Endstops offsets for X, Y, and/or Z.
|
||||
@@ -64,6 +82,8 @@
|
||||
* Set All: M666 Z<offset>
|
||||
*/
|
||||
void GcodeSuite::M666() {
|
||||
if (!parser.seen_any()) return M666_report();
|
||||
|
||||
#if ENABLED(X_DUAL_ENDSTOPS)
|
||||
if (parser.seenval('X')) endstops.x2_endstop_adj = parser.value_linear_units();
|
||||
#endif
|
||||
@@ -72,33 +92,40 @@
|
||||
#endif
|
||||
#if ENABLED(Z_MULTI_ENDSTOPS)
|
||||
if (parser.seenval('Z')) {
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 3
|
||||
const float z_adj = parser.value_linear_units();
|
||||
const int ind = parser.intval('S');
|
||||
if (!ind || ind == 2) endstops.z2_endstop_adj = z_adj;
|
||||
if (!ind || ind == 3) endstops.z3_endstop_adj = z_adj;
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 4
|
||||
if (!ind || ind == 4) endstops.z4_endstop_adj = z_adj;
|
||||
#endif
|
||||
const float z_adj = parser.value_linear_units();
|
||||
#if NUM_Z_STEPPER_DRIVERS == 2
|
||||
endstops.z2_endstop_adj = z_adj;
|
||||
#else
|
||||
endstops.z2_endstop_adj = parser.value_linear_units();
|
||||
const int ind = parser.intval('S');
|
||||
#define _SET_ZADJ(N) if (!ind || ind == N) endstops.z##N##_endstop_adj = z_adj;
|
||||
REPEAT_S(2, INCREMENT(NUM_Z_STEPPER_DRIVERS), _SET_ZADJ)
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (!parser.seen("XYZ")) {
|
||||
SERIAL_ECHOPGM("Dual Endstop Adjustment (mm): ");
|
||||
#if ENABLED(X_DUAL_ENDSTOPS)
|
||||
SERIAL_ECHOPAIR(" X2:", endstops.x2_endstop_adj);
|
||||
}
|
||||
|
||||
void GcodeSuite::M666_report(const bool forReplay/*=true*/) {
|
||||
report_heading_etc(forReplay, PSTR(STR_ENDSTOP_ADJUSTMENT));
|
||||
SERIAL_ECHOPGM(" M666");
|
||||
#if ENABLED(X_DUAL_ENDSTOPS)
|
||||
SERIAL_ECHOLNPGM_P(SP_X_STR, LINEAR_UNIT(endstops.x2_endstop_adj));
|
||||
#endif
|
||||
#if ENABLED(Y_DUAL_ENDSTOPS)
|
||||
SERIAL_ECHOLNPGM_P(SP_Y_STR, LINEAR_UNIT(endstops.y2_endstop_adj));
|
||||
#endif
|
||||
#if ENABLED(Z_MULTI_ENDSTOPS)
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 3
|
||||
SERIAL_ECHOPGM(" S2 Z", LINEAR_UNIT(endstops.z3_endstop_adj));
|
||||
report_echo_start(forReplay);
|
||||
SERIAL_ECHOPGM(" M666 S3 Z", LINEAR_UNIT(endstops.z3_endstop_adj));
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 4
|
||||
report_echo_start(forReplay);
|
||||
SERIAL_ECHOPGM(" M666 S4 Z", LINEAR_UNIT(endstops.z4_endstop_adj));
|
||||
#endif
|
||||
#else
|
||||
SERIAL_ECHOLNPGM_P(SP_Z_STR, LINEAR_UNIT(endstops.z2_endstop_adj));
|
||||
#endif
|
||||
#if ENABLED(Y_DUAL_ENDSTOPS)
|
||||
SERIAL_ECHOPAIR(" Y2:", endstops.y2_endstop_adj);
|
||||
#endif
|
||||
#if ENABLED(Z_MULTI_ENDSTOPS)
|
||||
#define _ECHO_ZADJ(N) SERIAL_ECHOPAIR(" Z" STRINGIFY(N) ":", endstops.z##N##_endstop_adj);
|
||||
REPEAT_S(2, INCREMENT(NUM_Z_STEPPER_DRIVERS), _ECHO_ZADJ)
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // HAS_EXTRA_ENDSTOPS
|
||||
|
38
Marlin/src/gcode/calibrate/M852.cpp
Executable file → Normal file
38
Marlin/src/gcode/calibrate/M852.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -36,10 +36,11 @@
|
||||
* K[yz_factor] - New YZ skew factor
|
||||
*/
|
||||
void GcodeSuite::M852() {
|
||||
uint8_t ijk = 0, badval = 0, setval = 0;
|
||||
if (!parser.seen("SIJK")) return M852_report();
|
||||
|
||||
if (parser.seen('I') || parser.seen('S')) {
|
||||
++ijk;
|
||||
uint8_t badval = 0, setval = 0;
|
||||
|
||||
if (parser.seenval('I') || parser.seenval('S')) {
|
||||
const float value = parser.value_linear_units();
|
||||
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
|
||||
if (planner.skew_factor.xy != value) {
|
||||
@@ -53,8 +54,7 @@ void GcodeSuite::M852() {
|
||||
|
||||
#if ENABLED(SKEW_CORRECTION_FOR_Z)
|
||||
|
||||
if (parser.seen('J')) {
|
||||
++ijk;
|
||||
if (parser.seenval('J')) {
|
||||
const float value = parser.value_linear_units();
|
||||
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
|
||||
if (planner.skew_factor.xz != value) {
|
||||
@@ -66,8 +66,7 @@ void GcodeSuite::M852() {
|
||||
++badval;
|
||||
}
|
||||
|
||||
if (parser.seen('K')) {
|
||||
++ijk;
|
||||
if (parser.seenval('K')) {
|
||||
const float value = parser.value_linear_units();
|
||||
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
|
||||
if (planner.skew_factor.yz != value) {
|
||||
@@ -86,21 +85,22 @@ void GcodeSuite::M852() {
|
||||
|
||||
// When skew is changed the current position changes
|
||||
if (setval) {
|
||||
set_current_from_steppers_for_axis(ALL_AXES);
|
||||
set_current_from_steppers_for_axis(ALL_AXES_ENUM);
|
||||
sync_plan_position();
|
||||
report_current_position();
|
||||
}
|
||||
}
|
||||
|
||||
if (!ijk) {
|
||||
SERIAL_ECHO_START();
|
||||
serialprintPGM(GET_TEXT(MSG_SKEW_FACTOR));
|
||||
SERIAL_ECHOPAIR_F(" XY: ", planner.skew_factor.xy, 6);
|
||||
#if ENABLED(SKEW_CORRECTION_FOR_Z)
|
||||
SERIAL_ECHOPAIR_F(" XZ: ", planner.skew_factor.xz, 6);
|
||||
SERIAL_ECHOPAIR_F(" YZ: ", planner.skew_factor.yz, 6);
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
}
|
||||
void GcodeSuite::M852_report(const bool forReplay/*=true*/) {
|
||||
report_heading_etc(forReplay, PSTR(STR_SKEW_FACTOR));
|
||||
SERIAL_ECHOPAIR_F(" M851 I", planner.skew_factor.xy, 6);
|
||||
#if ENABLED(SKEW_CORRECTION_FOR_Z)
|
||||
SERIAL_ECHOPAIR_F(" J", planner.skew_factor.xz, 6);
|
||||
SERIAL_ECHOPAIR_F(" K", planner.skew_factor.yz, 6);
|
||||
SERIAL_ECHOLNPGM(" ; XY, XZ, YZ");
|
||||
#else
|
||||
SERIAL_ECHOLNPGM(" ; XY");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // SKEW_CORRECTION_GCODE
|
||||
|
Reference in New Issue
Block a user