Browse Source

🚀 ZV Input Shaping (#24797)

tombrazier 1 year ago
parent
commit
a460b01c87
No account linked to committer's email address

+ 29
- 0
Marlin/Configuration_adv.h View File

@@ -1055,6 +1055,35 @@
1055 1055
 
1056 1056
 // @section motion
1057 1057
 
1058
+/**
1059
+ * Input Shaping -- EXPERIMENTAL
1060
+ *
1061
+ * Zero Vibration (ZV) Input Shaping for X and/or Y movements.
1062
+ *
1063
+ * This option uses a lot of SRAM for the step buffer, which is proportional
1064
+ * to the largest step rate possible for any axis. If the build fails due to
1065
+ * low SRAM the buffer size may be reduced by setting smaller values for
1066
+ * DEFAULT_AXIS_STEPS_PER_UNIT and/or DEFAULT_MAX_FEEDRATE. Runtime editing
1067
+ * of max feedrate (M203) or resonant frequency (M593) may result feedrate
1068
+ * being capped to prevent buffer overruns.
1069
+ *
1070
+ * Tune with M593 D<factor> F<frequency>:
1071
+ *
1072
+ *  D<factor>    Set the zeta/damping factor. If axes (X, Y, etc.) are not specified, set for all axes.
1073
+ *  F<frequency> Set the frequency. If axes (X, Y, etc.) are not specified, set for all axes.
1074
+ *  T[map]       Input Shaping type, 0:ZV, 1:EI, 2:2H EI (not implemented yet)
1075
+ *  X<1>         Set the given parameters only for the X axis.
1076
+ *  Y<1>         Set the given parameters only for the Y axis.
1077
+ */
1078
+//#define INPUT_SHAPING
1079
+#if ENABLED(INPUT_SHAPING)
1080
+  #define SHAPING_FREQ_X    40  // (Hz) The dominant resonant frequency of the X axis.
1081
+  #define SHAPING_FREQ_Y    40  // (Hz) The dominant resonant frequency of the Y axis.
1082
+  #define SHAPING_ZETA_X  0.3f  // Damping ratio of the X axis (range: 0.0 = no damping to 1.0 = critical damping).
1083
+  #define SHAPING_ZETA_Y  0.3f  // Damping ratio of the Y axis (range: 0.0 = no damping to 1.0 = critical damping).
1084
+  //#define SHAPING_MENU        // Add a menu to the LCD to set shaping parameters.
1085
+#endif
1086
+
1058 1087
 #define AXIS_RELATIVE_MODES { false, false, false, false }
1059 1088
 
1060 1089
 // Add a Duplicate option for well-separated conjoined nozzles

+ 83
- 0
Marlin/src/gcode/feature/input_shaping/M593.cpp View File

@@ -0,0 +1,83 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+#include "../../../inc/MarlinConfig.h"
24
+
25
+#if ENABLED(INPUT_SHAPING)
26
+
27
+#include "../../gcode.h"
28
+#include "../../../module/stepper.h"
29
+
30
+void GcodeSuite::M593_report(const bool forReplay/*=true*/) {
31
+  report_heading_etc(forReplay, F("Input Shaping"));
32
+  #if HAS_SHAPING_X
33
+    SERIAL_ECHO_MSG("M593 X"
34
+      " F", stepper.get_shaping_frequency(X_AXIS),
35
+      " D", stepper.get_shaping_damping_ratio(X_AXIS)
36
+    );
37
+  #endif
38
+  #if HAS_SHAPING_Y
39
+    SERIAL_ECHO_MSG("M593 Y"
40
+      " F", stepper.get_shaping_frequency(Y_AXIS),
41
+      " D", stepper.get_shaping_damping_ratio(Y_AXIS)
42
+    );
43
+  #endif
44
+}
45
+
46
+/**
47
+ * M593: Get or Set Input Shaping Parameters
48
+ *  D<factor>    Set the zeta/damping factor. If axes (X, Y, etc.) are not specified, set for all axes.
49
+ *  F<frequency> Set the frequency. If axes (X, Y, etc.) are not specified, set for all axes.
50
+ *  T[map]       Input Shaping type, 0:ZV, 1:EI, 2:2H EI (not implemented yet)
51
+ *  X<1>         Set the given parameters only for the X axis.
52
+ *  Y<1>         Set the given parameters only for the Y axis.
53
+ */
54
+void GcodeSuite::M593() {
55
+  if (!parser.seen_any()) return M593_report();
56
+
57
+  const bool seen_X = TERN0(HAS_SHAPING_X, parser.seen_test('X')),
58
+             seen_Y = TERN0(HAS_SHAPING_Y, parser.seen_test('Y')),
59
+             for_X = seen_X || TERN0(HAS_SHAPING_X, (!seen_X && !seen_Y)),
60
+             for_Y = seen_Y || TERN0(HAS_SHAPING_Y, (!seen_X && !seen_Y));
61
+
62
+  if (parser.seen('D')) {
63
+    const float zeta = parser.value_float();
64
+    if (WITHIN(zeta, 0, 1)) {
65
+      if (for_X) stepper.set_shaping_damping_ratio(X_AXIS, zeta);
66
+      if (for_Y) stepper.set_shaping_damping_ratio(Y_AXIS, zeta);
67
+    }
68
+    else
69
+      SERIAL_ECHO_MSG("?Zeta (D) value out of range (0-1)");
70
+  }
71
+
72
+  if (parser.seen('F')) {
73
+    const float freq = parser.value_float();
74
+    if (freq > 0) {
75
+      if (for_X) stepper.set_shaping_frequency(X_AXIS, freq);
76
+      if (for_Y) stepper.set_shaping_frequency(Y_AXIS, freq);
77
+    }
78
+    else
79
+      SERIAL_ECHO_MSG("?Frequency (F) must be greater than 0");
80
+  }
81
+}
82
+
83
+#endif

+ 4
- 0
Marlin/src/gcode/gcode.cpp View File

@@ -933,6 +933,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
933 933
         case 575: M575(); break;                                  // M575: Set serial baudrate
934 934
       #endif
935 935
 
936
+      #if ENABLED(INPUT_SHAPING)
937
+        case 593: M593(); break;                                  // M593: Set Input Shaping parameters
938
+      #endif
939
+
936 940
       #if ENABLED(ADVANCED_PAUSE_FEATURE)
937 941
         case 600: M600(); break;                                  // M600: Pause for Filament Change
938 942
         case 603: M603(); break;                                  // M603: Configure Filament Change

+ 6
- 0
Marlin/src/gcode/gcode.h View File

@@ -259,6 +259,7 @@
259 259
  * M554 - Get or set IP gateway. (Requires enabled Ethernet port)
260 260
  * M569 - Enable stealthChop on an axis. (Requires at least one _DRIVER_TYPE to be TMC2130/2160/2208/2209/5130/5160)
261 261
  * M575 - Change the serial baud rate. (Requires BAUD_RATE_GCODE)
262
+ * M593 - Get or set input shaping parameters. (Requires INPUT_SHAPING)
262 263
  * M600 - Pause for filament change: "M600 X<pos> Y<pos> Z<raise> E<first_retract> L<later_retract>". (Requires ADVANCED_PAUSE_FEATURE)
263 264
  * M603 - Configure filament change: "M603 T<tool> U<unload_length> L<load_length>". (Requires ADVANCED_PAUSE_FEATURE)
264 265
  * M605 - Set Dual X-Carriage movement mode: "M605 S<mode> [X<x_offset>] [R<temp_offset>]". (Requires DUAL_X_CARRIAGE)
@@ -1080,6 +1081,11 @@ private:
1080 1081
     static void M575();
1081 1082
   #endif
1082 1083
 
1084
+  #if ENABLED(INPUT_SHAPING)
1085
+    static void M593();
1086
+    static void M593_report(const bool forReplay=true);
1087
+  #endif
1088
+
1083 1089
   #if ENABLED(ADVANCED_PAUSE_FEATURE)
1084 1090
     static void M600();
1085 1091
     static void M603();

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

@@ -1085,3 +1085,17 @@
1085 1085
 #else
1086 1086
   #define CALC_FAN_SPEED(f) (f ? map(f, 1, 255, FAN_MIN_PWM, FAN_MAX_PWM) : FAN_OFF_PWM)
1087 1087
 #endif
1088
+
1089
+// Input shaping
1090
+#if ENABLED(INPUT_SHAPING)
1091
+  #if !HAS_Y_AXIS
1092
+    #undef SHAPING_FREQ_Y
1093
+    #undef SHAPING_BUFFER_Y
1094
+  #endif
1095
+  #ifdef SHAPING_FREQ_X
1096
+    #define HAS_SHAPING_X 1
1097
+  #endif
1098
+  #ifdef SHAPING_FREQ_Y
1099
+    #define HAS_SHAPING_Y 1
1100
+  #endif
1101
+#endif

+ 30
- 5
Marlin/src/inc/SanityCheck.h View File

@@ -4238,11 +4238,6 @@ static_assert(_PLUS_TEST(4), "HOMING_FEEDRATE_MM_M values must be positive.");
4238 4238
   #endif
4239 4239
 #endif
4240 4240
 
4241
-// Misc. Cleanup
4242
-#undef _TEST_PWM
4243
-#undef _NUM_AXES_STR
4244
-#undef _LOGICAL_AXES_STR
4245
-
4246 4241
 // JTAG support in the HAL
4247 4242
 #if ENABLED(DISABLE_DEBUG) && !defined(JTAGSWD_DISABLE)
4248 4243
   #error "DISABLE_DEBUG is not supported for the selected MCU/Board."
@@ -4254,3 +4249,33 @@ static_assert(_PLUS_TEST(4), "HOMING_FEEDRATE_MM_M values must be positive.");
4254 4249
 #if ENABLED(XFER_BUILD) && !BOTH(BINARY_FILE_TRANSFER, CUSTOM_FIRMWARE_UPLOAD)
4255 4250
   #error "BINARY_FILE_TRANSFER and CUSTOM_FIRMWARE_UPLOAD are required for custom upload."
4256 4251
 #endif
4252
+
4253
+// Check requirements for Input Shaping
4254
+#if ENABLED(INPUT_SHAPING) && defined(__AVR__)
4255
+  #if HAS_SHAPING_X
4256
+    #if F_CPU > 16000000
4257
+      static_assert((SHAPING_FREQ_X) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_X is below the minimum (20) for AVR 20MHz.");
4258
+    #else
4259
+      static_assert((SHAPING_FREQ_X) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_X is below the minimum (16) for AVR 16MHz.");
4260
+    #endif
4261
+  #elif HAS_SHAPING_Y
4262
+    #if F_CPU > 16000000
4263
+      static_assert((SHAPING_FREQ_Y) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Y is below the minimum (20) for AVR 20MHz.");
4264
+    #else
4265
+      static_assert((SHAPING_FREQ_Y) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Y is below the minimum (16) for AVR 16MHz.");
4266
+    #endif
4267
+  #endif
4268
+#endif
4269
+
4270
+#if ENABLED(INPUT_SHAPING)
4271
+  #if ENABLED(DIRECT_STEPPING)
4272
+    #error "INPUT_SHAPING cannot currently be used with DIRECT_STEPPING."
4273
+  #elif ENABLED(LASER_FEATURE)
4274
+    #error "INPUT_SHAPING cannot currently be used with LASER_FEATURE."
4275
+  #endif
4276
+#endif
4277
+
4278
+// Misc. Cleanup
4279
+#undef _TEST_PWM
4280
+#undef _NUM_AXES_STR
4281
+#undef _LOGICAL_AXES_STR

+ 5
- 0
Marlin/src/lcd/language/language_en.h View File

@@ -399,6 +399,11 @@ namespace Language_en {
399 399
   LSTR MSG_AMAX_EN                        = _UxGT("Max * Accel");
400 400
   LSTR MSG_A_RETRACT                      = _UxGT("Retract Accel");
401 401
   LSTR MSG_A_TRAVEL                       = _UxGT("Travel Accel");
402
+  LSTR MSG_INPUT_SHAPING                  = _UxGT("Input Shaping");
403
+  LSTR MSG_SHAPING_X_FREQ                 = STR_X _UxGT(" frequency");
404
+  LSTR MSG_SHAPING_Y_FREQ                 = STR_Y _UxGT(" frequency");
405
+  LSTR MSG_SHAPING_X_ZETA                 = STR_X _UxGT(" damping");
406
+  LSTR MSG_SHAPING_Y_ZETA                 = STR_Y _UxGT(" damping");
402 407
   LSTR MSG_XY_FREQUENCY_LIMIT             = _UxGT("XY Freq Limit");
403 408
   LSTR MSG_XY_FREQUENCY_FEEDRATE          = _UxGT("Min FR Factor");
404 409
   LSTR MSG_STEPS_PER_MM                   = _UxGT("Steps/mm");

+ 39
- 2
Marlin/src/lcd/menu/menu_advanced.cpp View File

@@ -31,6 +31,7 @@
31 31
 #include "menu_item.h"
32 32
 #include "../../MarlinCore.h"
33 33
 #include "../../module/planner.h"
34
+#include "../../module/stepper.h"
34 35
 
35 36
 #if DISABLED(NO_VOLUMETRICS)
36 37
   #include "../../gcode/parser.h"
@@ -80,8 +81,6 @@ void menu_backlash();
80 81
 
81 82
 #if HAS_MOTOR_CURRENT_PWM
82 83
 
83
-  #include "../../module/stepper.h"
84
-
85 84
   void menu_pwm() {
86 85
     START_MENU();
87 86
     BACK_ITEM(MSG_ADVANCED_SETTINGS);
@@ -538,6 +537,39 @@ void menu_backlash();
538 537
     END_MENU();
539 538
   }
540 539
 
540
+  #if ENABLED(SHAPING_MENU)
541
+
542
+    void menu_advanced_input_shaping() {
543
+      constexpr float min_frequency = TERN(__AVR__, float(STEPPER_TIMER_RATE) / 2 / 0x10000, 1.0f);
544
+
545
+      START_MENU();
546
+      BACK_ITEM(MSG_ADVANCED_SETTINGS);
547
+
548
+      // M593 F Frequency
549
+      #if HAS_SHAPING_X
550
+        editable.decimal = stepper.get_shaping_frequency(X_AXIS);
551
+        EDIT_ITEM_FAST(float61, MSG_SHAPING_X_FREQ, &editable.decimal, min_frequency, 200.0f, []{ stepper.set_shaping_frequency(X_AXIS, editable.decimal); });
552
+      #endif
553
+      #if HAS_SHAPING_Y
554
+        editable.decimal = stepper.get_shaping_frequency(Y_AXIS);
555
+        EDIT_ITEM_FAST(float61, MSG_SHAPING_Y_FREQ, &editable.decimal, min_frequency, 200.0f, []{ stepper.set_shaping_frequency(Y_AXIS, editable.decimal); });
556
+      #endif
557
+
558
+      // M593 D Damping ratio
559
+      #if HAS_SHAPING_X
560
+        editable.decimal = stepper.get_shaping_damping_ratio(X_AXIS);
561
+        EDIT_ITEM_FAST(float42_52, MSG_SHAPING_X_ZETA, &editable.decimal, 0.0f, 1.0f, []{ stepper.set_shaping_damping_ratio(X_AXIS, editable.decimal); });
562
+      #endif
563
+      #if HAS_SHAPING_Y
564
+        editable.decimal = stepper.get_shaping_damping_ratio(Y_AXIS);
565
+        EDIT_ITEM_FAST(float42_52, MSG_SHAPING_Y_ZETA, &editable.decimal, 0.0f, 1.0f, []{ stepper.set_shaping_damping_ratio(Y_AXIS, editable.decimal); });
566
+      #endif
567
+
568
+      END_MENU();
569
+    }
570
+
571
+  #endif
572
+
541 573
   #if HAS_CLASSIC_JERK
542 574
 
543 575
     void menu_advanced_jerk() {
@@ -657,6 +689,11 @@ void menu_advanced_settings() {
657 689
     // M201 - Acceleration items
658 690
     SUBMENU(MSG_ACCELERATION, menu_advanced_acceleration);
659 691
 
692
+    // M593 - Acceleration items
693
+    #if ENABLED(SHAPING_MENU)
694
+      SUBMENU(MSG_INPUT_SHAPING, menu_advanced_input_shaping);
695
+    #endif
696
+
660 697
     #if HAS_CLASSIC_JERK
661 698
       // M205 - Max Jerk
662 699
       SUBMENU(MSG_JERK, menu_advanced_jerk);

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

@@ -2483,6 +2483,14 @@ bool Planner::_populate_block(
2483 2483
 
2484 2484
   #endif // XY_FREQUENCY_LIMIT
2485 2485
 
2486
+  #if ENABLED(INPUT_SHAPING)
2487
+    const float top_freq = _MIN(float(0x7FFFFFFFL)
2488
+                                OPTARG(HAS_SHAPING_X, stepper.get_shaping_frequency(X_AXIS))
2489
+                                OPTARG(HAS_SHAPING_Y, stepper.get_shaping_frequency(Y_AXIS))),
2490
+                max_factor = (top_freq * float(shaping_dividends - 3) * 2.0f) / block->nominal_rate;
2491
+    NOMORE(speed_factor, max_factor);
2492
+  #endif
2493
+
2486 2494
   // Correct the speed
2487 2495
   if (speed_factor < 1.0f) {
2488 2496
     current_speed *= speed_factor;

+ 66
- 0
Marlin/src/module/settings.cpp View File

@@ -577,6 +577,18 @@ typedef struct SettingsDataStruct {
577 577
     MPC_t mpc_constants[HOTENDS];                       // M306
578 578
   #endif
579 579
 
580
+  //
581
+  // Input Shaping
582
+  //
583
+  #if HAS_SHAPING_X
584
+    float shaping_x_frequency, // M593 X F
585
+          shaping_x_zeta;      // M593 X D
586
+  #endif
587
+  #if HAS_SHAPING_Y
588
+    float shaping_y_frequency, // M593 Y F
589
+          shaping_y_zeta;      // M593 Y D
590
+  #endif
591
+
580 592
 } SettingsData;
581 593
 
582 594
 //static_assert(sizeof(SettingsData) <= MARLIN_EEPROM_SIZE, "EEPROM too small to contain SettingsData!");
@@ -1603,6 +1615,20 @@ void MarlinSettings::postprocess() {
1603 1615
     #endif
1604 1616
 
1605 1617
     //
1618
+    // Input Shaping
1619
+    ///
1620
+    #if ENABLED(INPUT_SHAPING)
1621
+      #if HAS_SHAPING_X
1622
+        EEPROM_WRITE(stepper.get_shaping_frequency(X_AXIS));
1623
+        EEPROM_WRITE(stepper.get_shaping_damping_ratio(X_AXIS));
1624
+      #endif
1625
+      #if HAS_SHAPING_Y
1626
+        EEPROM_WRITE(stepper.get_shaping_frequency(Y_AXIS));
1627
+        EEPROM_WRITE(stepper.get_shaping_damping_ratio(Y_AXIS));
1628
+      #endif
1629
+    #endif
1630
+
1631
+    //
1606 1632
     // Report final CRC and Data Size
1607 1633
     //
1608 1634
     if (!eeprom_error) {
@@ -2574,6 +2600,27 @@ void MarlinSettings::postprocess() {
2574 2600
       #endif
2575 2601
 
2576 2602
       //
2603
+      // Input Shaping
2604
+      //
2605
+      #if HAS_SHAPING_X
2606
+      {
2607
+        float _data[2];
2608
+        EEPROM_READ(_data);
2609
+        stepper.set_shaping_frequency(X_AXIS, _data[0]);
2610
+        stepper.set_shaping_damping_ratio(X_AXIS, _data[1]);
2611
+      }
2612
+      #endif
2613
+
2614
+      #if HAS_SHAPING_Y
2615
+      {
2616
+        float _data[2];
2617
+        EEPROM_READ(_data);
2618
+        stepper.set_shaping_frequency(Y_AXIS, _data[0]);
2619
+        stepper.set_shaping_damping_ratio(Y_AXIS, _data[1]);
2620
+      }
2621
+      #endif
2622
+
2623
+      //
2577 2624
       // Validate Final Size and CRC
2578 2625
       //
2579 2626
       eeprom_error = size_error(eeprom_index - (EEPROM_OFFSET));
@@ -3343,6 +3390,20 @@ void MarlinSettings::reset() {
3343 3390
     }
3344 3391
   #endif
3345 3392
 
3393
+  //
3394
+  // Input Shaping
3395
+  //
3396
+  #if ENABLED(INPUT_SHAPING)
3397
+    #if HAS_SHAPING_X
3398
+      stepper.set_shaping_frequency(X_AXIS, SHAPING_FREQ_X);
3399
+      stepper.set_shaping_damping_ratio(X_AXIS, SHAPING_ZETA_X);
3400
+    #endif
3401
+    #if HAS_SHAPING_Y
3402
+      stepper.set_shaping_frequency(Y_AXIS, SHAPING_FREQ_Y);
3403
+      stepper.set_shaping_damping_ratio(Y_AXIS, SHAPING_ZETA_Y);
3404
+    #endif
3405
+  #endif
3406
+
3346 3407
   postprocess();
3347 3408
 
3348 3409
   #if EITHER(EEPROM_CHITCHAT, DEBUG_LEVELING_FEATURE)
@@ -3591,6 +3652,11 @@ void MarlinSettings::reset() {
3591 3652
     TERN_(HAS_STEALTHCHOP, gcode.M569_report(forReplay));
3592 3653
 
3593 3654
     //
3655
+    // Input Shaping
3656
+    //
3657
+    TERN_(INPUT_SHAPING, gcode.M593_report(forReplay));
3658
+
3659
+    //
3594 3660
     // Linear Advance
3595 3661
     //
3596 3662
     TERN_(LIN_ADVANCE, gcode.M900_report(forReplay));

+ 236
- 33
Marlin/src/module/stepper.cpp View File

@@ -199,7 +199,7 @@ IF_DISABLED(ADAPTIVE_STEP_SMOOTHING, constexpr) uint8_t Stepper::oversampling_fa
199 199
 
200 200
 xyze_long_t Stepper::delta_error{0};
201 201
 
202
-xyze_ulong_t Stepper::advance_dividend{0};
202
+xyze_long_t Stepper::advance_dividend{0};
203 203
 uint32_t Stepper::advance_divisor = 0,
204 204
          Stepper::step_events_completed = 0, // The number of step events executed in the current block
205 205
          Stepper::accelerate_until,          // The count at which to stop accelerating
@@ -232,6 +232,20 @@ uint32_t Stepper::advance_divisor = 0,
232 232
            Stepper::la_advance_steps = 0;
233 233
 #endif
234 234
 
235
+#if ENABLED(INPUT_SHAPING)
236
+  shaping_time_t                DelayTimeManager::now = 0;
237
+  ParamDelayQueue               Stepper::shaping_dividend_queue;
238
+  DelayQueue<shaping_dividends> Stepper::shaping_queue;
239
+  #if HAS_SHAPING_X
240
+    shaping_time_t DelayTimeManager::delay_x;
241
+    ShapeParams Stepper::shaping_x;
242
+  #endif
243
+  #if HAS_SHAPING_Y
244
+    shaping_time_t DelayTimeManager::delay_y;
245
+    ShapeParams Stepper::shaping_y;
246
+  #endif
247
+#endif
248
+
235 249
 #if ENABLED(INTEGRATED_BABYSTEPPING)
236 250
   uint32_t Stepper::nextBabystepISR = BABYSTEP_NEVER;
237 251
 #endif
@@ -458,12 +472,10 @@ xyze_int8_t Stepper::count_direction{0};
458 472
 #define PULSE_LOW_TICK_COUNT hal_timer_t(NS_TO_PULSE_TIMER_TICKS(_MIN_PULSE_LOW_NS - _MIN(_MIN_PULSE_LOW_NS, TIMER_SETUP_NS)))
459 473
 
460 474
 #define USING_TIMED_PULSE() hal_timer_t start_pulse_count = 0
461
-#define START_TIMED_PULSE(DIR) (start_pulse_count = HAL_timer_get_count(MF_TIMER_PULSE))
462
-#define AWAIT_TIMED_PULSE(DIR) while (PULSE_##DIR##_TICK_COUNT > HAL_timer_get_count(MF_TIMER_PULSE) - start_pulse_count) { }
463
-#define START_HIGH_PULSE()  START_TIMED_PULSE(HIGH)
464
-#define AWAIT_HIGH_PULSE()  AWAIT_TIMED_PULSE(HIGH)
465
-#define START_LOW_PULSE()   START_TIMED_PULSE(LOW)
466
-#define AWAIT_LOW_PULSE()   AWAIT_TIMED_PULSE(LOW)
475
+#define START_TIMED_PULSE() (start_pulse_count = HAL_timer_get_count(MF_TIMER_PULSE))
476
+#define AWAIT_TIMED_PULSE(DIR) while (PULSE_##DIR##_TICK_COUNT > HAL_timer_get_count(MF_TIMER_PULSE) - start_pulse_count) { /* nada */ }
477
+#define AWAIT_HIGH_PULSE() AWAIT_TIMED_PULSE(HIGH)
478
+#define AWAIT_LOW_PULSE()  AWAIT_TIMED_PULSE(LOW)
467 479
 
468 480
 #if MINIMUM_STEPPER_PRE_DIR_DELAY > 0
469 481
   #define DIR_WAIT_BEFORE() DELAY_NS(MINIMUM_STEPPER_PRE_DIR_DELAY)
@@ -559,6 +571,16 @@ void Stepper::disable_all_steppers() {
559 571
   TERN_(EXTENSIBLE_UI, ExtUI::onSteppersDisabled());
560 572
 }
561 573
 
574
+#define SET_STEP_DIR(A)                       \
575
+  if (motor_direction(_AXIS(A))) {            \
576
+    A##_APPLY_DIR(INVERT_##A##_DIR, false);   \
577
+    count_direction[_AXIS(A)] = -1;           \
578
+  }                                           \
579
+  else {                                      \
580
+    A##_APPLY_DIR(!INVERT_##A##_DIR, false);  \
581
+    count_direction[_AXIS(A)] = 1;            \
582
+  }
583
+
562 584
 /**
563 585
  * Set the stepper direction of each axis
564 586
  *
@@ -570,16 +592,6 @@ void Stepper::set_directions() {
570 592
 
571 593
   DIR_WAIT_BEFORE();
572 594
 
573
-  #define SET_STEP_DIR(A)                       \
574
-    if (motor_direction(_AXIS(A))) {            \
575
-      A##_APPLY_DIR(INVERT_##A##_DIR, false);   \
576
-      count_direction[_AXIS(A)] = -1;           \
577
-    }                                           \
578
-    else {                                      \
579
-      A##_APPLY_DIR(!INVERT_##A##_DIR, false);  \
580
-      count_direction[_AXIS(A)] = 1;            \
581
-    }
582
-
583 595
   TERN_(HAS_X_DIR, SET_STEP_DIR(X)); // A
584 596
   TERN_(HAS_Y_DIR, SET_STEP_DIR(Y)); // B
585 597
   TERN_(HAS_Z_DIR, SET_STEP_DIR(Z)); // C
@@ -1467,8 +1479,20 @@ void Stepper::isr() {
1467 1479
     // Enable ISRs to reduce USART processing latency
1468 1480
     hal.isr_on();
1469 1481
 
1482
+    #if ENABLED(INPUT_SHAPING)
1483
+      // Speed limiting should ensure the buffers never get full. But if somehow they do, stutter rather than overflow.
1484
+      if (!nextMainISR) {
1485
+        TERN_(HAS_SHAPING_X, if (shaping_dividend_queue.free_count_x() == 0) nextMainISR = shaping_dividend_queue.peek_x() + 1);
1486
+        TERN_(HAS_SHAPING_Y, if (shaping_dividend_queue.free_count_y() == 0) NOLESS(nextMainISR, shaping_dividend_queue.peek_y() + 1));
1487
+        TERN_(HAS_SHAPING_X, if (shaping_queue.free_count_x() < steps_per_isr) NOLESS(nextMainISR, shaping_queue.peek_x() + 1));
1488
+        TERN_(HAS_SHAPING_Y, if (shaping_queue.free_count_y() < steps_per_isr) NOLESS(nextMainISR, shaping_queue.peek_y() + 1));
1489
+      }
1490
+    #endif
1491
+
1470 1492
     if (!nextMainISR) pulse_phase_isr();                // 0 = Do coordinated axes Stepper pulses
1471 1493
 
1494
+    TERN_(INPUT_SHAPING, shaping_isr());                // Do Shaper stepping, if needed
1495
+
1472 1496
     #if ENABLED(LIN_ADVANCE)
1473 1497
       if (!nextAdvanceISR) {                            // 0 = Do Linear Advance E Stepper pulses
1474 1498
         advance_isr();
@@ -1497,10 +1521,14 @@ void Stepper::isr() {
1497 1521
 
1498 1522
     // Get the interval to the next ISR call
1499 1523
     const uint32_t interval = _MIN(
1500
-      uint32_t(HAL_TIMER_TYPE_MAX),                     // Come back in a very long time
1501
-      nextMainISR                                       // Time until the next Pulse / Block phase
1502
-      OPTARG(LIN_ADVANCE, nextAdvanceISR)               // Come back early for Linear Advance?
1503
-      OPTARG(INTEGRATED_BABYSTEPPING, nextBabystepISR)  // Come back early for Babystepping?
1524
+      uint32_t(HAL_TIMER_TYPE_MAX),                           // Come back in a very long time
1525
+      nextMainISR                                             // Time until the next Pulse / Block phase
1526
+      OPTARG(HAS_SHAPING_X, shaping_dividend_queue.peek_x())  // Time until next input shaping dividend change for X
1527
+      OPTARG(HAS_SHAPING_Y, shaping_dividend_queue.peek_y())  // Time until next input shaping dividend change for Y
1528
+      OPTARG(HAS_SHAPING_X, shaping_queue.peek_x())           // Time until next input shaping echo for X
1529
+      OPTARG(HAS_SHAPING_Y, shaping_queue.peek_y())           // Time until next input shaping echo for Y
1530
+      OPTARG(LIN_ADVANCE, nextAdvanceISR)                     // Come back early for Linear Advance?
1531
+      OPTARG(INTEGRATED_BABYSTEPPING, nextBabystepISR)        // Come back early for Babystepping?
1504 1532
     );
1505 1533
 
1506 1534
     //
@@ -1512,6 +1540,8 @@ void Stepper::isr() {
1512 1540
 
1513 1541
     nextMainISR -= interval;
1514 1542
 
1543
+    TERN_(INPUT_SHAPING, DelayTimeManager::decrement_delays(interval));
1544
+
1515 1545
     #if ENABLED(LIN_ADVANCE)
1516 1546
       if (nextAdvanceISR != LA_ADV_NEVER) nextAdvanceISR -= interval;
1517 1547
     #endif
@@ -1604,11 +1634,19 @@ void Stepper::pulse_phase_isr() {
1604 1634
   // If we must abort the current block, do so!
1605 1635
   if (abort_current_block) {
1606 1636
     abort_current_block = false;
1607
-    if (current_block) discard_current_block();
1637
+    if (current_block) {
1638
+      discard_current_block();
1639
+      #if ENABLED(INPUT_SHAPING)
1640
+        shaping_dividend_queue.purge();
1641
+        shaping_queue.purge();
1642
+        TERN_(HAS_SHAPING_X, delta_error.x = 0);
1643
+        TERN_(HAS_SHAPING_Y, delta_error.y = 0);
1644
+      #endif
1645
+    }
1608 1646
   }
1609 1647
 
1610 1648
   // If there is no current block, do nothing
1611
-  if (!current_block) return;
1649
+  if (!current_block || step_events_completed >= step_event_count) return;
1612 1650
 
1613 1651
   // Skipping step processing causes motion to freeze
1614 1652
   if (TERN0(FREEZE_FEATURE, frozen)) return;
@@ -1627,6 +1665,9 @@ void Stepper::pulse_phase_isr() {
1627 1665
   #endif
1628 1666
   xyze_bool_t step_needed{0};
1629 1667
 
1668
+  // Direct Stepping page?
1669
+  const bool is_page = current_block->is_page();
1670
+
1630 1671
   do {
1631 1672
     #define _APPLY_STEP(AXIS, INV, ALWAYS) AXIS ##_APPLY_STEP(INV, ALWAYS)
1632 1673
     #define _INVERT_STEP_PIN(AXIS) INVERT_## AXIS ##_STEP_PIN
@@ -1641,6 +1682,22 @@ void Stepper::pulse_phase_isr() {
1641 1682
       } \
1642 1683
     }while(0)
1643 1684
 
1685
+    #define PULSE_PREP_SHAPING(AXIS, DIVIDEND) do{ \
1686
+      delta_error[_AXIS(AXIS)] += (DIVIDEND); \
1687
+      if ((MAXDIR(AXIS) && delta_error[_AXIS(AXIS)] <= -0x30000000L) || (MINDIR(AXIS) && delta_error[_AXIS(AXIS)] >= 0x30000000L)) { \
1688
+        TBI(last_direction_bits, _AXIS(AXIS)); \
1689
+        DIR_WAIT_BEFORE(); \
1690
+        SET_STEP_DIR(AXIS); \
1691
+        DIR_WAIT_AFTER(); \
1692
+      } \
1693
+      step_needed[_AXIS(AXIS)] = (MAXDIR(AXIS) && delta_error[_AXIS(AXIS)] >= 0x10000000L) || \
1694
+                                 (MINDIR(AXIS) && delta_error[_AXIS(AXIS)] <= -0x10000000L); \
1695
+      if (step_needed[_AXIS(AXIS)]) { \
1696
+        count_position[_AXIS(AXIS)] += count_direction[_AXIS(AXIS)]; \
1697
+        delta_error[_AXIS(AXIS)] += MAXDIR(AXIS) ? -0x20000000L : 0x20000000L; \
1698
+      } \
1699
+    }while(0)
1700
+
1644 1701
     // Start an active pulse if needed
1645 1702
     #define PULSE_START(AXIS) do{ \
1646 1703
       if (step_needed[_AXIS(AXIS)]) { \
@@ -1655,9 +1712,6 @@ void Stepper::pulse_phase_isr() {
1655 1712
       } \
1656 1713
     }while(0)
1657 1714
 
1658
-    // Direct Stepping page?
1659
-    const bool is_page = current_block->is_page();
1660
-
1661 1715
     #if ENABLED(DIRECT_STEPPING)
1662 1716
       // Direct stepping is currently not ready for HAS_I_AXIS
1663 1717
       if (is_page) {
@@ -1765,12 +1819,22 @@ void Stepper::pulse_phase_isr() {
1765 1819
     #endif // DIRECT_STEPPING
1766 1820
 
1767 1821
     if (!is_page) {
1822
+      TERN_(INPUT_SHAPING, shaping_queue.enqueue());
1823
+
1768 1824
       // Determine if pulses are needed
1769 1825
       #if HAS_X_STEP
1770
-        PULSE_PREP(X);
1826
+        #if HAS_SHAPING_X
1827
+          PULSE_PREP_SHAPING(X, advance_dividend.x);
1828
+        #else
1829
+          PULSE_PREP(X);
1830
+        #endif
1771 1831
       #endif
1772 1832
       #if HAS_Y_STEP
1773
-        PULSE_PREP(Y);
1833
+        #if HAS_SHAPING_Y
1834
+          PULSE_PREP_SHAPING(Y, advance_dividend.y);
1835
+        #else
1836
+          PULSE_PREP(Y);
1837
+        #endif
1774 1838
       #endif
1775 1839
       #if HAS_Z_STEP
1776 1840
         PULSE_PREP(Z);
@@ -1855,7 +1919,7 @@ void Stepper::pulse_phase_isr() {
1855 1919
 
1856 1920
     // TODO: need to deal with MINIMUM_STEPPER_PULSE over i2s
1857 1921
     #if ISR_MULTI_STEPS
1858
-      START_HIGH_PULSE();
1922
+      START_TIMED_PULSE();
1859 1923
       AWAIT_HIGH_PULSE();
1860 1924
     #endif
1861 1925
 
@@ -1895,12 +1959,62 @@ void Stepper::pulse_phase_isr() {
1895 1959
     #endif
1896 1960
 
1897 1961
     #if ISR_MULTI_STEPS
1898
-      if (events_to_do) START_LOW_PULSE();
1962
+      if (events_to_do) START_TIMED_PULSE();
1899 1963
     #endif
1900 1964
 
1901 1965
   } while (--events_to_do);
1902 1966
 }
1903 1967
 
1968
+#if ENABLED(INPUT_SHAPING)
1969
+
1970
+  void Stepper::shaping_isr() {
1971
+    xyze_bool_t step_needed{0};
1972
+
1973
+    const bool shapex = TERN0(HAS_SHAPING_X, !shaping_queue.peek_x()),
1974
+               shapey = TERN0(HAS_SHAPING_Y, !shaping_queue.peek_y());
1975
+
1976
+    #if HAS_SHAPING_X
1977
+      if (!shaping_dividend_queue.peek_x()) shaping_x.dividend = shaping_dividend_queue.dequeue_x();
1978
+    #endif
1979
+    #if HAS_SHAPING_Y
1980
+      if (!shaping_dividend_queue.peek_y()) shaping_y.dividend = shaping_dividend_queue.dequeue_y();
1981
+    #endif
1982
+
1983
+    #if HAS_SHAPING_X
1984
+      if (shapex) {
1985
+        shaping_queue.dequeue_x();
1986
+        PULSE_PREP_SHAPING(X, shaping_x.dividend);
1987
+        PULSE_START(X);
1988
+      }
1989
+    #endif
1990
+
1991
+    #if HAS_SHAPING_Y
1992
+      if (shapey) {
1993
+        shaping_queue.dequeue_y();
1994
+        PULSE_PREP_SHAPING(Y, shaping_y.dividend);
1995
+        PULSE_START(Y);
1996
+      }
1997
+    #endif
1998
+
1999
+    TERN_(I2S_STEPPER_STREAM, i2s_push_sample());
2000
+
2001
+    if (shapex || shapey) {
2002
+      #if ISR_MULTI_STEPS
2003
+        USING_TIMED_PULSE();
2004
+        START_TIMED_PULSE();
2005
+        AWAIT_HIGH_PULSE();
2006
+      #endif
2007
+      #if HAS_SHAPING_X
2008
+        if (shapex) PULSE_STOP(X);
2009
+      #endif
2010
+      #if HAS_SHAPING_Y
2011
+        if (shapey) PULSE_STOP(Y);
2012
+      #endif
2013
+    }
2014
+  }
2015
+
2016
+#endif // INPUT_SHAPING
2017
+
1904 2018
 // Calculate timer interval, with all limits applied.
1905 2019
 uint32_t Stepper::calc_timer_interval(uint32_t step_rate) {
1906 2020
   #ifdef CPU_32_BIT
@@ -2365,12 +2479,56 @@ uint32_t Stepper::block_phase_isr() {
2365 2479
       step_event_count = current_block->step_event_count << oversampling;
2366 2480
 
2367 2481
       // Initialize Bresenham delta errors to 1/2
2482
+      #if HAS_SHAPING_X
2483
+        const int32_t old_delta_error_x = delta_error.x;
2484
+      #endif
2485
+      #if HAS_SHAPING_Y
2486
+        const int32_t old_delta_error_y = delta_error.y;
2487
+      #endif
2368 2488
       delta_error = TERN_(LIN_ADVANCE, la_delta_error =) -int32_t(step_event_count);
2369 2489
 
2370 2490
       // Calculate Bresenham dividends and divisors
2371
-      advance_dividend = current_block->steps << 1;
2491
+      advance_dividend = (current_block->steps << 1).asLong();
2372 2492
       advance_divisor = step_event_count << 1;
2373 2493
 
2494
+      // for input shaped axes, advance_divisor is replaced with 0x40000000
2495
+      // and steps are repeated twice so dividends have to be scaled and halved
2496
+      // and the dividend is directional, i.e. signed
2497
+      TERN_(HAS_SHAPING_X, advance_dividend.x = (uint64_t(current_block->steps.x) << 29) / step_event_count);
2498
+      TERN_(HAS_SHAPING_X, if (TEST(current_block->direction_bits, X_AXIS)) advance_dividend.x *= -1);
2499
+      TERN_(HAS_SHAPING_X, if (!shaping_queue.empty_x()) SET_BIT_TO(current_block->direction_bits, X_AXIS, TEST(last_direction_bits, X_AXIS)));
2500
+      TERN_(HAS_SHAPING_Y, advance_dividend.y = (uint64_t(current_block->steps.y) << 29) / step_event_count);
2501
+      TERN_(HAS_SHAPING_Y, if (TEST(current_block->direction_bits, Y_AXIS)) advance_dividend.y *= -1);
2502
+      TERN_(HAS_SHAPING_Y, if (!shaping_queue.empty_y()) SET_BIT_TO(current_block->direction_bits, Y_AXIS, TEST(last_direction_bits, Y_AXIS)));
2503
+
2504
+      // The scaling operation above introduces rounding errors which must now be removed.
2505
+      // For this segment, there will be step_event_count calls to the Bresenham logic and the same number of echoes.
2506
+      // For each pair of calls to the Bresenham logic, delta_error will increase by advance_dividend modulo 0x20000000
2507
+      // so (e.g. for x) delta_error.x will end up changing by (advance_dividend.x * step_event_count) % 0x20000000.
2508
+      // For a divisor which is a power of 2, modulo is the same as as a bitmask, i.e.
2509
+      // (advance_dividend.x * step_event_count) & 0x1FFFFFFF.
2510
+      // This segment's final change in delta_error should actually be zero so we need to increase delta_error by
2511
+      // 0 - ((advance_dividend.x * step_event_count) & 0x1FFFFFFF)
2512
+      // And this needs to be adjusted to the range -0x10000000 to 0x10000000.
2513
+      // Adding and subtracting 0x10000000 inside the outside the modulo achieves this.
2514
+      TERN_(HAS_SHAPING_X, delta_error.x = old_delta_error_x + 0x10000000L - ((0x10000000L + advance_dividend.x * step_event_count) & 0x1FFFFFFFUL));
2515
+      TERN_(HAS_SHAPING_Y, delta_error.y = old_delta_error_y + 0x10000000L - ((0x10000000L + advance_dividend.y * step_event_count) & 0x1FFFFFFFUL));
2516
+
2517
+      // when there is damping, the signal and its echo have different amplitudes
2518
+      #if ENABLED(HAS_SHAPING_X)
2519
+        const int32_t echo_x = shaping_x.factor * (advance_dividend.x >> 7);
2520
+      #endif
2521
+      #if ENABLED(HAS_SHAPING_Y)
2522
+        const int32_t echo_y = shaping_y.factor * (advance_dividend.y >> 7);
2523
+      #endif
2524
+
2525
+      // plan the change of values for advance_dividend for the input shaping echoes
2526
+      TERN_(INPUT_SHAPING, shaping_dividend_queue.enqueue(TERN0(HAS_SHAPING_X, echo_x), TERN0(HAS_SHAPING_Y, echo_y)));
2527
+
2528
+      // apply the adjustment to the primary signal
2529
+      TERN_(HAS_SHAPING_X, advance_dividend.x -= echo_x);
2530
+      TERN_(HAS_SHAPING_Y, advance_dividend.y -= echo_y);
2531
+
2374 2532
       // No step events completed so far
2375 2533
       step_events_completed = 0;
2376 2534
 
@@ -2485,7 +2643,7 @@ uint32_t Stepper::block_phase_isr() {
2485 2643
       // Enforce a minimum duration for STEP pulse ON
2486 2644
       #if ISR_PULSE_CONTROL
2487 2645
         USING_TIMED_PULSE();
2488
-        START_HIGH_PULSE();
2646
+        START_TIMED_PULSE();
2489 2647
         AWAIT_HIGH_PULSE();
2490 2648
       #endif
2491 2649
 
@@ -2816,6 +2974,51 @@ void Stepper::init() {
2816 2974
   #endif
2817 2975
 }
2818 2976
 
2977
+#if ENABLED(INPUT_SHAPING)
2978
+  /**
2979
+   * Calculate a fixed point factor to apply to the signal and its echo
2980
+   * when shaping an axis.
2981
+   */
2982
+  void Stepper::set_shaping_damping_ratio(const AxisEnum axis, const float zeta) {
2983
+    // from the damping ratio, get a factor that can be applied to advance_dividend for fixed point maths
2984
+    // for ZV, we use amplitudes 1/(1+K) and K/(1+K) where K = exp(-zeta * M_PI / sqrt(1.0f - zeta * zeta))
2985
+    // which can be converted to 1:7 fixed point with an excellent fit with a 3rd order polynomial
2986
+    float shaping_factor;
2987
+    if (zeta <= 0.0f) shaping_factor = 64.0f;
2988
+    else if (zeta >= 1.0f) shaping_factor = 0.0f;
2989
+    else {
2990
+      shaping_factor = 64.44056192 + -99.02008832 * zeta;
2991
+      const float zeta2 = zeta * zeta;
2992
+      shaping_factor += -7.58095488 * zeta2;
2993
+      const float zeta3 = zeta2 * zeta;
2994
+      shaping_factor += 43.073216 * zeta3;
2995
+    }
2996
+
2997
+    const bool was_on = hal.isr_state();
2998
+    hal.isr_off();
2999
+    TERN_(HAS_SHAPING_X, if (axis == X_AXIS) { shaping_x.factor = floor(shaping_factor); shaping_x.zeta = zeta; })
3000
+    TERN_(HAS_SHAPING_Y, if (axis == Y_AXIS) { shaping_y.factor = floor(shaping_factor); shaping_y.zeta = zeta; })
3001
+    if (was_on) hal.isr_on();
3002
+  }
3003
+
3004
+  float Stepper::get_shaping_damping_ratio(const AxisEnum axis) {
3005
+    TERN_(HAS_SHAPING_X, if (axis == X_AXIS) return shaping_x.zeta);
3006
+    TERN_(HAS_SHAPING_Y, if (axis == Y_AXIS) return shaping_y.zeta);
3007
+    return -1;
3008
+  }
3009
+
3010
+  void Stepper::set_shaping_frequency(const AxisEnum axis, const float freq) {
3011
+    TERN_(HAS_SHAPING_X, if (axis == X_AXIS) { DelayTimeManager::set_delay(axis, float(uint32_t(STEPPER_TIMER_RATE) / 2) / freq); shaping_x.frequency = freq; })
3012
+    TERN_(HAS_SHAPING_Y, if (axis == Y_AXIS) { DelayTimeManager::set_delay(axis, float(uint32_t(STEPPER_TIMER_RATE) / 2) / freq); shaping_y.frequency = freq; })
3013
+  }
3014
+
3015
+  float Stepper::get_shaping_frequency(const AxisEnum axis) {
3016
+    TERN_(HAS_SHAPING_X, if (axis == X_AXIS) return shaping_x.frequency);
3017
+    TERN_(HAS_SHAPING_Y, if (axis == Y_AXIS) return shaping_y.frequency);
3018
+    return -1;
3019
+  }
3020
+#endif
3021
+
2819 3022
 /**
2820 3023
  * Set the stepper positions directly in steps
2821 3024
  *
@@ -3021,7 +3224,7 @@ void Stepper::report_positions() {
3021 3224
 
3022 3225
   #if EXTRA_CYCLES_BABYSTEP > 20
3023 3226
     #define _SAVE_START() const hal_timer_t pulse_start = HAL_timer_get_count(MF_TIMER_PULSE)
3024
-    #define _PULSE_WAIT() while (EXTRA_CYCLES_BABYSTEP > (uint32_t)(HAL_timer_get_count(MF_TIMER_PULSE) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
3227
+    #define _PULSE_WAIT() while (EXTRA_CYCLES_BABYSTEP > uint32_t(HAL_timer_get_count(MF_TIMER_PULSE) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
3025 3228
   #else
3026 3229
     #define _SAVE_START() NOOP
3027 3230
     #if EXTRA_CYCLES_BABYSTEP > 0

+ 134
- 1
Marlin/src/module/stepper.h View File

@@ -312,6 +312,117 @@ constexpr ena_mask_t enable_overlap[] = {
312 312
 
313 313
 //static_assert(!any_enable_overlap(), "There is some overlap.");
314 314
 
315
+#if ENABLED(INPUT_SHAPING)
316
+
317
+  typedef IF<ENABLED(__AVR__), uint16_t, uint32_t>::type shaping_time_t;
318
+
319
+  // These constexpr are used to calculate the shaping queue buffer sizes
320
+  constexpr xyze_float_t max_feedrate = DEFAULT_MAX_FEEDRATE;
321
+  constexpr xyze_float_t steps_per_unit = DEFAULT_AXIS_STEPS_PER_UNIT;
322
+  constexpr float max_steprate = _MAX(LOGICAL_AXIS_LIST(
323
+                                      max_feedrate.e * steps_per_unit.e,
324
+                                      max_feedrate.x * steps_per_unit.x,
325
+                                      max_feedrate.y * steps_per_unit.y,
326
+                                      max_feedrate.z * steps_per_unit.z,
327
+                                      max_feedrate.i * steps_per_unit.i,
328
+                                      max_feedrate.j * steps_per_unit.j,
329
+                                      max_feedrate.k * steps_per_unit.k,
330
+                                      max_feedrate.u * steps_per_unit.u,
331
+                                      max_feedrate.v * steps_per_unit.v,
332
+                                      max_feedrate.w * steps_per_unit.w
333
+                                    ));
334
+  constexpr uint16_t shaping_dividends = max_steprate / _MIN(0x7FFFFFFFL OPTARG(HAS_SHAPING_X, SHAPING_FREQ_X) OPTARG(HAS_SHAPING_Y, SHAPING_FREQ_Y)) / 2 + 3;
335
+  constexpr uint16_t shaping_segments = max_steprate / (MIN_STEPS_PER_SEGMENT) / _MIN(0x7FFFFFFFL OPTARG(HAS_SHAPING_X, SHAPING_FREQ_X) OPTARG(HAS_SHAPING_Y, SHAPING_FREQ_Y)) / 2 + 3;
336
+
337
+  class DelayTimeManager {
338
+    private:
339
+      static shaping_time_t now;
340
+      #ifdef HAS_SHAPING_X
341
+        static shaping_time_t delay_x;
342
+      #endif
343
+      #ifdef HAS_SHAPING_Y
344
+        static shaping_time_t delay_y;
345
+      #endif
346
+    public:
347
+      static void decrement_delays(const shaping_time_t interval) { now += interval; }
348
+      static void set_delay(const AxisEnum axis, const shaping_time_t delay) {
349
+        TERN_(HAS_SHAPING_X, if (axis == X_AXIS) delay_x = delay);
350
+        TERN_(HAS_SHAPING_Y, if (axis == Y_AXIS) delay_y = delay);
351
+      }
352
+  };
353
+
354
+  template<int SIZE>
355
+  class DelayQueue : public DelayTimeManager {
356
+    protected:
357
+      shaping_time_t times[SIZE];
358
+      uint16_t tail = 0 OPTARG(HAS_SHAPING_X, head_x = 0) OPTARG(HAS_SHAPING_Y, head_y = 0);
359
+
360
+    public:
361
+      void enqueue() {
362
+        times[tail] = now;
363
+        if (++tail == SIZE) tail = 0;
364
+      }
365
+      #ifdef HAS_SHAPING_X
366
+        shaping_time_t peek_x() {
367
+          if (head_x != tail) return times[head_x] + delay_x - now;
368
+          else return shaping_time_t(-1);
369
+        }
370
+        void dequeue_x() { if (++head_x == SIZE) head_x = 0; }
371
+        bool empty_x() { return head_x == tail; }
372
+        uint16_t free_count_x() { return head_x > tail ? head_x - tail - 1 : head_x + SIZE - tail - 1; }
373
+      #endif
374
+      #ifdef HAS_SHAPING_Y
375
+        shaping_time_t peek_y() {
376
+          if (head_y != tail) return times[head_y] + delay_y - now;
377
+          else return shaping_time_t(-1);
378
+        }
379
+        void dequeue_y() { if (++head_y == SIZE) head_y = 0; }
380
+        bool empty_y() { return head_y == tail; }
381
+        uint16_t free_count_y() { return head_y > tail ? head_y - tail - 1 : head_y + SIZE - tail - 1; }
382
+      #endif
383
+      void purge() { auto temp = TERN_(HAS_SHAPING_X, head_x) = TERN_(HAS_SHAPING_Y, head_y) = tail; UNUSED(temp);}
384
+  };
385
+
386
+  class ParamDelayQueue : public DelayQueue<shaping_segments> {
387
+    private:
388
+      #ifdef HAS_SHAPING_X
389
+        int32_t params_x[shaping_segments];
390
+      #endif
391
+      #ifdef HAS_SHAPING_Y
392
+        int32_t params_y[shaping_segments];
393
+      #endif
394
+
395
+    public:
396
+      void enqueue(const int32_t param_x, const int32_t param_y) {
397
+        TERN(HAS_SHAPING_X, params_x[DelayQueue<shaping_segments>::tail] = param_x, UNUSED(param_x));
398
+        TERN(HAS_SHAPING_Y, params_y[DelayQueue<shaping_segments>::tail] = param_y, UNUSED(param_y));
399
+        DelayQueue<shaping_segments>::enqueue();
400
+      }
401
+      #ifdef HAS_SHAPING_X
402
+        const int32_t dequeue_x() {
403
+          const int32_t result = params_x[DelayQueue<shaping_segments>::head_x];
404
+          DelayQueue<shaping_segments>::dequeue_x();
405
+          return result;
406
+        }
407
+      #endif
408
+      #ifdef HAS_SHAPING_Y
409
+        const int32_t dequeue_y() {
410
+          const int32_t result = params_y[DelayQueue<shaping_segments>::head_y];
411
+          DelayQueue<shaping_segments>::dequeue_y();
412
+          return result;
413
+        }
414
+      #endif
415
+  };
416
+
417
+  struct ShapeParams {
418
+    float frequency;
419
+    float zeta;
420
+    uint8_t factor;
421
+    int32_t dividend;
422
+  };
423
+
424
+#endif // INPUT_SHAPING
425
+
315 426
 //
316 427
 // Stepper class definition
317 428
 //
@@ -391,7 +502,7 @@ class Stepper {
391 502
 
392 503
     // Delta error variables for the Bresenham line tracer
393 504
     static xyze_long_t delta_error;
394
-    static xyze_ulong_t advance_dividend;
505
+    static xyze_long_t advance_dividend;
395 506
     static uint32_t advance_divisor,
396 507
                     step_events_completed,  // The number of step events executed in the current block
397 508
                     accelerate_until,       // The point from where we need to stop acceleration
@@ -416,6 +527,17 @@ class Stepper {
416 527
       static bool bezier_2nd_half; // If Bézier curve has been initialized or not
417 528
     #endif
418 529
 
530
+    #if ENABLED(INPUT_SHAPING)
531
+      static ParamDelayQueue shaping_dividend_queue;
532
+      static DelayQueue<shaping_dividends> shaping_queue;
533
+      #if HAS_SHAPING_X
534
+        static ShapeParams shaping_x;
535
+      #endif
536
+      #if HAS_SHAPING_Y
537
+        static ShapeParams shaping_y;
538
+      #endif
539
+    #endif
540
+
419 541
     #if ENABLED(LIN_ADVANCE)
420 542
       static constexpr uint32_t LA_ADV_NEVER = 0xFFFFFFFF;
421 543
       static uint32_t nextAdvanceISR,
@@ -475,6 +597,10 @@ class Stepper {
475 597
     // The stepper block processing ISR phase
476 598
     static uint32_t block_phase_isr();
477 599
 
600
+    #if ENABLED(INPUT_SHAPING)
601
+      static void shaping_isr();
602
+    #endif
603
+
478 604
     #if ENABLED(LIN_ADVANCE)
479 605
       // The Linear advance ISR phase
480 606
       static void advance_isr();
@@ -628,6 +754,13 @@ class Stepper {
628 754
       set_directions();
629 755
     }
630 756
 
757
+    #if ENABLED(INPUT_SHAPING)
758
+      static void set_shaping_damping_ratio(const AxisEnum axis, const float zeta);
759
+      static float get_shaping_damping_ratio(const AxisEnum axis);
760
+      static void set_shaping_frequency(const AxisEnum axis, const float freq);
761
+      static float get_shaping_frequency(const AxisEnum axis);
762
+    #endif
763
+
631 764
   private:
632 765
 
633 766
     // Set the current position in steps

+ 1
- 1
buildroot/tests/mega2560 View File

@@ -80,7 +80,7 @@ opt_set MOTHERBOARD BOARD_AZTEEG_X3_PRO MIXING_STEPPERS 5 LCD_LANGUAGE ru \
80 80
         FIL_RUNOUT2_PIN 16 FIL_RUNOUT3_PIN 17 FIL_RUNOUT4_PIN 4 FIL_RUNOUT5_PIN 5
81 81
 opt_enable MIXING_EXTRUDER GRADIENT_MIX GRADIENT_VTOOL CR10_STOCKDISPLAY \
82 82
            USE_CONTROLLER_FAN CONTROLLER_FAN_EDITABLE CONTROLLER_FAN_IGNORE_Z \
83
-           FILAMENT_RUNOUT_SENSOR ADVANCED_PAUSE_FEATURE NOZZLE_PARK_FEATURE
83
+           FILAMENT_RUNOUT_SENSOR ADVANCED_PAUSE_FEATURE NOZZLE_PARK_FEATURE INPUT_SHAPING
84 84
 opt_disable DISABLE_INACTIVE_EXTRUDER
85 85
 exec_test $1 $2 "Azteeg X3 | Mixing Extruder (x5) | Gradient Mix | Greek" "$3"
86 86
 

+ 1
- 0
ini/features.ini View File

@@ -187,6 +187,7 @@ HAS_DUPLICATION_MODE                   = src_filter=+<src/gcode/control/M605.cpp
187 187
 LIN_ADVANCE                            = src_filter=+<src/gcode/feature/advance>
188 188
 PHOTO_GCODE                            = src_filter=+<src/gcode/feature/camera>
189 189
 CONTROLLER_FAN_EDITABLE                = src_filter=+<src/gcode/feature/controllerfan>
190
+INPUT_SHAPING                          = src_filter=+<src/gcode/feature/input_shaping>
190 191
 GCODE_MACROS                           = src_filter=+<src/gcode/feature/macro>
191 192
 GRADIENT_MIX                           = src_filter=+<src/gcode/feature/mixing/M166.cpp>
192 193
 HAS_SAVED_POSITIONS                    = src_filter=+<src/gcode/feature/pause/G60.cpp> +<src/gcode/feature/pause/G61.cpp>

+ 1
- 0
platformio.ini View File

@@ -192,6 +192,7 @@ default_src_filter = +<src/*> -<src/config> -<src/HAL> +<src/HAL/shared> -<src/t
192 192
   -<src/gcode/feature/advance>
193 193
   -<src/gcode/feature/camera>
194 194
   -<src/gcode/feature/i2c>
195
+  -<src/gcode/feature/input_shaping>
195 196
   -<src/gcode/feature/L6470>
196 197
   -<src/gcode/feature/leds/M150.cpp>
197 198
   -<src/gcode/feature/leds/M7219.cpp>

Loading…
Cancel
Save