Merge branch 'bugfix-2.1.x' into bugfix-2.1.x-February2

This commit is contained in:
Andrew 2026-02-19 09:00:14 -05:00 committed by GitHub
commit e1e8ef8f52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 467 additions and 447 deletions

View file

@ -41,7 +41,7 @@
* here we define this default string as the date where the latest release
* version was tagged.
*/
//#define STRING_DISTRIBUTION_DATE "2026-02-14"
//#define STRING_DISTRIBUTION_DATE "2026-02-19"
/**
* The protocol for communication to the host. Protocol indicates communication

View file

@ -109,7 +109,7 @@ bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) {
WITH_RETRY(SDIO_READ_RETRIES, {
en_result_t rc = SDCARD_ReadBlocks(handle, block, 1, dst, SDIO_READ_TIMEOUT);
if (rc == Ok) return true;
printf("SDIO_ReadBlock error (rc=%u; ErrorCode=%lu)\n", rc, handle->u32ErrorCode);
printf("SDIO_ReadBlock error (rc=%u; ErrorCode=%" PRIu32 ")\n", rc, handle->u32ErrorCode);
})
return false;
@ -122,7 +122,7 @@ bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) {
WITH_RETRY(SDIO_WRITE_RETRIES, {
en_result_t rc = SDCARD_WriteBlocks(handle, block, 1, (uint8_t *)src, SDIO_WRITE_TIMEOUT);
if (rc == Ok) return true;
printf("SDIO_WriteBlock error (rc=%u; ErrorCode=%lu)\n", rc, handle->u32ErrorCode);
printf("SDIO_WriteBlock error (rc=%u; ErrorCode=%" PRIu32 ")\n", rc, handle->u32ErrorCode);
})
return false;

View file

@ -88,9 +88,9 @@ void USBHost::setUsbTaskState(uint8_t state) {
capacity = info.capacity.block_nbr / 2000;
block_size = info.capacity.block_size;
block_count = info.capacity.block_nbr;
//SERIAL_ECHOLNPGM("info.capacity.block_nbr : %ld\n", info.capacity.block_nbr);
//SERIAL_ECHOLNPGM("info.capacity.block_size: %d\n", info.capacity.block_size);
//SERIAL_ECHOLNPGM("capacity : %d MB\n", capacity);
//SERIAL_ECHOLNPGM("info.capacity.block_nbr : ", info.capacity.block_nbr);
//SERIAL_ECHOLNPGM("info.capacity.block_size: ", info.capacity.block_size);
//SERIAL_ECHOLNPGM("capacity : ", capacity, "MB");
}
};

View file

@ -732,9 +732,9 @@ void Marlin::manage_inactivity(const bool no_stepper_sleep/*=false*/) {
#if ENABLED(DUAL_X_CARRIAGE)
// handle delayed move timeout
if (delayed_move_time && ELAPSED(ms, delayed_move_time) && isRunning()) {
if (motion.delayed_move_time && ELAPSED(ms, motion.delayed_move_time) && isRunning()) {
// travel moves have been received so enact them
delayed_move_time = UINT32_MAX; // force moves to be done
motion.delayed_move_time = UINT32_MAX; // force moves to be done
motion.destination = motion.position;
motion.prepare_line_to_destination();
planner.synchronize();

View file

@ -235,8 +235,8 @@ bool load_filament(const float slow_load_length/*=0*/, const float fast_load_len
#if ENABLED(DUAL_X_CARRIAGE)
const int8_t saved_ext = motion.extruder;
const bool saved_ext_dup_mode = extruder_duplication_enabled;
set_duplication_enabled(false, DXC_ext);
const bool saved_ext_dup_mode = motion.extruder_duplication;
motion.set_extruder_duplication(false, DXC_ext);
#endif
TERN_(BELTPRINTER, motion.blocking_move_xy(0.00, 50.00));
@ -261,7 +261,7 @@ bool load_filament(const float slow_load_length/*=0*/, const float fast_load_len
}
#if ENABLED(DUAL_X_CARRIAGE) // Tie the two extruders movement back together.
set_duplication_enabled(saved_ext_dup_mode, saved_ext);
motion.set_extruder_duplication(saved_ext_dup_mode, saved_ext);
#endif
#if ENABLED(ADVANCED_PAUSE_CONTINUOUS_PURGE)
@ -485,15 +485,15 @@ bool pause_print(const float retract, const xyz_pos_t &park_point, const bool sh
#if ENABLED(DUAL_X_CARRIAGE)
const int8_t saved_ext = motion.extruder;
const bool saved_ext_dup_mode = extruder_duplication_enabled;
set_duplication_enabled(false, DXC_ext);
const bool saved_ext_dup_mode = motion.extruder_duplication;
motion.set_extruder_duplication(false, DXC_ext);
#endif
// Unload the filament, if specified
if (unload_length)
unload_filament(unload_length, show_lcd, PAUSE_MODE_CHANGE_FILAMENT);
TERN_(DUAL_X_CARRIAGE, set_duplication_enabled(saved_ext_dup_mode, saved_ext));
TERN_(DUAL_X_CARRIAGE, motion.set_extruder_duplication(saved_ext_dup_mode, saved_ext));
// Disable the Extruder for manual change
disable_active_extruder();
@ -545,8 +545,8 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
#if ENABLED(DUAL_X_CARRIAGE)
const int8_t saved_ext = motion.extruder;
const bool saved_ext_dup_mode = extruder_duplication_enabled;
set_duplication_enabled(false, DXC_ext);
const bool saved_ext_dup_mode = motion.extruder_duplication;
motion.set_extruder_duplication(false, DXC_ext);
#endif
// Wait for filament insert by user and press button
@ -613,7 +613,7 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
}
marlin.idle_no_sleep();
}
TERN_(DUAL_X_CARRIAGE, set_duplication_enabled(saved_ext_dup_mode, saved_ext));
TERN_(DUAL_X_CARRIAGE, motion.set_extruder_duplication(saved_ext_dup_mode, saved_ext));
}
/**
@ -660,9 +660,9 @@ void resume_print(
/*
SERIAL_ECHOLNPGM(
"start of resume_print()\ndual_x_carriage_mode:", dual_x_carriage_mode,
"\nextruder_duplication_enabled:", extruder_duplication_enabled,
"\nactive_extruder:", motion.extruder,
"start of resume_print()\ndual_x_carriage_mode:", motion.idex_mode,
"\nmotion.extruder_duplication:", motion.extruder_duplication,
"\nmotion.extruder:", motion.extruder,
"\n"
);
//*/

View file

@ -303,8 +303,8 @@ class FilamentSensorBase {
static bool poll_runout_state(const uint8_t extruder) {
const uint8_t runout_states = poll_runout_states();
#if MULTI_FILAMENT_SENSOR
if ( !TERN0(DUAL_X_CARRIAGE, idex_is_duplicating())
&& !TERN0(MULTI_NOZZLE_DUPLICATION, extruder_duplication_enabled)
if ( !TERN0(DUAL_X_CARRIAGE, motion.idex_is_duplicating())
&& !TERN0(MULTI_NOZZLE_DUPLICATION, motion.extruder_duplication)
) return TEST(runout_states, extruder); // A specific extruder ran out
#else
UNUSED(extruder);

View file

@ -84,7 +84,7 @@ void GcodeSuite::G35() {
probe.use_probing_tool();
// Disable duplication mode on homing
TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false));
TERN_(HAS_DUPLICATION_MODE, motion.set_extruder_duplication(false));
// Home only Z axis when X and Y is trusted, otherwise all axes, if needed before this procedure
if (!motion.all_axes_trusted()) process_subcommands_now(F("G28Z"));

View file

@ -42,7 +42,7 @@
* P : Flag to put the probe at the given point
*/
void GcodeSuite::G42() {
if (!MOTION_CONDITIONS) return;
if (motion.gcode_motion_ignored()) return;
const bool hasI = parser.seenval('I');
const int8_t ix = hasI ? parser.value_int() : 0;

View file

@ -395,14 +395,16 @@ G29_TYPE GcodeSuite::G29() {
#endif
#if ABL_USES_GRID
#if HAS_VARIABLE_XY_PROBE_FEEDRATE
constexpr feedRate_t min_probe_feedrate_mm_s = XY_PROBE_FEEDRATE_MIN;
xy_probe_feedrate_mm_s = MMM_TO_MMS(parser.linearval('S', XY_PROBE_FEEDRATE));
if (xy_probe_feedrate_mm_s < min_probe_feedrate_mm_s) {
xy_probe_feedrate_mm_s = min_probe_feedrate_mm_s;
motion.xy_probe_feedrate_mm_s = MMM_TO_MMS(parser.linearval('S', XY_PROBE_FEEDRATE));
if (motion.xy_probe_feedrate_mm_s < min_probe_feedrate_mm_s) {
motion.xy_probe_feedrate_mm_s = min_probe_feedrate_mm_s;
SERIAL_ECHOLNPGM(GCODE_ERR_MSG("Feedrate (S) too low. (Using ", min_probe_feedrate_mm_s, ")"));
}
#endif
#if ABL_USES_GRID
const float x_min = probe.min_x(), x_max = probe.max_x(),
y_min = probe.min_y(), y_max = probe.max_y();

View file

@ -81,16 +81,16 @@
motion.position.set(0.0, 0.0);
motion.sync_plan_position();
const int x_axis_home_dir = TOOL_X_HOME_DIR(motion.extruder);
const int x_axis_home_dir = motion.tool_x_home_dir();
// Use a higher diagonal feedrate so axes move at homing speed
const float minfr = _MIN(motion.homing_feedrate(X_AXIS), motion.homing_feedrate(Y_AXIS)),
fr_mm_s = HYPOT(minfr, minfr);
// Set homing current to X and Y axis if defined
TERN_(X_HAS_HOME_CURRENT, set_homing_current(X_AXIS));
TERN_(X_HAS_HOME_CURRENT, motion.set_homing_current(X_AXIS));
#if Y_HAS_HOME_CURRENT && NONE(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
set_homing_current(Y_AXIS);
motion.set_homing_current(Y_AXIS);
#endif
#if ENABLED(SENSORLESS_HOMING)
@ -105,15 +105,15 @@
};
#endif
motion.blocking_move_xy(1.5 * max_length(X_AXIS) * x_axis_home_dir, 1.5 * max_length(Y_AXIS) * Y_HOME_DIR, fr_mm_s);
motion.blocking_move_xy(1.5 * motion.max_axis_length(X_AXIS) * x_axis_home_dir, 1.5 * motion.max_axis_length(Y_AXIS) * Y_HOME_DIR, fr_mm_s);
endstops.validate_homing_move();
motion.position.set(0.0, 0.0);
TERN_(X_HAS_HOME_CURRENT, restore_homing_current(X_AXIS));
TERN_(X_HAS_HOME_CURRENT, motion.restore_homing_current(X_AXIS));
#if Y_HAS_HOME_CURRENT && NONE(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
restore_homing_current(Y_AXIS);
motion.restore_homing_current(Y_AXIS);
#endif
#if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)
@ -153,7 +153,7 @@
if (DEBUGGING(LEVELING)) DEBUG_POS("home_z_safely", motion.destination);
// Free the active extruder for movement
TERN_(DUAL_X_CARRIAGE, idex_set_parked(false));
TERN_(DUAL_X_CARRIAGE, motion.idex_set_parked(false));
TERN_(SENSORLESS_HOMING, safe_delay(500)); // Short delay needed to settle
@ -264,8 +264,8 @@ void GcodeSuite::G28() {
#if NUM_AXES
#if ENABLED(DUAL_X_CARRIAGE)
bool IDEX_saved_duplication_state = extruder_duplication_enabled;
DualXMode IDEX_saved_mode = dual_x_carriage_mode;
bool IDEX_saved_duplication_state = motion.extruder_duplication;
DualXMode IDEX_saved_mode = motion.idex_mode;
#endif
motion.set_soft_endstop_loose(false); // Reset a leftover 'loose' motion state
@ -304,7 +304,7 @@ void GcodeSuite::G28() {
tool_change(0, true);
#endif
TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false));
TERN_(HAS_DUPLICATION_MODE, motion.set_extruder_duplication(false));
motion.remember_feedrate_scaling_off();
@ -415,27 +415,10 @@ void GcodeSuite::G28() {
// Home X
#if HAS_X_AXIS
if (doX || (doY && ENABLED(CODEPENDENT_XY_HOMING) && DISABLED(HOME_Y_BEFORE_X))) {
#if ENABLED(DUAL_X_CARRIAGE)
// Always home the 2nd (right) extruder first
motion.extruder = 1;
motion.homeaxis(X_AXIS);
// Remember this extruder's position for later tool change
inactive_extruder_x = motion.position.x;
// Home the 1st (left) extruder
motion.extruder = 0;
motion.homeaxis(X_AXIS);
// Consider the active extruder to be in its "parked" position
idex_set_parked();
motion.idex_home_x();
#else
motion.homeaxis(X_AXIS);
#endif
}
#endif // HAS_X_AXIS
@ -502,7 +485,7 @@ void GcodeSuite::G28() {
motion.sync_plan_position();
#endif
#endif // !DELTA && !AXEL_TPARA
/**
* Preserve DXC mode across a G28 for IDEX printers in DXC_DUPLICATION_MODE.
@ -511,31 +494,13 @@ void GcodeSuite::G28() {
* IDEX specific commands in it.
*/
#if ENABLED(DUAL_X_CARRIAGE)
if (idex_is_duplicating()) {
if (motion.idex_is_duplicating()) {
TERN_(IMPROVE_HOMING_RELIABILITY, saved_motion_state = begin_slow_homing());
// Always home the 2nd (right) extruder first
motion.extruder = 1;
motion.homeaxis(X_AXIS);
// Remember this extruder's position for later tool change
inactive_extruder_x = motion.position.x;
// Home the 1st (left) extruder
motion.extruder = 0;
motion.homeaxis(X_AXIS);
// Consider the active extruder to be parked
idex_set_parked();
dual_x_carriage_mode = IDEX_saved_mode;
set_duplication_enabled(IDEX_saved_duplication_state);
motion.idex_home_x();
motion.idex_mode = IDEX_saved_mode;
motion.set_extruder_duplication(IDEX_saved_duplication_state);
TERN_(IMPROVE_HOMING_RELIABILITY, end_slow_homing(saved_motion_state));
}
#endif // DUAL_X_CARRIAGE
endstops.not_homing();

View file

@ -148,7 +148,7 @@ void GcodeSuite::G34() {
gcode.process_subcommands_now(F(EVENT_GCODE_BEFORE_G34));
#endif
TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false));
TERN_(HAS_DUPLICATION_MODE, motion.set_extruder_duplication(false));
// Compute a worst-case clearance height to probe from. After the first
// iteration this will be re-calculated based on the actual bed position

View file

@ -177,8 +177,8 @@ inline void park_above_object(measurements_t &m, const float uncertainty) {
inline void normalize_hotend_offsets() {
for (uint8_t e = 1; e < HOTENDS; ++e)
hotend_offset[e] -= hotend_offset[0];
hotend_offset[0].reset();
motion.hotend_offset[e] -= motion.hotend_offset[0];
motion.hotend_offset[0].reset();
}
#endif
@ -568,7 +568,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
//
inline void report_hotend_offsets() {
for (uint8_t e = 1; e < HOTENDS; ++e)
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);
SERIAL_ECHOLNPGM_P(PSTR("T"), e, PSTR(" Hotend Offset X"), motion.hotend_offset[e].x, SP_Y_STR, motion.hotend_offset[e].y, SP_Z_STR, motion.hotend_offset[e].z);
}
#endif
@ -706,9 +706,10 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
// Adjust the hotend offset
#if HAS_HOTEND_OFFSET
if (ENABLED(HAS_X_CENTER) && AXIS_CAN_CALIBRATE(X)) hotend_offset[extruder].x += m.pos_error.x;
if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) hotend_offset[extruder].y += m.pos_error.y;
if (AXIS_CAN_CALIBRATE(Z)) hotend_offset[extruder].z += m.pos_error.z;
xyz_pos_t &hotoff = motion.active_hotend_offset();
if (ENABLED(HAS_X_CENTER) && AXIS_CAN_CALIBRATE(X)) hotoff.x += m.pos_error.x;
if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) hotoff.y += m.pos_error.y;
if (AXIS_CAN_CALIBRATE(Z)) hotoff.z += m.pos_error.z;
normalize_hotend_offsets();
#endif
@ -761,7 +762,7 @@ inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty)
inline void calibrate_all() {
measurements_t m;
TERN_(HAS_HOTEND_OFFSET, reset_hotend_offsets());
TERN_(HAS_HOTEND_OFFSET, motion.reset_hotend_offsets());
TEMPORARY_BACKLASH_CORRECTION(backlash.all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);

View file

@ -47,13 +47,13 @@ void GcodeSuite::M218() {
if (target_extruder < 0) return;
#if HAS_X_AXIS
if (parser.seenval('X')) hotend_offset[target_extruder].x = parser.value_linear_units();
if (parser.seenval('X')) motion.hotend_offset[target_extruder].x = parser.value_linear_units();
#endif
#if HAS_Y_AXIS
if (parser.seenval('Y')) hotend_offset[target_extruder].y = parser.value_linear_units();
if (parser.seenval('Y')) motion.hotend_offset[target_extruder].y = parser.value_linear_units();
#endif
#if HAS_Z_AXIS
if (parser.seenval('Z')) hotend_offset[target_extruder].z = parser.value_linear_units();
if (parser.seenval('Z')) motion.hotend_offset[target_extruder].z = parser.value_linear_units();
#endif
#if ENABLED(DELTA)
@ -70,9 +70,9 @@ void GcodeSuite::M218_report(const bool forReplay/*=true*/) {
report_echo_start(forReplay);
SERIAL_ECHOLNPGM_P(
PSTR(" M218 T"), e,
SP_X_STR, LINEAR_UNIT(hotend_offset[e].x),
SP_Y_STR, LINEAR_UNIT(hotend_offset[e].y),
SP_Z_STR, p_float_t(LINEAR_UNIT(hotend_offset[e].z), 3)
SP_X_STR, LINEAR_UNIT(motion.hotend_offset[e].x),
SP_Y_STR, LINEAR_UNIT(motion.hotend_offset[e].y),
SP_Z_STR, p_float_t(LINEAR_UNIT(motion.hotend_offset[e].z), 3)
);
}
}

View file

@ -64,12 +64,12 @@
planner.synchronize();
if (parser.seenval('S')) {
const DualXMode previous_mode = dual_x_carriage_mode;
const DualXMode previous_mode = motion.idex_mode;
dual_x_carriage_mode = (DualXMode)parser.value_byte();
idex_set_mirrored_mode(false);
motion.idex_mode = (DualXMode)parser.value_byte();
motion.idex_set_mirrored_mode(false);
switch (dual_x_carriage_mode) {
switch (motion.idex_mode) {
case DXC_FULL_CONTROL_MODE:
case DXC_AUTO_PARK_MODE:
@ -77,8 +77,8 @@
case DXC_DUPLICATION_MODE:
// Set the X offset, but no less than the safety gap
if (parser.seenval('X')) duplicate_extruder_x_offset = _MAX(parser.value_linear_units(), (X2_MIN_POS) - (X1_MIN_POS));
if (parser.seenval('R')) duplicate_extruder_temp_offset = parser.value_celsius_diff();
if (parser.seenval('X')) motion.duplicate_extruder_x_offset = _MAX(parser.value_linear_units(), (X2_MIN_POS) - (X1_MIN_POS));
if (parser.seenval('R')) motion.duplicate_extruder_temp_offset = parser.value_celsius_diff();
// Always switch back to tool 0
if (motion.extruder != 0) tool_change(0);
break;
@ -87,10 +87,10 @@
if (previous_mode != DXC_DUPLICATION_MODE) {
SERIAL_ECHOLNPGM("Printer must be in DXC_DUPLICATION_MODE prior to ");
SERIAL_ECHOLNPGM("specifying DXC_MIRRORED_MODE.");
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
motion.idex_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
return;
}
idex_set_mirrored_mode(true);
motion.idex_set_mirrored_mode(true);
// Do a small 'jog' motion in the X axis
xyze_pos_t dest = motion.position; dest.x -= 0.1f;
@ -101,50 +101,50 @@
} return;
default:
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
motion.idex_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
break;
}
idex_set_parked(false);
set_duplication_enabled(false);
motion.idex_set_parked(false);
motion.set_extruder_duplication(false);
#ifdef EVENT_GCODE_IDEX_AFTER_MODECHANGE
process_subcommands_now(F(EVENT_GCODE_IDEX_AFTER_MODECHANGE));
#endif
}
else if (!parser.seen('W')) // if no S or W parameter, the DXC mode gets reset to the user's default
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
motion.idex_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
#if ENABLED(DEBUG_DXC_MODE)
if (parser.seen('W')) {
DEBUG_ECHO_START();
DEBUG_ECHOPGM("Dual X Carriage Mode ");
switch (dual_x_carriage_mode) {
switch (motion.idex_mode) {
case DXC_FULL_CONTROL_MODE: DEBUG_ECHOPGM("FULL_CONTROL"); break;
case DXC_AUTO_PARK_MODE: DEBUG_ECHOPGM("AUTO_PARK"); break;
case DXC_DUPLICATION_MODE: DEBUG_ECHOPGM("DUPLICATION"); break;
case DXC_MIRRORED_MODE: DEBUG_ECHOPGM("MIRRORED"); break;
}
DEBUG_ECHOPGM("\nActive Ext: ", motion.extruder);
if (!active_extruder_parked) DEBUG_ECHOPGM(" NOT ", F(" parked."));
if (!motion.active_extruder_parked) DEBUG_ECHOPGM(" NOT ", F(" parked."));
DEBUG_ECHOLNPGM(
"\nactive_extruder_x_pos: ", motion.position.x,
"\ninactive_extruder_x: ", inactive_extruder_x,
"\nextruder_duplication_enabled: ", extruder_duplication_enabled,
"\nduplicate_extruder_x_offset: ", duplicate_extruder_x_offset,
"\nduplicate_extruder_temp_offset: ", duplicate_extruder_temp_offset,
"\ndelayed_move_time: ", delayed_move_time,
"\nX1 Home: ", x_home_pos(0), " X1_MIN_POS=", X1_MIN_POS, " X1_MAX_POS=", X1_MAX_POS,
"\nX2 Home: ", x_home_pos(1), " X2_MIN_POS=", X2_MIN_POS, " X2_MAX_POS=", X2_MAX_POS,
"\nmotion.position.x: ", motion.position.x,
"\nmotion.inactive_extruder_x: ", motion.inactive_extruder_x,
"\nmotion.extruder_duplication: ", motion.extruder_duplication,
"\nmotion.duplicate_extruder_x_offset: ", motion.duplicate_extruder_x_offset,
"\nmotion.duplicate_extruder_temp_offset: ", motion.duplicate_extruder_temp_offset,
"\nmotion.delayed_move_time: ", motion.delayed_move_time,
"\nX1 Home: ", motion.x_home_pos(0), " X1_MIN_POS=", X1_MIN_POS, " X1_MAX_POS=", X1_MAX_POS,
"\nX2 Home: ", motion.x_home_pos(1), " X2_MIN_POS=", X2_MIN_POS, " X2_MAX_POS=", X2_MAX_POS,
"\nDEFAULT_DUAL_X_CARRIAGE_MODE=", STRINGIFY(DEFAULT_DUAL_X_CARRIAGE_MODE),
"\toolchange_settings.z_raise=", toolchange_settings.z_raise,
"\ntoolchange_settings.z_raise=", toolchange_settings.z_raise,
"\nDEFAULT_DUPLICATION_X_OFFSET=", DEFAULT_DUPLICATION_X_OFFSET
);
HOTEND_LOOP() {
DEBUG_ECHOPGM_P(SP_T_STR, e);
LOOP_NUM_AXES(a) DEBUG_ECHOPGM(" hotend_offset[", e, "].", C(AXIS_CHAR(a) | 0x20), "=", hotend_offset[e][a]);
LOOP_NUM_AXES(a) DEBUG_ECHOPGM(" hotend_offset[", e, "].", C(AXIS_CHAR(a) | 0x20), "=", motion.hotend_offset[e][a]);
DEBUG_EOL();
}
DEBUG_EOL();
@ -170,16 +170,16 @@
bool ena = false;
if (parser.seen("EPS")) {
planner.synchronize();
if (parser.seenval('P')) duplication_e_mask = parser.value_int(); // Set the mask directly
else if (parser.seenval('E')) duplication_e_mask = _BV(parser.value_int() + 1) - 1; // Set the mask by E index
ena = (2 == parser.intval('S', extruder_duplication_enabled ? 2 : 0));
set_duplication_enabled(ena && (duplication_e_mask >= 3));
if (parser.seenval('P')) motion.duplication_e_mask = parser.value_int(); // Set the mask directly
else if (parser.seenval('E')) motion.duplication_e_mask = _BV(parser.value_int() + 1) - 1; // Set the mask by E index
ena = (2 == parser.intval('S', motion.extruder_duplication ? 2 : 0));
motion.set_extruder_duplication(ena && (motion.duplication_e_mask >= 3));
}
SERIAL_ECHO_START();
SERIAL_ECHOPGM(STR_DUPLICATION_MODE, ON_OFF(extruder_duplication_enabled));
SERIAL_ECHOPGM(STR_DUPLICATION_MODE, ON_OFF(motion.extruder_duplication));
if (ena) {
SERIAL_ECHOPGM(" ( ");
HOTEND_LOOP() if (TEST(duplication_e_mask, e)) { SERIAL_ECHO(e); SERIAL_CHAR(' '); }
HOTEND_LOOP() if (TEST(motion.duplication_e_mask, e)) { SERIAL_ECHO(e); SERIAL_CHAR(' '); }
SERIAL_CHAR(')');
}
SERIAL_EOL();

View file

@ -85,7 +85,7 @@ void GcodeSuite::M125() {
#endif
#if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
park_point += hotend_offset[motion.extruder];
park_point += motion.active_hotend_offset();
#endif
const bool sd_printing = card.isStillPrinting();

View file

@ -105,7 +105,7 @@ void GcodeSuite::M600() {
if (!parser.seen_test('T')) { // If no tool index is specified, M600 was (probably) sent in response to filament runout.
// In this case, for duplicating modes set DXC_ext to the extruder that ran out.
#if MULTI_FILAMENT_SENSOR
if (idex_is_duplicating())
if (motion.idex_is_duplicating())
DXC_ext = (READ(FIL_RUNOUT2_PIN) == FIL_RUNOUT2_STATE) ? 1 : 0;
#else
DXC_ext = motion.extruder;
@ -127,7 +127,7 @@ void GcodeSuite::M600() {
#if HAS_MULTI_EXTRUDER
// Change toolhead if specified
const uint8_t active_extruder_before_filament_change = motion.extruder;
if (motion.extruder != target_extruder && TERN1(DUAL_X_CARRIAGE, !idex_is_duplicating()))
if (motion.extruder != target_extruder && TERN1(DUAL_X_CARRIAGE, !motion.idex_is_duplicating()))
tool_change(target_extruder);
#endif
@ -150,7 +150,7 @@ void GcodeSuite::M600() {
);
#if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
park_point += hotend_offset[motion.extruder];
park_point += motion.active_hotend_offset();
#endif
// Unload filament

View file

@ -83,8 +83,8 @@ void GcodeSuite::M428() {
xyz_float_t diff;
LOOP_NUM_AXES(i) {
diff[i] = base_home_pos((AxisEnum)i) - motion.position[i];
if (!WITHIN(diff[i], -20, 20) && home_dir((AxisEnum)i) > 0)
diff[i] = motion.base_home_pos((AxisEnum)i) - motion.position[i];
if (!WITHIN(diff[i], -20, 20) && motion.home_dir((AxisEnum)i) > 0)
diff[i] = -motion.position[i];
if (!WITHIN(diff[i], -20, 20)) {
SERIAL_ERROR_MSG(STR_ERR_M428_TOO_FAR);

View file

@ -47,7 +47,7 @@
* G0, G1: Coordinated movement of X Y Z E axes
*/
void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) {
if (!MOTION_CONDITIONS) return;
if (motion.gcode_motion_ignored()) return;
TERN_(FULL_REPORT_TO_HOST_FEATURE, motion.set_and_report_grblstate(M_RUNNING));

View file

@ -423,7 +423,7 @@ void plan_arc(
* G3 X20 Y12 R14 ; CCW circle with r=14 ending at X20 Y12
*/
void GcodeSuite::G2_G3(const bool clockwise) {
if (!MOTION_CONDITIONS) return;
if (motion.gcode_motion_ignored()) return;
TERN_(FULL_REPORT_TO_HOST_FEATURE, motion.set_and_report_grblstate(M_RUNNING));

View file

@ -44,7 +44,7 @@
* G5: Cubic B-spline
*/
void GcodeSuite::G5() {
if (!MOTION_CONDITIONS) return;
if (motion.gcode_motion_ignored()) return;
#if ENABLED(CNC_WORKSPACE_PLANES)
if (workspace_plane != PLANE_XY) {

View file

@ -42,8 +42,8 @@
}
else {
#if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
hotend_offset[motion.extruder].z -= offs;
SERIAL_ECHO_MSG(STR_PROBE_OFFSET STR_Z ": ", hotend_offset[motion.extruder].z);
motion.active_hotend_offset().z -= offs;
SERIAL_ECHO_MSG(STR_PROBE_OFFSET STR_Z ": ", motion.active_hotend_offset().z);
#endif
}
}
@ -95,13 +95,13 @@ void GcodeSuite::M290() {
SERIAL_ECHOLNPGM_P(
PSTR("Hotend "), motion.extruder
#if ENABLED(BABYSTEP_XY)
, PSTR("Offset X"), hotend_offset[motion.extruder].x
, SP_Y_STR, hotend_offset[motion.extruder].y
, PSTR("Offset X"), motion.active_hotend_offset().x
, SP_Y_STR, motion.active_hotend_offset().y
, SP_Z_STR
#else
, PSTR("Offset Z")
#endif
, hotend_offset[motion.extruder].z
, motion.active_hotend_offset().z
);
}
#endif

View file

@ -60,7 +60,7 @@ FORCE_INLINE bool G38_run_probe() {
xyz_float_t retract_mm;
LOOP_NUM_AXES(i) {
const float dist = motion.destination[i] - motion.position[i];
retract_mm[i] = ABS(dist) < G38_MINIMUM_MOVE ? 0 : home_bump_mm((AxisEnum)i) * (dist > 0 ? -1 : 1);
retract_mm[i] = ABS(dist) < G38_MINIMUM_MOVE ? 0 : motion.home_bump_mm((AxisEnum)i) * (dist > 0 ? -1 : 1);
}
#endif

View file

@ -111,8 +111,8 @@ void GcodeSuite::M104_M109(const bool isM109) {
thermalManager.setTargetHotend(temp, target_extruder);
#if ENABLED(DUAL_X_CARRIAGE)
if (idex_is_duplicating() && target_extruder == 0)
thermalManager.setTargetHotend(temp ? temp + duplicate_extruder_temp_offset : 0, 1);
if (motion.idex_is_duplicating() && target_extruder == 0)
thermalManager.setTargetHotend(temp ? temp + motion.duplicate_extruder_temp_offset : 0, 1);
#endif
#if ENABLED(PRINTJOB_TIMER_AUTOSTART)

View file

@ -93,7 +93,7 @@ void GcodeSuite::M106() {
TERN_(LASER_SYNCHRONOUS_M106_M107, planner.buffer_sync_block(BLOCK_BIT_SYNC_FANS));
if (TERN0(DUAL_X_CARRIAGE, idex_is_duplicating())) // pfan == 0 when duplicating
if (TERN0(DUAL_X_CARRIAGE, motion.idex_is_duplicating())) // pfan == 0 when duplicating
thermalManager.set_fan_speed(1 - pfan, speed);
}
@ -107,7 +107,7 @@ void GcodeSuite::M107() {
thermalManager.set_fan_speed(pfan, 0);
if (TERN0(DUAL_X_CARRIAGE, idex_is_duplicating())) // pfan == 0 when duplicating
if (TERN0(DUAL_X_CARRIAGE, motion.idex_is_duplicating())) // pfan == 0 when duplicating
thermalManager.set_fan_speed(1 - pfan, 0);
TERN_(LASER_SYNCHRONOUS_M106_M107, planner.buffer_sync_block(BLOCK_BIT_SYNC_FANS));

View file

@ -462,6 +462,7 @@
#endif
#if ANY(AUTO_BED_LEVELING_LINEAR, AUTO_BED_LEVELING_BILINEAR)
#define ABL_USES_GRID 1
#define HAS_VARIABLE_XY_PROBE_FEEDRATE 1
#ifndef XY_PROBE_FEEDRATE_MIN
#define XY_PROBE_FEEDRATE_MIN 60 // Minimum mm/min value for 'G29 S<feedrate>'
#endif

View file

@ -42,7 +42,7 @@
* version was tagged.
*/
#ifndef STRING_DISTRIBUTION_DATE
#define STRING_DISTRIBUTION_DATE "2026-02-14"
#define STRING_DISTRIBUTION_DATE "2026-02-19"
#endif
/**

View file

@ -1078,7 +1078,7 @@ void MarlinUI::draw_status_screen() {
#if ENABLED(LCD_SHOW_E_TOTAL)
char tmp[20];
const uint8_t escale = motion.e_move_accumulator >= 100000.0f ? 10 : 1; // After 100m switch to cm
sprintf_P(tmp, PSTR("E %ld%cm "), uint32_t(_MAX(motion.e_move_accumulator, 0.0f)) / escale, escale == 10 ? 'c' : 'm'); // 1234567mm
sprintf_P(tmp, PSTR("E %" PRIu32 "%cm "), uint32_t(_MAX(motion.e_move_accumulator, 0.0f)) / escale, escale == 10 ? 'c' : 'm'); // 1234567mm
lcd_put_u8str(tmp);
#endif
}

View file

@ -580,7 +580,7 @@ void MarlinUI::draw_status_screen() {
if (show_e_total) {
#if ENABLED(LCD_SHOW_E_TOTAL)
const uint8_t escale = motion.e_move_accumulator >= 100000.0f ? 10 : 1; // After 100m switch to cm
sprintf_P(xstring, PSTR("%ld%cm"), uint32_t(_MAX(motion.e_move_accumulator, 0.0f)) / escale, escale == 10 ? 'c' : 'm'); // 1234567mm
sprintf_P(xstring, PSTR("%" PRIu32 "%cm"), uint32_t(_MAX(motion.e_move_accumulator, 0.0f)) / escale, escale == 10 ? 'c' : 'm'); // 1234567mm
#endif
}
else {

View file

@ -615,7 +615,7 @@ void ST7920_Lite_Status_Screen::draw_position(const xyze_pos_t &pos, const bool
#if ENABLED(LCD_SHOW_E_TOTAL)
char tmp[15];
const uint8_t escale = motion.e_move_accumulator >= 100000.0f ? 10 : 1; // After 100m switch to cm
sprintf_P(tmp, PSTR("E%-7ld%cm "), uint32_t(_MAX(motion.e_move_accumulator, 0.0f)) / escale, escale == 10 ? 'c' : 'm'); // 1234567mm
sprintf_P(tmp, PSTR("E%-7" PRIu32 "%cm "), uint32_t(_MAX(motion.e_move_accumulator, 0.0f)) / escale, escale == 10 ? 'c' : 'm'); // 1234567mm
write_str(tmp);
#endif
}

View file

@ -104,7 +104,7 @@ void MarlinGame::frame_end() {
char perf_str[32];
sprintf_P(
perf_str,
PSTR("d%04lu w%04lu c%04lu"),
PSTR("d%04" PRIu32 " w%04" PRIu32 " c%04" PRIu32),
frame_draw_millis,
frame_wait_millis,
dwin_game_perf.draw_calls

View file

@ -687,7 +687,7 @@ namespace ExtUI {
#endif
#if ENABLED(DUAL_X_CARRIAGE)
uint8_t getIDEX_Mode() { return dual_x_carriage_mode; }
uint8_t getIDEX_Mode() { return motion.idex_mode; }
#endif
#if HAS_PREHEAT
@ -765,7 +765,7 @@ namespace ExtUI {
if (!linked_nozzles) {
HOTEND_LOOP()
if (e != motion.extruder)
hotend_offset[e][axis] += mm;
motion.hotend_offset[e][axis] += mm;
TERN_(HAS_X_AXIS, normalizeNozzleOffset(X));
TERN_(HAS_Y_AXIS, normalizeNozzleOffset(Y));
@ -818,12 +818,12 @@ namespace ExtUI {
float getNozzleOffset_mm(const axis_t axis, const extruder_t extruder) {
if (extruder - E0 >= HOTENDS) return 0;
return hotend_offset[extruder - E0][axis];
return motion.hotend_offset[extruder - E0][axis];
}
void setNozzleOffset_mm(const float value, const axis_t axis, const extruder_t extruder) {
if (extruder - E0 >= HOTENDS) return;
hotend_offset[extruder - E0][axis] = value;
motion.hotend_offset[extruder - E0][axis] = value;
}
/**
@ -832,8 +832,8 @@ namespace ExtUI {
* user to edit the offset the first nozzle).
*/
void normalizeNozzleOffset(const axis_t axis) {
const float offs = hotend_offset[0][axis];
HOTEND_LOOP() hotend_offset[e][axis] -= offs;
const float offs = motion.hotend_offset[0][axis];
HOTEND_LOOP() motion.hotend_offset[e][axis] -= offs;
}
#endif // HAS_HOTEND_OFFSET
@ -887,14 +887,14 @@ namespace ExtUI {
y_target = MESH_MIN_Y + pos.y * (MESH_Y_DIST);
if (x_target != motion.position.x || y_target != motion.position.y) {
// If moving across bed, raise nozzle to safe height over bed
motion.feedrate_mm_s = z_probe_fast_mm_s;
motion.destination.set(motion.position.x, motion.position.y, Z_CLEARANCE_BETWEEN_PROBES);
motion.feedrate_mm_s = motion.z_probe_fast_mm_s;
motion.destination.set(motion.position.x, motion.position.y, Z_TWEEN_SAFE_CLEARANCE);
motion.prepare_line_to_destination();
if (XY_PROBE_FEEDRATE_MM_S) motion.feedrate_mm_s = XY_PROBE_FEEDRATE_MM_S;
motion.destination.set(x_target, y_target);
motion.prepare_line_to_destination();
}
motion.feedrate_mm_s = z_probe_fast_mm_s;
motion.feedrate_mm_s = motion.z_probe_fast_mm_s;
motion.destination.z = z;
motion.prepare_line_to_destination();
#else

View file

@ -317,7 +317,7 @@ void scroll_screen(const uint8_t limit, const bool is_menu) {
const float diff = planner.mm_per_step[Z_AXIS] * babystep_increment,
new_probe_offset = probe.offset.z + diff,
new_offs = TERN(BABYSTEP_HOTEND_Z_OFFSET
, do_probe ? new_probe_offset : hotend_offset[motion.extruder].z - diff
, do_probe ? new_probe_offset : motion.active_hotend_offset().z - diff
, new_probe_offset
);
if (WITHIN(new_offs, PROBE_OFFSET_ZMIN, PROBE_OFFSET_ZMAX)) {
@ -327,7 +327,7 @@ void scroll_screen(const uint8_t limit, const bool is_menu) {
if (do_probe)
probe.offset.z = new_offs;
else
TERN(BABYSTEP_HOTEND_Z_OFFSET, hotend_offset[motion.extruder].z = new_offs, NOOP);
TERN(BABYSTEP_HOTEND_Z_OFFSET, motion.active_hotend_offset().z = new_offs, NOOP);
ui.refresh(LCDVIEW_CALL_REDRAW_NEXT);
}
@ -339,7 +339,7 @@ void scroll_screen(const uint8_t limit, const bool is_menu) {
}
else {
#if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
MenuEditItemBase::draw_edit_screen(GET_TEXT_F(MSG_HOTEND_OFFSET_Z), ftostr54sign(hotend_offset[motion.extruder].z));
MenuEditItemBase::draw_edit_screen(GET_TEXT_F(MSG_HOTEND_OFFSET_Z), ftostr54sign(motion.active_hotend_offset().z));
#endif
}
}

View file

@ -234,7 +234,7 @@ static void _lcd_goto_next_corner() {
bool _lcd_bed_tramming_probe(const bool verify=false) {
if (verify) motion.do_z_clearance_by(BED_TRAMMING_Z_HOP); // Do clearance if needed
TERN_(BLTOUCH, if (!bltouch.high_speed_mode) bltouch.deploy()); // Deploy in LOW SPEED MODE on every probe action
motion.blocking_move_z(last_z - BED_TRAMMING_PROBE_TOLERANCE, z_probe_slow_mm_s); // Move down to lower tolerance
motion.blocking_move_z(last_z - BED_TRAMMING_PROBE_TOLERANCE, motion.z_probe_slow_mm_s); // Move down to lower tolerance
if (TEST(endstops.trigger_state(), Z_MIN_PROBE)) { // Probe triggered?
endstops.hit_on_purpose();
motion.set_current_from_steppers_for_axis(Z_AXIS);

View file

@ -241,12 +241,12 @@ void menu_advanced_settings();
START_MENU();
BACK_ITEM(MSG_CONFIGURATION);
#if ENABLED(DUAL_X_CARRIAGE)
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_HOTEND_OFFSET_N, &hotend_offset[1].x, float(X2_HOME_POS - 25), float(X2_HOME_POS + 25), _recalc_offsets);
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_HOTEND_OFFSET_N, &motion.hotend_offset[1].x, float(X2_HOME_POS - 25), float(X2_HOME_POS + 25), _recalc_offsets);
#else
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_HOTEND_OFFSET_N, &hotend_offset[1].x, -99.0f, 99.0f, _recalc_offsets);
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_HOTEND_OFFSET_N, &motion.hotend_offset[1].x, -99.0f, 99.0f, _recalc_offsets);
#endif
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_HOTEND_OFFSET_N, &hotend_offset[1].y, -99.0f, 99.0f, _recalc_offsets);
EDIT_ITEM_FAST_N(float42_52, Z_AXIS, MSG_HOTEND_OFFSET_N, &hotend_offset[1].z, -10.0f, 10.0f, _recalc_offsets);
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_HOTEND_OFFSET_N, &motion.hotend_offset[1].y, -99.0f, 99.0f, _recalc_offsets);
EDIT_ITEM_FAST_N(float42_52, Z_AXIS, MSG_HOTEND_OFFSET_N, &motion.hotend_offset[1].z, -10.0f, 10.0f, _recalc_offsets);
#if ENABLED(EEPROM_SETTINGS)
ACTION_ITEM(MSG_STORE_EEPROM, ui.store_settings);
#endif
@ -293,7 +293,7 @@ void menu_advanced_settings();
);
GCODES_ITEM(MSG_IDEX_MODE_FULL_CTRL, F("M605S0\nG28X"));
EDIT_ITEM(float42_52, MSG_IDEX_DUPE_GAP, &duplicate_extruder_x_offset, (X2_MIN_POS) - (X1_MIN_POS), (X_BED_SIZE) - 20);
EDIT_ITEM(float42_52, MSG_IDEX_DUPE_GAP, &motion.duplicate_extruder_x_offset, (X2_MIN_POS) - (X1_MIN_POS), (X_BED_SIZE) - 20);
END_MENU();
}

View file

@ -209,7 +209,7 @@ void menu_mmu3_toolchange_stat_total() {
sprintf_P(buffer1, PSTR("%u"), MMU3::operation_statistics.tool_change_counter);
char buffer2[LCD_WIDTH];
sprintf_P(buffer2, PSTR("%lu"), MMU3::operation_statistics.tool_change_total_counter);
sprintf_P(buffer2, PSTR("%" PRIu32 ""), MMU3::operation_statistics.tool_change_total_counter);
START_SCREEN();
STATIC_ITEM(MSG_MMU_MATERIAL_CHANGES, SS_INVERT);

View file

@ -150,7 +150,7 @@ void _menu_move_distance(const AxisEnum axis, const screenFunc_t func, const int
BACK_ITEM(MSG_MOVE_AXIS);
#define __LINEAR_LIMIT(D) ((D) < max_length(axis) / 2 + 1)
#define __LINEAR_LIMIT(D) ((D) < motion.max_axis_length(axis) / 2 + 1)
#if HAS_EXTRUDERS
#ifndef EXTRUDE_MAXLENGTH
#define EXTRUDE_MAXLENGTH 50
@ -159,9 +159,9 @@ void _menu_move_distance(const AxisEnum axis, const screenFunc_t func, const int
#else
#define _LINEAR_LIMIT __LINEAR_LIMIT
#endif
#define __MOVE_SUB(L,T,D) if (rotational[axis] || _LINEAR_LIMIT(D)) SUBMENU_S(F(T), L, []{ _goto_manual_move(D); })
#define __MOVE_SUB(L,T,D) if (motion.rotational[axis] || _LINEAR_LIMIT(D)) SUBMENU_S(F(T), L, []{ _goto_manual_move(D); })
if (rotational[axis]) {
if (motion.rotational[axis]) {
#ifdef MANUAL_MOVE_DISTANCE_DEG
#define _MOVE_DEG(D) __MOVE_SUB(MSG_MOVE_N_DEG, STRINGIFY(D), D);
MAP(_MOVE_DEG, MANUAL_MOVE_DISTANCE_DEG)

View file

@ -86,7 +86,7 @@ void moveAxis(const AxisEnum axis, const int8_t direction) {
const float bsDiff = planner.mm_per_step[Z_AXIS] * babystep_increment,
new_probe_offset = probe.offset.z + bsDiff,
new_offs = TERN(BABYSTEP_HOTEND_Z_OFFSET
, do_probe ? new_probe_offset : hotend_offset[motion.extruder].z - bsDiff
, do_probe ? new_probe_offset : motion.active_hotend_offset().z - bsDiff
, new_probe_offset
);
if (WITHIN(new_offs, PROBE_OFFSET_ZMIN, PROBE_OFFSET_ZMAX)) {
@ -94,7 +94,7 @@ void moveAxis(const AxisEnum axis, const int8_t direction) {
if (do_probe)
probe.offset.z = new_offs;
else
TERN(BABYSTEP_HOTEND_Z_OFFSET, hotend_offset[motion.extruder].z = new_offs, NOOP);
TERN(BABYSTEP_HOTEND_Z_OFFSET, motion.active_hotend_offset().z = new_offs, NOOP);
drawMessage_P(NUL_STR); // Clear the error
}
else

View file

@ -112,8 +112,8 @@ void recalc_delta_settings() {
void inverse_kinematics(const xyz_pos_t &raw) {
#if HAS_HOTEND_OFFSET
// Delta hotend offsets must be applied in Cartesian space with no "spoofing"
xyz_pos_t pos = { raw.x - hotend_offset[motion.extruder].x,
raw.y - hotend_offset[motion.extruder].y,
xyz_pos_t pos = { raw.x - motion.active_hotend_offset().x,
raw.y - motion.active_hotend_offset().y,
raw.z };
DELTA_IK(pos);
//DELTA_DEBUG(pos);
@ -226,22 +226,22 @@ void home_delta() {
// Disable stealthChop if used. Enable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING)
TERN_(X_SENSORLESS, sensorless_t stealth_states_x = start_sensorless_homing_per_axis(X_AXIS));
TERN_(Y_SENSORLESS, sensorless_t stealth_states_y = start_sensorless_homing_per_axis(Y_AXIS));
TERN_(Z_SENSORLESS, sensorless_t stealth_states_z = start_sensorless_homing_per_axis(Z_AXIS));
TERN_(I_SENSORLESS, sensorless_t stealth_states_i = start_sensorless_homing_per_axis(I_AXIS));
TERN_(J_SENSORLESS, sensorless_t stealth_states_j = start_sensorless_homing_per_axis(J_AXIS));
TERN_(K_SENSORLESS, sensorless_t stealth_states_k = start_sensorless_homing_per_axis(K_AXIS));
TERN_(U_SENSORLESS, sensorless_t stealth_states_u = start_sensorless_homing_per_axis(U_AXIS));
TERN_(V_SENSORLESS, sensorless_t stealth_states_v = start_sensorless_homing_per_axis(V_AXIS));
TERN_(W_SENSORLESS, sensorless_t stealth_states_w = start_sensorless_homing_per_axis(W_AXIS));
TERN_(X_SENSORLESS, sensorless_t stealth_states_x = motion.sensorless_axis_homing_start(X_AXIS));
TERN_(Y_SENSORLESS, sensorless_t stealth_states_y = motion.sensorless_axis_homing_start(Y_AXIS));
TERN_(Z_SENSORLESS, sensorless_t stealth_states_z = motion.sensorless_axis_homing_start(Z_AXIS));
TERN_(I_SENSORLESS, sensorless_t stealth_states_i = motion.sensorless_axis_homing_start(I_AXIS));
TERN_(J_SENSORLESS, sensorless_t stealth_states_j = motion.sensorless_axis_homing_start(J_AXIS));
TERN_(K_SENSORLESS, sensorless_t stealth_states_k = motion.sensorless_axis_homing_start(K_AXIS));
TERN_(U_SENSORLESS, sensorless_t stealth_states_u = motion.sensorless_axis_homing_start(U_AXIS));
TERN_(V_SENSORLESS, sensorless_t stealth_states_v = motion.sensorless_axis_homing_start(V_AXIS));
TERN_(W_SENSORLESS, sensorless_t stealth_states_w = motion.sensorless_axis_homing_start(W_AXIS));
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif
#endif
// Set homing current for all motors
TERN_(HAS_HOMING_CURRENT, set_homing_current(Z_AXIS));
TERN_(HAS_HOMING_CURRENT, motion.set_homing_current(Z_AXIS));
// Move all carriages together linearly until an endstop is hit.
motion.position.z = DIFF_TERN(HAS_BED_PROBE, delta_height + 10, probe.offset.z);
@ -250,19 +250,19 @@ void home_delta() {
TERN_(HAS_DELTA_SENSORLESS_PROBING, endstops.report_states());
// Restore the homing current for all motors
TERN_(HAS_HOMING_CURRENT, restore_homing_current(Z_AXIS));
TERN_(HAS_HOMING_CURRENT, motion.restore_homing_current(Z_AXIS));
// Re-enable stealthChop if used. Disable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)
TERN_(X_SENSORLESS, end_sensorless_homing_per_axis(X_AXIS, stealth_states_x));
TERN_(Y_SENSORLESS, end_sensorless_homing_per_axis(Y_AXIS, stealth_states_y));
TERN_(Z_SENSORLESS, end_sensorless_homing_per_axis(Z_AXIS, stealth_states_z));
TERN_(I_SENSORLESS, end_sensorless_homing_per_axis(I_AXIS, stealth_states_i));
TERN_(J_SENSORLESS, end_sensorless_homing_per_axis(J_AXIS, stealth_states_j));
TERN_(K_SENSORLESS, end_sensorless_homing_per_axis(K_AXIS, stealth_states_k));
TERN_(U_SENSORLESS, end_sensorless_homing_per_axis(U_AXIS, stealth_states_u));
TERN_(V_SENSORLESS, end_sensorless_homing_per_axis(V_AXIS, stealth_states_v));
TERN_(W_SENSORLESS, end_sensorless_homing_per_axis(W_AXIS, stealth_states_w));
TERN_(X_SENSORLESS, motion.sensorless_axis_homing_end(X_AXIS, stealth_states_x));
TERN_(Y_SENSORLESS, motion.sensorless_axis_homing_end(Y_AXIS, stealth_states_y));
TERN_(Z_SENSORLESS, motion.sensorless_axis_homing_end(Z_AXIS, stealth_states_z));
TERN_(I_SENSORLESS, motion.sensorless_axis_homing_end(I_AXIS, stealth_states_i));
TERN_(J_SENSORLESS, motion.sensorless_axis_homing_end(J_AXIS, stealth_states_j));
TERN_(K_SENSORLESS, motion.sensorless_axis_homing_end(K_AXIS, stealth_states_k));
TERN_(U_SENSORLESS, motion.sensorless_axis_homing_end(U_AXIS, stealth_states_u));
TERN_(V_SENSORLESS, motion.sensorless_axis_homing_end(V_AXIS, stealth_states_v));
TERN_(W_SENSORLESS, motion.sensorless_axis_homing_end(W_AXIS, stealth_states_w));
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif

View file

@ -78,6 +78,9 @@ Motion motion;
// Relative Mode. Enable with G91, disable with G90.
bool Motion::relative_mode; // = false
// Flags for rotational axes
constexpr AxisFlags Motion::rotational;
// The active extruder (tool). Set with T<extruder> command.
#if HAS_MULTI_EXTRUDER
uint8_t Motion::extruder = 0; // = 0
@ -134,10 +137,14 @@ xyz_pos_t Motion::cartes;
float Motion::e_move_accumulator; // = 0
#endif
#if ENABLED(DUAL_X_CARRIAGE)
DualXMode Motion::idex_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
#endif
// Extruder offsets
#if HAS_HOTEND_OFFSET
xyz_pos_t hotend_offset[HOTENDS]; // Initialized by settings.load
void reset_hotend_offsets() {
xyz_pos_t Motion::hotend_offset[HOTENDS]; // Initialized by settings.load
void Motion::reset_hotend_offsets() {
constexpr float tmp[3][HOTENDS] = { HOTEND_OFFSET_X, HOTEND_OFFSET_Y, HOTEND_OFFSET_Z };
static_assert(
!tmp[X_AXIS][0] && !tmp[Y_AXIS][0] && !tmp[Z_AXIS][0],
@ -147,6 +154,10 @@ xyz_pos_t Motion::cartes;
HOTEND_LOOP() LOOP_ABC(a) hotend_offset[e][a] = tmp[a][e];
TERN_(DUAL_X_CARRIAGE, hotend_offset[1].x = _MAX(X2_HOME_POS, X2_MAX_POS));
}
#elif HOTENDS
constexpr xyz_pos_t Motion::hotend_offset[HOTENDS];
#else
constexpr xyz_pos_t Motion::hotend_offset[1];
#endif
// The feedrate for the current move, often used as the default if
@ -203,8 +214,15 @@ int16_t Motion::feedrate_percentage = 100;
xyz_pos_t Motion::workspace_offset{0};
#endif
#if ABL_USES_GRID
feedRate_t xy_probe_feedrate_mm_s = MMM_TO_MMS(XY_PROBE_FEEDRATE);
#if HAS_VARIABLE_XY_PROBE_FEEDRATE
feedRate_t Motion::xy_probe_feedrate_mm_s = MMM_TO_MMS(XY_PROBE_FEEDRATE);
#endif
#ifdef Z_PROBE_FEEDRATE_SLOW
constexpr feedRate_t Motion::z_probe_slow_mm_s;
#endif
#ifdef Z_PROBE_FEEDRATE_FAST
constexpr feedRate_t Motion::z_probe_fast_mm_s;
#endif
/**
@ -280,7 +298,7 @@ void Motion::report_position_projected() {
* Set motors to their homing / probing currents.
* Currents are saved first so they can be restored afterward.
*/
void set_homing_current(const AxisEnum axis) {
void Motion::set_homing_current(const AxisEnum axis) {
#define HOMING_CURRENT(A) TERN(EDITABLE_HOMING_CURRENT, homing_current_mA.A, A##_CURRENT_HOME)
@ -439,7 +457,7 @@ void Motion::report_position_projected() {
* Restore motors to their previously-stored currents.
* Always call set_homing_current() first!
*/
void restore_homing_current(const AxisEnum axis) {
void Motion::restore_homing_current(const AxisEnum axis) {
// Restore the saved current
#define _RESTORE_CURRENT(A) \
@ -746,7 +764,7 @@ void Motion::quickstop_stepper() {
*/
void Motion::sync_plan_position() {
if (DEBUGGING(LEVELING)) DEBUG_POS("sync_plan_position", position);
planner.set_position_mm(motion.position);
planner.set_position_mm(position);
//SERIAL_ECHOLNPGM("Sync_plan_position: ", position.x, ", ", position.y, ", ", position.z);
//SERIAL_EOL();
}
@ -816,6 +834,10 @@ void Motion::set_current_from_steppers_for_axis(const AxisEnum axis) {
position[axis] = pos[axis];
}
bool Motion::gcode_motion_ignored() {
return !marlin.isRunning() || TERN0(NO_MOTION_BEFORE_HOMING, homing_needed_error());
}
/**
* Move the planner to the current position from wherever it last moved
* (or from wherever it has been told it is located).
@ -1264,7 +1286,7 @@ void Motion::restore_feedrate_and_scaling() {
// retain the same physical limit when other tools are selected.
if (new_tool_index == old_tool_index || axis == Z_AXIS) { // The Z axis is "special" and shouldn't be modified
const float offs = (axis == Z_AXIS) ? 0 : hotend_offset[extruder][axis];
const float offs = (axis == Z_AXIS) ? 0 : active_hotend_offset()[axis];
soft_endstop.min[axis] = base_min_pos(axis) + offs;
soft_endstop.max[axis] = base_max_pos(axis) + offs;
}
@ -1303,7 +1325,7 @@ void Motion::restore_feedrate_and_scaling() {
#if ALL(HAS_HOTEND_OFFSET, DELTA)
// The effector center position will be the target minus the hotend offset.
const xy_pos_t offs = hotend_offset[extruder];
const xy_pos_t offs = active_hotend_offset();
#elif ENABLED(POLARGRAPH)
// POLARGRAPH uses draw_area_* below...
#elif ENABLED(POLAR)
@ -1774,50 +1796,37 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE
#endif // !IS_KINEMATIC
#if HAS_DUPLICATION_MODE
bool extruder_duplication_enabled;
bool Motion::extruder_duplication;
#if ENABLED(MULTI_NOZZLE_DUPLICATION)
uint8_t duplication_e_mask; // = 0
uint8_t Motion::duplication_e_mask; // = 0
#endif
#endif
#if ENABLED(DUAL_X_CARRIAGE)
DualXMode dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
float inactive_extruder_x = X2_MAX_POS, // Used in mode 0 & 1
duplicate_extruder_x_offset = DEFAULT_DUPLICATION_X_OFFSET; // Used in mode 2 & 3
xyz_pos_t raised_parked_position; // Used in mode 1
bool active_extruder_parked = false; // Used in mode 1, 2 & 3
millis_t delayed_move_time = 0; // Used in mode 1
celsius_t duplicate_extruder_temp_offset = 0; // Used in mode 2 & 3
bool idex_mirrored_mode = false; // Used in mode 3
float Motion::inactive_extruder_x = X2_MAX_POS, // Used in mode 0 & 1
Motion::duplicate_extruder_x_offset = DEFAULT_DUPLICATION_X_OFFSET; // Used in mode 2 & 3
bool Motion::idex_mirrored_mode = false; // Used in mode 3
xyz_pos_t Motion::raised_parked_position; // Used in mode 1
bool Motion::active_extruder_parked = false; // Used in mode 1, 2 & 3
millis_t Motion::delayed_move_time = 0; // Used in mode 1
celsius_t Motion::duplicate_extruder_temp_offset = 0; // Used in mode 2 & 3
float x_home_pos(const uint8_t extruder) {
if (extruder == 0) return X_HOME_POS;
/**
* In dual carriage mode the extruder offset provides an override of the
* second X-carriage position when homed - otherwise X2_HOME_POS is used.
* This allows soft recalibration of the second extruder home position
* (with M218 T1 Xn) without firmware reflash.
*/
return hotend_offset[1].x > 0 ? hotend_offset[1].x : X2_HOME_POS;
void Motion::set_extruder_duplication(const bool dupe, const int8_t tool_index/*=-1*/) {
_set_duplication_enabled(dupe);
if (tool_index >= 0) extruder = tool_index;
stepper.apply_directions();
}
void idex_set_mirrored_mode(const bool mirr) {
void Motion::idex_set_mirrored_mode(const bool mirr) {
idex_mirrored_mode = mirr;
stepper.apply_directions();
}
void set_duplication_enabled(const bool dupe, const int8_t tool_index/*=-1*/) {
extruder_duplication_enabled = dupe;
if (tool_index >= 0) motion.extruder = tool_index;
stepper.apply_directions();
}
void idex_set_parked(const bool park/*=true*/) {
void Motion::idex_set_parked(const bool park/*=true*/) {
delayed_move_time = 0;
active_extruder_parked = park;
if (park) raised_parked_position = motion.position; // Remember current raised toolhead position for use by unpark
if (park) raised_parked_position = position; // Remember current raised toolhead position for use by unpark
}
/**
@ -1825,20 +1834,20 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE
*
* Return true if position[] was set to destination[]
*/
inline bool dual_x_carriage_unpark() {
bool Motion::unpark_before_move() {
if (active_extruder_parked) {
switch (dual_x_carriage_mode) {
switch (idex_mode) {
case DXC_FULL_CONTROL_MODE: break;
case DXC_AUTO_PARK_MODE: {
if (motion.position.e == motion.destination.e) {
if (position.e == destination.e) {
// This is a travel move (with no extrusion)
// Skip it, but keep track of the current position
// (so it can be used as the start of the next non-travel move)
if (delayed_move_time != UINT32_MAX) {
motion.position = motion.destination;
NOLESS(raised_parked_position.z, motion.destination.z);
position = destination;
NOLESS(raised_parked_position.z, destination.z);
delayed_move_time = millis() + 1000UL;
return true;
}
@ -1848,13 +1857,13 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE
//
const feedRate_t fr_zfast = planner.settings.max_feedrate_mm_s[Z_AXIS];
// 1. Move to the raised parked XYZ. Presumably the tool is already at XY.
xyze_pos_t raised = raised_parked_position; raised.e = motion.position.e;
xyze_pos_t raised = raised_parked_position; raised.e = position.e;
if (planner.buffer_line(raised, fr_zfast)) {
// 2. Move to the current native XY and raised Z. Presumably this is a null move.
xyze_pos_t curpos = motion.position; curpos.z = raised_parked_position.z;
xyze_pos_t curpos = position; curpos.z = raised_parked_position.z;
if (planner.buffer_line(curpos, PLANNER_XY_FEEDRATE_MM_S)) {
// 3. Lower Z back down
motion.goto_current_position(fr_zfast);
goto_current_position(fr_zfast);
}
}
stepper.apply_directions();
@ -1865,17 +1874,17 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE
case DXC_MIRRORED_MODE:
case DXC_DUPLICATION_MODE:
if (motion.extruder == 0) {
set_duplication_enabled(false); // Clear stale duplication state
if (extruder == 0) {
set_extruder_duplication(false); // Clear stale duplication state
// Restore planner to parked head (T1) X position
float x0_pos = motion.position.x;
xyze_pos_t pos_now = motion.position;
float x0_pos = position.x;
xyze_pos_t pos_now = position;
pos_now.x = inactive_extruder_x;
planner.set_position_mm(pos_now);
// Keep the same X or add the duplication X offset
xyze_pos_t new_pos = pos_now;
if (dual_x_carriage_mode == DXC_DUPLICATION_MODE)
if (idex_mode == DXC_DUPLICATION_MODE)
new_pos.x = x0_pos + duplicate_extruder_x_offset;
else
new_pos.x = _MIN(X_BED_SIZE - x0_pos, X_MAX_POS);
@ -1885,10 +1894,10 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE
if (!planner.buffer_line(new_pos, planner.settings.max_feedrate_mm_s[X_AXIS], 1)) break;
planner.synchronize();
motion.sync_plan_position(); // Extra sync for good measure
set_duplication_enabled(true); // Enable Duplication
idex_set_parked(false); // No longer parked
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("set_duplication_enabled(true)\nidex_set_parked(false)");
sync_plan_position(); // Extra sync for good measure
set_extruder_duplication(true); // Enable Duplication
idex_set_parked(false); // No longer parked
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("set_extruder_duplication(true)\nidex_set_parked(false)");
}
else if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Active extruder not 0");
break;
@ -1897,6 +1906,22 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE
return false;
}
void Motion::idex_home_x() {
// Always home the 2nd (right) extruder first
extruder = 1;
homeaxis(X_AXIS);
// Remember this extruder's position for later tool change
inactive_extruder_x = position.x;
// Home the 1st (left) extruder
extruder = 0;
homeaxis(X_AXIS);
// Consider the active extruder to be in its "parked" position
idex_set_parked();
}
#endif // DUAL_X_CARRIAGE
/**
@ -1950,7 +1975,7 @@ void Motion::prepare_line_to_destination() {
#endif // PREVENT_COLD_EXTRUSION || PREVENT_LENGTHY_EXTRUDE
if (TERN0(DUAL_X_CARRIAGE, dual_x_carriage_unpark())) return;
if (unpark_before_move()) return;
if (
#if UBL_SEGMENTED
@ -2028,7 +2053,7 @@ void Motion::prepare_line_to_destination() {
/**
* Set sensorless homing if the axis has it, accounting for Core Kinematics.
*/
sensorless_t start_sensorless_homing_per_axis(const AxisEnum axis) {
sensorless_t Motion::sensorless_axis_homing_start(const AxisEnum axis) {
sensorless_t stealth_states { false };
switch (axis) {
@ -2124,7 +2149,7 @@ void Motion::prepare_line_to_destination() {
return stealth_states;
}
void end_sensorless_homing_per_axis(const AxisEnum axis, sensorless_t enable_stealth) {
void Motion::sensorless_axis_homing_end(const AxisEnum axis, sensorless_t enable_stealth) {
switch (axis) {
default: break;
#if X_SENSORLESS
@ -2235,7 +2260,7 @@ void Motion::prepare_line_to_destination() {
// Only do some things when moving towards an endstop
const int8_t axis_home_dir = TERN0(DUAL_X_CARRIAGE, axis == X_AXIS)
? TOOL_X_HOME_DIR(extruder) : home_dir(axis);
? tool_x_home_dir() : home_dir(axis);
const bool is_home_dir = (axis_home_dir > 0) == (distance > 0);
#if ENABLED(SENSORLESS_HOMING)
@ -2260,12 +2285,12 @@ void Motion::prepare_line_to_destination() {
// Disable stealthChop if used. Enable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING)
stealth_states = start_sensorless_homing_per_axis(axis);
stealth_states = sensorless_axis_homing_start(axis);
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif
#endif
}
} // is_home_dir
#if ANY(MORGAN_SCARA, MP_SCARA)
// Tell the planner the axis is at 0
@ -2301,13 +2326,15 @@ void Motion::prepare_line_to_destination() {
// Re-enable stealthChop if used. Disable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING)
end_sensorless_homing_per_axis(axis, stealth_states);
sensorless_axis_homing_end(axis, stealth_states);
#if SENSORLESS_STALLGUARD_DELAY
safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle
#endif
#endif
}
}
} // is_home_dir
} // do_homing_move
/**
* Set an axis to be unhomed. (Unless we are on a machine - e.g. a cheap Chinese CNC machine -
@ -2333,7 +2360,7 @@ void Motion::prepare_line_to_destination() {
* phase position. Trinamic drivers use a stepper phase table with 1024 values
* spanning 4 full steps with 256 positions each (ergo, 1024 positions).
*/
void backout_to_tmc_homing_phase(const AxisEnum axis) {
void Motion::backout_to_tmc_homing_phase(const AxisEnum axis) {
const xyz_long_t home_phase = TMC_HOME_PHASE;
// check if home phase is disabled for this axis.
@ -2477,7 +2504,7 @@ void Motion::prepare_line_to_destination() {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM(">>> homeaxis(", C(AXIS_CHAR(axis)), ")");
const int axis_home_dir = TERN0(DUAL_X_CARRIAGE, axis == X_AXIS)
? TOOL_X_HOME_DIR(extruder) : home_dir(axis);
? tool_x_home_dir() : home_dir(axis);
//
// Homing Z with a probe? Raise Z (maybe) and deploy the Z probe.
@ -2556,7 +2583,7 @@ void Motion::prepare_line_to_destination() {
//
// Fast move towards endstop until triggered
//
const float move_length = 1.5f * max_length(TERN(DELTA, Z_AXIS, axis)) * axis_home_dir;
const float move_length = 1.5f * max_axis_length(TERN(DELTA, Z_AXIS, axis)) * axis_home_dir;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Home Fast: ", move_length, "mm");
do_homing_move(axis, move_length, 0.0, !use_probe_bump);
@ -2843,7 +2870,7 @@ void Motion::set_axis_is_at_home(const AxisEnum axis) {
set_axis_homed(axis);
#if ENABLED(DUAL_X_CARRIAGE)
if (axis == X_AXIS && (extruder == 1 || dual_x_carriage_mode == DXC_DUPLICATION_MODE)) {
if (axis == X_AXIS && (extruder == 1 || idex_mode == DXC_DUPLICATION_MODE)) {
position.x = SUM_TERN(HAS_HOME_OFFSET, x_home_pos(extruder), home_offset.x);
return;
}

View file

@ -68,6 +68,22 @@ constexpr float fslop = 0.0001;
};
#endif
/**
* Dual X Carriage
*/
#if ENABLED(DUAL_X_CARRIAGE)
enum DualXMode : char {
DXC_FULL_CONTROL_MODE,
DXC_AUTO_PARK_MODE,
DXC_DUPLICATION_MODE,
DXC_MIRRORED_MODE
};
#endif
#if USE_SENSORLESS
struct sensorless_t;
#endif
/**
* Homing and Trusted Axes
*/
@ -78,6 +94,13 @@ class Motion {
public:
static bool relative_mode; // Relative Mode - G90/G91
// Flags for rotational axes
static constexpr AxisFlags rotational{0 LOGICAL_AXIS_GANG(
| 0, | 0, | 0, | 0,
| (ENABLED(AXIS4_ROTATES)<<I_AXIS), | (ENABLED(AXIS5_ROTATES)<<J_AXIS), | (ENABLED(AXIS6_ROTATES)<<K_AXIS),
| (ENABLED(AXIS7_ROTATES)<<U_AXIS), | (ENABLED(AXIS8_ROTATES)<<V_AXIS), | (ENABLED(AXIS9_ROTATES)<<W_AXIS))
};
#if HAS_MULTI_EXTRUDER
static uint8_t extruder; // Selected extruder (tool) - T<extruder>
#else
@ -102,6 +125,21 @@ public:
static abc_pos_t scara_home_offset; // A and B angular offsets, Z mm offset
#endif
#if HAS_HOTEND_OFFSET
static xyz_pos_t hotend_offset[HOTENDS];
static void reset_hotend_offsets();
#elif HOTENDS
static constexpr xyz_pos_t hotend_offset[HOTENDS] = { { TERN_(HAS_X_AXIS, 0) } };
#else
static constexpr xyz_pos_t hotend_offset[1] = { { TERN_(HAS_X_AXIS, 0) } };
#endif
#if HAS_HOTEND_OFFSET
static xyz_pos_t& active_hotend_offset() { return hotend_offset[extruder]; }
#else
static const xyz_pos_t& active_hotend_offset() { return hotend_offset[extruder]; }
#endif
#if ENABLED(LCD_SHOW_E_TOTAL)
static float e_move_accumulator;
#endif
@ -175,6 +213,93 @@ public:
static void set_home_offset(const AxisEnum axis, const float v) { home_offset[axis] = v; }
#endif
#if HAS_DUPLICATION_MODE
static bool extruder_duplication; // Used in Dual X mode 2
static void _set_duplication_enabled(const bool dupe) { extruder_duplication = dupe; }
#endif
#if ENABLED(DUAL_X_CARRIAGE)
static DualXMode idex_mode;
static bool idex_is_duplicating() { return idex_mode >= DXC_DUPLICATION_MODE; }
static float inactive_extruder_x, // Used in mode 0 & 1
duplicate_extruder_x_offset; // Used in mode 2 & 3
static bool active_extruder_parked; // Used in mode 1, 2 & 3
static millis_t delayed_move_time; // Used in mode 1
static celsius_t duplicate_extruder_temp_offset; // Used in mode 2 & 3
static bool idex_mirrored_mode; // Used in mode 3
static void idex_set_mirrored_mode(const bool mirr);
static float x_home_pos(const uint8_t tool) {
if (tool == 0) return X_HOME_POS;
/**
* In dual carriage mode the extruder offset provides an override of the
* second X-carriage position when homed - otherwise X2_HOME_POS is used.
* This allows soft recalibration of the second extruder home position
* (with M218 T1 Xn) without firmware reflash.
*/
return hotend_offset[1].x > 0 ? hotend_offset[1].x : X2_HOME_POS;
}
static void idex_set_parked(const bool park=true);
static bool unpark_before_move();
static void set_extruder_duplication(const bool dupe, const int8_t tool_index=-1);
static void idex_home_x();
#else
static bool unpark_before_move() { return false; }
#if ENABLED(MULTI_NOZZLE_DUPLICATION)
static uint8_t duplication_e_mask;
static void set_extruder_duplication(const bool dupe) { _set_duplication_enabled(dupe); }
#endif
#endif
//
// Probing
//
#if HAS_VARIABLE_XY_PROBE_FEEDRATE
static feedRate_t xy_probe_feedrate_mm_s; // Set with 'G29 S' for ABL LINEAR/BILINEAR. TODO: Store to EEPROM.
#endif
#ifdef Z_PROBE_FEEDRATE_SLOW
static constexpr feedRate_t z_probe_slow_mm_s = MMM_TO_MMS(Z_PROBE_FEEDRATE_SLOW);
#endif
#ifdef Z_PROBE_FEEDRATE_FAST
static constexpr feedRate_t z_probe_fast_mm_s = MMM_TO_MMS(Z_PROBE_FEEDRATE_FAST);
#endif
#ifdef __IMXRT1062__
#define DEFS_PROGMEM
#else
#define DEFS_PROGMEM PROGMEM
#endif
static float pgm_read_any(const float *p) { return TERN(__IMXRT1062__, *p, pgm_read_float(p)); }
static int8_t pgm_read_any(const int8_t *p) { return TERN(__IMXRT1062__, *p, pgm_read_byte(p)); }
#define XYZ_DEFS(T, NAME, OPT) \
static T NAME(const AxisEnum axis) { \
static constexpr XYZval<T> NAME##_P DEFS_PROGMEM = NUM_AXIS_ARRAY(X_##OPT, Y_##OPT, Z_##OPT, I_##OPT, J_##OPT, K_##OPT, U_##OPT, V_##OPT, W_##OPT); \
return pgm_read_any(&NAME##_P[axis]); \
}
XYZ_DEFS(float, base_min_pos, MIN_POS); // base_min_pos(axis)
XYZ_DEFS(float, base_max_pos, MAX_POS); // base_max_pos(axis)
XYZ_DEFS(float, base_home_pos, HOME_POS); // base_home_pos(axis)
XYZ_DEFS(float, max_axis_length, MAX_LENGTH); // max_axis_length(axis)
XYZ_DEFS(int8_t, home_dir, HOME_DIR); // home_dir(axis)
static float home_bump_mm(const AxisEnum axis) {
static const xyz_pos_t home_bump_mm_P DEFS_PROGMEM = HOMING_BUMP_MM;
return pgm_read_any(&home_bump_mm_P[axis]);
}
#if HAS_X_AXIS
static int8_t tool_x_home_dir(const uint8_t tool=extruder) {
UNUSED(tool);
return TERN(DUAL_X_CARRIAGE, tool ? 1 : -1, X_HOME_DIR);
}
#endif
//
// Workspace offsets
//
@ -251,6 +376,9 @@ public:
* Cleared whenever a stepper powers off, potentially losing its position.
*/
#if HAS_ENDSTOPS
#ifdef TMC_HOME_PHASE
static void backout_to_tmc_homing_phase(const AxisEnum axis);
#endif
static main_axes_bits_t axes_homed, axes_trusted;
static void homeaxis(const AxisEnum axis);
static void set_axis_never_homed(const AxisEnum axis);
@ -281,6 +409,8 @@ public:
static void home_if_needed(const bool keeplev=false);
static bool gcode_motion_ignored();
//
// Software Endstops
//
@ -503,7 +633,23 @@ public:
static void quickresume_stepper();
#endif // REALTIME_REPORTING_COMMANDS
//
// Trinamic Stepper Drivers
//
#if USE_SENSORLESS
static sensorless_t sensorless_axis_homing_start(const AxisEnum axis);
static void sensorless_axis_homing_end(const AxisEnum axis, sensorless_t enable_stealth);
#endif
#if HAS_HOMING_CURRENT
static void set_homing_current(const AxisEnum axis);
static void restore_homing_current(const AxisEnum axis);
#endif
private:
#if ENABLED(DUAL_X_CARRIAGE)
static xyz_pos_t raised_parked_position; // Used in mode 1
#endif
#if SECONDARY_AXES
static void secondary_axis_moves(SECONDARY_AXIS_ARGS_LC(const float), const feedRate_t fr_mm_s);
#endif
@ -519,137 +665,20 @@ private:
}; // class Motion
// Determine XY_PROBE_FEEDRATE_MM_S - The feedrate used between Probe Points
#if ABL_USES_GRID
// Specify read-only XY_PROBE_FEEDRATE_MM_S, feed rate between Probe Points.
#if HAS_VARIABLE_XY_PROBE_FEEDRATE
// ABL LINEAR and BILINEAR use 'G29 S' value, or MMM_TO_MMS(XY_PROBE_FEEDRATE)
extern feedRate_t xy_probe_feedrate_mm_s;
#define XY_PROBE_FEEDRATE_MM_S xy_probe_feedrate_mm_s
#define XY_PROBE_FEEDRATE_MM_S motion.xy_probe_feedrate_mm_s
#elif defined(XY_PROBE_FEEDRATE)
// Probe feedrate can be hard-coded by configuration
#define XY_PROBE_FEEDRATE_MM_S MMM_TO_MMS(XY_PROBE_FEEDRATE)
#else
// Defer to Planner XY max feedrate, or 60 mm/s
#define XY_PROBE_FEEDRATE_MM_S PLANNER_XY_FEEDRATE_MM_S
#endif
#ifdef Z_PROBE_FEEDRATE_SLOW
constexpr feedRate_t z_probe_slow_mm_s = MMM_TO_MMS(Z_PROBE_FEEDRATE_SLOW);
#endif
#ifdef Z_PROBE_FEEDRATE_FAST
constexpr feedRate_t z_probe_fast_mm_s = MMM_TO_MMS(Z_PROBE_FEEDRATE_FAST);
#endif
#ifdef __IMXRT1062__
#define DEFS_PROGMEM
#else
#define DEFS_PROGMEM PROGMEM
#endif
inline float pgm_read_any(const float *p) { return TERN(__IMXRT1062__, *p, pgm_read_float(p)); }
inline int8_t pgm_read_any(const int8_t *p) { return TERN(__IMXRT1062__, *p, pgm_read_byte(p)); }
#define XYZ_DEFS(T, NAME, OPT) \
inline T NAME(const AxisEnum axis) { \
static constexpr XYZval<T> NAME##_P DEFS_PROGMEM = NUM_AXIS_ARRAY(X_##OPT, Y_##OPT, Z_##OPT, I_##OPT, J_##OPT, K_##OPT, U_##OPT, V_##OPT, W_##OPT); \
return pgm_read_any(&NAME##_P[axis]); \
}
XYZ_DEFS(float, base_min_pos, MIN_POS); // base_min_pos(axis)
XYZ_DEFS(float, base_max_pos, MAX_POS); // base_max_pos(axis)
XYZ_DEFS(float, base_home_pos, HOME_POS); // base_home_pos(axis)
XYZ_DEFS(float, max_length, MAX_LENGTH); // max_length(axis)
XYZ_DEFS(int8_t, home_dir, HOME_DIR); // home_dir(axis)
// Flags for rotational axes
constexpr AxisFlags rotational{0 LOGICAL_AXIS_GANG(
| 0, | 0, | 0, | 0,
| (ENABLED(AXIS4_ROTATES)<<I_AXIS), | (ENABLED(AXIS5_ROTATES)<<J_AXIS), | (ENABLED(AXIS6_ROTATES)<<K_AXIS),
| (ENABLED(AXIS7_ROTATES)<<U_AXIS), | (ENABLED(AXIS8_ROTATES)<<V_AXIS), | (ENABLED(AXIS9_ROTATES)<<W_AXIS))
};
inline float home_bump_mm(const AxisEnum axis) {
static const xyz_pos_t home_bump_mm_P DEFS_PROGMEM = HOMING_BUMP_MM;
return pgm_read_any(&home_bump_mm_P[axis]);
}
#if HAS_HOTEND_OFFSET
extern xyz_pos_t hotend_offset[HOTENDS];
void reset_hotend_offsets();
#elif HOTENDS
constexpr xyz_pos_t hotend_offset[HOTENDS] = { { TERN_(HAS_X_AXIS, 0) } };
#else
constexpr xyz_pos_t hotend_offset[1] = { { TERN_(HAS_X_AXIS, 0) } };
#endif
#if ENABLED(NO_MOTION_BEFORE_HOMING)
#define MOTION_CONDITIONS (marlin.isRunning() && !motion.homing_needed_error())
#else
#define MOTION_CONDITIONS marlin.isRunning()
#endif
#define BABYSTEP_ALLOWED() ((ENABLED(BABYSTEP_WITHOUT_HOMING) || motion.all_axes_trusted()) && (ENABLED(BABYSTEP_ALWAYS_AVAILABLE) || marlin.printer_busy()))
/**
* Duplication mode
*/
#if HAS_DUPLICATION_MODE
extern bool extruder_duplication_enabled; // Used in Dual X mode 2
#endif
/**
* Dual X Carriage
*/
#if ENABLED(DUAL_X_CARRIAGE)
enum DualXMode : char {
DXC_FULL_CONTROL_MODE,
DXC_AUTO_PARK_MODE,
DXC_DUPLICATION_MODE,
DXC_MIRRORED_MODE
};
extern DualXMode dual_x_carriage_mode;
extern float inactive_extruder_x, // Used in mode 0 & 1
duplicate_extruder_x_offset; // Used in mode 2 & 3
extern xyz_pos_t raised_parked_position; // Used in mode 1
extern bool active_extruder_parked; // Used in mode 1, 2 & 3
extern millis_t delayed_move_time; // Used in mode 1
extern celsius_t duplicate_extruder_temp_offset; // Used in mode 2 & 3
extern bool idex_mirrored_mode; // Used in mode 3
FORCE_INLINE bool idex_is_duplicating() { return dual_x_carriage_mode >= DXC_DUPLICATION_MODE; }
float x_home_pos(const uint8_t extruder);
#define TOOL_X_HOME_DIR(T) ((T) ? 1 : -1)
void set_duplication_enabled(const bool dupe, const int8_t tool_index=-1);
void idex_set_mirrored_mode(const bool mirr);
void idex_set_parked(const bool park=true);
#else
#if ENABLED(MULTI_NOZZLE_DUPLICATION)
extern uint8_t duplication_e_mask;
enum DualXMode : char { DXC_DUPLICATION_MODE = 2 };
FORCE_INLINE void set_duplication_enabled(const bool dupe) { extruder_duplication_enabled = dupe; }
#endif
#define TOOL_X_HOME_DIR(T) X_HOME_DIR
#endif
//
// Trinamic Stepper Drivers
//
#if USE_SENSORLESS
struct sensorless_t;
sensorless_t start_sensorless_homing_per_axis(const AxisEnum axis);
void end_sensorless_homing_per_axis(const AxisEnum axis, sensorless_t enable_stealth);
#endif
#if HAS_HOMING_CURRENT
void set_homing_current(const AxisEnum axis);
void restore_homing_current(const AxisEnum axis);
#endif
extern Motion motion;
// External conversion methods (motion.h)

View file

@ -2151,7 +2151,7 @@ bool Planner::_populate_block(
#define E_STEPPER_INDEX(E) TERN(HAS_SWITCHING_EXTRUDER, (E) / 2, E)
// Enable all (i.e., both) E steppers for IDEX-style duplication, but only active E steppers for multi-nozzle (i.e., single wide X carriage) duplication
#define _IS_DUPE(N) TERN0(HAS_DUPLICATION_MODE, (extruder_duplication_enabled && TERN1(MULTI_NOZZLE_DUPLICATION, TEST(duplication_e_mask, N))))
#define _IS_DUPE(N) TERN0(HAS_DUPLICATION_MODE, (motion.extruder_duplication && TERN1(MULTI_NOZZLE_DUPLICATION, TEST(motion.duplication_e_mask, N))))
#define ENABLE_ONE_E(N) do{ \
if (N == E_STEPPER_INDEX(extruder) || _IS_DUPE(N)) { /* N is 'extruder', or N is duplicating */ \

View file

@ -552,7 +552,7 @@ bool Probe::set_deployed(const bool deploy, const bool no_return/*=false*/) {
#endif
if (z_raise_wanted) {
const float zdest = DIFF_TERN(HAS_HOTEND_OFFSET, Z_CLEARANCE_DEPLOY_PROBE, hotend_offset[motion.extruder].z);
const float zdest = DIFF_TERN(HAS_HOTEND_OFFSET, Z_CLEARANCE_DEPLOY_PROBE, motion.active_hotend_offset().z);
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Raise Z to ", zdest);
motion.do_z_clearance(zdest);
}
@ -780,7 +780,7 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) {
float Probe::run_z_probe(const bool sanity_check/*=true*/, const float z_min_point/*=Z_PROBE_LOW_POINT*/, const float z_clearance/*=Z_TWEEN_SAFE_CLEARANCE*/) {
DEBUG_SECTION(log_probe, "Probe::run_z_probe", DEBUGGING(LEVELING));
const float zoffs = SUM_TERN(HAS_HOTEND_OFFSET, -offset.z, hotend_offset[motion.extruder].z);
const float zoffs = SUM_TERN(HAS_HOTEND_OFFSET, -offset.z, motion.active_hotend_offset().z);
auto try_to_probe = [&](PGM_P const plbl, const float z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck) -> bool {
constexpr float error_tolerance = Z_PROBE_ERROR_TOLERANCE;
@ -819,7 +819,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/, const float z_min_poi
if (TERN0(PROBE_TARE, tare())) return NAN;
// Do a first probe at the fast speed
if (try_to_probe(PSTR("FAST"), z_probe_low_point, z_probe_fast_mm_s, sanity_check)) return NAN;
if (try_to_probe(PSTR("FAST"), z_probe_low_point, motion.z_probe_fast_mm_s, sanity_check)) return NAN;
const float z1 = DIFF_TERN(HAS_DELTA_SENSORLESS_PROBING, motion.position.z, largest_sensorless_adj);
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("1st Probe Z:", z1);
@ -834,7 +834,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/, const float z_min_poi
const float z = (Z_CLEARANCE_DEPLOY_PROBE) + 5.0f + _MAX(zoffs, 0.0f);
if (motion.position.z > z) {
// Probe down fast. If the probe never triggered, raise for probe clearance
if (!probe_down_to_z(z, z_probe_fast_mm_s))
if (!probe_down_to_z(z, motion.z_probe_fast_mm_s))
motion.do_z_clearance(z_clearance);
}
#endif
@ -859,7 +859,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/, const float z_min_poi
// Probe downward slowly to find the bed
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Slow Probe:");
if (try_to_probe(PSTR("SLOW"), z_probe_low_point, z_probe_slow_mm_s, sanity_check)) return NAN;
if (try_to_probe(PSTR("SLOW"), z_probe_low_point, motion.z_probe_slow_mm_s, sanity_check)) return NAN;
TERN_(MEASURE_BACKLASH_WHEN_PROBING, backlash.measure_with_probe());
@ -928,7 +928,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/, const float z_min_poi
#endif
return DIFF_TERN(HAS_HOTEND_OFFSET, measured_z, hotend_offset[motion.extruder].z);
return DIFF_TERN(HAS_HOTEND_OFFSET, measured_z, motion.active_hotend_offset().z);
}
#if DO_TOOLCHANGE_FOR_PROBING
@ -1007,7 +1007,7 @@ float Probe::probe_at_point(
if (DEBUGGING(LEVELING)) DEBUG_ECHOPGM("Move to probe");
if (probe_relative) { // Get the nozzle position, adjust for active hotend if not 0
if (DEBUGGING(LEVELING)) DEBUG_ECHOPGM("-relative");
npos -= DIFF_TERN(HAS_HOTEND_OFFSET, offset_xy, xy_pos_t(hotend_offset[motion.extruder]));
npos -= DIFF_TERN(HAS_HOTEND_OFFSET, offset_xy, motion.active_hotend_offset());
}
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM(" point");
@ -1015,7 +1015,7 @@ float Probe::probe_at_point(
motion.blocking_move(npos, feedRate_t(XY_PROBE_FEEDRATE_MM_S));
// Change Z motor current to homing current
TERN_(PROBING_USE_CURRENT_HOME, set_homing_current(Z_AXIS));
TERN_(PROBING_USE_CURRENT_HOME, motion.set_homing_current(Z_AXIS));
float measured_z;
@ -1075,7 +1075,7 @@ float Probe::probe_at_point(
#endif // !BD_SENSOR
// Restore the Z homing current
TERN_(PROBING_USE_CURRENT_HOME, restore_homing_current(Z_AXIS));
TERN_(PROBING_USE_CURRENT_HOME, motion.restore_homing_current(Z_AXIS));
return measured_z;
}

View file

@ -305,21 +305,21 @@ float segments_per_second = DEFAULT_SEGMENTS_PER_SECOND;
// Disable stealthChop if used. Enable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING)
#if X_SENSORLESS
sensorless_t stealth_states_x = start_sensorless_homing_per_axis(X_AXIS);
sensorless_t stealth_states_x = motion.sensorless_axis_homing_start(X_AXIS);
#endif
#if Y_SENSORLESS
sensorless_t stealth_states_y = start_sensorless_homing_per_axis(Y_AXIS);
sensorless_t stealth_states_y = motion.sensorless_axis_homing_start(Y_AXIS);
#endif
#if Z_SENSORLESS
sensorless_t stealth_states_z = start_sensorless_homing_per_axis(Z_AXIS);
sensorless_t stealth_states_z = motion.sensorless_axis_homing_start(Z_AXIS);
#endif
#endif
// Set the homing current for all motors
TERN_(HAS_HOMING_CURRENT, set_homing_current(Z_AXIS));
TERN_(HAS_HOMING_CURRENT, motion.set_homing_current(Z_AXIS));
// Move to home, should move Z, Y, then X. Move X to near 0 (to avoid div by zero
// and sign/angle stability around 0 for trigonometric functions), Y to 0 and Z to max_length
// and sign/angle stability around 0 for trigonometric functions), Y to 0 and Z to Z_MAX_LENGTH
constexpr xyz_pos_t homing_pos_dir = apply_T_W_offset(xyz_pos_t({ 1, 0, Z_MAX_LENGTH }));
motion.position.set(homing_pos_dir.x, homing_pos_dir.y, homing_pos_dir.z);
@ -327,13 +327,13 @@ float segments_per_second = DEFAULT_SEGMENTS_PER_SECOND;
planner.synchronize();
// Restore the homing current for all motors
TERN_(HAS_HOMING_CURRENT, restore_homing_current(Z_AXIS));
TERN_(HAS_HOMING_CURRENT, motion.restore_homing_current(Z_AXIS));
// Re-enable stealthChop if used. Disable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING)
TERN_(X_SENSORLESS, end_sensorless_homing_per_axis(X_AXIS, stealth_states_x));
TERN_(Y_SENSORLESS, end_sensorless_homing_per_axis(Y_AXIS, stealth_states_y));
TERN_(Z_SENSORLESS, end_sensorless_homing_per_axis(Z_AXIS, stealth_states_z));
TERN_(X_SENSORLESS, motion.sensorless_axis_homing_end(X_AXIS, stealth_states_x));
TERN_(Y_SENSORLESS, motion.sensorless_axis_homing_end(Y_AXIS, stealth_states_y));
TERN_(Z_SENSORLESS, motion.sensorless_axis_homing_end(Z_AXIS, stealth_states_z));
#endif
endstops.validate_homing_move();

View file

@ -960,7 +960,7 @@ void MarlinSettings::postprocess() {
#if HAS_HOTEND_OFFSET
// Skip hotend 0 which must be 0
for (uint8_t e = 1; e < HOTENDS; ++e)
EEPROM_WRITE(hotend_offset[e]);
EEPROM_WRITE(motion.hotend_offset[e]);
#endif
}
@ -2031,7 +2031,7 @@ void MarlinSettings::postprocess() {
#if HAS_HOTEND_OFFSET
// Skip hotend 0 which must be 0
for (uint8_t e = 1; e < HOTENDS; ++e)
EEPROM_READ(hotend_offset[e]);
EEPROM_READ(motion.hotend_offset[e]);
#endif
}
@ -3373,7 +3373,7 @@ void MarlinSettings::reset() {
//
// Hotend Offsets
//
TERN_(HAS_HOTEND_OFFSET, reset_hotend_offsets());
TERN_(HAS_HOTEND_OFFSET, motion.reset_hotend_offsets());
//
// Spindle Acceleration

View file

@ -436,11 +436,11 @@ xyze_int8_t Stepper::count_direction{0};
#endif
#elif ENABLED(DUAL_X_CARRIAGE)
#define X_APPLY_DIR(FWD,ALWAYS) do{ \
if (extruder_duplication_enabled || ALWAYS) { X_DIR_WRITE(FWD); X2_DIR_WRITE((FWD) ^ idex_mirrored_mode); } \
if (motion.extruder_duplication || ALWAYS) { X_DIR_WRITE(FWD); X2_DIR_WRITE((FWD) ^ motion.idex_mirrored_mode); } \
else if (last_moved_extruder) X2_DIR_WRITE(FWD); else X_DIR_WRITE(FWD); \
}while(0)
#define X_APPLY_STEP(STATE,ALWAYS) do{ \
if (extruder_duplication_enabled || ALWAYS) { X_STEP_WRITE(STATE); X2_STEP_WRITE(STATE); } \
if (motion.extruder_duplication || ALWAYS) { X_STEP_WRITE(STATE); X2_STEP_WRITE(STATE); } \
else if (last_moved_extruder) X2_STEP_WRITE(STATE); else X_STEP_WRITE(STATE); \
}while(0)
#elif HAS_X_AXIS

View file

@ -38,7 +38,7 @@
* E Special Cases
* - SINGLENOZZLE: All Extruders have a single nozzle so there is one heater and no XYZ offset.
* - Switching Extruder: One stepper is used for each pair of nozzles with a switching mechanism.
* - Duplication Mode: Two or more steppers move in sync when `extruder_duplication_enabled` is set.
* - Duplication Mode: Two or more steppers move in sync when `motion.extruder_duplication` is set.
* With MULTI_NOZZLE_DUPLICATION a `duplication_e_mask` is also used.
* - Průša MMU1: One stepper is used with a switching mechanism. Odd numbered E indexes are reversed.
* - Průša MMU2: One stepper is used with a switching mechanism.
@ -774,7 +774,7 @@ void reset_stepper_drivers(); // Called by settings.load / settings.reset
#if HAS_DUPLICATION_MODE
#if ENABLED(MULTI_NOZZLE_DUPLICATION)
#define DUPE(N,T,V) do{ if (TEST(duplication_e_mask, N)) E##N##_##T##_WRITE(V); }while(0);
#define DUPE(N,T,V) do{ if (TEST(motion.duplication_e_mask, N)) E##N##_##T##_WRITE(V); }while(0);
#else
#define DUPE(N,T,V) E##N##_##T##_WRITE(V);
#endif
@ -782,9 +782,9 @@ void reset_stepper_drivers(); // Called by settings.load / settings.reset
#define NDIR(N) DUPE(N,DIR,HIGH);
#define RDIR(N) DUPE(N,DIR,LOW );
#define E_STEP_WRITE(E,V) do{ if (extruder_duplication_enabled) { REPEAT2(E_STEPPERS, DUPE, STEP, V); } else _E_STEP_WRITE(E,V); }while(0)
#define FWD_E_DIR(E) do{ if (extruder_duplication_enabled) { REPEAT(E_STEPPERS, NDIR); } else _FWD_E_DIR(E); }while(0)
#define REV_E_DIR(E) do{ if (extruder_duplication_enabled) { REPEAT(E_STEPPERS, RDIR); } else _REV_E_DIR(E); }while(0)
#define E_STEP_WRITE(E,V) do{ if (motion.extruder_duplication) { REPEAT2(E_STEPPERS, DUPE, STEP, V); } else _E_STEP_WRITE(E,V); }while(0)
#define FWD_E_DIR(E) do{ if (motion.extruder_duplication) { REPEAT(E_STEPPERS, NDIR); } else _FWD_E_DIR(E); }while(0)
#define REV_E_DIR(E) do{ if (motion.extruder_duplication) { REPEAT(E_STEPPERS, RDIR); } else _REV_E_DIR(E); }while(0)
#else

View file

@ -171,7 +171,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
const float oldx = motion.position.x,
grabpos = mpe_settings.parking_xpos[new_tool] + (new_tool ? mpe_settings.grab_distance : -mpe_settings.grab_distance),
offsetcompensation = TERN0(HAS_HOTEND_OFFSET, hotend_offset[motion.extruder].x * mpe_settings.compensation_factor);
offsetcompensation = TERN0(HAS_HOTEND_OFFSET, motion.active_hotend_offset().x * mpe_settings.compensation_factor);
if (motion.homing_needed_error(_BV(X_AXIS))) return;
@ -295,7 +295,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
constexpr float parkingposx[] = PARKING_EXTRUDER_PARKING_X;
#if HAS_HOTEND_OFFSET
const float x_offset = hotend_offset[motion.extruder].x;
const float x_offset = motion.active_hotend_offset().x;
#else
constexpr float x_offset = 0;
#endif
@ -364,7 +364,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
// STEP 6
motion.position.x = DIFF_TERN(HAS_HOTEND_OFFSET, midpos, hotend_offset[new_tool].x);
motion.position.x = DIFF_TERN(HAS_HOTEND_OFFSET, midpos, motion.hotend_offset[new_tool].x);
DEBUG_SYNCHRONIZE();
DEBUG_POS("(6) Move midway between hotends", motion.position);
@ -732,7 +732,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
constexpr float toolheadposx[] = SWITCHING_TOOLHEAD_X_POS;
const float placexpos = toolheadposx[motion.extruder],
grabxpos = toolheadposx[new_tool];
const xyz_pos_t &hoffs = hotend_offset[motion.extruder];
const xyz_pos_t &hoffs = motion.active_hotend_offset();
/**
* 1. Raise Z-Axis to give enough clearance
@ -815,7 +815,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
// 9. Apply Z hotend offset to current position
DEBUG_POS("(9) Applying Z-offset", motion.position);
motion.position.z += hoffs.z - hotend_offset[new_tool].z;
motion.position.z += hoffs.z - motion.hotend_offset[new_tool].z;
DEBUG_POS("EMST Tool-Change done.", motion.position);
}
@ -842,7 +842,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
inline void dualx_tool_change(const uint8_t new_tool, bool &no_move) {
DEBUG_ECHOPGM("Dual X Carriage Mode ");
switch (dual_x_carriage_mode) {
switch (motion.idex_mode) {
case DXC_FULL_CONTROL_MODE: DEBUG_ECHOLNPGM("FULL_CONTROL"); break;
case DXC_AUTO_PARK_MODE: DEBUG_ECHOLNPGM("AUTO_PARK"); break;
case DXC_DUPLICATION_MODE: DEBUG_ECHOLNPGM("DUPLICATION"); break;
@ -850,13 +850,13 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
}
// Get the home position of the currently-active tool
const float xhome = x_home_pos(motion.extruder);
const float xhome = motion.x_home_pos(motion.extruder);
if (dual_x_carriage_mode == DXC_AUTO_PARK_MODE // If Auto-Park mode is enabled
if (motion.idex_mode == DXC_AUTO_PARK_MODE // If Auto-Park mode is enabled
&& marlin.isRunning() && !no_move // ...and movement is permitted
&& (delayed_move_time || motion.position.x != xhome) // ...and delayed_move_time is set OR not "already parked"...
&& (motion.delayed_move_time || motion.position.x != xhome) // ...and delayed_move_time is set OR not "already parked"...
) {
DEBUG_ECHOLNPGM("MoveX to ", xhome);
DEBUG_ECHOLNPGM("Move X to ", xhome);
motion.position.x = xhome;
motion.goto_current_position(planner.settings.max_feedrate_mm_s[X_AXIS]); // Park the current head
planner.synchronize();
@ -870,16 +870,16 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
DEBUG_POS("New Extruder", motion.position);
switch (dual_x_carriage_mode) {
switch (motion.idex_mode) {
case DXC_FULL_CONTROL_MODE:
// New current position is the position of the activated extruder
motion.position.x = inactive_extruder_x;
motion.position.x = motion.inactive_extruder_x;
// Save the inactive extruder's position (from the old motion.position)
inactive_extruder_x = motion.destination.x;
motion.inactive_extruder_x = motion.destination.x;
DEBUG_ECHOLNPGM("DXC Full Control curr.x=", motion.position.x, " dest.x=", motion.destination.x);
break;
case DXC_AUTO_PARK_MODE:
idex_set_parked();
motion.idex_set_parked();
break;
default:
break;
@ -888,7 +888,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
// Ensure X axis DIR pertains to the correct carriage
stepper.apply_directions();
DEBUG_ECHOLNPGM("Active extruder parked: ", active_extruder_parked ? "yes" : "no");
DEBUG_ECHOLNPGM("Active extruder parked: ", motion.active_extruder_parked ? "yes" : "no");
DEBUG_POS("New extruder (parked)", motion.position);
}
@ -1150,7 +1150,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
planner.synchronize();
#if ENABLED(DUAL_X_CARRIAGE) // Only T0 allowed if the Printer is in DXC_DUPLICATION_MODE or DXC_MIRRORED_MODE
if (new_tool != 0 && idex_is_duplicating())
if (new_tool != 0 && motion.idex_is_duplicating())
return invalid_extruder_error(new_tool);
#endif
@ -1164,12 +1164,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
TERN_(HAS_MARLINUI_MENU, if (!no_move) ui.update());
#if ENABLED(DUAL_X_CARRIAGE)
const bool idex_full_control = dual_x_carriage_mode == DXC_FULL_CONTROL_MODE;
#else
constexpr bool idex_full_control = false;
#endif
const bool idex_full_control = TERN0(DUAL_X_CARRIAGE, motion.idex_mode == DXC_FULL_CONTROL_MODE);
const uint8_t old_tool = motion.extruder;
const bool can_move_away = !no_move && !idex_full_control;
@ -1267,7 +1262,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
#endif
#if HAS_HOTEND_OFFSET
xyz_pos_t diff = hotend_offset[new_tool] - hotend_offset[old_tool];
xyz_pos_t diff = motion.hotend_offset[new_tool] - motion.hotend_offset[old_tool];
TERN_(DUAL_X_CARRIAGE, diff.x = 0);
#else
constexpr xyz_pos_t diff{0};
@ -1402,7 +1397,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
}
#endif
TERN_(DUAL_X_CARRIAGE, idex_set_parked(false));
TERN_(DUAL_X_CARRIAGE, motion.idex_set_parked(false));
} // should_move
#if HAS_SWITCHING_NOZZLE
@ -1446,7 +1441,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
xyz_pos_t old_workspace_offset;
if (new_tool > 0) {
old_workspace_offset = motion.workspace_offset;
const xyz_pos_t &he = hotend_offset[new_tool];
const xyz_pos_t &he = motion.hotend_offset[new_tool];
TERN_(TC_GCODE_USE_GLOBAL_X, motion.workspace_offset.x -= he.x);
TERN_(TC_GCODE_USE_GLOBAL_Y, motion.workspace_offset.y -= he.y);
TERN_(TC_GCODE_USE_GLOBAL_Z, motion.workspace_offset.z -= he.z);
@ -1489,7 +1484,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
// so that nozzle does not lower below print surface if new hotend Z offset is higher than old hotend Z offset.
#if ANY(MECHANICAL_SWITCHING_EXTRUDER, MECHANICAL_SWITCHING_NOZZLE)
#if HAS_HOTEND_OFFSET
xyz_pos_t diff = hotend_offset[new_tool] - hotend_offset[old_tool];
xyz_pos_t diff = motion.hotend_offset[new_tool] - motion.hotend_offset[old_tool];
TERN_(DUAL_X_CARRIAGE, diff.x = 0);
#else
constexpr xyz_pos_t diff{0};
@ -1509,7 +1504,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
#endif
#ifdef EVENT_GCODE_AFTER_TOOLCHANGE
if (TERN1(DUAL_X_CARRIAGE, dual_x_carriage_mode == DXC_AUTO_PARK_MODE))
if (TERN1(DUAL_X_CARRIAGE, motion.idex_mode == DXC_AUTO_PARK_MODE))
gcode.process_subcommands_now(F(EVENT_GCODE_AFTER_TOOLCHANGE));
#endif

View file

@ -475,10 +475,10 @@ uint8_t UHS_NI UHS_Bulk_Storage::Start() {
if(!rcode) {
if(!UHS_SLEEP_MS(3)) goto FailUnPlug;
BS_HOST_DEBUG("CheckLUN...\r\n");
BS_HOST_DEBUG("%lu\r\n", millis()/1000);
BS_HOST_DEBUG("%" PRIu32 "\r\n", millis()/1000);
// Stalls on ***some*** devices, ***WHY***?! Device SAID it is READY!!
LUNOk[lun] = CheckLUN(lun);
BS_HOST_DEBUG("%lu\r\n", millis()/1000);
BS_HOST_DEBUG("%" PRIu32 "\r\n", millis()/1000);
if(!LUNOk[lun]) LUNOk[lun] = CheckLUN(lun);
if(!UHS_SLEEP_MS(1)) goto FailUnPlug;
BS_HOST_DEBUG("Checked LUN...\r\n");

View file

@ -331,7 +331,7 @@ int16_t UHS_NI MAX3421E_HOST::Init(int16_t mseconds) {
again:
MAX3421E_SPI_Settings = SPISettings(spd, MSBFIRST, SPI_MODE0);
if (reset() == 0) {
MAX_HOST_DEBUG(PSTR("Fail SPI speed %lu\r\n"), spd);
MAX_HOST_DEBUG(PSTR("Fail SPI speed %" PRIu32 "\r\n"), spd);
if (spd > 1999999) {
spd -= 1000000;
goto again;
@ -346,7 +346,7 @@ int16_t UHS_NI MAX3421E_HOST::Init(int16_t mseconds) {
regWr(rGPINPOL, sample_wr);
sample_rd = regRd(rGPINPOL);
if (sample_rd != sample_wr) {
MAX_HOST_DEBUG(PSTR("Fail SPI speed %lu\r\n"), spd);
MAX_HOST_DEBUG(PSTR("Fail SPI speed %" PRIu32 "\r\n"), spd);
if (spd > 1999999) {
spd -= 1000000;
goto again;
@ -358,7 +358,7 @@ int16_t UHS_NI MAX3421E_HOST::Init(int16_t mseconds) {
regWr(rGPINPOL, gpinpol_copy);
}
MAX_HOST_DEBUG(PSTR("Pass SPI speed %lu\r\n"), spd);
MAX_HOST_DEBUG(PSTR("Pass SPI speed %" PRIu32 "\r\n"), spd);
#endif
if (reset() == 0) { // OSCOKIRQ hasn't asserted in time

View file

@ -41,6 +41,6 @@ extra_scripts = ${common_stm32.extra_scripts}
#
[stm_flash_drive]
# Arduino_Core_STM32 uses usb-host-msc-cdc-msc-3 branch
platform_packages = framework-arduinoststm32@https://github.com/rhapsodyv/Arduino_Core_STM32/archive/39f37d6d6a.zip
platform_packages = framework-arduinoststm32@https://github.com/MarlinFirmware/Arduino_Core_STM32/archive/stm-flash-drive.zip
build_flags = -DHAL_PCD_MODULE_ENABLED -DHAL_HCD_MODULE_ENABLED
-DUSBHOST -DUSBH_IRQ_PRIO=3 -DUSBH_IRQ_SUBPRIO=4

View file

@ -25,7 +25,7 @@
[env:BTT_SKR_SE_BX]
extends = stm32_variant
# framework-arduinoststm32 uses biqu-bx-workaround branch
platform_packages = framework-arduinoststm32@https://github.com/thisiskeithb/Arduino_Core_STM32/archive/8b3522051a.zip
platform_packages = framework-arduinoststm32@https://github.com/MarlinFirmware/Arduino_Core_STM32/archive/biqu-bx.zip
board = marlin_BTT_SKR_SE_BX
board_build.offset = 0x20000
build_flags = ${stm32_variant.build_flags} ${stm_flash_drive.build_flags}