Browse Source

♻️ Refactor Linear / Logical / Distinct Axes (#21953)

* More patches supporting EXTRUDERS 0
* Extend types in prep for more axes
Scott Lahteine 3 years ago
parent
commit
dd4990252e
No account linked to committer's email address
43 changed files with 1141 additions and 787 deletions
  1. 5
    2
      Marlin/src/core/serial.cpp
  2. 5
    2
      Marlin/src/core/serial.h
  3. 248
    192
      Marlin/src/core/types.h
  4. 8
    0
      Marlin/src/core/utility.h
  5. 17
    26
      Marlin/src/feature/encoder_i2c.cpp
  6. 25
    9
      Marlin/src/feature/tmc_util.cpp
  7. 10
    4
      Marlin/src/feature/tmc_util.h
  8. 19
    8
      Marlin/src/gcode/calibrate/G28.cpp
  9. 8
    4
      Marlin/src/gcode/calibrate/G425.cpp
  10. 6
    4
      Marlin/src/gcode/calibrate/M425.cpp
  11. 13
    14
      Marlin/src/gcode/config/M200-M205.cpp
  12. 24
    19
      Marlin/src/gcode/config/M92.cpp
  13. 14
    10
      Marlin/src/gcode/control/M17_M18_M84.cpp
  14. 7
    5
      Marlin/src/gcode/feature/pause/G61.cpp
  15. 13
    5
      Marlin/src/gcode/feature/trinamic/M122.cpp
  16. 14
    12
      Marlin/src/gcode/gcode.cpp
  17. 23
    14
      Marlin/src/gcode/gcode.h
  18. 26
    11
      Marlin/src/gcode/geometry/G92.cpp
  19. 1
    1
      Marlin/src/gcode/host/M114.cpp
  20. 6
    4
      Marlin/src/gcode/motion/G0_G1.cpp
  21. 22
    9
      Marlin/src/gcode/motion/G2_G3.cpp
  22. 1
    1
      Marlin/src/gcode/motion/M290.cpp
  23. 2
    1
      Marlin/src/gcode/parser.cpp
  24. 1
    1
      Marlin/src/gcode/parser.h
  25. 56
    38
      Marlin/src/inc/Conditionals_LCD.h
  26. 20
    0
      Marlin/src/inc/Conditionals_adv.h
  27. 128
    121
      Marlin/src/inc/Conditionals_post.h
  28. 16
    11
      Marlin/src/inc/SanityCheck.h
  29. 4
    2
      Marlin/src/lcd/dogm/status_screen_DOGM.cpp
  30. 4
    4
      Marlin/src/lcd/extui/dgus/mks/DGUSDisplayDef.cpp
  31. 7
    3
      Marlin/src/lcd/marlinui.cpp
  32. 18
    22
      Marlin/src/lcd/menu/menu_advanced.cpp
  33. 9
    2
      Marlin/src/module/endstops.cpp
  34. 68
    34
      Marlin/src/module/motion.cpp
  35. 10
    4
      Marlin/src/module/motion.h
  36. 153
    113
      Marlin/src/module/planner.cpp
  37. 33
    17
      Marlin/src/module/planner.h
  38. 26
    20
      Marlin/src/module/settings.cpp
  39. 34
    22
      Marlin/src/module/stepper.cpp
  40. 12
    4
      Marlin/src/module/stepper.h
  41. 15
    8
      Marlin/src/module/stepper/trinamic.cpp
  42. 8
    4
      buildroot/tests/mega2560
  43. 2
    0
      buildroot/tests/rambo

+ 5
- 2
Marlin/src/core/serial.cpp View File

@@ -101,8 +101,11 @@ void print_bin(uint16_t val) {
101 101
   }
102 102
 }
103 103
 
104
-void print_pos(const_float_t x, const_float_t y, const_float_t z, PGM_P const prefix/*=nullptr*/, PGM_P const suffix/*=nullptr*/) {
104
+void print_pos(
105
+  LINEAR_AXIS_LIST(const_float_t x, const_float_t y, const_float_t z)
106
+  , PGM_P const prefix/*=nullptr*/, PGM_P const suffix/*=nullptr*/
107
+) {
105 108
   if (prefix) serialprintPGM(prefix);
106
-  SERIAL_ECHOPAIR_P(SP_X_STR, x, SP_Y_STR, y, SP_Z_STR, z);
109
+  SERIAL_ECHOPAIR_P(LIST_N(DOUBLE(LINEAR_AXES), SP_X_STR, x, SP_Y_STR, y, SP_Z_STR, z));
107 110
   if (suffix) serialprintPGM(suffix); else SERIAL_EOL();
108 111
 }

+ 5
- 2
Marlin/src/core/serial.h View File

@@ -310,10 +310,13 @@ void serialprint_truefalse(const bool tf);
310 310
 void serial_spaces(uint8_t count);
311 311
 
312 312
 void print_bin(const uint16_t val);
313
-void print_pos(const_float_t x, const_float_t y, const_float_t z, PGM_P const prefix=nullptr, PGM_P const suffix=nullptr);
313
+void print_pos(
314
+  LINEAR_AXIS_LIST(const_float_t x, const_float_t y, const_float_t z),
315
+  PGM_P const prefix=nullptr, PGM_P const suffix=nullptr
316
+);
314 317
 
315 318
 inline void print_pos(const xyz_pos_t &xyz, PGM_P const prefix=nullptr, PGM_P const suffix=nullptr) {
316
-  print_pos(xyz.x, xyz.y, xyz.z, prefix, suffix);
319
+  print_pos(LINEAR_AXIS_LIST(xyz.x, xyz.y, xyz.z), prefix, suffix);
317 320
 }
318 321
 
319 322
 #define SERIAL_POS(SUFFIX,VAR) do { print_pos(VAR, PSTR("  " STRINGIFY(VAR) "="), PSTR(" : " SUFFIX "\n")); }while(0)

+ 248
- 192
Marlin/src/core/types.h View File

@@ -39,6 +39,26 @@ struct IF { typedef R type; };
39 39
 template <class L, class R>
40 40
 struct IF<true, L, R> { typedef L type; };
41 41
 
42
+#define LINEAR_AXIS_GANG(V...) GANG_N(LINEAR_AXES, V)
43
+#define LINEAR_AXIS_CODE(V...) CODE_N(LINEAR_AXES, V)
44
+#define LINEAR_AXIS_LIST(V...) LIST_N(LINEAR_AXES, V)
45
+#define LINEAR_AXIS_ARRAY(V...) { LINEAR_AXIS_LIST(V) }
46
+
47
+#define LOGICAL_AXIS_GANG(E,V...) LINEAR_AXIS_GANG(V) GANG_ITEM_E(E)
48
+#define LOGICAL_AXIS_CODE(E,V...) LINEAR_AXIS_CODE(V) CODE_ITEM_E(E)
49
+#define LOGICAL_AXIS_LIST(E,V...) LINEAR_AXIS_LIST(V) LIST_ITEM_E(E)
50
+#define LOGICAL_AXIS_ARRAY(E,V...) { LOGICAL_AXIS_LIST(E,V) }
51
+
52
+#if HAS_EXTRUDERS
53
+  #define LIST_ITEM_E(N) , N
54
+  #define CODE_ITEM_E(N) ; N
55
+  #define GANG_ITEM_E(N) N
56
+#else
57
+  #define LIST_ITEM_E(N)
58
+  #define CODE_ITEM_E(N)
59
+  #define GANG_ITEM_E(N)
60
+#endif
61
+
42 62
 //
43 63
 // Enumerated axis indices
44 64
 //
@@ -47,16 +67,43 @@ struct IF<true, L, R> { typedef L type; };
47 67
 //  - X_HEAD, Y_HEAD, and Z_HEAD should be used for Steppers on Core kinematics
48 68
 //
49 69
 enum AxisEnum : uint8_t {
50
-  X_AXIS = 0, A_AXIS = X_AXIS,
51
-  Y_AXIS = 1, B_AXIS = Y_AXIS,
52
-  Z_AXIS = 2, C_AXIS = Z_AXIS,
53
-  E_AXIS,
54
-  X_HEAD, Y_HEAD, Z_HEAD,
55
-  E0_AXIS = E_AXIS,
56
-  E1_AXIS, E2_AXIS, E3_AXIS, E4_AXIS, E5_AXIS, E6_AXIS, E7_AXIS,
70
+
71
+  // Linear axes may be controlled directly or indirectly
72
+  LINEAR_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS),
73
+
74
+  // Extruder axes may be considered distinctly
75
+  #define _EN_ITEM(N) E##N##_AXIS,
76
+  REPEAT(EXTRUDERS, _EN_ITEM)
77
+  #undef _EN_ITEM
78
+
79
+  // Core also keeps toolhead directions
80
+  #if IS_CORE
81
+    X_HEAD, Y_HEAD, Z_HEAD,
82
+  #endif
83
+
84
+  // Distinct axes, including all E and Core
85
+  NUM_AXIS_ENUMS,
86
+
87
+  // Most of the time we refer only to the single E_AXIS
88
+  #if HAS_EXTRUDERS
89
+    E_AXIS = E0_AXIS,
90
+  #endif
91
+
92
+  // A, B, and C are for DELTA, SCARA, etc.
93
+  A_AXIS = X_AXIS,
94
+  #if LINEAR_AXES >= 2
95
+    B_AXIS = Y_AXIS,
96
+  #endif
97
+  #if LINEAR_AXES >= 3
98
+    C_AXIS = Z_AXIS,
99
+  #endif
100
+
101
+  // To refer to all or none
57 102
   ALL_AXES_ENUM = 0xFE, NO_AXIS_ENUM = 0xFF
58 103
 };
59 104
 
105
+typedef IF<(NUM_AXIS_ENUMS > 8), uint16_t, uint8_t>::type axis_bits_t;
106
+
60 107
 //
61 108
 // Loop over axes
62 109
 //
@@ -185,7 +232,7 @@ void toNative(xyz_pos_t &raw);
185 232
 void toNative(xyze_pos_t &raw);
186 233
 
187 234
 //
188
-// XY coordinates, counters, etc.
235
+// Paired XY coordinates, counters, flags, etc.
189 236
 //
190 237
 template<typename T>
191 238
 struct XYval {
@@ -197,10 +244,14 @@ struct XYval {
197 244
   FI void set(const T px)                               { x = px; }
198 245
   FI void set(const T px, const T py)                   { x = px; y = py; }
199 246
   FI void set(const T (&arr)[XY])                       { x = arr[0]; y = arr[1]; }
200
-  FI void set(const T (&arr)[XYZ])                      { x = arr[0]; y = arr[1]; }
201
-  FI void set(const T (&arr)[XYZE])                     { x = arr[0]; y = arr[1]; }
202
-  #if DISTINCT_AXES > LOGICAL_AXES
203
-    FI void set(const T (&arr)[DISTINCT_AXES])          { x = arr[0]; y = arr[1]; }
247
+  #if LINEAR_AXES > XY
248
+    FI void set(const T (&arr)[LINEAR_AXES])            { x = arr[0]; y = arr[1]; }
249
+  #endif
250
+  #if LOGICAL_AXES > LINEAR_AXES
251
+    FI void set(const T (&arr)[LOGICAL_AXES])           { x = arr[0]; y = arr[1]; }
252
+    #if DISTINCT_AXES > LOGICAL_AXES
253
+      FI void set(const T (&arr)[DISTINCT_AXES])        { x = arr[0]; y = arr[1]; }
254
+    #endif
204 255
   #endif
205 256
   FI void reset()                                       { x = y = 0; }
206 257
   FI T magnitude()                                const { return (T)sqrtf(x*x + y*y); }
@@ -223,8 +274,8 @@ struct XYval {
223 274
   FI operator XYZval<T>()                         const { return { x, y }; }
224 275
   FI operator XYZEval<T>()                              { return { x, y }; }
225 276
   FI operator XYZEval<T>()                        const { return { x, y }; }
226
-  FI       T&  operator[](const int i)                  { return pos[i]; }
227
-  FI const T&  operator[](const int i)            const { return pos[i]; }
277
+  FI       T&  operator[](const int n)                  { return pos[n]; }
278
+  FI const T&  operator[](const int n)            const { return pos[n]; }
228 279
   FI XYval<T>& operator= (const T v)                    { set(v,    v   ); return *this; }
229 280
   FI XYval<T>& operator= (const XYZval<T>  &rs)         { set(rs.x, rs.y); return *this; }
230 281
   FI XYval<T>& operator= (const XYZEval<T> &rs)         { set(rs.x, rs.y); return *this; }
@@ -294,219 +345,227 @@ struct XYval {
294 345
 };
295 346
 
296 347
 //
297
-// XYZ coordinates, counters, etc.
348
+// Linear Axes coordinates, counters, flags, etc.
298 349
 //
299 350
 template<typename T>
300 351
 struct XYZval {
301 352
   union {
302
-    struct { T x, y, z; };
303
-    struct { T a, b, c; };
304
-    T pos[3];
353
+    struct { T LINEAR_AXIS_LIST(x, y, z); };
354
+    struct { T LINEAR_AXIS_LIST(a, b, c); };
355
+    T pos[LINEAR_AXES];
305 356
   };
306 357
   FI void set(const T px)                              { x = px; }
307 358
   FI void set(const T px, const T py)                  { x = px; y = py; }
308
-  FI void set(const T px, const T py, const T pz)      { x = px; y = py; z = pz; }
359
+  FI void set(const XYval<T> pxy)                      { x = pxy.x; y = pxy.y; }
309 360
   FI void set(const XYval<T> pxy, const T pz)          { x = pxy.x; y = pxy.y; z = pz; }
310 361
   FI void set(const T (&arr)[XY])                      { x = arr[0]; y = arr[1]; }
311
-  FI void set(const T (&arr)[XYZ])                     { x = arr[0]; y = arr[1]; z = arr[2]; }
312
-  FI void set(const T (&arr)[XYZE])                    { x = arr[0]; y = arr[1]; z = arr[2]; }
313
-  #if DISTINCT_AXES > XYZE
314
-    FI void set(const T (&arr)[DISTINCT_AXES])         { x = arr[0]; y = arr[1]; z = arr[2]; }
362
+  FI void set(const T (&arr)[LINEAR_AXES])             { LINEAR_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2]); }
363
+  #if LINEAR_AXES >= XYZ
364
+    FI void set(LINEAR_AXIS_LIST(const T px, const T py, const T pz))
365
+                                                       { LINEAR_AXIS_CODE(x = px, y = py, z = pz); }
366
+  #endif
367
+  #if LOGICAL_AXES > LINEAR_AXES
368
+    FI void set(const T (&arr)[LOGICAL_AXES])          { LINEAR_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2]); }
369
+    FI void set(LOGICAL_AXIS_LIST(const T, const T px, const T py, const T pz))
370
+                                                       { LINEAR_AXIS_CODE(x = px, y = py, z = pz); }
371
+    #if DISTINCT_AXES > LOGICAL_AXES
372
+      FI void set(const T (&arr)[DISTINCT_AXES])       { LINEAR_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2]); }
373
+    #endif
315 374
   #endif
316
-  FI void reset()                                      { x = y = z = 0; }
317
-  FI T magnitude()                               const { return (T)sqrtf(x*x + y*y + z*z); }
375
+  FI void reset()                                      { LINEAR_AXIS_GANG(x =, y =, z =) 0; }
376
+  FI T magnitude()                               const { return (T)sqrtf(LINEAR_AXIS_GANG(x*x, + y*y, + z*z)); }
318 377
   FI operator T* ()                                    { return pos; }
319
-  FI operator bool()                                   { return z || x || y; }
378
+  FI operator bool()                                   { return LINEAR_AXIS_GANG(z, || x, || y); }
320 379
   FI XYZval<T>          copy()                   const { XYZval<T> o = *this; return o; }
321
-  FI XYZval<T>           ABS()                   const { return { T(_ABS(x)), T(_ABS(y)), T(_ABS(z)) }; }
322
-  FI XYZval<int16_t>   asInt()                         { return { int16_t(x), int16_t(y), int16_t(z) }; }
323
-  FI XYZval<int16_t>   asInt()                   const { return { int16_t(x), int16_t(y), int16_t(z) }; }
324
-  FI XYZval<int32_t>  asLong()                         { return { int32_t(x), int32_t(y), int32_t(z) }; }
325
-  FI XYZval<int32_t>  asLong()                   const { return { int32_t(x), int32_t(y), int32_t(z) }; }
326
-  FI XYZval<int32_t>  ROUNDL()                         { return { int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z)) }; }
327
-  FI XYZval<int32_t>  ROUNDL()                   const { return { int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z)) }; }
328
-  FI XYZval<float>   asFloat()                         { return { static_cast<float>(x), static_cast<float>(y), static_cast<float>(z) }; }
329
-  FI XYZval<float>   asFloat()                   const { return { static_cast<float>(x), static_cast<float>(y), static_cast<float>(z) }; }
330
-  FI XYZval<float> reciprocal()                  const { return {  _RECIP(x),  _RECIP(y),  _RECIP(z) }; }
380
+  FI XYZval<T>           ABS()                   const { return LINEAR_AXIS_ARRAY(T(_ABS(x)), T(_ABS(y)), T(_ABS(z))); }
381
+  FI XYZval<int16_t>   asInt()                         { return LINEAR_AXIS_ARRAY(int16_t(x), int16_t(y), int16_t(z)); }
382
+  FI XYZval<int16_t>   asInt()                   const { return LINEAR_AXIS_ARRAY(int16_t(x), int16_t(y), int16_t(z)); }
383
+  FI XYZval<int32_t>  asLong()                         { return LINEAR_AXIS_ARRAY(int32_t(x), int32_t(y), int32_t(z)); }
384
+  FI XYZval<int32_t>  asLong()                   const { return LINEAR_AXIS_ARRAY(int32_t(x), int32_t(y), int32_t(z)); }
385
+  FI XYZval<int32_t>  ROUNDL()                         { return LINEAR_AXIS_ARRAY(int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z))); }
386
+  FI XYZval<int32_t>  ROUNDL()                   const { return LINEAR_AXIS_ARRAY(int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z))); }
387
+  FI XYZval<float>   asFloat()                         { return LINEAR_AXIS_ARRAY(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)); }
388
+  FI XYZval<float>   asFloat()                   const { return LINEAR_AXIS_ARRAY(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)); }
389
+  FI XYZval<float> reciprocal()                  const { return LINEAR_AXIS_ARRAY(_RECIP(x),  _RECIP(y),  _RECIP(z)); }
331 390
   FI XYZval<float> asLogical()                   const { XYZval<float> o = asFloat(); toLogical(o); return o; }
332 391
   FI XYZval<float>  asNative()                   const { XYZval<float> o = asFloat(); toNative(o);  return o; }
333 392
   FI operator       XYval<T>&()                        { return *(XYval<T>*)this; }
334 393
   FI operator const XYval<T>&()                  const { return *(const XYval<T>*)this; }
335
-  FI operator       XYZEval<T>()                 const { return { x, y, z }; }
336
-  FI       T&   operator[](const int i)                { return pos[i]; }
337
-  FI const T&   operator[](const int i)          const { return pos[i]; }
338
-  FI XYZval<T>& operator= (const T v)                  { set(v,    v,    v   ); return *this; }
394
+  FI operator       XYZEval<T>()                 const { return LINEAR_AXIS_ARRAY(x, y, z); }
395
+  FI       T&   operator[](const int n)                { return pos[n]; }
396
+  FI const T&   operator[](const int n)          const { return pos[n]; }
397
+  FI XYZval<T>& operator= (const T v)                  { set(ARRAY_N_1(LINEAR_AXES, v)); return *this; }
339 398
   FI XYZval<T>& operator= (const XYval<T>   &rs)       { set(rs.x, rs.y      ); return *this; }
340
-  FI XYZval<T>& operator= (const XYZEval<T> &rs)       { set(rs.x, rs.y, rs.z); return *this; }
341
-  FI XYZval<T>  operator+ (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;               return ls; }
342
-  FI XYZval<T>  operator+ (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;               return ls; }
343
-  FI XYZval<T>  operator- (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;               return ls; }
344
-  FI XYZval<T>  operator- (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;               return ls; }
345
-  FI XYZval<T>  operator* (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;               return ls; }
346
-  FI XYZval<T>  operator* (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;               return ls; }
347
-  FI XYZval<T>  operator/ (const XYval<T>   &rs) const { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;               return ls; }
348
-  FI XYZval<T>  operator/ (const XYval<T>   &rs)       { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;               return ls; }
349
-  FI XYZval<T>  operator+ (const XYZval<T>  &rs) const { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; return ls; }
350
-  FI XYZval<T>  operator+ (const XYZval<T>  &rs)       { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; return ls; }
351
-  FI XYZval<T>  operator- (const XYZval<T>  &rs) const { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; return ls; }
352
-  FI XYZval<T>  operator- (const XYZval<T>  &rs)       { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; return ls; }
353
-  FI XYZval<T>  operator* (const XYZval<T>  &rs) const { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; return ls; }
354
-  FI XYZval<T>  operator* (const XYZval<T>  &rs)       { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; return ls; }
355
-  FI XYZval<T>  operator/ (const XYZval<T>  &rs) const { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; return ls; }
356
-  FI XYZval<T>  operator/ (const XYZval<T>  &rs)       { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; return ls; }
357
-  FI XYZval<T>  operator+ (const XYZEval<T> &rs) const { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; return ls; }
358
-  FI XYZval<T>  operator+ (const XYZEval<T> &rs)       { XYZval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; return ls; }
359
-  FI XYZval<T>  operator- (const XYZEval<T> &rs) const { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; return ls; }
360
-  FI XYZval<T>  operator- (const XYZEval<T> &rs)       { XYZval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; return ls; }
361
-  FI XYZval<T>  operator* (const XYZEval<T> &rs) const { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; return ls; }
362
-  FI XYZval<T>  operator* (const XYZEval<T> &rs)       { XYZval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; return ls; }
363
-  FI XYZval<T>  operator/ (const XYZEval<T> &rs) const { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; return ls; }
364
-  FI XYZval<T>  operator/ (const XYZEval<T> &rs)       { XYZval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; return ls; }
365
-  FI XYZval<T>  operator* (const float &v)       const { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    return ls; }
366
-  FI XYZval<T>  operator* (const float &v)             { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    return ls; }
367
-  FI XYZval<T>  operator* (const int &v)         const { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    return ls; }
368
-  FI XYZval<T>  operator* (const int &v)               { XYZval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    return ls; }
369
-  FI XYZval<T>  operator/ (const float &v)       const { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    return ls; }
370
-  FI XYZval<T>  operator/ (const float &v)             { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    return ls; }
371
-  FI XYZval<T>  operator/ (const int &v)         const { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    return ls; }
372
-  FI XYZval<T>  operator/ (const int &v)               { XYZval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    return ls; }
373
-  FI XYZval<T>  operator>>(const int &v)         const { XYZval<T> ls = *this; _RS(ls.x); _RS(ls.y); _RS(ls.z); return ls; }
374
-  FI XYZval<T>  operator>>(const int &v)               { XYZval<T> ls = *this; _RS(ls.x); _RS(ls.y); _RS(ls.z); return ls; }
375
-  FI XYZval<T>  operator<<(const int &v)         const { XYZval<T> ls = *this; _LS(ls.x); _LS(ls.y); _LS(ls.z); return ls; }
376
-  FI XYZval<T>  operator<<(const int &v)               { XYZval<T> ls = *this; _LS(ls.x); _LS(ls.y); _LS(ls.z); return ls; }
377
-  FI XYZval<T>& operator+=(const XYval<T>   &rs)       { x += rs.x; y += rs.y;            return *this; }
378
-  FI XYZval<T>& operator-=(const XYval<T>   &rs)       { x -= rs.x; y -= rs.y;            return *this; }
379
-  FI XYZval<T>& operator*=(const XYval<T>   &rs)       { x *= rs.x; y *= rs.y;            return *this; }
380
-  FI XYZval<T>& operator/=(const XYval<T>   &rs)       { x /= rs.x; y /= rs.y;            return *this; }
381
-  FI XYZval<T>& operator+=(const XYZval<T>  &rs)       { x += rs.x; y += rs.y; z += rs.z; return *this; }
382
-  FI XYZval<T>& operator-=(const XYZval<T>  &rs)       { x -= rs.x; y -= rs.y; z -= rs.z; return *this; }
383
-  FI XYZval<T>& operator*=(const XYZval<T>  &rs)       { x *= rs.x; y *= rs.y; z *= rs.z; return *this; }
384
-  FI XYZval<T>& operator/=(const XYZval<T>  &rs)       { x /= rs.x; y /= rs.y; z /= rs.z; return *this; }
385
-  FI XYZval<T>& operator+=(const XYZEval<T> &rs)       { x += rs.x; y += rs.y; z += rs.z; return *this; }
386
-  FI XYZval<T>& operator-=(const XYZEval<T> &rs)       { x -= rs.x; y -= rs.y; z -= rs.z; return *this; }
387
-  FI XYZval<T>& operator*=(const XYZEval<T> &rs)       { x *= rs.x; y *= rs.y; z *= rs.z; return *this; }
388
-  FI XYZval<T>& operator/=(const XYZEval<T> &rs)       { x /= rs.x; y /= rs.y; z /= rs.z; return *this; }
389
-  FI XYZval<T>& operator*=(const float &v)             { x *= v;    y *= v;    z *= v;    return *this; }
390
-  FI XYZval<T>& operator*=(const int &v)               { x *= v;    y *= v;    z *= v;    return *this; }
391
-  FI XYZval<T>& operator>>=(const int &v)              { _RS(x);   _RS(y);   _RS(z);   return *this; }
392
-  FI XYZval<T>& operator<<=(const int &v)              { _LS(x);   _LS(y);   _LS(z);   return *this; }
393
-  FI bool       operator==(const XYZEval<T> &rs)       { return x == rs.x && y == rs.y && z == rs.z; }
399
+  FI XYZval<T>& operator= (const XYZEval<T> &rs)       { set(LINEAR_AXIS_LIST(rs.x, rs.y, rs.z)); return *this; }
400
+  FI XYZval<T>  operator+ (const XYval<T>   &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, NOOP        ); return ls; }
401
+  FI XYZval<T>  operator+ (const XYval<T>   &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, NOOP        ); return ls; }
402
+  FI XYZval<T>  operator- (const XYval<T>   &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, NOOP        ); return ls; }
403
+  FI XYZval<T>  operator- (const XYval<T>   &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, NOOP        ); return ls; }
404
+  FI XYZval<T>  operator* (const XYval<T>   &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, NOOP        ); return ls; }
405
+  FI XYZval<T>  operator* (const XYval<T>   &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, NOOP        ); return ls; }
406
+  FI XYZval<T>  operator/ (const XYval<T>   &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, NOOP        ); return ls; }
407
+  FI XYZval<T>  operator/ (const XYval<T>   &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, NOOP        ); return ls; }
408
+  FI XYZval<T>  operator+ (const XYZval<T>  &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z); return ls; }
409
+  FI XYZval<T>  operator+ (const XYZval<T>  &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z); return ls; }
410
+  FI XYZval<T>  operator- (const XYZval<T>  &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z); return ls; }
411
+  FI XYZval<T>  operator- (const XYZval<T>  &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z); return ls; }
412
+  FI XYZval<T>  operator* (const XYZval<T>  &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z); return ls; }
413
+  FI XYZval<T>  operator* (const XYZval<T>  &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z); return ls; }
414
+  FI XYZval<T>  operator/ (const XYZval<T>  &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z); return ls; }
415
+  FI XYZval<T>  operator/ (const XYZval<T>  &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z); return ls; }
416
+  FI XYZval<T>  operator+ (const XYZEval<T> &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z); return ls; }
417
+  FI XYZval<T>  operator+ (const XYZEval<T> &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z); return ls; }
418
+  FI XYZval<T>  operator- (const XYZEval<T> &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z); return ls; }
419
+  FI XYZval<T>  operator- (const XYZEval<T> &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z); return ls; }
420
+  FI XYZval<T>  operator* (const XYZEval<T> &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z); return ls; }
421
+  FI XYZval<T>  operator* (const XYZEval<T> &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z); return ls; }
422
+  FI XYZval<T>  operator/ (const XYZEval<T> &rs) const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z); return ls; }
423
+  FI XYZval<T>  operator/ (const XYZEval<T> &rs)       { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z); return ls; }
424
+  FI XYZval<T>  operator* (const float &v)       const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= v,    ls.y *= v,    ls.z *= v   ); return ls; }
425
+  FI XYZval<T>  operator* (const float &v)             { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= v,    ls.y *= v,    ls.z *= v   ); return ls; }
426
+  FI XYZval<T>  operator* (const int &v)         const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= v,    ls.y *= v,    ls.z *= v   ); return ls; }
427
+  FI XYZval<T>  operator* (const int &v)               { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x *= v,    ls.y *= v,    ls.z *= v   ); return ls; }
428
+  FI XYZval<T>  operator/ (const float &v)       const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= v,    ls.y /= v,    ls.z /= v   ); return ls; }
429
+  FI XYZval<T>  operator/ (const float &v)             { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= v,    ls.y /= v,    ls.z /= v   ); return ls; }
430
+  FI XYZval<T>  operator/ (const int &v)         const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= v,    ls.y /= v,    ls.z /= v   ); return ls; }
431
+  FI XYZval<T>  operator/ (const int &v)               { XYZval<T> ls = *this; LINEAR_AXIS_CODE(ls.x /= v,    ls.y /= v,    ls.z /= v   ); return ls; }
432
+  FI XYZval<T>  operator>>(const int &v)         const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(_RS(ls.x),    _RS(ls.y),    _RS(ls.z)   ); return ls; }
433
+  FI XYZval<T>  operator>>(const int &v)               { XYZval<T> ls = *this; LINEAR_AXIS_CODE(_RS(ls.x),    _RS(ls.y),    _RS(ls.z)   ); return ls; }
434
+  FI XYZval<T>  operator<<(const int &v)         const { XYZval<T> ls = *this; LINEAR_AXIS_CODE(_LS(ls.x),    _LS(ls.y),    _LS(ls.z)   ); return ls; }
435
+  FI XYZval<T>  operator<<(const int &v)               { XYZval<T> ls = *this; LINEAR_AXIS_CODE(_LS(ls.x),    _LS(ls.y),    _LS(ls.z)   ); return ls; }
436
+  FI XYZval<T>& operator+=(const XYval<T>   &rs)       { LINEAR_AXIS_CODE(x += rs.x, y += rs.y, NOOP      ); return *this; }
437
+  FI XYZval<T>& operator-=(const XYval<T>   &rs)       { LINEAR_AXIS_CODE(x -= rs.x, y -= rs.y, NOOP      ); return *this; }
438
+  FI XYZval<T>& operator*=(const XYval<T>   &rs)       { LINEAR_AXIS_CODE(x *= rs.x, y *= rs.y, NOOP      ); return *this; }
439
+  FI XYZval<T>& operator/=(const XYval<T>   &rs)       { LINEAR_AXIS_CODE(x /= rs.x, y /= rs.y, NOOP      ); return *this; }
440
+  FI XYZval<T>& operator+=(const XYZval<T>  &rs)       { LINEAR_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z ); return *this; }
441
+  FI XYZval<T>& operator-=(const XYZval<T>  &rs)       { LINEAR_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z ); return *this; }
442
+  FI XYZval<T>& operator*=(const XYZval<T>  &rs)       { LINEAR_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z ); return *this; }
443
+  FI XYZval<T>& operator/=(const XYZval<T>  &rs)       { LINEAR_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z ); return *this; }
444
+  FI XYZval<T>& operator+=(const XYZEval<T> &rs)       { LINEAR_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z ); return *this; }
445
+  FI XYZval<T>& operator-=(const XYZEval<T> &rs)       { LINEAR_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z ); return *this; }
446
+  FI XYZval<T>& operator*=(const XYZEval<T> &rs)       { LINEAR_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z ); return *this; }
447
+  FI XYZval<T>& operator/=(const XYZEval<T> &rs)       { LINEAR_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z ); return *this; }
448
+  FI XYZval<T>& operator*=(const float &v)             { LINEAR_AXIS_CODE(x *= v,    y *= v,    z *= v    ); return *this; }
449
+  FI XYZval<T>& operator*=(const int &v)               { LINEAR_AXIS_CODE(x *= v,    y *= v,    z *= v    ); return *this; }
450
+  FI XYZval<T>& operator>>=(const int &v)              { LINEAR_AXIS_CODE(_RS(x),    _RS(y),    _RS(z)    ); return *this; }
451
+  FI XYZval<T>& operator<<=(const int &v)              { LINEAR_AXIS_CODE(_LS(x),    _LS(y),    _LS(z)    ); return *this; }
452
+  FI bool       operator==(const XYZEval<T> &rs)       { return true LINEAR_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z); }
453
+  FI bool       operator==(const XYZEval<T> &rs) const { return true LINEAR_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z); }
394 454
   FI bool       operator!=(const XYZEval<T> &rs)       { return !operator==(rs); }
