Prusa MINI Firmware overview
tool_change.cpp File Reference
#include "../inc/MarlinConfigPre.h"
#include "tool_change.h"
#include "probe.h"
#include "motion.h"
#include "planner.h"
#include "temperature.h"
#include "../Marlin.h"
#include "../core/debug_out.h"
#include "servo.h"
#include "../feature/bedlevel/bedlevel.h"

Macros

#define DEBUG_OUT   ENABLED(DEBUG_LEVELING_FEATURE)
 

Functions

void _line_to_current (const AxisEnum fr_axis, const float fscale=1)
 
void slow_line_to_current (const AxisEnum fr_axis)
 
void fast_line_to_current (const AxisEnum fr_axis)
 
void tool_change (const uint8_t new_tool, bool no_move)
 

Macro Definition Documentation

◆ DEBUG_OUT

#define DEBUG_OUT   ENABLED(DEBUG_LEVELING_FEATURE)

Marlin 3D Printer Firmware Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]

Based on Sprinter and grbl. Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Function Documentation

◆ _line_to_current()

void _line_to_current ( const AxisEnum  fr_axis,
const float  fscale = 1 
)
136  {
138 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ slow_line_to_current()

void slow_line_to_current ( const AxisEnum  fr_axis)
139 { _line_to_current(fr_axis, 0.5f); }
Here is the call graph for this function:

◆ fast_line_to_current()

void fast_line_to_current ( const AxisEnum  fr_axis)
140 { _line_to_current(fr_axis); }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ tool_change()

void tool_change ( const uint8_t  new_tool,
bool  no_move 
)

Perform a tool-change, which may result in moving the previous tool out of the way and the new tool into place.

771  {
772 
773  #if ENABLED(MAGNETIC_SWITCHING_TOOLHEAD)
774  if (new_tool == active_extruder) return;
775  #endif
776 
777  #if ENABLED(MIXING_EXTRUDER)
778 
779  UNUSED(no_move);
780 
781  if (new_tool >= MIXING_VIRTUAL_TOOLS)
782  return invalid_extruder_error(new_tool);
783 
784  #if MIXING_VIRTUAL_TOOLS > 1
785  // T0-Tnnn: Switch virtual tool by changing the index to the mix
786  mixer.T(new_tool);
787  #endif
788 
789  #elif ENABLED(PRUSA_MMU2)
790 
791  UNUSED(no_move);
792 
793  mmu2.tool_change(new_tool);
794 
795  #elif EXTRUDERS == 0
796 
797  // Nothing to do
798  UNUSED(new_tool); UNUSED(no_move);
799 
800  #elif EXTRUDERS < 2
801 
802  UNUSED(no_move);
803 
804  if (new_tool) invalid_extruder_error(new_tool);
805  return;
806 
807  #else // EXTRUDERS > 1
808 
810 
811  #if ENABLED(DUAL_X_CARRIAGE) // Only T0 allowed if the Printer is in DXC_DUPLICATION_MODE or DXC_MIRRORED_MODE
812  if (new_tool != 0 && dxc_is_duplicating())
813  return invalid_extruder_error(new_tool);
814  #endif
815 
816  if (new_tool >= EXTRUDERS)
817  return invalid_extruder_error(new_tool);
818 
819  if (!no_move && !all_axes_homed()) {
820  no_move = true;
821  if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("No move (not homed)");
822  }
823 
824  #if HAS_LCD_MENU
825  ui.return_to_status();
826  #endif
827 
828  #if ENABLED(DUAL_X_CARRIAGE)
829  const bool idex_full_control = dual_x_carriage_mode == DXC_FULL_CONTROL_MODE;
830  #else
831  constexpr bool idex_full_control = false;
832  #endif
833 
834  const uint8_t old_tool = active_extruder;
835  const bool can_move_away = !no_move && !idex_full_control;
836 
837  #if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
838  const bool should_swap = can_move_away && toolchange_settings.swap_length;
839  #if ENABLED(PREVENT_COLD_EXTRUSION)
840  const bool too_cold = !DEBUGGING(DRYRUN) && (thermalManager.targetTooColdToExtrude(old_tool) || thermalManager.targetTooColdToExtrude(new_tool));
841  #else
842  constexpr bool too_cold = false;
843  #endif
844  if (should_swap) {
845  if (too_cold) {
847  #if ENABLED(SINGLENOZZLE)
848  active_extruder = new_tool;
849  return;
850  #endif
851  }
852  else {
853  #if ENABLED(ADVANCED_PAUSE_FEATURE)
854  do_pause_e_move(-toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.retract_speed));
855  #else
856  current_position.e -= toolchange_settings.swap_length / planner.e_factor[old_tool];
857  planner.buffer_line(current_position, MMM_TO_MMS(toolchange_settings.retract_speed), old_tool);
859  #endif
860  }
861  }
862  #endif // TOOLCHANGE_FILAMENT_SWAP
863 
864  #if HAS_LEVELING
865  // Set current position to the physical position
867  #endif
868 
869  if (new_tool != old_tool) {
870 
871  #if SWITCHING_NOZZLE_TWO_SERVOS
872  raise_nozzle(old_tool);
873  #endif
874 
876 
877  #if HAS_SOFTWARE_ENDSTOPS
878  #if HAS_HOTEND_OFFSET
879  #define _EXT_ARGS , old_tool, new_tool
880  #else
881  #define _EXT_ARGS
882  #endif
883  update_software_endstops(X_AXIS _EXT_ARGS);
884  #if DISABLED(DUAL_X_CARRIAGE)
885  update_software_endstops(Y_AXIS _EXT_ARGS);
886  update_software_endstops(Z_AXIS _EXT_ARGS);
887  #endif
888  #endif
889 
891 
892  #if DISABLED(SWITCHING_NOZZLE)
893  if (can_move_away) {
894  // Do a small lift to avoid the workpiece in the move back (below)
895  current_position.z += toolchange_settings.z_raise;
896  #if HAS_SOFTWARE_ENDSTOPS
898  #endif
900  #if ENABLED(TOOLCHANGE_PARK)
901  current_position = toolchange_settings.change_point;
902  #endif
905  }
906  #endif
907 
908  #if HAS_HOTEND_OFFSET
909  xyz_pos_t diff = hotend_offset[new_tool] - hotend_offset[old_tool];
910  #if ENABLED(DUAL_X_CARRIAGE)
911  diff.x = 0;
912  #endif
913  #else
914  constexpr xyz_pos_t diff{0};
915  #endif
916 
917  #if ENABLED(DUAL_X_CARRIAGE)
918  dualx_tool_change(new_tool, no_move);
919  #elif ENABLED(PARKING_EXTRUDER) // Dual Parking extruder
920  parking_extruder_tool_change(new_tool, no_move);
921  #elif ENABLED(MAGNETIC_PARKING_EXTRUDER) // Magnetic Parking extruder
922  magnetic_parking_extruder_tool_change(new_tool);
923  #elif ENABLED(SWITCHING_TOOLHEAD) // Switching Toolhead
924  switching_toolhead_tool_change(new_tool, no_move);
925  #elif ENABLED(MAGNETIC_SWITCHING_TOOLHEAD) // Magnetic Switching Toolhead
926  magnetic_switching_toolhead_tool_change(new_tool, no_move);
927  #elif ENABLED(ELECTROMAGNETIC_SWITCHING_TOOLHEAD) // Magnetic Switching ToolChanger
928  em_switching_toolhead_tool_change(new_tool, no_move);
929  #elif ENABLED(SWITCHING_NOZZLE) && !SWITCHING_NOZZLE_TWO_SERVOS // Switching Nozzle (single servo)
930  // Raise by a configured distance to avoid workpiece, except with
931  // SWITCHING_NOZZLE_TWO_SERVOS, as both nozzles will lift instead.
932  current_position.z += _MAX(-diff.z, 0.0) + toolchange_settings.z_raise;
933  #if HAS_SOFTWARE_ENDSTOPS
935  #endif
936  if (!no_move) fast_line_to_current(Z_AXIS);
937  move_nozzle_servo(new_tool);
938  #endif
939 
940  #if DISABLED(DUAL_X_CARRIAGE)
941  active_extruder = new_tool; // Set the new active extruder
942  #endif
943 
944  // The newly-selected extruder XYZ is actually at...
945  if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Offset Tool XY by { ", diff.x, ", ", diff.y, ", ", diff.z, " }");
946  current_position += diff;
947 
948  // Tell the planner the new "current position"
950 
951  #if ENABLED(DELTA)
952  //LOOP_XYZ(i) update_software_endstops(i); // or modify the constrain function
953  const bool safe_to_move = current_position.z < delta_clip_start_height - 1;
954  #else
955  constexpr bool safe_to_move = true;
956  #endif
957 
958  // Return to position and lower again
959  if (safe_to_move && !no_move && IsRunning()) {
960 
961  #if ENABLED(SINGLENOZZLE)
962  #if FAN_COUNT > 0
963  singlenozzle_fan_speed[old_tool] = thermalManager.fan_speed[0];
964  thermalManager.fan_speed[0] = singlenozzle_fan_speed[new_tool];
965  #endif
966 
967  singlenozzle_temp[old_tool] = thermalManager.temp_hotend[0].target;
968  if (singlenozzle_temp[new_tool] && singlenozzle_temp[new_tool] != singlenozzle_temp[old_tool]) {
969  thermalManager.setTargetHotend(singlenozzle_temp[new_tool], 0);
970  #if HAS_DISPLAY
971  thermalManager.set_heating_message(0);
972  #endif
973  (void)thermalManager.wait_for_hotend(0, false); // Wait for heating or cooling
974  }
975  #endif
976 
977  #if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
978  if (should_swap && !too_cold) {
979  #if ENABLED(ADVANCED_PAUSE_FEATURE)
980  do_pause_e_move(toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.prime_speed));
981  do_pause_e_move(toolchange_settings.extra_prime, ADVANCED_PAUSE_PURGE_FEEDRATE);
982  #else
983  current_position.e += toolchange_settings.swap_length / planner.e_factor[new_tool];
984  planner.buffer_line(current_position, MMM_TO_MMS(toolchange_settings.prime_speed), new_tool);
985  current_position.e += toolchange_settings.extra_prime / planner.e_factor[new_tool];
986  planner.buffer_line(current_position, MMM_TO_MMS(toolchange_settings.prime_speed * 0.2f), new_tool);
987  #endif
989  planner.set_e_position_mm((destination.e = current_position.e = current_position.e - (TOOLCHANGE_FIL_EXTRA_PRIME)));
990  }
991  #endif
992 
993  // Prevent a move outside physical bounds
994  #if ENABLED(MAGNETIC_SWITCHING_TOOLHEAD)
995  // If the original position is within tool store area, go to X origin at once
996  if (destination.y < SWITCHING_TOOLHEAD_Y_POS + SWITCHING_TOOLHEAD_Y_CLEAR) {
997  current_position.x = 0;
1000  }
1001  #else
1003  #endif
1004 
1005  // Should the nozzle move back to the old position?
1006  if (can_move_away) {
1007  #if ENABLED(TOOLCHANGE_NO_RETURN)
1008  // Just move back down
1009  if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Move back Z only");
1011  #else
1012  // Move back to the original (or adjusted) position
1013  if (DEBUGGING(LEVELING)) DEBUG_POS("Move back", destination);
1015  #endif
1016  }
1017  else if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Move back skipped");
1018 
1019  #if ENABLED(DUAL_X_CARRIAGE)
1020  active_extruder_parked = false;
1021  #endif
1022  }
1023  #if ENABLED(SWITCHING_NOZZLE)
1024  else {
1025  // Move back down. (Including when the new tool is higher.)
1027  }
1028  #endif
1029 
1030  #if ENABLED(PRUSA_MMU2)
1031  mmu2.tool_change(new_tool);
1032  #endif
1033 
1034  #if SWITCHING_NOZZLE_TWO_SERVOS
1035  lower_nozzle(new_tool);
1036  #endif
1037 
1038  } // (new_tool != old_tool)
1039 
1040  planner.synchronize();
1041 
1042  #if ENABLED(EXT_SOLENOID) && DISABLED(PARKING_EXTRUDER)
1045  #endif
1046 
1047  #if ENABLED(MK2_MULTIPLEXER)
1048  if (new_tool >= E_STEPPERS) return invalid_extruder_error(new_tool);
1049  select_multiplexed_stepper(new_tool);
1050  #endif
1051 
1052  #if DO_SWITCH_EXTRUDER
1053  planner.synchronize();
1054  move_extruder_servo(active_extruder);
1055  #endif
1056 
1057  #if HAS_FANMUX
1059  #endif
1060 
1063 
1064  #endif // EXTRUDERS > 1
1065 }
Here is the call graph for this function:
delta_clip_start_height
float delta_clip_start_height
XYZval::z
T z
Definition: types.h:286
enable_solenoid_on_active_extruder
void enable_solenoid_on_active_extruder()
Definition: solenoid.cpp:77
MIXING_VIRTUAL_TOOLS
#define MIXING_VIRTUAL_TOOLS
Definition: mixing.h:47
XYZEval::z
T z
Definition: types.h:383
Planner::synchronize
static void synchronize()
Definition: planner.cpp:1556
axis_limits_t::max
xyz_pos_t max
Definition: motion.h:139
mixer
Mixer mixer
XYZval::x
T x
Definition: types.h:286
planner_settings_t::max_feedrate_mm_s
feedRate_t max_feedrate_mm_s[XYZE_N]
Definition: planner.h:182
X_AXIS
Definition: types.h:37
DEBUG_ECHOLNPGM
#define DEBUG_ECHOLNPGM(...)
Definition: debug_out.h:79
_MAX
#define _MAX(V...)
Definition: macros.h:346
do_blocking_move_to_z
void do_blocking_move_to_z(const float &rz, const feedRate_t &fr_mm_s)
Definition: motion.cpp:450
XYZEval::e
T e
Definition: types.h:383
destination
xyze_pos_t destination
Definition: motion.cpp:110
DEBUG_ECHOLNPAIR
#define DEBUG_ECHOLNPAIR(...)
Definition: debug_out.h:82
SERIAL_ECHO_START
#define SERIAL_ECHO_START()
Definition: serial.h:179
mmu2
MMU2 mmu2
_line_to_current
void _line_to_current(const AxisEnum fr_axis, const float fscale=1)
Definition: tool_change.cpp:136
IsRunning
bool IsRunning()
Definition: Marlin.h:331
NOMORE
#define NOMORE(v, n)
Definition: macros.h:133
disable_all_solenoids
void disable_all_solenoids()
Definition: solenoid.cpp:79
SERIAL_ECHO_MSG
#define SERIAL_ECHO_MSG(S)
Definition: serial.h:183
current_position
xyze_pos_t current_position
Definition: motion.cpp:102
Temperature::targetTooColdToExtrude
static FORCE_INLINE bool targetTooColdToExtrude(const uint8_t)
Definition: temperature.h:315
line_to_current_position
void line_to_current_position(const feedRate_t &fr_mm_s)
Definition: motion.cpp:285
do_blocking_move_to
void do_blocking_move_to(const float rx, const float ry, const float rz, const feedRate_t &fr_mm_s)
Definition: motion.cpp:344
sync_plan_position
void sync_plan_position()
Definition: motion.cpp:216
REMEMBER
#define REMEMBER(N, X, V...)
Definition: utility.h:76
void
void
Definition: png.h:1083
XYZEval::x
T x
Definition: types.h:383
XY_PROBE_FEEDRATE_MM_S
#define XY_PROBE_FEEDRATE_MM_S
Definition: motion.h:77
fast_line_to_current
void fast_line_to_current(const AxisEnum fr_axis)
Definition: tool_change.cpp:140
Planner::buffer_line
static bool buffer_line(const float &rx, const float &ry, const float &rz, const float &e, const feedRate_t &fr_mm_s, const uint8_t extruder, const float millimeters=0.0)
Definition: planner.cpp:2663
feedrate_mm_s
feedRate_t feedrate_mm_s
Definition: motion.cpp:138
SERIAL_ECHOLNPAIR
#define SERIAL_ECHOLNPAIR(V...)
Definition: serial.h:144
DEBUG_POS
#define DEBUG_POS(...)
Definition: debug_out.h:87
uint8_t
const uint8_t[]
Definition: 404_html.c:3
ui
MarlinUI ui
update_software_endstops
void update_software_endstops(const AxisEnum axis)
Definition: motion.cpp:503
E_STEPPERS
#define E_STEPPERS
Definition: Conditionals_LCD.h:429
XYZval::y
T y
Definition: types.h:286
select_multiplexed_stepper
void select_multiplexed_stepper(const uint8_t e)
Y_AXIS
Definition: types.h:38
Mixer::T
static FORCE_INLINE void T(const uint_fast8_t c)
Definition: mixing.h:113
hotend_offset
constexpr xyz_pos_t hotend_offset[1]
Definition: motion.h:136
Planner::set_e_position_mm
static void set_e_position_mm(const float &e)
Definition: planner.cpp:2764
Z_AXIS
Definition: types.h:39
EXTRUDERS
#define EXTRUDERS
Definition: Configuration_A3ides_2209_MINI.h:148
apply_motion_limits
void apply_motion_limits(xyz_pos_t &target)
Definition: motion.cpp:589
DEBUGGING
#define DEBUGGING(F)
Definition: serial.h:47
MSG_ERR_HOTEND_TOO_COLD
#define MSG_ERR_HOTEND_TOO_COLD
Definition: language.h:244
MMM_TO_MMS
#define MMM_TO_MMS(MM_M)
Definition: types.h:83
XYZval< float >
Planner::settings
static planner_settings_t settings
Definition: planner.h:251
MSG_ACTIVE_EXTRUDER
#define MSG_ACTIVE_EXTRUDER
Definition: language.h:183
soft_endstop
axis_limits_t soft_endstop
Definition: motion.cpp:489
thermalManager
Temperature thermalManager
Definition: temperature.cpp:89
UNUSED
#define UNUSED(X)
Definition: stm32f4xx_hal_def.h:74
TEMPORARY_BED_LEVELING_STATE
#define TEMPORARY_BED_LEVELING_STATE(enable)
Definition: bedlevel.h:54
active_extruder
constexpr uint8_t active_extruder
Definition: motion.h:107
MMU2::tool_change
static void tool_change(uint8_t index)
planner
Planner planner
Definition: planner.cpp:111
all_axes_homed
FORCE_INLINE bool all_axes_homed()
Definition: motion.h:44
XYZEval::y
T y
Definition: types.h:383
fanmux_switch
void fanmux_switch(const uint8_t e)