diff --git a/Marlin/Conditionals_LCD.h b/Marlin/Conditionals_LCD.h index 13dbf8a00..841838502 100644 --- a/Marlin/Conditionals_LCD.h +++ b/Marlin/Conditionals_LCD.h @@ -286,7 +286,7 @@ * HOTENDS - Number of hotends, whether connected or separate * E_STEPPERS - Number of actual E stepper motors * TOOL_E_INDEX - Index to use when getting/setting the tool state - * + * */ #if ENABLED(SINGLENOZZLE) // One hotend, multi-extruder #define HOTENDS 1 @@ -316,6 +316,18 @@ #define TOOL_E_INDEX current_block->active_extruder #endif + /** + * Distinct E Factors – Disable by commenting out DISTINCT_E_FACTORS + */ + #if ENABLED(DISTINCT_E_FACTORS) && E_STEPPERS > 1 + #define XYZE_N (XYZ + E_STEPPERS) + #define E_AXIS_N (E_AXIS + extruder) + #else + #undef DISTINCT_E_FACTORS + #define XYZE_N XYZE + #define E_AXIS_N E_AXIS + #endif + /** * The BLTouch Probe emulates a servo probe * and uses "special" angles for its state. diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index e616fa3fd..5565afb3b 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -145,6 +145,9 @@ // :[1, 2, 3, 4] #define EXTRUDERS 1 +// Enable if your E steppers or extruder gear ratios are not identical +//#define DISTINCT_E_FACTORS + // For Cyclops or any "multi-extruder" that shares a single nozzle. //#define SINGLENOZZLE @@ -472,26 +475,31 @@ * * These settings can be reset by M502 * + * You can set distinct factors for each E stepper, if needed. + * If fewer factors are given, the last will apply to the rest. + * * Note that if EEPROM is enabled, saved values will override these. */ /** * Default Axis Steps Per Unit (steps/mm) * Override with M92 + * X, Y, Z, E0 [, E1[, E2[, E3]]] */ #define DEFAULT_AXIS_STEPS_PER_UNIT { 80, 80, 4000, 500 } /** * Default Max Feed Rate (mm/s) * Override with M203 + * X, Y, Z, E0 [, E1[, E2[, E3]]] */ #define DEFAULT_MAX_FEEDRATE { 300, 300, 5, 25 } /** * Default Max Acceleration (change/s) change = mm/s + * (Maximum start speed for accelerated moves) * Override with M201 - * - * Maximum start speed for accelerated moves: { X, Y, Z, E } + * X, Y, Z, E0 [, E1[, E2[, E3]]] */ #define DEFAULT_MAX_ACCELERATION { 3000, 3000, 100, 10000 } diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index c02c02c74..8a9234e12 100755 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -1227,7 +1227,7 @@ inline bool code_value_bool() { return !code_has_value() || code_value_byte() > } inline float axis_unit_factor(int axis) { - return (axis == E_AXIS && volumetric_enabled ? volumetric_unit_factor : linear_unit_factor); + return (axis >= E_AXIS && volumetric_enabled ? volumetric_unit_factor : linear_unit_factor); } inline float code_value_linear_units() { return code_value_float() * linear_unit_factor; } @@ -5772,22 +5772,38 @@ inline void gcode_M85() { if (code_seen('S')) max_inactive_time = code_value_millis_from_seconds(); } +/** + * Multi-stepper support for M92, M201, M203 + */ +#if ENABLED(DISTINCT_E_FACTORS) + #define GET_TARGET_EXTRUDER(CMD) if (get_target_extruder_from_command(CMD)) return + #define TARGET_EXTRUDER target_extruder +#else + #define GET_TARGET_EXTRUDER(CMD) NOOP + #define TARGET_EXTRUDER 0 +#endif + /** * M92: Set axis steps-per-unit for one or more axes, X, Y, Z, and E. * (Follows the same syntax as G92) + * + * With multiple extruders use T to specify which one. */ inline void gcode_M92() { + + GET_TARGET_EXTRUDER(92); + LOOP_XYZE(i) { if (code_seen(axis_codes[i])) { if (i == E_AXIS) { - float value = code_value_per_axis_unit(i); + float value = code_value_per_axis_unit(E_AXIS + TARGET_EXTRUDER); if (value < 20.0) { - float factor = planner.axis_steps_per_mm[i] / value; // increase e constants if M92 E14 is given for netfab. + float factor = planner.axis_steps_per_mm[E_AXIS + TARGET_EXTRUDER] / value; // increase e constants if M92 E14 is given for netfab. planner.max_jerk[E_AXIS] *= factor; - planner.max_feedrate_mm_s[E_AXIS] *= factor; - planner.max_acceleration_steps_per_s2[E_AXIS] *= factor; + planner.max_feedrate_mm_s[E_AXIS + TARGET_EXTRUDER] *= factor; + planner.max_acceleration_steps_per_s2[E_AXIS + TARGET_EXTRUDER] *= factor; } - planner.axis_steps_per_mm[E_AXIS] = value; + planner.axis_steps_per_mm[E_AXIS + TARGET_EXTRUDER] = value; } else { planner.axis_steps_per_mm[i] = code_value_per_axis_unit(i); @@ -6038,11 +6054,17 @@ inline void gcode_M200() { /** * M201: Set max acceleration in units/s^2 for print moves (M201 X1000 Y1000) + * + * With multiple extruders use T to specify which one. */ inline void gcode_M201() { + + GET_TARGET_EXTRUDER(201); + LOOP_XYZE(i) { if (code_seen(axis_codes[i])) { - planner.max_acceleration_mm_per_s2[i] = code_value_axis_units(i); + const uint8_t a = i + (i == E_AXIS ? TARGET_EXTRUDER : 0); + planner.max_acceleration_mm_per_s2[a] = code_value_axis_units(a); } } // steps per sq second need to be updated to agree with the units per sq second (as they are what is used in the planner) @@ -6060,11 +6082,18 @@ inline void gcode_M201() { /** * M203: Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in units/sec + * + * With multiple extruders use T to specify which one. */ inline void gcode_M203() { + + GET_TARGET_EXTRUDER(203); + LOOP_XYZE(i) - if (code_seen(axis_codes[i])) - planner.max_feedrate_mm_s[i] = code_value_axis_units(i); + if (code_seen(axis_codes[i])) { + const uint8_t a = i + (i == E_AXIS ? TARGET_EXTRUDER : 0); + planner.max_feedrate_mm_s[a] = code_value_axis_units(a); + } } /** diff --git a/Marlin/configuration_store.cpp b/Marlin/configuration_store.cpp index f1f898c4a..a3ae225b2 100644 --- a/Marlin/configuration_store.cpp +++ b/Marlin/configuration_store.cpp @@ -47,9 +47,9 @@ * 100 Version (char x4) * 104 EEPROM Checksum (uint16_t) * - * 106 M92 XYZE planner.axis_steps_per_mm (float x4) - * 122 M203 XYZE planner.max_feedrate_mm_s (float x4) - * 138 M201 XYZE planner.max_acceleration_mm_per_s2 (uint32_t x4) + * 106 M92 XYZE planner.axis_steps_per_mm (float x4 ... x7) + * 122 M203 XYZE planner.max_feedrate_mm_s (float x4 ... x7) + * 138 M201 XYZE planner.max_acceleration_mm_per_s2 (uint32_t x4 ... x7) * 154 M204 P planner.acceleration (float) * 158 M204 R planner.retract_acceleration (float) * 162 M204 T planner.travel_acceleration (float) @@ -571,10 +571,10 @@ void Config_Postprocess() { void Config_ResetDefault() { const float tmp1[] = DEFAULT_AXIS_STEPS_PER_UNIT, tmp2[] = DEFAULT_MAX_FEEDRATE; const long tmp3[] = DEFAULT_MAX_ACCELERATION; - LOOP_XYZE(i) { - planner.axis_steps_per_mm[i] = tmp1[i]; - planner.max_feedrate_mm_s[i] = tmp2[i]; - planner.max_acceleration_mm_per_s2[i] = tmp3[i]; + LOOP_XYZE_N(i) { + planner.axis_steps_per_mm[i] = tmp1[i < COUNT(tmp1) ? i : COUNT(tmp1) - 1]; + planner.max_feedrate_mm_s[i] = tmp2[i < COUNT(tmp2) ? i : COUNT(tmp2) - 1]; + planner.max_acceleration_mm_per_s2[i] = tmp3[i < COUNT(tmp3) ? i : COUNT(tmp3) - 1]; } planner.acceleration = DEFAULT_ACCELERATION; @@ -719,8 +719,16 @@ void Config_ResetDefault() { SERIAL_ECHOPAIR(" M92 X", planner.axis_steps_per_mm[X_AXIS]); SERIAL_ECHOPAIR(" Y", planner.axis_steps_per_mm[Y_AXIS]); SERIAL_ECHOPAIR(" Z", planner.axis_steps_per_mm[Z_AXIS]); - SERIAL_ECHOPAIR(" E", planner.axis_steps_per_mm[E_AXIS]); + #if E_STEPPERS == 1 + SERIAL_ECHOPAIR(" E", planner.axis_steps_per_mm[E_AXIS]); + #endif SERIAL_EOL; + #if ENABLED(DISTINCT_E_FACTORS) + for (uint8_t i = 0; i < E_STEPPERS; i++) { + SERIAL_ECHOPAIR(" M92 T", (int)i); + SERIAL_ECHOLNPAIR(" E", planner.axis_steps_per_mm[E_AXIS + i]); + } + #endif CONFIG_ECHO_START; @@ -731,8 +739,16 @@ void Config_ResetDefault() { SERIAL_ECHOPAIR(" M203 X", planner.max_feedrate_mm_s[X_AXIS]); SERIAL_ECHOPAIR(" Y", planner.max_feedrate_mm_s[Y_AXIS]); SERIAL_ECHOPAIR(" Z", planner.max_feedrate_mm_s[Z_AXIS]); - SERIAL_ECHOPAIR(" E", planner.max_feedrate_mm_s[E_AXIS]); + #if E_STEPPERS == 1 + SERIAL_ECHOPAIR(" E", planner.max_feedrate_mm_s[E_AXIS]); + #endif SERIAL_EOL; + #if ENABLED(DISTINCT_E_FACTORS) + for (uint8_t i = 0; i < E_STEPPERS; i++) { + SERIAL_ECHOPAIR(" M203 T", (int)i); + SERIAL_ECHOLNPAIR(" E", planner.max_feedrate_mm_s[E_AXIS + i]); + } + #endif CONFIG_ECHO_START; if (!forReplay) { @@ -742,8 +758,17 @@ void Config_ResetDefault() { SERIAL_ECHOPAIR(" M201 X", planner.max_acceleration_mm_per_s2[X_AXIS]); SERIAL_ECHOPAIR(" Y", planner.max_acceleration_mm_per_s2[Y_AXIS]); SERIAL_ECHOPAIR(" Z", planner.max_acceleration_mm_per_s2[Z_AXIS]); - SERIAL_ECHOPAIR(" E", planner.max_acceleration_mm_per_s2[E_AXIS]); + #if E_STEPPERS == 1 + SERIAL_ECHOPAIR(" E", planner.max_acceleration_mm_per_s2[E_AXIS]); + #endif SERIAL_EOL; + #if ENABLED(DISTINCT_E_FACTORS) + for (uint8_t i = 0; i < E_STEPPERS; i++) { + SERIAL_ECHOPAIR(" M201 T", (int)i); + SERIAL_ECHOLNPAIR(" E", planner.max_acceleration_mm_per_s2[E_AXIS + i]); + } + #endif + CONFIG_ECHO_START; if (!forReplay) { SERIAL_ECHOLNPGM("Accelerations: P=printing, R=retract and T=travel"); diff --git a/Marlin/enum.h b/Marlin/enum.h index d7042e2a8..23b1fd033 100644 --- a/Marlin/enum.h +++ b/Marlin/enum.h @@ -50,6 +50,7 @@ enum AxisEnum { #define LOOP_XYZ(VAR) for (uint8_t VAR=X_AXIS; VAR<=Z_AXIS; VAR++) #define LOOP_XYZE(VAR) for (uint8_t VAR=X_AXIS; VAR<=E_AXIS; VAR++) +#define LOOP_XYZE_N(VAR) for (uint8_t VAR=X_AXIS; VAR (int32_t)axis_steps_per_mm[E_AXIS] * (EXTRUDE_MAXLENGTH)) { // It's not important to get max. extrusion length in a precision < 1mm, so save some cycles and cast to int + if (labs(de) > (int32_t)axis_steps_per_mm[E_AXIS_N] * (EXTRUDE_MAXLENGTH)) { // It's not important to get max. extrusion length in a precision < 1mm, so save some cycles and cast to int position[E_AXIS] = target[E_AXIS]; // Behave as if the move really took place, but ignore E part de = 0; // no difference SERIAL_ECHO_START; @@ -941,7 +953,7 @@ void Planner::_buffer_line(const float &a, const float &b, const float &c, const delta_mm[Y_AXIS] = db * steps_to_mm[Y_AXIS]; delta_mm[Z_AXIS] = dc * steps_to_mm[Z_AXIS]; #endif - delta_mm[E_AXIS] = esteps_float * steps_to_mm[E_AXIS]; + delta_mm[E_AXIS] = esteps_float * steps_to_mm[E_AXIS_N]; if (block->steps[X_AXIS] < MIN_STEPS_PER_SEGMENT && block->steps[Y_AXIS] < MIN_STEPS_PER_SEGMENT && block->steps[Z_AXIS] < MIN_STEPS_PER_SEGMENT) { block->millimeters = fabs(delta_mm[E_AXIS]); @@ -1091,16 +1103,16 @@ void Planner::_buffer_line(const float &a, const float &b, const float &c, const accel = ceil(retract_acceleration * steps_per_mm); } else { - #define LIMIT_ACCEL_LONG(AXIS) do{ \ - if (block->steps[AXIS] && max_acceleration_steps_per_s2[AXIS] < accel) { \ - const uint32_t comp = max_acceleration_steps_per_s2[AXIS] * block->step_event_count; \ + #define LIMIT_ACCEL_LONG(AXIS,INDX) do{ \ + if (block->steps[AXIS] && max_acceleration_steps_per_s2[AXIS+INDX] < accel) { \ + const uint32_t comp = max_acceleration_steps_per_s2[AXIS+INDX] * block->step_event_count; \ if (accel * block->steps[AXIS] > comp) accel = comp / block->steps[AXIS]; \ } \ }while(0) - #define LIMIT_ACCEL_FLOAT(AXIS) do{ \ - if (block->steps[AXIS] && max_acceleration_steps_per_s2[AXIS] < accel) { \ - const float comp = (float)max_acceleration_steps_per_s2[AXIS] * (float)block->step_event_count; \ + #define LIMIT_ACCEL_FLOAT(AXIS,INDX) do{ \ + if (block->steps[AXIS] && max_acceleration_steps_per_s2[AXIS+INDX] < accel) { \ + const float comp = (float)max_acceleration_steps_per_s2[AXIS+INDX] * (float)block->step_event_count; \ if ((float)accel * (float)block->steps[AXIS] > comp) accel = comp / (float)block->steps[AXIS]; \ } \ }while(0) @@ -1109,16 +1121,17 @@ void Planner::_buffer_line(const float &a, const float &b, const float &c, const accel = ceil((esteps ? acceleration : travel_acceleration) * steps_per_mm); // Limit acceleration per axis - if (block->step_event_count <= cutoff_long){ - LIMIT_ACCEL_LONG(X_AXIS); - LIMIT_ACCEL_LONG(Y_AXIS); - LIMIT_ACCEL_LONG(Z_AXIS); - LIMIT_ACCEL_LONG(E_AXIS); - } else { - LIMIT_ACCEL_FLOAT(X_AXIS); - LIMIT_ACCEL_FLOAT(Y_AXIS); - LIMIT_ACCEL_FLOAT(Z_AXIS); - LIMIT_ACCEL_FLOAT(E_AXIS); + if (block->step_event_count <= cutoff_long) { + LIMIT_ACCEL_LONG(X_AXIS,0); + LIMIT_ACCEL_LONG(Y_AXIS,0); + LIMIT_ACCEL_LONG(Z_AXIS,0); + LIMIT_ACCEL_LONG(E_AXIS,extruder); + } + else { + LIMIT_ACCEL_FLOAT(X_AXIS,0); + LIMIT_ACCEL_FLOAT(Y_AXIS,0); + LIMIT_ACCEL_FLOAT(Z_AXIS,0); + LIMIT_ACCEL_FLOAT(E_AXIS,extruder); } } block->acceleration_steps_per_s2 = accel; @@ -1302,7 +1315,7 @@ void Planner::_buffer_line(const float &a, const float &b, const float &c, const } else { block->use_advance_lead = true; - block->abs_adv_steps_multiplier8 = lround(extruder_advance_k * (de_float / mm_D_float) * block->nominal_speed / (float)block->nominal_rate * axis_steps_per_mm[E_AXIS] * 256.0); + block->abs_adv_steps_multiplier8 = lround(extruder_advance_k * (de_float / mm_D_float) * block->nominal_speed / (float)block->nominal_rate * axis_steps_per_mm[E_AXIS_N] * 256.0); } #elif ENABLED(ADVANCE) @@ -1350,13 +1363,18 @@ void Planner::_buffer_line(const float &a, const float &b, const float &c, const */ void Planner::_set_position_mm(const float &a, const float &b, const float &c, const float &e) { + #if ENABLED(DISTINCT_E_FACTORS) + #define _EINDEX (E_AXIS + active_extruder) + last_extruder = active_extruder; + #else + #define _EINDEX E_AXIS + #endif long na = position[X_AXIS] = lround(a * axis_steps_per_mm[X_AXIS]), nb = position[Y_AXIS] = lround(b * axis_steps_per_mm[Y_AXIS]), nc = position[Z_AXIS] = lround(c * axis_steps_per_mm[Z_AXIS]), - ne = position[E_AXIS] = lround(e * axis_steps_per_mm[E_AXIS]); + ne = position[E_AXIS] = lround(e * axis_steps_per_mm[_EINDEX]); stepper.set_position(na, nb, nc, ne); previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. - ZERO(previous_speed); } @@ -1386,24 +1404,35 @@ void Planner::sync_from_steppers() { * Setters for planner position (also setting stepper position). */ void Planner::set_position_mm(const AxisEnum axis, const float& v) { - position[axis] = lround(v * axis_steps_per_mm[axis]); + #if ENABLED(DISTINCT_E_FACTORS) + const uint8_t axis_index = axis + (axis == E_AXIS ? active_extruder : 0); + last_extruder = active_extruder; + #else + const uint8_t axis_index = axis; + #endif + position[axis] = lround(v * axis_steps_per_mm[axis_index]); stepper.set_position(axis, v); previous_speed[axis] = 0.0; } // Recalculate the steps/s^2 acceleration rates, based on the mm/s^2 void Planner::reset_acceleration_rates() { + #if ENABLED(DISTINCT_E_FACTORS) + #define HIGHEST_CONDITION (i < E_AXIS || i == E_AXIS + active_extruder) + #else + #define HIGHEST_CONDITION true + #endif uint32_t highest_rate = 1; - LOOP_XYZE(i) { + LOOP_XYZE_N(i) { max_acceleration_steps_per_s2[i] = max_acceleration_mm_per_s2[i] * axis_steps_per_mm[i]; - NOLESS(highest_rate, max_acceleration_steps_per_s2[i]); + if (HIGHEST_CONDITION) NOLESS(highest_rate, max_acceleration_steps_per_s2[i]); } cutoff_long = 4294967295UL / highest_rate; } // Recalculate position, steps_to_mm if axis_steps_per_mm changes! void Planner::refresh_positioning() { - LOOP_XYZE(i) steps_to_mm[i] = 1.0 / axis_steps_per_mm[i]; + LOOP_XYZE_N(i) steps_to_mm[i] = 1.0 / axis_steps_per_mm[i]; set_position_mm_kinematic(current_position); reset_acceleration_rates(); } diff --git a/Marlin/planner.h b/Marlin/planner.h index 64153207c..9ba2e69f3 100644 --- a/Marlin/planner.h +++ b/Marlin/planner.h @@ -143,11 +143,15 @@ class Planner { static volatile uint8_t block_buffer_head, // Index of the next block to be pushed block_buffer_tail; - static float max_feedrate_mm_s[NUM_AXIS]; // Max speeds in mm per second - static float axis_steps_per_mm[NUM_AXIS]; - static float steps_to_mm[NUM_AXIS]; - static unsigned long max_acceleration_steps_per_s2[NUM_AXIS]; - static unsigned long max_acceleration_mm_per_s2[NUM_AXIS]; // Use M201 to override by software + #if ENABLED(DISTINCT_E_FACTORS) + static uint8_t last_extruder; // Respond to extruder change + #endif + + static float max_feedrate_mm_s[XYZE_N], // Max speeds in mm per second + axis_steps_per_mm[XYZE_N], + steps_to_mm[XYZE_N]; + static unsigned long max_acceleration_steps_per_s2[XYZE_N], + max_acceleration_mm_per_s2[XYZE_N]; // Use M201 to override by software static millis_t min_segment_time; static float min_feedrate_mm_s, @@ -343,7 +347,13 @@ class Planner { static void set_position_mm_kinematic(const float position[NUM_AXIS]); static void set_position_mm(const AxisEnum axis, const float &v); static FORCE_INLINE void set_z_position_mm(const float &z) { set_position_mm(Z_AXIS, z); } - static FORCE_INLINE void set_e_position_mm(const float &e) { set_position_mm(E_AXIS, e); } + static FORCE_INLINE void set_e_position_mm(const float &e) { + set_position_mm(E_AXIS + #if ENABLED(DISTINCT_E_FACTORS) + + active_extruder + #endif + , e); + } /** * Sync from the stepper positions. (e.g., after an interrupted move)