Browse Source

Support dual x-carriage printers

Dual x-carriage designs offer some substantial improvements for dual
extruder printing.
Robert F-C 11 years ago
parent
commit
d7390e13d9
5 changed files with 242 additions and 25 deletions
  1. 30
    0
      Marlin/Configuration_adv.h
  2. 5
    1
      Marlin/Marlin.h
  3. 139
    2
      Marlin/Marlin_main.cpp
  4. 66
    22
      Marlin/stepper.cpp
  5. 2
    0
      README.md

+ 30
- 0
Marlin/Configuration_adv.h View File

@@ -146,6 +146,36 @@
146 146
   #define EXTRUDERS 1
147 147
 #endif
148 148
 
149
+// Enable this for dual x-carriage printers. 
150
+// A dual x-carriage design has the advantage that the inactive extruder can be parked which
151
+// prevents hot-end ooze contaminating the print. It also reduces the weight of each x-carriage
152
+// allowing faster printing speeds.
153
+#define DUAL_X_CARRIAGE
154
+#ifdef DUAL_X_CARRIAGE
155
+// Configuration for second X-carriage
156
+// Note: the first x-carriage is defined as the x-carriage which homes to the minimum endstop;
157
+// the second x-carriage always homes to the maximum endstop.
158
+#define X2_MIN_POS 88     // set minimum to ensure second x-carriage doesn't hit the parked first X-carriage
159
+#define X2_MAX_POS 350.45 // set maximum to the distance between toolheads when both heads are homed 
160
+#define X2_HOME_DIR 1     // the second X-carriage always homes to the maximum endstop position
161
+#define X2_HOME_POS X2_MAX_POS // default home position is the maximum carriage position 
162
+    // However: In this mode the EXTRUDER_OFFSET_X value for the second extruder provides a software 
163
+    // override for X2_HOME_POS. This also allow recalibration of the distance between the two endstops
164
+    // without modifying the firmware (through the "M218 T1 X???" command).
165
+    // Remember: you should set the second extruder x-offset to 0 in your slicer.
166
+
167
+// Pins for second x-carriage stepper driver (defined here to avoid further complicating pins.h)
168
+#define X2_ENABLE_PIN 29
169
+#define X2_STEP_PIN 25
170
+#define X2_DIR_PIN 23
171
+
172
+// The following settings control the behaviour of the automatic parking and unparking of inactive extruder
173
+#define TOOLCHANGE_PARK_ZLIFT 0.1        // the distance to raise Z axis when parking an extruder
174
+#define TOOLCHANGE_UNPARK_ZLIFT 1        // the distance to raise Z axis when unparking an extruder
175
+#define TOOLCHANGE_UNPARK_SKIP_TRAVEL_MOVES // disable if slicer natively suports dual x-carriage mode. 
176
+    // When enabled this avoids unnecessary & inadvertant moves from the last position of old extruder. 
177
+#endif // DUAL_X_CARRIAGE
178
+    
149 179
 //homing hits the endstop, then retracts by this distance, before it tries to slowly bump again:
150 180
 #define X_HOME_RETRACT_MM 5 
151 181
 #define Y_HOME_RETRACT_MM 5 

+ 5
- 1
Marlin/Marlin.h View File

@@ -96,7 +96,11 @@ void process_commands();
96 96
 
97 97
 void manage_inactivity();
98 98
 
99
-#if defined(X_ENABLE_PIN) && X_ENABLE_PIN > -1
99
+#if defined(DUAL_X_CARRIAGE) && defined(X_ENABLE_PIN) && X_ENABLE_PIN > -1 \
100
+    && defined(X2_ENABLE_PIN) && X2_ENABLE_PIN > -1
101
+  #define  enable_x() do { WRITE(X_ENABLE_PIN, X_ENABLE_ON); WRITE(X2_ENABLE_PIN, X_ENABLE_ON); } while (0)
102
+  #define disable_x() do { WRITE(X_ENABLE_PIN,!X_ENABLE_ON); WRITE(X2_ENABLE_PIN,!X_ENABLE_ON); } while (0)
103
+#elif defined(X_ENABLE_PIN) && X_ENABLE_PIN > -1
100 104
   #define  enable_x() WRITE(X_ENABLE_PIN, X_ENABLE_ON)
