Browse Source

Add gcode.cpp, motion.*, queue.* - Apply to some G-codes.

Scott Lahteine 7 years ago
parent
commit
722786966a
42 changed files with 1898 additions and 1370 deletions
  1. 19
    1139
      Marlin/src/Marlin.cpp
  2. 4
    122
      Marlin/src/Marlin.h
  3. 2
    0
      Marlin/src/core/utility.h
  4. 61
    0
      Marlin/src/feature/mbl/mesh_bed_leveling.cpp
  5. 2
    0
      Marlin/src/feature/mbl/mesh_bed_leveling.h
  6. 1
    1
      Marlin/src/feature/ubl/G26_Mesh_Validation_Tool.cpp
  7. 1
    0
      Marlin/src/feature/ubl/ubl.cpp
  8. 1
    0
      Marlin/src/feature/ubl/ubl_motion.cpp
  9. 2
    0
      Marlin/src/gcode/calibrate/G29-mbl.h
  10. 5
    2
      Marlin/src/gcode/config/M200.cpp
  11. 1
    1
      Marlin/src/gcode/config/M201.h
  12. 1
    1
      Marlin/src/gcode/config/M203.h
  13. 11
    2
      Marlin/src/gcode/config/M218.cpp
  14. 5
    2
      Marlin/src/gcode/config/M221.cpp
  15. 4
    2
      Marlin/src/gcode/config/M43.h
  16. 1
    1
      Marlin/src/gcode/config/M92.h
  17. 3
    1
      Marlin/src/gcode/control/M999.h
  18. 267
    62
      Marlin/src/gcode/gcode.cpp
  19. 28
    4
      Marlin/src/gcode/gcode.h
  20. 2
    0
      Marlin/src/gcode/host/M110.h
  21. 3
    1
      Marlin/src/gcode/lcd/M0_M1.h
  22. 12
    3
      Marlin/src/gcode/motion/G0_G1.cpp
  23. 4
    2
      Marlin/src/gcode/motion/G2_G3.h
  24. 2
    1
      Marlin/src/gcode/motion/G5.h
  25. 1
    1
      Marlin/src/gcode/parser.h
  26. 3
    1
      Marlin/src/gcode/probe/G38.h
  27. 473
    0
      Marlin/src/gcode/queue.cpp
  28. 106
    0
      Marlin/src/gcode/queue.h
  29. 19
    7
      Marlin/src/gcode/temperature/M104.cpp
  30. 1
    1
      Marlin/src/gcode/temperature/M105.h
  31. 16
    2
      Marlin/src/gcode/temperature/M109.cpp
  32. 4
    2
      Marlin/src/gcode/temperature/M190.h
  33. 4
    1
      Marlin/src/gcode/temperature/M303.cpp
  34. 2
    2
      Marlin/src/gcode/units/M82_M83.h
  35. 7
    4
      Marlin/src/lcd/ultralcd.cpp
  36. 1
    0
      Marlin/src/libs/nozzle.cpp
  37. 574
    0
      Marlin/src/module/motion.cpp
  38. 237
    0
      Marlin/src/module/motion.h
  39. 1
    0
      Marlin/src/module/planner.cpp
  40. 5
    2
      Marlin/src/module/planner_bezier.cpp
  41. 1
    0
      Marlin/src/module/stepper.cpp
  42. 1
    0
      Marlin/src/sd/cardreader.cpp

+ 19
- 1139
Marlin/src/Marlin.cpp
File diff suppressed because it is too large
View File


+ 4
- 122
Marlin/src/Marlin.h View File

@@ -167,9 +167,6 @@ void enable_all_steppers();
167 167
 void disable_e_steppers();
168 168
 void disable_all_steppers();
169 169
 
170
-void FlushSerialRequestResend();
171
-void ok_to_send();
172
-
173 170
 void kill(const char*);
174 171
 
175 172
 void quickstop_stepper();
@@ -182,13 +179,6 @@ extern bool Running;
182 179
 inline bool IsRunning() { return  Running; }
183 180
 inline bool IsStopped() { return !Running; }
184 181
 
185
-bool enqueue_and_echo_command(const char* cmd, bool say_ok=false); // Add a single command to the end of the buffer. Return false on failure.
186
-void enqueue_and_echo_commands_P(const char * const cmd);          // Set one or more commands to be prioritized over the next Serial/SD command.
187
-void clear_command_queue();
188
-
189
-extern millis_t previous_cmd_ms;
190
-inline void refresh_cmd_timeout() { previous_cmd_ms = millis(); }
191
-
192 182
 /**
193 183
  * Feedrate scaling and conversion
194 184
  */
@@ -196,11 +186,11 @@ extern int16_t feedrate_percentage;
196 186
 
197 187
 #define MMS_SCALED(MM_S) ((MM_S)*feedrate_percentage*0.01)
198 188
 
199
-extern bool axis_relative_modes[];
200 189
 extern bool volumetric_enabled;
201 190
 extern int16_t flow_percentage[EXTRUDERS]; // Extrusion factor for each extruder
202 191
 extern float filament_size[EXTRUDERS]; // cross-sectional area of filament (in millimeters), typically around 1.75 or 2.85, 0 disables the volumetric calculations for the extruder.
203 192
 extern float volumetric_multiplier[EXTRUDERS]; // reciprocal of cross-sectional area of filament (in square millimeters), stored this way to reduce computational burden in planner
193
+
204 194
 extern bool axis_known_position[XYZ];
205 195
 extern bool axis_homed[XYZ];
206 196
 extern volatile bool wait_for_heatup;
@@ -209,48 +199,6 @@ extern volatile bool wait_for_heatup;
209 199
   extern volatile bool wait_for_user;
210 200
 #endif
211 201
 
212
-extern float current_position[NUM_AXIS];
213
-
214
-// Workspace offsets
215
-#if HAS_WORKSPACE_OFFSET
216
-  #if HAS_HOME_OFFSET
217
-    extern float home_offset[XYZ];
218
-  #endif
219
-  #if HAS_POSITION_SHIFT
220
-    extern float position_shift[XYZ];
221
-  #endif
222
-#endif
223
-
224
-#if HAS_HOME_OFFSET && HAS_POSITION_SHIFT
225
-  extern float workspace_offset[XYZ];
226
-  #define WORKSPACE_OFFSET(AXIS) workspace_offset[AXIS]
227
-#elif HAS_HOME_OFFSET
228
-  #define WORKSPACE_OFFSET(AXIS) home_offset[AXIS]
229
-#elif HAS_POSITION_SHIFT
230
-  #define WORKSPACE_OFFSET(AXIS) position_shift[AXIS]
231
-#else
232
-  #define WORKSPACE_OFFSET(AXIS) 0
233
-#endif
234
-
235
-#define LOGICAL_POSITION(POS, AXIS) ((POS) + WORKSPACE_OFFSET(AXIS))
236
-#define RAW_POSITION(POS, AXIS)     ((POS) - WORKSPACE_OFFSET(AXIS))
237
-
238
-#if HAS_POSITION_SHIFT || DISABLED(DELTA)
239
-  #define LOGICAL_X_POSITION(POS)   LOGICAL_POSITION(POS, X_AXIS)
240
-  #define LOGICAL_Y_POSITION(POS)   LOGICAL_POSITION(POS, Y_AXIS)
241
-  #define RAW_X_POSITION(POS)       RAW_POSITION(POS, X_AXIS)
242
-  #define RAW_Y_POSITION(POS)       RAW_POSITION(POS, Y_AXIS)
243
-#else
244
-  #define LOGICAL_X_POSITION(POS)   (POS)
245
-  #define LOGICAL_Y_POSITION(POS)   (POS)
246
-  #define RAW_X_POSITION(POS)       (POS)
247
-  #define RAW_Y_POSITION(POS)       (POS)
248
-#endif
249
-
250
-#define LOGICAL_Z_POSITION(POS)     LOGICAL_POSITION(POS, Z_AXIS)
251
-#define RAW_Z_POSITION(POS)         RAW_POSITION(POS, Z_AXIS)
252
-#define RAW_CURRENT_POSITION(A)     RAW_##A##_POSITION(current_position[A##_AXIS])
253
-
254 202
 // Hotend Offsets
255 203
 #if HOTENDS > 1
256 204
   extern float hotend_offset[XYZ][HOTENDS];
@@ -259,14 +207,6 @@ extern float current_position[NUM_AXIS];
259 207
 // Software Endstops
260 208
 extern float soft_endstop_min[XYZ], soft_endstop_max[XYZ];
261 209
 
262
-#if HAS_SOFTWARE_ENDSTOPS
263
-  extern bool soft_endstops_enabled;
264
-  void clamp_to_software_endstops(float target[XYZ]);
265
-#else
266
-  #define soft_endstops_enabled false
267
-  #define clamp_to_software_endstops(x) NOOP
268
-#endif
269
-
270 210
 #if HAS_WORKSPACE_OFFSET || ENABLED(DUAL_X_CARRIAGE)
271 211
   void update_software_endstops(const AxisEnum axis);
272 212
 #endif
@@ -381,15 +321,15 @@ extern float soft_endstop_min[XYZ], soft_endstop_max[XYZ];
381 321
   extern Stopwatch print_job_timer;
382 322
 #endif
383 323
 
384
-// Handling multiple extruders pins
385
-extern uint8_t active_extruder;
386
-
387 324
 #if HAS_TEMP_HOTEND || HAS_TEMP_BED
388 325
   void print_heaterstates();
389 326
 #endif
390 327
 
391 328
 #if ENABLED(MIXING_EXTRUDER)
392 329
   extern float mixing_factor[MIXING_STEPPERS];
330
+  #if MIXING_VIRTUAL_TOOLS > 1
331
+    extern float mixing_virtual_tool_mix[MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS];
332
+  #endif
393 333
 #endif
394 334
 
395 335
 void calculate_volumetric_multipliers();
@@ -406,62 +346,4 @@ void do_blocking_move_to_xy(const float &x, const float &y, const float &fr_mm_s
406 346
   bool axis_unhomed_error(const bool x=true, const bool y=true, const bool z=true);
407 347
 #endif
408 348
 
409
-/**
410
- * position_is_reachable family of functions
411
- */
412
-
413
-#if IS_KINEMATIC // (DELTA or SCARA)
414
-
415
-  #if IS_SCARA
416
-    extern const float L1, L2;
417
-  #endif
418
-
419
-  inline bool position_is_reachable_raw_xy(const float &rx, const float &ry) {
420
-    #if ENABLED(DELTA)
421
-      return HYPOT2(rx, ry) <= sq(DELTA_PRINTABLE_RADIUS);
422
-    #elif IS_SCARA
423
-      #if MIDDLE_DEAD_ZONE_R > 0
424
-        const float R2 = HYPOT2(rx - SCARA_OFFSET_X, ry - SCARA_OFFSET_Y);
425
-        return R2 >= sq(float(MIDDLE_DEAD_ZONE_R)) && R2 <= sq(L1 + L2);
426
-      #else
427
-        return HYPOT2(rx - SCARA_OFFSET_X, ry - SCARA_OFFSET_Y) <= sq(L1 + L2);
428
-      #endif
429
-    #else // CARTESIAN
430
-      // To be migrated from MakerArm branch in future
431
-    #endif
432
-  }
433
-
434
-  inline bool position_is_reachable_by_probe_raw_xy(const float &rx, const float &ry) {
435
-
436
-    // Both the nozzle and the probe must be able to reach the point.
437
-    // This won't work on SCARA since the probe offset rotates with the arm.
438
-
439
-    return position_is_reachable_raw_xy(rx, ry)
440
-        && position_is_reachable_raw_xy(rx - X_PROBE_OFFSET_FROM_EXTRUDER, ry - Y_PROBE_OFFSET_FROM_EXTRUDER);
441
-  }
442
-
443
-#else // CARTESIAN
444
-
445
-  inline bool position_is_reachable_raw_xy(const float &rx, const float &ry) {
446
-      // Add 0.001 margin to deal with float imprecision
447
-      return WITHIN(rx, X_MIN_POS - 0.001, X_MAX_POS + 0.001)
448
-          && WITHIN(ry, Y_MIN_POS - 0.001, Y_MAX_POS + 0.001);
449
-  }
450
-
451
-  inline bool position_is_reachable_by_probe_raw_xy(const float &rx, const float &ry) {
452
-      // Add 0.001 margin to deal with float imprecision
453
-      return WITHIN(rx, MIN_PROBE_X - 0.001, MAX_PROBE_X + 0.001)
454
-          && WITHIN(ry, MIN_PROBE_Y - 0.001, MAX_PROBE_Y + 0.001);
455
-  }
456
-
457
-#endif // CARTESIAN
458
-
459
-FORCE_INLINE bool position_is_reachable_by_probe_xy(const float &lx, const float &ly) {
460
-  return position_is_reachable_by_probe_raw_xy(RAW_X_POSITION(lx), RAW_Y_POSITION(ly));
461
-}
462
-
463
-FORCE_INLINE bool position_is_reachable_xy(const float &lx, const float &ly) {
464
-  return position_is_reachable_raw_xy(RAW_X_POSITION(lx), RAW_Y_POSITION(ly));
465
-}
466
-
467 349
 #endif // __MARLIN_H__

+ 2
- 0
Marlin/src/core/utility.h View File

@@ -25,6 +25,8 @@
25 25
 
26 26
 #include "../inc/MarlinConfig.h"
27 27
 
28
+constexpr char axis_codes[XYZE] = { 'X', 'Y', 'Z', 'E' };
29
+
28 30
 void safe_delay(millis_t ms);
29 31
 
30 32
 #if ENABLED(EEPROM_SETTINGS)

+ 61
- 0
Marlin/src/feature/mbl/mesh_bed_leveling.cpp View File

@@ -26,6 +26,8 @@
26 26
 
27 27
   #include "mesh_bed_leveling.h"
28 28
 
29
+  #include "../../module/motion.h"
30
+
29 31
   mesh_bed_leveling mbl;
30 32
 
31 33
   uint8_t mesh_bed_leveling::status;
@@ -49,4 +51,63 @@
49 51
     ZERO(z_values);
50 52
   }
51 53
 
