소스 검색

Various Laser / Spindle improvements (#15335)

Ben 5 년 전
부모
커밋
df8b7dfc40
No account linked to committer's email address

+ 107
- 15
Marlin/Configuration_adv.h 파일 보기

2662
   #define SPINDLE_LASER_ACTIVE_HIGH     false  // Set to "true" if the on/off function is active HIGH
2662
   #define SPINDLE_LASER_ACTIVE_HIGH     false  // Set to "true" if the on/off function is active HIGH
2663
   #define SPINDLE_LASER_PWM             true   // Set to "true" if your controller supports setting the speed/power
2663
   #define SPINDLE_LASER_PWM             true   // Set to "true" if your controller supports setting the speed/power
2664
   #define SPINDLE_LASER_PWM_INVERT      true   // Set to "true" if the speed/power goes up when you want it to go slower
2664
   #define SPINDLE_LASER_PWM_INVERT      true   // Set to "true" if the speed/power goes up when you want it to go slower
2665
-  #define SPINDLE_LASER_POWERUP_DELAY   5000   // (ms) Delay to allow the spindle/laser to come up to speed/power
2666
-  #define SPINDLE_LASER_POWERDOWN_DELAY 5000   // (ms) Delay to allow the spindle to stop
2665
+
2666
+  #define SPINDLE_LASER_FREQUENCY       2500   // (Hz) Spindle/laser frequency (only on supported HALs: AVR and LPC)
2667
+
2668
+  /**
2669
+   * Speed / Power can be set ('M3 S') and displayed in terms of:
2670
+   *  - PWM     (S0 - S255)
2671
+   *  - PERCENT (S0 - S100)
2672
+   *  - RPM     (S0 - S50000)  Best for use with a spindle
2673
+   */
2674
+  #define CUTTER_POWER_DISPLAY PWM
2675
+
2676
+  /**
2677
+   * Relative mode uses relative range (SPEED_POWER_MIN to SPEED_POWER_MAX) instead of normal range (0 to SPEED_POWER_MAX)
2678
+   * Best use with SuperPID router controller where for example S0 = 5,000 RPM and S255 = 30,000 RPM
2679
+   */
2680
+  //#define CUTTER_POWER_RELATIVE              // Set speed proportional to [SPEED_POWER_MIN...SPEED_POWER_MAX] instead of directly
2667
 
2681
 
2668
   #if ENABLED(SPINDLE_FEATURE)
2682
   #if ENABLED(SPINDLE_FEATURE)
2669
     //#define SPINDLE_CHANGE_DIR               // Enable if your spindle controller can change spindle direction
2683
     //#define SPINDLE_CHANGE_DIR               // Enable if your spindle controller can change spindle direction
2670
     #define SPINDLE_CHANGE_DIR_STOP            // Enable if the spindle should stop before changing spin direction
2684
     #define SPINDLE_CHANGE_DIR_STOP            // Enable if the spindle should stop before changing spin direction
2671
     #define SPINDLE_INVERT_DIR          false  // Set to "true" if the spin direction is reversed
2685
     #define SPINDLE_INVERT_DIR          false  // Set to "true" if the spin direction is reversed
2672
 
2686
 
2687
+    #define SPINDLE_LASER_POWERUP_DELAY   5000 // (ms) Delay to allow the spindle/laser to come up to speed/power
2688
+    #define SPINDLE_LASER_POWERDOWN_DELAY 5000 // (ms) Delay to allow the spindle to stop
2689
+
2673
     /**
2690
     /**
2674
-     *  The M3 & M4 commands use the following equation to convert PWM duty cycle to speed/power
2691
+     * M3/M4 uses the following equation to convert speed/power to PWM duty cycle
2692
+     * Power = ((DC / 255 * 100) - SPEED_POWER_INTERCEPT)) * (1 / SPEED_POWER_SLOPE)
2693
+     *   where PWM DC varies from 0 to 255
2675
      *
2694
      *
2676
-     *  SPEED/POWER = PWM duty cycle * SPEED_POWER_SLOPE + SPEED_POWER_INTERCEPT
2677
-     *    where PWM duty cycle varies from 0 to 255
2678
-     *
2679
-     *  set the following for your controller (ALL MUST BE SET)
2695
+     * Set these required parameters for your controller
2680
      */
2696
      */
2681
-    #define SPEED_POWER_SLOPE    118.4
2682
-    #define SPEED_POWER_INTERCEPT  0
2683
-    #define SPEED_POWER_MIN     5000
2684
-    #define SPEED_POWER_MAX    30000    // SuperPID router controller 0 - 30,000 RPM
2697
+    #define SPEED_POWER_SLOPE           118.4  // SPEED_POWER_SLOPE = SPEED_POWER_MAX / 255
2698
+    #define SPEED_POWER_INTERCEPT         0
2699
+    #define SPEED_POWER_MIN            5000
2700
+    #define SPEED_POWER_MAX           30000    // SuperPID router controller 0 - 30,000 RPM
2701
+    #define SPEED_POWER_STARTUP       25000    // The default value for speed power when M3 is called without arguments
2702
+
2685
   #else
2703
   #else
2686
-    #define SPEED_POWER_SLOPE      0.3922
2687
-    #define SPEED_POWER_INTERCEPT  0
2688
-    #define SPEED_POWER_MIN       10
2689
-    #define SPEED_POWER_MAX      100    // 0-100%
2704
+
2705
+    #define SPEED_POWER_SLOPE             0.3922 // SPEED_POWER_SLOPE = SPEED_POWER_MAX / 255
2706
+    #define SPEED_POWER_INTERCEPT         0
2707
+    #define SPEED_POWER_MIN               0
2708
+    #define SPEED_POWER_MAX             100    // 0-100%
2709
+    #define SPEED_POWER_STARTUP          80    // The default value for speed power when M3 is called without arguments
2710
+
2711
+    /**
2712
+     * Enable inline laser power to be handled in the planner / stepper routines.
2713
+     * Inline power is specified by the I (inline) flag in an M3 command (e.g., M3 S20 I)
2714
+     * or by the 'S' parameter in G0/G1/G2/G3 moves (see LASER_MOVE_POWER).
2715
+     *
2716
+     * This allows the laser to keep in perfect sync with the planner and removes
2717
+     * the powerup/down delay since lasers require negligible time.
2718
+     */
2719
+    #define LASER_POWER_INLINE
2720
+
2721
+    #if ENABLED(LASER_POWER_INLINE)
2722
+      /**
2723
+       * Scale the laser's power in proportion to the movement rate.
2724
+       *
2725
+       * - Sets the entry power proportional to the entry speed over the nominal speed.
2726
+       * - Ramps the power up every N steps to approximate the speed trapezoid.
2727
+       * - Due to the limited power resolution this is only approximate.
2728
+       */
2729
+      #define LASER_POWER_INLINE_TRAPEZOID
2730
+
2731
+      /**
2732
+       * Continuously calculate the current power (nominal_power * current_rate / nominal_rate).
2733
+       * Required for accurate power with non-trapezoidal acceleration (e.g., S_CURVE_ACCELERATION).
2734
+       * This is a costly calculation so this option is discouraged on 8-bit AVR boards.
2735
+       *
2736
+       * LASER_POWER_INLINE_TRAPEZOID_CONT_PER defines how many step cycles there are between power updates. If your
2737
+       * board isn't able to generate steps fast enough (and you are using LASER_POWER_INLINE_TRAPEZOID_CONT), increase this.
2738
+       * Note that when this is zero it means it occurs every cycle; 1 means a delay wait one cycle then run, etc.
2739
+       */
2740
+      //#define LASER_POWER_INLINE_TRAPEZOID_CONT
2741
+
2742
+      /**
2743
+       * Stepper iterations between power updates. Increase this value if the board
2744
+       * can't keep up with the processing demands of LASER_POWER_INLINE_TRAPEZOID_CONT.
2745
+       * Disable (or set to 0) to recalculate power on every stepper iteration.
2746
+       */
2747
+      //#define LASER_POWER_INLINE_TRAPEZOID_CONT_PER 10
2748
+
2749
+      /**
2750
+       * Include laser power in G0/G1/G2/G3/G5 commands with the 'S' parameter
2751
+       */
2752
+      //#define LASER_MOVE_POWER
2753
+
2754
+      #if ENABLED(LASER_MOVE_POWER)
2755
+        // Turn off the laser on G0 moves with no power parameter.
2756
+        // If a power parameter is provided, use that instead.
2757
+        //#define LASER_MOVE_G0_OFF
2758
+      #endif
2759
+
2760
+      /**
2761
+       * Inline flag inverted
2762
+       *
2763
+       * WARNING: M5 will NOT turn off the laser unless another move
2764
+       *          is done (so G-code files must end with 'M5 I').
2765
+       */
2766
+      //#define LASER_POWER_INLINE_INVERT
2767
+
2768
+      /**
2769
+       * Continuously apply inline power. ('M3 S3' == 'G1 S3' == 'M3 S3 I')
2770
+       *
2771
+       * The laser might do some weird things, so only enable this
2772
+       * feature if you understand the implications.
2773
+       */
2774
+      //#define LASER_POWER_INLINE_CONTINUOUS
2775
+
2776
+    #else
2777
+
2778
+      #define SPINDLE_LASER_POWERUP_DELAY     50 // (ms) Delay to allow the spindle/laser to come up to speed/power
2779
+      #define SPINDLE_LASER_POWERDOWN_DELAY   50 // (ms) Delay to allow the spindle to stop
2780
+
2781
+    #endif
2690
   #endif
2782
   #endif
2691
 #endif
2783
 #endif
2692
 
2784
 

+ 2
- 0
Marlin/src/HAL/AVR/HAL.h 파일 보기

395
 // AVR compatibility
395
 // AVR compatibility
396
 #define strtof strtod
396
 #define strtof strtod
397
 
397
 
398
+#define HAL_CAN_SET_PWM_FREQ   // This HAL supports PWM Frequency adjustment
399
+
398
 /**
400
 /**
399
  *  set_pwm_frequency
401
  *  set_pwm_frequency
400
  *  Sets the frequency of the timer corresponding to the provided pin
402
  *  Sets the frequency of the timer corresponding to the provided pin

+ 2
- 2
Marlin/src/HAL/AVR/fast_pwm.cpp 파일 보기

23
 
23
 
24
 #include "../../inc/MarlinConfigPre.h"
24
 #include "../../inc/MarlinConfigPre.h"
25
 
25
 
26
-#if ENABLED(FAST_PWM_FAN) || SPINDLE_LASER_PWM
26
+#if NEEDS_HARDWARE_PWM // Specific meta-flag for features that mandate PWM
27
 
27
 
28
 #include "HAL.h"
28
 #include "HAL.h"
29
 
29
 
278
   }
278
   }
279
 }
279
 }
280
 
280
 
281
-#endif // FAST_PWM_FAN || SPINDLE_LASER_PWM
281
+#endif // NEEDS_HARDWARE_PWM
282
 #endif // __AVR__
282
 #endif // __AVR__

+ 2
- 0
Marlin/src/HAL/LPC1768/HAL.h 파일 보기

197
 #define PLATFORM_M997_SUPPORT
197
 #define PLATFORM_M997_SUPPORT
198
 void flashFirmware(const int16_t);
198
 void flashFirmware(const int16_t);
199
 
199
 
200
+#define HAL_CAN_SET_PWM_FREQ   // This HAL supports PWM Frequency adjustment
201
+
200
 /**
202
 /**
201
  * set_pwm_frequency
203
  * set_pwm_frequency
202
  *  Set the frequency of the timer corresponding to the provided pin
204
  *  Set the frequency of the timer corresponding to the provided pin

+ 1
- 1
Marlin/src/HAL/LPC1768/fast_pwm.cpp 파일 보기

24
 
24
 
25
 #include "../../inc/MarlinConfigPre.h"
25
 #include "../../inc/MarlinConfigPre.h"
26
 
26
 
27
-#if ENABLED(FAST_PWM_FAN) || SPINDLE_LASER_PWM
27
+#if NEEDS_HARDWARE_PWM // Specific meta-flag for features that mandate PWM
28
 
28
 
29
 #include <pwm.h>
29
 #include <pwm.h>
30
 
30
 

+ 17
- 5
Marlin/src/MarlinCore.cpp 파일 보기

420
     #if DISABLED(SD_ABORT_NO_COOLDOWN)
420
     #if DISABLED(SD_ABORT_NO_COOLDOWN)
421
       thermalManager.disable_all_heaters();
421
       thermalManager.disable_all_heaters();
422
     #endif
422
     #endif
423
-    thermalManager.zero_fan_speeds();
423
+    #if !HAS_CUTTER
424
+      thermalManager.zero_fan_speeds();
425
+    #else
426
+      cutter.kill();              // Full cutter shutdown including ISR control
427
+    #endif
424
     wait_for_heatup = false;
428
     wait_for_heatup = false;
425
     #if ENABLED(POWER_LOSS_RECOVERY)
429
     #if ENABLED(POWER_LOSS_RECOVERY)
426
       recovery.purge();
430
       recovery.purge();
741
 void kill(PGM_P const lcd_error/*=nullptr*/, PGM_P const lcd_component/*=nullptr*/, const bool steppers_off/*=false*/) {
745
 void kill(PGM_P const lcd_error/*=nullptr*/, PGM_P const lcd_component/*=nullptr*/, const bool steppers_off/*=false*/) {
742
   thermalManager.disable_all_heaters();
746
   thermalManager.disable_all_heaters();
743
 
747
 
748
+  #if HAS_CUTTER
749
+    cutter.kill();              // Full cutter shutdown including ISR control
750
+  #endif
751
+
744
   SERIAL_ERROR_MSG(STR_ERR_KILLED);
752
   SERIAL_ERROR_MSG(STR_ERR_KILLED);
745
 
753
 
746
   #if HAS_DISPLAY
754
   #if HAS_DISPLAY
770
   // Reiterate heaters off
778
   // Reiterate heaters off
771
   thermalManager.disable_all_heaters();
779
   thermalManager.disable_all_heaters();
772
 
780
 
781
+  #if HAS_CUTTER
782
+    cutter.kill();  // Reiterate cutter shutdown
783
+  #endif
784
+
773
   // Power off all steppers (for M112) or just the E steppers
785
   // Power off all steppers (for M112) or just the E steppers
774
   steppers_off ? disable_all_steppers() : disable_e_steppers();
786
   steppers_off ? disable_all_steppers() : disable_e_steppers();
775
 
787
 
789
     // Wait for kill to be pressed
801
     // Wait for kill to be pressed
790
     while (READ(KILL_PIN)) watchdog_refresh();
802
     while (READ(KILL_PIN)) watchdog_refresh();
791
 
803
 
792
-    void (*resetFunc)() = 0;  // Declare resetFunc() at address 0
804
+    void (*resetFunc)() = 0;      // Declare resetFunc() at address 0
793
     resetFunc();                  // Jump to address 0
805
     resetFunc();                  // Jump to address 0
794
 
806
 
795
-  #else // !HAS_KILL
807
+  #else
796
 
808
 
797
-    for (;;) watchdog_refresh(); // Wait for reset
809
+    for (;;) watchdog_refresh();  // Wait for reset
798
 
810
 
799
-  #endif // !HAS_KILL
811
+  #endif
800
 }
812
 }
