diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 2e0ca3d4a1..d4b4307b05 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -3682,6 +3682,8 @@ #define SPEED_POWER_MIN 5000 // (RPM) #define SPEED_POWER_MAX 30000 // (RPM) SuperPID router controller 0 - 30,000 RPM #define SPEED_POWER_STARTUP 25000 // (RPM) M3/M4 speed/power default (with no arguments) + + //#define DEFAULT_ACCELERATION_SPINDLE 1000 // (°/s/s) Default spindle acceleration (speed change with time) #endif #else diff --git a/Marlin/src/feature/spindle_laser.cpp b/Marlin/src/feature/spindle_laser.cpp index b83c1c1797..5f0ea7dd7b 100644 --- a/Marlin/src/feature/spindle_laser.cpp +++ b/Marlin/src/feature/spindle_laser.cpp @@ -43,6 +43,10 @@ bool SpindleLaser::enable_state; // Virtual uint8_t SpindleLaser::power, // Actual power output 0-255 ocr or "0 = off" > 0 = "on" SpindleLaser::last_power_applied; // = 0 // Basic power state tracking +#if HAS_SPINDLE_ACCELERATION + uint32_t SpindleLaser::acceleration_spindle_deg_per_s2; // (°/s/s) Spindle acceleration. Initialized by settings.load +#endif + #if ENABLED(LASER_FEATURE) cutter_test_pulse_t SpindleLaser::testPulse = 50; // (ms) Test fire pulse default duration uint8_t SpindleLaser::last_block_power; // = 0 // Track power changes for dynamic inline power @@ -100,7 +104,22 @@ void SpindleLaser::init() { #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); #endif - hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); + #if HAS_SPINDLE_ACCELERATION + const int16_t diff = ocr - last_power_applied; + const uint8_t abs_diff = ABS(diff); + uint8_t current_ocr = last_power_applied; + // Duration between ocr increments. SPEED_POWER_MAX is in RPM. + const millis_t duration = (float(SPEED_POWER_MAX) * (60000.f / 2550.f) / float(acceleration_spindle_deg_per_s2)) * abs_diff; + millis_t next_ocr_change = millis() + duration; + while (current_ocr != ocr) { + while (PENDING(millis(), next_ocr_change)) idle(); + current_ocr += diff > 0 ? 1 : -1; + hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), current_ocr ^ SPINDLE_LASER_PWM_OFF); + next_ocr_change += duration; + } + #else + hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); + #endif } void SpindleLaser::set_ocr(const uint8_t ocr) { @@ -111,10 +130,10 @@ void SpindleLaser::init() { } void SpindleLaser::ocr_off() { + _set_ocr(0); #if PIN_EXISTS(SPINDLE_LASER_ENA) WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Cutter OFF #endif - _set_ocr(0); } #endif // SPINDLE_LASER_USE_PWM @@ -127,9 +146,8 @@ void SpindleLaser::init() { */ void SpindleLaser::apply_power(const uint8_t opwr) { if (enabled() || opwr == 0) { // 0 check allows us to disable where no ENA pin exists - // Test and set the last power used to improve performance + // Test the last power used to improve performance if (opwr == last_power_applied) return; - last_power_applied = opwr; // Handle PWM driven or just simple on/off #if ENABLED(SPINDLE_LASER_USE_PWM) if (CUTTER_UNIT_IS(RPM) && unitPower == 0) @@ -146,6 +164,7 @@ void SpindleLaser::apply_power(const uint8_t opwr) { WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); isReadyForUI = true; #endif + last_power_applied = opwr; } else { #if PIN_EXISTS(SPINDLE_LASER_ENA) diff --git a/Marlin/src/feature/spindle_laser.h b/Marlin/src/feature/spindle_laser.h index a443d7df5e..a283f0786d 100644 --- a/Marlin/src/feature/spindle_laser.h +++ b/Marlin/src/feature/spindle_laser.h @@ -109,11 +109,14 @@ public: static uint8_t power, last_power_applied; // Basic power state tracking - static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K + static cutter_frequency_t frequency; // (Hz) Laser/Spindle PWM frequency (2000..50000) - static cutter_power_t menuPower, // Power as set via LCD menu in PWM, Percentage or RPM - unitPower; // Power as displayed status in PWM, Percentage or RPM + static cutter_power_t menuPower, // Power as set via LCD menu in PWM, Percentage, or RPM + unitPower; // Power as displayed status in PWM, Percentage, or RPM + #if HAS_SPINDLE_ACCELERATION + static uint32_t acceleration_spindle_deg_per_s2; // (°/s/s) Spindle acceleration + #endif static void init(); #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h index 653a754700..109fca6b29 100644 --- a/Marlin/src/inc/Conditionals-4-adv.h +++ b/Marlin/src/inc/Conditionals-4-adv.h @@ -1090,6 +1090,9 @@ #define _CUTTER_POWER_RPM 3 #define _CUTTER_POWER(V) _CAT(_CUTTER_POWER_, V) #define CUTTER_UNIT_IS(V) (_CUTTER_POWER(CUTTER_POWER_UNIT) == _CUTTER_POWER(V)) + #if DEFAULT_ACCELERATION_SPINDLE + #define HAS_SPINDLE_ACCELERATION 1 + #endif #endif #if !defined(__AVR__) || !defined(USBCON) diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index c40a2f7632..169f84983c 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -4072,6 +4072,8 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive." #error "SPINDLE_LASER_PWM_INVERT is required for (SPINDLE|LASER)_FEATURE." #elif !(defined(SPEED_POWER_MIN) && defined(SPEED_POWER_MAX) && defined(SPEED_POWER_STARTUP)) #error "SPINDLE_LASER_USE_PWM equation constant(s) missing." + #elif DEFAULT_ACCELERATION_SPINDLE > SPEED_POWER_MAX - SPEED_POWER_MIN + #error "DEFAULT_ACCELERATION_SPINDLE must be <= SPEED_POWER_MAX - SPEED_POWER_MIN." #elif _PIN_CONFLICT(X_MIN) #error "SPINDLE_LASER_PWM_PIN conflicts with X_MIN_PIN." #elif _PIN_CONFLICT(X_MAX) diff --git a/Marlin/src/lcd/e3v2/common/limits.h b/Marlin/src/lcd/e3v2/common/limits.h index 560c8735a7..c773240b28 100644 --- a/Marlin/src/lcd/e3v2/common/limits.h +++ b/Marlin/src/lcd/e3v2/common/limits.h @@ -65,6 +65,12 @@ constexpr xyze_float_t max_acceleration_edit_values = #endif ; +#if HAS_SPINDLE_ACCELERATION + constexpr float min_acceleration_edit_values_spindle = 1, + default_acceleration_spindle = DEFAULT_ACCELERATION_SPINDLE, + max_acceleration_edit_values_spindle = default_acceleration_spindle * DEFAULT_MAX_MULTIPLIER; +#endif + // // Max Jerk limits // diff --git a/Marlin/src/lcd/e3v2/creality/dwin.cpp b/Marlin/src/lcd/e3v2/creality/dwin.cpp index fcbeecde83..56830086fd 100644 --- a/Marlin/src/lcd/e3v2/creality/dwin.cpp +++ b/Marlin/src/lcd/e3v2/creality/dwin.cpp @@ -1595,6 +1595,27 @@ void hmiMaxAccelerationXYZE() { drawEditInteger4(select_acc.now, hmiValues.maxAcceleration, true); } + +#if HAS_SPINDLE_ACCELERATION + + void hmiSpindleAcceleration() { + EncoderState encoder_diffState = encoderReceiveAnalyze(); + if (encoder_diffState == ENCODER_DIFF_NO) return; + if (applyEncoder(encoder_diffState, hmiValues.spindleAcceleration)) { + checkkey = ID_SpindleAcceleration; + encoderRate.enabled = false; + cutter.spindle_acceleration_deg_per_s2 = hmiValues.spindleAcceleration; + drawEditInteger4(select_acc.now, hmiValues.spindleAcceleration); + return; + } + // SpindleAcceleration limit + LIMIT(hmiValues.spindleAcceleration, min_acceleration_edit_values_spindle, max_acceleration_edit_values_spindle); + // SpindleAcceleration value + drawEditInteger4(select_acc.now, hmiValues.spindleAcceleration, true); + } + +#endif // HAS_SPINDLE_ACCELERATION + #if ENABLED(CLASSIC_JERK) void hmiMaxJerkXYZE() { @@ -4283,6 +4304,9 @@ void dwinHandleScreen() { case ID_PrintSpeed: hmiPrintSpeed(); break; case ID_MaxSpeedValue: hmiMaxFeedspeedXYZE(); break; case ID_MaxAccelerationValue: hmiMaxAccelerationXYZE(); break; + #if HAS_SPINDLE_ACCELERATION + case ID_SpindleAccelerationValue: hmiSpindleAcceleration(); break; + #endif #if ENABLED(CLASSIC_JERK) case ID_MaxJerkValue: hmiMaxJerkXYZE(); break; #endif diff --git a/Marlin/src/lcd/e3v2/creality/dwin.h b/Marlin/src/lcd/e3v2/creality/dwin.h index fce52d8cf4..50723d015c 100644 --- a/Marlin/src/lcd/e3v2/creality/dwin.h +++ b/Marlin/src/lcd/e3v2/creality/dwin.h @@ -53,6 +53,9 @@ enum processID : uint8_t { #endif ID_MaxSpeed, ID_MaxSpeedValue, ID_MaxAcceleration, ID_MaxAccelerationValue, + #if HAS_SPINDLE_ACCELERATION + ID_SpindleAccelerationValue, + #endif ID_MaxJerk, ID_MaxJerkValue, ID_Step, ID_StepValue, ID_HomeOff, ID_HomeOffX, ID_HomeOffY, ID_HomeOffZ, @@ -105,6 +108,9 @@ typedef struct { int16_t printSpeed = 100; float maxFeedSpeed = 0; float maxAcceleration = 0; + #if HAS_SPINDLE_ACCELERATION + float spindleAcceleration = 0; + #endif float maxJerkScaled = 0; float maxStepScaled = 0; float offset_value = 0; @@ -203,6 +209,9 @@ void hmiPrintSpeed(); void hmiMaxFeedspeedXYZE(); void hmiMaxAccelerationXYZE(); +#if HAS_SPINDLE_ACCELERATION + void hmiSpindleAcceleration(); +#endif void hmiMaxJerkXYZE(); #if ENABLED(EDITABLE_STEPS_PER_UNIT) void hmiStepXYZE(); diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index 686a0b759c..d184c0358f 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -452,6 +452,7 @@ namespace LanguageNarrow_en { LSTR MSG_AMAX_EN = _UxGT("Max * Accel"); LSTR MSG_A_RETRACT = _UxGT("Retract Accel"); LSTR MSG_A_TRAVEL = _UxGT("Travel Accel"); + LSTR MSG_A_SPINDLE = _UxGT("Spindle Accel"); LSTR MSG_INPUT_SHAPING = _UxGT("Input Shaping"); LSTR MSG_SHAPING_ENABLE_N = _UxGT("Enable @ shaping"); LSTR MSG_SHAPING_DISABLE_N = _UxGT("Disable @ shaping"); diff --git a/Marlin/src/lcd/menu/menu_advanced.cpp b/Marlin/src/lcd/menu/menu_advanced.cpp index 99ece89f27..8080a23ba4 100644 --- a/Marlin/src/lcd/menu/menu_advanced.cpp +++ b/Marlin/src/lcd/menu/menu_advanced.cpp @@ -37,6 +37,10 @@ #include "../../gcode/parser.h" #endif +#if HAS_SPINDLE_ACCELERATION + #include "../../feature/spindle_laser.h" +#endif + #if HAS_BED_PROBE #include "../../module/probe.h" #endif @@ -511,6 +515,9 @@ void menu_backlash(); #else const xyze_ulong_t &max_accel_edit_scaled = max_accel_edit; #endif + #if HAS_SPINDLE_ACCELERATION + constexpr uint32_t max_spindle_accel_edit = 99000; + #endif START_MENU(); BACK_ITEM(MSG_ADVANCED_SETTINGS); @@ -544,6 +551,10 @@ void menu_backlash(); EDIT_ITEM_FAST(long5_25, MSG_AMAX_E, &planner.settings.max_acceleration_mm_per_s2[E_AXIS], 100, max_accel_edit_scaled.e, []{ planner.refresh_acceleration_rates(); }); #endif + #if HAS_SPINDLE_ACCELERATION + EDIT_ITEM_FAST(long5_25, MSG_A_SPINDLE, &cutter.acceleration_spindle_deg_per_s2, 100, max_spindle_accel_edit); + #endif + #ifdef XY_FREQUENCY_LIMIT EDIT_ITEM(int8, MSG_XY_FREQUENCY_LIMIT, &planner.xy_freq_limit_hz, 0, 100, planner.refresh_frequency_limit, true); editable.uint8 = uint8_t(LROUND(planner.xy_freq_min_speed_factor * 255)); // percent to u8 diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp index 4205261a3c..7062164dc9 100644 --- a/Marlin/src/module/settings.cpp +++ b/Marlin/src/module/settings.cpp @@ -60,6 +60,10 @@ #include "../HAL/shared/eeprom_api.h" #endif +#if HAS_SPINDLE_ACCELERATION + #include "../feature/spindle_laser.h" +#endif + #if HAS_BED_PROBE #include "probe.h" #endif @@ -252,6 +256,13 @@ typedef struct SettingsDataStruct { xyz_pos_t hotend_offset[HOTENDS - 1]; // M218 XYZ #endif + // + // Spindle Acceleration + // + #if HAS_SPINDLE_ACCELERATION + uint32_t acceleration_spindle; // cutter.acceleration_spindle_deg_per_s2 + #endif + // // FILAMENT_RUNOUT_SENSOR // @@ -941,7 +952,7 @@ void MarlinSettings::postprocess() { #endif // NUM_AXES // - // Hotend Offsets, if any + // Hotend Offsets // { #if HAS_HOTEND_OFFSET @@ -951,6 +962,16 @@ void MarlinSettings::postprocess() { #endif } + // + // Spindle Acceleration + // + { + #if HAS_SPINDLE_ACCELERATION + _FIELD_TEST(acceleration_spindle); + EEPROM_WRITE(cutter.acceleration_spindle_deg_per_s2); + #endif + } + // // Filament Runout Sensor // @@ -1985,7 +2006,7 @@ void MarlinSettings::postprocess() { #endif // NUM_AXES // - // Hotend Offsets, if any + // Hotend Offsets // { #if HAS_HOTEND_OFFSET @@ -1995,6 +2016,16 @@ void MarlinSettings::postprocess() { #endif } + // + // Spindle Acceleration + // + { + #if HAS_SPINDLE_ACCELERATION + _FIELD_TEST(acceleration_spindle); + EEPROM_READ(cutter.acceleration_spindle_deg_per_s2); + #endif + } + // // Filament Runout Sensor // @@ -2003,7 +2034,7 @@ void MarlinSettings::postprocess() { _FIELD_TEST(runout_sensor_enabled); EEPROM_READ(runout_sensor_enabled); #if HAS_FILAMENT_SENSOR - if (!validating) runout.enabled = runout_sensor_enabled < 0 ? FIL_RUNOUT_ENABLED_DEFAULT : runout_sensor_enabled; + if (!validating) runout.enabled = runout_sensor_enabled < 0 ? FIL_RUNOUT_ENABLED_DEFAULT : runout_sensor_enabled; #endif TERN_(HAS_FILAMENT_SENSOR, if (runout.enabled) runout.reset()); @@ -3286,14 +3317,27 @@ void MarlinSettings::reset() { TERN_(HAS_JUNCTION_DEVIATION, planner.junction_deviation_mm = float(JUNCTION_DEVIATION_MM)); + // + // Home Offset + // #if HAS_SCARA_OFFSET scara_home_offset.reset(); #elif HAS_HOME_OFFSET home_offset.reset(); #endif + // + // Hotend Offsets + // TERN_(HAS_HOTEND_OFFSET, reset_hotend_offsets()); + // + // Spindle Acceleration + // + #if HAS_SPINDLE_ACCELERATION + cutter.acceleration_spindle_deg_per_s2 = DEFAULT_ACCELERATION_SPINDLE; + #endif + // // Filament Runout Sensor // diff --git a/buildroot/tests/mega1280 b/buildroot/tests/mega1280 index 1b1a8f86b7..6d703ac8f6 100755 --- a/buildroot/tests/mega1280 +++ b/buildroot/tests/mega1280 @@ -19,7 +19,8 @@ restore_configs opt_set LCD_LANGUAGE an \ POWER_MONITOR_CURRENT_PIN 14 POWER_MONITOR_VOLTAGE_PIN 15 \ CLOSED_LOOP_ENABLE_PIN 44 CLOSED_LOOP_MOVE_COMPLETE_PIN 45 -opt_enable SPINDLE_FEATURE ULTIMAKERCONTROLLER LCD_BED_LEVELING EDITABLE_HOMING_FEEDRATE \ +opt_enable SPINDLE_FEATURE DEFAULT_ACCELERATION_SPINDLE \ + ULTIMAKERCONTROLLER LCD_BED_LEVELING EDITABLE_HOMING_FEEDRATE \ EEPROM_SETTINGS EEPROM_BOOT_SILENT EEPROM_AUTO_INIT \ SENSORLESS_BACKOFF_MM HOMING_BACKOFF_POST_MM HOME_Y_BEFORE_X CODEPENDENT_XY_HOMING \ MESH_BED_LEVELING ENABLE_LEVELING_FADE_HEIGHT MESH_G28_REST_ORIGIN \