101 105
   #define disable_x() WRITE(X_ENABLE_PIN,!X_ENABLE_ON)
102 106
 #else

+ 139
- 2
Marlin/Marlin_main.cpp View File

@@ -677,7 +677,46 @@ XYZ_CONSTS_FROM_CONFIG(float, max_length,      MAX_LENGTH);
677 677
 XYZ_CONSTS_FROM_CONFIG(float, home_retract_mm, HOME_RETRACT_MM);
678 678
 XYZ_CONSTS_FROM_CONFIG(signed char, home_dir,  HOME_DIR);
679 679
 
680
+#ifdef DUAL_X_CARRIAGE
681
+  #if EXTRUDERS == 1 || defined(COREXY) \
682
+      || !defined(X2_ENABLE_PIN) || !defined(X2_STEP_PIN) || !defined(X2_DIR_PIN) \
683
+      || !defined(X2_HOME_POS) || !defined(X2_MIN_POS) || !defined(X2_MAX_POS) \
684
+      || !defined(X_MAX_PIN) || X_MAX_PIN < 0
685
+    #error "Missing or invalid definitions for DUAL_X_CARRIAGE mode."
686
+  #endif
687
+  #if X_HOME_DIR != -1 || X2_HOME_DIR != 1
688
+    #error "Please use canonical x-carriage assignment" // the x-carriages are defined by their homing directions
689
+  #endif  
690
+    
691
+static float x_home_pos(int extruder) {
692
+  if (extruder == 0)
693
+    return base_home_pos(X_AXIS) + add_homeing[X_AXIS];
694
+  else
695
+    // In dual carriage mode the extruder offset provides an override of the
696
+    // second X-carriage offset when homed - otherwise X2_HOME_POS is used.
697
+    // This allow soft recalibration of the second extruder offset position without firmware reflash 
698
+    // (through the M218 command).
699
+    return (extruder_offset[X_AXIS][1] != 0) ? extruder_offset[X_AXIS][1] : X2_HOME_POS;
700
+}
701
+
702
+static int x_home_dir(int extruder) {
703
+  return (extruder == 0) ? X_HOME_DIR : X2_HOME_DIR;
704
+}
705
+
706
+static bool active_extruder_parked = false;
707
+static float raised_parked_position[NUM_AXIS];
708
+static unsigned long delayed_move_time = 0;
709
+#endif     
710
+
680 711
 static void axis_is_at_home(int axis) {
712
+#ifdef DUAL_X_CARRIAGE
713
+  if (axis == X_AXIS && active_extruder != 0) {
714
+    current_position[X_AXIS] = x_home_pos(active_extruder);
715
+    min_pos[X_AXIS] =          X2_MIN_POS;
716
+    max_pos[X_AXIS] =          X2_MAX_POS;
717
+    return;
718
+  }
719
+#endif  
681 720
   current_position[axis] = base_home_pos(axis) + add_homeing[axis];
682 721
   min_pos[axis] =          base_min_pos(axis) + add_homeing[axis];
683 722
   max_pos[axis] =          base_max_pos(axis) + add_homeing[axis];
@@ -686,10 +725,16 @@ static void axis_is_at_home(int axis) {
686 725
 static void homeaxis(int axis) {
687 726
 #define HOMEAXIS_DO(LETTER) \
688 727
   ((LETTER##_MIN_PIN > -1 && LETTER##_HOME_DIR==-1) || (LETTER##_MAX_PIN > -1 && LETTER##_HOME_DIR==1))
728
+
689 729
   if (axis==X_AXIS ? HOMEAXIS_DO(X) :
690 730
       axis==Y_AXIS ? HOMEAXIS_DO(Y) :
691 731
       axis==Z_AXIS ? HOMEAXIS_DO(Z) :
692 732
       0) {
733
+    int axis_home_dir = home_dir(axis);
734
+#ifdef DUAL_X_CARRIAGE
735
+    if (axis == X_AXIS)
736
+      axis_home_dir = x_home_dir(active_extruder);
737
+#endif
693 738
 
694 739
     // Engage Servo endstop if enabled
695 740
     #ifdef SERVO_ENDSTOPS
@@ -864,8 +909,14 @@ void process_commands()
864 909
       {
865 910
         current_position[X_AXIS] = 0;current_position[Y_AXIS] = 0;
866 911
 
912
+       #ifdef DUAL_X_CARRIAGE
913
+        int x_axis_home_dir = home_dir(X_AXIS);
914
+       #else
915
+        int x_axis_home_dir = x_home_dir(active_extruder);
916
+       #endif
917
+        
867 918
         plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
868
-        destination[X_AXIS] = 1.5 * X_MAX_LENGTH * X_HOME_DIR;destination[Y_AXIS] = 1.5 * Y_MAX_LENGTH * Y_HOME_DIR;
919
+        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);
869 920
         feedrate = homing_feedrate[X_AXIS];
870 921
         if(homing_feedrate[Y_AXIS]<feedrate)
871 922
           feedrate =homing_feedrate[Y_AXIS];
@@ -890,6 +941,14 @@ void process_commands()
890 941
 
891 942
       if((home_all_axis) || (code_seen(axis_codes[X_AXIS])))
892 943
       {
944
+      #ifdef DUAL_X_CARRIAGE
945
+        int tmp_extruder = active_extruder;
946
+        active_extruder = !active_extruder;
947
+        HOMEAXIS(X);
948
+        active_extruder = tmp_extruder;
949
+        active_extruder_parked = false; 
950
+        delayed_move_time = 0;
951
+      #endif         
893 952
         HOMEAXIS(X);
894 953
       }
895 954
 
@@ -922,7 +981,7 @@ void process_commands()
922 981
         }
923 982
       }
924 983
       plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
925
-#endif // DELTA
984
+#endif // else DELTA
926 985
           
927 986
       #ifdef ENDSTOPS_ONLY_FOR_HOMING
928 987
         enable_endstops(false);
@@ -2001,6 +2060,36 @@ void process_commands()
2001 2060
       if(tmp_extruder != active_extruder) {
2002 2061
         // Save current position to return to after applying extruder offset
2003 2062
         memcpy(destination, current_position, sizeof(destination));
2063
+      #ifdef DUAL_X_CARRIAGE
2064
+        if (Stopped == false && delayed_move_time == 0 && current_position[X_AXIS] != x_home_pos(active_extruder))
2065
+        {
2066
+          // Park old head: 1) raise 2) move to park position 3) lower
2067
+          plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT, 
2068
+                current_position[E_AXIS], max_feedrate[Z_AXIS], active_extruder);
2069
+          plan_buffer_line(x_home_pos(active_extruder), current_position[Y_AXIS], current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT, 
2070
+                current_position[E_AXIS], max_feedrate[X_AXIS], active_extruder);
2071
+          plan_buffer_line(x_home_pos(active_extruder), current_position[Y_AXIS], current_position[Z_AXIS], 
2072
+                current_position[E_AXIS], max_feedrate[Z_AXIS], active_extruder);
2073
+          st_synchronize();
2074
+        }
2075
+        
2076
+        // only apply Y extruder offset in dual x carriage mode (x offset is already used in determining home pos)
2077
+        current_position[Y_AXIS] = current_position[Y_AXIS] -
2078
+                     extruder_offset[Y_AXIS][active_extruder] +
2079
+                     extruder_offset[Y_AXIS][tmp_extruder];
2080
+                     
2081
+        active_extruder = tmp_extruder;
2082
+
2083
+        // Inactive head always starts at its parked position.
2084
+        axis_is_at_home(X_AXIS);
2085
+
2086
+        // record raised toolhead position for use by unpark
2087
+        memcpy(raised_parked_position, current_position, sizeof(raised_parked_position));
2088
+        raised_parked_position[Z_AXIS] += TOOLCHANGE_UNPARK_ZLIFT;
2089
+        
2090
+        active_extruder_parked = true;
2091
+        delayed_move_time = 0;
2092
+      #else    
2004 2093
         // Offset extruder (only by XY)
2005 2094
         int i;
2006 2095
         for(i = 0; i < 2; i++) {
@@ -2010,6 +2099,7 @@ void process_commands()
2010 2099
         }
2011 2100
         // Set the new active extruder and position
2012 2101
         active_extruder = tmp_extruder;
2102
+      #endif //else DUAL_X_CARRIAGE
2013 2103
         plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
2014 2104
         // Move to the old position if 'F' was in the parameters
2015 2105
         if(make_move && Stopped == false) {
@@ -2204,6 +2294,40 @@ void prepare_move()
2204 2294
                      active_extruder);
2205 2295
   }
2206 2296
 #else
2297
+
2298
+#if defined(DUAL_X_CARRIAGE)
2299
+  if (active_extruder_parked)
2300
+  {
2301
+    if (current_position[E_AXIS] == destination[E_AXIS])
2302
+    {
2303
+      // this is a travel move
2304
+#ifdef TOOLCHANGE_UNPARK_SKIP_TRAVEL_MOVES
2305
+      if (delayed_move_time != 0xFFFFFFFFUL)
2306
+      {
2307
+        // skip this move but still update current_position in main so that it can 
2308
+        // be used as starting position before extrusion (but not in planner)
2309
+        memcpy(current_position, destination, sizeof(current_position)); 
2310
+        if (destination[Z_AXIS] > raised_parked_position[Z_AXIS])
2311
+          raised_parked_position[Z_AXIS] = destination[Z_AXIS];
2312
+        delayed_move_time = millis();
2313
+        return;
2314
+      }
2315
+      delayed_move_time = 0;
2316
+#else
2317
+      // this will cause the unpark code below to execute the specified lift in moving to the initial (travel move) position.
2318
+      memcpy(current_position, destination, sizeof(current_position)); 
2319
+#endif      
2320
+    }
2321
+    // unpark extruder: 1) raise, 2) move into starting XY position, 3) lower
2322
+    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);
2323
+    plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], raised_parked_position[Z_AXIS], 
2324
+        current_position[E_AXIS], min(max_feedrate[X_AXIS],max_feedrate[Y_AXIS]), active_extruder);
2325
+    plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], 
2326
+        current_position[E_AXIS], max_feedrate[Z_AXIS], active_extruder);
2327
+    active_extruder_parked = false;
2328
+  }
2329
+#endif //DUAL_X_CARRIAGE
2330
+
2207 2331
   // Do not use feedmultiply for E or Z only moves
