Skip to content

Commit b025c18

Browse files
descipherthinkyhead
authored andcommitted
⚡️ Fix and improve Inline Laser Power (#22690)
1 parent b49da1d commit b025c18

File tree

18 files changed

+831
-694
lines changed

18 files changed

+831
-694
lines changed

Marlin/Configuration_adv.h

Lines changed: 34 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3475,7 +3475,7 @@
34753475
// ESP32: If SPINDLE_LASER_PWM_PIN is onboard then <=78125Hz. For I2S expander
34763476
// the frequency determines the PWM resolution. 2500Hz = 0-100, 977Hz = 0-255, ...
34773477
// (250000 / SPINDLE_LASER_FREQUENCY) = max value.
3478-
#endif
3478+
#endif
34793479

34803480
//#define AIR_EVACUATION // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11
34813481
#if ENABLED(AIR_EVACUATION)
@@ -3548,8 +3548,11 @@
35483548
#endif
35493549

35503550
// Define the minimum and maximum test pulse time values for a laser test fire function
3551-
#define LASER_TEST_PULSE_MIN 1 // Used with Laser Control Menu
3552-
#define LASER_TEST_PULSE_MAX 999 // Caution: Menu may not show more than 3 characters
3551+
#define LASER_TEST_PULSE_MIN 1 // (ms) Used with Laser Control Menu
3552+
#define LASER_TEST_PULSE_MAX 999 // (ms) Caution: Menu may not show more than 3 characters
3553+
3554+
#define SPINDLE_LASER_POWERUP_DELAY 50 // (ms) Delay to allow the spindle/laser to come up to speed/power
3555+
#define SPINDLE_LASER_POWERDOWN_DELAY 50 // (ms) Delay to allow the spindle to stop
35533556

35543557
/**
35553558
* Laser Safety Timeout
@@ -3562,79 +3565,38 @@
35623565
#define LASER_SAFETY_TIMEOUT_MS 1000 // (ms)
35633566

35643567
/**
3565-
* Enable inline laser power to be handled in the planner / stepper routines.
3566-
* Inline power is specified by the I (inline) flag in an M3 command (e.g., M3 S20 I)
3567-
* or by the 'S' parameter in G0/G1/G2/G3 moves (see LASER_MOVE_POWER).
3568+
* Any M3 or G1/2/3/5 command with the 'I' parameter enables continuous inline power mode.
3569+
*
3570+
* e.g., 'M3 I' enables continuous inline power which is processed by the planner.
3571+
* Power is stored in move blocks and applied when blocks are processed by the Stepper ISR.
3572+
*
3573+
* 'M4 I' sets dynamic mode which uses the current feedrate to calculate a laser power OCR value.
35683574
*
3569-
* This allows the laser to keep in perfect sync with the planner and removes
3570-
* the powerup/down delay since lasers require negligible time.
3575+
* Any move in dynamic mode will use the current feedrate to calculate the laser power.
3576+
* Feed rates are set by the F parameter of a move command e.g. G1 X0 Y10 F6000
3577+
* Laser power would be calculated by bit shifting off 8 LSB's. In binary this is div 256.
3578+
* The calculation gives us ocr values from 0 to 255, values over F65535 will be set as 255 .
3579+
* More refined power control such as compesation for accell/decell will be addressed in future releases.
3580+
*
3581+
* M5 I clears inline mode and set power to 0, M5 sets the power output to 0 but leaves inline mode on.
35713582
*/
3572-
//#define LASER_POWER_INLINE
3573-
3574-
#if ENABLED(LASER_POWER_INLINE)
3575-
/**
3576-
* Scale the laser's power in proportion to the movement rate.
3577-
*
3578-
* - Sets the entry power proportional to the entry speed over the nominal speed.
3579-
* - Ramps the power up every N steps to approximate the speed trapezoid.
3580-
* - Due to the limited power resolution this is only approximate.
3581-
*/
3582-
#define LASER_POWER_INLINE_TRAPEZOID
3583-
3584-
/**
3585-
* Continuously calculate the current power (nominal_power * current_rate / nominal_rate).
3586-
* Required for accurate power with non-trapezoidal acceleration (e.g., S_CURVE_ACCELERATION).
3587-
* This is a costly calculation so this option is discouraged on 8-bit AVR boards.
3588-
*
3589-
* LASER_POWER_INLINE_TRAPEZOID_CONT_PER defines how many step cycles there are between power updates. If your
3590-
* board isn't able to generate steps fast enough (and you are using LASER_POWER_INLINE_TRAPEZOID_CONT), increase this.
3591-
* Note that when this is zero it means it occurs every cycle; 1 means a delay wait one cycle then run, etc.
3592-
*/
3593-
//#define LASER_POWER_INLINE_TRAPEZOID_CONT
3594-
3595-
/**
3596-
* Stepper iterations between power updates. Increase this value if the board
3597-
* can't keep up with the processing demands of LASER_POWER_INLINE_TRAPEZOID_CONT.
3598-
* Disable (or set to 0) to recalculate power on every stepper iteration.
3599-
*/
3600-
//#define LASER_POWER_INLINE_TRAPEZOID_CONT_PER 10
3601-
3602-
/**
3603-
* Include laser power in G0/G1/G2/G3/G5 commands with the 'S' parameter
3604-
*/
3605-
//#define LASER_MOVE_POWER
36063583

3607-
#if ENABLED(LASER_MOVE_POWER)
3608-
// Turn off the laser on G0 moves with no power parameter.
3609-
// If a power parameter is provided, use that instead.
3610-
//#define LASER_MOVE_G0_OFF
3611-
3612-
// Turn off the laser on G28 homing.
3613-
//#define LASER_MOVE_G28_OFF
3614-
#endif
3615-
3616-
/**
3617-
* Inline flag inverted
3618-
*
3619-
* WARNING: M5 will NOT turn off the laser unless another move
3620-
* is done (so G-code files must end with 'M5 I').
3621-
*/
3622-
//#define LASER_POWER_INLINE_INVERT
3623-
3624-
/**
3625-
* Continuously apply inline power. ('M3 S3' == 'G1 S3' == 'M3 S3 I')
3626-
*
3627-
* The laser might do some weird things, so only enable this
3628-
* feature if you understand the implications.
3629-
*/
3630-
//#define LASER_POWER_INLINE_CONTINUOUS
3631-
3632-
#else
3633-
3634-
#define SPINDLE_LASER_POWERUP_DELAY 50 // (ms) Delay to allow the spindle/laser to come up to speed/power
3635-
#define SPINDLE_LASER_POWERDOWN_DELAY 50 // (ms) Delay to allow the spindle to stop
3584+
/**
3585+
* Enable M3 commands for laser mode inline power planner syncing.
3586+
* This feature enables any M3 S-value to be injected into the block buffers while in
3587+
* CUTTER_MODE_CONTINUOUS. The option allows M3 laser power to be commited without waiting
3588+
* for a planner syncronization
3589+
*/
3590+
//#define LASER_POWER_SYNC
36363591

3637-
#endif
3592+
/**
3593+
* Scale the laser's power in proportion to the movement rate.
3594+
*
3595+
* - Sets the entry power proportional to the entry speed over the nominal speed.
3596+
* - Ramps the power up every N steps to approximate the speed trapezoid.
3597+
* - Due to the limited power resolution this is only approximate.
3598+
*/
3599+
//#define LASER_POWER_TRAP
36383600

36393601
//
36403602
// Laser I2C Ammeter (High precision INA226 low/high side module)

Marlin/src/feature/spindle_laser.cpp

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,26 @@
3939
#endif
4040

4141
SpindleLaser cutter;
42-
uint8_t SpindleLaser::power,
42+
bool SpindleLaser::enable_state; // Virtual enable state, controls enable pin if present and or apply power if > 0
43+
uint8_t SpindleLaser::power, // Actual power output 0-255 ocr or "0 = off" > 0 = "on"
4344
SpindleLaser::last_power_applied; // = 0 // Basic power state tracking
45+
4446
#if ENABLED(LASER_FEATURE)
45-
cutter_test_pulse_t SpindleLaser::testPulse = 50; // Test fire Pulse time ms value.
47+
cutter_test_pulse_t SpindleLaser::testPulse = 50; // (ms) Test fire pulse default duration
48+
uint8_t SpindleLaser::last_block_power; // = 0 // Track power changes for dynamic inline power
49+
feedRate_t SpindleLaser::feedrate_mm_m = 1500,
50+
SpindleLaser::last_feedrate_mm_m; // = 0 // (mm/min) Track feedrate changes for dynamic power
4651
#endif
47-
bool SpindleLaser::isReady; // Ready to apply power setting from the UI to OCR
48-
cutter_power_t SpindleLaser::menuPower, // Power set via LCD menu in PWM, PERCENT, or RPM
49-
SpindleLaser::unitPower; // LCD status power in PWM, PERCENT, or RPM
5052

51-
#if ENABLED(MARLIN_DEV_MODE)
52-
cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K
53-
#endif
53+
bool SpindleLaser::isReadyForUI = false; // Ready to apply power setting from the UI to OCR
54+
CutterMode SpindleLaser::cutter_mode = CUTTER_MODE_STANDARD; // Default is standard mode
55+
56+
constexpr cutter_cpower_t SpindleLaser::power_floor;
57+
cutter_power_t SpindleLaser::menuPower = 0, // Power value via LCD menu in PWM, PERCENT, or RPM based on configured format set by CUTTER_POWER_UNIT.
58+
SpindleLaser::unitPower = 0; // Unit power is in PWM, PERCENT, or RPM based on CUTTER_POWER_UNIT.
59+
60+
cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K
61+
5462
#define SPINDLE_LASER_PWM_OFF TERN(SPINDLE_LASER_PWM_INVERT, 255, 0)
5563

5664
/**
@@ -65,14 +73,14 @@ void SpindleLaser::init() {
6573
#if ENABLED(SPINDLE_CHANGE_DIR)
6674
OUT_WRITE(SPINDLE_DIR_PIN, SPINDLE_INVERT_DIR); // Init rotation to clockwise (M3)
6775
#endif
76+
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
77+
frequency = SPINDLE_LASER_FREQUENCY;
78+
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
79+
#endif
6880
#if ENABLED(SPINDLE_LASER_USE_PWM)
6981
SET_PWM(SPINDLE_LASER_PWM_PIN);
7082
hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Set to lowest speed
7183
#endif
72-
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
73-
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
74-
TERN_(MARLIN_DEV_MODE, frequency = SPINDLE_LASER_FREQUENCY);
75-
#endif
7684
#if ENABLED(AIR_EVACUATION)
7785
OUT_WRITE(AIR_EVACUATION_PIN, !AIR_EVACUATION_ACTIVE); // Init Vacuum/Blower OFF
7886
#endif
@@ -90,7 +98,7 @@ void SpindleLaser::init() {
9098
*/
9199
void SpindleLaser::_set_ocr(const uint8_t ocr) {
92100
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
93-
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), TERN(MARLIN_DEV_MODE, frequency, SPINDLE_LASER_FREQUENCY));
101+
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency);
94102
#endif
95103
hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF);
96104
}
@@ -107,35 +115,41 @@ void SpindleLaser::init() {
107115
#endif // SPINDLE_LASER_USE_PWM
108116

109117
/**
110-
* Apply power for laser/spindle
118+
* Apply power for Laser or Spindle
111119
*
112120
* Apply cutter power value for PWM, Servo, and on/off pin.
113121
*
114-
* @param opwr Power value. Range 0 to MAX. When 0 disable spindle/laser.
122+
* @param opwr Power value. Range 0 to MAX.
115123
*/
116124
void SpindleLaser::apply_power(const uint8_t opwr) {
117-
if (opwr == last_power_applied) return;
118-
last_power_applied = opwr;
119-
power = opwr;
120-
#if ENABLED(SPINDLE_LASER_USE_PWM)
121-
if (cutter.unitPower == 0 && CUTTER_UNIT_IS(RPM)) {
122-
ocr_off();
123-
isReady = false;
124-
}
125-
else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled()) {
126-
set_ocr(power);
127-
isReady = true;
128-
}
129-
else {
130-
ocr_off();
131-
isReady = false;
132-
}
133-
#elif ENABLED(SPINDLE_SERVO)
134-
servo[SPINDLE_SERVO_NR].move(power);
135-
#else
136-
WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
137-
isReady = true;
138-
#endif
125+
if (enabled() || opwr == 0) { // 0 check allows us to disable where no ENA pin exists
126+
// Test and set the last power used to improve performance
127+
if (opwr == last_power_applied) return;
128+
last_power_applied = opwr;
129+
// Handle PWM driven or just simple on/off
130+
#if ENABLED(SPINDLE_LASER_USE_PWM)
131+
if (CUTTER_UNIT_IS(RPM) && unitPower == 0)
132+
ocr_off();
133+
else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled() || opwr == 0) {
134+
set_ocr(opwr);
135+
isReadyForUI = true;
136+
}
137+
else
138+
ocr_off();
139+
#elif ENABLED(SPINDLE_SERVO)
140+
MOVE_SERVO(SPINDLE_SERVO_NR, power);
141+
#else
142+
WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
143+
isReadyForUI = true;
144+
#endif
145+
}
146+
else {
147+
#if PIN_EXISTS(SPINDLE_LASER_ENA)
148+
WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE);
149+
#endif
150+
isReadyForUI = false; // Only used for UI display updates.
151+
TERN_(SPINDLE_LASER_USE_PWM, ocr_off());
152+
}
139153
}
140154

141155
#if ENABLED(SPINDLE_CHANGE_DIR)

0 commit comments

Comments
 (0)