801
 
813
 
802
 /**
814
 /**

+ 0
- 9
Marlin/src/core/drivers.h 파일 보기

174
 // Defines that can't be evaluated now
174
 // Defines that can't be evaluated now
175
 #define HAS_TMC_SW_SERIAL ANY_AXIS_HAS(SW_SERIAL)
175
 #define HAS_TMC_SW_SERIAL ANY_AXIS_HAS(SW_SERIAL)
176
 
176
 
177
-//
178
-// Stretching 'drivers.h' to include LPC/SAMD51 SD options
179
-//
180
-#define _SDCARD_LCD          1
181
-#define _SDCARD_ONBOARD      2
182
-#define _SDCARD_CUSTOM_CABLE 3
183
-#define _SDCARD_ID(V) _CAT(_SDCARD_, V)
184
-#define SD_CONNECTION_IS(V) (_SDCARD_ID(SDCARD_CONNECTION) == _SDCARD_ID(V))
185
-
186
 #if HAS_DRIVER(L6470) || HAS_DRIVER(L6474) || HAS_DRIVER(L6480) || HAS_DRIVER(POWERSTEP01)
177
 #if HAS_DRIVER(L6470) || HAS_DRIVER(L6474) || HAS_DRIVER(L6480) || HAS_DRIVER(POWERSTEP01)
187
   #define HAS_L64XX 1
178
   #define HAS_L64XX 1
188
 #endif
179
 #endif

+ 29
- 20
Marlin/src/feature/spindle_laser.cpp 파일 보기

32
 
32
 
33
 SpindleLaser cutter;
33
 SpindleLaser cutter;
34
 
34
 
35
-cutter_power_t SpindleLaser::power; // = 0
36
-
35
+cutter_power_t SpindleLaser::power;
36
+bool SpindleLaser::isOn;                                                       // state to determine when to apply setPower to power
37
+cutter_setPower_t SpindleLaser::setPower = interpret_power(SPEED_POWER_MIN);   // spindle/laser speed/power control in PWM, Percentage or RPM
38
+#if ENABLED(MARLIN_DEV_MODE)
39
+  cutter_frequency_t SpindleLaser::frequency;                                  // setting PWM frequency; range: 2K - 50K
40
+#endif
37
 #define SPINDLE_LASER_PWM_OFF ((SPINDLE_LASER_PWM_INVERT) ? 255 : 0)
41
 #define SPINDLE_LASER_PWM_OFF ((SPINDLE_LASER_PWM_INVERT) ? 255 : 0)
38
 
42
 
43
+//
44
+// Init the cutter to a safe OFF state
45
+//
39
 void SpindleLaser::init() {
46
 void SpindleLaser::init() {
40
   OUT_WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_HIGH); // Init spindle to off
47
   OUT_WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_HIGH); // Init spindle to off
41
   #if ENABLED(SPINDLE_CHANGE_DIR)
48
   #if ENABLED(SPINDLE_CHANGE_DIR)
45
     SET_PWM(SPINDLE_LASER_PWM_PIN);
52
     SET_PWM(SPINDLE_LASER_PWM_PIN);
46
     analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF);  // set to lowest speed
53
     analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF);  // set to lowest speed
47
   #endif
54
   #endif
55
+  #if ENABLED(HAL_CAN_SET_PWM_FREQ) && defined(SPINDLE_LASER_FREQUENCY)
56
+    set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
57
+    #if ENABLED(MARLIN_DEV_MODE)
58
+      frequency = SPINDLE_LASER_FREQUENCY;
59
+    #endif
60
+  #endif
48
 }
61
 }
49
 
62
 
50
 #if ENABLED(SPINDLE_LASER_PWM)
63
 #if ENABLED(SPINDLE_LASER_PWM)
51
 
64
 
52
   /**
65
   /**
53
-   * ocr_val_mode() is used for debugging and to get the points needed to compute the RPM vs ocr_val line
54
-   *
55
-   * it accepts inputs of 0-255
56
-   */
66
+  * Set the cutter PWM directly to the given ocr value
67
+  **/
57
   void SpindleLaser::set_ocr(const uint8_t ocr) {
68
   void SpindleLaser::set_ocr(const uint8_t ocr) {
58
-    WRITE(SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_HIGH); // turn spindle on (active low)
69
+    WRITE(SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_HIGH); // turn spindle on
59
     analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF);
70
     analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF);
60
   }
71
   }
61
 
72
 
62
 #endif
73
 #endif
63
 
74
 
75
+//
76
+// Set cutter ON state (and PWM) to the given cutter power value
77
+//
64
 void SpindleLaser::apply_power(const cutter_power_t inpow) {
78
 void SpindleLaser::apply_power(const cutter_power_t inpow) {
65
   static cutter_power_t last_power_applied = 0;
79
   static cutter_power_t last_power_applied = 0;
66
   if (inpow == last_power_applied) return;
80
   if (inpow == last_power_applied) return;
67
   last_power_applied = inpow;
81
   last_power_applied = inpow;
68
   #if ENABLED(SPINDLE_LASER_PWM)
82
   #if ENABLED(SPINDLE_LASER_PWM)
69
-    if (enabled()) {
70
-      #define _scaled(F) ((F - (SPEED_POWER_INTERCEPT)) * inv_slope)
71
-      constexpr float inv_slope = RECIPROCAL(SPEED_POWER_SLOPE),
72
-                      min_ocr = _scaled(SPEED_POWER_MIN),
73
-                      max_ocr = _scaled(SPEED_POWER_MAX);
74
-      int16_t ocr_val;
75
-           if (inpow <= SPEED_POWER_MIN) ocr_val = min_ocr;       // Use minimum if set below
76
-      else if (inpow >= SPEED_POWER_MAX) ocr_val = max_ocr;       // Use maximum if set above
77
-      else ocr_val = _scaled(inpow);                              // Use calculated OCR value
78
-      set_ocr(ocr_val & 0xFF);                                    // ...limited to Atmel PWM max
79
-    }
83
+    if (enabled())
84
+      set_ocr(translate_power(inpow));
80
     else {
85
     else {
81
-      WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_HIGH);   // Turn spindle off (active low)
82
-      analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF);  // Only write low byte
86
+      WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_HIGH);                           // Turn spindle off
87
+      analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF);                   // Only write low byte
83
     }
88
     }
84
   #else
89
   #else
85
     WRITE(SPINDLE_LASER_ENA_PIN, (SPINDLE_LASER_ACTIVE_HIGH) ? enabled() : !enabled());
