Support dual x-carriage printers
Dual x-carriage designs offer some substantial improvements for dual extruder printing.
This commit is contained in:
		| @@ -146,6 +146,36 @@ | ||||
|   #define EXTRUDERS 1 | ||||
| #endif | ||||
|  | ||||
| // Enable this for dual x-carriage printers.  | ||||
| // A dual x-carriage design has the advantage that the inactive extruder can be parked which | ||||
| // prevents hot-end ooze contaminating the print. It also reduces the weight of each x-carriage | ||||
| // allowing faster printing speeds. | ||||
| #define DUAL_X_CARRIAGE | ||||
| #ifdef DUAL_X_CARRIAGE | ||||
| // Configuration for second X-carriage | ||||
| // Note: the first x-carriage is defined as the x-carriage which homes to the minimum endstop; | ||||
| // the second x-carriage always homes to the maximum endstop. | ||||
| #define X2_MIN_POS 88     // set minimum to ensure second x-carriage doesn't hit the parked first X-carriage | ||||
| #define X2_MAX_POS 350.45 // set maximum to the distance between toolheads when both heads are homed  | ||||
| #define X2_HOME_DIR 1     // the second X-carriage always homes to the maximum endstop position | ||||
| #define X2_HOME_POS X2_MAX_POS // default home position is the maximum carriage position  | ||||
|     // However: In this mode the EXTRUDER_OFFSET_X value for the second extruder provides a software  | ||||
|     // override for X2_HOME_POS. This also allow recalibration of the distance between the two endstops | ||||
|     // without modifying the firmware (through the "M218 T1 X???" command). | ||||
|     // Remember: you should set the second extruder x-offset to 0 in your slicer. | ||||
|  | ||||
| // Pins for second x-carriage stepper driver (defined here to avoid further complicating pins.h) | ||||
| #define X2_ENABLE_PIN 29 | ||||
| #define X2_STEP_PIN 25 | ||||
| #define X2_DIR_PIN 23 | ||||
|  | ||||
| // The following settings control the behaviour of the automatic parking and unparking of inactive extruder | ||||
| #define TOOLCHANGE_PARK_ZLIFT 0.1        // the distance to raise Z axis when parking an extruder | ||||
| #define TOOLCHANGE_UNPARK_ZLIFT 1        // the distance to raise Z axis when unparking an extruder | ||||
| #define TOOLCHANGE_UNPARK_SKIP_TRAVEL_MOVES // disable if slicer natively suports dual x-carriage mode.  | ||||
|     // When enabled this avoids unnecessary & inadvertant moves from the last position of old extruder.  | ||||
| #endif // DUAL_X_CARRIAGE | ||||
|      | ||||
| //homing hits the endstop, then retracts by this distance, before it tries to slowly bump again: | ||||
| #define X_HOME_RETRACT_MM 5  | ||||
| #define Y_HOME_RETRACT_MM 5  | ||||
|   | ||||
| @@ -96,7 +96,11 @@ void process_commands(); | ||||
|  | ||||
| void manage_inactivity(); | ||||
|  | ||||
| #if defined(X_ENABLE_PIN) && X_ENABLE_PIN > -1 | ||||
| #if defined(DUAL_X_CARRIAGE) && defined(X_ENABLE_PIN) && X_ENABLE_PIN > -1 \ | ||||
|     && defined(X2_ENABLE_PIN) && X2_ENABLE_PIN > -1 | ||||
|   #define  enable_x() do { WRITE(X_ENABLE_PIN, X_ENABLE_ON); WRITE(X2_ENABLE_PIN, X_ENABLE_ON); } while (0) | ||||
|   #define disable_x() do { WRITE(X_ENABLE_PIN,!X_ENABLE_ON); WRITE(X2_ENABLE_PIN,!X_ENABLE_ON); } while (0) | ||||
| #elif defined(X_ENABLE_PIN) && X_ENABLE_PIN > -1 | ||||
|   #define  enable_x() WRITE(X_ENABLE_PIN, X_ENABLE_ON) | ||||
|   #define disable_x() WRITE(X_ENABLE_PIN,!X_ENABLE_ON) | ||||
| #else | ||||
|   | ||||
| @@ -677,7 +677,46 @@ XYZ_CONSTS_FROM_CONFIG(float, max_length,      MAX_LENGTH); | ||||
| XYZ_CONSTS_FROM_CONFIG(float, home_retract_mm, HOME_RETRACT_MM); | ||||
| XYZ_CONSTS_FROM_CONFIG(signed char, home_dir,  HOME_DIR); | ||||
|  | ||||
| #ifdef DUAL_X_CARRIAGE | ||||
|   #if EXTRUDERS == 1 || defined(COREXY) \ | ||||
|       || !defined(X2_ENABLE_PIN) || !defined(X2_STEP_PIN) || !defined(X2_DIR_PIN) \ | ||||
|       || !defined(X2_HOME_POS) || !defined(X2_MIN_POS) || !defined(X2_MAX_POS) \ | ||||
|       || !defined(X_MAX_PIN) || X_MAX_PIN < 0 | ||||
|     #error "Missing or invalid definitions for DUAL_X_CARRIAGE mode." | ||||
|   #endif | ||||
|   #if X_HOME_DIR != -1 || X2_HOME_DIR != 1 | ||||
|     #error "Please use canonical x-carriage assignment" // the x-carriages are defined by their homing directions | ||||
|   #endif   | ||||
|      | ||||
| static float x_home_pos(int extruder) { | ||||
|   if (extruder == 0) | ||||
|     return base_home_pos(X_AXIS) + add_homeing[X_AXIS]; | ||||
|   else | ||||
|     // In dual carriage mode the extruder offset provides an override of the | ||||
|     // second X-carriage offset when homed - otherwise X2_HOME_POS is used. | ||||
|     // This allow soft recalibration of the second extruder offset position without firmware reflash  | ||||
|     // (through the M218 command). | ||||
|     return (extruder_offset[X_AXIS][1] != 0) ? extruder_offset[X_AXIS][1] : X2_HOME_POS; | ||||
| } | ||||
|  | ||||
| static int x_home_dir(int extruder) { | ||||
|   return (extruder == 0) ? X_HOME_DIR : X2_HOME_DIR; | ||||
| } | ||||
|  | ||||
| static bool active_extruder_parked = false; | ||||
| static float raised_parked_position[NUM_AXIS]; | ||||
| static unsigned long delayed_move_time = 0; | ||||
| #endif      | ||||
|  | ||||
| static void axis_is_at_home(int axis) { | ||||
| #ifdef DUAL_X_CARRIAGE | ||||
|   if (axis == X_AXIS && active_extruder != 0) { | ||||
|     current_position[X_AXIS] = x_home_pos(active_extruder); | ||||
|     min_pos[X_AXIS] =          X2_MIN_POS; | ||||
|     max_pos[X_AXIS] =          X2_MAX_POS; | ||||
|     return; | ||||
|   } | ||||
| #endif   | ||||
|   current_position[axis] = base_home_pos(axis) + add_homeing[axis]; | ||||
|   min_pos[axis] =          base_min_pos(axis) + add_homeing[axis]; | ||||
|   max_pos[axis] =          base_max_pos(axis) + add_homeing[axis]; | ||||
| @@ -686,10 +725,16 @@ static void axis_is_at_home(int axis) { | ||||
| static void homeaxis(int axis) { | ||||
| #define HOMEAXIS_DO(LETTER) \ | ||||
|   ((LETTER##_MIN_PIN > -1 && LETTER##_HOME_DIR==-1) || (LETTER##_MAX_PIN > -1 && LETTER##_HOME_DIR==1)) | ||||
|  | ||||
|   if (axis==X_AXIS ? HOMEAXIS_DO(X) : | ||||
|       axis==Y_AXIS ? HOMEAXIS_DO(Y) : | ||||
|       axis==Z_AXIS ? HOMEAXIS_DO(Z) : | ||||
|       0) { | ||||
|     int axis_home_dir = home_dir(axis); | ||||
| #ifdef DUAL_X_CARRIAGE | ||||
|     if (axis == X_AXIS) | ||||
|       axis_home_dir = x_home_dir(active_extruder); | ||||
| #endif | ||||
|  | ||||
|     // Engage Servo endstop if enabled | ||||
|     #ifdef SERVO_ENDSTOPS | ||||
| @@ -864,8 +909,14 @@ void process_commands() | ||||
|       { | ||||
|         current_position[X_AXIS] = 0;current_position[Y_AXIS] = 0; | ||||
|  | ||||
|        #ifdef DUAL_X_CARRIAGE | ||||
|         int x_axis_home_dir = home_dir(X_AXIS); | ||||
|        #else | ||||
|         int x_axis_home_dir = x_home_dir(active_extruder); | ||||
|        #endif | ||||
|          | ||||
|         plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]); | ||||
|         destination[X_AXIS] = 1.5 * X_MAX_LENGTH * X_HOME_DIR;destination[Y_AXIS] = 1.5 * Y_MAX_LENGTH * Y_HOME_DIR; | ||||
|         destination[X_AXIS] = 1.5 * max_length(X_AXIS) * x_axis_home_dir;destination[Y_AXIS] = 1.5 * max_length(Y_AXIS) * home_dir(Y_AXIS); | ||||
|         feedrate = homing_feedrate[X_AXIS]; | ||||
|         if(homing_feedrate[Y_AXIS]<feedrate) | ||||
|           feedrate =homing_feedrate[Y_AXIS]; | ||||
| @@ -890,6 +941,14 @@ void process_commands() | ||||
|  | ||||
|       if((home_all_axis) || (code_seen(axis_codes[X_AXIS]))) | ||||
|       { | ||||
|       #ifdef DUAL_X_CARRIAGE | ||||
|         int tmp_extruder = active_extruder; | ||||
|         active_extruder = !active_extruder; | ||||
|         HOMEAXIS(X); | ||||
|         active_extruder = tmp_extruder; | ||||
|         active_extruder_parked = false;  | ||||
|         delayed_move_time = 0; | ||||
|       #endif          | ||||
|         HOMEAXIS(X); | ||||
|       } | ||||
|  | ||||
| @@ -922,7 +981,7 @@ void process_commands() | ||||
|         } | ||||
|       } | ||||
|       plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]); | ||||
| #endif // DELTA | ||||
| #endif // else DELTA | ||||
|            | ||||
|       #ifdef ENDSTOPS_ONLY_FOR_HOMING | ||||
|         enable_endstops(false); | ||||
| @@ -2001,6 +2060,36 @@ void process_commands() | ||||
|       if(tmp_extruder != active_extruder) { | ||||
|         // Save current position to return to after applying extruder offset | ||||
|         memcpy(destination, current_position, sizeof(destination)); | ||||
|       #ifdef DUAL_X_CARRIAGE | ||||
|         if (Stopped == false && delayed_move_time == 0 && current_position[X_AXIS] != x_home_pos(active_extruder)) | ||||
|         { | ||||
|           // Park old head: 1) raise 2) move to park position 3) lower | ||||
|           plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT,  | ||||
|                 current_position[E_AXIS], max_feedrate[Z_AXIS], active_extruder); | ||||
|           plan_buffer_line(x_home_pos(active_extruder), current_position[Y_AXIS], current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT,  | ||||
|                 current_position[E_AXIS], max_feedrate[X_AXIS], active_extruder); | ||||
|           plan_buffer_line(x_home_pos(active_extruder), current_position[Y_AXIS], current_position[Z_AXIS],  | ||||
|                 current_position[E_AXIS], max_feedrate[Z_AXIS], active_extruder); | ||||
|           st_synchronize(); | ||||
|         } | ||||
|          | ||||
|         // only apply Y extruder offset in dual x carriage mode (x offset is already used in determining home pos) | ||||
|         current_position[Y_AXIS] = current_position[Y_AXIS] - | ||||
|                      extruder_offset[Y_AXIS][active_extruder] + | ||||
|                      extruder_offset[Y_AXIS][tmp_extruder]; | ||||
|                       | ||||
|         active_extruder = tmp_extruder; | ||||
|  | ||||
|         // Inactive head always starts at its parked position. | ||||
|         axis_is_at_home(X_AXIS); | ||||
|  | ||||
|         // 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; | ||||
|       #else     | ||||
|         // Offset extruder (only by XY) | ||||
|         int i; | ||||
|         for(i = 0; i < 2; i++) { | ||||
| @@ -2010,6 +2099,7 @@ void process_commands() | ||||
|         } | ||||
|         // Set the new active extruder and position | ||||
|         active_extruder = tmp_extruder; | ||||
|       #endif //else DUAL_X_CARRIAGE | ||||
|         plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]); | ||||
|         // Move to the old position if 'F' was in the parameters | ||||
|         if(make_move && Stopped == false) { | ||||
| @@ -2204,6 +2294,40 @@ void prepare_move() | ||||
|                      active_extruder); | ||||
|   } | ||||
| #else | ||||
|  | ||||
| #if defined(DUAL_X_CARRIAGE) | ||||
|   if (active_extruder_parked) | ||||
|   { | ||||
|     if (current_position[E_AXIS] == destination[E_AXIS]) | ||||
|     { | ||||
|       // this is a travel move | ||||
| #ifdef TOOLCHANGE_UNPARK_SKIP_TRAVEL_MOVES | ||||
|       if (delayed_move_time != 0xFFFFFFFFUL) | ||||
|       { | ||||
|         // skip this move but still update current_position in main so that it can  | ||||
|         // be used as starting position before extrusion (but not in planner) | ||||
|         memcpy(current_position, destination, sizeof(current_position));  | ||||
|         if (destination[Z_AXIS] > raised_parked_position[Z_AXIS]) | ||||
|           raised_parked_position[Z_AXIS] = destination[Z_AXIS]; | ||||
|         delayed_move_time = millis(); | ||||
|         return; | ||||
|       } | ||||
|       delayed_move_time = 0; | ||||
| #else | ||||
|       // this will cause the unpark code below to execute the specified lift in moving to the initial (travel move) position. | ||||
|       memcpy(current_position, destination, sizeof(current_position));  | ||||
| #endif       | ||||
|     } | ||||
|     // unpark extruder: 1) raise, 2) move into starting XY position, 3) lower | ||||
|     plan_buffer_line(raised_parked_position[X_AXIS], raised_parked_position[Y_AXIS], raised_parked_position[Z_AXIS],    current_position[E_AXIS], max_feedrate[Z_AXIS], active_extruder); | ||||
|     plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], raised_parked_position[Z_AXIS],  | ||||
|         current_position[E_AXIS], min(max_feedrate[X_AXIS],max_feedrate[Y_AXIS]), active_extruder); | ||||
|     plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS],  | ||||
|         current_position[E_AXIS], max_feedrate[Z_AXIS], active_extruder); | ||||
|     active_extruder_parked = false; | ||||
|   } | ||||
| #endif //DUAL_X_CARRIAGE | ||||
|  | ||||
|   // Do not use feedmultiply for E or Z only moves | ||||
|   if( (current_position[X_AXIS] == destination [X_AXIS]) && (current_position[Y_AXIS] == destination [Y_AXIS])) { | ||||
|       plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder); | ||||
| @@ -2254,6 +2378,9 @@ void controllerFan() | ||||
|        || !READ(E2_ENABLE_PIN) | ||||
|     #endif | ||||
|     #if EXTRUDER > 1 | ||||
|       #if defined(X2_ENABLE_PIN) && X2_ENABLE_PIN > -1 | ||||
|        || !READ(X2_ENABLE_PIN) | ||||
|       #endif | ||||
|        || !READ(E1_ENABLE_PIN) | ||||
|     #endif | ||||
|        || !READ(E0_ENABLE_PIN)) //If any of the drivers are enabled... | ||||
| @@ -2320,6 +2447,16 @@ void manage_inactivity() | ||||
|      WRITE(E0_ENABLE_PIN,oldstatus); | ||||
|     } | ||||
|   #endif | ||||
|   #if defined(DUAL_X_CARRIAGE) && defined(TOOLCHANGE_UNPARK_SKIP_TRAVEL_MOVES) | ||||
|     // handle delayed move timeout | ||||
|     if (delayed_move_time != 0 && (millis() - delayed_move_time) > 1000) | ||||
|     { | ||||
|       // travel moves have been received so enact them | ||||
|       delayed_move_time = 0xFFFFFFFFUL; // force moves to be done | ||||
|       memcpy(destination,current_position,sizeof(destination)); | ||||
|       prepare_move();  | ||||
|     } | ||||
|   #endif   | ||||
|   check_axes_activity(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -348,11 +348,21 @@ ISR(TIMER1_COMPA_vect) | ||||
|  | ||||
|     // Set the direction bits (X_AXIS=A_AXIS and Y_AXIS=B_AXIS for COREXY) | ||||
|     if((out_bits & (1<<X_AXIS))!=0){ | ||||
|       WRITE(X_DIR_PIN, INVERT_X_DIR); | ||||
|       #ifdef DUAL_X_CARRIAGE | ||||
|       if (active_extruder != 0) | ||||
|         WRITE(X2_DIR_PIN,INVERT_X_DIR); | ||||
|       else | ||||
|       #endif         | ||||
|         WRITE(X_DIR_PIN, INVERT_X_DIR); | ||||
|       count_direction[X_AXIS]=-1; | ||||
|     } | ||||
|     else{ | ||||
|       WRITE(X_DIR_PIN, !INVERT_X_DIR); | ||||
|       #ifdef DUAL_X_CARRIAGE | ||||
|       if (active_extruder != 0) | ||||
|         WRITE(X2_DIR_PIN,!INVERT_X_DIR); | ||||
|       else | ||||
|       #endif         | ||||
|         WRITE(X_DIR_PIN, !INVERT_X_DIR); | ||||
|       count_direction[X_AXIS]=1; | ||||
|     } | ||||
|     if((out_bits & (1<<Y_AXIS))!=0){ | ||||
| @@ -372,29 +382,41 @@ ISR(TIMER1_COMPA_vect) | ||||
|     #endif | ||||
|       CHECK_ENDSTOPS | ||||
|       { | ||||
|         #if defined(X_MIN_PIN) && X_MIN_PIN > -1 | ||||
|           bool x_min_endstop=(READ(X_MIN_PIN) != X_ENDSTOPS_INVERTING); | ||||
|           if(x_min_endstop && old_x_min_endstop && (current_block->steps_x > 0)) { | ||||
|             endstops_trigsteps[X_AXIS] = count_position[X_AXIS]; | ||||
|             endstop_x_hit=true; | ||||
|             step_events_completed = current_block->step_event_count; | ||||
|           } | ||||
|           old_x_min_endstop = x_min_endstop; | ||||
|         #ifdef DUAL_X_CARRIAGE | ||||
|         // with 2 x-carriages, endstops are only checked in the homing direction for the active extruder | ||||
|         if ((active_extruder == 0 && X_HOME_DIR == -1) || (active_extruder != 0 && X2_HOME_DIR == -1)) | ||||
|         #endif           | ||||
|         { | ||||
|           #if defined(X_MIN_PIN) && X_MIN_PIN > -1 | ||||
|             bool x_min_endstop=(READ(X_MIN_PIN) != X_ENDSTOPS_INVERTING); | ||||
|             if(x_min_endstop && old_x_min_endstop && (current_block->steps_x > 0)) { | ||||
|               endstops_trigsteps[X_AXIS] = count_position[X_AXIS]; | ||||
|               endstop_x_hit=true; | ||||
|               step_events_completed = current_block->step_event_count; | ||||
|             } | ||||
|             old_x_min_endstop = x_min_endstop; | ||||
|           #endif | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     else { // +direction | ||||
|       CHECK_ENDSTOPS  | ||||
|       { | ||||
|         #if defined(X_MAX_PIN) && X_MAX_PIN > -1 | ||||
|           bool x_max_endstop=(READ(X_MAX_PIN) != X_ENDSTOPS_INVERTING); | ||||
|           if(x_max_endstop && old_x_max_endstop && (current_block->steps_x > 0)){ | ||||
|             endstops_trigsteps[X_AXIS] = count_position[X_AXIS]; | ||||
|             endstop_x_hit=true; | ||||
|             step_events_completed = current_block->step_event_count; | ||||
|           } | ||||
|           old_x_max_endstop = x_max_endstop; | ||||
|         #ifdef DUAL_X_CARRIAGE | ||||
|         // with 2 x-carriages, endstops are only checked in the homing direction for the active extruder | ||||
|         if ((active_extruder == 0 && X_HOME_DIR == 1) || (active_extruder != 0 && X2_HOME_DIR == 1)) | ||||
|         #endif           | ||||
|         { | ||||
|           #if defined(X_MAX_PIN) && X_MAX_PIN > -1 | ||||
|             bool x_max_endstop=(READ(X_MAX_PIN) != X_ENDSTOPS_INVERTING); | ||||
|             if(x_max_endstop && old_x_max_endstop && (current_block->steps_x > 0)){ | ||||
|               endstops_trigsteps[X_AXIS] = count_position[X_AXIS]; | ||||
|               endstop_x_hit=true; | ||||
|               step_events_completed = current_block->step_event_count; | ||||
|             } | ||||
|             old_x_max_endstop = x_max_endstop; | ||||
|           #endif | ||||
|         }   | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @@ -507,10 +529,20 @@ ISR(TIMER1_COMPA_vect) | ||||
|  | ||||
|         counter_x += current_block->steps_x; | ||||
|         if (counter_x > 0) { | ||||
|           WRITE(X_STEP_PIN, !INVERT_X_STEP_PIN); | ||||
|           #ifdef DUAL_X_CARRIAGE | ||||
|           if (active_extruder != 0) | ||||
|             WRITE(X2_STEP_PIN,!INVERT_X_STEP_PIN); | ||||
|           else | ||||
|           #endif         | ||||
|             WRITE(X_STEP_PIN, !INVERT_X_STEP_PIN); | ||||
|           counter_x -= current_block->step_event_count; | ||||
|           count_position[X_AXIS]+=count_direction[X_AXIS];    | ||||
|           WRITE(X_STEP_PIN, INVERT_X_STEP_PIN); | ||||
|           #ifdef DUAL_X_CARRIAGE | ||||
|           if (active_extruder != 0) | ||||
|             WRITE(X2_STEP_PIN,INVERT_X_STEP_PIN); | ||||
|           else | ||||
|           #endif         | ||||
|             WRITE(X_STEP_PIN, INVERT_X_STEP_PIN); | ||||
|         } | ||||
|    | ||||
|         counter_y += current_block->steps_y; | ||||
| @@ -685,6 +717,9 @@ void st_init() | ||||
|   #if defined(X_DIR_PIN) && X_DIR_PIN > -1 | ||||
|     SET_OUTPUT(X_DIR_PIN); | ||||
|   #endif | ||||
|   #if defined(X2_DIR_PIN) && X2_DIR_PIN > -1 | ||||
|     SET_OUTPUT(X2_DIR_PIN); | ||||
|   #endif | ||||
|   #if defined(Y_DIR_PIN) && Y_DIR_PIN > -1  | ||||
|     SET_OUTPUT(Y_DIR_PIN); | ||||
|   #endif | ||||
| @@ -711,6 +746,10 @@ void st_init() | ||||
|     SET_OUTPUT(X_ENABLE_PIN); | ||||
|     if(!X_ENABLE_ON) WRITE(X_ENABLE_PIN,HIGH); | ||||
|   #endif | ||||
|   #if defined(X2_ENABLE_PIN) && X2_ENABLE_PIN > -1 | ||||
|     SET_OUTPUT(X2_ENABLE_PIN); | ||||
|     if(!X_ENABLE_ON) WRITE(X2_ENABLE_PIN,HIGH); | ||||
|   #endif | ||||
|   #if defined(Y_ENABLE_PIN) && Y_ENABLE_PIN > -1 | ||||
|     SET_OUTPUT(Y_ENABLE_PIN); | ||||
|     if(!Y_ENABLE_ON) WRITE(Y_ENABLE_PIN,HIGH); | ||||
| @@ -788,6 +827,11 @@ void st_init() | ||||
|     WRITE(X_STEP_PIN,INVERT_X_STEP_PIN); | ||||
|     disable_x(); | ||||
|   #endif   | ||||
|   #if defined(X2_STEP_PIN) && (X2_STEP_PIN > -1)  | ||||
|     SET_OUTPUT(X2_STEP_PIN); | ||||
|     WRITE(X2_STEP_PIN,INVERT_X_STEP_PIN); | ||||
|     disable_x(); | ||||
|   #endif   | ||||
|   #if defined(Y_STEP_PIN) && (Y_STEP_PIN > -1)  | ||||
|     SET_OUTPUT(Y_STEP_PIN); | ||||
|     WRITE(Y_STEP_PIN,INVERT_Y_STEP_PIN); | ||||
|   | ||||
| @@ -41,6 +41,8 @@ Features: | ||||
| *   Heater power reporting. Useful for PID monitoring. | ||||
| *   PID tuning | ||||
| *   CoreXY kinematics (www.corexy.com/theory.html) | ||||
| *   Delta kinematics | ||||
| *   Dual X-carriage support for multiple extruder systems | ||||
| *   Configurable serial port to support connection of wireless adaptors. | ||||
| *   Automatic operation of extruder/cold-end cooling fans based on nozzle temperature | ||||
| *   RC Servo Support, specify angle or duration for continuous rotation servos. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user