2208 2332
   if( (current_position[X_AXIS] == destination [X_AXIS]) && (current_position[Y_AXIS] == destination [Y_AXIS])) {
2209 2333
       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()
2254 2378
        || !READ(E2_ENABLE_PIN)
2255 2379
     #endif
2256 2380
     #if EXTRUDER > 1
2381
+      #if defined(X2_ENABLE_PIN) && X2_ENABLE_PIN > -1
2382
+       || !READ(X2_ENABLE_PIN)
2383
+      #endif
2257 2384
        || !READ(E1_ENABLE_PIN)
2258 2385
     #endif
2259 2386
        || !READ(E0_ENABLE_PIN)) //If any of the drivers are enabled...
@@ -2320,6 +2447,16 @@ void manage_inactivity()
2320 2447
      WRITE(E0_ENABLE_PIN,oldstatus);
2321 2448
     }
2322 2449
   #endif
2450
+  #if defined(DUAL_X_CARRIAGE) && defined(TOOLCHANGE_UNPARK_SKIP_TRAVEL_MOVES)
2451
+    // handle delayed move timeout
2452
+    if (delayed_move_time != 0 && (millis() - delayed_move_time) > 1000)
2453
+    {
2454
+      // travel moves have been received so enact them
2455
+      delayed_move_time = 0xFFFFFFFFUL; // force moves to be done
2456
+      memcpy(destination,current_position,sizeof(destination));
2457
+      prepare_move(); 
2458
+    }
2459
+  #endif  
2323 2460
   check_axes_activity();