90
     WRITE(SPINDLE_LASER_ENA_PIN, (SPINDLE_LASER_ACTIVE_HIGH) ? enabled() : !enabled());
88
 
93
 
89
 #if ENABLED(SPINDLE_CHANGE_DIR)
94
 #if ENABLED(SPINDLE_CHANGE_DIR)
90
 
95
 
96
+  //
97
+  // Set the spindle direction and apply immediately
98
+  // Stop on direction change if SPINDLE_STOP_ON_DIR_CHANGE is enabled
99
+  //
91
   void SpindleLaser::set_direction(const bool reverse) {
100
   void SpindleLaser::set_direction(const bool reverse) {
92
     const bool dir_state = (reverse == SPINDLE_INVERT_DIR); // Forward (M3) HIGH when not inverted
101
     const bool dir_state = (reverse == SPINDLE_INVERT_DIR); // Forward (M3) HIGH when not inverted
93
     #if ENABLED(SPINDLE_STOP_ON_DIR_CHANGE)
102
     #if ENABLED(SPINDLE_STOP_ON_DIR_CHANGE)

+ 110
- 33
Marlin/src/feature/spindle_laser.h 파일 보기

28
 
28
 
29
 #include "../inc/MarlinConfig.h"
29
 #include "../inc/MarlinConfig.h"
30
 
30
 
31
-#if ENABLED(SPINDLE_FEATURE)
32
-  #define _MSG_CUTTER(M) MSG_SPINDLE_##M
33
-#else
34
-  #define _MSG_CUTTER(M) MSG_LASER_##M
35
-#endif
36
-#define MSG_CUTTER(M) _MSG_CUTTER(M)
37
-
38
-#if SPEED_POWER_MAX > 255
39
-  typedef uint16_t cutter_power_t;
40
-  #define CUTTER_MENU_TYPE uint16_5
41
-#else
42
-  typedef uint8_t cutter_power_t;
43
-  #define CUTTER_MENU_TYPE uint8
31
+#include "spindle_laser_types.h"
32
+
33
+#if ENABLED(LASER_POWER_INLINE)
34
+  #include "../module/planner.h"
44
 #endif
35
 #endif
45
 
36
 
46
 class SpindleLaser {
37
 class SpindleLaser {
47
 public:
38
 public:
39
+  static bool isOn;                             //  state to determine when to apply setPower to power
48
   static cutter_power_t power;
40
   static cutter_power_t power;
49
-  static inline uint8_t powerPercent(const uint8_t pp) { return ui8_to_percent(pp); } // for display
50
-
51
-  static void init();
52
-
53
-  static inline bool enabled() { return !!power; }
41
+  static cutter_setPower_t setPower;            //  spindle/laser menu set power; in PWM, Percentage or RPM
42
+  #if ENABLED(MARLIN_DEV_MODE)
43
+    static cutter_frequency_t frequency;        //  set PWM frequency; range: 2K-50K
44
+  #endif
54
 
45
 
55
-  static inline void set_power(const cutter_power_t pwr) { power = pwr; }
46
+  static cutter_setPower_t interpret_power(const float pwr) {     // convert speed/power to configured PWM, Percentage or RPM in relative or normal range
47
+    #if CUTTER_DISPLAY_IS(PERCENT)
48
+      return (pwr / SPEED_POWER_MAX) * 100;                                               // to percent
49
+    #elif CUTTER_DISPLAY_IS(RPM)                                                          // to RPM is unaltered
50
+      return pwr;
51
+    #else                                                                                 // to PWM
52
+      #if ENABLED(CUTTER_POWER_RELATIVE)
53
+        return (pwr - SPEED_POWER_MIN) / (SPEED_POWER_MAX - SPEED_POWER_MIN) * 255;     // using rpm range as relative percentage
54
+      #else
55
+        return (pwr / SPEED_POWER_MAX) * 255;
56
+      #endif
57
+    #endif
58
+  }
59
+  /**
60
+  * Translate speed/power --> percentage --> PWM value
61
+  **/
62
+  static cutter_power_t translate_power(const float pwr) {
63
+    float pwrpc;
64
+    #if CUTTER_DISPLAY_IS(PERCENT)
65
+      pwrpc = pwr;
66
+    #elif CUTTER_DISPLAY_IS(RPM)            // RPM to percent
67
+     #if ENABLED(CUTTER_POWER_RELATIVE)
68
+        pwrpc = (pwr - SPEED_POWER_MIN) / (SPEED_POWER_MAX - SPEED_POWER_MIN) * 100;
69
+      #else
70
+        pwrpc = pwr / SPEED_POWER_MAX * 100;
71
+      #endif
72
+    #else
73
+      return pwr;                           // PWM
74
+    #endif
56
 
75
 
57
-  static inline void refresh() { apply_power(power); }
76
+    #if ENABLED(SPINDLE_FEATURE)
77
+      #if ENABLED(CUTTER_POWER_RELATIVE)
78
+        constexpr float spmin = 0;
79
+      #else
80
+        constexpr float spmin = SPEED_POWER_MIN / SPEED_POWER_MAX * 100; // convert to percentage
81
+      #endif
82
+      constexpr float spmax = 100;
83
+    #else
84
+      constexpr float spmin = SPEED_POWER_MIN;
85
+      constexpr float spmax = SPEED_POWER_MAX;
86
+    #endif
58
 
87
 
59
-  static inline void set_enabled(const bool enable) {
60
-    const bool was = enabled();
61
-    set_power(enable ? 255 : 0);
62
-    if (was != enable) power_delay();
88
+    constexpr float inv_slope = RECIPROCAL(SPEED_POWER_SLOPE),
89
+                    min_ocr = (spmin - (SPEED_POWER_INTERCEPT)) * inv_slope,         // Minimum allowed
90
+                    max_ocr = (spmax - (SPEED_POWER_INTERCEPT)) * inv_slope;         // Maximum allowed
91
+    float ocr_val;
92
+    if (pwrpc < spmin) ocr_val = min_ocr;                                           // Use minimum if set below
93
+    else if (pwrpc > spmax) ocr_val = max_ocr;                                      // Use maximum if set above
94
+    else ocr_val = (pwrpc - (SPEED_POWER_INTERCEPT)) * inv_slope;                   // Use calculated OCR value
95
+    return ocr_val;                                                                 // ...limited to Atmel PWM max
63
   }
96
   }
64
 
97
 
98
+  static void init();
99
+
100
+  // Modifying this function should update everywhere
101
+  static inline bool enabled(const cutter_power_t pwr) { return pwr > 0; }
102
+  static inline bool enabled() { return enabled(power); }
103
+  #if ENABLED(MARLIN_DEV_MODE)
104
+    static inline void refresh_frequency() { set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); }
105
+  #endif
65
   static void apply_power(const cutter_power_t inpow);
106
   static void apply_power(const cutter_power_t inpow);
66
 
107
 
67
-  //static bool active() { return READ(SPINDLE_LASER_ENA_PIN) == SPINDLE_LASER_ACTIVE_HIGH; }
108
+  FORCE_INLINE static void refresh() { apply_power(power); }
109
+  FORCE_INLINE static void set_power(const cutter_power_t pwr) { power = pwr; refresh(); }
68
 
110
 
69
-  static void update_output();
111
+  static inline void set_enabled(const bool enable) { set_power(enable ? (power ?: interpret_power(SPEED_POWER_STARTUP)) : 0); }
70
 
112
 
71
   #if ENABLED(SPINDLE_LASER_PWM)
113
   #if ENABLED(SPINDLE_LASER_PWM)
72
     static void set_ocr(const uint8_t ocr);
114
     static void set_ocr(const uint8_t ocr);
73
-    static inline void set_ocr_power(const cutter_power_t pwr) { power = pwr; set_ocr(pwr); }
115
+    static inline void set_ocr_power(const uint8_t pwr) { power = pwr; set_ocr(pwr); }
116
+    // static uint8_t translate_power(const cutter_power_t pwr); // Used by update output for power->OCR translation
74
   #endif
117
   #endif
75
 
118
 
76
   // Wait for spindle to spin up or spin down
119
   // Wait for spindle to spin up or spin down
77
-  static inline void power_delay() {
78
-    #if SPINDLE_LASER_POWERUP_DELAY || SPINDLE_LASER_POWERDOWN_DELAY
79
-      safe_delay(enabled() ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY);
120
+  static inline void power_delay(const bool on) {
121
+    #if DISABLED(LASER_POWER_INLINE)
122
+      safe_delay(on ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY);
80
     #endif
123
     #endif
81
   }
124
   }
82
 
125
 
86
     static inline void set_direction(const bool) {}
129
     static inline void set_direction(const bool) {}
87
   #endif
130
   #endif
88
 
131
 
89
-  static inline void disable() { set_enabled(false); }
90
-  static inline void enable_forward() { set_direction(false); set_enabled(true); }
91
-  static inline void enable_reverse() { set_direction(true); set_enabled(true); }
132
+  static inline void disable() { isOn = false; set_enabled(false); }
133
+  #if HAS_LCD_MENU
134
+    static inline void enable_forward() { isOn = true; setPower ? (power = setPower) : (setPower = interpret_power(SPEED_POWER_STARTUP)); set_direction(false); set_enabled(true); }
135
+    static inline void enable_reverse() { isOn = true; setPower ? (power = setPower) : (setPower = interpret_power(SPEED_POWER_STARTUP)); set_direction(true); set_enabled(true); }
136
+  #endif
92
 
137
 
138
+  #if ENABLED(LASER_POWER_INLINE)
139
+    // Force disengage planner power control
140
+    static inline void inline_disable() { planner.settings.laser.status = 0; planner.settings.laser.power = 0; isOn = false;}
141
+
142
+    // Inline modes of all other functions; all enable planner inline power control
143
+    static inline void inline_enabled(const bool enable) { enable ? inline_power(SPEED_POWER_STARTUP) : inline_ocr_power(0); }
144
+
145
+    static void inline_power(const cutter_power_t pwr) {
146
+      #if ENABLED(SPINDLE_LASER_PWM)
147
+        inline_ocr_power(translate_power(pwr));
148
+      #else
149
+        planner.settings.laser.status = enabled(pwr) ? 0x03 : 0x01;
150
+        planner.settings.laser.power = pwr;
151
+      #endif
152
+    }
153
+
154
+    static inline void inline_direction(const bool reverse) { UNUSED(reverse); } // TODO is this ever going to be needed
155
+
156
+    #if ENABLED(SPINDLE_LASER_PWM)
157
+      static inline void inline_ocr_power(const uint8_t pwr) {
158
+        planner.settings.laser.status = pwr ? 0x03 : 0x01;
159
+        planner.settings.laser.power = pwr;
160
+      }
161
+    #endif
162
+  #endif
163
+
164
+  static inline void kill() {
165
+    #if ENABLED(LASER_POWER_INLINE)
166
+      inline_disable();
167
+    #endif
168
+    disable();
169
+  }
93
 };
170
 };
94
 
171
 
95
 extern SpindleLaser cutter;
172
 extern SpindleLaser cutter;

+ 50
- 0
Marlin/src/feature/spindle_laser_types.h 파일 보기

1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#pragma once
23
+
24
+/**
25
+ * feature/spindle_laser_types.h
26
+ * Support for Laser Power or Spindle Power & Direction
27
+ */
28
+
29
+#include "../inc/MarlinConfigPre.h"
30
+
31
+#if ENABLED(SPINDLE_FEATURE)
32
+  #define _MSG_CUTTER(M) MSG_SPINDLE_##M
33
+#else
34
+  #define _MSG_CUTTER(M) MSG_LASER_##M
35
+#endif
36
+#define MSG_CUTTER(M) _MSG_CUTTER(M)
37
+#if CUTTER_DISPLAY_IS(RPM) && SPEED_POWER_MAX > 255
38
+  #define cutter_power_t              uint16_t
39
+  #define cutter_setPower_t           uint16_t
40
+  #define CUTTER_MENU_POWER_TYPE      uint16_5
41
+#else
42
+  #define cutter_power_t              uint8_t
43
+  #define cutter_setPower_t           uint8_t
44
+  #define CUTTER_MENU_POWER_TYPE      uint8
45
+#endif
46
+
47
+#if ENABLED(MARLIN_DEV_MODE)
48
+  #define cutter_frequency_t          uint16_t
49
+  #define CUTTER_MENU_FREQUENCY_TYPE  uint16_5
50
+#endif

+ 35
- 6
Marlin/src/gcode/control/M3-M5.cpp 파일 보기

28
 #include "../../feature/spindle_laser.h"
28
 #include "../../feature/spindle_laser.h"
29
 #include "../../module/stepper.h"
29
 #include "../../module/stepper.h"
30
 
30
 
31
+inline cutter_power_t get_s_power() {
32
+  return cutter_power_t(
33
+    parser.intval('S', cutter.interpret_power(SPEED_POWER_STARTUP))
34
+  );
35
+}
36
+
31
 /**
37
 /**
32
  * Laser:
38
  * Laser:
33
  *
39
  *
71
  */
77
  */
72
 void GcodeSuite::M3_M4(const bool is_M4) {
78
 void GcodeSuite::M3_M4(const bool is_M4) {
73
 
79
 
74
-  #if ENABLED(SPINDLE_FEATURE)
75
-    planner.synchronize();   // Wait for movement to complete before changing power
80
+  #if ENABLED(LASER_POWER_INLINE)
81
+    if (parser.seen('I') == DISABLED(LASER_POWER_INLINE_INVERT)) {
82
+      // Laser power in inline mode
83
+      cutter.inline_direction(is_M4); // Should always be unused
84
+
85
+      #if ENABLED(SPINDLE_LASER_PWM)
86
+        if (parser.seen('O'))
87
+          cutter.inline_ocr_power(parser.value_byte()); // The OCR is a value from 0 to 255 (uint8_t)
88
+        else
89
+          cutter.inline_power(get_s_power());
90
+      #else
91
+        cutter.inline_enabled(true);
92
+      #endif
93
+      return;
94
+    }
95
+    // Non-inline, standard case
96
+    cutter.inline_disable(); // Prevent future blocks re-setting the power
76
   #endif
97
   #endif
77
 
98
 
99
+  planner.synchronize();   // Wait for previous movement commands (G0/G0/G2/G3) to complete before changing power
100
+
78
   cutter.set_direction(is_M4);
101
   cutter.set_direction(is_M4);
79
 
102
 
80
   #if ENABLED(SPINDLE_LASER_PWM)
103
   #if ENABLED(SPINDLE_LASER_PWM)
81
     if (parser.seenval('O'))
104
     if (parser.seenval('O'))
82
       cutter.set_ocr_power(parser.value_byte()); // The OCR is a value from 0 to 255 (uint8_t)
105
       cutter.set_ocr_power(parser.value_byte()); // The OCR is a value from 0 to 255 (uint8_t)
83
     else
106
     else
84
-      cutter.set_power(parser.intval('S', 255));
107
+      cutter.set_power(get_s_power());
85
   #else
108
   #else
86
     cutter.set_enabled(true);
109
     cutter.set_enabled(true);
87
   #endif
110
   #endif
88
 }
111
 }
89
 
112
 
90
 /**
113
 /**
91
- * M5 - Cutter OFF
114
+ * M5 - Cutter OFF (when moves are complete)
92
  */
115
  */
93
 void GcodeSuite::M5() {
116
 void GcodeSuite::M5() {
94
-  #if ENABLED(SPINDLE_FEATURE)
95
-    planner.synchronize();
117
+  #if ENABLED(LASER_POWER_INLINE)
118
+    if (parser.seen('I') == DISABLED(LASER_POWER_INLINE_INVERT)) {
119
+      cutter.inline_enabled(false); // Laser power in inline mode
120
+      return;
121
+    }
122
+    // Non-inline, standard case
123
+    cutter.inline_disable(); // Prevent future blocks re-setting the power
96
   #endif
124
   #endif
125
+  planner.synchronize();
97
   cutter.set_enabled(false);
126
   cutter.set_enabled(false);
98
 }
127
 }
99
 
128
 

+ 16
- 0
Marlin/src/gcode/gcode.cpp 파일 보기

53
   #include "../feature/cancel_object.h"
53
   #include "../feature/cancel_object.h"
54
 #endif
54
 #endif
55
 
55
 
56
+#if ENABLED(LASER_MOVE_POWER)
57
+  #include "../feature/spindle_laser.h"
58
+#endif
59
+
56
 #include "../MarlinCore.h" // for idle()
60
 #include "../MarlinCore.h" // for idle()
57
 
61
 
58
 millis_t GcodeSuite::previous_move_ms;
62
 millis_t GcodeSuite::previous_move_ms;
172
   #if BOTH(MIXING_EXTRUDER, DIRECT_MIXING_IN_G1)
176
   #if BOTH(MIXING_EXTRUDER, DIRECT_MIXING_IN_G1)
173
     M165();
177
     M165();
174
   #endif
178
   #endif
179
+
180
+  #if ENABLED(LASER_MOVE_POWER)
181
+    // Set the laser power in the planner to configure this move
182
+    if (parser.seen('S'))
183
+      cutter.inline_power(parser.value_int());
184
+    else {
185
+      #if ENABLED(LASER_MOVE_G0_OFF)
186
+        if (parser.codenum == 0)        // G0
187
+          cutter.inline_enabled(false);
188
+      #endif
189
+    }
190
+  #endif
175
 }