395
-  FI bool       operator==(const XYZEval<T> &rs) const { return x == rs.x && y == rs.y && z == rs.z; }
396 455
   FI bool       operator!=(const XYZEval<T> &rs) const { return !operator==(rs); }
397
-  FI XYZval<T>       operator-()                       { XYZval<T> o = *this; o.x = -x; o.y = -y; o.z = -z; return o; }
398
-  FI const XYZval<T> operator-()                 const { XYZval<T> o = *this; o.x = -x; o.y = -y; o.z = -z; return o; }
456
+  FI XYZval<T>       operator-()                       { XYZval<T> o = *this; LINEAR_AXIS_CODE(o.x = -x, o.y = -y, o.z = -z); return o; }
457
+  FI const XYZval<T> operator-()                 const { XYZval<T> o = *this; LINEAR_AXIS_CODE(o.x = -x, o.y = -y, o.z = -z); return o; }
399 458
 };
400 459
 
401 460
 //
402
-// XYZE coordinates, counters, etc.
461
+// Logical Axes coordinates, counters, etc.
403 462
 //
404 463
 template<typename T>
405 464
 struct XYZEval {
406 465
   union {
407
-    struct{ T x, y, z, e; };
408
-    struct{ T a, b, c; };
409
-    T pos[4];
466
+    struct{ T LOGICAL_AXIS_LIST(e, x, y, z); };
467
+    struct{ T LINEAR_AXIS_LIST(a, b, c); };
468
+    T pos[LOGICAL_AXES];
410 469
   };
411
-  FI void reset()                                             { x = y = z = e = 0; }
412
-  FI T magnitude()                                      const { return (T)sqrtf(x*x + y*y + z*z + e*e); }
470
+  FI void reset()                                             { LOGICAL_AXIS_GANG(e =, x =, y =, z =) 0; }
471
+  FI T magnitude()                                      const { return (T)sqrtf(LOGICAL_AXIS_GANG(+ e*e, + x*x, + y*y, + z*z)); }
413 472
   FI operator T* ()                                           { return pos; }
414
-  FI operator bool()                                          { return e || z || x || y; }
415
-  FI void set(const T px)                                     { x = px;                                        }
416
-  FI void set(const T px, const T py)                         { x = px;     y = py;                            }
417
-  FI void set(const T px, const T py, const T pz)             { x = px;     y = py;     z = pz;                }
418
-  FI void set(const T px, const T py, const T pz, const T pe) { x = px;     y = py;     z = pz;     e = pe;    }
419
-  FI void set(const XYval<T> pxy)                             { x = pxy.x;  y = pxy.y;                         }
420
-  FI void set(const XYval<T> pxy, const T pz)                 { x = pxy.x;  y = pxy.y;  z = pz;                }
421
-  FI void set(const XYZval<T> pxyz)                           { x = pxyz.x; y = pxyz.y; z = pxyz.z;            }
422
-  FI void set(const XYval<T> pxy, const T pz, const T pe)     { x = pxy.x;  y = pxy.y;  z = pz;     e = pe;    }
423
-  FI void set(const XYval<T> pxy, const XYval<T> pze)         { x = pxy.x;  y = pxy.y;  z = pze.z;  e = pze.e; }
424
-  FI void set(const XYZval<T> pxyz, const T pe)               { x = pxyz.x; y = pxyz.y; z = pxyz.z; e = pe;    }
425
-  FI void set(const T (&arr)[XY])                             { x = arr[0]; y = arr[1]; }
426
-  FI void set(const T (&arr)[XYZ])                            { x = arr[0]; y = arr[1]; z = arr[2]; }
427
-  FI void set(const T (&arr)[XYZE])                           { x = arr[0]; y = arr[1]; z = arr[2]; e = arr[3]; }
428
-  #if DISTINCT_AXES > XYZE
429
-    FI void set(const T (&arr)[DISTINCT_AXES])                { x = arr[0]; y = arr[1]; z = arr[2]; e = arr[3]; }
473
+  FI operator bool()                                          { return false LOGICAL_AXIS_GANG(|| e, || x, || y, || z); }
474
+  FI void set(const T px)                                     { x = px;               }
475
+  FI void set(const T px, const T py)                         { x = px;    y = py;    }
476
+  FI void set(const XYval<T> pxy)                             { x = pxy.x; y = pxy.y; }
477
+  FI void set(const XYZval<T> pxyz)                           { set(LINEAR_AXIS_LIST(pxyz.x, pxyz.y, pxyz.z)); }
478
+  #if LINEAR_AXES >= XYZ
479
+    FI void set(LINEAR_AXIS_LIST(const T px, const T py, const T pz)) {
480
+      LINEAR_AXIS_CODE(x = px, y = py, z = pz);
481
+    }
430 482
   #endif
431
-  FI XYZEval<T>          copy()                         const { return *this; }
432
-  FI XYZEval<T>           ABS()                         const { return { T(_ABS(x)), T(_ABS(y)), T(_ABS(z)), T(_ABS(e)) }; }
433
-  FI XYZEval<int16_t>   asInt()                               { return { int16_t(x), int16_t(y), int16_t(z), int16_t(e) }; }
434
-  FI XYZEval<int16_t>   asInt()                         const { return { int16_t(x), int16_t(y), int16_t(z), int16_t(e) }; }
435
-  FI XYZEval<int32_t>  asLong()                               { return { int32_t(x), int32_t(y), int32_t(z), int32_t(e) }; }
436
-  FI XYZEval<int32_t>  asLong()                         const { return { int32_t(x), int32_t(y), int32_t(z), int32_t(e) }; }
437
-  FI XYZEval<int32_t>  ROUNDL()                               { return { int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z)), int32_t(LROUND(e)) }; }
438
-  FI XYZEval<int32_t>  ROUNDL()                         const { return { int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z)), int32_t(LROUND(e)) }; }
439
-  FI XYZEval<float>   asFloat()                               { return { static_cast<float>(x), static_cast<float>(y), static_cast<float>(z), static_cast<float>(e) }; }
440
-  FI XYZEval<float>   asFloat()                         const { return { static_cast<float>(x), static_cast<float>(y), static_cast<float>(z), static_cast<float>(e) }; }
441
-  FI XYZEval<float> reciprocal()                        const { return {  _RECIP(x),  _RECIP(y),  _RECIP(z),  _RECIP(e) }; }
483
+  #if LOGICAL_AXES > LINEAR_AXES
484
+    FI void set(LOGICAL_AXIS_LIST(const T pe, const T px, const T py, const T pz)) {
485
+      LOGICAL_AXIS_CODE(e = pe, x = px, y = py, z = pz);
486
+    }
487
+    FI void set(const XYval<T> pxy, const T pe)               { set(pxy); e = pe; }
488
+    FI void set(const XYZval<T> pxyz, const T pe)             { set(pxyz); e = pe; }
489
+  #endif
490
+  FI XYZEval<T>          copy()                         const { XYZEval<T> o = *this; return o; }
491
+  FI XYZEval<T>           ABS()                         const { return LOGICAL_AXIS_ARRAY(T(_ABS(e)), T(_ABS(x)), T(_ABS(y)), T(_ABS(z))); }
492
+  FI XYZEval<int16_t>   asInt()                               { return LOGICAL_AXIS_ARRAY(int16_t(e), int16_t(x), int16_t(y), int16_t(z)); }
493
+  FI XYZEval<int16_t>   asInt()                         const { return LOGICAL_AXIS_ARRAY(int16_t(e), int16_t(x), int16_t(y), int16_t(z)); }
494
+  FI XYZEval<int32_t>  asLong()                               { return LOGICAL_AXIS_ARRAY(int32_t(e), int32_t(x), int32_t(y), int32_t(z)); }
495
+  FI XYZEval<int32_t>  asLong()                         const { return LOGICAL_AXIS_ARRAY(int32_t(e), int32_t(x), int32_t(y), int32_t(z)); }
496
+  FI XYZEval<int32_t>  ROUNDL()                               { return LOGICAL_AXIS_ARRAY(int32_t(LROUND(e)), int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z))); }
497
+  FI XYZEval<int32_t>  ROUNDL()                         const { return LOGICAL_AXIS_ARRAY(int32_t(LROUND(e)), int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z))); }
498
+  FI XYZEval<float>   asFloat()                               { return LOGICAL_AXIS_ARRAY(static_cast<float>(e), static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)); }
499
+  FI XYZEval<float>   asFloat()                         const { return LOGICAL_AXIS_ARRAY(static_cast<float>(e), static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)); }
500
+  FI XYZEval<float> reciprocal()                        const { return LOGICAL_AXIS_ARRAY(_RECIP(e),  _RECIP(x),  _RECIP(y),  _RECIP(z)); }
442 501
   FI XYZEval<float> asLogical()                         const { XYZEval<float> o = asFloat(); toLogical(o); return o; }
443 502
   FI XYZEval<float>  asNative()                         const { XYZEval<float> o = asFloat(); toNative(o);  return o; }
444 503
   FI operator       XYval<T>&()                               { return *(XYval<T>*)this; }
445 504
   FI operator const XYval<T>&()                         const { return *(const XYval<T>*)this; }
446 505
   FI operator       XYZval<T>&()                              { return *(XYZval<T>*)this; }
447 506
   FI operator const XYZval<T>&()                        const { return *(const XYZval<T>*)this; }
448
-  FI       T&    operator[](const int i)                      { return pos[i]; }
449
-  FI const T&    operator[](const int i)                const { return pos[i]; }
450
-  FI XYZEval<T>& operator= (const T v)                        { set(v, v, v, v); return *this; }
507
+  FI       T&    operator[](const int n)                      { return pos[n]; }
508
+  FI const T&    operator[](const int n)                const { return pos[n]; }
509
+  FI XYZEval<T>& operator= (const T v)                        { set(LIST_N_1(LINEAR_AXES, v)); return *this; }
451 510
   FI XYZEval<T>& operator= (const XYval<T>   &rs)             { set(rs.x, rs.y); return *this; }
452
-  FI XYZEval<T>& operator= (const XYZval<T>  &rs)             { set(rs.x, rs.y, rs.z); return *this; }
453
-  FI XYZEval<T>  operator+ (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;                             return ls; }
454
-  FI XYZEval<T>  operator+ (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y;                             return ls; }
455
-  FI XYZEval<T>  operator- (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;                             return ls; }
456
-  FI XYZEval<T>  operator- (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y;                             return ls; }
457
-  FI XYZEval<T>  operator* (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;                             return ls; }
458
-  FI XYZEval<T>  operator* (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y;                             return ls; }
459
-  FI XYZEval<T>  operator/ (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;                             return ls; }
460
-  FI XYZEval<T>  operator/ (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y;                             return ls; }
461
-  FI XYZEval<T>  operator+ (const XYZval<T>  &rs)       const { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z;               return ls; }
462
-  FI XYZEval<T>  operator+ (const XYZval<T>  &rs)             { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z;               return ls; }
463
-  FI XYZEval<T>  operator- (const XYZval<T>  &rs)       const { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z;               return ls; }
464
-  FI XYZEval<T>  operator- (const XYZval<T>  &rs)             { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z;               return ls; }
465
-  FI XYZEval<T>  operator* (const XYZval<T>  &rs)       const { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z;               return ls; }
466
-  FI XYZEval<T>  operator* (const XYZval<T>  &rs)             { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z;               return ls; }
467
-  FI XYZEval<T>  operator/ (const XYZval<T>  &rs)       const { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z;               return ls; }
468
-  FI XYZEval<T>  operator/ (const XYZval<T>  &rs)             { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z;               return ls; }
469
-  FI XYZEval<T>  operator+ (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; ls.e += rs.e; return ls; }
470
-  FI XYZEval<T>  operator+ (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; ls.z += rs.z; ls.e += rs.e; return ls; }
471
-  FI XYZEval<T>  operator- (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; ls.e -= rs.e; return ls; }
472
-  FI XYZEval<T>  operator- (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; ls.z -= rs.z; ls.e -= rs.e; return ls; }
473
-  FI XYZEval<T>  operator* (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; ls.e *= rs.e; return ls; }
474
-  FI XYZEval<T>  operator* (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; ls.z *= rs.z; ls.e *= rs.e; return ls; }
475
-  FI XYZEval<T>  operator/ (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; ls.e /= rs.e; return ls; }
476
-  FI XYZEval<T>  operator/ (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; ls.z /= rs.z; ls.e /= rs.e; return ls; }
477
-  FI XYZEval<T>  operator* (const float &v)             const { XYZEval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    ls.e *= v;    return ls; }
478
-  FI XYZEval<T>  operator* (const float &v)                   { XYZEval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    ls.e *= v;    return ls; }
479
-  FI XYZEval<T>  operator* (const int &v)               const { XYZEval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    ls.e *= v;    return ls; }
480
-  FI XYZEval<T>  operator* (const int &v)                     { XYZEval<T> ls = *this; ls.x *= v;    ls.y *= v;    ls.z *= v;    ls.e *= v;    return ls; }
481
-  FI XYZEval<T>  operator/ (const float &v)             const { XYZEval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    ls.e /= v;    return ls; }
482
-  FI XYZEval<T>  operator/ (const float &v)                   { XYZEval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    ls.e /= v;    return ls; }
483
-  FI XYZEval<T>  operator/ (const int &v)               const { XYZEval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    ls.e /= v;    return ls; }
484
-  FI XYZEval<T>  operator/ (const int &v)                     { XYZEval<T> ls = *this; ls.x /= v;    ls.y /= v;    ls.z /= v;    ls.e /= v;    return ls; }
485
-  FI XYZEval<T>  operator>>(const int &v)               const { XYZEval<T> ls = *this; _RS(ls.x);    _RS(ls.y);    _RS(ls.z);    _RS(ls.e);    return ls; }
486
-  FI XYZEval<T>  operator>>(const int &v)                     { XYZEval<T> ls = *this; _RS(ls.x);    _RS(ls.y);    _RS(ls.z);    _RS(ls.e);    return ls; }
487
-  FI XYZEval<T>  operator<<(const int &v)               const { XYZEval<T> ls = *this; _LS(ls.x);    _LS(ls.y);    _LS(ls.z);    _LS(ls.e);    return ls; }
488
-  FI XYZEval<T>  operator<<(const int &v)                     { XYZEval<T> ls = *this; _LS(ls.x);    _LS(ls.y);    _LS(ls.z);    _LS(ls.e);    return ls; }
489
-  FI XYZEval<T>& operator+=(const XYval<T>   &rs)             { x += rs.x; y += rs.y;                       return *this; }
490
-  FI XYZEval<T>& operator-=(const XYval<T>   &rs)             { x -= rs.x; y -= rs.y;                       return *this; }
491
-  FI XYZEval<T>& operator*=(const XYval<T>   &rs)             { x *= rs.x; y *= rs.y;                       return *this; }
492
-  FI XYZEval<T>& operator/=(const XYval<T>   &rs)             { x /= rs.x; y /= rs.y;                       return *this; }
493
-  FI XYZEval<T>& operator+=(const XYZval<T>  &rs)             { x += rs.x; y += rs.y; z += rs.z;            return *this; }
494
-  FI XYZEval<T>& operator-=(const XYZval<T>  &rs)             { x -= rs.x; y -= rs.y; z -= rs.z;            return *this; }
495
-  FI XYZEval<T>& operator*=(const XYZval<T>  &rs)             { x *= rs.x; y *= rs.y; z *= rs.z;            return *this; }
496
-  FI XYZEval<T>& operator/=(const XYZval<T>  &rs)             { x /= rs.x; y /= rs.y; z /= rs.z;            return *this; }
497
-  FI XYZEval<T>& operator+=(const XYZEval<T> &rs)             { x += rs.x; y += rs.y; z += rs.z; e += rs.e; return *this; }
498
-  FI XYZEval<T>& operator-=(const XYZEval<T> &rs)             { x -= rs.x; y -= rs.y; z -= rs.z; e -= rs.e; return *this; }
499
-  FI XYZEval<T>& operator*=(const XYZEval<T> &rs)             { x *= rs.x; y *= rs.y; z *= rs.z; e *= rs.e; return *this; }
500
-  FI XYZEval<T>& operator/=(const XYZEval<T> &rs)             { x /= rs.x; y /= rs.y; z /= rs.z; e /= rs.e; return *this; }
501
-  FI XYZEval<T>& operator*=(const T &v)                       { x *= v;    y *= v;    z *= v;    e *= v;    return *this; }
502
-  FI XYZEval<T>& operator>>=(const int &v)                    { _RS(x);    _RS(y);    _RS(z);    _RS(e);    return *this; }
503
-  FI XYZEval<T>& operator<<=(const int &v)                    { _LS(x);    _LS(y);    _LS(z);    _LS(e);    return *this; }
504
-  FI bool        operator==(const XYZval<T>  &rs)             { return x == rs.x && y == rs.y && z == rs.z; }
511
+  FI XYZEval<T>& operator= (const XYZval<T>  &rs)             { set(LINEAR_AXIS_LIST(rs.x, rs.y, rs.z)); return *this; }
512
+  FI XYZEval<T>  operator+ (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; }
513
+  FI XYZEval<T>  operator+ (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; }
514
+  FI XYZEval<T>  operator- (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; }
515
+  FI XYZEval<T>  operator- (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x -= rs.x; ls.y -= rs.y; return ls; }
516
+  FI XYZEval<T>  operator* (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; }
517
+  FI XYZEval<T>  operator* (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; }
518
+  FI XYZEval<T>  operator/ (const XYval<T>   &rs)       const { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; }
519
+  FI XYZEval<T>  operator/ (const XYval<T>   &rs)             { XYZEval<T> ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; }
520
+  FI XYZEval<T>  operator+ (const XYZval<T>  &rs)       const { XYZval<T>  ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z); return ls; }
521
+  FI XYZEval<T>  operator+ (const XYZval<T>  &rs)             { XYZval<T>  ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z); return ls; }
522
+  FI XYZEval<T>  operator- (const XYZval<T>  &rs)       const { XYZval<T>  ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z); return ls; }
523
+  FI XYZEval<T>  operator- (const XYZval<T>  &rs)             { XYZval<T>  ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z); return ls; }
524
+  FI XYZEval<T>  operator* (const XYZval<T>  &rs)       const { XYZval<T>  ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z); return ls; }
525
+  FI XYZEval<T>  operator* (const XYZval<T>  &rs)             { XYZval<T>  ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z); return ls; }
526
+  FI XYZEval<T>  operator/ (const XYZval<T>  &rs)       const { XYZval<T>  ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z); return ls; }
527
+  FI XYZEval<T>  operator/ (const XYZval<T>  &rs)             { XYZval<T>  ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z); return ls; }
528
+  FI XYZEval<T>  operator+ (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e += rs.e, ls.x += rs.x, ls.y += rs.y, ls.z += rs.z ); return ls; }
529
+  FI XYZEval<T>  operator+ (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e += rs.e, ls.x += rs.x, ls.y += rs.y, ls.z += rs.z ); return ls; }
530
+  FI XYZEval<T>  operator- (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e -= rs.e, ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z ); return ls; }
531
+  FI XYZEval<T>  operator- (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e -= rs.e, ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z ); return ls; }
532
+  FI XYZEval<T>  operator* (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e *= rs.e, ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z ); return ls; }
533
+  FI XYZEval<T>  operator* (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e *= rs.e, ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z ); return ls; }
534
+  FI XYZEval<T>  operator/ (const XYZEval<T> &rs)       const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e /= rs.e, ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z ); return ls; }
535
+  FI XYZEval<T>  operator/ (const XYZEval<T> &rs)             { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e /= rs.e, ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z ); return ls; }
536
+  FI XYZEval<T>  operator* (const float &v)             const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e *= v,    ls.x *= v,    ls.y *= v,    ls.z *= v    ); return ls; }
537
+  FI XYZEval<T>  operator* (const float &v)                   { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e *= v,    ls.x *= v,    ls.y *= v,    ls.z *= v    ); return ls; }
538
+  FI XYZEval<T>  operator* (const int &v)               const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e *= v,    ls.x *= v,    ls.y *= v,    ls.z *= v    ); return ls; }
539
+  FI XYZEval<T>  operator* (const int &v)                     { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e *= v,    ls.x *= v,    ls.y *= v,    ls.z *= v    ); return ls; }
540
+  FI XYZEval<T>  operator/ (const float &v)             const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e /= v,    ls.x /= v,    ls.y /= v,    ls.z /= v    ); return ls; }
541
+  FI XYZEval<T>  operator/ (const float &v)                   { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e /= v,    ls.x /= v,    ls.y /= v,    ls.z /= v    ); return ls; }
542
+  FI XYZEval<T>  operator/ (const int &v)               const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e /= v,    ls.x /= v,    ls.y /= v,    ls.z /= v    ); return ls; }
543
+  FI XYZEval<T>  operator/ (const int &v)                     { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(ls.e /= v,    ls.x /= v,    ls.y /= v,    ls.z /= v    ); return ls; }
544
+  FI XYZEval<T>  operator>>(const int &v)               const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(_RS(ls.e),    _RS(ls.x),    _RS(ls.y),    _RS(ls.z)    ); return ls; }
545
+  FI XYZEval<T>  operator>>(const int &v)                     { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(_RS(ls.e),    _RS(ls.x),    _RS(ls.y),    _RS(ls.z)    ); return ls; }
546
+  FI XYZEval<T>  operator<<(const int &v)               const { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(_LS(ls.e),    _LS(ls.x),    _LS(ls.y),    _LS(ls.z)    ); return ls; }
547
+  FI XYZEval<T>  operator<<(const int &v)                     { XYZEval<T> ls = *this; LOGICAL_AXIS_CODE(_LS(ls.e),    _LS(ls.x),    _LS(ls.y),    _LS(ls.z)    ); return ls; }
548
+  FI XYZEval<T>& operator+=(const XYval<T>   &rs)             { x += rs.x; y += rs.y; return *this; }
549
+  FI XYZEval<T>& operator-=(const XYval<T>   &rs)             { x -= rs.x; y -= rs.y; return *this; }
550
+  FI XYZEval<T>& operator*=(const XYval<T>   &rs)             { x *= rs.x; y *= rs.y; return *this; }
551
+  FI XYZEval<T>& operator/=(const XYval<T>   &rs)             { x /= rs.x; y /= rs.y; return *this; }
552
+  FI XYZEval<T>& operator+=(const XYZval<T>  &rs)             { LINEAR_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z); return *this; }
553
+  FI XYZEval<T>& operator-=(const XYZval<T>  &rs)             { LINEAR_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z); return *this; }
554
+  FI XYZEval<T>& operator*=(const XYZval<T>  &rs)             { LINEAR_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z); return *this; }
555
+  FI XYZEval<T>& operator/=(const XYZval<T>  &rs)             { LINEAR_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z); return *this; }
556
+  FI XYZEval<T>& operator+=(const XYZEval<T> &rs)             { LOGICAL_AXIS_CODE(e += rs.e, x += rs.x, y += rs.y, z += rs.z); return *this; }
557
+  FI XYZEval<T>& operator-=(const XYZEval<T> &rs)             { LOGICAL_AXIS_CODE(e -= rs.e, x -= rs.x, y -= rs.y, z -= rs.z); return *this; }
558
+  FI XYZEval<T>& operator*=(const XYZEval<T> &rs)             { LOGICAL_AXIS_CODE(e *= rs.e, x *= rs.x, y *= rs.y, z *= rs.z); return *this; }
559
+  FI XYZEval<T>& operator/=(const XYZEval<T> &rs)             { LOGICAL_AXIS_CODE(e /= rs.e, x /= rs.x, y /= rs.y, z /= rs.z); return *this; }
560
+  FI XYZEval<T>& operator*=(const T &v)                       { LOGICAL_AXIS_CODE(e *= v,    x *= v,    y *= v,    z *= v);    return *this; }
561
+  FI XYZEval<T>& operator>>=(const int &v)                    { LOGICAL_AXIS_CODE(_RS(e),    _RS(x),    _RS(y),    _RS(z));    return *this; }
562
+  FI XYZEval<T>& operator<<=(const int &v)                    { LOGICAL_AXIS_CODE(_LS(e),    _LS(x),    _LS(y),    _LS(z));    return *this; }
563
+  FI bool        operator==(const XYZval<T>  &rs)             { return true LINEAR_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z); }
564
+  FI bool        operator==(const XYZval<T>  &rs)       const { return true LINEAR_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z); }
505 565
   FI bool        operator!=(const XYZval<T>  &rs)             { return !operator==(rs); }