2324 2461
 }
2325 2462
 

+ 66
- 22
Marlin/stepper.cpp View File

@@ -348,11 +348,21 @@ ISR(TIMER1_COMPA_vect)
348 348
 
349 349
     // Set the direction bits (X_AXIS=A_AXIS and Y_AXIS=B_AXIS for COREXY)
350 350
     if((out_bits & (1<<X_AXIS))!=0){
351
-      WRITE(X_DIR_PIN, INVERT_X_DIR);
351
+      #ifdef DUAL_X_CARRIAGE
352
+      if (active_extruder != 0)
353
+        WRITE(X2_DIR_PIN,INVERT_X_DIR);
354
+      else
355
+      #endif        
356
+        WRITE(X_DIR_PIN, INVERT_X_DIR);
352 357
       count_direction[X_AXIS]=-1;
353 358
     }
354 359
     else{
355
-      WRITE(X_DIR_PIN, !INVERT_X_DIR);
360
+      #ifdef DUAL_X_CARRIAGE
361
+      if (active_extruder != 0)
362
+        WRITE(X2_DIR_PIN,!INVERT_X_DIR);
363
+      else
364
+      #endif        
365
+        WRITE(X_DIR_PIN, !INVERT_X_DIR);
356 366
       count_direction[X_AXIS]=1;
357 367
     }