191
 }
176
 
192
 
177
 /**
193
 /**

+ 1
- 1
Marlin/src/gcode/motion/G0_G1.cpp 파일 보기

69
       #endif
69
       #endif
70
     #endif
70
     #endif
71
 
71
 
72
-    get_destination_from_command();                 // Process X Y Z E F parameters
72
+    get_destination_from_command();                 // Get X Y Z E F (and set cutter power)
73
 
73
 
74
     #ifdef G0_FEEDRATE
74
     #ifdef G0_FEEDRATE
75
       if (fast_move) {
75
       if (fast_move) {

+ 1
- 1
Marlin/src/gcode/motion/G2_G3.cpp 파일 보기

283
       relative_mode = true;
283
       relative_mode = true;
284
     #endif
284
     #endif
285
 
285
 
286
-    get_destination_from_command();
286
+    get_destination_from_command();   // Get X Y Z E F (and set cutter power)
287
 
287
 
288
     #if ENABLED(SF_ARC_FIX)
288
     #if ENABLED(SF_ARC_FIX)
289
       relative_mode = relative_mode_backup;
289
       relative_mode = relative_mode_backup;

+ 31
- 1
Marlin/src/inc/Conditionals_adv.h 파일 보기

116
   #define Z_STEPPER_ALIGN_AMP 1.0
116
   #define Z_STEPPER_ALIGN_AMP 1.0
117
 #endif
117
 #endif
118
 
118
 
119
-#define HAS_CUTTER EITHER(SPINDLE_FEATURE, LASER_FEATURE)
119
+//
120
+// Spindle/Laser power display types
121
+// Defined here so sanity checks can use them
122
+//
123
+#if EITHER(SPINDLE_FEATURE, LASER_FEATURE)
124
+  #define HAS_CUTTER 1
125
+  #define _CUTTER_DISP_PWM     1
126
+  #define _CUTTER_DISP_PERCENT 2
127
+  #define _CUTTER_DISP_RPM     3
128
+  #define _CUTTER_DISP(V)      _CAT(_CUTTER_DISP_, V)
129
+  #define CUTTER_DISPLAY_IS(V) (_CUTTER_DISP(CUTTER_POWER_DISPLAY) == _CUTTER_DISP(V))
130
+#endif
131
+
132
+// Add features that need hardware PWM here
133
+#if ANY(FAST_PWM_FAN, SPINDLE_LASER_PWM)
134
+  #define NEEDS_HARDWARE_PWM 1
135
+#endif
120
 
136
 
121
 #if !defined(__AVR__) || !defined(USBCON)
137
 #if !defined(__AVR__) || !defined(USBCON)
122
   // Define constants and variables for buffering serial data.
138
   // Define constants and variables for buffering serial data.
290
     #define MAXIMUM_STEPPER_RATE 250000
306
     #define MAXIMUM_STEPPER_RATE 250000
291
   #endif
307
   #endif
292
 #endif
308
 #endif
309
+
310
+//
311
+// SD Card connection methods
312
+// Defined here so pins and sanity checks can use them
313
+//
314
+#if ENABLED(SDSUPPORT)
315
+  #define _SDCARD_LCD          1
316
+  #define _SDCARD_ONBOARD      2
317
+  #define _SDCARD_CUSTOM_CABLE 3
318
+  #define _SDCARD_ID(V) _CAT(_SDCARD_, V)
319
+  #define SD_CONNECTION_IS(V) (_SDCARD_ID(SDCARD_CONNECTION) == _SDCARD_ID(V))
320
+#else
321
+  #define SD_CONNECTION_IS(...) 0
322
+#endif

+ 26
- 22
Marlin/src/inc/Conditionals_post.h 파일 보기

324
 
324
 
325
 /**
325
 /**
326
  * Override the SD_DETECT_STATE set in Configuration_adv.h
326
  * Override the SD_DETECT_STATE set in Configuration_adv.h
327
+ * and enable sharing of onboard SD host drives (all platforms but AGCM4)
327
  */
328
  */
328
 #if ENABLED(SDSUPPORT)
329
 #if ENABLED(SDSUPPORT)