54
+  /**
55
+   * Prepare a mesh-leveled linear move in a Cartesian setup,
56
+   * splitting the move where it crosses mesh borders.
57
+   */
58
+  void mesh_line_to_destination(const float fr_mm_s, uint8_t x_splits, uint8_t y_splits) {
59
+    int cx1 = mbl.cell_index_x(RAW_CURRENT_POSITION(X)),
60
+        cy1 = mbl.cell_index_y(RAW_CURRENT_POSITION(Y)),
61
+        cx2 = mbl.cell_index_x(RAW_X_POSITION(destination[X_AXIS])),
62
+        cy2 = mbl.cell_index_y(RAW_Y_POSITION(destination[Y_AXIS]));
63
+    NOMORE(cx1, GRID_MAX_POINTS_X - 2);
64
+    NOMORE(cy1, GRID_MAX_POINTS_Y - 2);
65
+    NOMORE(cx2, GRID_MAX_POINTS_X - 2);
66
+    NOMORE(cy2, GRID_MAX_POINTS_Y - 2);
67
+
68
+    if (cx1 == cx2 && cy1 == cy2) {
69
+      // Start and end on same mesh square
70
+      line_to_destination(fr_mm_s);
71
+      set_current_to_destination();
72
+      return;
73
+    }
74
+
75
+    #define MBL_SEGMENT_END(A) (current_position[A ##_AXIS] + (destination[A ##_AXIS] - current_position[A ##_AXIS]) * normalized_dist)
76
+
77
+    float normalized_dist, end[XYZE];
78
+
79
+    // Split at the left/front border of the right/top square
80
+    const int8_t gcx = max(cx1, cx2), gcy = max(cy1, cy2);
81
+    if (cx2 != cx1 && TEST(x_splits, gcx)) {
82
+      COPY(end, destination);
83
+      destination[X_AXIS] = LOGICAL_X_POSITION(mbl.index_to_xpos[gcx]);
84
+      normalized_dist = (destination[X_AXIS] - current_position[X_AXIS]) / (end[X_AXIS] - current_position[X_AXIS]);
85
+      destination[Y_AXIS] = MBL_SEGMENT_END(Y);
86
+      CBI(x_splits, gcx);
87
+    }
88
+    else if (cy2 != cy1 && TEST(y_splits, gcy)) {
89
+      COPY(end, destination);
90
+      destination[Y_AXIS] = LOGICAL_Y_POSITION(mbl.index_to_ypos[gcy]);
91
+      normalized_dist = (destination[Y_AXIS] - current_position[Y_AXIS]) / (end[Y_AXIS] - current_position[Y_AXIS]);
92
+      destination[X_AXIS] = MBL_SEGMENT_END(X);
93
+      CBI(y_splits, gcy);
94
+    }
95
+    else {
96
+      // Already split on a border
97
+      line_to_destination(fr_mm_s);
98
+      set_current_to_destination();
99
+      return;
100
+    }
101
+
102
+    destination[Z_AXIS] = MBL_SEGMENT_END(Z);
103
+    destination[E_AXIS] = MBL_SEGMENT_END(E);
104
+
105
+    // Do the split and look for more borders
106
+    mesh_line_to_destination(fr_mm_s, x_splits, y_splits);
107
+
108
+    // Restore destination from stack
109
+    COPY(destination, end);
110
+    mesh_line_to_destination(fr_mm_s, x_splits, y_splits);
111
+  }
112
+
52 113
 #endif // MESH_BED_LEVELING

+ 2
- 0
Marlin/src/feature/mbl/mesh_bed_leveling.h View File

@@ -120,4 +120,6 @@ public:
120 120
 
121 121
 extern mesh_bed_leveling mbl;
122 122
 
123
+void mesh_line_to_destination(const float fr_mm_s, uint8_t x_splits=0xFF, uint8_t y_splits=0xFF);
124
+
123 125
 #endif // _MESH_BED_LEVELING_H_

+ 1
- 1
Marlin/src/feature/ubl/G26_Mesh_Validation_Tool.cpp View File

@@ -33,6 +33,7 @@
33 33
   #include "../../Marlin.h"
34 34
   #include "../../module/planner.h"
35 35
   #include "../../module/stepper.h"
36
+  #include "../../module/motion.h"
36 37
   #include "../../module/temperature.h"
37 38
   #include "../../lcd/ultralcd.h"
38 39
   #include "../../gcode/parser.h"
@@ -129,7 +130,6 @@
129 130
 
130 131
   // External references
131 132
 
132
-  extern float feedrate_mm_s; // must set before calling prepare_move_to_destination
133 133
   extern Planner planner;
134 134
   #if ENABLED(ULTRA_LCD)
135 135
     extern char lcd_status_message[];

+ 1
- 0
Marlin/src/feature/ubl/ubl.cpp View File

@@ -30,6 +30,7 @@
30 30
   #include "../../module/configuration_store.h"
31 31
   #include "../../core/serial.h"
32 32
   #include "../../module/planner.h"
33
+  #include "../../module/motion.h"
33 34
 
34 35
   #include "math.h"
35 36
 

+ 1
- 0
Marlin/src/feature/ubl/ubl_motion.cpp View File

@@ -28,6 +28,7 @@
28 28
   #include "../../Marlin.h"
29 29
   #include "../../module/planner.h"
30 30
   #include "../../module/stepper.h"
31
+  #include "../../module/motion.h"
31 32
 
32 33
   #include <math.h>
33 34
 

+ 2
- 0
Marlin/src/gcode/calibrate/G29-mbl.h View File

@@ -20,6 +20,8 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../queue.h"
24
+
23 25
 #include "../../libs/buzzer.h"
24 26
 #include "../../lcd/ultralcd.h"
25 27
 

Marlin/src/gcode/config/M200.h → Marlin/src/gcode/config/M200.cpp View File

@@ -20,15 +20,18 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+#include "../../Marlin.h"
25
+
23 26
 /**
24 27
  * M200: Set filament diameter and set E axis units to cubic units
25 28
  *
26 29
  *    T<extruder> - Optional extruder number. Current extruder if omitted.
27 30
  *    D<linear> - Diameter of the filament. Use "D0" to switch back to linear units on the E axis.
28 31
  */
