diff --git a/Marlin/Marlin.h b/Marlin/Marlin.h index d7c14d29a..bc1f567fe 100644 --- a/Marlin/Marlin.h +++ b/Marlin/Marlin.h @@ -167,37 +167,63 @@ void manage_inactivity(bool ignore_stepper_queue = false); #define disable_z() NOOP #endif -#if HAS_E0_ENABLE - #define enable_e0() E0_ENABLE_WRITE( E_ENABLE_ON) - #define disable_e0() E0_ENABLE_WRITE(!E_ENABLE_ON) -#else - #define enable_e0() NOOP - #define disable_e0() NOOP -#endif - -#if (EXTRUDERS > 1) && HAS_E1_ENABLE - #define enable_e1() E1_ENABLE_WRITE( E_ENABLE_ON) - #define disable_e1() E1_ENABLE_WRITE(!E_ENABLE_ON) -#else +#if ENABLED(MIXING_EXTRUDER) + + /** + * Mixing steppers synchronize their enable (and direction) together + */ + #if MIXING_STEPPERS > 3 + #define enable_e0() { E0_ENABLE_WRITE( E_ENABLE_ON); E1_ENABLE_WRITE( E_ENABLE_ON); E2_ENABLE_WRITE( E_ENABLE_ON); E3_ENABLE_WRITE( E_ENABLE_ON); } + #define disable_e0() { E0_ENABLE_WRITE(!E_ENABLE_ON); E1_ENABLE_WRITE(!E_ENABLE_ON); E2_ENABLE_WRITE(!E_ENABLE_ON); E3_ENABLE_WRITE(!E_ENABLE_ON); } + #elif MIXING_STEPPERS > 2 + #define enable_e0() { E0_ENABLE_WRITE( E_ENABLE_ON); E1_ENABLE_WRITE( E_ENABLE_ON); E2_ENABLE_WRITE( E_ENABLE_ON); } + #define disable_e0() { E0_ENABLE_WRITE(!E_ENABLE_ON); E1_ENABLE_WRITE(!E_ENABLE_ON); E2_ENABLE_WRITE(!E_ENABLE_ON); } + #else + #define enable_e0() { E0_ENABLE_WRITE( E_ENABLE_ON); E1_ENABLE_WRITE( E_ENABLE_ON); } + #define disable_e0() { E0_ENABLE_WRITE(!E_ENABLE_ON); E1_ENABLE_WRITE(!E_ENABLE_ON); } + #endif #define enable_e1() NOOP #define disable_e1() NOOP -#endif - -#if (EXTRUDERS > 2) && HAS_E2_ENABLE - #define enable_e2() E2_ENABLE_WRITE( E_ENABLE_ON) - #define disable_e2() E2_ENABLE_WRITE(!E_ENABLE_ON) -#else #define enable_e2() NOOP #define disable_e2() NOOP -#endif - -#if (EXTRUDERS > 3) && HAS_E3_ENABLE - #define enable_e3() E3_ENABLE_WRITE( E_ENABLE_ON) - #define disable_e3() E3_ENABLE_WRITE(!E_ENABLE_ON) -#else #define enable_e3() NOOP #define disable_e3() NOOP -#endif + +#else // !MIXING_EXTRUDER + + #if HAS_E0_ENABLE + #define enable_e0() E0_ENABLE_WRITE( E_ENABLE_ON) + #define disable_e0() E0_ENABLE_WRITE(!E_ENABLE_ON) + #else + #define enable_e0() NOOP + #define disable_e0() NOOP + #endif + + #if E_STEPPERS > 1 && HAS_E1_ENABLE + #define enable_e1() E1_ENABLE_WRITE( E_ENABLE_ON) + #define disable_e1() E1_ENABLE_WRITE(!E_ENABLE_ON) + #else + #define enable_e1() NOOP + #define disable_e1() NOOP + #endif + + #if E_STEPPERS > 2 && HAS_E2_ENABLE + #define enable_e2() E2_ENABLE_WRITE( E_ENABLE_ON) + #define disable_e2() E2_ENABLE_WRITE(!E_ENABLE_ON) + #else + #define enable_e2() NOOP + #define disable_e2() NOOP + #endif + + #if E_STEPPERS > 3 && HAS_E3_ENABLE + #define enable_e3() E3_ENABLE_WRITE( E_ENABLE_ON) + #define disable_e3() E3_ENABLE_WRITE(!E_ENABLE_ON) + #else + #define enable_e3() NOOP + #define disable_e3() NOOP + #endif + +#endif // !MIXING_EXTRUDER /** * The axis order in all axis related arrays is X, Y, Z, E @@ -376,6 +402,10 @@ extern uint8_t active_extruder; void print_heaterstates(); #endif +#if ENABLED(MIXING_EXTRUDER) + extern float mixing_factor[MIXING_STEPPERS]; +#endif + void calculate_volumetric_multipliers(); // Buzzer diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 14c427be4..837550e8f 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -183,6 +183,9 @@ * M145 - Set the heatup state H B F for S (0=PLA, 1=ABS) * M149 - Set temperature units * M150 - Set BlinkM Color Output R: Red<0-255> U(!): Green<0-255> B: Blue<0-255> over i2c, G for green does not work. + * M163 - Set a single proportion for a mixing extruder. Requires MIXING_EXTRUDER. + * M164 - Save the mix as a virtual extruder. Requires MIXING_EXTRUDER and MIXING_VIRTUAL_TOOLS. + * M165 - Set the proportions for a mixing extruder. Use parameters ABCDHI to set the mixing factors. Requires MIXING_EXTRUDER. * M190 - Sxxx Wait for bed current temp to reach target temp. Waits only when heating * Rxxx Wait for bed current temp to reach target temp. Waits when heating and cooling * M200 - Set filament diameter, D, setting E axis units to cubic. (Use S0 to revert to linear units.) @@ -397,17 +400,11 @@ static uint8_t target_extruder; // Extruder offsets #if HOTENDS > 1 - #ifndef HOTEND_OFFSET_X - #define HOTEND_OFFSET_X { 0 } // X offsets for each extruder - #endif - #ifndef HOTEND_OFFSET_Y - #define HOTEND_OFFSET_Y { 0 } // Y offsets for each extruder - #endif float hotend_offset[][HOTENDS] = { HOTEND_OFFSET_X, HOTEND_OFFSET_Y - #if ENABLED(DUAL_X_CARRIAGE) - , { 0 } // Z offsets for each extruder + #ifdef HOTEND_OFFSET_Z + , HOTEND_OFFSET_Z #endif }; #endif @@ -507,6 +504,13 @@ static uint8_t target_extruder; FilamentChangeMenuResponse filament_change_menu_response; #endif +#if ENABLED(MIXING_EXTRUDER) + float mixing_factor[MIXING_STEPPERS]; + #if MIXING_VIRTUAL_TOOLS > 1 + float mixing_virtual_tool_mix[MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS]; + #endif +#endif + static bool send_ok[BUFSIZE]; #if HAS_SERVOS @@ -952,6 +956,15 @@ void setup() { lcd_init(); #endif #endif + + #if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1 + // Initialize mixing to 100% color 1 + for (uint8_t i = 0; i < MIXING_STEPPERS; i++) + mixing_factor[i] = (i == 0) ? 1 : 0; + for (uint8_t t = 0; t < MIXING_VIRTUAL_TOOLS; t++) + for (uint8_t i = 0; i < MIXING_STEPPERS; i++) + mixing_virtual_tool_mix[t][i] = mixing_factor[i]; + #endif } /** @@ -2544,6 +2557,39 @@ static void homeaxis(AxisEnum axis) { #endif // FWRETRACT +#if ENABLED(MIXING_EXTRUDER) + + void normalize_mix() { + float mix_total = 0.0; + for (int i = 0; i < MIXING_STEPPERS; i++) { + float v = mixing_factor[i]; + if (v < 0) v = mixing_factor[i] = 0; + mix_total += v; + } + // Scale all values if they don't add up to ~1.0 + if (mix_total < 0.9999 || mix_total > 1.0001) { + SERIAL_PROTOCOLLNPGM("Warning: Mix factors must add up to 1.0. Scaling."); + float mix_scale = 1.0 / mix_total; + for (int i = 0; i < MIXING_STEPPERS; i++) + mixing_factor[i] *= mix_scale; + } + } + + #if ENABLED(DIRECT_MIXING_IN_G1) + // Get mixing parameters from the GCode + // Factors that are left out are set to 0 + // The total "must" be 1.0 (but it will be normalized) + void gcode_get_mix() { + const char* mixing_codes = "ABCDHI"; + for (int i = 0; i < MIXING_STEPPERS; i++) + mixing_factor[i] = code_seen(mixing_codes[i]) ? code_value_float() : 0; + + normalize_mix(); + } + #endif + +#endif + /** * *************************************************************************** * ***************************** G-CODE HANDLING ***************************** @@ -2572,6 +2618,11 @@ void gcode_get_destination() { if(!DEBUGGING(DRYRUN)) print_job_timer.incFilamentUsed(destination[E_AXIS] - current_position[E_AXIS]); #endif + + // Get ABCDHI mixing factors + #if ENABLED(MIXING_EXTRUDER) && ENABLED(DIRECT_MIXING_IN_G1) + gcode_get_mix(); + #endif } void unknown_command_error() { @@ -4733,6 +4784,8 @@ inline void gcode_M109() { KEEPALIVE_STATE(NOT_BUSY); + target_extruder = active_extruder; // for print_heaterstates + do { // Target temperature might be changed during the loop if (theTarget != thermalManager.degTargetBed()) { @@ -5258,7 +5311,7 @@ inline void gcode_M200() { if (volumetric_enabled) { filament_size[target_extruder] = code_value_linear_units(); // make sure all extruders have some sane value for the filament size - for (int i = 0; i < EXTRUDERS; i++) + for (int i = 0; i < COUNT(filament_size); i++) if (! filament_size[i]) filament_size[i] = DEFAULT_NOMINAL_FILAMENT_DIA; } } @@ -5496,7 +5549,7 @@ inline void gcode_M206() { * T * X * Y - * Z - Available with DUAL_X_CARRIAGE + * Z - Available with DUAL_X_CARRIAGE and SWITCHING_EXTRUDER */ inline void gcode_M218() { if (get_target_extruder_from_command(218)) return; @@ -5504,7 +5557,7 @@ inline void gcode_M206() { if (code_seen('X')) hotend_offset[X_AXIS][target_extruder] = code_value_axis_units(X_AXIS); if (code_seen('Y')) hotend_offset[Y_AXIS][target_extruder] = code_value_axis_units(Y_AXIS); - #if ENABLED(DUAL_X_CARRIAGE) + #if ENABLED(DUAL_X_CARRIAGE) || ENABLED(SWITCHING_EXTRUDER) if (code_seen('Z')) hotend_offset[Z_AXIS][target_extruder] = code_value_axis_units(Z_AXIS); #endif @@ -5515,7 +5568,7 @@ inline void gcode_M206() { SERIAL_ECHO(hotend_offset[X_AXIS][e]); SERIAL_CHAR(','); SERIAL_ECHO(hotend_offset[Y_AXIS][e]); - #if ENABLED(DUAL_X_CARRIAGE) + #if ENABLED(DUAL_X_CARRIAGE) || ENABLED(SWITCHING_EXTRUDER) SERIAL_CHAR(','); SERIAL_ECHO(hotend_offset[Z_AXIS][e]); #endif @@ -6528,6 +6581,60 @@ inline void gcode_M907() { #endif // HAS_MICROSTEPS +#if ENABLED(MIXING_EXTRUDER) + + /** + * M163: Set a single mix factor for a mixing extruder + * This is called "weight" by some systems. + * + * S[index] The channel index to set + * P[float] The mix value + * + */ + inline void gcode_M163() { + int mix_index = code_seen('S') ? code_value_int() : 0; + float mix_value = code_seen('P') ? code_value_float() : 0.0; + if (mix_index < MIXING_STEPPERS) mixing_factor[mix_index] = mix_value; + } + + #if MIXING_VIRTUAL_TOOLS > 1 + + /** + * M164: Store the current mix factors as a virtual tool. + * + * S[index] The virtual tool to store + * + */ + inline void gcode_M164() { + int tool_index = code_seen('S') ? code_value_int() : 0; + if (tool_index < MIXING_VIRTUAL_TOOLS) { + normalize_mix(); + for (uint8_t i = 0; i < MIXING_STEPPERS; i++) + mixing_virtual_tool_mix[tool_index][i] = mixing_factor[i]; + } + } + + #endif + + #if ENABLED(DIRECT_MIXING_IN_G1) + /** + * M165: Set multiple mix factors for a mixing extruder. + * Factors that are left out will be set to 0. + * All factors together must add up to 1.0. + * + * A[factor] Mix factor for extruder stepper 1 + * B[factor] Mix factor for extruder stepper 2 + * C[factor] Mix factor for extruder stepper 3 + * D[factor] Mix factor for extruder stepper 4 + * H[factor] Mix factor for extruder stepper 5 + * I[factor] Mix factor for extruder stepper 6 + * + */ + inline void gcode_M165() { gcode_get_mix(); } + #endif + +#endif // MIXING_EXTRUDER + /** * M999: Restart after being stopped * @@ -6548,6 +6655,20 @@ inline void gcode_M999() { FlushSerialRequestResend(); } +#if ENABLED(SWITCHING_EXTRUDER) + inline void move_extruder_servo(uint8_t e) { + const int angles[2] = SWITCHING_EXTRUDER_SERVO_ANGLES; + MOVE_SERVO(SWITCHING_EXTRUDER_SERVO_NR, angles[e]); + } +#endif + +inline void invalid_extruder_error(const uint8_t &e) { + SERIAL_ECHO_START; + SERIAL_CHAR('T'); + SERIAL_PROTOCOL_F(e, DEC); + SERIAL_ECHOLN(MSG_INVALID_EXTRUDER); +} + /** * T0-T3: Switch tool, usually switching extruders * @@ -6555,264 +6676,314 @@ inline void gcode_M999() { * S1 Don't move the tool in XY after change */ inline void gcode_T(uint8_t tmp_extruder) { - if (tmp_extruder >= EXTRUDERS) { - SERIAL_ECHO_START; - SERIAL_CHAR('T'); - SERIAL_PROTOCOL_F(tmp_extruder, DEC); - SERIAL_ECHOLN(MSG_INVALID_EXTRUDER); - return; - } - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) { - SERIAL_ECHOLNPGM(">>> gcode_T"); - DEBUG_POS("BEFORE", current_position); + #if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1 + + if (tmp_extruder >= MIXING_VIRTUAL_TOOLS) { + invalid_extruder_error(tmp_extruder); + return; } - #endif - #if HOTENDS > 1 + // T0-Tnnn: Switch virtual tool by changing the mix + for (uint8_t j = 0; j < MIXING_STEPPERS; j++) + mixing_factor[j] = mixing_virtual_tool_mix[tmp_extruder][j]; - float old_feedrate = feedrate; + #else //!MIXING_EXTRUDER || MIXING_VIRTUAL_TOOLS <= 1 - if (code_seen('F')) { - float next_feedrate = code_value_axis_units(X_AXIS); - if (next_feedrate > 0.0) old_feedrate = feedrate = next_feedrate; - } - else - feedrate = XY_PROBE_FEEDRATE; + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + SERIAL_ECHOLNPGM(">>> gcode_T"); + DEBUG_POS("BEFORE", current_position); + } + #endif - if (tmp_extruder != active_extruder) { - bool no_move = code_seen('S') && code_value_bool(); - if (!no_move && axis_unhomed_error(true, true, true)) { - SERIAL_ECHOLNPGM("No move on toolchange"); - no_move = true; + #if HOTENDS > 1 + + if (tmp_extruder >= EXTRUDERS) { + invalid_extruder_error(tmp_extruder); + return; } - // Save current position to destination, for use later - set_destination_to_current(); + float old_feedrate = feedrate; - #if ENABLED(DUAL_X_CARRIAGE) + if (code_seen('F')) { + float next_feedrate = code_value_axis_units(X_AXIS); + if (next_feedrate > 0.0) old_feedrate = feedrate = next_feedrate; + } + else + feedrate = XY_PROBE_FEEDRATE; - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) { - SERIAL_ECHOPGM("Dual X Carriage Mode "); - switch (dual_x_carriage_mode) { - case DXC_DUPLICATION_MODE: SERIAL_ECHOLNPGM("DXC_DUPLICATION_MODE"); break; - case DXC_AUTO_PARK_MODE: SERIAL_ECHOLNPGM("DXC_AUTO_PARK_MODE"); break; - case DXC_FULL_CONTROL_MODE: SERIAL_ECHOLNPGM("DXC_FULL_CONTROL_MODE"); break; - } - } - #endif + if (tmp_extruder != active_extruder) { + bool no_move = code_seen('S') && code_value_bool(); + + if (!no_move && axis_unhomed_error(true, true, true)) { + SERIAL_ECHOLNPGM("No move on toolchange"); + no_move = true; + } + + // Save current position to destination, for use later + set_destination_to_current(); + + #if ENABLED(DUAL_X_CARRIAGE) - if (dual_x_carriage_mode == DXC_AUTO_PARK_MODE && IsRunning() - && (delayed_move_time || current_position[X_AXIS] != x_home_pos(active_extruder)) - ) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) { - SERIAL_ECHOPAIR("Raise to ", current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT); SERIAL_EOL; - SERIAL_ECHOPAIR("MoveX to ", x_home_pos(active_extruder)); SERIAL_EOL; - SERIAL_ECHOPAIR("Lower to ", current_position[Z_AXIS]); SERIAL_EOL; + SERIAL_ECHOPGM("Dual X Carriage Mode "); + switch (dual_x_carriage_mode) { + case DXC_DUPLICATION_MODE: SERIAL_ECHOLNPGM("DXC_DUPLICATION_MODE"); break; + case DXC_AUTO_PARK_MODE: SERIAL_ECHOLNPGM("DXC_AUTO_PARK_MODE"); break; + case DXC_FULL_CONTROL_MODE: SERIAL_ECHOLNPGM("DXC_FULL_CONTROL_MODE"); break; + } } #endif - // Park old head: 1) raise 2) move to park position 3) lower - planner.buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT, - current_position[E_AXIS], planner.max_feedrate[Z_AXIS], active_extruder); - planner.buffer_line(x_home_pos(active_extruder), current_position[Y_AXIS], current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT, - current_position[E_AXIS], planner.max_feedrate[X_AXIS], active_extruder); - planner.buffer_line(x_home_pos(active_extruder), current_position[Y_AXIS], current_position[Z_AXIS], - current_position[E_AXIS], planner.max_feedrate[Z_AXIS], active_extruder); - stepper.synchronize(); - } - // apply Y & Z extruder offset (x offset is already used in determining home pos) - current_position[Y_AXIS] -= hotend_offset[Y_AXIS][active_extruder] - hotend_offset[Y_AXIS][tmp_extruder]; - current_position[Z_AXIS] -= hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder]; - active_extruder = tmp_extruder; + if (dual_x_carriage_mode == DXC_AUTO_PARK_MODE && IsRunning() && + (delayed_move_time || current_position[X_AXIS] != x_home_pos(active_extruder)) + ) { + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + SERIAL_ECHOPAIR("Raise to ", current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT); SERIAL_EOL; + SERIAL_ECHOPAIR("MoveX to ", x_home_pos(active_extruder)); SERIAL_EOL; + SERIAL_ECHOPAIR("Lower to ", current_position[Z_AXIS]); SERIAL_EOL; + } + #endif + // Park old head: 1) raise 2) move to park position 3) lower + for (uint8_t i = 0; i < 3; i++) + planner.buffer_line( + i == 0 ? current_position[X_AXIS] : x_home_pos(active_extruder), + current_position[Y_AXIS], + current_position[Z_AXIS] + (i == 2 ? 0 : TOOLCHANGE_PARK_ZLIFT), + current_position[E_AXIS], + planner.max_feedrate[i == 1 ? X_AXIS : Z_AXIS], + active_extruder + ); + stepper.synchronize(); + } - // This function resets the max/min values - the current position may be overwritten below. - set_axis_is_at_home(X_AXIS); + // apply Y & Z extruder offset (x offset is already used in determining home pos) + current_position[Y_AXIS] -= hotend_offset[Y_AXIS][active_extruder] - hotend_offset[Y_AXIS][tmp_extruder]; + current_position[Z_AXIS] -= hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder]; + active_extruder = tmp_extruder; - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) DEBUG_POS("New Extruder", current_position); - #endif + // This function resets the max/min values - the current position may be overwritten below. + set_axis_is_at_home(X_AXIS); - switch (dual_x_carriage_mode) { - case DXC_FULL_CONTROL_MODE: - current_position[X_AXIS] = inactive_extruder_x_pos; - inactive_extruder_x_pos = destination[X_AXIS]; - break; - case DXC_DUPLICATION_MODE: - active_extruder_parked = (active_extruder == 0); // this triggers the second extruder to move into the duplication position - if (active_extruder_parked) - current_position[X_AXIS] = inactive_extruder_x_pos; - else - current_position[X_AXIS] = destination[X_AXIS] + duplicate_extruder_x_offset; - inactive_extruder_x_pos = destination[X_AXIS]; - extruder_duplication_enabled = false; - break; - default: - // record raised toolhead position for use by unpark - memcpy(raised_parked_position, current_position, sizeof(raised_parked_position)); - raised_parked_position[Z_AXIS] += TOOLCHANGE_UNPARK_ZLIFT; - active_extruder_parked = true; - delayed_move_time = 0; - break; - } + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) DEBUG_POS("New Extruder", current_position); + #endif - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) { - SERIAL_ECHOPAIR("Active extruder parked: ", active_extruder_parked ? "yes" : "no"); - SERIAL_EOL; - DEBUG_POS("New extruder (parked)", current_position); + switch (dual_x_carriage_mode) { + case DXC_FULL_CONTROL_MODE: + current_position[X_AXIS] = inactive_extruder_x_pos; + inactive_extruder_x_pos = destination[X_AXIS]; + break; + case DXC_DUPLICATION_MODE: + active_extruder_parked = (active_extruder == 0); // this triggers the second extruder to move into the duplication position + if (active_extruder_parked) + current_position[X_AXIS] = inactive_extruder_x_pos; + else + current_position[X_AXIS] = destination[X_AXIS] + duplicate_extruder_x_offset; + inactive_extruder_x_pos = destination[X_AXIS]; + extruder_duplication_enabled = false; + break; + default: + // record raised toolhead position for use by unpark + memcpy(raised_parked_position, current_position, sizeof(raised_parked_position)); + raised_parked_position[Z_AXIS] += TOOLCHANGE_UNPARK_ZLIFT; + active_extruder_parked = true; + delayed_move_time = 0; + break; } - #endif - - // No extra case for AUTO_BED_LEVELING_FEATURE in DUAL_X_CARRIAGE. Does that mean they don't work together? - #else // !DUAL_X_CARRIAGE - - /** - * Set current_position to the position of the new nozzle. - * Offsets are based on linear distance, so we need to get - * the resulting position in coordinate space. - * - * - With grid or 3-point leveling, offset XYZ by a tilted vector - * - With mesh leveling, update Z for the new position - * - Otherwise, just use the raw linear distance - * - * Software endstops are altered here too. Consider a case where: - * E0 at X=0 ... E1 at X=10 - * When we switch to E1 now X=10, but E1 can't move left. - * To express this we apply the change in XY to the software endstops. - * E1 can move farther right than E0, so the right limit is extended. - * - * Note that we don't adjust the Z software endstops. Why not? - * Consider a case where Z=0 (here) and switching to E1 makes Z=1 - * because the bed is 1mm lower at the new position. As long as - * the first nozzle is out of the way, the carriage should be - * allowed to move 1mm lower. This technically "breaks" the - * Z software endstop. But this is technically correct (and - * there is no viable alternative). - */ - #if ENABLED(AUTO_BED_LEVELING_FEATURE) - // Offset extruder, make sure to apply the bed level rotation matrix - vector_3 tmp_offset_vec = vector_3(hotend_offset[X_AXIS][tmp_extruder], - hotend_offset[Y_AXIS][tmp_extruder], - 0), - act_offset_vec = vector_3(hotend_offset[X_AXIS][active_extruder], - hotend_offset[Y_AXIS][active_extruder], - 0), - offset_vec = tmp_offset_vec - act_offset_vec; #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) { - tmp_offset_vec.debug("tmp_offset_vec"); - act_offset_vec.debug("act_offset_vec"); - offset_vec.debug("offset_vec (BEFORE)"); + SERIAL_ECHOPAIR("Active extruder parked: ", active_extruder_parked ? "yes" : "no"); + SERIAL_EOL; + DEBUG_POS("New extruder (parked)", current_position); } #endif - offset_vec.apply_rotation(planner.bed_level_matrix.transpose(planner.bed_level_matrix)); - - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) offset_vec.debug("offset_vec (AFTER)"); + // No extra case for AUTO_BED_LEVELING_FEATURE in DUAL_X_CARRIAGE. Does that mean they don't work together? + #else // !DUAL_X_CARRIAGE + + #if ENABLED(SWITCHING_EXTRUDER) + // <0 if the new nozzle is higher, >0 if lower. A bigger raise when lower. + float z_diff = hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder], + z_raise = 0.3 + (z_diff > 0.0 ? z_diff : 0.0); + + // Always raise by some amount + planner.buffer_line( + current_position[X_AXIS], + current_position[Y_AXIS], + current_position[Z_AXIS] + z_raise, + current_position[E_AXIS], + planner.max_feedrate[Z_AXIS], + active_extruder + ); + stepper.synchronize(); + + move_extruder_servo(active_extruder); + delay(500); + + // Move back down, if needed + if (z_raise != z_diff) { + planner.buffer_line( + current_position[X_AXIS], + current_position[Y_AXIS], + current_position[Z_AXIS] + z_diff, + current_position[E_AXIS], + planner.max_feedrate[Z_AXIS], + active_extruder + ); + stepper.synchronize(); + } #endif + + /** + * Set current_position to the position of the new nozzle. + * Offsets are based on linear distance, so we need to get + * the resulting position in coordinate space. + * + * - With grid or 3-point leveling, offset XYZ by a tilted vector + * - With mesh leveling, update Z for the new position + * - Otherwise, just use the raw linear distance + * + * Software endstops are altered here too. Consider a case where: + * E0 at X=0 ... E1 at X=10 + * When we switch to E1 now X=10, but E1 can't move left. + * To express this we apply the change in XY to the software endstops. + * E1 can move farther right than E0, so the right limit is extended. + * + * Note that we don't adjust the Z software endstops. Why not? + * Consider a case where Z=0 (here) and switching to E1 makes Z=1 + * because the bed is 1mm lower at the new position. As long as + * the first nozzle is out of the way, the carriage should be + * allowed to move 1mm lower. This technically "breaks" the + * Z software endstop. But this is technically correct (and + * there is no viable alternative). + */ + #if ENABLED(AUTO_BED_LEVELING_FEATURE) + // Offset extruder, make sure to apply the bed level rotation matrix + vector_3 tmp_offset_vec = vector_3(hotend_offset[X_AXIS][tmp_extruder], + hotend_offset[Y_AXIS][tmp_extruder], + 0), + act_offset_vec = vector_3(hotend_offset[X_AXIS][active_extruder], + hotend_offset[Y_AXIS][active_extruder], + 0), + offset_vec = tmp_offset_vec - act_offset_vec; - // Adjustments to the current position - float xydiff[2] = { offset_vec.x, offset_vec.y }; - current_position[Z_AXIS] += offset_vec.z; + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + tmp_offset_vec.debug("tmp_offset_vec"); + act_offset_vec.debug("act_offset_vec"); + offset_vec.debug("offset_vec (BEFORE)"); + } + #endif - #else // !AUTO_BED_LEVELING_FEATURE + offset_vec.apply_rotation(planner.bed_level_matrix.transpose(planner.bed_level_matrix)); - float xydiff[2] = { - hotend_offset[X_AXIS][tmp_extruder] - hotend_offset[X_AXIS][active_extruder], - hotend_offset[Y_AXIS][tmp_extruder] - hotend_offset[Y_AXIS][active_extruder] - }; + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) offset_vec.debug("offset_vec (AFTER)"); + #endif - #if ENABLED(MESH_BED_LEVELING) + // Adjustments to the current position + float xydiff[2] = { offset_vec.x, offset_vec.y }; + current_position[Z_AXIS] += offset_vec.z; - if (mbl.active()) { - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) SERIAL_ECHOPAIR("Z before MBL: ", current_position[Z_AXIS]); - #endif - float xpos = RAW_CURRENT_POSITION(X_AXIS), - ypos = RAW_CURRENT_POSITION(Y_AXIS); - current_position[Z_AXIS] += mbl.get_z(xpos + xydiff[X_AXIS], ypos + xydiff[Y_AXIS]) - mbl.get_z(xpos, ypos); - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) { - SERIAL_ECHOPAIR(" after: ", current_position[Z_AXIS]); - SERIAL_EOL; - } - #endif - } + #else // !AUTO_BED_LEVELING_FEATURE + + float xydiff[2] = { + hotend_offset[X_AXIS][tmp_extruder] - hotend_offset[X_AXIS][active_extruder], + hotend_offset[Y_AXIS][tmp_extruder] - hotend_offset[Y_AXIS][active_extruder] + }; - #endif // MESH_BED_LEVELING + #if ENABLED(MESH_BED_LEVELING) - #endif // !AUTO_BED_LEVELING_FEATURE + if (mbl.active()) { + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) SERIAL_ECHOPAIR("Z before MBL: ", current_position[Z_AXIS]); + #endif + float xpos = RAW_CURRENT_POSITION(X_AXIS), + ypos = RAW_CURRENT_POSITION(Y_AXIS); + current_position[Z_AXIS] += mbl.get_z(xpos + xydiff[X_AXIS], ypos + xydiff[Y_AXIS]) - mbl.get_z(xpos, ypos); + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + SERIAL_ECHOPAIR(" after: ", current_position[Z_AXIS]); + SERIAL_EOL; + } + #endif + } - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) { - SERIAL_ECHOPAIR("Offset Tool XY by { ", xydiff[X_AXIS]); - SERIAL_ECHOPAIR(", ", xydiff[X_AXIS]); - SERIAL_ECHOLNPGM(" }"); - } - #endif + #endif // MESH_BED_LEVELING + + #endif // !AUTO_BED_LEVELING_FEATURE - // The newly-selected extruder XY is actually at... - current_position[X_AXIS] += xydiff[X_AXIS]; - current_position[Y_AXIS] += xydiff[Y_AXIS]; - for (uint8_t i = X_AXIS; i <= Y_AXIS; i++) { - position_shift[i] += xydiff[i]; - update_software_endstops((AxisEnum)i); - } + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + SERIAL_ECHOPAIR("Offset Tool XY by { ", xydiff[X_AXIS]); + SERIAL_ECHOPAIR(", ", xydiff[X_AXIS]); + SERIAL_ECHOLNPGM(" }"); + } + #endif - // Set the new active extruder - active_extruder = tmp_extruder; + // The newly-selected extruder XY is actually at... + current_position[X_AXIS] += xydiff[X_AXIS]; + current_position[Y_AXIS] += xydiff[Y_AXIS]; + for (uint8_t i = X_AXIS; i <= Y_AXIS; i++) { + position_shift[i] += xydiff[i]; + update_software_endstops((AxisEnum)i); + } - #endif // !DUAL_X_CARRIAGE + // Set the new active extruder + active_extruder = tmp_extruder; - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) DEBUG_POS("Sync After Toolchange", current_position); - #endif - - // Tell the planner the new "current position" - SYNC_PLAN_POSITION_KINEMATIC(); + #endif // !DUAL_X_CARRIAGE - // Move to the "old position" (move the extruder into place) - if (!no_move && IsRunning()) { #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) DEBUG_POS("Move back", destination); + if (DEBUGGING(LEVELING)) DEBUG_POS("Sync After Toolchange", current_position); #endif - prepare_move_to_destination(); - } - } // (tmp_extruder != active_extruder) + // Tell the planner the new "current position" + SYNC_PLAN_POSITION_KINEMATIC(); - stepper.synchronize(); + // Move to the "old position" (move the extruder into place) + if (!no_move && IsRunning()) { + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) DEBUG_POS("Move back", destination); + #endif + prepare_move_to_destination(); + } - #if ENABLED(EXT_SOLENOID) - disable_all_solenoids(); - enable_solenoid_on_active_extruder(); - #endif // EXT_SOLENOID + } // (tmp_extruder != active_extruder) - feedrate = old_feedrate; + stepper.synchronize(); - #else // !HOTENDS > 1 + #if ENABLED(EXT_SOLENOID) + disable_all_solenoids(); + enable_solenoid_on_active_extruder(); + #endif // EXT_SOLENOID - // Set the new active extruder - active_extruder = tmp_extruder; + feedrate = old_feedrate; - #endif + #else // HOTENDS <= 1 - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) { - DEBUG_POS("AFTER", current_position); - SERIAL_ECHOLNPGM("<<< gcode_T"); - } - #endif + // Set the new active extruder + active_extruder = tmp_extruder; - SERIAL_ECHO_START; - SERIAL_ECHOPGM(MSG_ACTIVE_EXTRUDER); - SERIAL_PROTOCOLLN((int)active_extruder); + #endif // HOTENDS <= 1 + + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + DEBUG_POS("AFTER", current_position); + SERIAL_ECHOLNPGM("<<< gcode_T"); + } + #endif + + SERIAL_ECHO_START; + SERIAL_ECHOPGM(MSG_ACTIVE_EXTRUDER); + SERIAL_PROTOCOLLN((int)active_extruder); + + #endif //!MIXING_EXTRUDER || MIXING_VIRTUAL_TOOLS <= 1 } /** @@ -7219,6 +7390,22 @@ void process_next_command() { #endif //EXPERIMENTAL_I2CBUS + #if ENABLED(MIXING_EXTRUDER) + case 163: // M163 S P set weight for a mixing extruder + gcode_M163(); + break; + #if MIXING_VIRTUAL_TOOLS > 1 + case 164: // M164 S save current mix as a virtual extruder + gcode_M164(); + break; + #endif + #if ENABLED(DIRECT_MIXING_IN_G1) + case 165: // M165 [ABCDHI] set multiple mix weights + gcode_M165(); + break; + #endif + #endif + case 200: // M200 D Set filament diameter and set E axis units to cubic. (Use S0 to revert to linear units.) gcode_M200(); break; @@ -8033,14 +8220,14 @@ void prepare_move_to_destination() { nextMotorCheck = ms + 2500UL; // Not a time critical function, so only check every 2.5s if (X_ENABLE_READ == X_ENABLE_ON || Y_ENABLE_READ == Y_ENABLE_ON || Z_ENABLE_READ == Z_ENABLE_ON || thermalManager.soft_pwm_bed > 0 || E0_ENABLE_READ == E_ENABLE_ON // If any of the drivers are enabled... - #if EXTRUDERS > 1 + #if E_STEPPERS > 1 || E1_ENABLE_READ == E_ENABLE_ON #if HAS_X2_ENABLE || X2_ENABLE_READ == X_ENABLE_ON #endif - #if EXTRUDERS > 2 + #if E_STEPPERS > 2 || E2_ENABLE_READ == E_ENABLE_ON - #if EXTRUDERS > 3 + #if E_STEPPERS > 3 || E3_ENABLE_READ == E_ENABLE_ON #endif #endif @@ -8303,25 +8490,29 @@ void manage_inactivity(bool ignore_stepper_queue/*=false*/) { #endif #if ENABLED(EXTRUDER_RUNOUT_PREVENT) - if (ELAPSED(ms, previous_cmd_ms + (EXTRUDER_RUNOUT_SECONDS) * 1000UL)) - if (thermalManager.degHotend(active_extruder) > EXTRUDER_RUNOUT_MINTEMP) { + if (ELAPSED(ms, previous_cmd_ms + (EXTRUDER_RUNOUT_SECONDS) * 1000UL) + && thermalManager.degHotend(active_extruder) > EXTRUDER_RUNOUT_MINTEMP) { + #if ENABLED(SWITCHING_EXTRUDER) + bool oldstatus = E0_ENABLE_READ; + enable_e0(); + #else // !SWITCHING_EXTRUDER bool oldstatus; switch (active_extruder) { case 0: oldstatus = E0_ENABLE_READ; enable_e0(); break; - #if EXTRUDERS > 1 + #if E_STEPPERS > 1 case 1: oldstatus = E1_ENABLE_READ; enable_e1(); break; - #if EXTRUDERS > 2 + #if E_STEPPERS > 2 case 2: oldstatus = E2_ENABLE_READ; enable_e2(); break; - #if EXTRUDERS > 3 + #if E_STEPPERS > 3 case 3: oldstatus = E3_ENABLE_READ; enable_e3(); @@ -8330,37 +8521,43 @@ void manage_inactivity(bool ignore_stepper_queue/*=false*/) { #endif #endif } - float oldepos = current_position[E_AXIS], oldedes = destination[E_AXIS]; - planner.buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], - destination[E_AXIS] + (EXTRUDER_RUNOUT_EXTRUDE) * (EXTRUDER_RUNOUT_ESTEPS) / planner.axis_steps_per_mm[E_AXIS], - (EXTRUDER_RUNOUT_SPEED) / 60. * (EXTRUDER_RUNOUT_ESTEPS) / planner.axis_steps_per_mm[E_AXIS], active_extruder); + #endif // !SWITCHING_EXTRUDER + + float oldepos = current_position[E_AXIS], oldedes = destination[E_AXIS]; + planner.buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], + destination[E_AXIS] + (EXTRUDER_RUNOUT_EXTRUDE) * (EXTRUDER_RUNOUT_ESTEPS) / planner.axis_steps_per_mm[E_AXIS], + (EXTRUDER_RUNOUT_SPEED) / 60. * (EXTRUDER_RUNOUT_ESTEPS) / planner.axis_steps_per_mm[E_AXIS], active_extruder); current_position[E_AXIS] = oldepos; destination[E_AXIS] = oldedes; planner.set_e_position_mm(oldepos); previous_cmd_ms = ms; // refresh_cmd_timeout() stepper.synchronize(); - switch (active_extruder) { - case 0: - E0_ENABLE_WRITE(oldstatus); - break; - #if EXTRUDERS > 1 - case 1: - E1_ENABLE_WRITE(oldstatus); + #if ENABLED(SWITCHING_EXTRUDER) + E0_ENABLE_WRITE(oldstatus); + #else + switch (active_extruder) { + case 0: + E0_ENABLE_WRITE(oldstatus); break; - #if EXTRUDERS > 2 - case 2: - E2_ENABLE_WRITE(oldstatus); + #if E_STEPPERS > 1 + case 1: + E1_ENABLE_WRITE(oldstatus); break; - #if EXTRUDERS > 3 - case 3: - E3_ENABLE_WRITE(oldstatus); + #if E_STEPPERS > 2 + case 2: + E2_ENABLE_WRITE(oldstatus); break; + #if E_STEPPERS > 3 + case 3: + E3_ENABLE_WRITE(oldstatus); + break; + #endif #endif #endif - #endif - } + } + #endif // !SWITCHING_EXTRUDER } - #endif + #endif // EXTRUDER_RUNOUT_PREVENT #if ENABLED(DUAL_X_CARRIAGE) // handle delayed move timeout @@ -8498,6 +8695,6 @@ float calculate_volumetric_multiplier(float diameter) { } void calculate_volumetric_multipliers() { - for (int i = 0; i < EXTRUDERS; i++) + for (int i = 0; i < COUNT(filament_size); i++) volumetric_multiplier[i] = calculate_volumetric_multiplier(filament_size[i]); } diff --git a/Marlin/configuration_store.cpp b/Marlin/configuration_store.cpp index 54c96b6e8..4cf9f5d36 100644 --- a/Marlin/configuration_store.cpp +++ b/Marlin/configuration_store.cpp @@ -349,7 +349,7 @@ void Config_StoreSettings() { // Save filament sizes for (uint8_t q = 0; q < MAX_EXTRUDERS; q++) { - if (q < EXTRUDERS) dummy = filament_size[q]; + if (q < COUNT(filament_size)) dummy = filament_size[q]; EEPROM_WRITE_VAR(i, dummy); } @@ -531,7 +531,7 @@ void Config_RetrieveSettings() { for (uint8_t q = 0; q < MAX_EXTRUDERS; q++) { EEPROM_READ_VAR(i, dummy); - if (q < EXTRUDERS) filament_size[q] = dummy; + if (q < COUNT(filament_size)) filament_size[q] = dummy; } if (eeprom_checksum == stored_checksum) { diff --git a/Marlin/language_en.h b/Marlin/language_en.h index efb48f553..eba87d3df 100644 --- a/Marlin/language_en.h +++ b/Marlin/language_en.h @@ -251,6 +251,9 @@ #ifndef MSG_PID_C #define MSG_PID_C "PID-C" #endif +#ifndef MSG_SELECT + #define MSG_SELECT "Select" +#endif #ifndef MSG_E1 #define MSG_E1 " E1" #endif diff --git a/Marlin/pins.h b/Marlin/pins.h index 880084588..cc18289bd 100644 --- a/Marlin/pins.h +++ b/Marlin/pins.h @@ -285,6 +285,17 @@ #define _H3_PINS HEATER_3_PIN, EXTRUDER_3_AUTO_FAN_PIN, marlinAnalogInputToDigitalPin(TEMP_3_PIN), #endif #endif +#elif ENABLED(MIXING_EXTRUDER) + #undef _E1_PINS + #define _E1_PINS E1_STEP_PIN, E1_DIR_PIN, E1_ENABLE_PIN, + #if MIXING_STEPPERS > 2 + #undef _E2_PINS + #define _E2_PINS E2_STEP_PIN, E2_DIR_PIN, E2_ENABLE_PIN, + #if MIXING_STEPPERS > 3 + #undef _E3_PINS + #define _E3_PINS E3_STEP_PIN, E3_DIR_PIN, E3_ENABLE_PIN, + #endif + #endif #endif #define BED_PINS HEATER_BED_PIN, marlinAnalogInputToDigitalPin(TEMP_BED_PIN), @@ -374,15 +385,15 @@ // The X2 axis, if any, should be the next open extruder port #if ENABLED(DUAL_X_CARRIAGE) || ENABLED(X_DUAL_STEPPER_DRIVERS) #ifndef X2_STEP_PIN - #define X2_STEP_PIN _EPIN(EXTRUDERS, STEP) - #define X2_DIR_PIN _EPIN(EXTRUDERS, DIR) - #define X2_ENABLE_PIN _EPIN(EXTRUDERS, ENABLE) + #define X2_STEP_PIN _EPIN(E_STEPPERS, STEP) + #define X2_DIR_PIN _EPIN(E_STEPPERS, DIR) + #define X2_ENABLE_PIN _EPIN(E_STEPPERS, ENABLE) #endif #undef _X2_PINS #define _X2_PINS X2_STEP_PIN, X2_DIR_PIN, X2_ENABLE_PIN, - #define Y2_E_INDEX INCREMENT(EXTRUDERS) + #define Y2_E_INDEX INCREMENT(E_STEPPERS) #else - #define Y2_E_INDEX EXTRUDERS + #define Y2_E_INDEX E_STEPPERS #endif // The Y2 axis, if any, should be the next open extruder port diff --git a/Marlin/pins_MEGACONTROLLER.h b/Marlin/pins_MEGACONTROLLER.h index 600f2f528..f21a80518 100644 --- a/Marlin/pins_MEGACONTROLLER.h +++ b/Marlin/pins_MEGACONTROLLER.h @@ -28,8 +28,8 @@ #error "Oops! Make sure you have 'Arduino Mega' selected from the 'Tools -> Boards' menu." #endif -#if EXTRUDERS > 2 || HOTENDS > 2 - #error "Mega Controller supports up to 2 extruders. Comment this line to keep going." +#if E_STEPPERS > 2 || HOTENDS > 2 + #error "Mega Controller supports up to 2 hotends / E-steppers. Comment this line to keep going." #endif #define BOARD_NAME "Mega Controller" diff --git a/Marlin/pins_RUMBA.h b/Marlin/pins_RUMBA.h index a0fe01360..642deb292 100644 --- a/Marlin/pins_RUMBA.h +++ b/Marlin/pins_RUMBA.h @@ -28,8 +28,8 @@ #error "Oops! Make sure you have 'Arduino Mega' selected from the 'Tools -> Boards' menu." #endif -#if EXTRUDERS > 3 || HOTENDS > 3 - #error "RUMBA supports up to 3 extruders. Comment this line to keep going." +#if E_STEPPERS > 3 || HOTENDS > 3 + #error "RUMBA supports up to 3 hotends / E-steppers. Comment this line to keep going." #endif #define DEFAULT_MACHINE_NAME "Rumba" diff --git a/Marlin/planner.cpp b/Marlin/planner.cpp index aacc041ef..900866e63 100644 --- a/Marlin/planner.cpp +++ b/Marlin/planner.cpp @@ -629,6 +629,12 @@ void Planner::check_axes_activity() { // Bail if this is a zero-length block if (block->step_event_count <= dropsegments) return; + // For a mixing extruder, get a magnified step_event_count for each + #if ENABLED(MIXING_EXTRUDER) + for (uint8_t i = 0; i < MIXING_STEPPERS; i++) + block->mix_event_count[i] = (mixing_factor[i] < 0.0001) ? 0 : block->step_event_count / mixing_factor[i]; + #endif + #if FAN_COUNT > 0 for (uint8_t i = 0; i < FAN_COUNT; i++) block->fan_speed[i] = fanSpeeds[i]; #endif diff --git a/Marlin/planner.h b/Marlin/planner.h index 71e6b8119..d963fba4c 100644 --- a/Marlin/planner.h +++ b/Marlin/planner.h @@ -58,6 +58,10 @@ typedef struct { long steps[NUM_AXIS]; // Step count along each axis unsigned long step_event_count; // The number of step events required to complete this block + #if ENABLED(MIXING_EXTRUDER) + unsigned long mix_event_count[MIXING_STEPPERS]; // Scaled step_event_count for the mixing steppers + #endif + long accelerate_until, // The index of the step event on which to stop acceleration decelerate_after, // The index of the step event on which to start decelerating acceleration_rate; // The acceleration rate used for acceleration calculation diff --git a/Marlin/stepper.cpp b/Marlin/stepper.cpp index 57fa3b5b7..63159a304 100644 --- a/Marlin/stepper.cpp +++ b/Marlin/stepper.cpp @@ -95,13 +95,13 @@ volatile unsigned long Stepper::step_events_completed = 0; // The number of step volatile unsigned char Stepper::eISR_Rate = 200; // Keep the ISR at a low rate until needed #if ENABLED(LIN_ADVANCE) - volatile int Stepper::e_steps[EXTRUDERS]; + volatile int Stepper::e_steps[E_STEPPERS]; int Stepper::extruder_advance_k = LIN_ADVANCE_K, Stepper::final_estep_rate, - Stepper::current_estep_rate[EXTRUDERS], - Stepper::current_adv_steps[EXTRUDERS]; + Stepper::current_estep_rate[E_STEPPERS], + Stepper::current_adv_steps[E_STEPPERS]; #else - long Stepper::e_steps[EXTRUDERS], + long Stepper::e_steps[E_STEPPERS], Stepper::final_advance = 0, Stepper::old_advance = 0, Stepper::advance_rate, @@ -114,6 +114,10 @@ long Stepper::acceleration_time, Stepper::deceleration_time; volatile long Stepper::count_position[NUM_AXIS] = { 0 }; volatile signed char Stepper::count_direction[NUM_AXIS] = { 1, 1, 1, 1 }; +#if ENABLED(MIXING_EXTRUDER) + long Stepper::counter_M[MIXING_STEPPERS]; +#endif + unsigned short Stepper::acc_step_rate; // needed for deceleration start point uint8_t Stepper::step_loops, Stepper::step_loops_nominal; unsigned short Stepper::OCR1A_nominal; @@ -179,7 +183,9 @@ volatile long Stepper::endstops_trigsteps[3]; #define Z_APPLY_STEP(v,Q) Z_STEP_WRITE(v) #endif -#define E_APPLY_STEP(v,Q) E_STEP_WRITE(v) +#if DISABLED(MIXING_EXTRUDER) + #define E_APPLY_STEP(v,Q) E_STEP_WRITE(v) +#endif // intRes = longIn1 * longIn2 >> 24 // uses: @@ -322,8 +328,15 @@ void Stepper::isr() { if (current_block) { current_block->busy = true; trapezoid_generator_reset(); - counter_X = -(current_block->step_event_count >> 1); - counter_Y = counter_Z = counter_E = counter_X; + + // Initialize Bresenham counters to 1/2 the ceiling + counter_X = counter_Y = counter_Z = counter_E = -(current_block->step_event_count >> 1); + + #if ENABLED(MIXING_EXTRUDER) + MIXING_STEPPERS_LOOP(i) + counter_M[i] = -(current_block->mix_event_count[i] >> 1); + #endif + step_events_completed = 0; #if ENABLED(Z_LATE_ENABLE) @@ -335,7 +348,7 @@ void Stepper::isr() { #endif // #if ENABLED(ADVANCE) - // e_steps[current_block->active_extruder] = 0; + // e_steps[TOOL_E_INDEX] = 0; // #endif } else { @@ -343,7 +356,7 @@ void Stepper::isr() { } } - if (current_block != NULL) { + if (current_block) { // Update endstops state, if enabled #if HAS_BED_PROBE @@ -363,25 +376,67 @@ void Stepper::isr() { counter_E += current_block->steps[E_AXIS]; if (counter_E > 0) { counter_E -= current_block->step_event_count; - count_position[E_AXIS] += count_direction[E_AXIS]; - e_steps[current_block->active_extruder] += motor_direction(E_AXIS) ? -1 : 1; + #if DISABLED(MIXING_EXTRUDER) + // Don't step E here for mixing extruder + count_position[E_AXIS] += count_direction[E_AXIS]; + e_steps[TOOL_E_INDEX] += motor_direction(E_AXIS) ? -1 : 1; + #endif } + #if ENABLED(MIXING_EXTRUDER) + // Step mixing steppers proportionally + long dir = motor_direction(E_AXIS) ? -1 : 1; + MIXING_STEPPERS_LOOP(j) { + counter_m[j] += current_block->steps[E_AXIS]; + if (counter_m[j] > 0) { + counter_m[j] -= current_block->mix_event_count[j]; + e_steps[j] += dir; + } + } + #endif + if (current_block->use_advance_lead) { - int delta_adv_steps; //Maybe a char would be enough? - delta_adv_steps = (((long)extruder_advance_k * current_estep_rate[current_block->active_extruder]) >> 9) - current_adv_steps[current_block->active_extruder]; - e_steps[current_block->active_extruder] += delta_adv_steps; - current_adv_steps[current_block->active_extruder] += delta_adv_steps; + int delta_adv_steps = (((long)extruder_advance_k * current_estep_rate[TOOL_E_INDEX]) >> 9) - current_adv_steps[TOOL_E_INDEX]; + #if ENABLED(MIXING_EXTRUDER) + // Mixing extruders apply advance lead proportionally + MIXING_STEPPERS_LOOP(j) { + int steps = delta_adv_steps * current_block->step_event_count / current_block->mix_event_count[j]; + e_steps[j] += steps; + current_adv_steps[j] += steps; + } + #else + // For most extruders, advance the single E stepper + e_steps[TOOL_E_INDEX] += delta_adv_steps; + current_adv_steps[TOOL_E_INDEX] += delta_adv_steps; + #endif } #elif ENABLED(ADVANCE) + // Always count the unified E axis counter_E += current_block->steps[E_AXIS]; if (counter_E > 0) { counter_E -= current_block->step_event_count; - e_steps[current_block->active_extruder] += motor_direction(E_AXIS) ? -1 : 1; + #if DISABLED(MIXING_EXTRUDER) + // Don't step E here for mixing extruder + e_steps[TOOL_E_INDEX] += motor_direction(E_AXIS) ? -1 : 1; + #endif } + #if ENABLED(MIXING_EXTRUDER) + + // Step mixing steppers proportionally + long dir = motor_direction(E_AXIS) ? -1 : 1; + MIXING_STEPPERS_LOOP(j) { + counter_m[j] += current_block->steps[E_AXIS]; + if (counter_m[j] > 0) { + counter_m[j] -= current_block->mix_event_count[j]; + e_steps[j] += dir; + } + } + + #endif // MIXING_EXTRUDER + #endif // ADVANCE or LIN_ADVANCE #define _COUNTER(AXIS) counter_## AXIS @@ -395,9 +450,22 @@ void Stepper::isr() { STEP_ADD(X); STEP_ADD(Y); STEP_ADD(Z); + #if DISABLED(ADVANCE) && DISABLED(LIN_ADVANCE) - STEP_ADD(E); - #endif + #if ENABLED(MIXING_EXTRUDER) + // Keep updating the single E axis + counter_E += current_block->steps[E_AXIS]; + // Tick the counters used for this mix + MIXING_STEPPERS_LOOP(j) { + // Step mixing steppers (proportionally) + counter_M[j] += current_block->steps[E_AXIS]; + // Step when the counter goes over zero + if (counter_M[j] > 0) En_STEP_WRITE(j, !INVERT_E_STEP_PIN); + } + #else // !MIXING_EXTRUDER + STEP_ADD(E); + #endif + #endif // !ADVANCE && !LIN_ADVANCE #define STEP_IF_COUNTER(AXIS) \ if (_COUNTER(AXIS) > 0) { \ @@ -409,17 +477,32 @@ void Stepper::isr() { STEP_IF_COUNTER(X); STEP_IF_COUNTER(Y); STEP_IF_COUNTER(Z); + #if DISABLED(ADVANCE) && DISABLED(LIN_ADVANCE) - STEP_IF_COUNTER(E); - #endif + #if ENABLED(MIXING_EXTRUDER) + // Always step the single E axis + if (counter_E > 0) { + counter_E -= current_block->step_event_count; + count_position[E_AXIS] += count_direction[E_AXIS]; + } + MIXING_STEPPERS_LOOP(j) { + if (counter_M[j] > 0) { + counter_M[j] -= current_block->mix_event_count[j]; + En_STEP_WRITE(j, INVERT_E_STEP_PIN); + } + } + #else // !MIXING_EXTRUDER + STEP_IF_COUNTER(E); + #endif + #endif // !ADVANCE && !LIN_ADVANCE step_events_completed++; if (step_events_completed >= current_block->step_event_count) break; } - #if ENABLED(LIN_ADVANCE) + #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) // If we have esteps to execute, fire the next ISR "now" - if (e_steps[current_block->active_extruder]) OCR0A = TCNT0 + 2; + if (e_steps[TOOL_E_INDEX]) OCR0A = TCNT0 + 2; #endif // Calculate new timer value @@ -440,21 +523,41 @@ void Stepper::isr() { #if ENABLED(LIN_ADVANCE) if (current_block->use_advance_lead) - current_estep_rate[current_block->active_extruder] = ((unsigned long)acc_step_rate * current_block->e_speed_multiplier8) >> 8; + current_estep_rate[TOOL_E_INDEX] = ((unsigned long)acc_step_rate * current_block->e_speed_multiplier8) >> 8; + + if (current_block->use_advance_lead) { + #if ENABLED(MIXING_EXTRUDER) + MIXING_STEPPERS_LOOP(j) + current_estep_rate[j] = ((unsigned long)acc_step_rate * current_block->e_speed_multiplier8 * current_block->step_event_count / current_block->mix_event_count[j]) >> 8; + #else + current_estep_rate[TOOL_E_INDEX] = ((unsigned long)acc_step_rate * current_block->e_speed_multiplier8) >> 8; + #endif + } #elif ENABLED(ADVANCE) advance += advance_rate * step_loops; //NOLESS(advance, current_block->advance); + long advance_whole = advance >> 8, + advance_factor = advance_whole - old_advance; + // Do E steps + advance steps - e_steps[current_block->active_extruder] += ((advance >> 8) - old_advance); - old_advance = advance >> 8; + #if ENABLED(MIXING_EXTRUDER) + // ...for mixing steppers proportionally + MIXING_STEPPERS_LOOP(j) + e_steps[j] += advance_factor * current_block->step_event_count / current_block->mix_event_count[j]; + #else + // ...for the active extruder + e_steps[TOOL_E_INDEX] += advance_factor; + #endif + + old_advance = advance_whole; #endif // ADVANCE or LIN_ADVANCE #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) - eISR_Rate = (timer >> 2) * step_loops / abs(e_steps[current_block->active_extruder]); + eISR_Rate = (timer >> 2) * step_loops / abs(e_steps[TOOL_E_INDEX]); #endif } else if (step_events_completed > (unsigned long)current_block->decelerate_after) { @@ -474,8 +577,14 @@ void Stepper::isr() { #if ENABLED(LIN_ADVANCE) - if (current_block->use_advance_lead) - current_estep_rate[current_block->active_extruder] = ((unsigned long)step_rate * current_block->e_speed_multiplier8) >> 8; + if (current_block->use_advance_lead) { + #if ENABLED(MIXING_EXTRUDER) + MIXING_STEPPERS_LOOP(j) + current_estep_rate[j] = ((unsigned long)step_rate * current_block->e_speed_multiplier8 * current_block->step_event_count / current_block->mix_event_count[j]) >> 8; + #else + current_estep_rate[TOOL_E_INDEX] = ((unsigned long)step_rate * current_block->e_speed_multiplier8) >> 8; + #endif + } #elif ENABLED(ADVANCE) @@ -483,14 +592,22 @@ void Stepper::isr() { NOLESS(advance, final_advance); // Do E steps + advance steps - uint32_t advance_whole = advance >> 8; - e_steps[current_block->active_extruder] += advance_whole - old_advance; + long advance_whole = advance >> 8, + advance_factor = advance_whole - old_advance; + + #if ENABLED(MIXING_EXTRUDER) + MIXING_STEPPERS_LOOP(j) + e_steps[j] += advance_factor * current_block->step_event_count / current_block->mix_event_count[j]; + #else + e_steps[TOOL_E_INDEX] += advance_factor; + #endif + old_advance = advance_whole; #endif // ADVANCE or LIN_ADVANCE #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) - eISR_Rate = (timer >> 2) * step_loops / abs(e_steps[current_block->active_extruder]); + eISR_Rate = (timer >> 2) * step_loops / abs(e_steps[TOOL_E_INDEX]); #endif } else { @@ -498,9 +615,9 @@ void Stepper::isr() { #if ENABLED(LIN_ADVANCE) if (current_block->use_advance_lead) - current_estep_rate[current_block->active_extruder] = final_estep_rate; + current_estep_rate[TOOL_E_INDEX] = final_estep_rate; - eISR_Rate = (OCR1A_nominal >> 2) * step_loops_nominal / abs(e_steps[current_block->active_extruder]); + eISR_Rate = (OCR1A_nominal >> 2) * step_loops_nominal / abs(e_steps[TOOL_E_INDEX]); #endif @@ -537,7 +654,7 @@ void Stepper::isr() { E## INDEX ##_DIR_WRITE(INVERT_E## INDEX ##_DIR); \ e_steps[INDEX]++; \ } \ - else if (e_steps[INDEX] > 0) { \ + else { \ E## INDEX ##_DIR_WRITE(!INVERT_E## INDEX ##_DIR); \ e_steps[INDEX]--; \ } \ @@ -547,11 +664,11 @@ void Stepper::isr() { // Step all E steppers that have steps for (uint8_t i = 0; i < step_loops; i++) { STEP_E_ONCE(0); - #if EXTRUDERS > 1 + #if E_STEPPERS > 1 STEP_E_ONCE(1); - #if EXTRUDERS > 2 + #if E_STEPPERS > 2 STEP_E_ONCE(2); - #if EXTRUDERS > 3 + #if E_STEPPERS > 3 STEP_E_ONCE(3); #endif #endif @@ -730,18 +847,12 @@ void Stepper::init() { #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) - #if ENABLED(LIN_ADVANCE) - - for (int i = 0; i < EXTRUDERS; i++) { - e_steps[i] = 0; + for (int i = 0; i < E_STEPPERS; i++) { + e_steps[i] = 0; + #if ENABLED(LIN_ADVANCE) current_adv_steps[i] = 0; - } - - #elif ENABLED(ADVANCE) - - for (uint8_t i = 0; i < EXTRUDERS; i++) e_steps[i] = 0; - - #endif + #endif + } #if defined(TCCR0A) && defined(WGM01) CBI(TCCR0A, WGM01); diff --git a/Marlin/stepper.h b/Marlin/stepper.h index 6ad55216d..45c8753aa 100644 --- a/Marlin/stepper.h +++ b/Marlin/stepper.h @@ -107,15 +107,15 @@ class Stepper { static unsigned char old_OCR0A; static volatile unsigned char eISR_Rate; #if ENABLED(LIN_ADVANCE) - static volatile int e_steps[EXTRUDERS]; + static volatile int e_steps[E_STEPPERS]; static int extruder_advance_k; static int final_estep_rate; - static int current_estep_rate[EXTRUDERS]; // Actual extruder speed [steps/s] - static int current_adv_steps[EXTRUDERS]; // The amount of current added esteps due to advance. + static int current_estep_rate[E_STEPPERS]; // Actual extruder speed [steps/s] + static int current_adv_steps[E_STEPPERS]; // The amount of current added esteps due to advance. // i.e., the current amount of pressure applied // to the spring (=filament). #else - static long e_steps[EXTRUDERS]; + static long e_steps[E_STEPPERS]; static long advance_rate, advance, final_advance; static long old_advance; #endif @@ -147,6 +147,16 @@ class Stepper { // static volatile signed char count_direction[NUM_AXIS]; + // + // Mixing extruder mix counters + // + #if ENABLED(MIXING_EXTRUDER) + static long counter_M[MIXING_STEPPERS]; + #define MIXING_STEPPERS_LOOP(VAR) \ + for (uint8_t VAR = 0; VAR < MIXING_STEPPERS; VAR++) \ + if (current_block->mix_event_count[VAR]) + #endif + public: // @@ -315,12 +325,25 @@ class Stepper { } #if ENABLED(ADVANCE) + advance = current_block->initial_advance; final_advance = current_block->final_advance; + // Do E steps + advance steps - e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); - old_advance = advance >>8; + #if ENABLED(MIXING_EXTRUDER) + long advance_factor = (advance >> 8) - old_advance; + // ...for mixing steppers proportionally + MIXING_STEPPERS_LOOP(j) + e_steps[j] += advance_factor * current_block->step_event_count / current_block->mix_event_count[j]; + #else + // ...for the active extruder + e_steps[TOOL_E_INDEX] += ((advance >> 8) - old_advance); + #endif + + old_advance = advance >> 8; + #endif + deceleration_time = 0; // step_rate to timer interval OCR1A_nominal = calc_timer(current_block->nominal_rate); diff --git a/Marlin/stepper_indirection.h b/Marlin/stepper_indirection.h index 56c15060e..65fde23c1 100644 --- a/Marlin/stepper_indirection.h +++ b/Marlin/stepper_indirection.h @@ -182,26 +182,42 @@ #define E3_ENABLE_WRITE(STATE) WRITE(E3_ENABLE_PIN,STATE) #define E3_ENABLE_READ READ(E3_ENABLE_PIN) -#if EXTRUDERS > 3 - #define E_STEP_WRITE(v) {switch(current_block->active_extruder){case 3:E3_STEP_WRITE(v);break;case 2:E2_STEP_WRITE(v);break;case 1:E1_STEP_WRITE(v);break;default:E0_STEP_WRITE(v);}} - #define NORM_E_DIR() {switch(current_block->active_extruder){case 3:E3_DIR_WRITE(!INVERT_E3_DIR);break;case 2:E2_DIR_WRITE(!INVERT_E2_DIR);break;case 1:E1_DIR_WRITE(!INVERT_E1_DIR);break;default:E0_DIR_WRITE(!INVERT_E0_DIR);}} - #define REV_E_DIR() {switch(current_block->active_extruder){case 3:E3_DIR_WRITE(INVERT_E3_DIR);break;case 2:E2_DIR_WRITE(INVERT_E2_DIR);break;case 1:E1_DIR_WRITE(INVERT_E1_DIR);break;default:E0_DIR_WRITE(INVERT_E0_DIR);}} +#if ENABLED(SWITCHING_EXTRUDER) + #define E_STEP_WRITE(v) E0_STEP_WRITE(v) + #define NORM_E_DIR() E0_DIR_WRITE(current_block->active_extruder ? INVERT_E0_DIR : !INVERT_E0_DIR) + #define REV_E_DIR() E0_DIR_WRITE(current_block->active_extruder ? !INVERT_E0_DIR : INVERT_E0_DIR) +#elif EXTRUDERS > 3 + #define E_STEP_WRITE(v) { switch (current_block->active_extruder) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); break; case 3: E3_STEP_WRITE(v); } } + #define NORM_E_DIR() { switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(!INVERT_E3_DIR); } } + #define REV_E_DIR() { switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(INVERT_E3_DIR); } } #elif EXTRUDERS > 2 - #define E_STEP_WRITE(v) {switch(current_block->active_extruder){case 2:E2_STEP_WRITE(v);break;case 1:E1_STEP_WRITE(v);break;default:E0_STEP_WRITE(v);}} - #define NORM_E_DIR() {switch(current_block->active_extruder){case 2:E2_DIR_WRITE(!INVERT_E2_DIR);break;case 1:E1_DIR_WRITE(!INVERT_E1_DIR);break;default:E0_DIR_WRITE(!INVERT_E0_DIR);}} - #define REV_E_DIR() {switch(current_block->active_extruder){case 2:E2_DIR_WRITE(INVERT_E2_DIR);break;case 1:E1_DIR_WRITE(INVERT_E1_DIR);break;default:E0_DIR_WRITE(INVERT_E0_DIR);}} + #define E_STEP_WRITE(v) { switch (current_block->active_extruder) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); } } + #define NORM_E_DIR() { switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); } } + #define REV_E_DIR() { switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(INVERT_E2_DIR); } } #elif EXTRUDERS > 1 - #define _E_STEP_WRITE(v) {if(current_block->active_extruder==1){E1_STEP_WRITE(v);}else{E0_STEP_WRITE(v);}} - #define _NORM_E_DIR() {if(current_block->active_extruder==1){E1_DIR_WRITE(!INVERT_E1_DIR);}else{E0_DIR_WRITE(!INVERT_E0_DIR);}} - #define _REV_E_DIR() {if(current_block->active_extruder==1){E1_DIR_WRITE(INVERT_E1_DIR);}else{E0_DIR_WRITE(INVERT_E0_DIR);}} #if DISABLED(DUAL_X_CARRIAGE) - #define E_STEP_WRITE(v) _E_STEP_WRITE(v) - #define NORM_E_DIR() _NORM_E_DIR() - #define REV_E_DIR() _REV_E_DIR() + #define E_STEP_WRITE(v) { if (current_block->active_extruder == 0) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } } + #define NORM_E_DIR() { if (current_block->active_extruder == 0) { E0_DIR_WRITE(!INVERT_E0_DIR); } else { E1_DIR_WRITE(!INVERT_E1_DIR); } } + #define REV_E_DIR() { if (current_block->active_extruder == 0) { E0_DIR_WRITE(INVERT_E0_DIR); } else { E1_DIR_WRITE(INVERT_E1_DIR); } } + #else + #define E_STEP_WRITE(v) { if (extruder_duplication_enabled) { E0_STEP_WRITE(v); E1_STEP_WRITE(v); } else if (current_block->active_extruder == 0) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } } + #define NORM_E_DIR() { if (extruder_duplication_enabled) { E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); } else if (current_block->active_extruder == 0) { E0_DIR_WRITE(!INVERT_E0_DIR); } else { E1_DIR_WRITE(!INVERT_E1_DIR); } } + #define REV_E_DIR() { if (extruder_duplication_enabled) { E0_DIR_WRITE(INVERT_E0_DIR); E1_DIR_WRITE(INVERT_E1_DIR); } else if (current_block->active_extruder == 0) { E0_DIR_WRITE(INVERT_E0_DIR); } else { E1_DIR_WRITE(INVERT_E1_DIR); } } + #endif +#elif ENABLED(MIXING_EXTRUDER) + #define E_STEP_WRITE(v) NOOP /* not used for mixing extruders! */ + #if MIXING_STEPPERS > 3 + #define En_STEP_WRITE(n,v) { switch (n) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); break; case 3: E3_STEP_WRITE(v); } } + #define NORM_E_DIR() { E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); E2_DIR_WRITE(!INVERT_E2_DIR); E3_DIR_WRITE(!INVERT_E3_DIR); } + #define REV_E_DIR() { E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); E2_DIR_WRITE( INVERT_E2_DIR); E3_DIR_WRITE( INVERT_E3_DIR); } + #elif MIXING_STEPPERS > 2 + #define En_STEP_WRITE(n,v) { switch (n) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); } } + #define NORM_E_DIR() { E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); E2_DIR_WRITE(!INVERT_E2_DIR); } + #define REV_E_DIR() { E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); E2_DIR_WRITE( INVERT_E2_DIR); } #else - #define E_STEP_WRITE(v) {if(extruder_duplication_enabled){E0_STEP_WRITE(v);E1_STEP_WRITE(v);}else _E_STEP_WRITE(v);} - #define NORM_E_DIR() {if(extruder_duplication_enabled){E0_DIR_WRITE(!INVERT_E0_DIR);E1_DIR_WRITE(!INVERT_E1_DIR);}else _NORM_E_DIR();} - #define REV_E_DIR() {if(extruder_duplication_enabled){E0_DIR_WRITE(INVERT_E0_DIR);E1_DIR_WRITE(INVERT_E1_DIR);}else _REV_E_DIR();} + #define En_STEP_WRITE(n,v) { switch (n) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); } } + #define NORM_E_DIR() { E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); } + #define REV_E_DIR() { E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); } #endif #else #define E_STEP_WRITE(v) E0_STEP_WRITE(v) diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index 543724814..bd8d6c393 100755 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -1365,7 +1365,7 @@ void kill_screen(const char* lcd_msg) { #endif static void lcd_move_z() { _lcd_move_xyz(PSTR(MSG_MOVE_Z), Z_AXIS, sw_endstop_min[Z_AXIS], sw_endstop_max[Z_AXIS]); } static void lcd_move_e( - #if EXTRUDERS > 1 + #if E_STEPPERS > 1 int8_t eindex = -1 #endif ) { @@ -1375,7 +1375,7 @@ void kill_screen(const char* lcd_msg) { current_position[E_AXIS] += float((int32_t)encoderPosition) * move_menu_scale; encoderPosition = 0; manual_move_to_current(E_AXIS - #if EXTRUDERS > 1 + #if E_STEPPERS > 1 , eindex #endif ); @@ -1383,34 +1383,34 @@ void kill_screen(const char* lcd_msg) { } if (lcdDrawUpdate) { PGM_P pos_label; - #if EXTRUDERS == 1 + #if E_STEPPERS == 1 pos_label = PSTR(MSG_MOVE_E); #else switch (eindex) { default: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E1); break; case 1: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E2); break; - #if EXTRUDERS > 2 + #if E_STEPPERS > 2 case 2: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E3); break; - #if EXTRUDERS > 3 + #if E_STEPPERS > 3 case 3: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E4); break; - #endif //EXTRUDERS > 3 - #endif //EXTRUDERS > 2 + #endif + #endif } - #endif //EXTRUDERS > 1 + #endif lcd_implementation_drawedit(pos_label, ftostr41sign(current_position[E_AXIS])); } } - #if EXTRUDERS > 1 + #if E_STEPPERS > 1 static void lcd_move_e0() { lcd_move_e(0); } static void lcd_move_e1() { lcd_move_e(1); } - #if EXTRUDERS > 2 + #if E_STEPPERS > 2 static void lcd_move_e2() { lcd_move_e(2); } - #if EXTRUDERS > 3 + #if E_STEPPERS > 3 static void lcd_move_e3() { lcd_move_e(3); } #endif #endif - #endif // EXTRUDERS > 1 + #endif /** * @@ -1432,20 +1432,29 @@ void kill_screen(const char* lcd_msg) { MENU_ITEM(submenu, MSG_MOVE_X, lcd_move_x); MENU_ITEM(submenu, MSG_MOVE_Y, lcd_move_y); } + if (move_menu_scale < 10.0) { if (_MOVE_XYZ_ALLOWED) MENU_ITEM(submenu, MSG_MOVE_Z, lcd_move_z); - #if EXTRUDERS == 1 + + #if ENABLED(SWITCHING_EXTRUDER) + if (active_extruder) + MENU_ITEM(gcode, MSG_SELECT MSG_E1, PSTR("T0")); + else + MENU_ITEM(gcode, MSG_SELECT MSG_E2, PSTR("T1")); + #endif + + #if E_STEPPERS == 1 MENU_ITEM(submenu, MSG_MOVE_E, lcd_move_e); #else MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E1, lcd_move_e0); MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E2, lcd_move_e1); - #if EXTRUDERS > 2 + #if E_STEPPERS > 2 MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E3, lcd_move_e2); - #if EXTRUDERS > 3 + #if E_STEPPERS > 3 MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E4, lcd_move_e3); #endif #endif - #endif // EXTRUDERS > 1 + #endif } END_MENU(); }