329
-  #if HAS_LCD_MENU && (SD_CONNECTION_IS(LCD) || !defined(SDCARD_CONNECTION))
330
-    #undef SD_DETECT_STATE
331
-    #if ENABLED(ELB_FULL_GRAPHIC_CONTROLLER)
332
-      #define SD_DETECT_STATE HIGH
333
-    #endif
330
+
331
+  #if SD_CONNECTION_IS(ONBOARD) && DISABLED(NO_SD_HOST_DRIVE) && !defined(ARDUINO_GRAND_CENTRAL_M4)
332
+    //
333
+    // The external SD card is not used. Hardware SPI is used to access the card.
334
+    // When sharing the SD card with a PC we want the menu options to
335
+    // mount/unmount the card and refresh it. So we disable card detect.
336
+    //
337
+    #undef SD_DETECT_PIN
338
+    #define SHARED_SD_CARD
339
+  #endif
340
+
341
+  #if DISABLED(SHARED_SD_CARD)
342
+    #define INIT_SDCARD_ON_BOOT 1
334
   #endif
343
   #endif
335
-  #ifndef SD_DETECT_STATE
336
-    #define SD_DETECT_STATE LOW
344
+
345
+  #if PIN_EXISTS(SD_DETECT)
346
+    #if HAS_LCD_MENU && (SD_CONNECTION_IS(LCD) || !defined(SDCARD_CONNECTION))
347
+      #undef SD_DETECT_STATE
348
+      #if ENABLED(ELB_FULL_GRAPHIC_CONTROLLER)
349
+        #define SD_DETECT_STATE HIGH
350
+      #endif
351
+    #endif
352
+    #ifndef SD_DETECT_STATE
353
+      #define SD_DETECT_STATE LOW
354
+    #endif
337
   #endif
355
   #endif
356
+
338
 #endif
357
 #endif
339
 
358
 
340
 /**
359
 /**
2153
   #endif
2172
   #endif
2154
 #endif
2173
 #endif
2155
 
2174
 
2156
-#if ENABLED(SDSUPPORT)
2157
-  #if SD_CONNECTION_IS(ONBOARD) && DISABLED(NO_SD_HOST_DRIVE) && !defined(ARDUINO_GRAND_CENTRAL_M4)
2158
-    //
2159
-    // The external SD card is not used. Hardware SPI is used to access the card.
2160
-    // When sharing the SD card with a PC we want the menu options to
2161
-    // mount/unmount the card and refresh it. So we disable card detect.
2162
-    //
2163
-    #undef SD_DETECT_PIN
2164
-    #define SHARED_SD_CARD
2165
-  #endif
2166
-  #if DISABLED(SHARED_SD_CARD)
2167
-    #define INIT_SDCARD_ON_BOOT 1
2168
-  #endif
2169
-#endif
2170
-
2171
 #if !NUM_SERIAL
2175
 #if !NUM_SERIAL
2172
   #undef BAUD_RATE_GCODE
2176
   #undef BAUD_RATE_GCODE
2173
 #endif
2177
 #endif

+ 43
- 8
Marlin/src/inc/SanityCheck.h 파일 보기

1451
  * Deploying the Allen Key probe uses big moves in z direction. Too dangerous for an unhomed z-axis.
1451
  * Deploying the Allen Key probe uses big moves in z direction. Too dangerous for an unhomed z-axis.
1452
  */
1452
  */
1453
 #if ENABLED(Z_PROBE_ALLEN_KEY) && (Z_HOME_DIR < 0) && ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
1453
 #if ENABLED(Z_PROBE_ALLEN_KEY) && (Z_HOME_DIR < 0) && ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
1454
-  #error "You can't home to a z min endstop with a Z_PROBE_ALLEN_KEY"
1454
+  #error "You can't home to a z min endstop with a Z_PROBE_ALLEN_KEY."
1455
 #endif
1455
 #endif
1456
 
1456
 