358 368
     if((out_bits & (1<<Y_AXIS))!=0){
@@ -372,29 +382,41 @@ ISR(TIMER1_COMPA_vect)
372 382
     #endif
373 383
       CHECK_ENDSTOPS
374 384
       {
375
-        #if defined(X_MIN_PIN) && X_MIN_PIN > -1
376
-          bool x_min_endstop=(READ(X_MIN_PIN) != X_ENDSTOPS_INVERTING);
377
-          if(x_min_endstop && old_x_min_endstop && (current_block->steps_x > 0)) {
378
-            endstops_trigsteps[X_AXIS] = count_position[X_AXIS];
379
-            endstop_x_hit=true;
380
-            step_events_completed = current_block->step_event_count;
381
-          }
382
-          old_x_min_endstop = x_min_endstop;
383
-        #endif
385
+        #ifdef DUAL_X_CARRIAGE
386
+        // with 2 x-carriages, endstops are only checked in the homing direction for the active extruder
387
+        if ((active_extruder == 0 && X_HOME_DIR == -1) || (active_extruder != 0 && X2_HOME_DIR == -1))
388
+        #endif          
389
+        {
390
+          #if defined(X_MIN_PIN) && X_MIN_PIN > -1
391
+            bool x_min_endstop=(READ(X_MIN_PIN) != X_ENDSTOPS_INVERTING);
392
+            if(x_min_endstop && old_x_min_endstop && (current_block->steps_x > 0)) {
393
+              endstops_trigsteps[X_AXIS] = count_position[X_AXIS];
394
+              endstop_x_hit=true;
395
+              step_events_completed = current_block->step_event_count;
396
+            }
397
+            old_x_min_endstop = x_min_endstop;
398
+          #endif
399
+        }
384 400
       }
385 401
     }
386 402
     else { // +direction
387 403
       CHECK_ENDSTOPS 
388 404
       {
389
-        #if defined(X_MAX_PIN) && X_MAX_PIN > -1
390
-          bool x_max_endstop=(READ(X_MAX_PIN) != X_ENDSTOPS_INVERTING);
391
-          if(x_max_endstop && old_x_max_endstop && (current_block->steps_x > 0)){
392
-            endstops_trigsteps[X_AXIS] = count_position[X_AXIS];
393
-            endstop_x_hit=true;
394
-            step_events_completed = current_block->step_event_count;
395
-          }
396
-          old_x_max_endstop = x_max_endstop;
397
-        #endif
405
+        #ifdef DUAL_X_CARRIAGE
406
+        // with 2 x-carriages, endstops are only checked in the homing direction for the active extruder
407
+        if ((active_extruder == 0 && X_HOME_DIR == 1) || (active_extruder != 0 && X2_HOME_DIR == 1))
408
+        #endif          
409
+        {
410
+          #if defined(X_MAX_PIN) && X_MAX_PIN > -1
411
+            bool x_max_endstop=(READ(X_MAX_PIN) != X_ENDSTOPS_INVERTING);
412
+            if(x_max_endstop && old_x_max_endstop && (current_block->steps_x > 0)){
413
+              endstops_trigsteps[X_AXIS] = count_position[X_AXIS];
414
+              endstop_x_hit=true;
415
+              step_events_completed = current_block->step_event_count;
416
+            }
417
+            old_x_max_endstop = x_max_endstop;
418
+          #endif
419
+        }  
398 420
       }
399 421
     }