506
-  FI bool        operator==(const XYZval<T>  &rs)       const { return x == rs.x && y == rs.y && z == rs.z; }
507 566
   FI bool        operator!=(const XYZval<T>  &rs)       const { return !operator==(rs); }
508
-  FI       XYZEval<T> operator-()                             { return { -x, -y, -z, -e }; }
509
-  FI const XYZEval<T> operator-()                       const { return { -x, -y, -z, -e }; }
567
+  FI       XYZEval<T> operator-()                             { return LOGICAL_AXIS_ARRAY(-e, -x, -y, -z); }
568
+  FI const XYZEval<T> operator-()                       const { return LOGICAL_AXIS_ARRAY(-e, -x, -y, -z); }
510 569
 };
511 570
 
512 571
 #undef _RECIP
@@ -514,6 +573,3 @@ struct XYZEval {
514 573
 #undef _LS
515 574
 #undef _RS
516 575
 #undef FI
517
-
518
-const xyze_char_t axis_codes { 'X', 'Y', 'Z', 'E' };
519
-#define AXIS_CHAR(A) ((char)('X' + A))

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

@@ -76,3 +76,11 @@ public:
76 76
 // Converts from an uint8_t in the range of 0-255 to an uint8_t
77 77
 // in the range 0-100 while avoiding rounding artifacts
78 78
 constexpr uint8_t ui8_to_percent(const uint8_t i) { return (int(i) * 100 + 127) / 255; }
79
+
80
+const xyze_char_t axis_codes LOGICAL_AXIS_ARRAY('E', 'X', 'Y', 'Z');
81
+
82
+#if LINEAR_AXES <= XYZ
83
+  #define AXIS_CHAR(A) ((char)('X' + A))
84
+#else
85
+  #define AXIS_CHAR(A) axis_codes[A]
86
+#endif

+ 17
- 26
Marlin/src/feature/encoder_i2c.cpp View File

@@ -327,7 +327,7 @@ int32_t I2CPositionEncoder::get_raw_count() {
327 327
 }
328 328
 
329 329
 bool I2CPositionEncoder::test_axis() {
330
-  //only works on XYZ cartesian machines for the time being
330
+  // Only works on XYZ Cartesian machines for the time being
331 331
   if (!(encoderAxis == X_AXIS || encoderAxis == Y_AXIS || encoderAxis == Z_AXIS)) return false;
332 332
 
333 333
   const float startPosition = soft_endstop.min[encoderAxis] + 10,
@@ -345,9 +345,12 @@ bool I2CPositionEncoder::test_axis() {
345 345
   endCoord[encoderAxis] = endPosition;
346 346
 
347 347
   planner.synchronize();
348
-  startCoord.e = planner.get_axis_position_mm(E_AXIS);
349
-  planner.buffer_line(startCoord, fr_mm_s, 0);
350
-  planner.synchronize();
348
+
349
+  #if HAS_EXTRUDERS
350
+    startCoord.e = planner.get_axis_position_mm(E_AXIS);
351
+    planner.buffer_line(startCoord, fr_mm_s, 0);
352
+    planner.synchronize();
353
+  #endif
351 354
 
352 355
   // if the module isn't currently trusted, wait until it is (or until it should be if things are working)
353 356
   if (!trusted) {
@@ -357,7 +360,7 @@ bool I2CPositionEncoder::test_axis() {
357 360
   }
358 361
 
359 362
   if (trusted) { // if trusted, commence test
360
-    endCoord.e = planner.get_axis_position_mm(E_AXIS);
363
+    TERN_(HAS_EXTRUDERS, endCoord.e = planner.get_axis_position_mm(E_AXIS));
361 364
     planner.buffer_line(endCoord, fr_mm_s, 0);
362 365
     planner.synchronize();
363 366
   }
@@ -402,7 +405,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
402 405
   planner.synchronize();
403 406
 
404 407
   LOOP_L_N(i, iter) {
405
-    startCoord.e = planner.get_axis_position_mm(E_AXIS);
408
+    TERN_(HAS_EXTRUDERS, startCoord.e = planner.get_axis_position_mm(E_AXIS));
406 409
     planner.buffer_line(startCoord, fr_mm_s, 0);
407 410
     planner.synchronize();
408 411
 
@@ -411,7 +414,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
411 414
 
412 415
     //do_blocking_move_to(endCoord);
413 416
 
414
-    endCoord.e = planner.get_axis_position_mm(E_AXIS);
417
+    TERN_(HAS_EXTRUDERS, endCoord.e = planner.get_axis_position_mm(E_AXIS));
415 418
     planner.buffer_line(endCoord, fr_mm_s, 0);
416 419
     planner.synchronize();
417 420
 
@@ -497,9 +500,7 @@ void I2CPositionEncodersMgr::init() {
497 500
 
498 501
     encoders[i].set_active(encoders[i].passes_test(true));
499 502
 
500
-    #if I2CPE_ENC_1_AXIS == E_AXIS
501
-      encoders[i].set_homed();
502
-    #endif
503
+    TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_1_AXIS == E_AXIS) encoders[i].set_homed());
503 504
   #endif
504 505
 
505 506
   #if I2CPE_ENCODER_CNT > 1
@@ -528,9 +529,7 @@ void I2CPositionEncodersMgr::init() {
528 529
 
529 530
     encoders[i].set_active(encoders[i].passes_test(true));
530 531
 
531
-    #if I2CPE_ENC_2_AXIS == E_AXIS
532
-      encoders[i].set_homed();
533
-    #endif
532
+    TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_2_AXIS == E_AXIS) encoders[i].set_homed());
534 533
   #endif
535 534
 
536 535
   #if I2CPE_ENCODER_CNT > 2
@@ -557,11 +556,9 @@ void I2CPositionEncodersMgr::init() {
557 556
       encoders[i].set_ec_threshold(I2CPE_ENC_3_EC_THRESH);
558 557
     #endif
559 558
 
560
-  encoders[i].set_active(encoders[i].passes_test(true));
559
+    encoders[i].set_active(encoders[i].passes_test(true));
561 560
 
562
-    #if I2CPE_ENC_3_AXIS == E_AXIS
563
-      encoders[i].set_homed();
564
-    #endif
561
+    TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_3_AXIS == E_AXIS) encoders[i].set_homed());
565 562
   #endif
566 563
 
567 564
   #if I2CPE_ENCODER_CNT > 3
@@ -590,9 +587,7 @@ void I2CPositionEncodersMgr::init() {
590 587
 
591 588
     encoders[i].set_active(encoders[i].passes_test(true));
592 589
 
593
-    #if I2CPE_ENC_4_AXIS == E_AXIS
594
-      encoders[i].set_homed();
595
-    #endif
590
+    TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_4_AXIS == E_AXIS) encoders[i].set_homed());
596 591
   #endif
597 592
 
598 593
   #if I2CPE_ENCODER_CNT > 4
@@ -621,9 +616,7 @@ void I2CPositionEncodersMgr::init() {
621 616
 
622 617
     encoders[i].set_active(encoders[i].passes_test(true));
623 618
 
624
-    #if I2CPE_ENC_5_AXIS == E_AXIS
625
-      encoders[i].set_homed();
626
-    #endif
619
+    TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_5_AXIS == E_AXIS) encoders[i].set_homed());
627 620
   #endif
628 621
 
629 622
   #if I2CPE_ENCODER_CNT > 5
@@ -652,9 +645,7 @@ void I2CPositionEncodersMgr::init() {
652 645
 
653 646
     encoders[i].set_active(encoders[i].passes_test(true));
654 647
 
655
-    #if I2CPE_ENC_6_AXIS == E_AXIS
656
-      encoders[i].set_homed();
657
-    #endif
648
+    TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_6_AXIS == E_AXIS) encoders[i].set_homed());
658 649
   #endif
659 650
 }
660 651
 

+ 25
- 9
Marlin/src/feature/tmc_util.cpp View File

@@ -757,7 +757,10 @@
757 757
     }
758 758
   }
759 759
 