1457
 /**
1457
 /**
2654
 
2654
 
2655
 #if ENABLED(BACKLASH_COMPENSATION)
2655
 #if ENABLED(BACKLASH_COMPENSATION)
2656
   #ifndef BACKLASH_DISTANCE_MM
2656
   #ifndef BACKLASH_DISTANCE_MM
2657
-    #error "BACKLASH_COMPENSATION requires BACKLASH_DISTANCE_MM"
2657
+    #error "BACKLASH_COMPENSATION requires BACKLASH_DISTANCE_MM."
2658
   #elif !defined(BACKLASH_CORRECTION)
2658
   #elif !defined(BACKLASH_CORRECTION)
2659
-    #error "BACKLASH_COMPENSATION requires BACKLASH_CORRECTION"
2659
+    #error "BACKLASH_COMPENSATION requires BACKLASH_CORRECTION."
2660
   #elif IS_CORE
2660
   #elif IS_CORE
2661
     constexpr float backlash_arr[] = BACKLASH_DISTANCE_MM;
2661
     constexpr float backlash_arr[] = BACKLASH_DISTANCE_MM;
2662
     static_assert(!backlash_arr[CORE_AXIS_1] && !backlash_arr[CORE_AXIS_2],
2662
     static_assert(!backlash_arr[CORE_AXIS_1] && !backlash_arr[CORE_AXIS_2],
2736
 #endif
2736
 #endif
2737
 
2737
 
2738
 #if HAS_CUTTER
2738
 #if HAS_CUTTER
2739
+  #ifndef CUTTER_POWER_DISPLAY
2740
+    #error "CUTTER_POWER_DISPLAY is required with a spindle or laser. Please update your Configuration_adv.h."
2741
+  #elif !CUTTER_DISPLAY_IS(PWM) && !CUTTER_DISPLAY_IS(PERCENT) && !CUTTER_DISPLAY_IS(RPM)
2742
+    #error "CUTTER_POWER_DISPLAY must be PWM, PERCENT, or RPM. Please update your Configuration_adv.h."
2743
+  #endif
2744
+
2745
+  #if ENABLED(LASER_POWER_INLINE)
2746
+    #if ENABLED(SPINDLE_CHANGE_DIR)
2747
+      #error "SPINDLE_CHANGE_DIR and LASER_POWER_INLINE are incompatible."
2748
+    #elif ENABLED(LASER_MOVE_G0_OFF) && DISABLED(LASER_MOVE_POWER)
2749
+      #error "LASER_MOVE_G0_OFF requires LASER_MOVE_POWER. Please update your Configuration_adv.h."
2750
+    #endif
2751
+    #if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
2752
+      #if DISABLED(SPINDLE_LASER_PWM)
2753
+        #error "LASER_POWER_INLINE_TRAPEZOID requires SPINDLE_LASER_PWM to function."
2754
+      #elif ENABLED(S_CURVE_ACCELERATION)
2755
+        //#ifndef LASER_POWER_INLINE_S_CURVE_ACCELERATION_WARN
2756
+        //  #define LASER_POWER_INLINE_S_CURVE_ACCELERATION_WARN
2757
+        //  #warning "Combining LASER_POWER_INLINE_TRAPEZOID with S_CURVE_ACCELERATION may result in unintended behavior."
2758
+        //#endif
2759
+      #endif
2760
+    #endif
2761
+    #if ENABLED(LASER_POWER_INLINE_INVERT)
2762
+      //#ifndef LASER_POWER_INLINE_INVERT_WARN
2763
+      //  #define LASER_POWER_INLINE_INVERT_WARN
2764
+      //  #warning "Enabling LASER_POWER_INLINE_INVERT means that `M5` won't kill the laser immediately; use `M5 I` instead."
2765
+      //#endif
2766
+    #endif
2767
+  #else
2768
+    #if SPINDLE_LASER_POWERUP_DELAY < 1
2769
+      #error "SPINDLE_LASER_POWERUP_DELAY must be greater than 0."
2770
+    #elif SPINDLE_LASER_POWERDOWN_DELAY < 1
2771
+      #error "SPINDLE_LASER_POWERDOWN_DELAY must be greater than 0."
2772
+    #elif ENABLED(LASER_MOVE_POWER)
2773
+      #error "LASER_MOVE_POWER requires LASER_POWER_INLINE."
2774
+    #elif ANY(LASER_POWER_INLINE_TRAPEZOID, LASER_POWER_INLINE_INVERT, LASER_MOVE_G0_OFF, LASER_MOVE_POWER)
2775
+      #error "Enabled an inline laser feature without inline laser power being enabled."
2776
+    #endif
2777
+  #endif
2739
   #define _PIN_CONFLICT(P) (PIN_EXISTS(P) && P##_PIN == SPINDLE_LASER_PWM_PIN)
2778
   #define _PIN_CONFLICT(P) (PIN_EXISTS(P) && P##_PIN == SPINDLE_LASER_PWM_PIN)
2740
   #if BOTH(SPINDLE_FEATURE, LASER_FEATURE)
2779
   #if BOTH(SPINDLE_FEATURE, LASER_FEATURE)
2741
     #error "Enable only one of SPINDLE_FEATURE or LASER_FEATURE."
2780
     #error "Enable only one of SPINDLE_FEATURE or LASER_FEATURE."
2748
       #error "SPINDLE_LASER_PWM_PIN is required for SPINDLE_LASER_PWM."
2787
       #error "SPINDLE_LASER_PWM_PIN is required for SPINDLE_LASER_PWM."
2749
     #elif !PWM_PIN(SPINDLE_LASER_PWM_PIN)
2788
     #elif !PWM_PIN(SPINDLE_LASER_PWM_PIN)
2750
       #error "SPINDLE_LASER_PWM_PIN not assigned to a PWM pin."
2789
       #error "SPINDLE_LASER_PWM_PIN not assigned to a PWM pin."
2751
-    #elif SPINDLE_LASER_POWERUP_DELAY < 1
2752
-      #error "SPINDLE_LASER_POWERUP_DELAY must be greater than 0."
2753
-    #elif SPINDLE_LASER_POWERDOWN_DELAY < 1
2754
-      #error "SPINDLE_LASER_POWERDOWN_DELAY must be greater than 0."
2755
     #elif !defined(SPINDLE_LASER_PWM_INVERT)
2790
     #elif !defined(SPINDLE_LASER_PWM_INVERT)
2756
       #error "SPINDLE_LASER_PWM_INVERT is required for (SPINDLE|LASER)_FEATURE."
2791
       #error "SPINDLE_LASER_PWM_INVERT is required for (SPINDLE|LASER)_FEATURE."
2757
-    #elif !defined(SPEED_POWER_SLOPE) || !defined(SPEED_POWER_INTERCEPT) || !defined(SPEED_POWER_MIN) || !defined(SPEED_POWER_MAX)
2792
+    #elif !defined(SPEED_POWER_SLOPE) || !defined(SPEED_POWER_INTERCEPT) || !defined(SPEED_POWER_MIN) || !defined(SPEED_POWER_MAX) || !defined(SPEED_POWER_STARTUP)
2758
       #error "SPINDLE_LASER_PWM equation constant(s) missing."
2793
       #error "SPINDLE_LASER_PWM equation constant(s) missing."
2759
     #elif _PIN_CONFLICT(X_MIN)
2794
     #elif _PIN_CONFLICT(X_MIN)
2760
       #error "SPINDLE_LASER_PWM pin conflicts with X_MIN_PIN."
2795
       #error "SPINDLE_LASER_PWM pin conflicts with X_MIN_PIN."

+ 6
- 2
Marlin/src/lcd/dogm/status_screen_DOGM.cpp 파일 보기

570
     // Laser / Spindle
570
     // Laser / Spindle
571
     #if DO_DRAW_CUTTER
571
     #if DO_DRAW_CUTTER
572
       if (cutter.power && PAGE_CONTAINS(STATUS_CUTTER_TEXT_Y - INFO_FONT_ASCENT, STATUS_CUTTER_TEXT_Y - 1)) {
572
       if (cutter.power && PAGE_CONTAINS(STATUS_CUTTER_TEXT_Y - INFO_FONT_ASCENT, STATUS_CUTTER_TEXT_Y - 1)) {
573
-        lcd_put_u8str(STATUS_CUTTER_TEXT_X, STATUS_CUTTER_TEXT_Y, i16tostr3rj(cutter.powerPercent(cutter.power)));
574
-        lcd_put_wchar('%');
573
+        lcd_put_u8str(STATUS_CUTTER_TEXT_X, STATUS_CUTTER_TEXT_Y, i16tostr3rj(cutter.power));
574
+        #if CUTTER_DISPLAY_IS(PERCENT)
575
+          lcd_put_wchar('%');
576
+        #elif CUTTER_DISPLAY_IS(RPM)
577
+          lcd_put_wchar('K');
578
+        #endif
575
       }
579
       }
576
     #endif
580
     #endif
577
 
581
 

+ 2
- 1
Marlin/src/lcd/language/language_en.h 파일 보기

90
   PROGMEM Language_Str MSG_PREHEAT_2_SETTINGS              = _UxGT("Preheat ") PREHEAT_2_LABEL _UxGT(" Conf");
90
   PROGMEM Language_Str MSG_PREHEAT_2_SETTINGS              = _UxGT("Preheat ") PREHEAT_2_LABEL _UxGT(" Conf");
91
   PROGMEM Language_Str MSG_PREHEAT_CUSTOM                  = _UxGT("Preheat Custom");
91
   PROGMEM Language_Str MSG_PREHEAT_CUSTOM                  = _UxGT("Preheat Custom");
92
   PROGMEM Language_Str MSG_COOLDOWN                        = _UxGT("Cooldown");
92
   PROGMEM Language_Str MSG_COOLDOWN                        = _UxGT("Cooldown");
93
+  PROGMEM Language_Str MSG_CUTTER_FREQUENCY                = _UxGT("Frequency");
93
   PROGMEM Language_Str MSG_LASER_MENU                      = _UxGT("Laser Control");
94
   PROGMEM Language_Str MSG_LASER_MENU                      = _UxGT("Laser Control");
94
   PROGMEM Language_Str MSG_LASER_OFF                       = _UxGT("Laser Off");
95
   PROGMEM Language_Str MSG_LASER_OFF                       = _UxGT("Laser Off");
95
   PROGMEM Language_Str MSG_LASER_ON                        = _UxGT("Laser On");
96
   PROGMEM Language_Str MSG_LASER_ON                        = _UxGT("Laser On");
603
   PROGMEM Language_Str MSG_BACKLASH_C                      = LCD_STR_C;
604
   PROGMEM Language_Str MSG_BACKLASH_C                      = LCD_STR_C;
604
   PROGMEM Language_Str MSG_BACKLASH_CORRECTION             = _UxGT("Correction");
605
   PROGMEM Language_Str MSG_BACKLASH_CORRECTION             = _UxGT("Correction");
605
   PROGMEM Language_Str MSG_BACKLASH_SMOOTHING              = _UxGT("Smoothing");
606
   PROGMEM Language_Str MSG_BACKLASH_SMOOTHING              = _UxGT("Smoothing");
606
-  
607
+
607
   PROGMEM Language_Str MSG_LEVEL_X_AXIS                    = _UxGT("Level X Axis");
608
   PROGMEM Language_Str MSG_LEVEL_X_AXIS                    = _UxGT("Level X Axis");
608
   PROGMEM Language_Str MSG_AUTO_CALIBRATE                  = _UxGT("Auto Calibrate");
609
   PROGMEM Language_Str MSG_AUTO_CALIBRATE                  = _UxGT("Auto Calibrate");
609
   PROGMEM Language_Str MSG_HEATER_TIMEOUT                  = _UxGT("Heater Timeout");
610
   PROGMEM Language_Str MSG_HEATER_TIMEOUT                  = _UxGT("Heater Timeout");

+ 16
- 5
Marlin/src/lcd/menu/menu_spindle_laser.cpp 파일 보기

36
 
36
 
37
     START_MENU();
37
     START_MENU();
38
     BACK_ITEM(MSG_MAIN);
38
     BACK_ITEM(MSG_MAIN);
39
-    if (cutter.enabled()) {
40
-      #if ENABLED(SPINDLE_LASER_PWM)
41
-        EDIT_ITEM(CUTTER_MENU_TYPE, MSG_CUTTER(POWER), &cutter.power, SPEED_POWER_MIN, SPEED_POWER_MAX);
42
-      #endif
39
+    #if ENABLED(SPINDLE_LASER_PWM)
40
+      EDIT_ITEM_FAST(CUTTER_MENU_POWER_TYPE, MSG_CUTTER(POWER), &cutter.setPower, cutter.interpret_power(SPEED_POWER_MIN), cutter.interpret_power(SPEED_POWER_MAX),
41
+      []{
42
+        if (cutter.isOn) {
43
+          cutter.power = cutter.setPower;
44
+        }
45
+      });
46
+    #endif
47
+
48
+    if (cutter.enabled() && cutter.isOn)
43
       ACTION_ITEM(MSG_CUTTER(OFF), cutter.disable);
49
       ACTION_ITEM(MSG_CUTTER(OFF), cutter.disable);
44
-    }
45
     else {
50
     else {
46
       ACTION_ITEM(MSG_CUTTER(ON), cutter.enable_forward);
51
       ACTION_ITEM(MSG_CUTTER(ON), cutter.enable_forward);
47
       #if ENABLED(SPINDLE_CHANGE_DIR)
52
       #if ENABLED(SPINDLE_CHANGE_DIR)
48
         ACTION_ITEM(MSG_SPINDLE_REVERSE, cutter.enable_reverse);
53
         ACTION_ITEM(MSG_SPINDLE_REVERSE, cutter.enable_reverse);
49
       #endif
54
       #endif
50
     }
55
     }
56
+
57
+    #if ENABLED(MARLIN_DEV_MODE)
58
+      #if ENABLED(HAL_CAN_SET_PWM_FREQ) && defined(SPINDLE_LASER_FREQUENCY)
59
+        EDIT_ITEM_FAST(CUTTER_MENU_FREQUENCY_TYPE, MSG_CUTTER_FREQUENCY, &cutter.frequency, 2000, 50000,[]{ cutter.refresh_frequency();});
60
+      #endif
61
+    #endif
51
     END_MENU();
62
     END_MENU();
52
   }
63
   }
53
 
64
 

+ 50
- 4
Marlin/src/module/planner.cpp 파일 보기

815
   #if ENABLED(S_CURVE_ACCELERATION)
815
   #if ENABLED(S_CURVE_ACCELERATION)
816
     // Jerk controlled speed requires to express speed versus time, NOT steps
816
     // Jerk controlled speed requires to express speed versus time, NOT steps
817
     uint32_t acceleration_time = ((float)(cruise_rate - initial_rate) / accel) * (STEPPER_TIMER_RATE),
817
     uint32_t acceleration_time = ((float)(cruise_rate - initial_rate) / accel) * (STEPPER_TIMER_RATE),
818
-             deceleration_time = ((float)(cruise_rate - final_rate) / accel) * (STEPPER_TIMER_RATE);
819
-
818
+             deceleration_time = ((float)(cruise_rate - final_rate) / accel) * (STEPPER_TIMER_RATE),
820
     // And to offload calculations from the ISR, we also calculate the inverse of those times here
819
     // And to offload calculations from the ISR, we also calculate the inverse of those times here
821
-    uint32_t acceleration_time_inverse = get_period_inverse(acceleration_time);
822
-    uint32_t deceleration_time_inverse = get_period_inverse(deceleration_time);
820
+             acceleration_time_inverse = get_period_inverse(acceleration_time),
821
+             deceleration_time_inverse = get_period_inverse(deceleration_time);
823
   #endif
822
   #endif
824
 
823
 
825
   // Store new block parameters
824
   // Store new block parameters
834
     block->cruise_rate = cruise_rate;
833
     block->cruise_rate = cruise_rate;
835
   #endif
834
   #endif
836
   block->final_rate = final_rate;
835
   block->final_rate = final_rate;
836
+
837
+  /**
838
+   * Laser trapezoid calculations
839
+   *
840
+   * Approximate the trapezoid with the laser, incrementing the power every `entry_per` while accelerating
841
+   * and decrementing it every `exit_power_per` while decelerating, thus ensuring power is related to feedrate.
842
+   *
843
+   * LASER_POWER_INLINE_TRAPEZOID_CONT doesn't need this as it continuously approximates
844
+   *
845
+   * Note this may behave unreliably when running with S_CURVE_ACCELERATION
846
+   */
847
+  #if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
848
+    if (block->laser.power > 0) { // No need to care if power == 0
849
+      const uint8_t entry_power = block->laser.power * entry_factor; // Power on block entry
850
+      #if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
851
+        // Speedup power
852
+        const uint8_t entry_power_diff = block->laser.power - entry_power;
853
+        if (entry_power_diff) {
854
+          block->laser.entry_per = accelerate_steps / entry_power_diff;
855
+          block->laser.power_entry = entry_power;
856
+        }
857
+        else {
858
+          block->laser.entry_per = 0;
859
+          block->laser.power_entry = block->laser.power;
860
+        }
861
+        // Slowdown power
862
+        const uint8_t exit_power = block->laser.power * exit_factor, // Power on block entry
863
+                      exit_power_diff = block->laser.power - exit_power;
864
+        if (exit_power_diff) {
865
+          block->laser.exit_per = (block->step_event_count - block->decelerate_after) / exit_power_diff;
866
+          block->laser.power_exit = exit_power;
867
+        }
868
+        else {
869
+          block->laser.exit_per = 0;
870
+          block->laser.power_exit = block->laser.power;
871
+        }
872
+      #else
873
+        block->laser.power_entry = entry_power;
874
+      #endif
875
+    }
876
+  #endif
837
 }
877
 }
838
 
878
 
