Skip to content

Commit

Permalink
Various Laser / Spindle improvements (MarlinFirmware#15335)
Browse files Browse the repository at this point in the history
  • Loading branch information
jediminer543 authored and Andrew McDonald committed Apr 15, 2020
1 parent c1b68d6 commit b35bbfe
Show file tree
Hide file tree
Showing 24 changed files with 775 additions and 168 deletions.
122 changes: 107 additions & 15 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -2662,31 +2662,123 @@
#define SPINDLE_LASER_ACTIVE_HIGH false // Set to "true" if the on/off function is active HIGH
#define SPINDLE_LASER_PWM true // Set to "true" if your controller supports setting the speed/power
#define SPINDLE_LASER_PWM_INVERT true // Set to "true" if the speed/power goes up when you want it to go slower
#define SPINDLE_LASER_POWERUP_DELAY 5000 // (ms) Delay to allow the spindle/laser to come up to speed/power
#define SPINDLE_LASER_POWERDOWN_DELAY 5000 // (ms) Delay to allow the spindle to stop

#define SPINDLE_LASER_FREQUENCY 2500 // (Hz) Spindle/laser frequency (only on supported HALs: AVR and LPC)

/**
* Speed / Power can be set ('M3 S') and displayed in terms of:
* - PWM (S0 - S255)
* - PERCENT (S0 - S100)
* - RPM (S0 - S50000) Best for use with a spindle
*/
#define CUTTER_POWER_DISPLAY PWM

/**
* Relative mode uses relative range (SPEED_POWER_MIN to SPEED_POWER_MAX) instead of normal range (0 to SPEED_POWER_MAX)
* Best use with SuperPID router controller where for example S0 = 5,000 RPM and S255 = 30,000 RPM
*/
//#define CUTTER_POWER_RELATIVE // Set speed proportional to [SPEED_POWER_MIN...SPEED_POWER_MAX] instead of directly

#if ENABLED(SPINDLE_FEATURE)
//#define SPINDLE_CHANGE_DIR // Enable if your spindle controller can change spindle direction
#define SPINDLE_CHANGE_DIR_STOP // Enable if the spindle should stop before changing spin direction
#define SPINDLE_INVERT_DIR false // Set to "true" if the spin direction is reversed

#define SPINDLE_LASER_POWERUP_DELAY 5000 // (ms) Delay to allow the spindle/laser to come up to speed/power
#define SPINDLE_LASER_POWERDOWN_DELAY 5000 // (ms) Delay to allow the spindle to stop

/**
* The M3 & M4 commands use the following equation to convert PWM duty cycle to speed/power
* M3/M4 uses the following equation to convert speed/power to PWM duty cycle
* Power = ((DC / 255 * 100) - SPEED_POWER_INTERCEPT)) * (1 / SPEED_POWER_SLOPE)
* where PWM DC varies from 0 to 255
*
* SPEED/POWER = PWM duty cycle * SPEED_POWER_SLOPE + SPEED_POWER_INTERCEPT
* where PWM duty cycle varies from 0 to 255
*
* set the following for your controller (ALL MUST BE SET)
* Set these required parameters for your controller
*/
#define SPEED_POWER_SLOPE 118.4
#define SPEED_POWER_INTERCEPT 0
#define SPEED_POWER_MIN 5000
#define SPEED_POWER_MAX 30000 // SuperPID router controller 0 - 30,000 RPM
#define SPEED_POWER_SLOPE 118.4 // SPEED_POWER_SLOPE = SPEED_POWER_MAX / 255
#define SPEED_POWER_INTERCEPT 0
#define SPEED_POWER_MIN 5000
#define SPEED_POWER_MAX 30000 // SuperPID router controller 0 - 30,000 RPM
#define SPEED_POWER_STARTUP 25000 // The default value for speed power when M3 is called without arguments

#else
#define SPEED_POWER_SLOPE 0.3922
#define SPEED_POWER_INTERCEPT 0
#define SPEED_POWER_MIN 10
#define SPEED_POWER_MAX 100 // 0-100%

#define SPEED_POWER_SLOPE 0.3922 // SPEED_POWER_SLOPE = SPEED_POWER_MAX / 255
#define SPEED_POWER_INTERCEPT 0
#define SPEED_POWER_MIN 0
#define SPEED_POWER_MAX 100 // 0-100%
#define SPEED_POWER_STARTUP 80 // The default value for speed power when M3 is called without arguments

/**
* Enable inline laser power to be handled in the planner / stepper routines.
* Inline power is specified by the I (inline) flag in an M3 command (e.g., M3 S20 I)
* or by the 'S' parameter in G0/G1/G2/G3 moves (see LASER_MOVE_POWER).
*
* This allows the laser to keep in perfect sync with the planner and removes
* the powerup/down delay since lasers require negligible time.
*/
#define LASER_POWER_INLINE

#if ENABLED(LASER_POWER_INLINE)
/**
* Scale the laser's power in proportion to the movement rate.
*
* - Sets the entry power proportional to the entry speed over the nominal speed.
* - Ramps the power up every N steps to approximate the speed trapezoid.
* - Due to the limited power resolution this is only approximate.
*/
#define LASER_POWER_INLINE_TRAPEZOID

/**
* Continuously calculate the current power (nominal_power * current_rate / nominal_rate).
* Required for accurate power with non-trapezoidal acceleration (e.g., S_CURVE_ACCELERATION).
* This is a costly calculation so this option is discouraged on 8-bit AVR boards.
*
* LASER_POWER_INLINE_TRAPEZOID_CONT_PER defines how many step cycles there are between power updates. If your
* board isn't able to generate steps fast enough (and you are using LASER_POWER_INLINE_TRAPEZOID_CONT), increase this.
* Note that when this is zero it means it occurs every cycle; 1 means a delay wait one cycle then run, etc.
*/
//#define LASER_POWER_INLINE_TRAPEZOID_CONT

/**
* Stepper iterations between power updates. Increase this value if the board
* can't keep up with the processing demands of LASER_POWER_INLINE_TRAPEZOID_CONT.
* Disable (or set to 0) to recalculate power on every stepper iteration.
*/
//#define LASER_POWER_INLINE_TRAPEZOID_CONT_PER 10

/**
* Include laser power in G0/G1/G2/G3/G5 commands with the 'S' parameter
*/
//#define LASER_MOVE_POWER

#if ENABLED(LASER_MOVE_POWER)
// Turn off the laser on G0 moves with no power parameter.
// If a power parameter is provided, use that instead.
//#define LASER_MOVE_G0_OFF
#endif

/**
* Inline flag inverted
*
* WARNING: M5 will NOT turn off the laser unless another move
* is done (so G-code files must end with 'M5 I').
*/
//#define LASER_POWER_INLINE_INVERT

/**
* Continuously apply inline power. ('M3 S3' == 'G1 S3' == 'M3 S3 I')
*
* The laser might do some weird things, so only enable this
* feature if you understand the implications.
*/
//#define LASER_POWER_INLINE_CONTINUOUS

#else

#define SPINDLE_LASER_POWERUP_DELAY 50 // (ms) Delay to allow the spindle/laser to come up to speed/power
#define SPINDLE_LASER_POWERDOWN_DELAY 50 // (ms) Delay to allow the spindle to stop

#endif
#endif
#endif

Expand Down
2 changes: 2 additions & 0 deletions Marlin/src/HAL/AVR/HAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ inline void HAL_adc_init() {
// AVR compatibility
#define strtof strtod

#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment

/**
* set_pwm_frequency
* Sets the frequency of the timer corresponding to the provided pin
Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/HAL/AVR/fast_pwm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

#include "../../inc/MarlinConfigPre.h"

#if ENABLED(FAST_PWM_FAN) || SPINDLE_LASER_PWM
#if NEEDS_HARDWARE_PWM // Specific meta-flag for features that mandate PWM

#include "HAL.h"

Expand Down Expand Up @@ -278,5 +278,5 @@ void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255
}
}

#endif // FAST_PWM_FAN || SPINDLE_LASER_PWM
#endif // NEEDS_HARDWARE_PWM
#endif // __AVR__
2 changes: 2 additions & 0 deletions Marlin/src/HAL/LPC1768/HAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ void HAL_idletask();
#define PLATFORM_M997_SUPPORT
void flashFirmware(const int16_t);

#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment

/**
* set_pwm_frequency
* Set the frequency of the timer corresponding to the provided pin
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/HAL/LPC1768/fast_pwm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

#include "../../inc/MarlinConfigPre.h"

#if ENABLED(FAST_PWM_FAN) || SPINDLE_LASER_PWM
#if NEEDS_HARDWARE_PWM // Specific meta-flag for features that mandate PWM

#include <pwm.h>

Expand Down
22 changes: 17 additions & 5 deletions Marlin/src/MarlinCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,11 @@ void startOrResumeJob() {
#if DISABLED(SD_ABORT_NO_COOLDOWN)
thermalManager.disable_all_heaters();
#endif
thermalManager.zero_fan_speeds();
#if !HAS_CUTTER
thermalManager.zero_fan_speeds();
#else
cutter.kill(); // Full cutter shutdown including ISR control
#endif
wait_for_heatup = false;
#if ENABLED(POWER_LOSS_RECOVERY)
recovery.purge();
Expand Down Expand Up @@ -741,6 +745,10 @@ void idle(TERN_(ADVANCED_PAUSE_FEATURE, bool no_stepper_sleep/*=false*/)) {
void kill(PGM_P const lcd_error/*=nullptr*/, PGM_P const lcd_component/*=nullptr*/, const bool steppers_off/*=false*/) {
thermalManager.disable_all_heaters();

#if HAS_CUTTER
cutter.kill(); // Full cutter shutdown including ISR control
#endif

SERIAL_ERROR_MSG(STR_ERR_KILLED);

#if HAS_DISPLAY
Expand Down Expand Up @@ -770,6 +778,10 @@ void minkill(const bool steppers_off/*=false*/) {
// Reiterate heaters off
thermalManager.disable_all_heaters();

#if HAS_CUTTER
cutter.kill(); // Reiterate cutter shutdown
#endif

// Power off all steppers (for M112) or just the E steppers
steppers_off ? disable_all_steppers() : disable_e_steppers();

Expand All @@ -789,14 +801,14 @@ void minkill(const bool steppers_off/*=false*/) {
// Wait for kill to be pressed
while (READ(KILL_PIN)) watchdog_refresh();

void (*resetFunc)() = 0; // Declare resetFunc() at address 0
void (*resetFunc)() = 0; // Declare resetFunc() at address 0
resetFunc(); // Jump to address 0

#else // !HAS_KILL
#else

for (;;) watchdog_refresh(); // Wait for reset
for (;;) watchdog_refresh(); // Wait for reset

#endif // !HAS_KILL
#endif
}

/**
Expand Down
9 changes: 0 additions & 9 deletions Marlin/src/core/drivers.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,6 @@
// Defines that can't be evaluated now
#define HAS_TMC_SW_SERIAL ANY_AXIS_HAS(SW_SERIAL)

//
// Stretching 'drivers.h' to include LPC/SAMD51 SD options
//
#define _SDCARD_LCD 1
#define _SDCARD_ONBOARD 2
#define _SDCARD_CUSTOM_CABLE 3
#define _SDCARD_ID(V) _CAT(_SDCARD_, V)
#define SD_CONNECTION_IS(V) (_SDCARD_ID(SDCARD_CONNECTION) == _SDCARD_ID(V))

#if HAS_DRIVER(L6470) || HAS_DRIVER(L6474) || HAS_DRIVER(L6480) || HAS_DRIVER(POWERSTEP01)
#define HAS_L64XX 1
#endif
Expand Down
49 changes: 29 additions & 20 deletions Marlin/src/feature/spindle_laser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,17 @@

SpindleLaser cutter;

cutter_power_t SpindleLaser::power; // = 0

cutter_power_t SpindleLaser::power;
bool SpindleLaser::isOn; // state to determine when to apply setPower to power
cutter_setPower_t SpindleLaser::setPower = interpret_power(SPEED_POWER_MIN); // spindle/laser speed/power control in PWM, Percentage or RPM
#if ENABLED(MARLIN_DEV_MODE)
cutter_frequency_t SpindleLaser::frequency; // setting PWM frequency; range: 2K - 50K
#endif
#define SPINDLE_LASER_PWM_OFF ((SPINDLE_LASER_PWM_INVERT) ? 255 : 0)

//
// Init the cutter to a safe OFF state
//
void SpindleLaser::init() {
OUT_WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_HIGH); // Init spindle to off
#if ENABLED(SPINDLE_CHANGE_DIR)
Expand All @@ -45,41 +52,39 @@ void SpindleLaser::init() {
SET_PWM(SPINDLE_LASER_PWM_PIN);
analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // set to lowest speed
#endif
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && defined(SPINDLE_LASER_FREQUENCY)
set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
#if ENABLED(MARLIN_DEV_MODE)
frequency = SPINDLE_LASER_FREQUENCY;
#endif
#endif
}

#if ENABLED(SPINDLE_LASER_PWM)

/**
* ocr_val_mode() is used for debugging and to get the points needed to compute the RPM vs ocr_val line
*
* it accepts inputs of 0-255
*/
* Set the cutter PWM directly to the given ocr value
**/
void SpindleLaser::set_ocr(const uint8_t ocr) {
WRITE(SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_HIGH); // turn spindle on (active low)
WRITE(SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_HIGH); // turn spindle on
analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF);
}

#endif

//
// Set cutter ON state (and PWM) to the given cutter power value
//
void SpindleLaser::apply_power(const cutter_power_t inpow) {
static cutter_power_t last_power_applied = 0;
if (inpow == last_power_applied) return;
last_power_applied = inpow;
#if ENABLED(SPINDLE_LASER_PWM)
if (enabled()) {
#define _scaled(F) ((F - (SPEED_POWER_INTERCEPT)) * inv_slope)
constexpr float inv_slope = RECIPROCAL(SPEED_POWER_SLOPE),
min_ocr = _scaled(SPEED_POWER_MIN),
max_ocr = _scaled(SPEED_POWER_MAX);
int16_t ocr_val;
if (inpow <= SPEED_POWER_MIN) ocr_val = min_ocr; // Use minimum if set below
else if (inpow >= SPEED_POWER_MAX) ocr_val = max_ocr; // Use maximum if set above
else ocr_val = _scaled(inpow); // Use calculated OCR value
set_ocr(ocr_val & 0xFF); // ...limited to Atmel PWM max
}
if (enabled())
set_ocr(translate_power(inpow));
else {
WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_HIGH); // Turn spindle off (active low)
analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Only write low byte
WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_HIGH); // Turn spindle off
analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Only write low byte
}
#else
WRITE(SPINDLE_LASER_ENA_PIN, (SPINDLE_LASER_ACTIVE_HIGH) ? enabled() : !enabled());
Expand All @@ -88,6 +93,10 @@ void SpindleLaser::apply_power(const cutter_power_t inpow) {

#if ENABLED(SPINDLE_CHANGE_DIR)

//
// Set the spindle direction and apply immediately
// Stop on direction change if SPINDLE_STOP_ON_DIR_CHANGE is enabled
//
void SpindleLaser::set_direction(const bool reverse) {
const bool dir_state = (reverse == SPINDLE_INVERT_DIR); // Forward (M3) HIGH when not inverted
#if ENABLED(SPINDLE_STOP_ON_DIR_CHANGE)
Expand Down
Loading

0 comments on commit b35bbfe

Please sign in to comment.