760
-  static void tmc_debug_loop(const TMC_debug_enum i, const bool print_x, const bool print_y, const bool print_z, const bool print_e) {
760
+  static void tmc_debug_loop(
761
+    const TMC_debug_enum i,
762
+    LOGICAL_AXIS_LIST(const bool print_e, const bool print_x, const bool print_y, const bool print_z)
763
+  ) {
761 764
     if (print_x) {
762 765
       #if AXIS_IS_TMC(X)
763 766
         tmc_status(stepperX, i);
@@ -821,7 +824,10 @@
821 824
     SERIAL_EOL();
822 825
   }
823 826
 
824
-  static void drv_status_loop(const TMC_drv_status_enum i, const bool print_x, const bool print_y, const bool print_z, const bool print_e) {
827
+  static void drv_status_loop(
828
+    const TMC_drv_status_enum i,
829
+    LOGICAL_AXIS_LIST(const bool print_e, const bool print_x, const bool print_y, const bool print_z)
830
+  ) {
825 831
     if (print_x) {
826 832
       #if AXIS_IS_TMC(X)
827 833
         tmc_parse_drv_status(stepperX, i);
@@ -889,9 +895,12 @@
889 895
    * M122 report functions
890 896
    */
891 897
 
892
-  void tmc_report_all(const bool print_x/*=true*/, const bool print_y/*=true*/, const bool print_z/*=true*/, const bool print_e/*=true*/) {
893
-    #define TMC_REPORT(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL);  tmc_debug_loop(ITEM, print_x, print_y, print_z, print_e); }while(0)
894
-    #define DRV_REPORT(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL); drv_status_loop(ITEM, print_x, print_y, print_z, print_e); }while(0)
898
+  void tmc_report_all(
899
+    LOGICAL_AXIS_LIST(const bool print_e/*=true*/, const bool print_x/*=true*/, const bool print_y/*=true*/, const bool print_z/*=true*/)
900
+  ) {
901
+    #define TMC_REPORT(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL);  tmc_debug_loop(ITEM, LOGICAL_AXIS_LIST(print_e, print_x, print_y, print_z)); }while(0)
902
+    #define DRV_REPORT(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL); drv_status_loop(ITEM, LOGICAL_AXIS_LIST(print_e, print_x, print_y, print_z)); }while(0)
903
+
895 904
     TMC_REPORT("\t",                 TMC_CODES);
896 905
     #if HAS_DRIVER(TMC2209)
897 906
       TMC_REPORT("Address\t",        TMC_UART_ADDR);
@@ -1015,7 +1024,10 @@
1015 1024
     }
1016 1025
   #endif
1017 1026
 
1018
-  static void tmc_get_registers(TMC_get_registers_enum i, const bool print_x, const bool print_y, const bool print_z, const bool print_e) {
1027
+  static void tmc_get_registers(
1028
+    TMC_get_registers_enum i,
1029
+    LOGICAL_AXIS_LIST(const bool print_e, const bool print_x, const bool print_y, const bool print_z)
1030
+  ) {
1019 1031
     if (print_x) {
1020 1032
       #if AXIS_IS_TMC(X)
1021 1033
         tmc_get_registers(stepperX, i);
@@ -1079,8 +1091,10 @@
1079 1091
     SERIAL_EOL();
1080 1092
   }
1081 1093
 
1082
-  void tmc_get_registers(bool print_x, bool print_y, bool print_z, bool print_e) {
1083
-    #define _TMC_GET_REG(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL); tmc_get_registers(ITEM, print_x, print_y, print_z, print_e); }while(0)
1094
+  void tmc_get_registers(
1095
+    LOGICAL_AXIS_LIST(bool print_e, bool print_x, bool print_y, bool print_z)
1096
+  ) {
1097
+    #define _TMC_GET_REG(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL); tmc_get_registers(ITEM, LOGICAL_AXIS_LIST(print_e, print_x, print_y, print_z)); }while(0)
1084 1098
     #define TMC_GET_REG(NAME, TABS) _TMC_GET_REG(STRINGIFY(NAME) TABS, TMC_GET_##NAME)
1085 1099
     _TMC_GET_REG("\t", TMC_AXIS_CODES);
1086 1100
     TMC_GET_REG(GCONF, "\t\t");
@@ -1214,7 +1228,9 @@ static bool test_connection(TMC &st) {
1214 1228
   return test_result;
1215 1229
 }
1216 1230
 
1217
-void test_tmc_connection(const bool test_x/*=true*/, const bool test_y/*=true*/, const bool test_z/*=true*/, const bool test_e/*=true*/) {
1231
+void test_tmc_connection(
1232
+  LOGICAL_AXIS_LIST(const bool test_e/*=true*/, const bool test_x/*=true*/, const bool test_y/*=true*/, const bool test_z/*=true*/)
1233
+) {
1218 1234
   uint8_t axis_connection = 0;
1219 1235
 
1220 1236
   if (test_x) {

+ 10
- 4
Marlin/src/feature/tmc_util.h View File

@@ -335,14 +335,20 @@ void tmc_print_current(TMC &st) {
335 335
 #endif
336 336
 
337 337
 void monitor_tmc_drivers();
338
-void test_tmc_connection(const bool test_x=true, const bool test_y=true, const bool test_z=true, const bool test_e=true);
338
+void test_tmc_connection(
339
+  LOGICAL_AXIS_LIST(const bool test_e=true, const bool test_x=true, const bool test_y=true, const bool test_z=true)
340
+);
339 341
 
340 342
 #if ENABLED(TMC_DEBUG)
341 343
   #if ENABLED(MONITOR_DRIVER_STATUS)
342 344
     void tmc_set_report_interval(const uint16_t update_interval);
343 345
   #endif
344
-  void tmc_report_all(const bool print_x=true, const bool print_y=true, const bool print_z=true, const bool print_e=true);
345
-  void tmc_get_registers(const bool print_x, const bool print_y, const bool print_z, const bool print_e);
346
+  void tmc_report_all(
347
+    LOGICAL_AXIS_LIST(const bool print_e=true, const bool print_x=true, const bool print_y=true, const bool print_z=true)
348
+  );
349
+  void tmc_get_registers(
350
+    LOGICAL_AXIS_LIST(const bool print_e, const bool print_x, const bool print_y, const bool print_z)
351
+  );
346 352
 #endif
347 353
 
348 354
 /**
@@ -355,7 +361,7 @@ void test_tmc_connection(const bool test_x=true, const bool test_y=true, const b
355 361
 #if USE_SENSORLESS
356 362
 
357 363
   // Track enabled status of stealthChop and only re-enable where applicable
358
-  struct sensorless_t { bool x, y, z, x2, y2, z2, z3, z4; };
364
+  struct sensorless_t { bool LINEAR_AXIS_LIST(x, y, z), x2, y2, z2, z3, z4; };
359 365
 
360 366
   #if ENABLED(IMPROVE_HOMING_RELIABILITY)
361 367
     extern millis_t sg_guard_period;

+ 19
- 8
Marlin/src/gcode/calibrate/G28.cpp View File

@@ -321,12 +321,23 @@ void GcodeSuite::G28() {
321 321
 
322 322
   #else
323 323
 
324
+    #define _UNSAFE(A) (homeZ && TERN0(Z_SAFE_HOMING, axes_should_home(_BV(A##_AXIS))))
325
+
324 326
     const bool homeZ = parser.seen_test('Z'),
325
-               needX = homeZ && TERN0(Z_SAFE_HOMING, axes_should_home(_BV(X_AXIS))),
326
-               needY = homeZ && TERN0(Z_SAFE_HOMING, axes_should_home(_BV(Y_AXIS))),
327
-               homeX = needX || parser.seen_test('X'), homeY = needY || parser.seen_test('Y'),
328
-               home_all = homeX == homeY && homeX == homeZ, // All or None
329
-               doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ;
327
+               LINEAR_AXIS_LIST( // Other axes should be homed before Z safe-homing
328
+                 needX = _UNSAFE(X), needY = _UNSAFE(Y), needZ = false // UNUSED
329
+               ),
330
+               LINEAR_AXIS_LIST( // Home each axis if needed or flagged
331
+                 homeX = needX || parser.seen_test('X'),
332
+                 homeY = needY || parser.seen_test('Y'),
333
+                 homeZZ = homeZ // UNUSED
334
+               ),
335
+               // Home-all if all or none are flagged
336
+               home_all = true LINEAR_AXIS_GANG(&& homeX == homeX, && homeX == homeY, && homeX == homeZ),
337
+               LINEAR_AXIS_LIST(doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ);
338
+
339
+   UNUSED(needZ);
340
+   UNUSED(homeZZ);
330 341
 
331 342
     #if ENABLED(HOME_Z_FIRST)
332 343
 
@@ -336,7 +347,7 @@ void GcodeSuite::G28() {
336 347
 
337 348
     const float z_homing_height = parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT;
338 349
 
339
-    if (z_homing_height && (doX || doY || TERN0(Z_SAFE_HOMING, doZ))) {
350
+    if (z_homing_height && (0 LINEAR_AXIS_GANG(|| doX, || doY, || TERN0(Z_SAFE_HOMING, doZ)))) {
340 351
       // Raise Z before homing any other axes and z is not already high enough (never lower z)
341 352
       if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) by ", z_homing_height);
342 353
       do_z_clearance(z_homing_height);
@@ -469,7 +480,7 @@ void GcodeSuite::G28() {
469 480
     #if HAS_CURRENT_HOME(Y2)
470 481
       stepperY2.rms_current(tmc_save_current_Y2);
471 482
     #endif
472
-  #endif
483
+  #endif // HAS_HOMING_CURRENT
473 484
 
474 485
   ui.refresh();
475 486
 
@@ -490,7 +501,7 @@ void GcodeSuite::G28() {
490 501
     static constexpr AxisEnum L64XX_axis_xref[MAX_L64XX] = {
491 502
       X_AXIS, Y_AXIS, Z_AXIS,
492 503
       X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS,
493
-      E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS
504
+      E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS
494 505
     };
495 506
     for (uint8_t j = 1; j <= L64XX::chain[0]; j++) {
496 507
       const uint8_t cv = L64XX::chain[j];

+ 8
- 4
Marlin/src/gcode/calibrate/G425.cpp View File

@@ -307,9 +307,11 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
307 307
 
308 308
   // The difference between the known and the measured location
309 309
   // of the calibration object is the positional error
310
-  m.pos_error.x = TERN0(HAS_X_CENTER, true_center.x - m.obj_center.x);
311
-  m.pos_error.y = TERN0(HAS_Y_CENTER, true_center.y - m.obj_center.y);
312
-  m.pos_error.z = true_center.z - m.obj_center.z;
310
+  LINEAR_AXIS_CODE(
311
+    m.pos_error.x = TERN0(HAS_X_CENTER, true_center.x - m.obj_center.x),
312
+    m.pos_error.y = TERN0(HAS_Y_CENTER, true_center.y - m.obj_center.y),
313
+    m.pos_error.z = true_center.z - m.obj_center.z
314
+  );
313 315
 }
314 316
 
315 317
 #if ENABLED(CALIBRATION_REPORTING)
@@ -455,7 +457,9 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
455 457
       // New scope for TEMPORARY_BACKLASH_CORRECTION
456 458
       TEMPORARY_BACKLASH_CORRECTION(all_on);
457 459
       TEMPORARY_BACKLASH_SMOOTHING(0.0f);
458
-      const xyz_float_t move = { AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3 };
460
+      const xyz_float_t move = LINEAR_AXIS_ARRAY(
461
+        AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3
462
+      );
459 463
       current_position += move; calibration_move();
460 464
       current_position -= move; calibration_move();
461 465
     }

+ 6
- 4
Marlin/src/gcode/calibrate/M425.cpp View File

@@ -48,10 +48,12 @@ void GcodeSuite::M425() {
48 48
 
49 49
   auto axis_can_calibrate = [](const uint8_t a) {
50 50
     switch (a) {
51
-      default:
52
-      case X_AXIS: return AXIS_CAN_CALIBRATE(X);
53
-      case Y_AXIS: return AXIS_CAN_CALIBRATE(Y);
54
-      case Z_AXIS: return AXIS_CAN_CALIBRATE(Z);
51
+      default: return false;
52
+      LINEAR_AXIS_CODE(
53
+        case X_AXIS: return AXIS_CAN_CALIBRATE(X),
54
+        case Y_AXIS: return AXIS_CAN_CALIBRATE(Y),
55
+        case Z_AXIS: return AXIS_CAN_CALIBRATE(Z)
56
+      );
55 57
     }
56 58
   };
57 59
 

+ 13
- 14
Marlin/src/gcode/config/M200-M205.cpp View File

@@ -88,7 +88,7 @@ void GcodeSuite::M201() {
88 88
 
89 89
   LOOP_LOGICAL_AXES(i) {
90 90
     if (parser.seenval(axis_codes[i])) {
91
-      const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i);
91
+      const uint8_t a = TERN(HAS_EXTRUDERS, (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i), i);
92 92
       planner.set_max_acceleration(a, parser.value_axis_units((AxisEnum)a));
93 93
     }
94 94
   }
@@ -106,7 +106,7 @@ void GcodeSuite::M203() {
106 106
 
107 107
   LOOP_LOGICAL_AXES(i)
108 108
     if (parser.seenval(axis_codes[i])) {
109
-      const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i);
109
+      const uint8_t a = TERN(HAS_EXTRUDERS, (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i), i);
110 110
       planner.set_max_feedrate(a, parser.value_axis_units((AxisEnum)a));
111 111
     }
112 112
 }
@@ -165,17 +165,16 @@ void GcodeSuite::M205() {
165 165
     }
166 166
   #endif
167 167
   #if HAS_CLASSIC_JERK
168
-    if (parser.seenval('X')) planner.set_max_jerk(X_AXIS, parser.value_linear_units());
169
-    if (parser.seenval('Y')) planner.set_max_jerk(Y_AXIS, parser.value_linear_units());
170
-    if (parser.seenval('Z')) {
171
-      planner.set_max_jerk(Z_AXIS, parser.value_linear_units());
172
-      #if HAS_MESH && DISABLED(LIMITED_JERK_EDITING)
173
-        if (planner.max_jerk.z <= 0.1f)
174
-          SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses.");
175
-      #endif
176
-    }
177
-    #if HAS_CLASSIC_E_JERK
178
-      if (parser.seenval('E')) planner.set_max_jerk(E_AXIS, parser.value_linear_units());
168
+    bool seenZ = false;
169
+    LOGICAL_AXIS_CODE(
170
+      if (parser.seenval('E')) planner.set_max_jerk(E_AXIS, parser.value_linear_units()),
171
+      if (parser.seenval('X')) planner.set_max_jerk(X_AXIS, parser.value_linear_units()),
172
+      if (parser.seenval('Y')) planner.set_max_jerk(Y_AXIS, parser.value_linear_units()),
173
+      if ((seenZ = parser.seenval('Z'))) planner.set_max_jerk(Z_AXIS, parser.value_linear_units())
174
+    );
175
+    #if HAS_MESH && DISABLED(LIMITED_JERK_EDITING)
176
+      if (seenZ && planner.max_jerk.z <= 0.1f)
177
+        SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses.");
179 178
     #endif
180
-  #endif
179
+  #endif // HAS_CLASSIC_JERK
181 180
 }

+ 24
- 19
Marlin/src/gcode/config/M92.cpp View File

@@ -25,10 +25,12 @@
25 25
 
26 26
 void report_M92(const bool echo=true, const int8_t e=-1) {
27 27
   if (echo) SERIAL_ECHO_START(); else SERIAL_CHAR(' ');
28
-  SERIAL_ECHOPAIR_P(PSTR(" M92 X"), LINEAR_UNIT(planner.settings.axis_steps_per_mm[X_AXIS]),
29
-                          SP_Y_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Y_AXIS]),
30
-                          SP_Z_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Z_AXIS]));
31
-  #if DISABLED(DISTINCT_E_FACTORS)
28
+  SERIAL_ECHOPAIR_P(LIST_N(DOUBLE(LINEAR_AXES),
29
+    PSTR(" M92 X"), LINEAR_UNIT(planner.settings.axis_steps_per_mm[X_AXIS]),
30
+    SP_Y_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Y_AXIS]),
31
+    SP_Z_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Z_AXIS])
32
+  ));
33
+  #if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)
32 34
     SERIAL_ECHOPAIR_P(SP_E_STR, VOLUMETRIC_UNIT(planner.settings.axis_steps_per_mm[E_AXIS]));
33 35
   #endif
34 36
   SERIAL_EOL();
@@ -64,25 +66,28 @@ void GcodeSuite::M92() {
64 66
   if (target_extruder < 0) return;
65 67
 
66 68
   // No arguments? Show M92 report.
67
-  if (!parser.seen("XYZE" TERN_(MAGIC_NUMBERS_GCODE, "HL")))
68
-    return report_M92(true, target_extruder);
69
+  if (!parser.seen(
70
+    LOGICAL_AXIS_GANG("E", "X", "Y", "Z")
71
+    TERN_(MAGIC_NUMBERS_GCODE, "HL")
72
+  )) return report_M92(true, target_extruder);
69 73
 
70 74
   LOOP_LOGICAL_AXES(i) {
71 75
     if (parser.seenval(axis_codes[i])) {
72
-      if (i == E_AXIS) {
73
-        const float value = parser.value_per_axis_units((AxisEnum)(E_AXIS_N(target_extruder)));
74
-        if (value < 20) {
75
-          float factor = planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] / value; // increase e constants if M92 E14 is given for netfab.
76
-          #if HAS_CLASSIC_JERK && HAS_CLASSIC_E_JERK
77
-            planner.max_jerk.e *= factor;
78
-          #endif
79
-          planner.settings.max_feedrate_mm_s[E_AXIS_N(target_extruder)] *= factor;
80
-          planner.max_acceleration_steps_per_s2[E_AXIS_N(target_extruder)] *= factor;
81
-        }
82
-        planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] = value;
83
-      }
84
-      else {
76
+      if (TERN1(HAS_EXTRUDERS, i != E_AXIS))
85 77
         planner.settings.axis_steps_per_mm[i] = parser.value_per_axis_units((AxisEnum)i);
78
+      else {
79
+        #if HAS_EXTRUDERS
80
+          const float value = parser.value_per_axis_units((AxisEnum)(E_AXIS_N(target_extruder)));
81
+          if (value < 20) {
82
+            float factor = planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] / value; // increase e constants if M92 E14 is given for netfab.
83
+            #if HAS_CLASSIC_JERK && HAS_CLASSIC_E_JERK
84
+              planner.max_jerk.e *= factor;
85
+            #endif
86
+            planner.settings.max_feedrate_mm_s[E_AXIS_N(target_extruder)] *= factor;
87
+            planner.max_acceleration_steps_per_s2[E_AXIS_N(target_extruder)] *= factor;
88
+          }
89
+          planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] = value;
90
+        #endif
86 91
       }
87 92
     }
88 93
   }

+ 14
- 10
Marlin/src/gcode/control/M17_M18_M84.cpp View File

@@ -33,11 +33,13 @@
33 33
  * M17: Enable stepper motors
34 34
  */
35 35
 void GcodeSuite::M17() {
36
-  if (parser.seen("XYZE")) {
37
-    if (parser.seen_test('X')) ENABLE_AXIS_X();
38
-    if (parser.seen_test('Y')) ENABLE_AXIS_Y();
39
-    if (parser.seen_test('Z')) ENABLE_AXIS_Z();
40
-    if (TERN0(HAS_E_STEPPER_ENABLE, parser.seen_test('E'))) enable_e_steppers();
36
+  if (parser.seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z"))) {
37
+    LOGICAL_AXIS_CODE(
38
+      if (TERN0(HAS_E_STEPPER_ENABLE, parser.seen_test('E'))) enable_e_steppers(),
39
+      if (parser.seen_test('X'))        ENABLE_AXIS_X(),
40
+      if (parser.seen_test('Y'))        ENABLE_AXIS_Y(),
41
+      if (parser.seen_test('Z'))        ENABLE_AXIS_Z()
42
+    );
41 43
   }
42 44
   else {
43 45
     LCD_MESSAGEPGM(MSG_NO_MOVE);
@@ -54,12 +56,14 @@ void GcodeSuite::M18_M84() {
54 56
     stepper_inactive_time = parser.value_millis_from_seconds();
55 57
   }
56 58
   else {
57
-    if (parser.seen("XYZE")) {
59
+    if (parser.seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z"))) {
58 60
       planner.synchronize();
59
-      if (parser.seen_test('X')) DISABLE_AXIS_X();
60
-      if (parser.seen_test('Y')) DISABLE_AXIS_Y();
61
-      if (parser.seen_test('Z')) DISABLE_AXIS_Z();
62
-      if (TERN0(HAS_E_STEPPER_ENABLE, parser.seen_test('E'))) disable_e_steppers();
61
+      LOGICAL_AXIS_CODE(
62
+        if (TERN0(HAS_E_STEPPER_ENABLE, parser.seen_test('E'))) disable_e_steppers(),
63
+        if (parser.seen_test('X'))        DISABLE_AXIS_X(),
64
+        if (parser.seen_test('Y'))        DISABLE_AXIS_Y(),
65
+        if (parser.seen_test('Z'))        DISABLE_AXIS_Z()
66
+      );
63 67
     }
64 68
     else
65 69
       planner.finish_and_disable();

+ 7
- 5
Marlin/src/gcode/feature/pause/G61.cpp View File

@@ -69,7 +69,7 @@ void GcodeSuite::G61(void) {
69 69
     SYNC_E(stored_position[slot].e);
70 70
   }
71 71
   else {
72
-    if (parser.seen("XYZ")) {
72
+    if (parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z"))) {
73 73
       DEBUG_ECHOPAIR(STR_RESTORING_POS " S", slot);
74 74
       LOOP_LINEAR_AXES(i) {
75 75
         destination[i] = parser.seen(AXIS_CHAR(i))
@@ -82,10 +82,12 @@ void GcodeSuite::G61(void) {
82 82
       // Move to the saved position
83 83
       prepare_line_to_destination();
84 84
     }
85
-    if (parser.seen_test('E')) {
86
-      DEBUG_ECHOLNPAIR(STR_RESTORING_POS " S", slot, " E", current_position.e, "=>", stored_position[slot].e);
87
-      SYNC_E(stored_position[slot].e);
88
-    }
85
+    #if HAS_EXTRUDERS
86
+      if (parser.seen_test('E')) {
87
+        DEBUG_ECHOLNPAIR(STR_RESTORING_POS " S", slot, " E", current_position.e, "=>", stored_position[slot].e);
88
+        SYNC_E(stored_position[slot].e);
89
+      }
90
+    #endif
89 91
   }
90 92
 
91 93
   feedrate_mm_s = saved_feedrate;

+ 13
- 5
Marlin/src/gcode/feature/trinamic/M122.cpp View File

@@ -49,13 +49,21 @@ void GcodeSuite::M122() {
49 49
       tmc_set_report_interval(interval);
50 50
     #endif
51 51
 
52
-    if (parser.seen_test('V'))
53
-      tmc_get_registers(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
54
-    else
55
-      tmc_report_all(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
52
+    if (parser.seen_test('V')) {
53
+      tmc_get_registers(
54
+        LOGICAL_AXIS_LIST(print_axis.e, print_axis.x, print_axis.y, print_axis.z)
55
+      );
56
+    }
57
+    else {
58
+      tmc_report_all(
59
+        LOGICAL_AXIS_LIST(print_axis.e, print_axis.x, print_axis.y, print_axis.z)
60
+      );
61
+    }
56 62
   #endif
57 63
 
58
-  test_tmc_connection(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
64
+  test_tmc_connection(
65
+    LOGICAL_AXIS_LIST(print_axis.e, print_axis.x, print_axis.y, print_axis.z)
66
+  );
59 67
 }
60 68
 
61 69
 #endif // HAS_TRINAMIC_CONFIG

+ 14
- 12
Marlin/src/gcode/gcode.cpp View File

@@ -74,11 +74,11 @@ millis_t GcodeSuite::previous_move_ms = 0,
74 74
 
75 75
 // Relative motion mode for each logical axis
76 76
 static constexpr xyze_bool_t ar_init = AXIS_RELATIVE_MODES;
77
-uint8_t GcodeSuite::axis_relative = (
78
-    (ar_init.x ? _BV(REL_X) : 0)
79
-  | (ar_init.y ? _BV(REL_Y) : 0)
80
-  | (ar_init.z ? _BV(REL_Z) : 0)
81
-  | (ar_init.e ? _BV(REL_E) : 0)
77
+uint8_t GcodeSuite::axis_relative = 0 LOGICAL_AXIS_GANG(
78
+  | (ar_init.e << REL_E),
79
+  | (ar_init.x << REL_X),
80
+  | (ar_init.y << REL_Y),
81
+  | (ar_init.z << REL_Z)
82 82
 );
83 83
 
84 84
 #if EITHER(HAS_AUTO_REPORTING, HOST_KEEPALIVE_FEATURE)
@@ -161,13 +161,15 @@ void GcodeSuite::get_destination_from_command() {
161 161
       destination[i] = current_position[i];
162 162
   }
163 163
 
164
-  // Get new E position, whether absolute or relative
165
-  if ( (seen.e = parser.seenval('E')) ) {
166
-    const float v = parser.value_axis_units(E_AXIS);
167
-    destination.e = axis_is_relative(E_AXIS) ? current_position.e + v : v;
168
-  }
169
-  else
170
-    destination.e = current_position.e;
164
+  #if HAS_EXTRUDERS
165
+    // Get new E position, whether absolute or relative
166
+    if ( (seen.e = parser.seenval('E')) ) {
167
+      const float v = parser.value_axis_units(E_AXIS);
168
+      destination.e = axis_is_relative(E_AXIS) ? current_position.e + v : v;
169
+    }
170
+    else
171
+      destination.e = current_position.e;
172
+  #endif
171 173
 
172 174
   #if ENABLED(POWER_LOSS_RECOVERY) && !PIN_EXISTS(POWER_LOSS)
173 175
     // Only update power loss recovery on moves with E

+ 23
- 14
Marlin/src/gcode/gcode.h View File

@@ -314,7 +314,12 @@
314 314
   #define HAS_FAST_MOVES 1
315 315
 #endif
316 316
 
317
-enum AxisRelative : uint8_t { REL_X, REL_Y, REL_Z, REL_E, E_MODE_ABS, E_MODE_REL };
317
+enum AxisRelative : uint8_t {
318
+  LOGICAL_AXIS_LIST(REL_E, REL_X, REL_Y, REL_Z)
319
+  #if HAS_EXTRUDERS
320
+    , E_MODE_ABS, E_MODE_REL
321
+  #endif
322
+};
318 323
 
319 324
 extern const char G28_STR[];
320 325
 
@@ -324,23 +329,27 @@ public:
324 329
   static uint8_t axis_relative;
325 330
 
326 331
   static inline bool axis_is_relative(const AxisEnum a) {
327
-    if (a == E_AXIS) {
328
-      if (TEST(axis_relative, E_MODE_REL)) return true;
329
-      if (TEST(axis_relative, E_MODE_ABS)) return false;
330
-    }
332
+    #if HAS_EXTRUDERS
333
+      if (a == E_AXIS) {
334
+        if (TEST(axis_relative, E_MODE_REL)) return true;
335
+        if (TEST(axis_relative, E_MODE_ABS)) return false;
336
+      }
337
+    #endif
331 338
     return TEST(axis_relative, a);
332 339
   }
333 340
   static inline void set_relative_mode(const bool rel) {
334
-    axis_relative = rel ? _BV(REL_X) | _BV(REL_Y) | _BV(REL_Z) | _BV(REL_E) : 0;
335
-  }
336
-  static inline void set_e_relative() {
337
-    CBI(axis_relative, E_MODE_ABS);
338
-    SBI(axis_relative, E_MODE_REL);
339
-  }
340
-  static inline void set_e_absolute() {
341
-    CBI(axis_relative, E_MODE_REL);
342
-    SBI(axis_relative, E_MODE_ABS);
341
+    axis_relative = rel ? (0 LOGICAL_AXIS_GANG(| _BV(REL_E), | _BV(REL_X), | _BV(REL_Y), | _BV(REL_Z))) : 0;
343 342
   }
343
+  #if HAS_EXTRUDERS
344
+    static inline void set_e_relative() {
345
+      CBI(axis_relative, E_MODE_ABS);
346
+      SBI(axis_relative, E_MODE_REL);
347
+    }
348
+    static inline void set_e_absolute() {
349
+      CBI(axis_relative, E_MODE_REL);
350
+      SBI(axis_relative, E_MODE_ABS);
351
+    }
352
+  #endif
344 353
 
345 354
   #if ENABLED(CNC_WORKSPACE_PLANES)
346 355
     /**

+ 26
- 11
Marlin/src/gcode/geometry/G92.cpp View File

@@ -48,7 +48,10 @@
48 48
  */
49 49
 void GcodeSuite::G92() {
50 50
 
51
-  bool sync_E = false, sync_XYZE = false;
51
+  #if HAS_EXTRUDERS
52
+    bool sync_E = false;
53
+  #endif
54
+  bool sync_XYZE = false;
52 55
 
53 56
   #if USE_GCODE_SUBCODES
54 57
     const uint8_t subcode_G92 = parser.subcode;
@@ -72,7 +75,11 @@ void GcodeSuite::G92() {
72 75
       case 9:                                                         // G92.9 - Set Current Position directly (like Marlin 1.0)
73 76
         LOOP_LOGICAL_AXES(i) {
74 77
           if (parser.seenval(axis_codes[i])) {
75
-            if (i == E_AXIS) sync_E = true; else sync_XYZE = true;
78
+            if (TERN1(HAS_EXTRUDERS, i != E_AXIS))
79
+              sync_XYZE = true;
80
+            else {
81
+              TERN_(HAS_EXTRUDERS, sync_E = true);
82
+            }
76 83
             current_position[i] = parser.value_axis_units((AxisEnum)i);
77 84
           }
78 85
         }
@@ -83,20 +90,26 @@ void GcodeSuite::G92() {
83 90
       LOOP_LOGICAL_AXES(i) {
84 91
         if (parser.seenval(axis_codes[i])) {
85 92
           const float l = parser.value_axis_units((AxisEnum)i),       // Given axis coordinate value, converted to millimeters
86
-                      v = i == E_AXIS ? l : LOGICAL_TO_NATIVE(l, i),  // Axis position in NATIVE space (applying the existing offset)
93
+                      v = TERN0(HAS_EXTRUDERS, i == E_AXIS) ? l : LOGICAL_TO_NATIVE(l, i),  // Axis position in NATIVE space (applying the existing offset)
87 94
                       d = v - current_position[i];                    // How much is the current axis position altered by?
88 95
           if (!NEAR_ZERO(d)) {
89 96
             #if HAS_POSITION_SHIFT && !IS_SCARA                       // When using workspaces...
90
-              if (i == E_AXIS) {
91
-                sync_E = true;
92
-                current_position.e = v;                               // ...E is still set directly
97
+              if (TERN1(HAS_EXTRUDERS, i != E_AXIS)) {
98
+                position_shift[i] += d;                               // ...most axes offset the workspace...
99
+                update_workspace_offset((AxisEnum)i);
93 100
               }
94 101
               else {
95
-                position_shift[i] += d;                               // ...but other axes offset the workspace.
96
-                update_workspace_offset((AxisEnum)i);
102
+                #if HAS_EXTRUDERS
103
+                  sync_E = true;
104
+                  current_position.e = v;                             // ...but E is set directly
105
+                #endif
97 106
               }
98 107
             #else                                                     // Without workspaces...
99
-              if (i == E_AXIS) sync_E = true; else sync_XYZE = true;
108
+              if (TERN1(HAS_EXTRUDERS, i != E_AXIS))
109
+                sync_XYZE = true;
110
+              else {
111
+                TERN_(HAS_EXTRUDERS, sync_E = true);
112
+              }
100 113
               current_position[i] = v;                                // ...set Current Position directly (like Marlin 1.0)
101 114
             #endif
102 115
           }
@@ -111,8 +124,10 @@ void GcodeSuite::G92() {
111 124
       coordinate_system[active_coordinate_system] = position_shift;
112 125
   #endif
113 126
 
114
-  if   (sync_XYZE) sync_plan_position();
115
-  else if (sync_E) sync_plan_position_e();
127
+  if (sync_XYZE) sync_plan_position();
128
+  #if HAS_EXTRUDERS
129
+    else if (sync_E) sync_plan_position_e();
130
+  #endif
116 131
 
117 132
   IF_DISABLED(DIRECT_STEPPING, report_current_position());
118 133
 }

+ 1
- 1
Marlin/src/gcode/host/M114.cpp View File

@@ -170,7 +170,7 @@
170 170
 
171 171
     SERIAL_ECHOPGM("FromStp:");
172 172
     get_cartesian_from_steppers();  // writes 'cartes' (with forward kinematics)
173
-    xyze_pos_t from_steppers = { cartes.x, cartes.y, cartes.z, planner.get_axis_position_mm(E_AXIS) };
173
+    xyze_pos_t from_steppers = LOGICAL_AXIS_ARRAY(planner.get_axis_position_mm(E_AXIS), cartes.x, cartes.y, cartes.z);
174 174
     report_all_axis_pos(from_steppers);
175 175
 
176 176
     const xyze_float_t diff = from_steppers - leveled;

+ 6
- 4
Marlin/src/gcode/motion/G0_G1.cpp View File

@@ -49,9 +49,11 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) {
49 49
   if (IsRunning()
50 50
     #if ENABLED(NO_MOTION_BEFORE_HOMING)
51 51
       && !homing_needed_error(
52
-          (parser.seen_test('X') ? _BV(X_AXIS) : 0)
53
-        | (parser.seen_test('Y') ? _BV(Y_AXIS) : 0)
54
-        | (parser.seen_test('Z') ? _BV(Z_AXIS) : 0) )
52
+        LINEAR_AXIS_GANG(
53
+            (parser.seen_test('X') ? _BV(X_AXIS) : 0),
54
+          | (parser.seen_test('Y') ? _BV(Y_AXIS) : 0),
55
+          | (parser.seen_test('Z') ? _BV(Z_AXIS) : 0))
56
+      )
55 57
     #endif
56 58
   ) {
57 59
     TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_RUNNING));
@@ -83,7 +85,7 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) {
83 85
 
84 86
       if (MIN_AUTORETRACT <= MAX_AUTORETRACT) {
85 87
         // When M209 Autoretract is enabled, convert E-only moves to firmware retract/recover moves
86
-        if (fwretract.autoretract_enabled && parser.seen('E') && !parser.seen("XYZ")) {
88
+        if (fwretract.autoretract_enabled && parser.seen_test('E') && !parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z"))) {
87 89
           const float echange = destination.e - current_position.e;
88 90
           // Is this a retract or recover move?
89 91
           if (WITHIN(ABS(echange), MIN_AUTORETRACT, MAX_AUTORETRACT) && fwretract.retracted[active_extruder] == (echange > 0.0)) {

+ 22
- 9
Marlin/src/gcode/motion/G2_G3.cpp View File

@@ -109,23 +109,32 @@ void plan_arc(
109 109
     #endif
110 110
   }
111 111
 
112
-  float linear_travel = cart[l_axis] - start_L,
113
-        extruder_travel = cart.e - current_position.e;
112
+  float linear_travel = cart[l_axis] - start_L;
113
+
114
+  #if HAS_EXTRUDERS
115
+    float extruder_travel = cart.e - current_position.e;
116
+  #endif
114 117
 
115 118
   // If circling around...
116 119
   if (ENABLED(ARC_P_CIRCLES) && circles) {
117 120
     const float total_angular = angular_travel + circles * RADIANS(360),  // Total rotation with all circles and remainder
118 121
               part_per_circle = RADIANS(360) / total_angular,             // Each circle's part of the total
119
-                 l_per_circle = linear_travel * part_per_circle,          // L movement per circle
120
-                 e_per_circle = extruder_travel * part_per_circle;        // E movement per circle
122
+                 l_per_circle = linear_travel * part_per_circle;          // L movement per circle
123
+
124
+    #if HAS_EXTRUDERS
125
+      const float e_per_circle = extruder_travel * part_per_circle;       // E movement per circle
126
+    #endif
127
+
121 128
     xyze_pos_t temp_position = current_position;                          // for plan_arc to compare to current_position
122 129
     for (uint16_t n = circles; n--;) {
123
-      temp_position.e += e_per_circle;                                    // Destination E axis
130
+      TERN_(HAS_EXTRUDERS, temp_position.e += e_per_circle);              // Destination E axis
124 131
       temp_position[l_axis] += l_per_circle;                              // Destination L axis
125 132
       plan_arc(temp_position, offset, clockwise, 0);                      // Plan a single whole circle
126 133
     }
127 134
     linear_travel = cart[l_axis] - current_position[l_axis];
128
-    extruder_travel = cart.e - current_position.e;
135
+    #if HAS_EXTRUDERS
136
+      extruder_travel = cart.e - current_position.e;
137
+    #endif
129 138
   }
130 139
 
131 140
   const float flat_mm = radius * angular_travel,
@@ -179,16 +188,19 @@ void plan_arc(
179 188
   xyze_pos_t raw;
180 189
   const float theta_per_segment = angular_travel / segments,
181 190
               linear_per_segment = linear_travel / segments,
182
-              extruder_per_segment = extruder_travel / segments,
183 191
               sq_theta_per_segment = sq(theta_per_segment),
184 192
               sin_T = theta_per_segment - sq_theta_per_segment * theta_per_segment / 6,
185 193
               cos_T = 1 - 0.5f * sq_theta_per_segment; // Small angle approximation
186 194
 
195
+  #if HAS_EXTRUDERS
196
+    const float extruder_per_segment = extruder_travel / segments;
197
+  #endif
198
+
187 199
   // Initialize the linear axis
188 200
   raw[l_axis] = current_position[l_axis];
189 201
 
190 202
   // Initialize the extruder axis
191
-  raw.e = current_position.e;
203
+  TERN_(HAS_EXTRUDERS, raw.e = current_position.e);
192 204
 
193 205
   #if ENABLED(SCARA_FEEDRATE_SCALING)
194 206
     const float inv_duration = scaled_fr_mm_s / seg_length;
@@ -240,7 +252,8 @@ void plan_arc(
240 252
     #else
241 253
       raw[l_axis] += linear_per_segment;
242 254
     #endif
243
-    raw.e += extruder_per_segment;
255
+
256
+    TERN_(HAS_EXTRUDERS, raw.e += extruder_per_segment);
244 257
 
245 258
     apply_motion_limits(raw);
246 259
 

+ 1
- 1
Marlin/src/gcode/motion/M290.cpp View File

@@ -87,7 +87,7 @@ void GcodeSuite::M290() {
87 87
     }
88 88
   #endif
89 89
 
90
-  if (!parser.seen("XYZ") || parser.seen('R')) {
90
+  if (!parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z")) || parser.seen('R')) {
91 91
     SERIAL_ECHO_START();
92 92
 
93 93
     #if ENABLED(BABYSTEP_ZPROBE_OFFSET)

+ 2
- 1
Marlin/src/gcode/parser.cpp View File

@@ -248,7 +248,8 @@ void GCodeParser::parse(char *p) {
248 248
         case 'R': if (!WITHIN(motion_mode_codenum, 2, 3)) return;
249 249
       #endif
250 250
 
251
-      case 'X' ... 'Z': case 'E' ... 'F':
251
+      LOGICAL_AXIS_GANG(case 'E':, case 'X':, case 'Y':, case 'Z':)
252
+      case 'F':
252 253
         if (motion_mode_codenum < 0) return;
253 254
         command_letter = 'G';
254 255
         codenum = motion_mode_codenum;

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

@@ -226,7 +226,7 @@ public:
226 226
 
227 227
   // Seen any axis parameter
228 228
   static inline bool seen_axis() {
229
-    return seen("XYZE");
229
+    return seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z"));
230 230
   }
231 231
 
232 232
   #if ENABLED(GCODE_QUOTED_STRINGS)

+ 56
- 38
Marlin/src/inc/Conditionals_LCD.h View File

@@ -537,12 +537,12 @@
537 537
  *  E_STEPPERS   - Number of actual E stepper motors
538 538
  *  E_MANUAL     - Number of E steppers for LCD move options
539 539
  */
540
-
541 540
 #if EXTRUDERS
542 541
   #define HAS_EXTRUDERS 1
543 542
   #if EXTRUDERS > 1
544 543
     #define HAS_MULTI_EXTRUDER 1
545 544
   #endif
545
+  #define E_AXIS_N(E) AxisEnum(E_AXIS + E_INDEX_N(E))
546 546
 #else
547 547
   #undef EXTRUDERS
548 548
   #define EXTRUDERS 0
@@ -551,6 +551,7 @@
551 551
   #undef SWITCHING_NOZZLE
552 552
   #undef MIXING_EXTRUDER
553 553
   #undef HOTEND_IDLE_TIMEOUT
554
+  #undef DISABLE_E
554 555
 #endif
555 556
 
556 557
 #if ENABLED(SWITCHING_EXTRUDER)   // One stepper for every two EXTRUDERS
@@ -604,6 +605,50 @@
604 605
   #define E_MANUAL EXTRUDERS
605 606
 #endif
606 607
 
608
+/**
609
+ * Number of Linear Axes (e.g., XYZ)
610
+ * All the logical axes except for the tool (E) axis
611
+ */
612
+#ifndef LINEAR_AXES
613
+  #define LINEAR_AXES XYZ
614
+#endif
615
+
616
+/**
617
+ * Number of Logical Axes (e.g., XYZE)
618
+ * All the logical axes that can be commanded directly by G-code.
619
+ * Delta maps stepper-specific values to ABC steppers.
620
+ */
621
+#if HAS_EXTRUDERS
622
+  #define LOGICAL_AXES INCREMENT(LINEAR_AXES)
623
+#else
624
+  #define LOGICAL_AXES LINEAR_AXES
625
+#endif
626
+
627
+/**
628
+ * DISTINCT_E_FACTORS is set to give extruders (some) individual settings.
629
+ *
630
+ * DISTINCT_AXES is the number of distinct addressable axes (not steppers).
631
+ *  Includes all linear axes plus all distinguished extruders.
632
+ *  The default behavior is to treat all extruders as a single E axis
633
+ *  with shared motion and temperature settings.
634
+ *
635
+ * DISTINCT_E is the number of distinguished extruders. By default this
636
+ *  well be 1 which indicates all extruders share the same settings.
637
+ *
638
+ * E_INDEX_N(E) should be used to get the E index of any item that might be
639
+ *  distinguished.
640
+ */
641
+#if ENABLED(DISTINCT_E_FACTORS) && E_STEPPERS > 1
642
+  #define DISTINCT_AXES (LINEAR_AXES + E_STEPPERS)
643
+  #define DISTINCT_E E_STEPPERS
644
+  #define E_INDEX_N(E) (E)
645
+#else
646
+  #undef DISTINCT_E_FACTORS
647
+  #define DISTINCT_AXES LOGICAL_AXES
648
+  #define DISTINCT_E 1
649
+  #define E_INDEX_N(E) 0
650
+#endif
651
+
607 652
 #if HOTENDS
608 653
   #define HAS_HOTEND 1
609 654
   #ifndef HOTEND_OVERSHOOT
@@ -624,10 +669,6 @@
624 669
 #define ARRAY_BY_HOTENDS(V...) ARRAY_N(HOTENDS, V)
625 670
 #define ARRAY_BY_HOTENDS1(v1) ARRAY_N_1(HOTENDS, v1)
626 671
 
627
-#if ENABLED(SWITCHING_EXTRUDER) && (DISABLED(SWITCHING_NOZZLE) || SWITCHING_EXTRUDER_SERVO_NR != SWITCHING_NOZZLE_SERVO_NR)
628
-  #define DO_SWITCH_EXTRUDER 1
629
-#endif
630
-
631 672
 /**
632 673
  * Default hotend offsets, if not defined
633 674
  */
@@ -653,39 +694,10 @@
653 694
   #undef SINGLENOZZLE_STANDBY_FAN
654 695
 #endif
655 696
 
656
-/**
657
- * Number of Linear Axes (e.g., XYZ)
658
- * All the logical axes except for the tool (E) axis
659
- */
660
-#ifndef LINEAR_AXES
661
-  #define LINEAR_AXES XYZ
662
-#endif
663
-
664
-/**
665
- * Number of Logical Axes (e.g., XYZE)
666
- * All the logical axes that can be commanded directly by G-code.
667
- * Delta maps stepper-specific values to ABC steppers.
668
- */
669
-#if HAS_EXTRUDERS
670
-  #define LOGICAL_AXES INCREMENT(LINEAR_AXES)
671
-#else
672
-  #define LOGICAL_AXES LINEAR_AXES
673
-#endif
674
-
675
-/**
676
- * DISTINCT_E_FACTORS affects whether Extruders use different settings
677
- */
678
-#if ENABLED(DISTINCT_E_FACTORS) && E_STEPPERS > 1
679
-  #define DISTINCT_E E_STEPPERS
680
-  #define DISTINCT_AXES (LINEAR_AXES + E_STEPPERS)
681
-  #define E_INDEX_N(E) (E)
682
-#else
683
-  #undef DISTINCT_E_FACTORS
684
-  #define DISTINCT_E 1
685
-  #define DISTINCT_AXES LOGICAL_AXES
686
-  #define E_INDEX_N(E) 0
697
+// Switching extruder has its own servo?
698
+#if ENABLED(SWITCHING_EXTRUDER) && (DISABLED(SWITCHING_NOZZLE) || SWITCHING_EXTRUDER_SERVO_NR != SWITCHING_NOZZLE_SERVO_NR)
699
+  #define DO_SWITCH_EXTRUDER 1
687 700
 #endif
688
-#define E_AXIS_N(E) AxisEnum(E_AXIS + E_INDEX_N(E))
689 701
 
690 702
 /**
691 703
  * The BLTouch Probe emulates a servo probe
@@ -726,6 +738,9 @@
726 738
   #define HAS_BED_PROBE 1
727 739
 #endif
728 740
 
741
+/**
742
+ * Fill in undefined Filament Sensor options
743
+ */
729 744
 #if ENABLED(FILAMENT_RUNOUT_SENSOR)
730 745
   #if NUM_RUNOUT_SENSORS >= 1
731 746
     #ifndef FIL_RUNOUT1_STATE
@@ -834,6 +849,9 @@
834 849
   #define Z_HOME_TO_MIN 1
835 850
 #endif
836 851
 
852
+/**
853
+ * Conditionals based on the type of Bed Probe
854
+ */
837 855
 #if HAS_BED_PROBE
838 856
   #if DISABLED(NOZZLE_AS_PROBE)
839 857
     #define HAS_PROBE_XY_OFFSET 1
@@ -868,7 +886,7 @@
868 886
 #endif
869 887
 
870 888
 /**
871
- * Set granular options based on the specific type of leveling
889
+ * Conditionals based on the type of Bed Leveling
872 890
  */
873 891
 #if ENABLED(AUTO_BED_LEVELING_UBL)
874 892
   #undef LCD_BED_LEVELING

+ 20
- 0
Marlin/src/inc/Conditionals_adv.h View File

@@ -103,6 +103,9 @@
103 103
   #undef THERMAL_PROTECTION_PERIOD
104 104
   #undef WATCH_TEMP_PERIOD
105 105
   #undef SHOW_TEMP_ADC_VALUES
106
+  #undef LCD_SHOW_E_TOTAL
107
+  #undef MANUAL_E_MOVES_RELATIVE
108
+  #undef STEALTHCHOP_E
106 109
 #endif
107 110
 
108 111
 #if TEMP_SENSOR_BED == 0
@@ -482,6 +485,23 @@
482 485
   #endif
483 486
 #endif
484 487
 
488
+// Remove unused STEALTHCHOP flags
489
+#if LINEAR_AXES < 6
490
+  #undef STEALTHCHOP_K
491
+  #if LINEAR_AXES < 5
492
+    #undef STEALTHCHOP_J
493
+    #if LINEAR_AXES < 4
494
+      #undef STEALTHCHOP_I
495
+      #if LINEAR_AXES < 3
496
+        #undef STEALTHCHOP_Z
497
+        #if LINEAR_AXES < 2
498
+          #undef STEALTHCHOP_Y
499
+        #endif
500
+      #endif
501
+    #endif
502
+  #endif
503
+#endif
504
+
485 505
 //
486 506
 // SD Card connection methods
487 507
 // Defined here so pins and sanity checks can use them

+ 128
- 121
Marlin/src/inc/Conditionals_post.h View File

@@ -1563,133 +1563,137 @@
1563 1563
 #endif
1564 1564
 
1565 1565
 // Extruder steppers and solenoids
1566
-#if PIN_EXISTS(E0_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E0))
1567
-  #define HAS_E0_ENABLE 1
1568
-#endif
1569
-#if PIN_EXISTS(E0_DIR)
1570
-  #define HAS_E0_DIR 1
1571
-#endif
1572
-#if PIN_EXISTS(E0_STEP)
1573
-  #define HAS_E0_STEP 1
1574
-#endif
1575
-#if PIN_EXISTS(E0_MS1)
1576
-  #define HAS_E0_MS_PINS 1
1577
-#endif
1578
-#if PIN_EXISTS(SOL0)
1579
-  #define HAS_SOLENOID_0 1
1580
-#endif
1566
+#if HAS_EXTRUDERS
1581 1567
 
1582
-#if PIN_EXISTS(E1_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E1))
1583
-  #define HAS_E1_ENABLE 1
1584
-#endif
1585
-#if PIN_EXISTS(E1_DIR)
1586
-  #define HAS_E1_DIR 1
1587
-#endif
1588
-#if PIN_EXISTS(E1_STEP)
1589
-  #define HAS_E1_STEP 1
1590
-#endif
1591
-#if PIN_EXISTS(E1_MS1)
1592
-  #define HAS_E1_MS_PINS 1
1593
-#endif
1594
-#if PIN_EXISTS(SOL1)
1595
-  #define HAS_SOLENOID_1 1
1596
-#endif
1568
+  #if PIN_EXISTS(E0_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E0))
1569
+    #define HAS_E0_ENABLE 1
1570
+  #endif
1571
+  #if PIN_EXISTS(E0_DIR)
1572
+    #define HAS_E0_DIR 1
1573
+  #endif
1574
+  #if PIN_EXISTS(E0_STEP)
1575
+    #define HAS_E0_STEP 1
1576
+  #endif
1577
+  #if PIN_EXISTS(E0_MS1)
1578
+    #define HAS_E0_MS_PINS 1
1579
+  #endif
1580
+  #if PIN_EXISTS(SOL0)
1581
+    #define HAS_SOLENOID_0 1
1582
+  #endif
1597 1583
 
1598
-#if PIN_EXISTS(E2_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E2))
1599
-  #define HAS_E2_ENABLE 1
1600
-#endif
1601
-#if PIN_EXISTS(E2_DIR)
1602
-  #define HAS_E2_DIR 1
1603
-#endif
1604
-#if PIN_EXISTS(E2_STEP)
1605
-  #define HAS_E2_STEP 1
1606
-#endif
1607
-#if PIN_EXISTS(E2_MS1)
1608
-  #define HAS_E2_MS_PINS 1
1609
-#endif
1610
-#if PIN_EXISTS(SOL2)
1611
-  #define HAS_SOLENOID_2 1
1612
-#endif
1584
+  #if PIN_EXISTS(E1_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E1))
1585
+    #define HAS_E1_ENABLE 1
1586
+  #endif
1587
+  #if PIN_EXISTS(E1_DIR)
1588
+    #define HAS_E1_DIR 1
1589
+  #endif
1590
+  #if PIN_EXISTS(E1_STEP)
1591
+    #define HAS_E1_STEP 1
1592
+  #endif
1593
+  #if PIN_EXISTS(E1_MS1)
1594
+    #define HAS_E1_MS_PINS 1
1595
+  #endif
1596
+  #if PIN_EXISTS(SOL1)
1597
+    #define HAS_SOLENOID_1 1
1598
+  #endif
1613 1599
 
1614
-#if PIN_EXISTS(E3_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E3))
1615
-  #define HAS_E3_ENABLE 1
1616
-#endif
1617
-#if PIN_EXISTS(E3_DIR)
1618
-  #define HAS_E3_DIR 1
1619
-#endif
1620
-#if PIN_EXISTS(E3_STEP)
1621
-  #define HAS_E3_STEP 1
1622
-#endif
1623
-#if PIN_EXISTS(E3_MS1)
1624
-  #define HAS_E3_MS_PINS 1
1625
-#endif
1626
-#if PIN_EXISTS(SOL3)
1627
-  #define HAS_SOLENOID_3 1
1628
-#endif
1600
+  #if PIN_EXISTS(E2_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E2))
1601
+    #define HAS_E2_ENABLE 1
1602
+  #endif
1603
+  #if PIN_EXISTS(E2_DIR)
1604
+    #define HAS_E2_DIR 1
1605
+  #endif
1606
+  #if PIN_EXISTS(E2_STEP)
1607
+    #define HAS_E2_STEP 1
1608
+  #endif
1609
+  #if PIN_EXISTS(E2_MS1)
1610
+    #define HAS_E2_MS_PINS 1
1611
+  #endif
1612
+  #if PIN_EXISTS(SOL2)
1613
+    #define HAS_SOLENOID_2 1
1614
+  #endif
1629 1615
 
1630
-#if PIN_EXISTS(E4_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E4))
1631
-  #define HAS_E4_ENABLE 1
1632
-#endif
1633
-#if PIN_EXISTS(E4_DIR)
1634
-  #define HAS_E4_DIR 1
1635
-#endif
1636
-#if PIN_EXISTS(E4_STEP)
1637
-  #define HAS_E4_STEP 1
1638
-#endif
1639
-#if PIN_EXISTS(E4_MS1)
1640
-  #define HAS_E4_MS_PINS 1
1641
-#endif
1642
-#if PIN_EXISTS(SOL4)
1643
-  #define HAS_SOLENOID_4 1
1644
-#endif
1616
+  #if PIN_EXISTS(E3_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E3))
1617
+    #define HAS_E3_ENABLE 1
1618
+  #endif
1619
+  #if PIN_EXISTS(E3_DIR)
1620
+    #define HAS_E3_DIR 1
1621
+  #endif
1622
+  #if PIN_EXISTS(E3_STEP)
1623
+    #define HAS_E3_STEP 1
1624
+  #endif
1625
+  #if PIN_EXISTS(E3_MS1)
1626
+    #define HAS_E3_MS_PINS 1
1627
+  #endif
1628
+  #if PIN_EXISTS(SOL3)
1629
+    #define HAS_SOLENOID_3 1
1630
+  #endif
1645 1631
 
1646
-#if PIN_EXISTS(E5_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E5))
1647
-  #define HAS_E5_ENABLE 1
1648
-#endif
1649
-#if PIN_EXISTS(E5_DIR)
1650
-  #define HAS_E5_DIR 1
1651
-#endif
1652
-#if PIN_EXISTS(E5_STEP)
1653
-  #define HAS_E5_STEP 1
1654
-#endif
1655
-#if PIN_EXISTS(E5_MS1)
1656
-  #define HAS_E5_MS_PINS 1
1657
-#endif
1658
-#if PIN_EXISTS(SOL5)
1659
-  #define HAS_SOLENOID_5 1
1660
-#endif
1632
+  #if PIN_EXISTS(E4_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E4))
1633
+    #define HAS_E4_ENABLE 1
1634
+  #endif
1635
+  #if PIN_EXISTS(E4_DIR)
1636
+    #define HAS_E4_DIR 1
1637
+  #endif
1638
+  #if PIN_EXISTS(E4_STEP)
1639
+    #define HAS_E4_STEP 1
1640
+  #endif
1641
+  #if PIN_EXISTS(E4_MS1)
1642
+    #define HAS_E4_MS_PINS 1
1643
+  #endif
1644
+  #if PIN_EXISTS(SOL4)
1645
+    #define HAS_SOLENOID_4 1
1646
+  #endif
1661 1647
 
1662
-#if PIN_EXISTS(E6_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E6))
1663
-  #define HAS_E6_ENABLE 1
1664
-#endif
1665
-#if PIN_EXISTS(E6_DIR)
1666
-  #define HAS_E6_DIR 1
1667
-#endif
1668
-#if PIN_EXISTS(E6_STEP)
1669
-  #define HAS_E6_STEP 1
1670
-#endif
1671
-#if PIN_EXISTS(E6_MS1)
1672
-  #define HAS_E6_MS_PINS 1
1673
-#endif
1674
-#if PIN_EXISTS(SOL6)
1675
-  #define HAS_SOLENOID_6 1
1676
-#endif
1648
+  #if PIN_EXISTS(E5_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E5))
1649
+    #define HAS_E5_ENABLE 1
1650
+  #endif
1651
+  #if PIN_EXISTS(E5_DIR)
1652
+    #define HAS_E5_DIR 1
1653
+  #endif
1654
+  #if PIN_EXISTS(E5_STEP)
1655
+    #define HAS_E5_STEP 1
1656
+  #endif
1657
+  #if PIN_EXISTS(E5_MS1)
1658
+    #define HAS_E5_MS_PINS 1
1659
+  #endif
1660
+  #if PIN_EXISTS(SOL5)
1661
+    #define HAS_SOLENOID_5 1
1662
+  #endif
1677 1663
 
1678
-#if PIN_EXISTS(E7_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E7))
1679
-  #define HAS_E7_ENABLE 1
1680
-#endif
1681
-#if PIN_EXISTS(E7_DIR)
1682
-  #define HAS_E7_DIR 1
1683
-#endif
1684
-#if PIN_EXISTS(E7_STEP)
1685
-  #define HAS_E7_STEP 1
1686
-#endif
1687
-#if PIN_EXISTS(E7_MS1)
1688
-  #define HAS_E7_MS_PINS 1
1689
-#endif
1690
-#if PIN_EXISTS(SOL7)
1691
-  #define HAS_SOLENOID_7 1
1692
-#endif
1664
+  #if PIN_EXISTS(E6_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E6))
1665
+    #define HAS_E6_ENABLE 1
1666
+  #endif
1667
+  #if PIN_EXISTS(E6_DIR)
1668
+    #define HAS_E6_DIR 1
1669
+  #endif
1670
+  #if PIN_EXISTS(E6_STEP)
1671
+    #define HAS_E6_STEP 1
1672
+  #endif
1673
+  #if PIN_EXISTS(E6_MS1)
1674
+    #define HAS_E6_MS_PINS 1
1675
+  #endif
1676
+  #if PIN_EXISTS(SOL6)
1677
+    #define HAS_SOLENOID_6 1
1678
+  #endif
1679
+
1680
+  #if PIN_EXISTS(E7_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E7))
1681
+    #define HAS_E7_ENABLE 1
1682
+  #endif
1683
+  #if PIN_EXISTS(E7_DIR)
1684
+    #define HAS_E7_DIR 1
1685
+  #endif
1686
+  #if PIN_EXISTS(E7_STEP)
1687
+    #define HAS_E7_STEP 1
1688
+  #endif
1689
+  #if PIN_EXISTS(E7_MS1)
1690
+    #define HAS_E7_MS_PINS 1
1691
+  #endif
1692
+  #if PIN_EXISTS(SOL7)
1693
+    #define HAS_SOLENOID_7 1
1694
+  #endif
1695
+
1696
+#endif // HAS_EXTRUDERS
1693 1697
 
1694 1698
 //
1695 1699
 // Trinamic Stepper Drivers
@@ -2348,7 +2352,10 @@
2348 2352
 #if PIN_EXISTS(DIGIPOTSS)
2349 2353
   #define HAS_MOTOR_CURRENT_SPI 1
2350 2354
 #endif
2351
-#if ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY, MOTOR_CURRENT_PWM_Z, MOTOR_CURRENT_PWM_E)
2355
+#if HAS_EXTRUDERS && PIN_EXISTS(MOTOR_CURRENT_PWM_E)
2356
+  #define HAS_MOTOR_CURRENT_PWM_E 1
2357
+#endif
2358
+#if HAS_MOTOR_CURRENT_PWM_E || ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY, MOTOR_CURRENT_PWM_Z)
2352 2359
   #define HAS_MOTOR_CURRENT_PWM 1
2353 2360
 #endif
2354 2361
 

+ 16
- 11
Marlin/src/inc/SanityCheck.h View File

@@ -1593,11 +1593,12 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
1593 1593
  * Homing
1594 1594
  */
1595 1595
 constexpr float hbm[] = HOMING_BUMP_MM;
1596
-static_assert(COUNT(hbm) == XYZ, "HOMING_BUMP_MM requires X, Y, and Z elements.");
1597
-static_assert(hbm[X_AXIS] >= 0, "HOMING_BUMP_MM.X must be greater than or equal to 0.");
1598
-static_assert(hbm[Y_AXIS] >= 0, "HOMING_BUMP_MM.Y must be greater than or equal to 0.");
1599
-static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal to 0.");
1600
-
1596
+static_assert(COUNT(hbm) == LINEAR_AXES, "HOMING_BUMP_MM requires one element per linear axis.");
1597
+LINEAR_AXIS_CODE(
1598
+  static_assert(hbm[X_AXIS] >= 0, "HOMING_BUMP_MM.X must be greater than or equal to 0."),
1599
+  static_assert(hbm[Y_AXIS] >= 0, "HOMING_BUMP_MM.Y must be greater than or equal to 0."),
1600
+  static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal to 0.")
1601
+);
1601 1602
 #if ENABLED(CODEPENDENT_XY_HOMING)
1602 1603
   #if ENABLED(QUICK_HOME)
1603 1604
     #error "QUICK_HOME is incompatible with CODEPENDENT_XY_HOMING."
@@ -1976,12 +1977,16 @@ static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal
1976 1977
   #error "HEATER_0_PIN not defined for this board."
1977 1978
 #elif !ANY_PIN(TEMP_0, MAX6675_SS)
1978 1979
   #error "TEMP_0_PIN or MAX6675_SS not defined for this board."
1979
-#elif ((defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && !PINS_EXIST(E0_STEP, E0_DIR))
1980
-  #error "E0_STEP_PIN or E0_DIR_PIN not defined for this board."
1981
-#elif ( !(defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && (!PINS_EXIST(E0_STEP, E0_DIR) || !HAS_E0_ENABLE))
1982
-  #error "E0_STEP_PIN, E0_DIR_PIN, or E0_ENABLE_PIN not defined for this board."
1983
-#elif EXTRUDERS && TEMP_SENSOR_0 == 0
1984
-  #error "TEMP_SENSOR_0 is required if there are any extruders."
1980
+#endif
1981
+
1982
+#if HAS_EXTRUDERS
1983
+  #if ((defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && !PINS_EXIST(E0_STEP, E0_DIR))
1984
+    #error "E0_STEP_PIN or E0_DIR_PIN not defined for this board."
1985
+  #elif ( !(defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && (!PINS_EXIST(E0_STEP, E0_DIR) || !HAS_E0_ENABLE))
1986
+    #error "E0_STEP_PIN, E0_DIR_PIN, or E0_ENABLE_PIN not defined for this board."
1987
+  #elif EXTRUDERS && TEMP_SENSOR_0 == 0
1988
+    #error "TEMP_SENSOR_0 is required if there are any extruders."
1989
+  #endif
1985 1990
 #endif
1986 1991
 
1987 1992
 /**

+ 4
- 2
Marlin/src/lcd/dogm/status_screen_DOGM.cpp View File

@@ -856,8 +856,10 @@ void MarlinUI::draw_status_screen() {
856 856
       #else
857 857
 
858 858
         if (show_e_total) {
859
-          _draw_axis_value(E_AXIS, xstring, true);
860
-          lcd_put_u8str_P(PSTR("       "));
859
+          #if ENABLED(LCD_SHOW_E_TOTAL)
860
+            _draw_axis_value(E_AXIS, xstring, true);
861
+            lcd_put_u8str_P(PSTR("       "));
862
+          #endif
861 863
         }
862 864
         else {
863 865
           _draw_axis_value(X_AXIS, xstring, blink);

+ 4
- 4
Marlin/src/lcd/extui/dgus/mks/DGUSDisplayDef.cpp View File

@@ -666,10 +666,10 @@ const struct DGUS_VP_Variable ListOfVP[] PROGMEM = {
666 666
   VPHELPER(VP_Z_MAX_SPEED, &planner.settings.max_feedrate_mm_s[Z_AXIS], ScreenHandler.HandleMaxSpeedChange_MKS, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<0>),
667 667
 
668 668
   #if HOTENDS >= 1
669
-    VPHELPER(VP_E0_MAX_SPEED, &planner.settings.max_feedrate_mm_s[E0_AXIS], ScreenHandler.HandleExtruderMaxSpeedChange_MKS, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<0>),
669
+    VPHELPER(VP_E0_MAX_SPEED, &planner.settings.max_feedrate_mm_s[E_AXIS_N(0)], ScreenHandler.HandleExtruderMaxSpeedChange_MKS, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<0>),
670 670
   #endif
671 671
   #if HOTENDS >= 2
672
-    VPHELPER(VP_E1_MAX_SPEED, &planner.settings.max_feedrate_mm_s[E1_AXIS], ScreenHandler.HandleExtruderMaxSpeedChange_MKS, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<0>),
672
+    VPHELPER(VP_E1_MAX_SPEED, &planner.settings.max_feedrate_mm_s[E_AXIS_N(1)], ScreenHandler.HandleExtruderMaxSpeedChange_MKS, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<0>),
673 673
   #endif
674 674
 
675 675
   VPHELPER(VP_X_ACC_MAX_SPEED, (uint16_t *)&planner.settings.max_acceleration_mm_per_s2[X_AXIS], ScreenHandler.HandleMaxAccChange_MKS, ScreenHandler.DGUSLCD_SendWordValueToDisplay),
@@ -677,10 +677,10 @@ const struct DGUS_VP_Variable ListOfVP[] PROGMEM = {
677 677
   VPHELPER(VP_Z_ACC_MAX_SPEED, (uint16_t *)&planner.settings.max_acceleration_mm_per_s2[Z_AXIS], ScreenHandler.HandleMaxAccChange_MKS, ScreenHandler.DGUSLCD_SendWordValueToDisplay),
678 678
 
679 679
   #if HOTENDS >= 1
680
-    VPHELPER(VP_E0_ACC_MAX_SPEED, (uint16_t *)&planner.settings.max_acceleration_mm_per_s2[E0_AXIS], ScreenHandler.HandleExtruderAccChange_MKS, ScreenHandler.DGUSLCD_SendWordValueToDisplay),
680
+    VPHELPER(VP_E0_ACC_MAX_SPEED, (uint16_t *)&planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(0)], ScreenHandler.HandleExtruderAccChange_MKS, ScreenHandler.DGUSLCD_SendWordValueToDisplay),
681 681
   #endif
682 682
   #if HOTENDS >= 2
683
-    VPHELPER(VP_E1_ACC_MAX_SPEED, (uint16_t *)&planner.settings.max_acceleration_mm_per_s2[E1_AXIS], ScreenHandler.HandleExtruderAccChange_MKS, ScreenHandler.DGUSLCD_SendWordValueToDisplay),
683
+    VPHELPER(VP_E1_ACC_MAX_SPEED, (uint16_t *)&planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(1)], ScreenHandler.HandleExtruderAccChange_MKS, ScreenHandler.DGUSLCD_SendWordValueToDisplay),
684 684
   #endif
685 685
 
686 686
   VPHELPER(VP_TRAVEL_SPEED, (uint16_t *)&planner.settings.travel_acceleration, ScreenHandler.HandleTravelAccChange_MKS, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<0>),

+ 7
- 3
Marlin/src/lcd/marlinui.cpp View File

@@ -712,13 +712,15 @@ void MarlinUI::quick_feedback(const bool clear_buttons/*=true*/) {
712 712
     // Add a manual move to the queue?
713 713
     if (axis != NO_AXIS_ENUM && ELAPSED(millis(), start_time) && !planner.is_full()) {
714 714
 
715
-      const feedRate_t fr_mm_s = (axis <= E_AXIS) ? manual_feedrate_mm_s[axis] : XY_PROBE_FEEDRATE_MM_S;
715
+      const feedRate_t fr_mm_s = (axis <= LOGICAL_AXES) ? manual_feedrate_mm_s[axis] : XY_PROBE_FEEDRATE_MM_S;
716 716
 
717 717
       #if IS_KINEMATIC
718 718
 
719 719
         #if HAS_MULTI_EXTRUDER
720 720
           REMEMBER(ae, active_extruder);
721
-          if (axis == E_AXIS) active_extruder = e_index;
721
+          #if MULTI_E_MANUAL
722
+            if (axis == E_AXIS) active_extruder = e_index;
723
+          #endif
722 724
         #endif
723 725
 
724 726
         // Apply a linear offset to a single axis
@@ -744,7 +746,9 @@ void MarlinUI::quick_feedback(const bool clear_buttons/*=true*/) {
744 746
       #else
745 747
 
746 748
         // For Cartesian / Core motion simply move to the current_position
747
-        planner.buffer_line(current_position, fr_mm_s, axis == E_AXIS ? e_index : active_extruder);
749
+        planner.buffer_line(current_position, fr_mm_s,
750
+          TERN_(MULTI_E_MANUAL, axis == E_AXIS ? e_index :) active_extruder
751
+        );
748 752
 
749 753
         //SERIAL_ECHOLNPAIR("Add planner.move with Axis ", AS_CHAR(axis_codes[axis]), " at FR ", fr_mm_s);
750 754
 

+ 18
- 22
Marlin/src/lcd/menu/menu_advanced.cpp View File

@@ -68,10 +68,7 @@ void menu_backlash();
68 68
     START_MENU();
69 69
     BACK_ITEM(MSG_ADVANCED_SETTINGS);
70 70
     #define EDIT_DAC_PERCENT(A) EDIT_ITEM(uint8, MSG_DAC_PERCENT_##A, &driverPercent[_AXIS(A)], 0, 100, []{ stepper_dac.set_current_percents(driverPercent); })
71
-    EDIT_DAC_PERCENT(X);
72
-    EDIT_DAC_PERCENT(Y);
73
-    EDIT_DAC_PERCENT(Z);
74
-    EDIT_DAC_PERCENT(E);
71
+    LOGICAL_AXIS_CODE(EDIT_DAC_PERCENT(E), EDIT_DAC_PERCENT(X), EDIT_DAC_PERCENT(Y), EDIT_DAC_PERCENT(Z), EDIT_DAC_PERCENT(I), EDIT_DAC_PERCENT(J), EDIT_DAC_PERCENT(K));
75 72
     ACTION_ITEM(MSG_DAC_EEPROM_WRITE, stepper_dac.commit_eeprom);
76 73
     END_MENU();
77 74
   }
@@ -359,7 +356,7 @@ void menu_backlash();
359 356
       #elif ENABLED(LIMITED_MAX_FR_EDITING)
360 357
         DEFAULT_MAX_FEEDRATE
361 358
       #else
362
-        { 9999, 9999, 9999, 9999 }
359
+        LOGICAL_AXIS_ARRAY(9999, 9999, 9999, 9999)
363 360
       #endif
364 361
     ;
365 362
     #if ENABLED(LIMITED_MAX_FR_EDITING) && !defined(MAX_FEEDRATE_EDIT_VALUES)
@@ -372,9 +369,7 @@ void menu_backlash();
372 369
     BACK_ITEM(MSG_ADVANCED_SETTINGS);
373 370
 
374 371
     #define EDIT_VMAX(N) EDIT_ITEM_FAST(float5, MSG_VMAX_##N, &planner.settings.max_feedrate_mm_s[_AXIS(N)], 1, max_fr_edit_scaled[_AXIS(N)])
375
-    EDIT_VMAX(A);
376
-    EDIT_VMAX(B);
377
-    EDIT_VMAX(C);
372
+    LINEAR_AXIS_CODE(EDIT_VMAX(A), EDIT_VMAX(B), EDIT_VMAX(C), EDIT_VMAX(I), EDIT_VMAX(J), EDIT_VMAX(K));
378 373
 
379 374
     #if E_STEPPERS
380 375
       EDIT_ITEM_FAST(float5, MSG_VMAX_E, &planner.settings.max_feedrate_mm_s[E_AXIS_N(active_extruder)], 1, max_fr_edit_scaled.e);
@@ -404,7 +399,7 @@ void menu_backlash();
404 399
       #elif ENABLED(LIMITED_MAX_ACCEL_EDITING)
405 400
         DEFAULT_MAX_ACCELERATION
406 401
       #else
407
-        { 99000, 99000, 99000, 99000 }
402
+        LOGICAL_AXIS_ARRAY(99000, 99000, 99000, 99000)
408 403
       #endif
409 404
     ;
410 405
     #if ENABLED(LIMITED_MAX_ACCEL_EDITING) && !defined(MAX_ACCEL_EDIT_VALUES)
@@ -419,16 +414,19 @@ void menu_backlash();
419 414
     // M204 P Acceleration
420 415
     EDIT_ITEM_FAST(float5_25, MSG_ACC, &planner.settings.acceleration, 25, max_accel);
421 416
 
422
-    // M204 R Retract Acceleration
423
-    EDIT_ITEM_FAST(float5, MSG_A_RETRACT, &planner.settings.retract_acceleration, 100, planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(active_extruder)]);
417
+    #if HAS_EXTRUDERS
418
+      // M204 R Retract Acceleration
419
+      EDIT_ITEM_FAST(float5, MSG_A_RETRACT, &planner.settings.retract_acceleration, 100, planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(active_extruder)]);
420
+    #endif
424 421
 
425 422
     // M204 T Travel Acceleration
426 423
     EDIT_ITEM_FAST(float5_25, MSG_A_TRAVEL, &planner.settings.travel_acceleration, 25, max_accel);
427 424
 
428 425
     #define EDIT_AMAX(Q,L) EDIT_ITEM_FAST(long5_25, MSG_AMAX_##Q, &planner.settings.max_acceleration_mm_per_s2[_AXIS(Q)], L, max_accel_edit_scaled[_AXIS(Q)], []{ planner.reset_acceleration_rates(); })
429
-    EDIT_AMAX(A, 100);
430
-    EDIT_AMAX(B, 100);
431
-    EDIT_AMAX(C,  10);
426
+    LINEAR_AXIS_CODE(
427
+      EDIT_AMAX(A, 100), EDIT_AMAX(B, 100), EDIT_AMAX(C, 10),
428
+      EDIT_AMAX(I,  10), EDIT_AMAX(J,  10), EDIT_AMAX(K, 10)
429
+    );
432 430
 
433 431
     #if ENABLED(DISTINCT_E_FACTORS)
434 432
       EDIT_ITEM_FAST(long5_25, MSG_AMAX_E, &planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(active_extruder)], 100, max_accel_edit_scaled.e, []{ planner.reset_acceleration_rates(); });
@@ -474,14 +472,14 @@ void menu_backlash();
474 472
         #endif
475 473
       ;
476 474
       #define EDIT_JERK(N) EDIT_ITEM_FAST(float3, MSG_V##N##_JERK, &planner.max_jerk[_AXIS(N)], 1, max_jerk_edit[_AXIS(N)])
477
-      EDIT_JERK(A);
478
-      EDIT_JERK(B);
479 475
       #if ENABLED(DELTA)
480
-        EDIT_JERK(C);
476
+        #define EDIT_JERK_C() EDIT_JERK(C)
481 477
       #else
482
-        EDIT_ITEM_FAST(float52sign, MSG_VC_JERK, &planner.max_jerk.c, 0.1f, max_jerk_edit.c);
478
+        #define EDIT_JERK_C() EDIT_ITEM_FAST(float52sign, MSG_VC_JERK, &planner.max_jerk.c, 0.1f, max_jerk_edit.c)
483 479
       #endif
484
-      #if HAS_CLASSIC_E_JERK
480
+      LINEAR_AXIS_CODE(EDIT_JERK(A), EDIT_JERK(B), EDIT_JERK_C());
481
+
482
+      #if HAS_EXTRUDERS
485 483
         EDIT_ITEM_FAST(float52sign, MSG_VE_JERK, &planner.max_jerk.e, 0.1f, max_jerk_edit.e);
486 484
       #endif
487 485
 
@@ -517,9 +515,7 @@ void menu_advanced_steps_per_mm() {
517 515
   BACK_ITEM(MSG_ADVANCED_SETTINGS);
518 516
 
519 517
   #define EDIT_QSTEPS(Q) EDIT_ITEM_FAST(float51, MSG_##Q##_STEPS, &planner.settings.axis_steps_per_mm[_AXIS(Q)], 5, 9999, []{ planner.refresh_positioning(); })
520
-  EDIT_QSTEPS(A);
521
-  EDIT_QSTEPS(B);
522
-  EDIT_QSTEPS(C);
518
+  LINEAR_AXIS_CODE(EDIT_QSTEPS(A), EDIT_QSTEPS(B), EDIT_QSTEPS(C));
523 519
 
524 520
   #if ENABLED(DISTINCT_E_FACTORS)
525 521
     LOOP_L_N(n, E_STEPPERS)

+ 9
- 2
Marlin/src/module/endstops.cpp View File

@@ -361,7 +361,8 @@ void Endstops::event_handler() {
361 361
   prev_hit_state = hit_state;
362 362
   if (hit_state) {
363 363
     #if HAS_STATUS_MESSAGE
364
-      char chrX = ' ', chrY = ' ', chrZ = ' ', chrP = ' ';
364
+      char LINEAR_AXIS_LIST(chrX = ' ', chrY = ' ', chrZ = ' '),
365
+           chrP = ' ';
365 366
       #define _SET_STOP_CHAR(A,C) (chr## A = C)
366 367
     #else
367 368
       #define _SET_STOP_CHAR(A,C) NOOP
@@ -390,7 +391,13 @@ void Endstops::event_handler() {
390 391
     #endif
391 392
     SERIAL_EOL();
392 393
 
393
-    TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %c %c %c %c"), GET_TEXT(MSG_LCD_ENDSTOPS), chrX, chrY, chrZ, chrP));
394
+    TERN_(HAS_STATUS_MESSAGE,
395
+      ui.status_printf_P(0,
396
+        PSTR(S_FMT GANG_N_1(LINEAR_AXES, " %c") " %c"),
397
+        GET_TEXT(MSG_LCD_ENDSTOPS),
398
+        LINEAR_AXIS_LIST(chrX, chrY, chrZ), chrP
399
+      )
400
+    );
394 401
 
395 402
     #if BOTH(SD_ABORT_ON_ENDSTOP_HIT, SDSUPPORT)
396 403
       if (planner.abort_on_endstop_hit) {

+ 68
- 34
Marlin/src/module/motion.cpp View File

@@ -89,7 +89,7 @@ bool relative_mode; // = false;
89 89
   #define Z_INIT_POS Z_HOME_POS
90 90
 #endif
91 91
 
92
-xyze_pos_t current_position = { X_HOME_POS, Y_HOME_POS, Z_INIT_POS };
92
+xyze_pos_t current_position = LOGICAL_AXIS_ARRAY(0, X_HOME_POS, Y_HOME_POS, Z_INIT_POS);
93 93
 
94 94
 /**
95 95
  * Cartesian Destination
@@ -195,16 +195,25 @@ inline void report_more_positions() {
195 195
 // Report the logical position for a given machine position
196 196
 inline void report_logical_position(const xyze_pos_t &rpos) {
197 197
   const xyze_pos_t lpos = rpos.asLogical();
198
-  SERIAL_ECHOPAIR_P(X_LBL, lpos.x, SP_Y_LBL, lpos.y, SP_Z_LBL, lpos.z, SP_E_LBL, lpos.e);
198
+  SERIAL_ECHOPAIR_P(
199
+    LIST_N(DOUBLE(LINEAR_AXES), X_LBL, lpos.x, SP_Y_LBL, lpos.y, SP_Z_LBL, lpos.z)
200
+    #if HAS_EXTRUDERS
201
+      , SP_E_LBL, lpos.e
202
+    #endif
203
+  );
199 204
 }
200 205
 
201 206
 // Report the real current position according to the steppers.
202 207
 // Forward kinematics and un-leveling are applied.
203 208
 void report_real_position() {
204 209
   get_cartesian_from_steppers();
205
-  xyze_pos_t npos = cartes;
206
-  npos.e = planner.get_axis_position_mm(E_AXIS);
210
+  xyze_pos_t npos = LOGICAL_AXIS_ARRAY(
211
+    planner.get_axis_position_mm(E_AXIS),
212
+    cartes.x, cartes.y, cartes.z
213
+  );
214
+
207 215
   TERN_(HAS_POSITION_MODIFIERS, planner.unapply_modifiers(npos, true));
216
+
208 217
   report_logical_position(npos);
209 218
   report_more_positions();
210 219
 }
@@ -309,7 +318,9 @@ void sync_plan_position() {
309 318
   planner.set_position_mm(current_position);
310 319
 }
311 320
 
312
-void sync_plan_position_e() { planner.set_e_position_mm(current_position.e); }
321
+#if HAS_EXTRUDERS
322
+  void sync_plan_position_e() { planner.set_e_position_mm(current_position.e); }
323
+#endif
313 324
 
314 325
 /**
315 326
  * Get the stepper positions in the cartes[] array.
@@ -354,7 +365,10 @@ void get_cartesian_from_steppers() {
354 365
 void set_current_from_steppers_for_axis(const AxisEnum axis) {
355 366
   get_cartesian_from_steppers();
356 367
   xyze_pos_t pos = cartes;
357
-  pos.e = planner.get_axis_position_mm(E_AXIS);
368
+
369
+  #if HAS_EXTRUDERS
370
+    pos.e = planner.get_axis_position_mm(E_AXIS);
371
+  #endif
358 372
 
359 373
   #if HAS_POSITION_MODIFIERS
360 374
     planner.unapply_modifiers(pos, true);
@@ -442,9 +456,12 @@ void _internal_move_to_destination(const_feedRate_t fr_mm_s/*=0.0f*/
442 456
  * - Delta may lower Z first to get into the free motion zone.
443 457
  * - Before returning, wait for the planner buffer to empty.
444 458
  */
445
-void do_blocking_move_to(const float rx, const float ry, const float rz, const_feedRate_t fr_mm_s/*=0.0*/) {
459
+void do_blocking_move_to(
460
+  LINEAR_AXIS_LIST(const float rx, const float ry, const float rz),
461
+  const_feedRate_t fr_mm_s/*=0.0f*/
462
+) {
446 463
   DEBUG_SECTION(log_move, "do_blocking_move_to", DEBUGGING(LEVELING));
447
-  if (DEBUGGING(LEVELING)) DEBUG_XYZ("> ", rx, ry, rz);
464
+  if (DEBUGGING(LEVELING)) DEBUG_XYZ("> ", LINEAR_AXIS_LIST(rx, ry, rz));
448 465
 
449 466
   const feedRate_t z_feedrate = fr_mm_s ?: homing_feedrate(Z_AXIS),
450 467
                   xy_feedrate = fr_mm_s ?: feedRate_t(XY_PROBE_FEEDRATE_MM_S);
@@ -529,34 +546,46 @@ void do_blocking_move_to(const float rx, const float ry, const float rz, const_f
529 546
 }
530 547
 
531 548
 void do_blocking_move_to(const xy_pos_t &raw, const_feedRate_t fr_mm_s/*=0.0f*/) {
532
-  do_blocking_move_to(raw.x, raw.y, current_position.z, fr_mm_s);
549
+  do_blocking_move_to(LINEAR_AXIS_LIST(raw.x, raw.y, current_position.z, current_position.i), fr_mm_s);
533 550
 }
534 551
 void do_blocking_move_to(const xyz_pos_t &raw, const_feedRate_t fr_mm_s/*=0.0f*/) {
535
-  do_blocking_move_to(raw.x, raw.y, raw.z, fr_mm_s);
552
+  do_blocking_move_to(LINEAR_AXIS_LIST(raw.x, raw.y, raw.z), fr_mm_s);
536 553
 }
537 554
 void do_blocking_move_to(const xyze_pos_t &raw, const_feedRate_t fr_mm_s/*=0.0f*/) {
538
-  do_blocking_move_to(raw.x, raw.y, raw.z, fr_mm_s);
555
+  do_blocking_move_to(LINEAR_AXIS_LIST(raw.x, raw.y, raw.z), fr_mm_s);
539 556
 }
540 557
 
541 558
 void do_blocking_move_to_x(const_float_t rx, const_feedRate_t fr_mm_s/*=0.0*/) {
542
-  do_blocking_move_to(rx, current_position.y, current_position.z, fr_mm_s);
559
+  do_blocking_move_to(
560
+    LINEAR_AXIS_LIST(rx, current_position.y, current_position.z),
561
+    fr_mm_s
562
+  );
543 563
 }
544 564
 void do_blocking_move_to_y(const_float_t ry, const_feedRate_t fr_mm_s/*=0.0*/) {
545
-  do_blocking_move_to(current_position.x, ry, current_position.z, fr_mm_s);
565
+  do_blocking_move_to(
566
+    LINEAR_AXIS_LIST(current_position.x, ry, current_position.z),
567
+    fr_mm_s
568
+  );
546 569
 }
547 570
 void do_blocking_move_to_z(const_float_t rz, const_feedRate_t fr_mm_s/*=0.0*/) {
548 571
   do_blocking_move_to_xy_z(current_position, rz, fr_mm_s);
549 572
 }
550 573
 
551 574
 void do_blocking_move_to_xy(const_float_t rx, const_float_t ry, const_feedRate_t fr_mm_s/*=0.0*/) {
552
-  do_blocking_move_to(rx, ry, current_position.z, fr_mm_s);
575
+  do_blocking_move_to(
576
+    LINEAR_AXIS_LIST(rx, ry, current_position.z),
577
+    fr_mm_s
578
+  );
553 579
 }
554 580
 void do_blocking_move_to_xy(const xy_pos_t &raw, const_feedRate_t fr_mm_s/*=0.0f*/) {
555 581
   do_blocking_move_to_xy(raw.x, raw.y, fr_mm_s);
556 582
 }
557 583
 
558 584
 void do_blocking_move_to_xy_z(const xy_pos_t &raw, const_float_t z, const_feedRate_t fr_mm_s/*=0.0f*/) {
559
-  do_blocking_move_to(raw.x, raw.y, z, fr_mm_s);
585
+  do_blocking_move_to(
586
+    LINEAR_AXIS_LIST(raw.x, raw.y, z),
587
+    fr_mm_s
588
+  );
560 589
 }
561 590
 
562 591
 void do_z_clearance(const_float_t zclear, const bool lower_allowed/*=false*/) {
@@ -589,8 +618,8 @@ void restore_feedrate_and_scaling() {
589 618
   // Software Endstops are based on the configured limits.
590 619
   soft_endstops_t soft_endstop = {
591 620
     true, false,
592
-    { X_MIN_POS, Y_MIN_POS, Z_MIN_POS },
593
-    { X_MAX_POS, Y_MAX_POS, Z_MAX_POS }
621
+    LINEAR_AXIS_ARRAY(X_MIN_POS, Y_MIN_POS, Z_MIN_POS),
622
+    LINEAR_AXIS_ARRAY(X_MAX_BED, Y_MAX_BED, Z_MAX_POS)
594 623
   };
595 624
 
596 625
   /**
@@ -1176,9 +1205,12 @@ void prepare_line_to_destination() {
1176 1205
       if (TEST(b, a) && TERN(HOME_AFTER_DEACTIVATE, axis_is_trusted, axis_was_homed)(a))
1177 1206
         CBI(b, a);
1178 1207
     };
1179
-    set_should(axis_bits, X_AXIS);  // Clear test bits that are trusted
1180
-    set_should(axis_bits, Y_AXIS);
1181
-    set_should(axis_bits, Z_AXIS);
1208
+    // Clear test bits that are trusted
1209
+    LINEAR_AXIS_CODE(
1210
+      set_should(axis_bits, X_AXIS),
1211
+      set_should(axis_bits, Y_AXIS),
1212
+      set_should(axis_bits, Z_AXIS)
1213
+    );
1182 1214
     return axis_bits;
1183 1215
   }
1184 1216
 
@@ -1187,9 +1219,11 @@ void prepare_line_to_destination() {
1187 1219
       PGM_P home_first = GET_TEXT(MSG_HOME_FIRST);
1188 1220
       char msg[strlen_P(home_first)+1];
1189 1221
       sprintf_P(msg, home_first,
1190
-        TEST(axis_bits, X_AXIS) ? "X" : "",
1191
-        TEST(axis_bits, Y_AXIS) ? "Y" : "",
1192
-        TEST(axis_bits, Z_AXIS) ? "Z" : ""
1222
+        LINEAR_AXIS_LIST(
1223
+          TEST(axis_bits, X_AXIS) ? "X" : "",
1224
+          TEST(axis_bits, Y_AXIS) ? "Y" : "",
1225
+          TEST(axis_bits, Z_AXIS) ? "Z" : ""
1226
+        )
1193 1227
       );
1194 1228
       SERIAL_ECHO_START();
1195 1229
       SERIAL_ECHOLN(msg);
@@ -1356,7 +1390,7 @@ void prepare_line_to_destination() {
1356 1390
     const feedRate_t home_fr_mm_s = fr_mm_s ?: homing_feedrate(axis);
1357 1391
 
1358 1392
     if (DEBUGGING(LEVELING)) {
1359
-      DEBUG_ECHOPAIR("...(", AS_CHAR(axis_codes[axis]), ", ", distance, ", ");
1393
+      DEBUG_ECHOPAIR("...(", AS_CHAR(AXIS_CHAR(axis)), ", ", distance, ", ");
1360 1394
       if (fr_mm_s)
1361 1395
         DEBUG_ECHO(fr_mm_s);
1362 1396
       else
@@ -1441,12 +1475,12 @@ void prepare_line_to_destination() {
1441 1475
    * "trusted" position).
1442 1476
    */
1443 1477
   void set_axis_never_homed(const AxisEnum axis) {
1444
-    if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(">>> set_axis_never_homed(", AS_CHAR(axis_codes[axis]), ")");
1478
+    if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(">>> set_axis_never_homed(", AS_CHAR(AXIS_CHAR(axis)), ")");
1445 1479
 
1446 1480
     set_axis_untrusted(axis);
1447 1481
     set_axis_unhomed(axis);
1448 1482
 
1449
-    if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("<<< set_axis_never_homed(", AS_CHAR(axis_codes[axis]), ")");
1483
+    if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("<<< set_axis_never_homed(", AS_CHAR(AXIS_CHAR(axis)), ")");
1450 1484
 
1451 1485
     TERN_(I2C_POSITION_ENCODERS, I2CPEM.unhomed(axis));
1452 1486
   }
@@ -1507,7 +1541,7 @@ void prepare_line_to_destination() {
1507 1541
       if (ABS(phaseDelta) * planner.steps_to_mm[axis] / phasePerUStep < 0.05f)
1508 1542
         SERIAL_ECHOLNPAIR("Selected home phase ", home_phase[axis],
1509 1543
                          " too close to endstop trigger phase ", phaseCurrent,
1510
-                         ". Pick a different phase for ", AS_CHAR(axis_codes[axis]));
1544
+                         ". Pick a different phase for ", AS_CHAR(AXIS_CHAR(axis)));
1511 1545
 
1512 1546
       // Skip to next if target position is behind current. So it only moves away from endstop.
1513 1547
       if (phaseDelta < 0) phaseDelta += 1024;
@@ -1518,7 +1552,7 @@ void prepare_line_to_destination() {
1518 1552
       // Optional debug messages
1519 1553
       if (DEBUGGING(LEVELING)) {
1520 1554
         DEBUG_ECHOLNPAIR(
1521
-          "Endstop ", AS_CHAR(axis_codes[axis]), " hit at Phase:", phaseCurrent,
1555
+          "Endstop ", AS_CHAR(AXIS_CHAR(axis)), " hit at Phase:", phaseCurrent,
1522 1556
           " Delta:", phaseDelta, " Distance:", mmDelta
1523 1557
         );
1524 1558
       }
@@ -1556,7 +1590,7 @@ void prepare_line_to_destination() {
1556 1590
       if (!_CAN_HOME(X) && !_CAN_HOME(Y) && !_CAN_HOME(Z)) return;
1557 1591
     #endif
1558 1592
 
1559
-    if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(">>> homeaxis(", AS_CHAR(axis_codes[axis]), ")");
1593
+    if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(">>> homeaxis(", AS_CHAR(AXIS_CHAR(axis)), ")");
1560 1594
 
1561 1595
     const int axis_home_dir = TERN0(DUAL_X_CARRIAGE, axis == X_AXIS)
1562 1596
                 ? TOOL_X_HOME_DIR(active_extruder) : home_dir(axis);
@@ -1634,7 +1668,7 @@ void prepare_line_to_destination() {
1634 1668
           case Z_AXIS: es = Z_ENDSTOP; break;
1635 1669
         }
1636 1670
         if (TEST(endstops.state(), es)) {
1637
-          SERIAL_ECHO_MSG("Bad ", AS_CHAR(axis_codes[axis]), " Endstop?");
1671
+          SERIAL_ECHO_MSG("Bad ", AS_CHAR(AXIS_CHAR(axis)), " Endstop?");
1638 1672
           kill(GET_TEXT(MSG_KILL_HOMING_FAILED));
1639 1673
         }
1640 1674
       #endif
@@ -1856,7 +1890,7 @@ void prepare_line_to_destination() {
1856 1890
       if (axis == Z_AXIS) fwretract.current_hop = 0.0;
1857 1891
     #endif
1858 1892
 
1859
-    if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("<<< homeaxis(", AS_CHAR(axis_codes[axis]), ")");
1893
+    if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("<<< homeaxis(", AS_CHAR(AXIS_CHAR(axis)), ")");
1860 1894
 
1861 1895
   } // homeaxis()
1862 1896
 
@@ -1881,7 +1915,7 @@ void prepare_line_to_destination() {
1881 1915
  * Callers must sync the planner position after calling this!
1882 1916
  */
1883 1917
 void set_axis_is_at_home(const AxisEnum axis) {
1884
-  if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(">>> set_axis_is_at_home(", AS_CHAR(axis_codes[axis]), ")");
1918
+  if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(">>> set_axis_is_at_home(", AS_CHAR(AXIS_CHAR(axis)), ")");
1885 1919
 
1886 1920
   set_axis_trusted(axis);
1887 1921
   set_axis_homed(axis);
@@ -1931,10 +1965,10 @@ void set_axis_is_at_home(const AxisEnum axis) {
1931 1965
 
1932 1966
   if (DEBUGGING(LEVELING)) {
1933 1967
     #if HAS_HOME_OFFSET
1934
-      DEBUG_ECHOLNPAIR("> home_offset[", AS_CHAR(axis_codes[axis]), "] = ", home_offset[axis]);
1968
+      DEBUG_ECHOLNPAIR("> home_offset[", AS_CHAR(AXIS_CHAR(axis)), "] = ", home_offset[axis]);
1935 1969
     #endif
1936 1970
     DEBUG_POS("", current_position);
1937
-    DEBUG_ECHOLNPAIR("<<< set_axis_is_at_home(", AS_CHAR(axis_codes[axis]), ")");
1971
+    DEBUG_ECHOLNPAIR("<<< set_axis_is_at_home(", AS_CHAR(AXIS_CHAR(axis)), ")");
1938 1972
   }
1939 1973
 }
1940 1974
 

+ 10
- 4
Marlin/src/module/motion.h View File

@@ -124,7 +124,7 @@ inline int8_t pgm_read_any(const int8_t *p) { return TERN(__IMXRT1062__, *p, pgm
124 124
 
125 125
 #define XYZ_DEFS(T, NAME, OPT) \
126 126
   inline T NAME(const AxisEnum axis) { \
127
-    static const XYZval<T> NAME##_P DEFS_PROGMEM = { X_##OPT, Y_##OPT, Z_##OPT }; \
127
+    static const XYZval<T> NAME##_P DEFS_PROGMEM = LINEAR_AXIS_ARRAY(X_##OPT, Y_##OPT, Z_##OPT); \
128 128
     return pgm_read_any(&NAME##_P[axis]); \
129 129
   }
130 130
 XYZ_DEFS(float, base_min_pos,   MIN_POS);
@@ -264,7 +264,10 @@ void quickstop_stepper();
264 264
  * no kinematic translation. Used for homing axes and cartesian/core syncing.
265 265
  */
266 266
 void sync_plan_position();
267
-void sync_plan_position_e();
267
+
268
+#if HAS_EXTRUDERS
269
+  void sync_plan_position_e();
270
+#endif
268 271
 
269 272
 /**
270 273
  * Move the planner to the current position from wherever it last moved
@@ -295,7 +298,10 @@ inline void prepare_internal_move_to_destination(const_feedRate_t fr_mm_s=0.0f)
295 298
 /**
296 299
  * Blocking movement and shorthand functions
297 300
  */
298
-void do_blocking_move_to(const float rx, const float ry, const float rz, const_feedRate_t fr_mm_s=0.0f);
301
+void do_blocking_move_to(
302
+  LINEAR_AXIS_LIST(const float rx, const float ry, const float rz),
303
+  const_feedRate_t fr_mm_s=0.0f
304
+);
299 305
 void do_blocking_move_to(const xy_pos_t &raw, const_feedRate_t fr_mm_s=0.0f);
300 306
 void do_blocking_move_to(const xyz_pos_t &raw, const_feedRate_t fr_mm_s=0.0f);
301 307
 void do_blocking_move_to(const xyze_pos_t &raw, const_feedRate_t fr_mm_s=0.0f);
@@ -322,7 +328,7 @@ void do_z_clearance(const_float_t zclear, const bool lower_allowed=false);
322 328
 /**
323 329
  * Homing and Trusted Axes
324 330
  */
325
-typedef IF<(LINEAR_AXES>8), uint16_t, uint8_t>::type linear_axis_bits_t;
331
+typedef IF<(LINEAR_AXES > 8), uint16_t, uint8_t>::type linear_axis_bits_t;
326 332
 constexpr linear_axis_bits_t linear_bits = _BV(LINEAR_AXES) - 1;
327 333
 
328 334
 void set_axis_is_at_home(const AxisEnum axis);

+ 153
- 113
Marlin/src/module/planner.cpp View File

@@ -1345,10 +1345,12 @@ void Planner::check_axes_activity() {
1345 1345
     #if ANY(DISABLE_X, DISABLE_Y, DISABLE_Z, DISABLE_E)
1346 1346
       for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) {
1347 1347
         block_t *block = &block_buffer[b];
1348
-        if (ENABLED(DISABLE_X) && block->steps.x) axis_active.x = true;
1349
-        if (ENABLED(DISABLE_Y) && block->steps.y) axis_active.y = true;
1350
-        if (ENABLED(DISABLE_Z) && block->steps.z) axis_active.z = true;
1351
-        if (ENABLED(DISABLE_E) && block->steps.e) axis_active.e = true;
1348
+        LOGICAL_AXIS_CODE(
1349
+          if (TERN0(DISABLE_E, block->steps.e)) axis_active.e = true,
1350
+          if (TERN0(DISABLE_X, block->steps.x)) axis_active.x = true,
1351
+          if (TERN0(DISABLE_Y, block->steps.y)) axis_active.y = true,
1352
+          if (TERN0(DISABLE_Z, block->steps.z)) axis_active.z = true
1353
+        );
1352 1354
       }
1353 1355
     #endif
1354 1356
   }
@@ -1369,10 +1371,12 @@ void Planner::check_axes_activity() {
1369 1371
   //
1370 1372
   // Disable inactive axes
1371 1373
   //
1372
-  if (TERN0(DISABLE_X, !axis_active.x)) DISABLE_AXIS_X();
1373
-  if (TERN0(DISABLE_Y, !axis_active.y)) DISABLE_AXIS_Y();
1374
-  if (TERN0(DISABLE_Z, !axis_active.z)) DISABLE_AXIS_Z();
1375
-  if (TERN0(DISABLE_E, !axis_active.e)) disable_e_steppers();
1374
+  LOGICAL_AXIS_CODE(
1375
+    if (TERN0(DISABLE_E, !axis_active.e)) disable_e_steppers(),
1376
+    if (TERN0(DISABLE_X, !axis_active.x)) DISABLE_AXIS_X(),
1377
+    if (TERN0(DISABLE_Y, !axis_active.y)) DISABLE_AXIS_Y(),
1378
+    if (TERN0(DISABLE_Z, !axis_active.z)) DISABLE_AXIS_Z()
1379
+  );
1376 1380
 
1377 1381
   //
1378 1382
   // Update Fan speeds
@@ -1823,16 +1827,12 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
1823 1827
   OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
1824 1828
   , feedRate_t fr_mm_s, const uint8_t extruder, const_float_t millimeters/*=0.0*/
1825 1829
 ) {
1826
-
1827
-  const int32_t da = target.a - position.a,
1828
-                db = target.b - position.b,
1829
-                dc = target.c - position.c;
1830
-
1831
-  #if HAS_EXTRUDERS
1832
-    int32_t de = target.e - position.e;
1833
-  #else
1834
-    constexpr int32_t de = 0;
1835
-  #endif
1830
+  int32_t LOGICAL_AXIS_LIST(
1831
+    de = target.e - position.e,
1832
+    da = target.a - position.a,
1833
+    db = target.b - position.b,
1834
+    dc = target.c - position.c
1835
+  );
1836 1836
 
1837 1837
   /* <-- add a slash to enable
1838 1838
     SERIAL_ECHOLNPAIR(
@@ -1883,35 +1883,39 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
1883 1883
   // Compute direction bit-mask for this block
1884 1884
   uint8_t dm = 0;
1885 1885
   #if CORE_IS_XY
1886
-    if (da < 0) SBI(dm, X_HEAD);                // Save the real Extruder (head) direction in X Axis
1886
+    if (da < 0) SBI(dm, X_HEAD);                // Save the toolhead's true direction in X
1887 1887
     if (db < 0) SBI(dm, Y_HEAD);                // ...and Y
1888 1888
     if (dc < 0) SBI(dm, Z_AXIS);
1889 1889
     if (da + db < 0) SBI(dm, A_AXIS);           // Motor A direction
1890 1890
     if (CORESIGN(da - db) < 0) SBI(dm, B_AXIS); // Motor B direction
1891 1891
   #elif CORE_IS_XZ
1892
-    if (da < 0) SBI(dm, X_HEAD);                // Save the real Extruder (head) direction in X Axis
1892
+    if (da < 0) SBI(dm, X_HEAD);                // Save the toolhead's true direction in X
1893 1893
     if (db < 0) SBI(dm, Y_AXIS);
1894 1894
     if (dc < 0) SBI(dm, Z_HEAD);                // ...and Z
1895 1895
     if (da + dc < 0) SBI(dm, A_AXIS);           // Motor A direction
1896 1896
     if (CORESIGN(da - dc) < 0) SBI(dm, C_AXIS); // Motor C direction
1897 1897
   #elif CORE_IS_YZ
1898 1898
     if (da < 0) SBI(dm, X_AXIS);
1899
-    if (db < 0) SBI(dm, Y_HEAD);                // Save the real Extruder (head) direction in Y Axis
1899
+    if (db < 0) SBI(dm, Y_HEAD);                // Save the toolhead's true direction in Y
1900 1900
     if (dc < 0) SBI(dm, Z_HEAD);                // ...and Z
1901 1901
     if (db + dc < 0) SBI(dm, B_AXIS);           // Motor B direction
1902 1902
     if (CORESIGN(db - dc) < 0) SBI(dm, C_AXIS); // Motor C direction
1903 1903
   #elif ENABLED(MARKFORGED_XY)
1904
-    if (da < 0) SBI(dm, X_HEAD);                // Save the real Extruder (head) direction in X Axis
1904
+    if (da < 0) SBI(dm, X_HEAD);                // Save the toolhead's true direction in X
1905 1905
     if (db < 0) SBI(dm, Y_HEAD);                // ...and Y
1906 1906
     if (dc < 0) SBI(dm, Z_AXIS);
1907 1907
     if (da + db < 0) SBI(dm, A_AXIS);           // Motor A direction
1908 1908
     if (db < 0) SBI(dm, B_AXIS);                // Motor B direction
1909 1909
   #else
1910
-    if (da < 0) SBI(dm, X_AXIS);
1911
-    if (db < 0) SBI(dm, Y_AXIS);
1912
-    if (dc < 0) SBI(dm, Z_AXIS);
1910
+    LINEAR_AXIS_CODE(
1911
+      if (da < 0) SBI(dm, X_AXIS),
1912
+      if (db < 0) SBI(dm, Y_AXIS),
1913
+      if (dc < 0) SBI(dm, Z_AXIS)
1914
+    );
1915
+  #endif
1916
+  #if HAS_EXTRUDERS
1917
+    if (de < 0) SBI(dm, E_AXIS);
1913 1918
   #endif
1914
-  if (de < 0) SBI(dm, E_AXIS);
1915 1919
 
1916 1920
   #if HAS_EXTRUDERS
1917 1921
     const float esteps_float = de * e_factor[extruder];
@@ -1947,7 +1951,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
1947 1951
     block->steps.set(ABS(da), ABS(db), ABS(dc));
1948 1952
   #else
1949 1953
     // default non-h-bot planning
1950
-    block->steps.set(ABS(da), ABS(db), ABS(dc));
1954
+    block->steps.set(LINEAR_AXIS_LIST(ABS(da), ABS(db), ABS(dc)));
1951 1955
   #endif
1952 1956
 
1953 1957
   /**
@@ -1990,41 +1994,51 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
1990 1994
     steps_dist_mm.a      = (da - db) * steps_to_mm[A_AXIS];
1991 1995
     steps_dist_mm.b      = db * steps_to_mm[B_AXIS];
1992 1996
   #else
1993
-    steps_dist_mm.a = da * steps_to_mm[A_AXIS];
1994
-    steps_dist_mm.b = db * steps_to_mm[B_AXIS];
1995
-    steps_dist_mm.c = dc * steps_to_mm[C_AXIS];
1997
+    LINEAR_AXIS_CODE(
1998
+      steps_dist_mm.a = da * steps_to_mm[A_AXIS],
1999
+      steps_dist_mm.b = db * steps_to_mm[B_AXIS],
2000
+      steps_dist_mm.c = dc * steps_to_mm[C_AXIS]
2001
+    );
1996 2002
   #endif
1997 2003
 
1998 2004
   #if HAS_EXTRUDERS
1999 2005
     steps_dist_mm.e = esteps_float * steps_to_mm[E_AXIS_N(extruder)];
2000
-  #else
2001
-    steps_dist_mm.e = 0.0f;
2002 2006
   #endif
2003 2007
 
2004 2008
   TERN_(LCD_SHOW_E_TOTAL, e_move_accumulator += steps_dist_mm.e);
2005 2009
 
2006
-  if (block->steps.a < MIN_STEPS_PER_SEGMENT && block->steps.b < MIN_STEPS_PER_SEGMENT && block->steps.c < MIN_STEPS_PER_SEGMENT) {
2007
-    block->millimeters = (0
2008
-      #if HAS_EXTRUDERS
2009
-        + ABS(steps_dist_mm.e)
2010
-      #endif
2011
-    );
2010
+  if (true LINEAR_AXIS_GANG(
2011
+      && block->steps.a < MIN_STEPS_PER_SEGMENT,
2012
+      && block->steps.b < MIN_STEPS_PER_SEGMENT,
2013
+      && block->steps.c < MIN_STEPS_PER_SEGMENT
2014
+    )
2015
+  ) {
2016
+    block->millimeters = TERN0(HAS_EXTRUDERS, ABS(steps_dist_mm.e));
2012 2017
   }
2013 2018
   else {
2014 2019
     if (millimeters)
2015 2020
       block->millimeters = millimeters;
2016
-    else
2021
+    else {
2017 2022
       block->millimeters = SQRT(
2018 2023
         #if EITHER(CORE_IS_XY, MARKFORGED_XY)
2019
-          sq(steps_dist_mm.head.x) + sq(steps_dist_mm.head.y) + sq(steps_dist_mm.z)
2024
+          LINEAR_AXIS_GANG(
2025
+            sq(steps_dist_mm.head.x), + sq(steps_dist_mm.head.y), + sq(steps_dist_mm.z)
2026
+          )
2020 2027
         #elif CORE_IS_XZ
2021
-          sq(steps_dist_mm.head.x) + sq(steps_dist_mm.y) + sq(steps_dist_mm.head.z)
2028
+          LINEAR_AXIS_GANG(
2029
+            sq(steps_dist_mm.head.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.head.z)
2030
+          )
2022 2031
         #elif CORE_IS_YZ
2023
-          sq(steps_dist_mm.x) + sq(steps_dist_mm.head.y) + sq(steps_dist_mm.head.z)
2032
+          LINEAR_AXIS_GANG(
2033
+            sq(steps_dist_mm.x), + sq(steps_dist_mm.head.y), + sq(steps_dist_mm.head.z)
2034
+          )
2024 2035
         #else
2025
-          sq(steps_dist_mm.x) + sq(steps_dist_mm.y) + sq(steps_dist_mm.z)
2036
+          LINEAR_AXIS_GANG(
2037
+            sq(steps_dist_mm.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.z)
2038
+          )
2026 2039
         #endif
2027 2040
       );
2041
+    }
2028 2042
 
2029 2043
     /**
2030 2044
      * At this point at least one of the axes has more steps than
@@ -2038,11 +2052,11 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
2038 2052
     TERN_(BACKLASH_COMPENSATION, backlash.add_correction_steps(da, db, dc, dm, block));
2039 2053
   }
2040 2054
 
2041
-  #if HAS_EXTRUDERS
2042
-    block->steps.e = esteps;
2043
-  #endif
2055
+  TERN_(HAS_EXTRUDERS, block->steps.e = esteps);
2044 2056
 
2045
-  block->step_event_count = _MAX(block->steps.a, block->steps.b, block->steps.c, esteps);
2057
+  block->step_event_count = _MAX(LOGICAL_AXIS_LIST(
2058
+    esteps, block->steps.a, block->steps.b, block->steps.c
2059
+  ));
2046 2060
 
2047 2061
   // Bail if this is a zero-length block
2048 2062
   if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return false;
@@ -2065,8 +2079,11 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
2065 2079
   #endif
2066 2080
 
2067 2081
   #if ENABLED(AUTO_POWER_CONTROL)
2068
-    if (block->steps.x || block->steps.y || block->steps.z)
2069
-      powerManager.power_on();
2082
+    if (LINEAR_AXIS_GANG(
2083
+         block->steps.x,
2084
+      || block->steps.y,
2085
+      || block->steps.z
2086
+    )) powerManager.power_on();
2070 2087
   #endif
2071 2088
 
2072 2089
   // Enable active axes
@@ -2091,11 +2108,11 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
2091 2108
     }
2092 2109
     if (block->steps.x) ENABLE_AXIS_X();
2093 2110
   #else
2094
-    if (block->steps.x) ENABLE_AXIS_X();
2095
-    if (block->steps.y) ENABLE_AXIS_Y();
2096
-    #if DISABLED(Z_LATE_ENABLE)
2097
-      if (block->steps.z) ENABLE_AXIS_Z();
2098
-    #endif
2111
+    LINEAR_AXIS_CODE(
2112
+      if (block->steps.x) ENABLE_AXIS_X(),
2113
+      if (block->steps.y) ENABLE_AXIS_Y(),
2114
+      if (TERN(Z_LATE_ENABLE, 0, block->steps.z)) ENABLE_AXIS_Z()
2115
+    );
2099 2116
   #endif
2100 2117
 
2101 2118
   // Enable extruder(s)
@@ -2281,7 +2298,9 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
2281 2298
   // Compute and limit the acceleration rate for the trapezoid generator.
2282 2299
   const float steps_per_mm = block->step_event_count * inverse_millimeters;
2283 2300
   uint32_t accel;
2284
-  if (!block->steps.a && !block->steps.b && !block->steps.c) {    // Is this a retract / recover move?
2301
+  if (LINEAR_AXIS_GANG(
2302
+    !block->steps.a, && !block->steps.b, && !block->steps.c
2303
+  )) {                                                            // Is this a retract / recover move?
2285 2304
     accel = CEIL(settings.retract_acceleration * steps_per_mm);   // Convert to: acceleration steps/sec^2
2286 2305
     TERN_(LIN_ADVANCE, block->use_advance_lead = false);          // No linear advance for simple retract/recover
2287 2306
   }
@@ -2346,16 +2365,20 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
2346 2365
 
2347 2366
     // Limit acceleration per axis
2348 2367
     if (block->step_event_count <= acceleration_long_cutoff) {
2349
-      LIMIT_ACCEL_LONG(A_AXIS, 0);
2350
-      LIMIT_ACCEL_LONG(B_AXIS, 0);
2351
-      LIMIT_ACCEL_LONG(C_AXIS, 0);
2352
-      LIMIT_ACCEL_LONG(E_AXIS, E_INDEX_N(extruder));
2368
+      LOGICAL_AXIS_CODE(
2369
+        LIMIT_ACCEL_LONG(E_AXIS, E_INDEX_N(extruder)),
2370
+        LIMIT_ACCEL_LONG(A_AXIS, 0),
2371
+        LIMIT_ACCEL_LONG(B_AXIS, 0),
2372
+        LIMIT_ACCEL_LONG(C_AXIS, 0)
2373
+      );
2353 2374
     }
2354 2375
     else {
2355
-      LIMIT_ACCEL_FLOAT(A_AXIS, 0);
2356
-      LIMIT_ACCEL_FLOAT(B_AXIS, 0);
2357
-      LIMIT_ACCEL_FLOAT(C_AXIS, 0);
2358
-      LIMIT_ACCEL_FLOAT(E_AXIS, E_INDEX_N(extruder));
2376
+      LOGICAL_AXIS_CODE(
2377
+        LIMIT_ACCEL_FLOAT(E_AXIS, E_INDEX_N(extruder)),
2378
+        LIMIT_ACCEL_FLOAT(A_AXIS, 0),
2379
+        LIMIT_ACCEL_FLOAT(B_AXIS, 0),
2380
+        LIMIT_ACCEL_FLOAT(C_AXIS, 0)
2381
+      );
2359 2382
     }
2360 2383
   }
2361 2384
   block->acceleration_steps_per_s2 = accel;
@@ -2419,7 +2442,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
2419 2442
       #if HAS_DIST_MM_ARG
2420 2443
         cart_dist_mm
2421 2444
       #else
2422
-        { steps_dist_mm.x, steps_dist_mm.y, steps_dist_mm.z, steps_dist_mm.e }
2445
+        LOGICAL_AXIS_ARRAY(steps_dist_mm.e, steps_dist_mm.x, steps_dist_mm.y, steps_dist_mm.z)
2423 2446
       #endif
2424 2447
     ;
2425 2448
 
@@ -2438,8 +2461,12 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
2438 2461
     if (moves_queued && !UNEAR_ZERO(previous_nominal_speed_sqr)) {
2439 2462
       // Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
2440 2463
       // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
2441
-      float junction_cos_theta = (-prev_unit_vec.x * unit_vec.x) + (-prev_unit_vec.y * unit_vec.y)
2442
-                               + (-prev_unit_vec.z * unit_vec.z) + (-prev_unit_vec.e * unit_vec.e);
2464
+      float junction_cos_theta = LOGICAL_AXIS_GANG(
2465
+                                 + (-prev_unit_vec.e * unit_vec.e),
2466
+                                   (-prev_unit_vec.x * unit_vec.x),
2467
+                                 + (-prev_unit_vec.y * unit_vec.y),
2468
+                                 + (-prev_unit_vec.z * unit_vec.z)
2469
+                               );
2443 2470
 
2444 2471
       // NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
2445 2472
       if (junction_cos_theta > 0.999999f) {
@@ -2754,7 +2781,8 @@ void Planner::buffer_sync_block(TERN_(LASER_SYNCHRONOUS_M106_M107, uint8_t sync_
2754 2781
  *
2755 2782
  * Return 'false' if no segment was queued due to cleaning, cold extrusion, full queue, etc.
2756 2783
  */
2757
-bool Planner::buffer_segment(const_float_t a, const_float_t b, const_float_t c, const_float_t e
2784
+bool Planner::buffer_segment(
2785
+  LOGICAL_AXIS_LIST(const_float_t e, const_float_t a, const_float_t b, const_float_t c)
2758 2786
   OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
2759 2787
   , const_feedRate_t fr_mm_s, const uint8_t extruder, const_float_t millimeters/*=0.0*/
2760 2788
 ) {
@@ -2773,21 +2801,25 @@ bool Planner::buffer_segment(const_float_t a, const_float_t b, const_float_t c,
2773 2801
   // The target position of the tool in absolute steps
2774 2802
   // Calculate target position in absolute steps
2775 2803
   const abce_long_t target = {
2776
-    int32_t(LROUND(a * settings.axis_steps_per_mm[A_AXIS])),
2777
-    int32_t(LROUND(b * settings.axis_steps_per_mm[B_AXIS])),
2778
-    int32_t(LROUND(c * settings.axis_steps_per_mm[C_AXIS])),
2779
-    int32_t(LROUND(e * settings.axis_steps_per_mm[E_AXIS_N(extruder)]))
2804
+     LOGICAL_AXIS_LIST(
2805
+      int32_t(LROUND(e * settings.axis_steps_per_mm[E_AXIS_N(extruder)])),
2806
+      int32_t(LROUND(a * settings.axis_steps_per_mm[A_AXIS])),
2807
+      int32_t(LROUND(b * settings.axis_steps_per_mm[B_AXIS])),
2808
+      int32_t(LROUND(c * settings.axis_steps_per_mm[C_AXIS]))
2809
+    )
2780 2810
   };
2781 2811
 
2782 2812
   #if HAS_POSITION_FLOAT
2783
-    const xyze_pos_t target_float = { a, b, c, e };
2813
+    const xyze_pos_t target_float = LOGICAL_AXIS_ARRAY(e, a, b, c);
2784 2814
   #endif
2785 2815
 
2786
-  // DRYRUN prevents E moves from taking place
2787
-  if (DEBUGGING(DRYRUN) || TERN0(CANCEL_OBJECTS, cancelable.skipping)) {
2788
-    position.e = target.e;
2789
-    TERN_(HAS_POSITION_FLOAT, position_float.e = e);
2790
-  }
2816
+  #if HAS_EXTRUDERS
2817
+    // DRYRUN prevents E moves from taking place
2818
+    if (DEBUGGING(DRYRUN) || TERN0(CANCEL_OBJECTS, cancelable.skipping)) {
2819
+      position.e = target.e;
2820
+      TERN_(HAS_POSITION_FLOAT, position_float.e = e);
2821
+    }
2822
+  #endif
2791 2823
 
2792 2824
   /* <-- add a slash to enable
2793 2825
     SERIAL_ECHOPAIR("  buffer_segment FR:", fr_mm_s);
@@ -2846,10 +2878,12 @@ bool Planner::buffer_segment(const_float_t a, const_float_t b, const_float_t c,
2846 2878
  *  millimeters  - the length of the movement, if known
2847 2879
  *  inv_duration - the reciprocal if the duration of the movement, if known (kinematic only if feeedrate scaling is enabled)
2848 2880
  */
2849
-bool Planner::buffer_line(const_float_t rx, const_float_t ry, const_float_t rz, const_float_t e, const_feedRate_t fr_mm_s, const uint8_t extruder, const float millimeters
2881
+bool Planner::buffer_line(
2882
+  LOGICAL_AXIS_LIST(const_float_t e, const_float_t rx, const_float_t ry, const_float_t rz)
2883
+  , const feedRate_t &fr_mm_s, const uint8_t extruder, const float millimeters
2850 2884
   OPTARG(SCARA_FEEDRATE_SCALING, const_float_t inv_duration)
2851 2885
 ) {
2852
-  xyze_pos_t machine = { rx, ry, rz, e };
2886
+  xyze_pos_t machine = LOGICAL_AXIS_ARRAY(e, rx, ry, rz);
2853 2887
   TERN_(HAS_POSITION_MODIFIERS, apply_modifiers(machine));
2854 2888
 
2855 2889
   #if IS_KINEMATIC
@@ -2912,16 +2946,12 @@ bool Planner::buffer_line(const_float_t rx, const_float_t ry, const_float_t rz,
2912 2946
       FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
2913 2947
     #endif
2914 2948
 
2915
-    #if HAS_MULTI_EXTRUDER
2916
-      block->extruder = extruder;
2917
-    #endif
2949
+    TERN_(HAS_MULTI_EXTRUDER, block->extruder = extruder);
2918 2950
 
2919 2951
     block->page_idx = page_idx;
2920 2952
 
2921 2953
     block->step_event_count = num_steps;
2922
-    block->initial_rate =
2923
-      block->final_rate =
2924
-      block->nominal_rate = last_page_step_rate; // steps/s
2954
+    block->initial_rate = block->final_rate = block->nominal_rate = last_page_step_rate; // steps/s
2925 2955
 
2926 2956
     block->accelerate_until = 0;
2927 2957
     block->decelerate_after = block->step_event_count;
@@ -2965,13 +2995,19 @@ bool Planner::buffer_line(const_float_t rx, const_float_t ry, const_float_t rz,
2965 2995
  * The provided ABC position is in machine units.
2966 2996
  */
2967 2997
 
2968
-void Planner::set_machine_position_mm(const_float_t a, const_float_t b, const_float_t c, const_float_t e) {
2998
+void Planner::set_machine_position_mm(
2999
+  LOGICAL_AXIS_LIST(const_float_t e, const_float_t a, const_float_t b, const_float_t c)
3000
+) {
2969 3001
   TERN_(DISTINCT_E_FACTORS, last_extruder = active_extruder);
2970
-  TERN_(HAS_POSITION_FLOAT, position_float.set(a, b, c, e));
2971
-  position.set(LROUND(a * settings.axis_steps_per_mm[A_AXIS]),
2972
-               LROUND(b * settings.axis_steps_per_mm[B_AXIS]),
2973
-               LROUND(c * settings.axis_steps_per_mm[C_AXIS]),
2974
-               LROUND(e * settings.axis_steps_per_mm[E_AXIS_N(active_extruder)]));
3002
+  TERN_(HAS_POSITION_FLOAT, position_float.set(LOGICAL_AXIS_LIST(e, a, b, c)));
3003
+  position.set(
3004
+    LOGICAL_AXIS_LIST(
3005
+      LROUND(e * settings.axis_steps_per_mm[E_AXIS_N(active_extruder)]),
3006
+      LROUND(a * settings.axis_steps_per_mm[A_AXIS]),
3007
+      LROUND(b * settings.axis_steps_per_mm[B_AXIS]),
3008
+      LROUND(c * settings.axis_steps_per_mm[C_AXIS])
3009
+    )
3010
+  );
2975 3011
   if (has_blocks_queued()) {
2976 3012
     //previous_nominal_speed_sqr = 0.0; // Reset planner junction speeds. Assume start from rest.
2977 3013
     //previous_speed.reset();
@@ -2981,11 +3017,11 @@ void Planner::set_machine_position_mm(const_float_t a, const_float_t b, const_fl
2981 3017
     stepper.set_position(position);
2982 3018
 }
2983 3019
 
2984
-void Planner::set_position_mm(const_float_t rx, const_float_t ry, const_float_t rz, const_float_t e) {
2985
-  xyze_pos_t machine = { rx, ry, rz, e };
2986
-  #if HAS_POSITION_MODIFIERS
2987
-    apply_modifiers(machine, true);
2988
-  #endif
3020
+void Planner::set_position_mm(
3021
+  LOGICAL_AXIS_LIST(const_float_t e, const_float_t rx, const_float_t ry, const_float_t rz)
3022
+) {
3023
+  xyze_pos_t machine = LOGICAL_AXIS_ARRAY(e, rx, ry, rz);
3024
+  TERN_(HAS_POSITION_MODIFIERS, apply_modifiers(machine, true));
2989 3025
   #if IS_KINEMATIC
2990 3026
     position_cart.set(rx, ry, rz, e);
2991 3027
     inverse_kinematics(machine);
@@ -2995,23 +3031,27 @@ void Planner::set_position_mm(const_float_t rx, const_float_t ry, const_float_t
2995 3031
   #endif
2996 3032
 }
2997 3033
 
2998
-/**
2999
- * Setters for planner position (also setting stepper position).
3000
- */
3001
-void Planner::set_e_position_mm(const_float_t e) {
3002
-  const uint8_t axis_index = E_AXIS_N(active_extruder);
3003
-  TERN_(DISTINCT_E_FACTORS, last_extruder = active_extruder);
3034
+#if HAS_EXTRUDERS
3004 3035
 
3005
-  const float e_new = DIFF_TERN(FWRETRACT, e, fwretract.current_retract[active_extruder]);
3006
-  position.e = LROUND(settings.axis_steps_per_mm[axis_index] * e_new);
3007
-  TERN_(HAS_POSITION_FLOAT, position_float.e = e_new);
3008
-  TERN_(IS_KINEMATIC, position_cart.e = e);
3036
+  /**
3037
+   * Setters for planner position (also setting stepper position).
3038
+   */
3039
+  void Planner::set_e_position_mm(const_float_t e) {
3040
+    const uint8_t axis_index = E_AXIS_N(active_extruder);
3041
+    TERN_(DISTINCT_E_FACTORS, last_extruder = active_extruder);
3009 3042
 
3010
-  if (has_blocks_queued())
3011
-    buffer_sync_block();
3012
-  else
3013
-    stepper.set_axis_position(E_AXIS, position.e);
3014
-}
3043
+    const float e_new = DIFF_TERN(FWRETRACT, e, fwretract.current_retract[active_extruder]);
3044
+    position.e = LROUND(settings.axis_steps_per_mm[axis_index] * e_new);
3045
+    TERN_(HAS_POSITION_FLOAT, position_float.e = e_new);
3046
+    TERN_(IS_KINEMATIC, position_cart.e = e);
3047
+
3048
+    if (has_blocks_queued())
3049
+      buffer_sync_block();
3050
+    else
3051
+      stepper.set_axis_position(E_AXIS, position.e);
3052
+  }
3053
+
3054
+#endif
3015 3055
 
3016 3056
 // Recalculate the steps/s^2 acceleration rates, based on the mm/s^2
3017 3057
 void Planner::reset_acceleration_rates() {
@@ -3041,11 +3081,11 @@ void Planner::refresh_positioning() {
3041 3081
 
3042 3082
 // Apply limits to a variable and give a warning if the value was out of range
3043 3083
 inline void limit_and_warn(float &val, const uint8_t axis, PGM_P const setting_name, const xyze_float_t &max_limit) {
3044
-  const uint8_t lim_axis = axis > E_AXIS ? E_AXIS : axis;
3084
+  const uint8_t lim_axis = TERN_(HAS_EXTRUDERS, axis > E_AXIS ? E_AXIS :) axis;
3045 3085
   const float before = val;
3046 3086
   LIMIT(val, 0.1, max_limit[lim_axis]);
3047 3087
   if (before != val) {
3048
-    SERIAL_CHAR(axis_codes[lim_axis]);
3088
+    SERIAL_CHAR(AXIS_CHAR(lim_axis));
3049 3089
     SERIAL_ECHOPGM(" Max ");
3050 3090
     SERIAL_ECHOPGM_P(setting_name);
3051 3091
     SERIAL_ECHOLNPAIR(" limited to ", val);

+ 33
- 17
Marlin/src/module/planner.h View File

@@ -76,7 +76,7 @@
76 76
 // Feedrate for manual moves
77 77
 #ifdef MANUAL_FEEDRATE
78 78
   constexpr xyze_feedrate_t _mf = MANUAL_FEEDRATE,
79
-                            manual_feedrate_mm_s { _mf.x / 60.0f, _mf.y / 60.0f, _mf.z / 60.0f, _mf.e / 60.0f };
79
+           manual_feedrate_mm_s = LOGICAL_AXIS_ARRAY(_mf.e / 60.0f, _mf.x / 60.0f, _mf.y / 60.0f, _mf.z / 60.0f);
80 80
 #endif
81 81
 
82 82
 #if IS_KINEMATIC && HAS_JUNCTION_DEVIATION
@@ -758,7 +758,8 @@ class Planner {
758 758
      *  extruder    - target extruder
759 759
      *  millimeters - the length of the movement, if known
760 760
      */
761
-    static bool buffer_segment(const_float_t a, const_float_t b, const_float_t c, const_float_t e
761
+    static bool buffer_segment(
762
+      LOGICAL_AXIS_LIST(const_float_t e, const_float_t a, const_float_t b, const_float_t c)
762 763
       OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
763 764
       , const_feedRate_t fr_mm_s, const uint8_t extruder, const_float_t millimeters=0.0
764 765
     );
@@ -767,9 +768,11 @@ class Planner {
767 768
       OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
768 769
       , const_feedRate_t fr_mm_s, const uint8_t extruder, const_float_t millimeters=0.0
769 770
     ) {
770
-      return buffer_segment(abce.a, abce.b, abce.c, abce.e
771
+      return buffer_segment(
772
+        LOGICAL_AXIS_LIST(abce.e, abce.a, abce.b, abce.c)
771 773
         OPTARG(HAS_DIST_MM_ARG, cart_dist_mm)
772
-        , fr_mm_s, extruder, millimeters);
774
+        , fr_mm_s, extruder, millimeters
775
+      );
773 776
     }
774 777
 
775 778
   public:
@@ -785,14 +788,18 @@ class Planner {
785 788
      *  millimeters  - the length of the movement, if known
786 789
      *  inv_duration - the reciprocal if the duration of the movement, if known (kinematic only if feeedrate scaling is enabled)
787 790
      */
788
-    static bool buffer_line(const_float_t rx, const_float_t ry, const_float_t rz, const_float_t e, const_feedRate_t fr_mm_s, const uint8_t extruder, const float millimeters=0.0
791
+    static bool buffer_line(
792
+      LOGICAL_AXIS_LIST(const_float_t e, const_float_t rx, const_float_t ry, const_float_t rz)
793
+      , const feedRate_t &fr_mm_s, const uint8_t extruder, const float millimeters=0.0
789 794
       OPTARG(SCARA_FEEDRATE_SCALING, const_float_t inv_duration=0.0)
790 795
     );
791 796
 
792 797
     FORCE_INLINE static bool buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s, const uint8_t extruder, const float millimeters=0.0
793 798
       OPTARG(SCARA_FEEDRATE_SCALING, const_float_t inv_duration=0.0)
794 799
     ) {
795
-      return buffer_line(cart.x, cart.y, cart.z, cart.e, fr_mm_s, extruder, millimeters
800
+      return buffer_line(
801
+        LOGICAL_AXIS_LIST(cart.e, cart.x, cart.y, cart.z)
802
+        , fr_mm_s, extruder, millimeters
796 803
         OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
797 804
       );
798 805
     }
@@ -814,9 +821,16 @@ class Planner {
814 821
      *
815 822
      * Clears previous speed values.
816 823
      */
817
-    static void set_position_mm(const_float_t rx, const_float_t ry, const_float_t rz, const_float_t e);
818
-    FORCE_INLINE static void set_position_mm(const xyze_pos_t &cart) { set_position_mm(cart.x, cart.y, cart.z, cart.e); }
819
-    static void set_e_position_mm(const_float_t e);
824
+    static void set_position_mm(
825
+      LOGICAL_AXIS_LIST(const_float_t e, const_float_t rx, const_float_t ry, const_float_t rz)
826
+    );
827
+    FORCE_INLINE static void set_position_mm(const xyze_pos_t &cart) {
828
+      set_position_mm(LOGICAL_AXIS_LIST(cart.e, cart.x, cart.y, cart.z, cart.i, cart.j, cart.k));
829
+    }
830
+
831
+    #if HAS_EXTRUDERS
832
+      static void set_e_position_mm(const_float_t e);
833
+    #endif
820 834
 
821 835
     /**
822 836
      * Set the planner.position and individual stepper positions.
@@ -824,8 +838,12 @@ class Planner {
824 838
      * The supplied position is in machine space, and no additional
825 839
      * conversions are applied.
826 840
      */
827
-    static void set_machine_position_mm(const_float_t a, const_float_t b, const_float_t c, const_float_t e);
828
-    FORCE_INLINE static void set_machine_position_mm(const abce_pos_t &abce) { set_machine_position_mm(abce.a, abce.b, abce.c, abce.e); }
841
+    static void set_machine_position_mm(
842
+      LOGICAL_AXIS_LIST(const_float_t e, const_float_t a, const_float_t b, const_float_t c)
843
+    );
844
+    FORCE_INLINE static void set_machine_position_mm(const abce_pos_t &abce) {
845
+      set_machine_position_mm(LOGICAL_AXIS_LIST(abce.e, abce.a, abce.b, abce.c));
846
+    }
829 847
 
830 848
     /**
831 849
      * Get an axis position according to stepper position(s)
@@ -834,12 +852,10 @@ class Planner {
834 852
     static float get_axis_position_mm(const AxisEnum axis);
835 853
 
836 854
     static inline abce_pos_t get_axis_positions_mm() {
837
-      const abce_pos_t out = {
838
-        get_axis_position_mm(A_AXIS),
839
-        get_axis_position_mm(B_AXIS),
840
-        get_axis_position_mm(C_AXIS),
841
-        get_axis_position_mm(E_AXIS)
842
-      };
855
+      const abce_pos_t out = LOGICAL_AXIS_ARRAY(
856
+        get_axis_position_mm(E_AXIS),
857
+        get_axis_position_mm(A_AXIS), get_axis_position_mm(B_AXIS), get_axis_position_mm(C_AXIS)
858
+      );
843 859
       return out;
844 860
     }
845 861
 

+ 26
- 20
Marlin/src/module/settings.cpp View File

@@ -168,10 +168,10 @@
168 168
   void M554_report();
169 169
 #endif
170 170
 
171
-typedef struct { uint16_t X, Y, Z, X2, Y2, Z2, Z3, Z4, E0, E1, E2, E3, E4, E5, E6, E7; } tmc_stepper_current_t;
172
-typedef struct { uint32_t X, Y, Z, X2, Y2, Z2, Z3, Z4, E0, E1, E2, E3, E4, E5, E6, E7; } tmc_hybrid_threshold_t;
173
-typedef struct {  int16_t X, Y, Z, X2, Y2, Z2, Z3, Z4;                                 } tmc_sgt_t;
174
-typedef struct {     bool X, Y, Z, X2, Y2, Z2, Z3, Z4, E0, E1, E2, E3, E4, E5, E6, E7; } tmc_stealth_enabled_t;
171
+typedef struct { uint16_t LINEAR_AXIS_LIST(X, Y, Z), X2, Y2, Z2, Z3, Z4, E0, E1, E2, E3, E4, E5, E6, E7; } tmc_stepper_current_t;
172
+typedef struct { uint32_t LINEAR_AXIS_LIST(X, Y, Z), X2, Y2, Z2, Z3, Z4, E0, E1, E2, E3, E4, E5, E6, E7; } tmc_hybrid_threshold_t;
173
+typedef struct {  int16_t LINEAR_AXIS_LIST(X, Y, Z), X2, Y2, Z2, Z3, Z4;                                 } tmc_sgt_t;
174
+typedef struct {     bool LINEAR_AXIS_LIST(X, Y, Z), X2, Y2, Z2, Z3, Z4, E0, E1, E2, E3, E4, E5, E6, E7; } tmc_stealth_enabled_t;
175 175
 
176 176
 // Limit an index to an array size
177 177
 #define ALIM(I,ARR) _MIN(I, (signed)COUNT(ARR) - 1)
@@ -654,7 +654,7 @@ void MarlinSettings::postprocess() {
654 654
           EEPROM_WRITE(dummyf);
655 655
         #endif
656 656
       #else
657
-        const xyze_pos_t planner_max_jerk = { 10, 10, 0.4, float(DEFAULT_EJERK) };
657
+        const xyze_pos_t planner_max_jerk = LOGICAL_AXIS_ARRAY(float(DEFAULT_EJERK), 10, 10, 0.4);
658 658
         EEPROM_WRITE(planner_max_jerk);
659 659
       #endif
660 660
 
@@ -1188,10 +1188,10 @@ void MarlinSettings::postprocess() {
1188 1188
         #endif
1189 1189
       #else
1190 1190
         const tmc_hybrid_threshold_t tmc_hybrid_threshold = {
1191
-          .X  = 100, .Y  = 100, .Z  =   3,
1191
+          LINEAR_AXIS_LIST(.X = 100, .Y = 100, .Z = 3),
1192 1192
           .X2 = 100, .Y2 = 100, .Z2 =   3, .Z3 =   3, .Z4 = 3,
1193
-          .E0 =  30, .E1 =  30, .E2 =  30,
1194
-          .E3 =  30, .E4 =  30, .E5 =  30
1193
+          .E0 =  30, .E1 =  30, .E2 =  30, .E3 =  30,
1194
+          .E4 =  30, .E5 =  30, .E6 =  30, .E7 =  30
1195 1195
         };
1196 1196
       #endif
1197 1197
       EEPROM_WRITE(tmc_hybrid_threshold);
@@ -2604,7 +2604,7 @@ void MarlinSettings::reset() {
2604 2604
     #ifndef DEFAULT_ZJERK
2605 2605
       #define DEFAULT_ZJERK 0
2606 2606
     #endif
2607
-    planner.max_jerk.set(DEFAULT_XJERK, DEFAULT_YJERK, DEFAULT_ZJERK);
2607
+    planner.max_jerk.set(LINEAR_AXIS_LIST(DEFAULT_XJERK, DEFAULT_YJERK, DEFAULT_ZJERK));
2608 2608
     TERN_(HAS_CLASSIC_E_JERK, planner.max_jerk.e = DEFAULT_EJERK;);
2609 2609
   #endif
2610 2610
 
@@ -3142,10 +3142,12 @@ void MarlinSettings::reset() {
3142 3142
     CONFIG_ECHO_HEADING("Maximum feedrates (units/s):");
3143 3143
     CONFIG_ECHO_START();
3144 3144
     SERIAL_ECHOLNPAIR_P(
3145
-        PSTR("  M203 X"), LINEAR_UNIT(planner.settings.max_feedrate_mm_s[X_AXIS])
3146
-      , SP_Y_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Y_AXIS])
3147
-      , SP_Z_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Z_AXIS])
3148
-      #if DISABLED(DISTINCT_E_FACTORS)
3145
+      LIST_N(DOUBLE(LINEAR_AXES),
3146
+        PSTR("  M203 X"), LINEAR_UNIT(planner.settings.max_feedrate_mm_s[X_AXIS]),
3147
+        SP_Y_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Y_AXIS]),
3148
+        SP_Z_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Z_AXIS])
3149
+      )
3150
+      #if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)
3149 3151
         , SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_feedrate_mm_s[E_AXIS])
3150 3152
       #endif
3151 3153
     );
@@ -3162,10 +3164,12 @@ void MarlinSettings::reset() {
3162 3164
     CONFIG_ECHO_HEADING("Maximum Acceleration (units/s2):");
3163 3165
     CONFIG_ECHO_START();
3164 3166
     SERIAL_ECHOLNPAIR_P(
3165
-        PSTR("  M201 X"), LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[X_AXIS])
3166
-      , SP_Y_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Y_AXIS])
3167
-      , SP_Z_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Z_AXIS])
3168
-      #if DISABLED(DISTINCT_E_FACTORS)
3167
+      LIST_N(DOUBLE(LINEAR_AXES),
3168
+        PSTR("  M201 X"), LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[X_AXIS]),
3169
+        SP_Y_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Y_AXIS]),
3170
+        SP_Z_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Z_AXIS])
3171
+      )
3172
+      #if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)
3169 3173
         , SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_acceleration_mm_per_s2[E_AXIS])
3170 3174
       #endif
3171 3175
     );
@@ -3894,9 +3898,11 @@ void MarlinSettings::reset() {
3894 3898
       CONFIG_ECHO_START();
3895 3899
       SERIAL_ECHOLNPAIR_P(
3896 3900
           PSTR("  M425 F"), backlash.get_correction()
3897
-        , SP_X_STR, LINEAR_UNIT(backlash.distance_mm.x)
3898
-        , SP_Y_STR, LINEAR_UNIT(backlash.distance_mm.y)
3899
-        , SP_Z_STR, LINEAR_UNIT(backlash.distance_mm.z)
3901
+        , LIST_N(DOUBLE(LINEAR_AXES),
3902
+            SP_X_STR, LINEAR_UNIT(backlash.distance_mm.x),
3903
+            SP_Y_STR, LINEAR_UNIT(backlash.distance_mm.y),
3904
+            SP_Z_STR, LINEAR_UNIT(backlash.distance_mm.z)
3905
+          )
3900 3906
         #ifdef BACKLASH_SMOOTHING_MM
3901 3907
           , PSTR(" S"), LINEAR_UNIT(backlash.smoothing_mm)
3902 3908
         #endif

+ 34
- 22
Marlin/src/module/stepper.cpp View File

@@ -498,7 +498,7 @@ void Stepper::set_directions() {
498 498
         MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
499 499
         count_direction.e = 1;
500 500
       }
501
-    #else
501
+    #elif HAS_EXTRUDERS
502 502
       if (motor_direction(E_AXIS)) {
503 503
         REV_E_DIR(stepper_extruder);
504 504
         count_direction.e = -1;
@@ -1627,7 +1627,7 @@ void Stepper::pulse_phase_isr() {
1627 1627
           PAGE_PULSE_PREP(X);
1628 1628
           PAGE_PULSE_PREP(Y);
1629 1629
           PAGE_PULSE_PREP(Z);
1630
-          PAGE_PULSE_PREP(E);
1630
+          TERN_(HAS_EXTRUDERS, PAGE_PULSE_PREP(E));
1631 1631
 
1632 1632
           page_step_state.segment_steps++;
1633 1633
 
@@ -1660,7 +1660,7 @@ void Stepper::pulse_phase_isr() {
1660 1660
           PAGE_PULSE_PREP(X);
1661 1661
           PAGE_PULSE_PREP(Y);
1662 1662
           PAGE_PULSE_PREP(Z);
1663
-          PAGE_PULSE_PREP(E);
1663
+          TERN_(HAS_EXTRUDERS, PAGE_PULSE_PREP(E));
1664 1664
 
1665 1665
           page_step_state.segment_steps++;
1666 1666
 
@@ -2103,13 +2103,15 @@ uint32_t Stepper::block_phase_isr() {
2103 2103
       #endif
2104 2104
 
2105 2105
       uint8_t axis_bits = 0;
2106
-      if (X_MOVE_TEST) SBI(axis_bits, A_AXIS);
2107
-      if (Y_MOVE_TEST) SBI(axis_bits, B_AXIS);
2108
-      if (Z_MOVE_TEST) SBI(axis_bits, C_AXIS);
2109
-      //if (!!current_block->steps.e) SBI(axis_bits, E_AXIS);
2110
-      //if (!!current_block->steps.a) SBI(axis_bits, X_HEAD);
2111
-      //if (!!current_block->steps.b) SBI(axis_bits, Y_HEAD);
2112
-      //if (!!current_block->steps.c) SBI(axis_bits, Z_HEAD);
2106
+      LINEAR_AXIS_CODE(
2107
+        if (X_MOVE_TEST) SBI(axis_bits, A_AXIS),
2108
+        if (Y_MOVE_TEST) SBI(axis_bits, B_AXIS),
2109
+        if (Z_MOVE_TEST) SBI(axis_bits, C_AXIS)
2110
+      );
2111
+      //if (current_block->steps.e) SBI(axis_bits, E_AXIS);
2112
+      //if (current_block->steps.a) SBI(axis_bits, X_HEAD);
2113
+      //if (current_block->steps.b) SBI(axis_bits, Y_HEAD);
2114
+      //if (current_block->steps.c) SBI(axis_bits, Z_HEAD);
2113 2115
       axis_did_move = axis_bits;
2114 2116
 
2115 2117
       // No acceleration / deceleration time elapsed so far
@@ -2606,9 +2608,13 @@ void Stepper::init() {
2606 2608
   #endif
2607 2609
 
2608 2610
   // Init direction bits for first moves
2609
-  set_directions((INVERT_X_DIR ? _BV(X_AXIS) : 0)
2610
-               | (INVERT_Y_DIR ? _BV(Y_AXIS) : 0)
2611
-               | (INVERT_Z_DIR ? _BV(Z_AXIS) : 0));
2611
+  set_directions(0
2612
+    LINEAR_AXIS_GANG(
2613
+      | TERN0(INVERT_X_DIR, _BV(X_AXIS)),
2614
+      | TERN0(INVERT_Y_DIR, _BV(Y_AXIS)),
2615
+      | TERN0(INVERT_Z_DIR, _BV(Z_AXIS))
2616
+    )
2617
+  );
2612 2618
 
2613 2619
   #if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM
2614 2620
     initialized = true;
@@ -2625,7 +2631,9 @@ void Stepper::init() {
2625 2631
  * This allows get_axis_position_mm to correctly
2626 2632
  * derive the current XYZ position later on.
2627 2633
  */
2628
-void Stepper::_set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e) {
2634
+void Stepper::_set_position(
2635
+  LOGICAL_AXIS_LIST(const int32_t &e, const int32_t &a, const int32_t &b, const int32_t &c)
2636
+) {
2629 2637
   #if CORE_IS_XY
2630 2638
     // corexy positioning
2631 2639
     // these equations follow the form of the dA and dB equations on https://www.corexy.com/theory.html
@@ -2640,9 +2648,9 @@ void Stepper::_set_position(const int32_t &a, const int32_t &b, const int32_t &c
2640 2648
     count_position.set(a - b, b, c);
2641 2649
   #else
2642 2650
     // default non-h-bot planning
2643
-    count_position.set(a, b, c);
2651
+    count_position.set(LINEAR_AXIS_LIST(a, b, c));
2644 2652
   #endif
2645
-  count_position.e = e;
2653
+  TERN_(HAS_EXTRUDERS, count_position.e = e);
2646 2654
 }
2647 2655
 
2648 2656
 /**
@@ -2665,10 +2673,13 @@ int32_t Stepper::position(const AxisEnum axis) {
2665 2673
 }
2666 2674
 
2667 2675
 // Set the current position in steps
2668
-void Stepper::set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e) {
2676
+//TODO: Test for LINEAR_AXES >= 4
2677
+void Stepper::set_position(
2678
+  LOGICAL_AXIS_LIST(const int32_t &e, const int32_t &a, const int32_t &b, const int32_t &c)
2679
+) {
2669 2680
   planner.synchronize();
2670 2681
   const bool was_enabled = suspend();
2671
-  _set_position(a, b, c, e);
2682
+  _set_position(LOGICAL_AXIS_LIST(e, a, b, c));
2672 2683
   if (was_enabled) wake_up();
2673 2684
 }
2674 2685
 
@@ -2743,10 +2754,11 @@ void Stepper::report_a_position(const xyz_long_t &pos) {
2743 2754
     SERIAL_ECHOPAIR_P(PSTR(STR_COUNT_X), pos.x, SP_Y_LBL, pos.y);
2744 2755
   #endif
2745 2756
   #if ANY(CORE_IS_XZ, CORE_IS_YZ, DELTA)
2746
-    SERIAL_ECHOLNPAIR(" C:", pos.z);
2747
-  #else
2748
-    SERIAL_ECHOLNPAIR_P(SP_Z_LBL, pos.z);
2757
+    SERIAL_ECHOPAIR(" C:", pos.z);
2758
+  #elif LINEAR_AXES >= 3
2759
+    SERIAL_ECHOPAIR_P(SP_Z_LBL, pos.z);
2749 2760
   #endif
2761
+  SERIAL_EOL();
2750 2762
 }
2751 2763
 
2752 2764
 void Stepper::report_positions() {
@@ -2903,7 +2915,7 @@ void Stepper::report_positions() {
2903 2915
 
2904 2916
           DIR_WAIT_BEFORE();
2905 2917
 
2906
-          const xyz_byte_t old_dir = { X_DIR_READ(), Y_DIR_READ(), Z_DIR_READ() };
2918
+          const xyz_byte_t old_dir = LINEAR_AXIS_ARRAY(X_DIR_READ(), Y_DIR_READ(), Z_DIR_READ());
2907 2919
 
2908 2920
           X_DIR_WRITE(INVERT_X_DIR ^ z_direction);
2909 2921
           Y_DIR_WRITE(INVERT_Y_DIR ^ z_direction);

+ 12
- 4
Marlin/src/module/stepper.h View File

@@ -433,8 +433,12 @@ class Stepper {
433 433
     static int32_t position(const AxisEnum axis);
434 434
 
435 435
     // Set the current position in steps
436
-    static void set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e);
437
-    static inline void set_position(const xyze_long_t &abce) { set_position(abce.a, abce.b, abce.c, abce.e); }
436
+    static void set_position(
437
+      LOGICAL_AXIS_LIST(const int32_t &e, const int32_t &a, const int32_t &b, const int32_t &c)
438
+    );
439
+    static inline void set_position(const xyze_long_t &abce) {
440
+      set_position(LOGICAL_AXIS_LIST(abce.e, abce.a, abce.b, abce.c));
441
+    }
438 442
     static void set_axis_position(const AxisEnum a, const int32_t &v);
439 443
 
440 444
     // Report the positions of the steppers, in steps
@@ -530,8 +534,12 @@ class Stepper {
530 534
   private:
531 535
 
532 536
     // Set the current position in steps
533
-    static void _set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e);
534
-    FORCE_INLINE static void _set_position(const abce_long_t &spos) { _set_position(spos.a, spos.b, spos.c, spos.e); }
537
+    static void _set_position(
538
+      LOGICAL_AXIS_LIST(const int32_t &e, const int32_t &a, const int32_t &b, const int32_t &c)
539
+    );
540
+    FORCE_INLINE static void _set_position(const abce_long_t &spos) {
541
+      _set_position(LOGICAL_AXIS_LIST(spos.e, spos.a, spos.b, spos.c));
542
+    }
535 543
 
536 544
     FORCE_INLINE static uint32_t calc_timer_interval(uint32_t step_rate, uint8_t *loops) {
537 545
       uint32_t timer;

+ 15
- 8
Marlin/src/module/stepper/trinamic.cpp View File

@@ -35,7 +35,9 @@
35 35
 #include <HardwareSerial.h>
36 36
 #include <SPI.h>
37 37
 
38
-enum StealthIndex : uint8_t { STEALTH_AXIS_XY, STEALTH_AXIS_Z, STEALTH_AXIS_E };
38
+enum StealthIndex : uint8_t {
39
+  LOGICAL_AXIS_LIST(STEALTH_AXIS_E, STEALTH_AXIS_X, STEALTH_AXIS_Y, STEALTH_AXIS_Z)
40
+};
39 41
 #define TMC_INIT(ST, STEALTH_INDEX) tmc_init(stepper##ST, ST##_CURRENT, ST##_MICROSTEPS, ST##_HYBRID_THRESHOLD, stealthchop_by_axis[STEALTH_INDEX], chopper_timing_##ST, ST##_INTERPOLATE)
40 42
 
41 43
 //   IC = TMC model number
@@ -351,7 +353,7 @@ enum StealthIndex : uint8_t { STEALTH_AXIS_XY, STEALTH_AXIS_Z, STEALTH_AXIS_E };
351 353
     #endif
352 354
   #endif
353 355
 
354
-  enum TMCAxis : uint8_t { X, Y, Z, X2, Y2, Z2, Z3, Z4, E0, E1, E2, E3, E4, E5, E6, E7, TOTAL };
356
+  enum TMCAxis : uint8_t { LINEAR_AXIS_LIST(X, Y, Z), X2, Y2, Z2, Z3, Z4, E0, E1, E2, E3, E4, E5, E6, E7, TOTAL };
355 357
 
356 358
   void tmc_serial_begin() {
357 359
     #if HAS_TMC_HW_SERIAL
@@ -716,19 +718,24 @@ void restore_trinamic_drivers() {
716 718
 }
717 719
 
718 720
 void reset_trinamic_drivers() {
719
-  static constexpr bool stealthchop_by_axis[] = { ENABLED(STEALTHCHOP_XY), ENABLED(STEALTHCHOP_Z), ENABLED(STEALTHCHOP_E) };
721
+  static constexpr bool stealthchop_by_axis[] = LOGICAL_AXIS_ARRAY(
722
+    ENABLED(STEALTHCHOP_E),
723
+    ENABLED(STEALTHCHOP_XY),
724
+    ENABLED(STEALTHCHOP_XY),
725
+    ENABLED(STEALTHCHOP_Z)
726
+  );
720 727
 
721 728
   #if AXIS_IS_TMC(X)
722
-    TMC_INIT(X, STEALTH_AXIS_XY);
729
+    TMC_INIT(X, STEALTH_AXIS_X);
723 730
   #endif
724 731
   #if AXIS_IS_TMC(X2)
725
-    TMC_INIT(X2, STEALTH_AXIS_XY);
732
+    TMC_INIT(X2, STEALTH_AXIS_X);
726 733
   #endif
727 734
   #if AXIS_IS_TMC(Y)
728
-    TMC_INIT(Y, STEALTH_AXIS_XY);
735
+    TMC_INIT(Y, STEALTH_AXIS_Y);
729 736
   #endif
730 737
   #if AXIS_IS_TMC(Y2)
731
-    TMC_INIT(Y2, STEALTH_AXIS_XY);
738
+    TMC_INIT(Y2, STEALTH_AXIS_Y);
732 739
   #endif
733 740
   #if AXIS_IS_TMC(Z)
734 741
     TMC_INIT(Z, STEALTH_AXIS_Z);
@@ -792,7 +799,7 @@ void reset_trinamic_drivers() {
792 799
         stepperZ4.homing_threshold(CAT(TERN(Z4_SENSORLESS, Z4, Z), _STALL_SENSITIVITY));
793 800
       #endif
794 801
     #endif
795
-  #endif
802
+  #endif // USE SENSORLESS
796 803
 
797 804
   #ifdef TMC_ADV
798 805
     TMC_ADV()

+ 8
- 4
buildroot/tests/mega2560 View File

@@ -171,10 +171,12 @@ exec_test $1 $2 "Azteeg X3 | Mixing Extruder (x5) | Gradient Mix | Greek" "$3"
171 171
 # Test Laser features with 12864 LCD
172 172
 #
173 173
 restore_configs
174
-opt_set MOTHERBOARD BOARD_RAMPS_14_EFB LCD_LANGUAGE en TEMP_SENSOR_COOLER 1 EXTRUDERS 0 TEMP_SENSOR_1 0 SERIAL_PORT_2 2 \
174
+opt_set MOTHERBOARD BOARD_RAMPS_14_EFB EXTRUDERS 0 LCD_LANGUAGE en TEMP_SENSOR_COOLER 1 TEMP_SENSOR_1 0 SERIAL_PORT_2 2 \
175 175
         DEFAULT_AXIS_STEPS_PER_UNIT '{ 80, 80, 400 }' \
176 176
         DEFAULT_MAX_FEEDRATE '{ 300, 300, 5 }' \
177
-        DEFAULT_MAX_ACCELERATION '{ 3000, 3000, 100 }'
177
+        DEFAULT_MAX_ACCELERATION '{ 3000, 3000, 100 }' \
178
+        MANUAL_FEEDRATE '{ 50*60, 50*60, 4*60 }' \
179
+        AXIS_RELATIVE_MODES '{ false, false, false }'
178 180
 opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER SDSUPPORT EEPROM_SETTINGS EEPROM_BOOT_SILENT EEPROM_AUTO_INIT \
179 181
            LASER_FEATURE AIR_EVACUATION AIR_EVACUATION_PIN AIR_ASSIST AIR_ASSIST_PIN LASER_COOLANT_FLOW_METER MEATPACK_ON_SERIAL_PORT_1
180 182
 
@@ -184,10 +186,12 @@ exec_test $1 $2 "REPRAP MEGA2560 RAMPS | Laser Feature | Air Evacuation | Air As
184 186
 # Test Laser features with 44780 LCD
185 187
 #
186 188
 restore_configs
187
-opt_set MOTHERBOARD BOARD_RAMPS_14_EFB LCD_LANGUAGE en TEMP_SENSOR_COOLER 1 EXTRUDERS 0 TEMP_SENSOR_1 0 \
189
+opt_set MOTHERBOARD BOARD_RAMPS_14_EFB EXTRUDERS 0 LCD_LANGUAGE en TEMP_SENSOR_COOLER 1 TEMP_SENSOR_1 0 \
188 190
         DEFAULT_AXIS_STEPS_PER_UNIT '{ 80, 80, 400 }' \
189 191
         DEFAULT_MAX_FEEDRATE '{ 300, 300, 5 }' \
190
-        DEFAULT_MAX_ACCELERATION '{ 3000, 3000, 100 }'
192
+        DEFAULT_MAX_ACCELERATION '{ 3000, 3000, 100 }' \
193
+        MANUAL_FEEDRATE '{ 50*60, 50*60, 4*60 }' \
194
+        AXIS_RELATIVE_MODES '{ false, false, false }'
191 195
 opt_enable REPRAP_DISCOUNT_SMART_CONTROLLER SDSUPPORT EEPROM_SETTINGS EEPROM_BOOT_SILENT EEPROM_AUTO_INIT \
192 196
            LASER_FEATURE AIR_EVACUATION AIR_EVACUATION_PIN AIR_ASSIST AIR_ASSIST_PIN LASER_COOLANT_FLOW_METER
193 197
 

+ 2
- 0
buildroot/tests/rambo View File

@@ -48,6 +48,7 @@ opt_set MOTHERBOARD BOARD_RAMBO \
48 48
         DEFAULT_AXIS_STEPS_PER_UNIT '{ 80, 80, 4000 }' \
49 49
         DEFAULT_MAX_FEEDRATE '{ 300, 300, 5 }' \
50 50
         DEFAULT_MAX_ACCELERATION '{ 3000, 3000, 100 }' \
51
+        MANUAL_FEEDRATE '{ 50*60, 50*60, 4*60 }' \
51 52
         AXIS_RELATIVE_MODES '{ false, false, false }' \
52 53
         LEVEL_CORNERS_LEVELING_ORDER '{ LF, RF }'
53 54
 opt_enable USE_XMAX_PLUG USE_YMAX_PLUG USE_ZMAX_PLUG \
@@ -66,6 +67,7 @@ opt_set MOTHERBOARD BOARD_RAMBO EXTRUDERS 0 TEMP_SENSOR_BED 1 \
66 67
         DEFAULT_AXIS_STEPS_PER_UNIT '{ 80, 80, 4000 }' \
67 68
         DEFAULT_MAX_FEEDRATE '{ 300, 300, 5 }' \
68 69
         DEFAULT_MAX_ACCELERATION '{ 3000, 3000, 100 }' \
70
+        MANUAL_FEEDRATE '{ 50*60, 50*60, 4*60 }' \
69 71
         AXIS_RELATIVE_MODES '{ false, false, false }'
70 72
 opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
71 73
 exec_test $1 $2 "Rambo heated bed only" "$3"

Loading…
Cancel
Save