839
 /*                            PLANNER SPEED DEFINITION
879
 /*                            PLANNER SPEED DEFINITION
1813
   // Set direction bits
1853
   // Set direction bits
1814
   block->direction_bits = dm;
1854
   block->direction_bits = dm;
1815
 
1855
 
1856
+  // Update block laser power
1857
+  #if ENABLED(LASER_POWER_INLINE)
1858
+    block->laser.status = settings.laser.status;
1859
+    block->laser.power = settings.laser.power;
1860
+  #endif
1861
+
1816
   // Number of steps for each axis
1862
   // Number of steps for each axis
1817
   // See http://www.corexy.com/theory.html
1863
   // See http://www.corexy.com/theory.html
1818
   #if CORE_IS_XY
1864
   #if CORE_IS_XY

+ 45
- 1
Marlin/src/module/planner.h 파일 보기

52
 #endif
52
 #endif
53
 
53
 
54
 #if HAS_CUTTER
54
 #if HAS_CUTTER
55
-  #include "../feature/spindle_laser.h"
55
+  #include "../feature/spindle_laser_types.h"
56
 #endif
56
 #endif
57
 
57
 
58
 // Feedrate for manual moves
58
 // Feedrate for manual moves
88
   BLOCK_FLAG_SYNC_POSITION        = _BV(BLOCK_BIT_SYNC_POSITION)
88
   BLOCK_FLAG_SYNC_POSITION        = _BV(BLOCK_BIT_SYNC_POSITION)
89
 };
89
 };
90
 
90
 
91
+#if ENABLED(LASER_POWER_INLINE)
92
+
93
+  typedef struct {
94
+    uint8_t status,           // See planner settings for meaning
95
+            power;            // Ditto; When in trapezoid mode this is nominal power
96
+    #if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
97
+      uint8_t   power_entry;  // Entry power for the laser
98
+      #if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
99
+        uint8_t   power_exit; // Exit power for the laser
100
+        uint32_t  entry_per,  // Steps per power increment (to avoid floats in stepper calcs)
101
+                  exit_per;   // Steps per power decrement
102
+      #endif
103
+    #endif
104
+  } block_laser_t;
105
+
106
+#endif
107
+
91
 /**
108
 /**
92
  * struct block_t
109
  * struct block_t
93
  *
110
  *
174
     uint32_t sdpos;
191
     uint32_t sdpos;
175
   #endif
192
   #endif
176
 
193
 
194
+  #if ENABLED(LASER_POWER_INLINE)
195
+    block_laser_t laser;
196
+  #endif
197
+
177
 } block_t;
198
 } block_t;
178
 
199
 
179
 #define HAS_POSITION_FLOAT ANY(LIN_ADVANCE, SCARA_FEEDRATE_SCALING, GRADIENT_MIX, LCD_SHOW_E_TOTAL)
200
 #define HAS_POSITION_FLOAT ANY(LIN_ADVANCE, SCARA_FEEDRATE_SCALING, GRADIENT_MIX, LCD_SHOW_E_TOTAL)
180
 
201
 
181
 #define BLOCK_MOD(n) ((n)&(BLOCK_BUFFER_SIZE-1))
202
 #define BLOCK_MOD(n) ((n)&(BLOCK_BUFFER_SIZE-1))
182
 
203
 
204
+#if ENABLED(LASER_POWER_INLINE)
205
+  typedef struct {
206
+    /**
207
+     * Laser status bitmask; most bits are unused;
208
+     *  0: Planner buffer enable
209
+     *  1: Laser enable
210
+     *  2: Reserved for direction
211
+     */
212
+    uint8_t status;
213
+    /**
214
+     * Laser power: 0 or 255 in case of PWM-less laser,
215
+     * or the OCR value;
216
+     *
217
+     * Using OCR instead of raw power,
218
+     * as it avoids floating points during move loop
219
+     */
220
+    uint8_t power;
221
+  } settings_laser_t;
222
+#endif
223
+
183
 typedef struct {
224
 typedef struct {
184
    uint32_t max_acceleration_mm_per_s2[XYZE_N], // (mm/s^2) M201 XYZE
225
    uint32_t max_acceleration_mm_per_s2[XYZE_N], // (mm/s^2) M201 XYZE
185
             min_segment_time_us;                // (µs) M205 B
226
             min_segment_time_us;                // (µs) M205 B
190
             travel_acceleration;                // (mm/s^2) M204 T - Travel acceleration. DEFAULT ACCELERATION for all NON printing moves.
231
             travel_acceleration;                // (mm/s^2) M204 T - Travel acceleration. DEFAULT ACCELERATION for all NON printing moves.
191
  feedRate_t min_feedrate_mm_s,                  // (mm/s) M205 S - Minimum linear feedrate
232
  feedRate_t min_feedrate_mm_s,                  // (mm/s) M205 S - Minimum linear feedrate
192
             min_travel_feedrate_mm_s;           // (mm/s) M205 T - Minimum travel feedrate
233
             min_travel_feedrate_mm_s;           // (mm/s) M205 T - Minimum travel feedrate
234
+  #if ENABLED(LASER_POWER_INLINE)
235
+    settings_laser_t laser;
236
+  #endif
193
 } planner_settings_t;
237
 } planner_settings_t;
194
 
238
 
195
 #if DISABLED(SKEW_CORRECTION)
239
 #if DISABLED(SKEW_CORRECTION)

+ 163
- 23
Marlin/src/module/stepper.cpp 파일 보기

133
   #include "../feature/powerloss.h"
133
   #include "../feature/powerloss.h"
134
 #endif
134
 #endif
135
 
135
 
136
+#if HAS_CUTTER
137
+  #include "../feature/spindle_laser.h"
138
+#endif
139
+
136
 // public:
140
 // public:
137
 
141
 
138
 #if HAS_EXTRA_ENDSTOPS || ENABLED(Z_STEPPER_AUTO_ALIGN)
142
 #if HAS_EXTRA_ENDSTOPS || ENABLED(Z_STEPPER_AUTO_ALIGN)
236
 xyze_long_t Stepper::count_position{0};
240
 xyze_long_t Stepper::count_position{0};
237
 xyze_int8_t Stepper::count_direction{0};
241
 xyze_int8_t Stepper::count_direction{0};
238
 
242
 
243
+#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
244
+  Stepper::stepper_laser_t Stepper::laser = {
245
+    .trap_en = false,
246
+    .cur_power = 0,
247
+    .cruise_set = false,
248
+    #if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
249
+      .last_step_count = 0,
250
+      .acc_step_count = 0
251
+    #else
252
+      .till_update = 0
253
+    #endif
254
+  };
255
+#endif
256
+
239
 #define DUAL_ENDSTOP_APPLY_STEP(A,V)                                                                                        \
257
 #define DUAL_ENDSTOP_APPLY_STEP(A,V)                                                                                        \
240
   if (separate_multi_axis) {                                                                                                \
258
   if (separate_multi_axis) {                                                                                                \
241
     if (A##_HOME_DIR < 0) {                                                                                                 \
259
     if (A##_HOME_DIR < 0) {                                                                                                 \
1674
 
1692
 
1675
         #if ENABLED(S_CURVE_ACCELERATION)
1693
         #if ENABLED(S_CURVE_ACCELERATION)
1676
           // Get the next speed to use (Jerk limited!)
1694
           // Get the next speed to use (Jerk limited!)
1677
-          uint32_t acc_step_rate =
1678
-            acceleration_time < current_block->acceleration_time
1679
-              ? _eval_bezier_curve(acceleration_time)
1680
-              : current_block->cruise_rate;
1695
+          uint32_t acc_step_rate = acceleration_time < current_block->acceleration_time
1696
+                                   ? _eval_bezier_curve(acceleration_time)
1697
+                                   : current_block->cruise_rate;
1681
         #else
1698
         #else
1682
           acc_step_rate = STEP_MULTIPLY(acceleration_time, current_block->acceleration_rate) + current_block->initial_rate;
1699
           acc_step_rate = STEP_MULTIPLY(acceleration_time, current_block->acceleration_rate) + current_block->initial_rate;
1683
           NOMORE(acc_step_rate, current_block->nominal_rate);
1700
           NOMORE(acc_step_rate, current_block->nominal_rate);
1690
         acceleration_time += interval;
1707
         acceleration_time += interval;
1691
 
1708
 
1692
         #if ENABLED(LIN_ADVANCE)
1709
         #if ENABLED(LIN_ADVANCE)
1693
-          // Fire ISR if final adv_rate is reached
1694
-          if (LA_steps && (!LA_use_advance_lead || LA_isr_rate != current_block->advance_speed))
1695
-            initiateLA();
1710
+          if (LA_use_advance_lead) {
1711
+            // Fire ISR if final adv_rate is reached
1712
+            if (LA_steps && LA_isr_rate != current_block->advance_speed) nextAdvanceISR = 0;
1713
+          }
1714
+          else if (LA_steps) nextAdvanceISR = 0;
1715
+        #endif
1716
+
1717
+        // Update laser - Accelerating
1718
+        #if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
1719
+          if (laser.trap_en) {
1720
+            #if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
1721
+              if (current_block->laser.entry_per) {
1722
+                laser.acc_step_count -= step_events_completed - laser.last_step_count;
1723
+                laser.last_step_count = step_events_completed;
1724
+
1725
+                // Should be faster than a divide, since this should trip just once
1726
+                if (laser.acc_step_count < 0) {
1727
+                  while (laser.acc_step_count < 0) {
1728
+                    laser.acc_step_count += current_block->laser.entry_per;
1729
+                    if (laser.cur_power < current_block->laser.power) laser.cur_power++;
1730
+                  }
1731
+                  cutter.set_ocr_power(laser.cur_power);
1732
+                }
1733
+              }
1734
+            #else
1735
+              if (laser.till_update)
1736
+                laser.till_update--;
1737
+              else {
1738
+                laser.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
1739
+                laser.cur_power = (current_block->laser.power * acc_step_rate) / current_block->nominal_rate;
1740
+                cutter.set_ocr_power(laser.cur_power); // Cycle efficiency is irrelevant it the last line was many cycles
1741
+              }
1742
+            #endif
1743
+          }
1696
         #endif
1744
         #endif
1697
       }
1745
       }
1698
       // Are we in Deceleration phase ?
1746
       // Are we in Deceleration phase ?
1740
               LA_isr_rate = current_block->advance_speed;
1788
               LA_isr_rate = current_block->advance_speed;
1741
             }
1789
             }
1742
           }
1790
           }