29
-void gcode_M200() {
32
+void GcodeSuite::M200() {
30 33
 
31
-  if (get_target_extruder_from_command(200)) return;
34
+  if (get_target_extruder_from_command()) return;
32 35
 
33 36
   if (parser.seen('D')) {
34 37
     // setting any extruder filament size disables volumetric on the assumption that

+ 1
- 1
Marlin/src/gcode/config/M201.h View File

@@ -27,7 +27,7 @@
27 27
  */
28 28
 void gcode_M201() {
29 29
 
30
-  GET_TARGET_EXTRUDER(201);
30
+  GET_TARGET_EXTRUDER();
31 31
 
32 32
   LOOP_XYZE(i) {
33 33
     if (parser.seen(axis_codes[i])) {

+ 1
- 1
Marlin/src/gcode/config/M203.h View File

@@ -27,7 +27,7 @@
27 27
  */
28 28
 void gcode_M203() {
29 29
 
30
-  GET_TARGET_EXTRUDER(203);
30
+  GET_TARGET_EXTRUDER();
31 31
 
32 32
   LOOP_XYZE(i)
33 33
     if (parser.seen(axis_codes[i])) {

Marlin/src/gcode/config/M218.h → Marlin/src/gcode/config/M218.cpp View File

@@ -20,6 +20,13 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../../inc/MarlinConfig.h"
24
+
25
+#if HOTENDS > 1
26
+
27
+#include "../gcode.h"
28
+#include "../../module/motion.h"
29
+
23 30
 /**
24 31
  * M218 - set hotend offset (in linear units)
25 32
  *
@@ -28,8 +35,8 @@
28 35
  *   Y<yoffset>
29 36
  *   Z<zoffset> - Available with DUAL_X_CARRIAGE and SWITCHING_NOZZLE
30 37
  */
31
-void gcode_M218() {
32
-  if (get_target_extruder_from_command(218) || target_extruder == 0) return;
38
+void GcodeSuite::M218() {
39
+  if (get_target_extruder_from_command() || target_extruder == 0) return;
33 40
 
34 41
   if (parser.seenval('X')) hotend_offset[X_AXIS][target_extruder] = parser.value_linear_units();
35 42
   if (parser.seenval('Y')) hotend_offset[Y_AXIS][target_extruder] = parser.value_linear_units();
@@ -52,3 +59,5 @@ void gcode_M218() {
52 59
   }
53 60
   SERIAL_EOL();
54 61
 }
62
+
63
+#endif // HOTENDS > 1

Marlin/src/gcode/config/M221.h → Marlin/src/gcode/config/M221.cpp View File

@@ -20,11 +20,14 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+#include "../../Marlin.h"
25
+
23 26
 /**
24 27
  * M221: Set extrusion percentage (M221 T0 S95)
25 28
  */
26
-void gcode_M221() {
27
-  if (get_target_extruder_from_command(221)) return;
29
+void GcodeSuite::M221() {
30
+  if (get_target_extruder_from_command()) return;
28 31
   if (parser.seenval('S'))
29 32
     flow_percentage[target_extruder] = parser.value_int();
30 33
 }

+ 4
- 2
Marlin/src/gcode/config/M43.h View File

@@ -20,6 +20,8 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+
23 25
 #include "../../pins/pinsDebug.h"
24 26
 
25 27
 inline void toggle_pins() {
@@ -141,7 +143,7 @@ inline void servo_probe_test() {
141 143
     }
142 144
     if (probe_inverting != deploy_state) SERIAL_PROTOCOLLNPGM("WARNING - INVERTING setting probably backwards");
143 145
 
144
-    refresh_cmd_timeout();
146
+    gcode.refresh_cmd_timeout();
145 147
 
146 148
     if (deploy_state != stow_state) {
147 149
       SERIAL_PROTOCOLLNPGM("BLTouch clone detected");
@@ -170,7 +172,7 @@ inline void servo_probe_test() {
170 172
         safe_delay(2);
171 173
 
172 174
         if (0 == j % (500 * 1)) // keep cmd_timeout happy
173
-          refresh_cmd_timeout();
175
+          gcode.refresh_cmd_timeout();
174 176
 
175 177
         if (deploy_state != READ(PROBE_TEST_PIN)) { // probe triggered
176 178
 

+ 1
- 1
Marlin/src/gcode/config/M92.h View File

@@ -28,7 +28,7 @@
28 28
  */
29 29
 void gcode_M92() {
30 30
 
31
-  GET_TARGET_EXTRUDER(92);
31
+  GET_TARGET_EXTRUDER();
32 32
 
33 33
   LOOP_XYZE(i) {
34 34
     if (parser.seen(axis_codes[i])) {

+ 3
- 1
Marlin/src/gcode/control/M999.h View File

@@ -20,6 +20,8 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../queue.h"
24
+
23 25
 /**
24 26
  * M999: Restart after being stopped
25 27
  *
@@ -37,5 +39,5 @@ void gcode_M999() {
37 39
   if (parser.boolval('S')) return;
38 40
 
39 41
   // gcode_LastN = Stopped_gcode_LastN;
40
-  FlushSerialRequestResend();
42
+  flush_and_request_resend();
41 43
 }

Marlin/src/gcode/process_next_command.h → Marlin/src/gcode/gcode.cpp View File

@@ -21,10 +21,242 @@
21 21
  */
22 22
 
23 23
 /**
24
+ * gcode.cpp - Temporary container for all gcode handlers
25
+ *             Most will migrate to classes, by feature.
26
+ */
27
+
28
+#include "gcode.h"
29
+GcodeSuite gcode;
30
+
31
+#include "parser.h"
32
+#include "queue.h"
33
+#include "../module/motion.h"
34
+
35
+#if ENABLED(PRINTCOUNTER)
36
+  #include "../module/printcounter.h"
37
+#endif
38
+
39
+uint8_t GcodeSuite::target_extruder;
40
+millis_t GcodeSuite::previous_cmd_ms;
41
+
42
+bool GcodeSuite::axis_relative_modes[] = AXIS_RELATIVE_MODES;
43
+
44
+/**
45
+ * Set target_extruder from the T parameter or the active_extruder
46
+ *
47
+ * Returns TRUE if the target is invalid
48
+ */
49
+bool GcodeSuite::get_target_extruder_from_command() {
50
+  if (parser.seenval('T')) {
51
+    const int8_t e = parser.value_byte();
52
+    if (e >= EXTRUDERS) {
53
+      SERIAL_ECHO_START();
54
+      SERIAL_CHAR('M');
55
+      SERIAL_ECHO(parser.codenum);
56
+      SERIAL_ECHOLNPAIR(" " MSG_INVALID_EXTRUDER " ", e);
57
+      return true;
58
+    }
59
+    target_extruder = e;
60
+  }
61
+  else
62
+    target_extruder = active_extruder;
63
+
64
+  return false;
65
+}
66
+
67
+/**
68
+ * Set XYZE destination and feedrate from the current GCode command
69
+ *
70
+ *  - Set destination from included axis codes
71
+ *  - Set to current for missing axis codes
72
+ *  - Set the feedrate, if included
73
+ */
74
+void GcodeSuite::get_destination_from_command() {
75
+  LOOP_XYZE(i) {
76
+    if (parser.seen(axis_codes[i]))
77
+      destination[i] = parser.value_axis_units((AxisEnum)i) + (axis_relative_modes[i] || relative_mode ? current_position[i] : 0);
78
+    else
79
+      destination[i] = current_position[i];
80
+  }
81
+
82
+  if (parser.linearval('F') > 0.0)
83
+    feedrate_mm_s = MMM_TO_MMS(parser.value_feedrate());
84
+
85
+  #if ENABLED(PRINTCOUNTER)
86
+    if (!DEBUGGING(DRYRUN))
87
+      print_job_timer.incFilamentUsed(destination[E_AXIS] - current_position[E_AXIS]);
88
+  #endif
89
+
90
+  // Get ABCDHI mixing factors
91
+  #if ENABLED(MIXING_EXTRUDER) && ENABLED(DIRECT_MIXING_IN_G1)
92
+    gcode_get_mix();
93
+  #endif
94
+}
95
+
96
+//
97
+// Placeholders for non-migrated codes
98
+//
99
+extern void gcode_G0_G1(
100
+  #if IS_SCARA
101
+    bool fast_move=false
102
+  #endif
103
+);
104
+extern void gcode_G2_G3(bool clockwise);
105
+extern void gcode_G4();
106
+extern void gcode_G5();
107
+extern void gcode_G12();
108
+extern void gcode_G17();
109
+extern void gcode_G18();
110
+extern void gcode_G19();
111
+extern void gcode_G20();
112
+extern void gcode_G21();
113
+extern void gcode_G26();
114
+extern void gcode_G27();
115
+extern void gcode_G28(const bool always_home_all);
116
+extern void gcode_G29();
117
+extern void gcode_G30();
118
+extern void gcode_G31();
119
+extern void gcode_G32();
120
+extern void gcode_G33();
121
+extern void gcode_G38(bool is_38_2);
122
+extern void gcode_G42();
123
+extern void gcode_G92();
124
+extern void gcode_M0_M1();
125
+extern void gcode_M3_M4(bool is_M3);
126
+extern void gcode_M5();
127
+extern void gcode_M17();
128
+extern void gcode_M18_M84();
129
+extern void gcode_M20();
130
+extern void gcode_M21();
131
+extern void gcode_M22();
132
+extern void gcode_M23();
133
+extern void gcode_M24();
134
+extern void gcode_M25();
135
+extern void gcode_M26();
136
+extern void gcode_M27();
137
+extern void gcode_M28();
138
+extern void gcode_M29();
139
+extern void gcode_M30();
140
+extern void gcode_M31();
141
+extern void gcode_M32();
142
+extern void gcode_M33();
143
+extern void gcode_M34();
144
+extern void gcode_M42();
145
+extern void gcode_M43();
146
+extern void gcode_M48();
147
+extern void gcode_M49();
148
+extern void gcode_M75();
149
+extern void gcode_M76();
150
+extern void gcode_M77();
151
+extern void gcode_M78();
152
+extern void gcode_M80();
153
+extern void gcode_M81();
154
+extern void gcode_M82();
155
+extern void gcode_M83();
156
+extern void gcode_M85();
157
+extern void gcode_M92();
158
+extern void gcode_M100();
159
+extern void gcode_M105();
160
+extern void gcode_M106();
161
+extern void gcode_M107();
162
+extern void gcode_M108();
163
+extern void gcode_M110();
164
+extern void gcode_M111();
165
+extern void gcode_M112();
166
+extern void gcode_M113();
167
+extern void gcode_M114();
168
+extern void gcode_M115();
169
+extern void gcode_M117();
170
+extern void gcode_M118();
171
+extern void gcode_M119();
172
+extern void gcode_M120();
173
+extern void gcode_M121();
174
+extern void gcode_M125();
175
+extern void gcode_M126();
176
+extern void gcode_M127();
177
+extern void gcode_M128();
178
+extern void gcode_M129();
179
+extern void gcode_M140();
180
+extern void gcode_M145();
181
+extern void gcode_M149();
182
+extern void gcode_M150();
183
+extern void gcode_M155();
184
+extern void gcode_M163();
185
+extern void gcode_M164();
186
+extern void gcode_M165();
187
+extern void gcode_M190();
188
+extern void gcode_M201();
189
+extern void gcode_M203();
190
+extern void gcode_M204();
191
+extern void gcode_M205();
192
+extern void gcode_M206();
193
+extern void gcode_M211();
194
+extern void gcode_M220();
195
+extern void gcode_M226();
196
+extern void gcode_M240();
197
+extern void gcode_M250();
198
+extern void gcode_M260();
199
+extern void gcode_M261();
200
+extern void gcode_M280();
201
+extern void gcode_M300();
202
+extern void gcode_M301();
203
+extern void gcode_M302();
204
+extern void gcode_M304();
205
+extern void gcode_M350();
206
+extern void gcode_M351();
207
+extern void gcode_M355();
208
+extern bool gcode_M360();
209
+extern bool gcode_M361();
210
+extern bool gcode_M362();
211
+extern bool gcode_M363();
212
+extern bool gcode_M364();
213
+extern void gcode_M380();
214
+extern void gcode_M381();
215
+extern void gcode_M400();
216
+extern void gcode_M401();
217
+extern void gcode_M402();
218
+extern void gcode_M404();
219
+extern void gcode_M405();
220
+extern void gcode_M406();
221
+extern void gcode_M407();
222
+extern void gcode_M410();
223
+extern void gcode_M420();
224
+extern void gcode_M421();
225
+extern void gcode_M428();
226
+extern void gcode_M500();
227
+extern void gcode_M501();
228
+extern void gcode_M502();
229
+extern void gcode_M503();
230
+extern void gcode_M540();
231
+extern void gcode_M600();
232
+extern void gcode_M605();
233
+extern void gcode_M665();
234
+extern void gcode_M666();
235
+extern void gcode_M702();
236
+extern void gcode_M851();
237
+extern void gcode_M900();
238
+extern void gcode_M906();
239
+extern void gcode_M911();
240
+extern void gcode_M912();
241
+extern void gcode_M913();
242
+extern void gcode_M914();
243
+extern void gcode_M907();
244
+extern void gcode_M908();
245
+extern void gcode_M909();
246
+extern void gcode_M910();
247
+extern void gcode_M928();
248
+extern void gcode_M999();
249
+extern void gcode_T(uint8_t tmp_extruder);
250
+
251
+#if ENABLED(M100_FREE_MEMORY_WATCHER)
252
+  extern void M100_dump_routine(const char * const title, const char *start, const char *end);
253
+#endif
254
+
255
+/**
24 256
  * Process a single command and dispatch it to its handler
25 257
  * This is called from the main loop()
26 258
  */
27
-void process_next_command() {
259
+void GcodeSuite::process_next_command() {
28 260
   char * const current_command = command_queue[cmd_queue_index_r];
29 261
 
30 262
   if (DEBUGGING(ECHO)) {
@@ -49,9 +281,9 @@ void process_next_command() {
49 281
       case 0:
50 282
       case 1:
51 283
         #if IS_SCARA
52
-          gcode_G0_G1(parser.codenum == 0);
284
+          G0_G1(parser.codenum == 0);
53 285
         #else
54
-          gcode_G0_G1();
286
+          G0_G1();
55 287
         #endif
56 288
         break;
57 289
 
@@ -76,10 +308,10 @@ void process_next_command() {
76 308
 
77 309
       #if ENABLED(FWRETRACT)
78 310
         case 10: // G10: retract
79
-          gcode_G10();
311
+          G10();
80 312
           break;
81 313
         case 11: // G11: retract_recover
82
-          gcode_G11();
314
+          G11();
83 315
           break;
84 316
       #endif // FWRETRACT
85 317
 
@@ -303,9 +535,8 @@ void process_next_command() {
303 535
           break;
304 536
       #endif
305 537
 
306
-      case 104: // M104: Set hot end temperature
307
-        gcode_M104();
308
-        break;
538
+      case 104: M104(); break;    // M104: Set hot end temperature
539
+      case 109: M109(); break;    // M109: Wait for hotend temperature to reach target
309 540
 
310 541
       case 110: // M110: Set Current Line Number
311 542
         gcode_M110();
@@ -353,10 +584,6 @@ void process_next_command() {
353 584
           break;
354 585
       #endif
355 586
 
356
-      case 109: // M109: Wait for hotend temperature to reach target
357
-        gcode_M109();
358
-        break;
359
-
360 587
       #if HAS_TEMP_BED
361 588
         case 190: // M190: Wait for bed temperature to reach target
362 589
           gcode_M190();
@@ -488,7 +715,7 @@ void process_next_command() {
488 715
       #endif
489 716
 
490 717
       case 200: // M200: Set filament diameter, E to cubic units
491
-        gcode_M200();
718
+        M200();
492 719
         break;
493 720
       case 201: // M201: Set max acceleration for print moves (units/s^2)
494 721
         gcode_M201();
@@ -528,13 +755,13 @@ void process_next_command() {
528 755
 
529 756
       #if ENABLED(FWRETRACT)
530 757
         case 207: // M207: Set Retract Length, Feedrate, and Z lift
531
-          gcode_M207();
758
+          M207();
532 759
           break;
533 760
         case 208: // M208: Set Recover (unretract) Additional Length and Feedrate
534
-          gcode_M208();
761
+          M208();
535 762
           break;
536 763
         case 209: // M209: Turn Automatic Retract Detection on/off
537
-          if (MIN_AUTORETRACT <= MAX_AUTORETRACT) gcode_M209();
764
+          if (MIN_AUTORETRACT <= MAX_AUTORETRACT) M209();
538 765
           break;
539 766
       #endif // FWRETRACT
540 767
 
@@ -544,7 +771,7 @@ void process_next_command() {
544 771
 
545 772
       #if HOTENDS > 1
546 773
         case 218: // M218: Set a tool offset
547
-          gcode_M218();
774
+          M218();
548 775
           break;
549 776
       #endif
550 777
 
@@ -553,7 +780,7 @@ void process_next_command() {
553 780
         break;
554 781
 
555 782
       case 221: // M221: Set Flow Percentage
556
-        gcode_M221();
783
+        M221();
557 784
         break;
558 785
 
559 786
       case 226: // M226: Wait until a pin reaches a state
@@ -615,7 +842,7 @@ void process_next_command() {
615 842
       #endif // PREVENT_COLD_EXTRUSION
616 843
 
617 844
       case 303: // M303: PID autotune
618
-        gcode_M303();
845
+        M303();
619 846
         break;
620 847
 
621 848
       #if ENABLED(MORGAN_SCARA)
@@ -636,6 +863,15 @@ void process_next_command() {
636 863
           break;
637 864
       #endif // SCARA
638 865
 
866
+      #if ENABLED(EXT_SOLENOID)
867
+        case 380: // M380: Activate solenoid on active extruder
868
+          gcode_M380();
869
+          break;
870
+        case 381: // M381: Disable all solenoids
871
+          gcode_M381();
872
+          break;
873
+      #endif
874
+
639 875
       case 400: // M400: Finish all moves
640 876
         gcode_M400();
641 877
         break;
@@ -809,48 +1045,17 @@ void process_next_command() {
809 1045
       #endif
810 1046
 
811 1047
       #if ENABLED(I2C_POSITION_ENCODERS)
812
-
813
-        case 860: // M860 Report encoder module position
814
-          gcode_M860();
815
-          break;
816
-
817
-        case 861: // M861 Report encoder module status
818
-          gcode_M861();
819
-          break;
820
-
821
-        case 862: // M862 Perform axis test
822
-          gcode_M862();
823
-          break;
824
-
825
-        case 863: // M863 Calibrate steps/mm
826
-          gcode_M863();
827
-          break;
828
-
829
-        case 864: // M864 Change module address
830
-          gcode_M864();
831
-          break;
832
-
833
-        case 865: // M865 Check module firmware version
834
-          gcode_M865();
835
-          break;
836
-
837
-        case 866: // M866 Report axis error count
838
-          gcode_M866();
839
-          break;
840
-
841
-        case 867: // M867 Toggle error correction
842
-          gcode_M867();
843
-          break;
844
-
845
-        case 868: // M868 Set error correction threshold
846
-          gcode_M868();
847
-          break;
848
-
849
-        case 869: // M869 Report axis error
850
-          gcode_M869();
851
-          break;
852
-
853
-      #endif // I2C_POSITION_ENCODERS
1048
+        case 860: M860(); break; // M860: Report encoder module position
1049
+        case 861: M861(); break; // M861: Report encoder module status
1050
+        case 862: M862(); break; // M862: Perform axis test
1051
+        case 863: M863(); break; // M863: Calibrate steps/mm
1052
+        case 864: M864(); break; // M864: Change module address
1053
+        case 865: M865(); break; // M865: Check module firmware version
1054
+        case 866: M866(); break; // M866: Report axis error count
1055
+        case 867: M867(); break; // M867: Toggle error correction
1056
+        case 868: M868(); break; // M868: Set error correction threshold
1057
+        case 869: M869(); break; // M869: Report axis error
1058
+      #endif
854 1059
 
855 1060
       case 999: // M999: Restart after being Stopped
856 1061
         gcode_M999();
@@ -868,4 +1073,4 @@ void process_next_command() {
868 1073
   KEEPALIVE_STATE(NOT_BUSY);
869 1074
 
870 1075
   ok_to_send();
871
-}
1076
+}

+ 28
- 4
Marlin/src/gcode/gcode.h View File

@@ -237,8 +237,8 @@
237 237
  *
238 238
  */
239 239
 
240
-#ifndef GCODE_H
241
-#define GCODE_H
240
+#ifndef _GCODE_H_
241
+#define _GCODE_H_
242 242
 
243 243
 #include "../inc/MarlinConfig.h"
244 244
 #include "parser.h"
@@ -252,6 +252,30 @@ public:
252 252
 
253 253
   GcodeSuite() {}
254 254
 
255
+  static uint8_t target_extruder;
256
+
257
+  static bool axis_relative_modes[];
258
+
259
+  static millis_t previous_cmd_ms;
260
+  FORCE_INLINE static void refresh_cmd_timeout() { previous_cmd_ms = millis(); }
261
+
262
+  static bool get_target_extruder_from_command();
263
+  static void get_destination_from_command();
264
+  static void process_next_command();
265
+
266
+  /**
267
+   * Multi-stepper support for M92, M201, M203
268
+   */
269
+  #if ENABLED(DISTINCT_E_FACTORS)
270
+    #define GET_TARGET_EXTRUDER() if (gcode.get_target_extruder_from_command()) return
271
+    #define TARGET_EXTRUDER gcode.target_extruder
272
+  #else
273
+    #define GET_TARGET_EXTRUDER() NOOP
274
+    #define TARGET_EXTRUDER 0
275
+  #endif
276
+
277
+  static FORCE_INLINE void home_all_axes() { G28(true); }
278
+
255 279
 private:
256 280
 
257 281
   static void G0_G1(
@@ -375,7 +399,7 @@ private:
375 399
     static void M48();
376 400
   #endif
377 401
 
378
-  #if ENABLED(AUTO_BED_LEVELING_UBL) && ENABLED(UBL_G26_MESH_VALIDATION)
402
+  #if ENABLED(UBL_G26_MESH_VALIDATION)
379 403
     static void M49();
380 404
   #endif
381 405
 
@@ -679,4 +703,4 @@ private:
679 703
 
680 704
 extern GcodeSuite gcode;
681 705
 
682
-#endif // GCODE_H
706
+#endif // _GCODE_H_

+ 2
- 0
Marlin/src/gcode/host/M110.h View File

@@ -20,6 +20,8 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../queue.h"
24
+
23 25
 /**
24 26
  * M110: Set Current Line Number
25 27
  */

+ 3
- 1
Marlin/src/gcode/lcd/M0_M1.h View File

@@ -20,6 +20,8 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+
23 25
 /**
24 26
  * M0: Unconditional stop - Wait for user button press on LCD
25 27
  * M1: Conditional stop   - Wait for user button press on LCD
@@ -62,7 +64,7 @@ void gcode_M0_M1() {
62 64
   wait_for_user = true;
63 65
 
64 66
   stepper.synchronize();
65
-  refresh_cmd_timeout();
67
+  gcode.refresh_cmd_timeout();
66 68
 
67 69
   if (ms > 0) {
68 70
     ms += previous_cmd_ms;  // wait until this time for a click

Marlin/src/gcode/motion/G0_G1.h → Marlin/src/gcode/motion/G0_G1.cpp View File

@@ -20,16 +20,25 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+#include "../../module/motion.h"
25
+
26
+#include "../../Marlin.h"
27
+
28
+#include "../../sd/cardreader.h"
29
+
30
+extern float destination[XYZE];
31
+
23 32
 /**
24 33
  * G0, G1: Coordinated movement of X Y Z E axes
25 34
  */
26
-void gcode_G0_G1(
35
+void GcodeSuite::G0_G1(
27 36
   #if IS_SCARA
28
-    bool fast_move=false
37
+    bool fast_move/*=false*/
29 38
   #endif
30 39
 ) {
31 40
   if (IsRunning()) {
32
-    gcode_get_destination(); // For X Y Z E F
41
+    get_destination_from_command(); // For X Y Z E F
33 42
 
34 43
     #if ENABLED(FWRETRACT)
35 44
       if (MIN_AUTORETRACT <= MAX_AUTORETRACT) {

+ 4
- 2
Marlin/src/gcode/motion/G2_G3.h View File

@@ -20,6 +20,8 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+
23 25
 #if N_ARC_CORRECTION < 1
24 26
   #undef N_ARC_CORRECTION
25 27
   #define N_ARC_CORRECTION 1
@@ -209,7 +211,7 @@ void gcode_G2_G3(bool clockwise) {
209 211
       relative_mode = true;
210 212
     #endif
211 213
 
212
-    gcode_get_destination();
214
+    gcode.get_destination_from_command();
213 215
 
214 216
     #if ENABLED(SF_ARC_FIX)
215 217
       relative_mode = relative_mode_backup;
@@ -252,7 +254,7 @@ void gcode_G2_G3(bool clockwise) {
252 254
 
253 255
       // Send the arc to the planner
254 256
       plan_arc(destination, arc_offset, clockwise);
255
-      refresh_cmd_timeout();
257
+      gcode.refresh_cmd_timeout();
256 258
     }
257 259
     else {
258 260
       // Bad arguments

+ 2
- 1
Marlin/src/gcode/motion/G5.h View File

@@ -21,6 +21,7 @@
21 21
  */
22 22
 
23 23
 #include "../../module/planner_bezier.h"
24
+#include "../../gcode/gcode.h"
24 25
 
25 26
 void plan_cubic_move(const float offset[4]) {
26 27
   cubic_b_spline(current_position, destination, offset, MMS_SCALED(feedrate_mm_s), active_extruder);
@@ -52,7 +53,7 @@ void gcode_G5() {
52 53
       }
53 54
     #endif
54 55
 
55
-    gcode_get_destination();
56
+    gcode.get_destination_from_command();
56 57
 
57 58
     const float offset[] = {
58 59
       parser.linearval('I'),

+ 1
- 1
Marlin/src/gcode/parser.h View File

@@ -242,7 +242,7 @@ public:
242 242
       FORCE_INLINE static char temp_units_code() {
243 243
         return input_temp_units == TEMPUNIT_K ? 'K' : input_temp_units == TEMPUNIT_F ? 'F' : 'C';
244 244
       }
245
-      FORCE_INLINE static char* temp_units_name() {
245
+      FORCE_INLINE static const char* temp_units_name() {
246 246
         return input_temp_units == TEMPUNIT_K ? PSTR("Kelvin") : input_temp_units == TEMPUNIT_F ? PSTR("Fahrenheit") : PSTR("Celsius");
247 247
       }
248 248
       inline static float to_temp_units(const float &f) {

+ 3
- 1
Marlin/src/gcode/probe/G38.h View File

@@ -20,6 +20,8 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+
23 25
 static bool G38_run_probe() {
24 26
 
25 27
   bool G38_pass_fail = false;
@@ -88,7 +90,7 @@ static bool G38_run_probe() {
88 90
  */
89 91
 void gcode_G38(bool is_38_2) {
90 92
   // Get X Y Z E F
91
-  gcode_get_destination();
93
+  gcode.get_destination_from_command();
92 94
 
93 95
   setup_for_endstop_or_probe_move();
94 96
 

+ 473
- 0
Marlin/src/gcode/queue.cpp View File

@@ -0,0 +1,473 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * queue.cpp - The G-code command queue
25
+ */
26
+
27
+#include "queue.h"
28
+#include "gcode.h"
29
+
30
+#include "../lcd/ultralcd.h"
31
+#include "../sd/cardreader.h"
32
+#include "../module/planner.h"
33
+#include "../Marlin.h"
34
+
35
+/**
36
+ * GCode line number handling. Hosts may opt to include line numbers when
37
+ * sending commands to Marlin, and lines will be checked for sequentiality.
38
+ * M110 N<int> sets the current line number.
39
+ */
40
+long gcode_N, gcode_LastN, Stopped_gcode_LastN = 0;
41
+
42
+/**
43
+ * GCode Command Queue
44
+ * A simple ring buffer of BUFSIZE command strings.
45
+ *
46
+ * Commands are copied into this buffer by the command injectors
47
+ * (immediate, serial, sd card) and they are processed sequentially by
48
+ * the main loop. The gcode.process_next_command method parses the next
49
+ * command and hands off execution to individual handler functions.
50
+ */
51
+uint8_t commands_in_queue = 0, // Count of commands in the queue
52
+        cmd_queue_index_r = 0, // Ring buffer read position
53
+        cmd_queue_index_w = 0; // Ring buffer write position
54
+
55
+char command_queue[BUFSIZE][MAX_CMD_SIZE];
56
+
57
+/**
58
+ * Serial command injection
59
+ */
60
+
61
+// Number of characters read in the current line of serial input
62
+static int serial_count = 0;
63
+
64
+bool send_ok[BUFSIZE];
65
+
66
+/**
67
+ * Next Injected Command pointer. NULL if no commands are being injected.
68
+ * Used by Marlin internally to ensure that commands initiated from within
69
+ * are enqueued ahead of any pending serial or sd card commands.
70
+ */
71
+static const char *injected_commands_P = NULL;
72
+
73
+void queue_setup() {
74
+  // Send "ok" after commands by default
75
+  for (uint8_t i = 0; i < COUNT(send_ok); i++) send_ok[i] = true;
76
+}
77
+
78
+/**
79
+ * Clear the Marlin command queue
80
+ */
81
+void clear_command_queue() {
82
+  cmd_queue_index_r = cmd_queue_index_w;
83
+  commands_in_queue = 0;
84
+}
85
+
86
+/**
87
+ * Once a new command is in the ring buffer, call this to commit it
88
+ */
89
+inline void _commit_command(bool say_ok) {
90
+  send_ok[cmd_queue_index_w] = say_ok;
91
+  if (++cmd_queue_index_w >= BUFSIZE) cmd_queue_index_w = 0;
92
+  commands_in_queue++;
93
+}
94
+
95
+/**
96
+ * Copy a command from RAM into the main command buffer.
97
+ * Return true if the command was successfully added.
98
+ * Return false for a full buffer, or if the 'command' is a comment.
99
+ */
100
+inline bool _enqueuecommand(const char* cmd, bool say_ok/*=false*/) {
101
+  if (*cmd == ';' || commands_in_queue >= BUFSIZE) return false;
102
+  strcpy(command_queue[cmd_queue_index_w], cmd);
103
+  _commit_command(say_ok);
104
+  return true;
105
+}
106
+
107
+/**
108
+ * Enqueue with Serial Echo
109
+ */
110
+bool enqueue_and_echo_command(const char* cmd, bool say_ok/*=false*/) {
111
+  if (_enqueuecommand(cmd, say_ok)) {
112
+    SERIAL_ECHO_START();
113
+    SERIAL_ECHOPAIR(MSG_ENQUEUEING, cmd);
114
+    SERIAL_CHAR('"');
115
+    SERIAL_EOL();
116
+    return true;
117
+  }
118
+  return false;
119
+}
120
+
121
+/**
122
+ * Inject the next "immediate" command, when possible, onto the front of the queue.
123
+ * Return true if any immediate commands remain to inject.
124
+ */
125
+static bool drain_injected_commands_P() {
126
+  if (injected_commands_P != NULL) {
127
+    size_t i = 0;
128
+    char c, cmd[30];
129
+    strncpy_P(cmd, injected_commands_P, sizeof(cmd) - 1);
130
+    cmd[sizeof(cmd) - 1] = '\0';
131
+    while ((c = cmd[i]) && c != '\n') i++; // find the end of this gcode command
132
+    cmd[i] = '\0';
133
+    if (enqueue_and_echo_command(cmd))     // success?
134
+      injected_commands_P = c ? injected_commands_P + i + 1 : NULL; // next command or done
135
+  }
136
+  return (injected_commands_P != NULL);    // return whether any more remain
137
+}
138
+
139
+/**
140
+ * Record one or many commands to run from program memory.
141
+ * Aborts the current queue, if any.
142
+ * Note: drain_injected_commands_P() must be called repeatedly to drain the commands afterwards
143
+ */
144
+void enqueue_and_echo_commands_P(const char * const pgcode) {
145
+  injected_commands_P = pgcode;
146
+  drain_injected_commands_P(); // first command executed asap (when possible)
147
+}
148
+
149
+/**
150
+ * Send an "ok" message to the host, indicating
151
+ * that a command was successfully processed.
152
+ *
153
+ * If ADVANCED_OK is enabled also include:
154
+ *   N<int>  Line number of the command, if any
155
+ *   P<int>  Planner space remaining
156
+ *   B<int>  Block queue space remaining
157
+ */
158
+void ok_to_send() {
159
+  gcode.refresh_cmd_timeout();
160
+  if (!send_ok[cmd_queue_index_r]) return;
161
+  SERIAL_PROTOCOLPGM(MSG_OK);
162
+  #if ENABLED(ADVANCED_OK)
163
+    char* p = command_queue[cmd_queue_index_r];
164
+    if (*p == 'N') {
165
+      SERIAL_PROTOCOL(' ');
166
+      SERIAL_ECHO(*p++);
167
+      while (NUMERIC_SIGNED(*p))
168
+        SERIAL_ECHO(*p++);
169
+    }
170
+    SERIAL_PROTOCOLPGM(" P"); SERIAL_PROTOCOL(int(BLOCK_BUFFER_SIZE - planner.movesplanned() - 1));
171
+    SERIAL_PROTOCOLPGM(" B"); SERIAL_PROTOCOL(BUFSIZE - commands_in_queue);
172
+  #endif
173
+  SERIAL_EOL();
174
+}
175
+
176
+/**
177
+ * Send a "Resend: nnn" message to the host to
178
+ * indicate that a command needs to be re-sent.
179
+ */
180
+void flush_and_request_resend() {
181
+  //char command_queue[cmd_queue_index_r][100]="Resend:";
182
+  MYSERIAL.flush();
183
+  SERIAL_PROTOCOLPGM(MSG_RESEND);
184
+  SERIAL_PROTOCOLLN(gcode_LastN + 1);
185
+  ok_to_send();
186
+}
187
+
188
+void gcode_line_error(const char* err, bool doFlush = true) {
189
+  SERIAL_ERROR_START();
190
+  serialprintPGM(err);
191
+  SERIAL_ERRORLN(gcode_LastN);
192
+  //Serial.println(gcode_N);
193
+  if (doFlush) flush_and_request_resend();
194
+  serial_count = 0;
195
+}
196
+
197
+/**
198
+ * Get all commands waiting on the serial port and queue them.
199
+ * Exit when the buffer is full or when no more characters are
200
+ * left on the serial port.
201
+ */
202
+inline void get_serial_commands() {
203
+  static char serial_line_buffer[MAX_CMD_SIZE];
204
+  static bool serial_comment_mode = false;
205
+
206
+  // If the command buffer is empty for too long,
207
+  // send "wait" to indicate Marlin is still waiting.
208
+  #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0
209
+    static millis_t last_command_time = 0;
210
+    const millis_t ms = millis();
211
+    if (commands_in_queue == 0 && !MYSERIAL.available() && ELAPSED(ms, last_command_time + NO_TIMEOUTS)) {
212
+      SERIAL_ECHOLNPGM(MSG_WAIT);
213
+      last_command_time = ms;
214
+    }
215
+  #endif
216
+
217
+  /**
218
+   * Loop while serial characters are incoming and the queue is not full
219
+   */
220
+  while (commands_in_queue < BUFSIZE && MYSERIAL.available() > 0) {
221
+
222
+    char serial_char = MYSERIAL.read();
223
+
224
+    /**
225
+     * If the character ends the line
226
+     */
227
+    if (serial_char == '\n' || serial_char == '\r') {
228
+
229
+      serial_comment_mode = false; // end of line == end of comment
230
+
231
+      if (!serial_count) continue; // skip empty lines
232
+
233
+      serial_line_buffer[serial_count] = 0; // terminate string
234
+      serial_count = 0; //reset buffer
235
+
236
+      char* command = serial_line_buffer;
237
+
238
+      while (*command == ' ') command++; // skip any leading spaces
239
+      char *npos = (*command == 'N') ? command : NULL, // Require the N parameter to start the line
240
+           *apos = strchr(command, '*');
241
+
242
+      if (npos) {
243
+
244
+        bool M110 = strstr_P(command, PSTR("M110")) != NULL;
245
+
246
+        if (M110) {
247
+          char* n2pos = strchr(command + 4, 'N');
248
+          if (n2pos) npos = n2pos;
249
+        }
250
+
251
+        gcode_N = strtol(npos + 1, NULL, 10);
252
+
253
+        if (gcode_N != gcode_LastN + 1 && !M110) {
254
+          gcode_line_error(PSTR(MSG_ERR_LINE_NO));
255
+          return;
256
+        }
257
+
258
+        if (apos) {
259
+          byte checksum = 0, count = 0;
260
+          while (command[count] != '*') checksum ^= command[count++];
261
+
262
+          if (strtol(apos + 1, NULL, 10) != checksum) {
263
+            gcode_line_error(PSTR(MSG_ERR_CHECKSUM_MISMATCH));
264
+            return;
265
+          }
266
+          // if no errors, continue parsing
267
+        }
268
+        else {
269
+          gcode_line_error(PSTR(MSG_ERR_NO_CHECKSUM));
270
+          return;
271
+        }
272
+
273
+        gcode_LastN = gcode_N;
274
+        // if no errors, continue parsing
275
+      }
276
+      else if (apos) { // No '*' without 'N'
277
+        gcode_line_error(PSTR(MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM), false);
278
+        return;
279
+      }
280
+
281
+      // Movement commands alert when stopped
282
+      if (IsStopped()) {
283
+        char* gpos = strchr(command, 'G');
284
+        if (gpos) {
285
+          const int codenum = strtol(gpos + 1, NULL, 10);
286
+          switch (codenum) {
287
+            case 0:
288
+            case 1:
289
+            case 2:
290
+            case 3:
291
+              SERIAL_ERRORLNPGM(MSG_ERR_STOPPED);
292
+              LCD_MESSAGEPGM(MSG_STOPPED);
293
+              break;
294
+          }
295
+        }
296
+      }
297
+
298
+      #if DISABLED(EMERGENCY_PARSER)
299
+        // If command was e-stop process now
300
+        if (strcmp(command, "M108") == 0) {
301
+          wait_for_heatup = false;
302
+          #if ENABLED(ULTIPANEL)
303
+            wait_for_user = false;
304
+          #endif
305
+        }
306
+        if (strcmp(command, "M112") == 0) kill(PSTR(MSG_KILLED));
307
+        if (strcmp(command, "M410") == 0) { quickstop_stepper(); }
308
+      #endif
309
+
310
+      #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0
311
+        last_command_time = ms;
312
+      #endif
313
+
314
+      // Add the command to the queue
315
+      _enqueuecommand(serial_line_buffer, true);
316
+    }
317
+    else if (serial_count >= MAX_CMD_SIZE - 1) {
318
+      // Keep fetching, but ignore normal characters beyond the max length
319
+      // The command will be injected when EOL is reached
320
+    }
321
+    else if (serial_char == '\\') {  // Handle escapes
322
+      if (MYSERIAL.available() > 0) {
323
+        // if we have one more character, copy it over
324
+        serial_char = MYSERIAL.read();
325
+        if (!serial_comment_mode) serial_line_buffer[serial_count++] = serial_char;
326
+      }
327
+      // otherwise do nothing
328
+    }
329
+    else { // it's not a newline, carriage return or escape char
330
+      if (serial_char == ';') serial_comment_mode = true;
331
+      if (!serial_comment_mode) serial_line_buffer[serial_count++] = serial_char;
332
+    }
333
+
334
+  } // queue has space, serial has data
335
+}
336
+
337
+#if ENABLED(SDSUPPORT)
338
+
339
+  /**
340
+   * Get commands from the SD Card until the command buffer is full
341
+   * or until the end of the file is reached. The special character '#'
342
+   * can also interrupt buffering.
343
+   */
344
+  inline void get_sdcard_commands() {
345
+    static bool stop_buffering = false,
346
+                sd_comment_mode = false;
347
+
348
+    if (!IS_SD_PRINTING) return;
349
+
350
+    /**
351
+     * '#' stops reading from SD to the buffer prematurely, so procedural
352
+     * macro calls are possible. If it occurs, stop_buffering is triggered
353
+     * and the buffer is run dry; this character _can_ occur in serial com
354
+     * due to checksums, however, no checksums are used in SD printing.
355
+     */
356
+
357
+    if (commands_in_queue == 0) stop_buffering = false;
358
+
359
+    uint16_t sd_count = 0;
360
+    bool card_eof = card.eof();
361
+    while (commands_in_queue < BUFSIZE && !card_eof && !stop_buffering) {
362
+      const int16_t n = card.get();
363
+      char sd_char = (char)n;
364
+      card_eof = card.eof();
365
+      if (card_eof || n == -1
366
+          || sd_char == '\n' || sd_char == '\r'
367
+          || ((sd_char == '#' || sd_char == ':') && !sd_comment_mode)
368
+      ) {
369
+        if (card_eof) {
370
+          SERIAL_PROTOCOLLNPGM(MSG_FILE_PRINTED);
371
+          card.printingHasFinished();
372
+          #if ENABLED(PRINTER_EVENT_LEDS)
373
+            LCD_MESSAGEPGM(MSG_INFO_COMPLETED_PRINTS);
374
+            set_led_color(0, 255, 0); // Green
375
+            #if HAS_RESUME_CONTINUE
376
+              enqueue_and_echo_commands_P(PSTR("M0")); // end of the queue!
377
+            #else
378
+              safe_delay(1000);
379
+            #endif
380
+            set_led_color(0, 0, 0);   // OFF
381
+          #endif
382
+          card.checkautostart(true);
383
+        }
384
+        else if (n == -1) {
385
+          SERIAL_ERROR_START();
386
+          SERIAL_ECHOLNPGM(MSG_SD_ERR_READ);
387
+        }
388
+        if (sd_char == '#') stop_buffering = true;
389
+
390
+        sd_comment_mode = false; // for new command
391
+
392
+        if (!sd_count) continue; // skip empty lines (and comment lines)
393
+
394
+        command_queue[cmd_queue_index_w][sd_count] = '\0'; // terminate string
395
+        sd_count = 0; // clear sd line buffer
396
+
397
+        _commit_command(false);
398
+      }
399
+      else if (sd_count >= MAX_CMD_SIZE - 1) {
400
+        /**
401
+         * Keep fetching, but ignore normal characters beyond the max length
402
+         * The command will be injected when EOL is reached
403
+         */
404
+      }
405
+      else {
406
+        if (sd_char == ';') sd_comment_mode = true;
407
+        if (!sd_comment_mode) command_queue[cmd_queue_index_w][sd_count++] = sd_char;
408
+      }
409
+    }
410
+  }
411
+
412
+#endif // SDSUPPORT
413
+
414
+/**
415
+ * Add to the circular command queue the next command from:
416
+ *  - The command-injection queue (injected_commands_P)
417
+ *  - The active serial input (usually USB)
418
+ *  - The SD card file being actively printed
419
+ */
420
+void get_available_commands() {
421
+
422
+  // if any immediate commands remain, don't get other commands yet
423
+  if (drain_injected_commands_P()) return;
424
+
425
+  get_serial_commands();
426
+
427
+  #if ENABLED(SDSUPPORT)
428
+    get_sdcard_commands();
429
+  #endif
430
+}
431
+
432
+/**
433
+ * Get the next command in the queue, optionally log it to SD, then dispatch it
434
+ */
435
+void advance_command_queue() {
436
+
437
+  if (!commands_in_queue) return;
438
+
439
+  #if ENABLED(SDSUPPORT)
440
+
441
+    if (card.saving) {
442
+      char* command = command_queue[cmd_queue_index_r];
443
+      if (strstr_P(command, PSTR("M29"))) {
444
+        // M29 closes the file
445
+        card.closefile();
446
+        SERIAL_PROTOCOLLNPGM(MSG_FILE_SAVED);
447
+        ok_to_send();
448
+      }
449
+      else {
450
+        // Write the string from the read buffer to SD
451
+        card.write_command(command);
452
+        if (card.logging)
453
+          gcode.process_next_command(); // The card is saving because it's logging
454
+        else
455
+          ok_to_send();
456
+      }
457
+    }
458
+    else
459
+      gcode.process_next_command();
460
+
461
+  #else
462
+
463
+    gcode.process_next_command();
464
+
465
+  #endif // SDSUPPORT
466
+
467
+  // The queue may be reset by a command handler or by code invoked by idle() within a handler
468
+  if (commands_in_queue) {
469
+    --commands_in_queue;
470
+    if (++cmd_queue_index_r >= BUFSIZE) cmd_queue_index_r = 0;
471
+  }
472
+
473
+}

+ 106
- 0
Marlin/src/gcode/queue.h View File

@@ -0,0 +1,106 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * queue.h - The G-code command queue, which holds commands before they
25
+ *           go to the parser and dispatcher.
26
+ */
27
+
28
+#ifndef GCODE_QUEUE_H
29
+#define GCODE_QUEUE_H
30
+
31
+#include "../inc/MarlinConfig.h"
32
+
33
+/**
34
+ * GCode line number handling. Hosts may include line numbers when sending
35
+ * commands to Marlin, and lines will be checked for sequentiality.
36
+ * M110 N<int> sets the current line number.
37
+ */
38
+extern long gcode_LastN, Stopped_gcode_LastN;
39
+
40
+/**
41
+ * GCode Command Queue
42
+ * A simple ring buffer of BUFSIZE command strings.
43
+ *
44
+ * Commands are copied into this buffer by the command injectors
45
+ * (immediate, serial, sd card) and they are processed sequentially by
46
+ * the main loop. The gcode.process_next_command method parses the next
47
+ * command and hands off execution to individual handler functions.
48
+ */
49
+extern uint8_t commands_in_queue, // Count of commands in the queue
50
+               cmd_queue_index_r; // Ring buffer read position
51
+
52
+extern char command_queue[BUFSIZE][MAX_CMD_SIZE];
53
+
54
+/**
55
+ * Initialization of queue for setup()
56
+ */
57
+void queue_setup();
58
+
59
+/**
60
+ * Clear the Marlin command queue
61
+ */
62
+void clear_command_queue();
63
+
64
+/**
65
+ * Clear the serial line and request a resend of
66
+ * the next expected line number.
67
+ */
68
+void flush_and_request_resend();
69
+
70
+/**
71
+ * Send an "ok" message to the host, indicating
72
+ * that a command was successfully processed.
73
+ *
74
+ * If ADVANCED_OK is enabled also include:
75
+ *   N<int>  Line number of the command, if any
76
+ *   P<int>  Planner space remaining
77
+ *   B<int>  Block queue space remaining
78
+ */
79
+void ok_to_send();
80
+
81
+/**
82
+ * Record one or many commands to run from program memory.
83
+ * Aborts the current queue, if any.
84
+ * Note: drain_injected_commands_P() must be called repeatedly to drain the commands afterwards
85
+ */
86
+void enqueue_and_echo_commands_P(const char * const pgcode);
87
+
88
+/**
89
+ * Enqueue with Serial Echo
90
+ */
91
+bool enqueue_and_echo_command(const char* cmd, bool say_ok=false);
92
+
93
+/**
94
+ * Add to the circular command queue the next command from:
95
+ *  - The command-injection queue (injected_commands_P)
96
+ *  - The active serial input (usually USB)
97
+ *  - The SD card file being actively printed
98
+ */
99
+void get_available_commands();
100
+
101
+/**
102
+ * Get the next command in the queue, optionally log it to SD, then dispatch it
103
+ */
104
+void advance_command_queue();
105
+
106
+#endif // GCODE_QUEUE_H

Marlin/src/gcode/temperature/M104.h → Marlin/src/gcode/temperature/M104.cpp View File

@@ -20,23 +20,35 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+#include "../../module/temperature.h"
25
+#include "../../module/motion.h"
26
+#include "../../module/planner.h"
27
+#include "../../lcd/ultralcd.h"
28
+
29
+#if ENABLED(PRINTJOB_TIMER_AUTOSTART)
30
+  #include "../../module/printcounter.h"
31
+#endif
32
+
23 33
 /**
24 34
  * M104: Set hot end temperature
25 35
  */
26
-void gcode_M104() {
27
-  if (get_target_extruder_from_command(104)) return;
36
+void GcodeSuite::M104() {
37
+  if (get_target_extruder_from_command()) return;
28 38
   if (DEBUGGING(DRYRUN)) return;
29 39
 
40
+  const uint8_t e = target_extruder;
41
+
30 42
   #if ENABLED(SINGLENOZZLE)
31
-    if (target_extruder != active_extruder) return;
43
+    if (e != active_extruder) return;
32 44
   #endif
33 45
 
34 46
   if (parser.seenval('S')) {
35 47
     const int16_t temp = parser.value_celsius();
36
-    thermalManager.setTargetHotend(temp, target_extruder);
48
+    thermalManager.setTargetHotend(temp, e);
37 49
 
38 50
     #if ENABLED(DUAL_X_CARRIAGE)
39
-      if (dual_x_carriage_mode == DXC_DUPLICATION_MODE && target_extruder == 0)
51
+      if (dual_x_carriage_mode == DXC_DUPLICATION_MODE && e == 0)
40 52
         thermalManager.setTargetHotend(temp ? temp + duplicate_extruder_temp_offset : 0, 1);
41 53
     #endif
42 54
 
@@ -53,8 +65,8 @@ void gcode_M104() {
53 65
       }
54 66
     #endif
55 67
 
56
-    if (parser.value_celsius() > thermalManager.degHotend(target_extruder))
57
-      lcd_status_printf_P(0, PSTR("E%i %s"), target_extruder + 1, MSG_HEATING);
68
+    if (parser.value_celsius() > thermalManager.degHotend(e))
69
+      lcd_status_printf_P(0, PSTR("E%i %s"), e + 1, MSG_HEATING);
58 70
   }
59 71
 
60 72
   #if ENABLED(AUTOTEMP)

+ 1
- 1
Marlin/src/gcode/temperature/M105.h View File

@@ -24,7 +24,7 @@
24 24
  * M105: Read hot end and bed temperature
25 25
  */
26 26
 void gcode_M105() {
27
-  if (get_target_extruder_from_command(105)) return;
27
+  if (gcode.get_target_extruder_from_command()) return;
28 28
 
29 29
   #if HAS_TEMP_HOTEND || HAS_TEMP_BED
30 30
     SERIAL_PROTOCOLPGM(MSG_OK);

Marlin/src/gcode/temperature/M109.h → Marlin/src/gcode/temperature/M109.cpp View File

@@ -20,6 +20,20 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+#include "../../module/temperature.h"
25
+#include "../../module/planner.h"
26
+#include "../../lcd/ultralcd.h"
27
+#include "../../Marlin.h"
28
+
29
+#if ENABLED(PRINTJOB_TIMER_AUTOSTART)
30
+  #include "../../module/printcounter.h"
31
+#endif
32
+
33
+#if ENABLED(DUAL_X_CARRIAGE)
34
+  #include "../../module/motion.h"
35
+#endif
36
+
23 37
 /**
24 38
  * M109: Sxxx Wait for extruder(s) to reach temperature. Waits only when heating.
25 39
  *       Rxxx Wait for extruder(s) to reach temperature. Waits when heating and cooling.
@@ -32,9 +46,9 @@
32 46
   #define MIN_COOLING_SLOPE_TIME 60
33 47
 #endif
34 48
 
35
-void gcode_M109() {
49
+void GcodeSuite::M109() {
36 50
 
37
-  if (get_target_extruder_from_command(109)) return;
51
+  if (get_target_extruder_from_command()) return;
38 52
   if (DEBUGGING(DRYRUN)) return;
39 53
 
40 54
   #if ENABLED(SINGLENOZZLE)

+ 4
- 2
Marlin/src/gcode/temperature/M190.h View File

@@ -20,6 +20,8 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+
23 25
 #ifndef MIN_COOLING_SLOPE_DEG_BED
24 26
   #define MIN_COOLING_SLOPE_DEG_BED 1.50
25 27
 #endif
@@ -63,7 +65,7 @@ void gcode_M190() {
63 65
     KEEPALIVE_STATE(NOT_BUSY);
64 66
   #endif
65 67
 
66
-  target_extruder = active_extruder; // for print_heaterstates
68
+  gcode.target_extruder = active_extruder; // for print_heaterstates
67 69
 
68 70
   #if ENABLED(PRINTER_EVENT_LEDS)
69 71
     const float start_temp = thermalManager.degBed();
@@ -95,7 +97,7 @@ void gcode_M190() {
95 97
     }
96 98
 
97 99
     idle();
98
-    refresh_cmd_timeout(); // to prevent stepper_inactive_time from running out
100
+    gcode.refresh_cmd_timeout(); // to prevent stepper_inactive_time from running out
99 101
 
100 102
     const float temp = thermalManager.degBed();
101 103
 

Marlin/src/gcode/temperature/M303.h → Marlin/src/gcode/temperature/M303.cpp View File

@@ -20,6 +20,9 @@
20 20
  *
21 21
  */
22 22
 
23
+#include "../gcode.h"
24
+#include "../../module/temperature.h"
25
+
23 26
 /**
24 27
  * M303: PID relay autotune
25 28
  *
@@ -28,7 +31,7 @@
28 31
  *       C<cycles>
29 32
  *       U<bool> with a non-zero value will apply the result to current settings
30 33
  */
31
-void gcode_M303() {
34
+void GcodeSuite::M303() {
32 35
   #if HAS_PID_HEATING
33 36
     const int e = parser.intval('E'), c = parser.intval('C', 5);
34 37
     const bool u = parser.boolval('U');

+ 2
- 2
Marlin/src/gcode/units/M82_M83.h View File

@@ -23,9 +23,9 @@
23 23
 /**
24 24
  * M82: Set E codes absolute (default)
25 25
  */
26
-void gcode_M82() { axis_relative_modes[E_AXIS] = false; }
26
+void gcode_M82() { gcode.axis_relative_modes[E_AXIS] = false; }
27 27
 
28 28
 /**
29 29
  * M83: Set E codes relative while in Absolute Coordinates (G90) mode
30 30
  */
31
-void gcode_M83() { axis_relative_modes[E_AXIS] = true; }
31
+void gcode_M83() { gcode.axis_relative_modes[E_AXIS] = true; }

+ 7
- 4
Marlin/src/lcd/ultralcd.cpp View File

@@ -30,6 +30,9 @@
30 30
 #include "../module/temperature.h"
31 31
 #include "../module/planner.h"
32 32
 #include "../module/stepper.h"
33
+#include "../module/motion.h"
34
+#include "../gcode/gcode.h"
35
+#include "../gcode/queue.h"
33 36
 #include "../module/configuration_store.h"
34 37
 
35 38
 #include "../Marlin.h"
@@ -1734,7 +1737,7 @@ void kill_screen(const char* lcd_msg) {
1734 1737
       // Encoder knob or keypad buttons adjust the Z position
1735 1738
       //
1736 1739
       if (encoderPosition) {
1737
-        refresh_cmd_timeout();
1740
+        gcode.refresh_cmd_timeout();
1738 1741
         const float z = current_position[Z_AXIS] + float((int32_t)encoderPosition) * (MBL_Z_STEP);
1739 1742
         line_to_z(constrain(z, -(LCD_PROBE_Z_RANGE) * 0.5, (LCD_PROBE_Z_RANGE) * 0.5));
1740 1743
         lcdDrawUpdate = LCDVIEW_CALL_REDRAW_NEXT;
@@ -2280,7 +2283,7 @@ void kill_screen(const char* lcd_msg) {
2280 2283
         x_plot += step_scaler / (ENCODER_STEPS_PER_MENU_ITEM);
2281 2284
         if (abs(step_scaler) >= ENCODER_STEPS_PER_MENU_ITEM)
2282 2285
           step_scaler = 0;
2283
-        refresh_cmd_timeout();
2286
+        gcode.refresh_cmd_timeout();
2284 2287
 
2285 2288
         encoderPosition = 0;
2286 2289
         lcdDrawUpdate = LCDVIEW_REDRAW_NOW;
@@ -2317,7 +2320,7 @@ void kill_screen(const char* lcd_msg) {
2317 2320
           set_current_from_steppers_for_axis(ALL_AXES);
2318 2321
           sync_plan_position();
2319 2322
           ubl_map_move_to_xy(); // Move to new location
2320
-          refresh_cmd_timeout();
2323
+          gcode.refresh_cmd_timeout();
2321 2324
         }
2322 2325
       }
2323 2326
     }
@@ -2702,7 +2705,7 @@ void kill_screen(const char* lcd_msg) {
2702 2705
     if (lcd_clicked) { return lcd_goto_previous_menu(); }
2703 2706
     ENCODER_DIRECTION_NORMAL();
2704 2707
     if (encoderPosition) {
2705
-      refresh_cmd_timeout();
2708
+      gcode.refresh_cmd_timeout();
2706 2709
 
2707 2710
       float min = current_position[axis] - 1000,
2708 2711
             max = current_position[axis] + 1000;

+ 1
- 0
Marlin/src/libs/nozzle.cpp View File

@@ -23,6 +23,7 @@
23 23
 #include "nozzle.h"
24 24
 
25 25
 #include "../Marlin.h"
26
+#include "../module/motion.h"
26 27
 #include "point_t.h"
27 28
 
28 29
 /**

+ 574
- 0
Marlin/src/module/motion.cpp View File

@@ -0,0 +1,574 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * motion.cpp
25
+ */
26
+
27
+#include "motion.h"
28
+
29
+#include "../gcode/gcode.h"
30
+// #include "../module/planner.h"
31
+// #include "../Marlin.h"
32
+// #include "../inc/MarlinConfig.h"
33
+
34
+#include "../core/serial.h"
35
+#include "../module/stepper.h"
36
+#include "../module/temperature.h"
37
+
38
+#if IS_SCARA
39
+  #include "../libs/buzzer.h"
40
+  #include "../lcd/ultralcd.h"
41
+#endif
42
+
43
+#if ENABLED(AUTO_BED_LEVELING_UBL)
44
+  #include "../feature/ubl/ubl.h"
45
+#endif
46
+
47
+#define XYZ_CONSTS(type, array, CONFIG) const PROGMEM type array##_P[XYZ] = { X_##CONFIG, Y_##CONFIG, Z_##CONFIG }
48
+
49
+XYZ_CONSTS(float, base_min_pos,   MIN_POS);
50
+XYZ_CONSTS(float, base_max_pos,   MAX_POS);
51
+XYZ_CONSTS(float, base_home_pos,  HOME_POS);
52
+XYZ_CONSTS(float, max_length,     MAX_LENGTH);
53
+XYZ_CONSTS(float, home_bump_mm,   HOME_BUMP_MM);
54
+XYZ_CONSTS(signed char, home_dir, HOME_DIR);
55
+
56
+// Relative Mode. Enable with G91, disable with G90.
57
+bool relative_mode = false;
58
+
59
+/**
60
+ * Cartesian Current Position
61
+ *   Used to track the logical position as moves are queued.
62
+ *   Used by 'line_to_current_position' to do a move after changing it.
63
+ *   Used by 'SYNC_PLAN_POSITION_KINEMATIC' to update 'planner.position'.
64
+ */
65
+float current_position[XYZE] = { 0.0 };
66
+
67
+/**
68
+ * Cartesian Destination
69
+ *   A temporary position, usually applied to 'current_position'.
70
+ *   Set with 'get_destination_from_command' or 'set_destination_to_current'.
71
+ *   'line_to_destination' sets 'current_position' to 'destination'.
72
+ */
73
+float destination[XYZE] = { 0.0 };
74
+
75
+// The active extruder (tool). Set with T<extruder> command.
76
+uint8_t active_extruder = 0;
77
+
78
+// The feedrate for the current move, often used as the default if
79
+// no other feedrate is specified. Overridden for special moves.
80
+// Set by the last G0 through G5 command's "F" parameter.
81
+// Functions that override this for custom moves *must always* restore it!
82
+float feedrate_mm_s = MMM_TO_MMS(1500.0);
83
+
84
+/**
85
+ * sync_plan_position
86
+ *
87
+ * Set the planner/stepper positions directly from current_position with
88
+ * no kinematic translation. Used for homing axes and cartesian/core syncing.
89
+ */
90
+void sync_plan_position() {
91
+  #if ENABLED(DEBUG_LEVELING_FEATURE)
92
+    if (DEBUGGING(LEVELING)) DEBUG_POS("sync_plan_position", current_position);
93
+  #endif
94
+  planner.set_position_mm(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
95
+}
96
+
97
+void sync_plan_position_e() { planner.set_e_position_mm(current_position[E_AXIS]); }
98
+
99
+/**
100
+ * Move the planner to the current position from wherever it last moved
101
+ * (or from wherever it has been told it is located).
102
+ */
103
+void line_to_current_position() {
104
+  planner.buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], feedrate_mm_s, active_extruder);
105
+}
106
+
107
+/**
108
+ * Move the planner to the position stored in the destination array, which is
109
+ * used by G0/G1/G2/G3/G5 and many other functions to set a destination.
110
+ */
111
+void line_to_destination(const float fr_mm_s) {
112
+  planner.buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], fr_mm_s, active_extruder);
113
+}
114
+
115
+#if IS_KINEMATIC
116
+
117
+  void sync_plan_position_kinematic() {
118
+    #if ENABLED(DEBUG_LEVELING_FEATURE)
119
+      if (DEBUGGING(LEVELING)) DEBUG_POS("sync_plan_position_kinematic", current_position);
120
+    #endif
121
+    planner.set_position_mm_kinematic(current_position);
122
+  }
123
+
124
+  /**
125
+   * Calculate delta, start a line, and set current_position to destination
126
+   */
127
+  void prepare_uninterpolated_move_to_destination(const float fr_mm_s/*=0.0*/) {
128
+    #if ENABLED(DEBUG_LEVELING_FEATURE)
129
+      if (DEBUGGING(LEVELING)) DEBUG_POS("prepare_uninterpolated_move_to_destination", destination);
130
+    #endif
131
+
132
+    gcode.refresh_cmd_timeout();
133
+
134
+    #if UBL_DELTA
135
+      // ubl segmented line will do z-only moves in single segment
136
+      ubl.prepare_segmented_line_to(destination, MMS_SCALED(fr_mm_s ? fr_mm_s : feedrate_mm_s));
137
+    #else
138
+      if ( current_position[X_AXIS] == destination[X_AXIS]
139
+        && current_position[Y_AXIS] == destination[Y_AXIS]
140
+        && current_position[Z_AXIS] == destination[Z_AXIS]
141
+        && current_position[E_AXIS] == destination[E_AXIS]
142
+      ) return;
143
+
144
+      planner.buffer_line_kinematic(destination, MMS_SCALED(fr_mm_s ? fr_mm_s : feedrate_mm_s), active_extruder);
145
+    #endif
146
+
147
+    set_current_to_destination();
148
+  }
149
+
150
+#endif // IS_KINEMATIC
151
+
152
+// Software Endstops are based on the configured limits.
153
+float soft_endstop_min[XYZ] = { X_MIN_BED, Y_MIN_BED, Z_MIN_POS },
154
+      soft_endstop_max[XYZ] = { X_MAX_BED, Y_MAX_BED, Z_MAX_POS };
155
+
156
+#if HAS_SOFTWARE_ENDSTOPS
157
+
158
+  // Software Endstops are based on the configured limits.
159
+  bool soft_endstops_enabled = true;
160
+
161
+  /**
162
+   * Constrain the given coordinates to the software endstops.
163
+   */
164
+
165
+  // NOTE: This makes no sense for delta beds other than Z-axis.
166
+  //       For delta the X/Y would need to be clamped at
167
+  //       DELTA_PRINTABLE_RADIUS from center of bed, but delta
168
+  //       now enforces is_position_reachable for X/Y regardless
169
+  //       of HAS_SOFTWARE_ENDSTOPS, so that enforcement would be
170
+  //       redundant here.
171
+
172
+  void clamp_to_software_endstops(float target[XYZ]) {
173
+    if (!soft_endstops_enabled) return;
174
+    #if ENABLED(MIN_SOFTWARE_ENDSTOPS)
175
+      #if DISABLED(DELTA)
176
+        NOLESS(target[X_AXIS], soft_endstop_min[X_AXIS]);
177
+        NOLESS(target[Y_AXIS], soft_endstop_min[Y_AXIS]);
178
+      #endif
179
+      NOLESS(target[Z_AXIS], soft_endstop_min[Z_AXIS]);
180
+    #endif
181
+    #if ENABLED(MAX_SOFTWARE_ENDSTOPS)
182
+      #if DISABLED(DELTA)
183
+        NOMORE(target[X_AXIS], soft_endstop_max[X_AXIS]);
184
+        NOMORE(target[Y_AXIS], soft_endstop_max[Y_AXIS]);
185
+      #endif
186
+      NOMORE(target[Z_AXIS], soft_endstop_max[Z_AXIS]);
187
+    #endif
188
+  }
189
+
190
+#endif
191
+
192
+#if ENABLED(AUTO_BED_LEVELING_BILINEAR) && !IS_KINEMATIC
193
+
194
+  #define CELL_INDEX(A,V) ((RAW_##A##_POSITION(V) - bilinear_start[A##_AXIS]) * ABL_BG_FACTOR(A##_AXIS))
195
+
196
+  /**
197
+   * Prepare a bilinear-leveled linear move on Cartesian,
198
+   * splitting the move where it crosses grid borders.
199
+   */
200
+  void bilinear_line_to_destination(const float fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF);
201
+    int cx1 = CELL_INDEX(X, current_position[X_AXIS]),
202
+        cy1 = CELL_INDEX(Y, current_position[Y_AXIS]),
203
+        cx2 = CELL_INDEX(X, destination[X_AXIS]),
204
+        cy2 = CELL_INDEX(Y, destination[Y_AXIS]);
205
+    cx1 = constrain(cx1, 0, ABL_BG_POINTS_X - 2);
206
+    cy1 = constrain(cy1, 0, ABL_BG_POINTS_Y - 2);
207
+    cx2 = constrain(cx2, 0, ABL_BG_POINTS_X - 2);
208
+    cy2 = constrain(cy2, 0, ABL_BG_POINTS_Y - 2);
209
+
210
+    if (cx1 == cx2 && cy1 == cy2) {
211
+      // Start and end on same mesh square
212
+      line_to_destination(fr_mm_s);
213
+      set_current_to_destination();
214
+      return;
215
+    }
216
+
217
+    #define LINE_SEGMENT_END(A) (current_position[A ##_AXIS] + (destination[A ##_AXIS] - current_position[A ##_AXIS]) * normalized_dist)
218
+
219
+    float normalized_dist, end[XYZE];
220
+
221
+    // Split at the left/front border of the right/top square
222
+    const int8_t gcx = max(cx1, cx2), gcy = max(cy1, cy2);
223
+    if (cx2 != cx1 && TEST(x_splits, gcx)) {
224
+      COPY(end, destination);
225
+      destination[X_AXIS] = LOGICAL_X_POSITION(bilinear_start[X_AXIS] + ABL_BG_SPACING(X_AXIS) * gcx);
226
+      normalized_dist = (destination[X_AXIS] - current_position[X_AXIS]) / (end[X_AXIS] - current_position[X_AXIS]);
227
+      destination[Y_AXIS] = LINE_SEGMENT_END(Y);
228
+      CBI(x_splits, gcx);
229
+    }
230
+    else if (cy2 != cy1 && TEST(y_splits, gcy)) {
231
+      COPY(end, destination);
232
+      destination[Y_AXIS] = LOGICAL_Y_POSITION(bilinear_start[Y_AXIS] + ABL_BG_SPACING(Y_AXIS) * gcy);
233
+      normalized_dist = (destination[Y_AXIS] - current_position[Y_AXIS]) / (end[Y_AXIS] - current_position[Y_AXIS]);
234
+      destination[X_AXIS] = LINE_SEGMENT_END(X);
235
+      CBI(y_splits, gcy);
236
+    }
237
+    else {
238
+      // Already split on a border
239
+      line_to_destination(fr_mm_s);
240
+      set_current_to_destination();
241
+      return;
242
+    }
243
+
244
+    destination[Z_AXIS] = LINE_SEGMENT_END(Z);
245
+    destination[E_AXIS] = LINE_SEGMENT_END(E);
246
+
247
+    // Do the split and look for more borders
248
+    bilinear_line_to_destination(fr_mm_s, x_splits, y_splits);
249
+
250
+    // Restore destination from stack
251
+    COPY(destination, end);
252
+    bilinear_line_to_destination(fr_mm_s, x_splits, y_splits);
253
+  }
254
+
255
+#endif // AUTO_BED_LEVELING_BILINEAR
256
+
257
+#if IS_KINEMATIC && !UBL_DELTA
258
+
259
+  /**
260
+   * Prepare a linear move in a DELTA or SCARA setup.
261
+   *
262
+   * This calls planner.buffer_line several times, adding
263
+   * small incremental moves for DELTA or SCARA.
264
+   */
265
+  inline bool prepare_kinematic_move_to(float ltarget[XYZE]) {
266
+
267
+    // Get the top feedrate of the move in the XY plane
268
+    const float _feedrate_mm_s = MMS_SCALED(feedrate_mm_s);
269
+
270
+    // If the move is only in Z/E don't split up the move
271
+    if (ltarget[X_AXIS] == current_position[X_AXIS] && ltarget[Y_AXIS] == current_position[Y_AXIS]) {
272
+      planner.buffer_line_kinematic(ltarget, _feedrate_mm_s, active_extruder);
273
+      return false;
274
+    }
275
+
276
+    // Fail if attempting move outside printable radius
277
+    if (!position_is_reachable_xy(ltarget[X_AXIS], ltarget[Y_AXIS])) return true;
278
+
279
+    // Get the cartesian distances moved in XYZE
280
+    const float difference[XYZE] = {
281
+      ltarget[X_AXIS] - current_position[X_AXIS],
282
+      ltarget[Y_AXIS] - current_position[Y_AXIS],
283
+      ltarget[Z_AXIS] - current_position[Z_AXIS],
284
+      ltarget[E_AXIS] - current_position[E_AXIS]
285
+    };
286
+
287
+    // Get the linear distance in XYZ
288
+    float cartesian_mm = SQRT(sq(difference[X_AXIS]) + sq(difference[Y_AXIS]) + sq(difference[Z_AXIS]));
289
+
290
+    // If the move is very short, check the E move distance
291
+    if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = FABS(difference[E_AXIS]);
292
+
293
+    // No E move either? Game over.
294
+    if (UNEAR_ZERO(cartesian_mm)) return true;
295
+
296
+    // Minimum number of seconds to move the given distance
297
+    const float seconds = cartesian_mm / _feedrate_mm_s;
298
+
299
+    // The number of segments-per-second times the duration
300
+    // gives the number of segments
301
+    uint16_t segments = delta_segments_per_second * seconds;
302
+
303
+    // For SCARA minimum segment size is 0.25mm
304
+    #if IS_SCARA
305
+      NOMORE(segments, cartesian_mm * 4);
306
+    #endif
307
+
308
+    // At least one segment is required
309
+    NOLESS(segments, 1);
310
+
311
+    // The approximate length of each segment
312
+    const float inv_segments = 1.0 / float(segments),
313
+                segment_distance[XYZE] = {
314
+                  difference[X_AXIS] * inv_segments,
315
+                  difference[Y_AXIS] * inv_segments,
316
+                  difference[Z_AXIS] * inv_segments,
317
+                  difference[E_AXIS] * inv_segments
318
+                };
319
+
320
+    // SERIAL_ECHOPAIR("mm=", cartesian_mm);
321
+    // SERIAL_ECHOPAIR(" seconds=", seconds);
322
+    // SERIAL_ECHOLNPAIR(" segments=", segments);
323
+
324
+    #if IS_SCARA && ENABLED(SCARA_FEEDRATE_SCALING)
325
+      // SCARA needs to scale the feed rate from mm/s to degrees/s
326
+      const float inv_segment_length = min(10.0, float(segments) / cartesian_mm), // 1/mm/segs
327
+                  feed_factor = inv_segment_length * _feedrate_mm_s;
328
+      float oldA = stepper.get_axis_position_degrees(A_AXIS),
329
+            oldB = stepper.get_axis_position_degrees(B_AXIS);
330
+    #endif
331
+
332
+    // Get the logical current position as starting point
333
+    float logical[XYZE];
334
+    COPY(logical, current_position);
335
+
336
+    // Drop one segment so the last move is to the exact target.
337
+    // If there's only 1 segment, loops will be skipped entirely.
338
+    --segments;
339
+
340
+    // Calculate and execute the segments
341
+    for (uint16_t s = segments + 1; --s;) {
342
+      LOOP_XYZE(i) logical[i] += segment_distance[i];
343
+      #if ENABLED(DELTA)
344
+        DELTA_LOGICAL_IK(); // Delta can inline its kinematics
345
+      #else
346
+        inverse_kinematics(logical);
347
+      #endif
348
+
349
+      ADJUST_DELTA(logical); // Adjust Z if bed leveling is enabled
350
+
351
+      #if IS_SCARA && ENABLED(SCARA_FEEDRATE_SCALING)
352
+        // For SCARA scale the feed rate from mm/s to degrees/s
353
+        // Use ratio between the length of the move and the larger angle change
354
+        const float adiff = abs(delta[A_AXIS] - oldA),
355
+                    bdiff = abs(delta[B_AXIS] - oldB);
356
+        planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], logical[E_AXIS], max(adiff, bdiff) * feed_factor, active_extruder);
357
+        oldA = delta[A_AXIS];
358
+        oldB = delta[B_AXIS];
359
+      #else
360
+        planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], logical[E_AXIS], _feedrate_mm_s, active_extruder);
361
+      #endif
362
+    }
363
+
364
+    // Since segment_distance is only approximate,
365
+    // the final move must be to the exact destination.
366
+
367
+    #if IS_SCARA && ENABLED(SCARA_FEEDRATE_SCALING)
368
+      // For SCARA scale the feed rate from mm/s to degrees/s
369
+      // With segments > 1 length is 1 segment, otherwise total length
370
+      inverse_kinematics(ltarget);
371
+      ADJUST_DELTA(ltarget);
372
+      const float adiff = abs(delta[A_AXIS] - oldA),
373
+                  bdiff = abs(delta[B_AXIS] - oldB);
374
+      planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], logical[E_AXIS], max(adiff, bdiff) * feed_factor, active_extruder);
375
+    #else
376
+      planner.buffer_line_kinematic(ltarget, _feedrate_mm_s, active_extruder);
377
+    #endif
378
+
379
+    return false;
380
+  }
381
+
382
+#else // !IS_KINEMATIC || UBL_DELTA
383
+
384
+  /**
385
+   * Prepare a linear move in a Cartesian setup.
386
+   * If Mesh Bed Leveling is enabled, perform a mesh move.
387
+   *
388
+   * Returns true if the caller didn't update current_position.
389
+   */
390
+  inline bool prepare_move_to_destination_cartesian() {
391
+    #if ENABLED(AUTO_BED_LEVELING_UBL)
392
+      const float fr_scaled = MMS_SCALED(feedrate_mm_s);
393
+      if (ubl.state.active) { // direct use of ubl.state.active for speed
394
+        ubl.line_to_destination_cartesian(fr_scaled, active_extruder);
395
+        return true;
396
+      }
397
+      else
398
+        line_to_destination(fr_scaled);
399
+    #else
400
+      // Do not use feedrate_percentage for E or Z only moves
401
+      if (current_position[X_AXIS] == destination[X_AXIS] && current_position[Y_AXIS] == destination[Y_AXIS])
402
+        line_to_destination();
403
+      else {
404
+        const float fr_scaled = MMS_SCALED(feedrate_mm_s);
405
+        #if ENABLED(MESH_BED_LEVELING)
406
+          if (mbl.active()) { // direct used of mbl.active() for speed
407
+            mesh_line_to_destination(fr_scaled);
408
+            return true;
409
+          }
410
+          else
411
+        #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
412
+          if (planner.abl_enabled) { // direct use of abl_enabled for speed
413
+            bilinear_line_to_destination(fr_scaled);
414
+            return true;
415
+          }
416
+          else
417
+        #endif
418
+            line_to_destination(fr_scaled);
419
+      }
420
+    #endif
421
+    return false;
422
+  }
423
+
424
+#endif // !IS_KINEMATIC || UBL_DELTA
425
+
426
+#if ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE)
427
+  bool extruder_duplication_enabled = false;                              // Used in Dual X mode 2
428
+#endif
429
+
430
+#if ENABLED(DUAL_X_CARRIAGE)
431
+
432
+  DualXMode dual_x_carriage_mode         = DEFAULT_DUAL_X_CARRIAGE_MODE;
433
+  float inactive_extruder_x_pos          = X2_MAX_POS,                    // used in mode 0 & 1
434
+        raised_parked_position[XYZE],                                     // used in mode 1
435
+        duplicate_extruder_x_offset      = DEFAULT_DUPLICATION_X_OFFSET;  // used in mode 2
436
+  bool active_extruder_parked            = false;                         // used in mode 1 & 2
437
+  millis_t delayed_move_time             = 0;                             // used in mode 1
438
+  int16_t duplicate_extruder_temp_offset = 0;                             // used in mode 2
439
+
440
+  float x_home_pos(const int extruder) {
441
+    if (extruder == 0)
442
+      return LOGICAL_X_POSITION(base_home_pos(X_AXIS));
443
+    else
444
+      /**
445
+       * In dual carriage mode the extruder offset provides an override of the
446
+       * second X-carriage position when homed - otherwise X2_HOME_POS is used.
447
+       * This allows soft recalibration of the second extruder home position
448
+       * without firmware reflash (through the M218 command).
449
+       */
450
+      return LOGICAL_X_POSITION(hotend_offset[X_AXIS][1] > 0 ? hotend_offset[X_AXIS][1] : X2_HOME_POS);
451
+  }
452
+
453
+  /**
454
+   * Prepare a linear move in a dual X axis setup
455
+   */
456
+  inline bool prepare_move_to_destination_dualx() {
457
+    if (active_extruder_parked) {
458
+      switch (dual_x_carriage_mode) {
459
+        case DXC_FULL_CONTROL_MODE:
460
+          break;
461
+        case DXC_AUTO_PARK_MODE:
462
+          if (current_position[E_AXIS] == destination[E_AXIS]) {
463
+            // This is a travel move (with no extrusion)
464
+            // Skip it, but keep track of the current position
465
+            // (so it can be used as the start of the next non-travel move)
466
+            if (delayed_move_time != 0xFFFFFFFFUL) {
467
+              set_current_to_destination();
468
+              NOLESS(raised_parked_position[Z_AXIS], destination[Z_AXIS]);
469
+              delayed_move_time = millis();
470
+              return true;
471
+            }
472
+          }
473
+          // unpark extruder: 1) raise, 2) move into starting XY position, 3) lower
474
+          for (uint8_t i = 0; i < 3; i++)
475
+            planner.buffer_line(
476
+              i == 0 ? raised_parked_position[X_AXIS] : current_position[X_AXIS],
477
+              i == 0 ? raised_parked_position[Y_AXIS] : current_position[Y_AXIS],
478
+              i == 2 ? current_position[Z_AXIS] : raised_parked_position[Z_AXIS],
479
+              current_position[E_AXIS],
480
+              i == 1 ? PLANNER_XY_FEEDRATE() : planner.max_feedrate_mm_s[Z_AXIS],
481
+              active_extruder
482
+            );
483
+          delayed_move_time = 0;
484
+          active_extruder_parked = false;
485
+          #if ENABLED(DEBUG_LEVELING_FEATURE)
486
+            if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("Clear active_extruder_parked");
487
+          #endif
488
+          break;
489
+        case DXC_DUPLICATION_MODE:
490
+          if (active_extruder == 0) {
491
+            #if ENABLED(DEBUG_LEVELING_FEATURE)
492
+              if (DEBUGGING(LEVELING)) {
493
+                SERIAL_ECHOPAIR("Set planner X", LOGICAL_X_POSITION(inactive_extruder_x_pos));
494
+                SERIAL_ECHOLNPAIR(" ... Line to X", current_position[X_AXIS] + duplicate_extruder_x_offset);
495
+              }
496
+            #endif
497
+            // move duplicate extruder into correct duplication position.
498
+            planner.set_position_mm(
499
+              LOGICAL_X_POSITION(inactive_extruder_x_pos),
500
+              current_position[Y_AXIS],
501
+              current_position[Z_AXIS],
502
+              current_position[E_AXIS]
503
+            );
504
+            planner.buffer_line(
505
+              current_position[X_AXIS] + duplicate_extruder_x_offset,
506
+              current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS],
507
+              planner.max_feedrate_mm_s[X_AXIS], 1
508
+            );
509
+            SYNC_PLAN_POSITION_KINEMATIC();
510
+            stepper.synchronize();
511
+            extruder_duplication_enabled = true;
512
+            active_extruder_parked = false;
513
+            #if ENABLED(DEBUG_LEVELING_FEATURE)
514
+              if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("Set extruder_duplication_enabled\nClear active_extruder_parked");
515
+            #endif
516
+          }
517
+          else {
518
+            #if ENABLED(DEBUG_LEVELING_FEATURE)
519
+              if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("Active extruder not 0");
520
+            #endif
521
+          }
522
+          break;
523
+      }
524
+    }
525
+    return false;
526
+  }
527
+
528
+#endif // DUAL_X_CARRIAGE
529
+
530
+/**
531
+ * Prepare a single move and get ready for the next one
532
+ *
533
+ * This may result in several calls to planner.buffer_line to
534
+ * do smaller moves for DELTA, SCARA, mesh moves, etc.
535
+ */
536
+void prepare_move_to_destination() {
537
+  clamp_to_software_endstops(destination);
538
+  gcode.refresh_cmd_timeout();
539
+
540
+  #if ENABLED(PREVENT_COLD_EXTRUSION)
541
+
542
+    if (!DEBUGGING(DRYRUN)) {
543
+      if (destination[E_AXIS] != current_position[E_AXIS]) {
544
+        if (thermalManager.tooColdToExtrude(active_extruder)) {
545
+          current_position[E_AXIS] = destination[E_AXIS]; // Behave as if the move really took place, but ignore E part
546
+          SERIAL_ECHO_START();
547
+          SERIAL_ECHOLNPGM(MSG_ERR_COLD_EXTRUDE_STOP);
548
+        }
549
+        #if ENABLED(PREVENT_LENGTHY_EXTRUDE)
550
+          if (destination[E_AXIS] - current_position[E_AXIS] > EXTRUDE_MAXLENGTH) {
551
+            current_position[E_AXIS] = destination[E_AXIS]; // Behave as if the move really took place, but ignore E part
552
+            SERIAL_ECHO_START();
553
+            SERIAL_ECHOLNPGM(MSG_ERR_LONG_EXTRUDE_STOP);
554
+          }
555
+        #endif
556
+      }
557
+    }
558
+
559
+  #endif
560
+
561
+  if (
562
+    #if UBL_DELTA // Also works for CARTESIAN (smaller segments follow mesh more closely)
563
+      ubl.prepare_segmented_line_to(destination, feedrate_mm_s)
564
+    #elif IS_KINEMATIC
565
+      prepare_kinematic_move_to(destination)
566
+    #elif ENABLED(DUAL_X_CARRIAGE)
567
+      prepare_move_to_destination_dualx() || prepare_move_to_destination_cartesian()
568
+    #else
569
+      prepare_move_to_destination_cartesian()
570
+    #endif
571
+  ) return;
572
+
573
+  set_current_to_destination();
574
+}

+ 237
- 0
Marlin/src/module/motion.h View File

@@ -0,0 +1,237 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * motion.h
25
+ *
26
+ * High-level motion commands to feed the planner
27
+ * Some of these methods may migrate to the planner class.
28
+ */
29
+
30
+#ifndef MOTION_H
31
+#define MOTION_H
32
+
33
+#include "../inc/MarlinConfig.h"
34
+
35
+//#include "../HAL/HAL.h"
36
+
37
+// #if ENABLED(DELTA)
38
+//   #include "../module/delta.h"
39
+// #endif
40
+
41
+extern bool relative_mode;
42
+
43
+extern float current_position[XYZE], destination[XYZE];
44
+
45
+extern float feedrate_mm_s;
46
+
47
+extern uint8_t active_extruder;
48
+
49
+extern float soft_endstop_min[XYZ], soft_endstop_max[XYZ];
50
+
51
+FORCE_INLINE float pgm_read_any(const float *p) { return pgm_read_float_near(p); }
52
+FORCE_INLINE signed char pgm_read_any(const signed char *p) { return pgm_read_byte_near(p); }
53
+
54
+#define XYZ_DEFS(type, array, CONFIG) \
55
+  extern const type array##_P[XYZ]; \
56
+  FORCE_INLINE type array(AxisEnum axis) { return pgm_read_any(&array##_P[axis]); } \
57
+  typedef void __void_##CONFIG##__
58
+
59
+XYZ_DEFS(float, base_min_pos,   MIN_POS);
60
+XYZ_DEFS(float, base_max_pos,   MAX_POS);
61
+XYZ_DEFS(float, base_home_pos,  HOME_POS);
62
+XYZ_DEFS(float, max_length,     MAX_LENGTH);
63
+XYZ_DEFS(float, home_bump_mm,   HOME_BUMP_MM);
64
+XYZ_DEFS(signed char, home_dir, HOME_DIR);
65
+
66
+#if HAS_SOFTWARE_ENDSTOPS
67
+  extern bool soft_endstops_enabled;
68
+  void clamp_to_software_endstops(float target[XYZ]);
69
+#else
70
+  #define soft_endstops_enabled false
71
+  #define clamp_to_software_endstops(x) NOOP
72
+#endif
73
+
74
+inline void set_current_to_destination() { COPY(current_position, destination); }
75
+inline void set_destination_to_current() { COPY(destination, current_position); }
76
+
77
+/**
78
+ * sync_plan_position
79
+ *
80
+ * Set the planner/stepper positions directly from current_position with
81
+ * no kinematic translation. Used for homing axes and cartesian/core syncing.
82
+ */
83
+void sync_plan_position();
84
+void sync_plan_position_e();
85
+
86
+#if IS_KINEMATIC
87
+  void sync_plan_position_kinematic();
88
+  #define SYNC_PLAN_POSITION_KINEMATIC() sync_plan_position_kinematic()
89
+#else
90
+  #define SYNC_PLAN_POSITION_KINEMATIC() sync_plan_position()
91
+#endif
92
+
93
+/**
94
+ * Move the planner to the current position from wherever it last moved
95
+ * (or from wherever it has been told it is located).
96
+ */
97
+void line_to_current_position();
98
+
99
+/**
100
+ * Move the planner to the position stored in the destination array, which is
101
+ * used by G0/G1/G2/G3/G5 and many other functions to set a destination.
102
+ */
103
+void line_to_destination(const float fr_mm_s);
104
+
105
+inline void line_to_destination() { line_to_destination(feedrate_mm_s); }
106
+
107
+#if IS_KINEMATIC
108
+  void prepare_uninterpolated_move_to_destination(const float fr_mm_s=0.0);
109
+#endif
110
+
111
+void prepare_move_to_destination();
112
+
113
+void clamp_to_software_endstops(float target[XYZ]);
114
+
115
+//
116
+// Macros
117
+//
118
+
119
+// Workspace offsets
120
+#if HAS_WORKSPACE_OFFSET
121
+  #if HAS_HOME_OFFSET
122
+    extern float home_offset[XYZ];
123
+  #endif
124
+  #if HAS_POSITION_SHIFT
125
+    extern float position_shift[XYZ];
126
+  #endif
127
+#endif
128
+
129
+#if HAS_HOME_OFFSET && HAS_POSITION_SHIFT
130
+  extern float workspace_offset[XYZ];
131
+  #define WORKSPACE_OFFSET(AXIS) workspace_offset[AXIS]
132
+#elif HAS_HOME_OFFSET
133
+  #define WORKSPACE_OFFSET(AXIS) home_offset[AXIS]
134
+#elif HAS_POSITION_SHIFT
135
+  #define WORKSPACE_OFFSET(AXIS) position_shift[AXIS]
136
+#else
137
+  #define WORKSPACE_OFFSET(AXIS) 0
138
+#endif
139
+
140
+#define LOGICAL_POSITION(POS, AXIS) ((POS) + WORKSPACE_OFFSET(AXIS))
141
+#define RAW_POSITION(POS, AXIS)     ((POS) - WORKSPACE_OFFSET(AXIS))
142
+
143
+#if HAS_POSITION_SHIFT || DISABLED(DELTA)
144
+  #define LOGICAL_X_POSITION(POS)   LOGICAL_POSITION(POS, X_AXIS)
145
+  #define LOGICAL_Y_POSITION(POS)   LOGICAL_POSITION(POS, Y_AXIS)
146
+  #define RAW_X_POSITION(POS)       RAW_POSITION(POS, X_AXIS)
147
+  #define RAW_Y_POSITION(POS)       RAW_POSITION(POS, Y_AXIS)
148
+#else
149
+  #define LOGICAL_X_POSITION(POS)   (POS)
150
+  #define LOGICAL_Y_POSITION(POS)   (POS)
151
+  #define RAW_X_POSITION(POS)       (POS)
152
+  #define RAW_Y_POSITION(POS)       (POS)
153
+#endif
154
+
155
+#define LOGICAL_Z_POSITION(POS)     LOGICAL_POSITION(POS, Z_AXIS)
156
+#define RAW_Z_POSITION(POS)         RAW_POSITION(POS, Z_AXIS)
157
+#define RAW_CURRENT_POSITION(A)     RAW_##A##_POSITION(current_position[A##_AXIS])
158
+
159
+/**
160
+ * position_is_reachable family of functions
161
+ */
162
+
163
+#if IS_KINEMATIC // (DELTA or SCARA)
164
+
165
+  #if IS_SCARA
166
+    extern const float L1, L2;
167
+  #endif
168
+
169
+  inline bool position_is_reachable_raw_xy(const float &rx, const float &ry) {
170
+    #if ENABLED(DELTA)
171
+      return HYPOT2(rx, ry) <= sq(DELTA_PRINTABLE_RADIUS);
172
+    #elif IS_SCARA
173
+      #if MIDDLE_DEAD_ZONE_R > 0
174
+        const float R2 = HYPOT2(rx - SCARA_OFFSET_X, ry - SCARA_OFFSET_Y);
175
+        return R2 >= sq(float(MIDDLE_DEAD_ZONE_R)) && R2 <= sq(L1 + L2);
176
+      #else
177
+        return HYPOT2(rx - SCARA_OFFSET_X, ry - SCARA_OFFSET_Y) <= sq(L1 + L2);
178
+      #endif
179
+    #else // CARTESIAN
180
+      // To be migrated from MakerArm branch in future
181
+    #endif
182
+  }
183
+
184
+  inline bool position_is_reachable_by_probe_raw_xy(const float &rx, const float &ry) {
185
+
186
+    // Both the nozzle and the probe must be able to reach the point.
187
+    // This won't work on SCARA since the probe offset rotates with the arm.
188
+
189
+    return position_is_reachable_raw_xy(rx, ry)
190
+        && position_is_reachable_raw_xy(rx - X_PROBE_OFFSET_FROM_EXTRUDER, ry - Y_PROBE_OFFSET_FROM_EXTRUDER);
191
+  }
192
+
193
+#else // CARTESIAN
194
+
195
+  inline bool position_is_reachable_raw_xy(const float &rx, const float &ry) {
196
+      // Add 0.001 margin to deal with float imprecision
197
+      return WITHIN(rx, X_MIN_POS - 0.001, X_MAX_POS + 0.001)
198
+          && WITHIN(ry, Y_MIN_POS - 0.001, Y_MAX_POS + 0.001);
199
+  }
200
+
201
+  inline bool position_is_reachable_by_probe_raw_xy(const float &rx, const float &ry) {
202
+      // Add 0.001 margin to deal with float imprecision
203
+      return WITHIN(rx, MIN_PROBE_X - 0.001, MAX_PROBE_X + 0.001)
204
+          && WITHIN(ry, MIN_PROBE_Y - 0.001, MAX_PROBE_Y + 0.001);
205
+  }
206
+
207
+#endif // CARTESIAN
208
+
209
+FORCE_INLINE bool position_is_reachable_by_probe_xy(const float &lx, const float &ly) {
210
+  return position_is_reachable_by_probe_raw_xy(RAW_X_POSITION(lx), RAW_Y_POSITION(ly));
211
+}
212
+
213
+FORCE_INLINE bool position_is_reachable_xy(const float &lx, const float &ly) {
214
+  return position_is_reachable_raw_xy(RAW_X_POSITION(lx), RAW_Y_POSITION(ly));
215
+}
216
+
217
+#if ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE)
218
+  extern bool extruder_duplication_enabled;       // Used in Dual X mode 2
219
+#endif
220
+
221
+#if ENABLED(DUAL_X_CARRIAGE)
222
+
223
+  extern DualXMode dual_x_carriage_mode;
224
+  extern float inactive_extruder_x_pos,           // used in mode 0 & 1
225
+               raised_parked_position[XYZE],      // used in mode 1
226
+               duplicate_extruder_x_offset;       // used in mode 2
227
+  extern bool active_extruder_parked;             // used in mode 1 & 2
228
+  extern millis_t delayed_move_time;              // used in mode 1
229
+  extern int16_t duplicate_extruder_temp_offset;  // used in mode 2
230
+
231
+  float x_home_pos(const int extruder);
232
+
233
+  FORCE_INLINE int x_home_dir(const uint8_t extruder) { return extruder ? X2_HOME_DIR : X_HOME_DIR; }
234
+
235
+#endif // DUAL_X_CARRIAGE
236
+
237
+#endif // MOTION_H

+ 1
- 0
Marlin/src/module/planner.cpp View File

@@ -60,6 +60,7 @@
60 60
 
61 61
 #include "planner.h"
62 62
 #include "stepper.h"
63
+#include "motion.h"
63 64
 #include "../module/temperature.h"
64 65
 #include "../lcd/ultralcd.h"
65 66
 #include "../core/language.h"

+ 5
- 2
Marlin/src/module/planner_bezier.cpp View File

@@ -31,10 +31,13 @@
31 31
 
32 32
 #if ENABLED(BEZIER_CURVE_SUPPORT)
33 33
 
34
+#include "planner.h"
35
+#include "motion.h"
36
+#include "temperature.h"
37
+
34 38
 #include "../Marlin.h"
35
-#include "../module/planner.h"
36 39
 #include "../core/language.h"
37
-#include "../module/temperature.h"
40
+#include "../gcode/queue.h"
38 41
 
39 42
 // See the meaning in the documentation of cubic_b_spline().
40 43
 #define MIN_STEP 0.002

+ 1
- 0
Marlin/src/module/stepper.cpp View File

@@ -57,6 +57,7 @@
57 57
 #include "../module/temperature.h"
58 58
 #include "../lcd/ultralcd.h"
59 59
 #include "../core/language.h"
60
+#include "../gcode/queue.h"
60 61
 #include "../sd/cardreader.h"
61 62
 
62 63
 #if MB(ALLIGATOR)

+ 1
- 0
Marlin/src/sd/cardreader.cpp View File

@@ -31,6 +31,7 @@
31 31
 #include "../module/stepper.h"
32 32
 #include "../module/printcounter.h"
33 33
 #include "../core/language.h"
34
+#include "../gcode/queue.h"
34 35
 
35 36
 #include <ctype.h>
36 37
 

Loading…
Cancel
Save