400 422
 
@@ -507,10 +529,20 @@ ISR(TIMER1_COMPA_vect)
507 529
 
508 530
         counter_x += current_block->steps_x;
509 531
         if (counter_x > 0) {
510
-          WRITE(X_STEP_PIN, !INVERT_X_STEP_PIN);
532
+          #ifdef DUAL_X_CARRIAGE
533
+          if (active_extruder != 0)
534
+            WRITE(X2_STEP_PIN,!INVERT_X_STEP_PIN);
535
+          else
536
+          #endif        
537
+            WRITE(X_STEP_PIN, !INVERT_X_STEP_PIN);
511 538
           counter_x -= current_block->step_event_count;
512 539
           count_position[X_AXIS]+=count_direction[X_AXIS];   
513
-          WRITE(X_STEP_PIN, INVERT_X_STEP_PIN);
540
+          #ifdef DUAL_X_CARRIAGE
541
+          if (active_extruder != 0)
542
+            WRITE(X2_STEP_PIN,INVERT_X_STEP_PIN);
543
+          else
544
+          #endif        
545
+            WRITE(X_STEP_PIN, INVERT_X_STEP_PIN);
514 546
         }
515 547
   
516 548
         counter_y += current_block->steps_y;
@@ -685,6 +717,9 @@ void st_init()
685 717
   #if defined(X_DIR_PIN) && X_DIR_PIN > -1
686 718
     SET_OUTPUT(X_DIR_PIN);
687 719
   #endif
720
+  #if defined(X2_DIR_PIN) && X2_DIR_PIN > -1
721
+    SET_OUTPUT(X2_DIR_PIN);
722
+  #endif
688 723
   #if defined(Y_DIR_PIN) && Y_DIR_PIN > -1 
689 724
     SET_OUTPUT(Y_DIR_PIN);
690 725
   #endif
@@ -711,6 +746,10 @@ void st_init()
711 746
     SET_OUTPUT(X_ENABLE_PIN);
712 747
     if(!X_ENABLE_ON) WRITE(X_ENABLE_PIN,HIGH);
713 748
   #endif
749
+  #if defined(X2_ENABLE_PIN) && X2_ENABLE_PIN > -1
750
+    SET_OUTPUT(X2_ENABLE_PIN);
751
+    if(!X_ENABLE_ON) WRITE(X2_ENABLE_PIN,HIGH);
752
+  #endif
714 753
   #if defined(Y_ENABLE_PIN) && Y_ENABLE_PIN > -1
715 754
     SET_OUTPUT(Y_ENABLE_PIN);
716 755
     if(!Y_ENABLE_ON) WRITE(Y_ENABLE_PIN,HIGH);
@@ -788,6 +827,11 @@ void st_init()
788 827
     WRITE(X_STEP_PIN,INVERT_X_STEP_PIN);
789 828
     disable_x();
790 829
   #endif  
830
+  #if defined(X2_STEP_PIN) && (X2_STEP_PIN > -1) 
831
+    SET_OUTPUT(X2_STEP_PIN);
832
+    WRITE(X2_STEP_PIN,INVERT_X_STEP_PIN);
833
+    disable_x();
834
+  #endif  
791 835
   #if defined(Y_STEP_PIN) && (Y_STEP_PIN > -1) 
792 836
     SET_OUTPUT(Y_STEP_PIN);
793 837
     WRITE(Y_STEP_PIN,INVERT_Y_STEP_PIN);

+ 2
- 0
README.md View File

@@ -41,6 +41,8 @@ Features:
41 41
 *   Heater power reporting. Useful for PID monitoring.
42 42
 *   PID tuning
43 43
 *   CoreXY kinematics (www.corexy.com/theory.html)
44
+*   Delta kinematics
45
+*   Dual X-carriage support for multiple extruder systems
44 46
 *   Configurable serial port to support connection of wireless adaptors.
45 47
 *   Automatic operation of extruder/cold-end cooling fans based on nozzle temperature
46 48
 *   RC Servo Support, specify angle or duration for continuous rotation servos.

Loading…
Cancel
Save