1743
-          else if (LA_steps) initiateLA();
1791
+          else if (LA_steps) nextAdvanceISR = 0;
1792
+        #endif // LIN_ADVANCE
1793
+
1794
+        // Update laser - Decelerating
1795
+        #if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
1796
+          if (laser.trap_en) {
1797
+            #if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
1798
+              if (current_block->laser.exit_per) {
1799
+                laser.acc_step_count -= step_events_completed - laser.last_step_count;
1800
+                laser.last_step_count = step_events_completed;
1801
+
1802
+                // Should be faster than a divide, since this should trip just once
1803
+                if (laser.acc_step_count < 0) {
1804
+                  while (laser.acc_step_count < 0) {
1805
+                    laser.acc_step_count += current_block->laser.exit_per;
1806
+                    if (laser.cur_power > current_block->laser.power_exit) laser.cur_power--;
1807
+                  }
1808
+                  cutter.set_ocr_power(laser.cur_power);
1809
+                }
1810
+              }
1811
+            #else
1812
+              if (laser.till_update)
1813
+                laser.till_update--;
1814
+              else {
1815
+                laser.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
1816
+                laser.cur_power = (current_block->laser.power * step_rate) / current_block->nominal_rate;
1817
+                cutter.set_ocr_power(laser.cur_power); // Cycle efficiency isn't relevant when the last line was many cycles
1818
+              }
1819
+            #endif
1820
+          }
1744
         #endif
1821
         #endif
1745
       }
1822
       }
1746
-      // We must be in cruise phase otherwise
1823
+      // Must be in cruise phase otherwise
1747
       else {
1824
       else {
1748
 
1825
 
1749
         #if ENABLED(LIN_ADVANCE)
1826
         #if ENABLED(LIN_ADVANCE)
1759
 
1836
 
1760
         // The timer interval is just the nominal value for the nominal speed
1837
         // The timer interval is just the nominal value for the nominal speed
1761
         interval = ticks_nominal;
1838
         interval = ticks_nominal;
1839
+
1840
+        // Update laser - Cruising
1841
+        #if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
1842
+          if (laser.trap_en) {
1843
+            if (!laser.cruise_set) {
1844
+              laser.cur_power = current_block->laser.power;
1845
+              cutter.set_ocr_power(laser.cur_power);
1846
+              laser.cruise_set = true;
1847
+            }
1848
+            #if ENABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
1849
+              laser.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
1850
+            #else
1851
+              laser.last_step_count = step_events_completed;
1852
+            #endif
1853
+          }
1854
+        #endif
1762
       }
1855
       }
1763
     }
1856
     }
1764
   }
1857
   }
1805
          * If DeltaA ==  DeltaB, the movement is only in the 1st axis (X)
1898
          * If DeltaA ==  DeltaB, the movement is only in the 1st axis (X)
1806
          */
1899
          */
1807
         #if EITHER(COREXY, COREXZ)
1900
         #if EITHER(COREXY, COREXZ)
1808
-          #define X_CMP ==
1901
+          #define X_CMP(A,B) ((A)==(B))
1809
         #else
1902
         #else
1810
-          #define X_CMP !=
1903
+          #define X_CMP(A,B) ((A)!=(B))
1811
         #endif
1904
         #endif
1812
-        #define X_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) X_CMP D_(2)) )
1905
+        #define X_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && X_CMP(D_(1),D_(2))) )
1813
       #else
1906
       #else
1814
         #define X_MOVE_TEST !!current_block->steps.a
1907
         #define X_MOVE_TEST !!current_block->steps.a
1815
       #endif
1908
       #endif
1823
          * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
1916
          * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
1824
          */
1917
          */
1825
         #if EITHER(COREYX, COREYZ)
1918
         #if EITHER(COREYX, COREYZ)
1826
-          #define Y_CMP ==
1919
+          #define Y_CMP(A,B) ((A)==(B))
1827
         #else
1920
         #else
1828
-          #define Y_CMP !=
1921
+          #define Y_CMP(A,B) ((A)!=(B))
1829
         #endif
1922
         #endif
1830
-        #define Y_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Y_CMP D_(2)) )
1923
+        #define Y_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && Y_CMP(D_(1),D_(2))) )
1831
       #else
1924
       #else
1832
         #define Y_MOVE_TEST !!current_block->steps.b
1925
         #define Y_MOVE_TEST !!current_block->steps.b
1833
       #endif
1926
       #endif
1841
          * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Z)
1934
          * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Z)
1842
          */
1935
          */
1843
         #if EITHER(COREZX, COREZY)
1936
         #if EITHER(COREZX, COREZY)
1844
-          #define Z_CMP ==
1937
+          #define Z_CMP(A,B) ((A)==(B))
1845
         #else
1938
         #else
1846
-          #define Z_CMP !=
1939
+          #define Z_CMP(A,B) ((A)!=(B))
1847
         #endif
1940
         #endif
1848
-        #define Z_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Z_CMP D_(2)) )
1941
+        #define Z_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && Z_CMP(D_(1),D_(2))) )
1849
       #else
1942
       #else
1850
         #define Z_MOVE_TEST !!current_block->steps.c
1943
         #define Z_MOVE_TEST !!current_block->steps.c
1851
       #endif
1944
       #endif
1938
         set_directions();
2031
         set_directions();
1939
       }
2032
       }
1940
 
2033
 
2034
+      #if ENABLED(LASER_POWER_INLINE)
2035
+        const uint8_t stat = current_block->laser.status;
2036
+        #if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
2037
+          laser.trap_en = (stat & 0x03) == 0x03;
2038
+          laser.cur_power = current_block->laser.power_entry; // RESET STATE
2039
+          laser.cruise_set = false;
2040
+          #if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
2041
+            laser.last_step_count = 0;
2042
+            laser.acc_step_count = current_block->laser.entry_per / 2;
2043
+          #else
2044
+            laser.till_update = 0;
2045
+          #endif
2046
+          // Always have PWM in this case
2047
+          if (TEST(stat, 0)) {                        // Planner controls the laser
2048
+            if (TEST(stat, 1))                        // Laser is on
2049
+              cutter.set_ocr_power(laser.cur_power);
2050
+            else
2051
+              cutter.set_power(0);
2052
+          }
2053
+        #else
2054
+          if (TEST(stat, 0)) {                        // Planner controls the laser
2055
+            #if ENABLED(SPINDLE_LASER_PWM)
2056
+              if (TEST(stat, 1))                      // Laser is on
2057
+                cutter.set_ocr_power(current_block->laser.power);
2058
+              else
2059
+                cutter.set_power(0);
2060
+            #else
2061
+              cutter.set_enabled(TEST(stat, 1));
2062
+            #endif
2063
+          }
2064
+        #endif
2065
+      #endif // LASER_POWER_INLINE
2066
+
1941
       // At this point, we must ensure the movement about to execute isn't
2067
       // At this point, we must ensure the movement about to execute isn't
1942
       // trying to force the head against a limit switch. If using interrupt-
2068
       // trying to force the head against a limit switch. If using interrupt-
1943
       // driven change detection, and already against a limit then no call to
2069
       // driven change detection, and already against a limit then no call to
1957
       // Mark the time_nominal as not calculated yet
2083
       // Mark the time_nominal as not calculated yet
1958
       ticks_nominal = -1;
2084
       ticks_nominal = -1;
1959
 
2085
 
1960
-      #if DISABLED(S_CURVE_ACCELERATION)
1961
-        // Set as deceleration point the initial rate of the block
1962
-        acc_step_rate = current_block->initial_rate;
1963
-      #endif
1964
-
1965
       #if ENABLED(S_CURVE_ACCELERATION)
2086
       #if ENABLED(S_CURVE_ACCELERATION)
1966
         // Initialize the Bézier speed curve
2087
         // Initialize the Bézier speed curve
1967
         _calc_bezier_curve_coeffs(current_block->initial_rate, current_block->cruise_rate, current_block->acceleration_time_inverse);
2088
         _calc_bezier_curve_coeffs(current_block->initial_rate, current_block->cruise_rate, current_block->acceleration_time_inverse);
1968
         // We haven't started the 2nd half of the trapezoid
2089
         // We haven't started the 2nd half of the trapezoid
1969
         bezier_2nd_half = false;
2090
         bezier_2nd_half = false;
2091
+      #else
2092
+        // Set as deceleration point the initial rate of the block
2093
+        acc_step_rate = current_block->initial_rate;
1970
       #endif
2094
       #endif
1971
 
2095
 
1972
       // Calculate the initial timer interval
2096
       // Calculate the initial timer interval
1973
       interval = calc_timer_interval(current_block->initial_rate, &steps_per_isr);
2097
       interval = calc_timer_interval(current_block->initial_rate, &steps_per_isr);
1974
     }
2098
     }
2099
+    #if ENABLED(LASER_POWER_INLINE_CONTINUOUS)
2100
+      else { // No new block found; so apply inline laser parameters
2101
+        // This should mean ending file with 'M5 I' will stop the laser; thus the inline flag isn't needed
2102
+        const uint8_t stat = planner.settings.laser.status;
2103
+        if (TEST(stat, 0)) {             // Planner controls the laser
2104
+          #if ENABLED(SPINDLE_LASER_PWM)
2105
+            if (TEST(stat, 1))           // Laser is on
2106
+              cutter.set_ocr_power(planner.settings.laser.power);
2107
+            else
2108
+              cutter.set_power(0);
2109
+          #else
2110
+            cutter.set_enabled(TEST(stat, 1));
2111
+          #endif
2112
+        }
2113
+      }
2114
+    #endif
1975
   }
2115
   }
1976
 
2116
 
1977
   // Return the interval to wait
2117
   // Return the interval to wait

+ 20
- 8
Marlin/src/module/stepper.h 파일 보기

339
       static uint32_t acc_step_rate; // needed for deceleration start point
339
       static uint32_t acc_step_rate; // needed for deceleration start point
340
     #endif
340
     #endif
341
 
341
 
342
-    //
343
     // Exact steps at which an endstop was triggered
342
     // Exact steps at which an endstop was triggered
344
-    //
345
     static xyz_long_t endstops_trigsteps;
343
     static xyz_long_t endstops_trigsteps;
346
 
344
 
347
-    //
348
     // Positions of stepper motors, in step units
345
     // Positions of stepper motors, in step units
349
-    //
350
     static xyze_long_t count_position;
346
     static xyze_long_t count_position;
351
 
347
 
352
-    //
353
-    // Current direction of stepper motors (+1 or -1)
354
-    //
348
+    // Current stepper motor directions (+1 or -1)
355
     static xyze_int8_t count_direction;
349
     static xyze_int8_t count_direction;
356
 
350
 
357
-  public:
351
+    #if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
352
+
353
+      typedef struct {
354
+        bool trap_en;       // Trapezoid needed flag (i.e., laser on, planner in control)
355
+        uint8_t cur_power;  // Current laser power
356
+        bool cruise_set;    // Power set up for cruising?
357
+
358
+        #if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
359
+          uint32_t last_step_count, // Step count from the last update
360
+                   acc_step_count;  // Bresenham counter for laser accel/decel
361
+        #else
362
+          uint16_t till_update;     // Countdown to the next update
363
+        #endif
364
+      } stepper_laser_t;
358
 
365
 
366
+      static stepper_laser_t laser;
367
+
368
+    #endif
369
+
370
+  public:
359
     // Initialize stepper hardware
371
     // Initialize stepper hardware
360
     static void init();
372
     static void init();
361
 
373
 

Loading…
취소
저장