From f6c891554574e00febec83208951fa52ad8af7b7 Mon Sep 17 00:00:00 2001 From: David Buezas Date: Sun, 20 Apr 2025 07:18:45 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20SMOOTH=5FLIN=5FADVANCE=20(#27710)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Scott Lahteine --- Marlin/Configuration_adv.h | 18 ++ Marlin/src/feature/bltouch.cpp | 2 +- Marlin/src/gcode/feature/advance/M900.cpp | 50 ++++- Marlin/src/inc/Conditionals-4-adv.h | 5 + Marlin/src/inc/SanityCheck.h | 18 +- Marlin/src/inc/Warnings.cpp | 9 + Marlin/src/lcd/language/language_en.h | 2 + Marlin/src/lcd/menu/menu_advanced.cpp | 28 ++- Marlin/src/lcd/menu/menu_tune.cpp | 12 ++ Marlin/src/module/planner.cpp | 63 ++++-- Marlin/src/module/planner.h | 37 +++- Marlin/src/module/settings.cpp | 44 +++- Marlin/src/module/stepper.cpp | 233 +++++++++++++++++++--- Marlin/src/module/stepper.h | 39 +++- buildroot/tests/LPC1768 | 28 ++- buildroot/tests/LPC1769 | 22 +- 16 files changed, 504 insertions(+), 106 deletions(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 73073762f1..9d291f6219 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -2352,6 +2352,24 @@ //#define ADVANCE_K_EXTRA // Add a second linear advance constant, configurable with M900 L. //#define LA_DEBUG // Print debug information to serial during operation. Disable for production use. //#define EXPERIMENTAL_I2S_LA // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz. + + //#define SMOOTH_LIN_ADVANCE // Remove limits on acceleration by gradual increase of nozzle pressure + #if ENABLED(SMOOTH_LIN_ADVANCE) + /** + * ADVANCE_TAU is also the time ahead that the smoother needs to look + * into the planner, so the planner needs to have enough blocks loaded. + * For k=0.04 at 10k acceleration and an "Orbiter 2" extruder it can be as low as 0.0075. + * Adjust by lowering the value until you observe the extruder skipping, then raise slightly. + * Higher k and higher XY acceleration may require larger ADVANCE_TAU to avoid skipping steps. + */ + #if ENABLED(DISTINCT_E_FACTORS) + #define ADVANCE_TAU { 0.01 } // (s) Smoothing time to reduce extruder acceleration, per extruder + #else + #define ADVANCE_TAU 0.01 // (s) Smoothing time to reduce extruder acceleration + #endif + #define SMOOTH_LIN_ADV_HZ 5000 // (Hz) How often to update extruder speed + #define INPUT_SHAPING_E_SYNC // Synchronize the extruder-shaped XY axes (to increase precision) + #endif #endif /** diff --git a/Marlin/src/feature/bltouch.cpp b/Marlin/src/feature/bltouch.cpp index 2559547b85..5b498cd474 100644 --- a/Marlin/src/feature/bltouch.cpp +++ b/Marlin/src/feature/bltouch.cpp @@ -48,7 +48,7 @@ bool BLTouch::command(const BLTCommand cmd, const millis_t &ms) { // The previous write should've already delayed to detect the alarm. if (cmd != current) { servo[Z_PROBE_SERVO_NR].move(cmd); - safe_delay(_MAX(ms, (uint32_t)BLTOUCH_DELAY)); // BLTOUCH_DELAY is also the *minimum* delay + safe_delay(_MAX(ms, uint32_t(BLTOUCH_DELAY))); // BLTOUCH_DELAY is also the *minimum* delay } return triggered(); } diff --git a/Marlin/src/gcode/feature/advance/M900.cpp b/Marlin/src/gcode/feature/advance/M900.cpp index e8a16d952f..51a40f934e 100644 --- a/Marlin/src/gcode/feature/advance/M900.cpp +++ b/Marlin/src/gcode/feature/advance/M900.cpp @@ -26,6 +26,7 @@ #include "../../gcode.h" #include "../../../module/planner.h" +#include "../../../module/stepper.h" #if ENABLED(ADVANCE_K_EXTRA) float other_extruder_advance_K[DISTINCT_E]; @@ -62,6 +63,11 @@ void GcodeSuite::M900() { float &kref = planner.extruder_advance_K[E_INDEX_N(tool_index)], newK = kref; const float oldK = newK; + #if ENABLED(SMOOTH_LIN_ADVANCE) + const float oldU = stepper.get_advance_tau(E_INDEX_N(tool_index)); + float newU = oldU; + #endif + #if ENABLED(ADVANCE_K_EXTRA) float &lref = other_extruder_advance_K[E_INDEX_N(tool_index)]; @@ -105,9 +111,22 @@ void GcodeSuite::M900() { #endif - if (newK != oldK) { + #if ENABLED(SMOOTH_LIN_ADVANCE) + if (parser.seenval('U')) { + const float tau = parser.value_float(); + if (WITHIN(tau, 0.0f, 0.5f)) + newU = tau; + else + echo_value_oor('U'); + } + #endif + + if (newK != oldK || TERN0(SMOOTH_LIN_ADVANCE, newU != oldU)) { planner.synchronize(); - kref = newK; + if (newK != oldK) kref = newK; + #if ENABLED(SMOOTH_LIN_ADVANCE) + if (newU != oldU) stepper.set_advance_tau(newU); + #endif } if (!parser.seen_any()) { @@ -124,18 +143,27 @@ void GcodeSuite::M900() { } #endif - #else + #else // !ADVANCE_K_EXTRA SERIAL_ECHO_START(); #if DISTINCT_E < 2 - SERIAL_ECHOLNPGM("Advance K=", planner.extruder_advance_K[0]); + SERIAL_ECHOPGM("Advance K=", planner.extruder_advance_K[0]); + #if ENABLED(SMOOTH_LIN_ADVANCE) + SERIAL_ECHOPGM(" TAU=", stepper.get_advance_tau()); + #endif + SERIAL_EOL(); #else SERIAL_ECHOPGM("Advance K"); EXTRUDER_LOOP() SERIAL_ECHO(C(' '), C('0' + e), C(':'), planner.extruder_advance_K[e]); SERIAL_EOL(); + #if ENABLED(SMOOTH_LIN_ADVANCE) + SERIAL_ECHOPGM("Advance TAU"); + EXTRUDER_LOOP() SERIAL_ECHO(C(' '), C('0' + e), C(':'), stepper.get_advance_tau(e)); + SERIAL_EOL(); + #endif #endif - #endif + #endif // !ADVANCE_K_EXTRA } } @@ -146,11 +174,19 @@ void GcodeSuite::M900_report(const bool forReplay/*=true*/) { report_heading(forReplay, F(STR_LINEAR_ADVANCE)); #if DISTINCT_E < 2 report_echo_start(forReplay); - SERIAL_ECHOLNPGM(" M900 K", planner.extruder_advance_K[0]); + SERIAL_ECHOPGM(" M900 K", planner.extruder_advance_K[0]); + #if ENABLED(SMOOTH_LIN_ADVANCE) + SERIAL_ECHOPGM(" M900 U", stepper.get_advance_tau()); + #endif + SERIAL_EOL(); #else EXTRUDER_LOOP() { report_echo_start(forReplay); - SERIAL_ECHOLNPGM(" M900 T", e, " K", planner.extruder_advance_K[e]); + SERIAL_ECHOPGM(" M900 T", e, " K", planner.extruder_advance_K[e]); + #if ENABLED(SMOOTH_LIN_ADVANCE) + SERIAL_ECHOPGM(" U", stepper.get_advance_tau(e)); + #endif + SERIAL_EOL(); } #endif } diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h index 109fca6b29..ca096c7661 100644 --- a/Marlin/src/inc/Conditionals-4-adv.h +++ b/Marlin/src/inc/Conditionals-4-adv.h @@ -231,6 +231,7 @@ #undef FWRETRACT #undef LCD_SHOW_E_TOTAL #undef LIN_ADVANCE + #undef SMOOTH_LIN_ADVANCE #undef MANUAL_E_MOVES_RELATIVE #undef PID_EXTRUSION_SCALING #undef SHOW_TEMP_ADC_VALUES @@ -339,6 +340,10 @@ #define HAS_LINEAR_E_JERK 1 #endif +#if ENABLED(LIN_ADVANCE) && DISABLED(SMOOTH_LIN_ADVANCE) + #define HAS_ROUGH_LIN_ADVANCE 1 +#endif + // Some displays can toggle Adaptive Step Smoothing. // The state is saved to EEPROM. // In future this may be added to a G-code such as M205 A. diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index cd3c1cd573..970847ecb5 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -847,7 +847,23 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L #if ENABLED(DIRECT_STEPPING) #error "DIRECT_STEPPING is incompatible with LIN_ADVANCE. (Extrusion is controlled externally by the Step Daemon.)" #endif -#endif + + /** + * Smooth Linear Advance + */ + #if ENABLED(SMOOTH_LIN_ADVANCE) + #ifndef CPU_32_BIT + #error "SMOOTH_LIN_ADVANCE requires a 32-bit CPU." + #elif DISTINCT_E > 1 + #error "SMOOTH_LIN_ADVANCE is not compatible with multiple extruders." + #elif ENABLED(S_CURVE_ACCELERATION) + #error "SMOOTH_LIN_ADVANCE is not compatible with S_CURVE_ACCELERATION." + #elif ENABLED(INPUT_SHAPING_E_SYNC) && NONE(INPUT_SHAPING_X, INPUT_SHAPING_Y) + #error "INPUT_SHAPING_E_SYNC requires INPUT_SHAPING_X or INPUT_SHAPING_Y." + #endif + #endif + +#endif // LIN_ADVANCE /** * Nonlinear Extrusion requirements diff --git a/Marlin/src/inc/Warnings.cpp b/Marlin/src/inc/Warnings.cpp index 119e2205ab..f11eedbe52 100644 --- a/Marlin/src/inc/Warnings.cpp +++ b/Marlin/src/inc/Warnings.cpp @@ -946,3 +946,12 @@ #if LCD_IS_SERIAL_HOST && defined(BOARD_LCD_SERIAL_PORT) && LCD_SERIAL_PORT != BOARD_LCD_SERIAL_PORT && DISABLED(NO_LCD_SERIAL_PORT_WARNING) #warning "LCD_SERIAL_PORT overrides the default (BOARD_LCD_SERIAL_PORT)." #endif + +/** + * Smooth Linear Advance with Mixing Extruder, S-Curve Acceleration + */ +#if ENABLED(SMOOTH_LIN_ADVANCE) + #if ENABLED(MIXING_EXTRUDER) + #warning "SMOOTH_LIN_ADVANCE with MIXING_EXTRUDER is untested. Use with caution." + #endif +#endif diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index d184c0358f..e0c1f53f77 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -492,7 +492,9 @@ namespace LanguageNarrow_en { LSTR MSG_MAX_BELT_LEN = _UxGT("Max Belt Len"); LSTR MSG_LINEAR_ADVANCE = _UxGT("Linear Advance"); LSTR MSG_ADVANCE_K = _UxGT("Advance K"); + LSTR MSG_ADVANCE_TAU = _UxGT("Advance Tau"); LSTR MSG_ADVANCE_K_E = _UxGT("Advance K *"); + LSTR MSG_ADVANCE_TAU_E = _UxGT("Advance Tau *"); LSTR MSG_CONTRAST = _UxGT("LCD Contrast"); LSTR MSG_BRIGHTNESS = _UxGT("LCD Brightness"); LSTR MSG_SCREEN_TIMEOUT = _UxGT("LCD Timeout (m)"); diff --git a/Marlin/src/lcd/menu/menu_advanced.cpp b/Marlin/src/lcd/menu/menu_advanced.cpp index 137439c6df..296312bb1f 100644 --- a/Marlin/src/lcd/menu/menu_advanced.cpp +++ b/Marlin/src/lcd/menu/menu_advanced.cpp @@ -122,7 +122,18 @@ void menu_backlash(); EXTRUDER_LOOP() EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10); #endif - #endif + #if ENABLED(SMOOTH_LIN_ADVANCE) + #if DISTINCT_E < 2 + editable.decimal = stepper.get_advance_tau(); + EDIT_ITEM(float54, MSG_ADVANCE_TAU, &editable.decimal, 0.0f, 0.5f, []{ stepper.set_advance_tau(editable.decimal); }); + #else + EXTRUDER_LOOP() { + editable.decimal = stepper.get_advance_tau(e); + EDIT_ITEM_N(float54, e, MSG_ADVANCE_TAU_E, &editable.decimal, 0.0f, 0.5f, []{ stepper.set_advance_tau(editable.decimal, MenuItemBase::itemIndex); }); + } + #endif + #endif + #endif // LIN_ADVANCE #if DISABLED(NO_VOLUMETRICS) EDIT_ITEM(bool, MSG_VOLUMETRIC_ENABLED, &parser.volumetric_enabled, planner.calculate_volumetric_multipliers); @@ -735,13 +746,26 @@ void menu_advanced_settings() { #if HAS_ADV_FILAMENT_MENU SUBMENU(MSG_FILAMENT, menu_advanced_filament); - #elif ENABLED(LIN_ADVANCE) + #endif + + #if ENABLED(LIN_ADVANCE) && DISABLED(HAS_ADV_FILAMENT_MENU) #if DISTINCT_E < 2 EDIT_ITEM(float42_52, MSG_ADVANCE_K, &planner.extruder_advance_K[0], 0, 10); #else EXTRUDER_LOOP() EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10); #endif + #if ENABLED(SMOOTH_LIN_ADVANCE) + #if DISTINCT_E < 2 + editable.decimal = stepper.get_advance_tau(); + EDIT_ITEM(float54, MSG_ADVANCE_TAU, &editable.decimal, 0.0f, 0.5f, []{ stepper.set_advance_tau(editable.decimal); }); + #else + EXTRUDER_LOOP() { + editable.decimal = stepper.get_advance_tau(e); + EDIT_ITEM_N(float54, e, MSG_ADVANCE_TAU_E, &editable.decimal, 0.0f, 0.5f, []{ stepper.set_advance_tau(editable.decimal, MenuItemBase::itemIndex); }); + } + #endif + #endif #endif // M540 S - Abort on endstop hit when SD printing diff --git a/Marlin/src/lcd/menu/menu_tune.cpp b/Marlin/src/lcd/menu/menu_tune.cpp index 1f1571d986..51d877fc5c 100644 --- a/Marlin/src/lcd/menu/menu_tune.cpp +++ b/Marlin/src/lcd/menu/menu_tune.cpp @@ -31,6 +31,7 @@ #include "menu_item.h" #include "../../module/motion.h" #include "../../module/planner.h" +#include "../../module/stepper.h" #include "../../module/temperature.h" #include "../../MarlinCore.h" @@ -220,6 +221,17 @@ void menu_tune() { EXTRUDER_LOOP() EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10); #endif + #if ENABLED(SMOOTH_LIN_ADVANCE) + #if DISTINCT_E < 2 + editable.decimal = stepper.get_advance_tau(); + EDIT_ITEM(float54, MSG_ADVANCE_TAU, &editable.decimal, 0.0f, 0.5f, []{ stepper.set_advance_tau(editable.decimal); }); + #else + EXTRUDER_LOOP() { + editable.decimal = stepper.get_advance_tau(e); + EDIT_ITEM_N(float54, e, MSG_ADVANCE_TAU_E, &editable.decimal, 0.0f, 0.5f, []{ stepper.set_advance_tau(editable.decimal, MenuItemBase::itemIndex); }); + } + #endif + #endif #endif // diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 881e11f4c0..87cb4cd21b 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -775,6 +775,14 @@ block_t* Planner::get_current_block() { return nullptr; } +block_t* Planner::get_future_block(const uint8_t offset) { + const uint8_t nr_moves = movesplanned(); + if (nr_moves <= offset) return nullptr; + block_t * const block = &block_buffer[block_inc_mod(block_buffer_tail, offset)]; + if (block->flag.recalculate) return nullptr; + return block; +} + /** * Calculate trapezoid parameters, multiplying the entry- and exit-speeds * by the provided factors. If entry_factor is 0 don't change the initial_rate. @@ -840,13 +848,15 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t } } - #if ENABLED(S_CURVE_ACCELERATION) + #if ANY(S_CURVE_ACCELERATION, SMOOTH_LIN_ADVANCE) const float rate_factor = inverse_accel * (STEPPER_TIMER_RATE); // Jerk controlled speed requires to express speed versus time, NOT steps uint32_t acceleration_time = rate_factor * float(cruise_rate - initial_rate), - deceleration_time = rate_factor * float(cruise_rate - final_rate), + deceleration_time = rate_factor * float(cruise_rate - final_rate); + #endif + #if ENABLED(S_CURVE_ACCELERATION) // And to offload calculations from the ISR, we also calculate the inverse of those times here - acceleration_time_inverse = get_period_inverse(acceleration_time), + uint32_t acceleration_time_inverse = get_period_inverse(acceleration_time), deceleration_time_inverse = get_period_inverse(deceleration_time); #endif @@ -856,15 +866,20 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t block->initial_rate = initial_rate; block->final_rate = final_rate; - #if ENABLED(S_CURVE_ACCELERATION) + #if ANY(S_CURVE_ACCELERATION, SMOOTH_LIN_ADVANCE) block->acceleration_time = acceleration_time; block->deceleration_time = deceleration_time; - block->acceleration_time_inverse = acceleration_time_inverse; - block->deceleration_time_inverse = deceleration_time_inverse; block->cruise_rate = cruise_rate; #endif + #if ENABLED(S_CURVE_ACCELERATION) + block->acceleration_time_inverse = acceleration_time_inverse; + block->deceleration_time_inverse = deceleration_time_inverse; + #endif + #if ENABLED(SMOOTH_LIN_ADVANCE) + block->cruise_time = plateau_steps > 0 ? float(plateau_steps) * float(STEPPER_TIMER_RATE) / float(cruise_rate) : 0; + #endif - #if ENABLED(LIN_ADVANCE) + #if HAS_ROUGH_LIN_ADVANCE if (block->la_advance_rate) { const float comp = extruder_advance_K[E_INDEX_N(block->extruder)] * block->steps.e / block->step_event_count; block->max_adv_steps = cruise_rate * comp; @@ -2409,15 +2424,17 @@ bool Planner::_populate_block( if (e_D_ratio > 3.0f) use_advance_lead = false; else { - // Scale E acceleration so that it will be possible to jump to the advance speed. - const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[E_INDEX_N(extruder)] * e_D_ratio) * steps_per_mm; - if (accel > max_accel_steps_per_s2) { - accel = max_accel_steps_per_s2; - if (ENABLED(LA_DEBUG)) SERIAL_ECHOLNPGM("Acceleration limited."); - } + #if HAS_ROUGH_LIN_ADVANCE + // Scale E acceleration so that it will be possible to jump to the advance speed. + const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[E_INDEX_N(extruder)] * e_D_ratio) * steps_per_mm; + if (accel > max_accel_steps_per_s2) { + accel = max_accel_steps_per_s2; + if (TERN0(LA_DEBUG, DEBUGGING(INFO))) SERIAL_ECHOLNPGM("Acceleration limited."); + } + #endif } } - #endif + #endif // LIN_ADVANCE // Limit acceleration per axis if (block->step_event_count <= acceleration_long_cutoff) { @@ -2443,10 +2460,9 @@ bool Planner::_populate_block( block->acceleration_rate = uint32_t(accel * (float(1UL << 24) / (STEPPER_TIMER_RATE))); #endif - #if ENABLED(LIN_ADVANCE) + #if HAS_ROUGH_LIN_ADVANCE block->la_advance_rate = 0; block->la_scaling = 0; - if (use_advance_lead) { // The Bresenham algorithm will convert this step rate into extruder steps block->la_advance_rate = extruder_advance_K[E_INDEX_N(extruder)] * block->acceleration_steps_per_s2; @@ -2456,12 +2472,14 @@ bool Planner::_populate_block( for (uint32_t dividend = block->steps.e << 1; dividend <= (block->step_event_count >> 2); dividend <<= 1) block->la_scaling++; - #if ENABLED(LA_DEBUG) - if (block->la_advance_rate >> block->la_scaling > 10000) + // Output debugging if the rate gets very high + if (TERN0(LA_DEBUG, DEBUGGING(INFO)) && block->la_advance_rate >> block->la_scaling > 10000) SERIAL_ECHOLNPGM("eISR running at > 10kHz: ", block->la_advance_rate); - #endif } - #endif // LIN_ADVANCE + #elif ENABLED(SMOOTH_LIN_ADVANCE) + block->use_advance_lead = use_advance_lead; + block->e_step_ratio = (block->direction_bits.e ? 1 : -1) * float(block->steps.e) / block->step_event_count; + #endif // Formula for the average speed over a 1 step worth of distance if starting from zero and // accelerating at the current limit. Since we can only change the speed every step this is a @@ -2688,7 +2706,8 @@ bool Planner::_populate_block( } #endif - #if ENABLED(LIN_ADVANCE) + // In the SMOOTH_LIN_ADVANCE case, the extra jerk will be applied by the residual current la_step_rate. + #if HAS_ROUGH_LIN_ADVANCE // Advance affects E_AXIS speed and therefore jerk. Add a speed correction whenever // LA is turned OFF. No correction is applied when LA is turned ON (because it didn't // perform well; it takes more time/effort to push/melt filament than the reverse). @@ -2703,7 +2722,7 @@ bool Planner::_populate_block( // Prepare for next segment. previous_advance_rate = block->la_advance_rate; previous_e_mm_per_step = mm_per_step[E_AXIS_N(extruder)]; - #endif + #endif // HAS_ROUGH_LIN_ADVANCE xyze_float_t speed_diff = current_speed; float vmax_junction; diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index e8bacddd9f..a2f91edc81 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -43,6 +43,11 @@ #define JD_USE_LOOKUP_TABLE #endif +#if ENABLED(SMOOTH_LIN_ADVANCE) + #define SMOOTH_LIN_ADV_EXP_ORDER 5 // Closest to Gaussian smoothing between 3 and 7 + #define SMOOTH_LIN_ADV_INTERVAL (STEPPER_TIMER_RATE / SMOOTH_LIN_ADV_HZ) // Hz +#endif + #include "motion.h" #include "../gcode/queue.h" @@ -240,11 +245,17 @@ typedef struct PlannerBlock { uint32_t accelerate_before, // The index of the step event where cruising starts decelerate_start; // The index of the step event on which to start decelerating - #if ENABLED(S_CURVE_ACCELERATION) + #if ENABLED(SMOOTH_LIN_ADVANCE) + uint32_t cruise_time; // Cruise time in STEP timer counts + float e_step_ratio; + #endif + #if ANY(S_CURVE_ACCELERATION, SMOOTH_LIN_ADVANCE) uint32_t cruise_rate, // The actual cruise rate to use, between end of the acceleration phase and start of deceleration phase acceleration_time, // Acceleration time and deceleration time in STEP timer counts - deceleration_time, - acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used + deceleration_time; + #endif + #if ENABLED(S_CURVE_ACCELERATION) + uint32_t acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used deceleration_time_inverse; #else uint32_t acceleration_rate; // Acceleration rate in (2^24 steps)/timer_ticks*s @@ -254,10 +265,14 @@ typedef struct PlannerBlock { // Advance extrusion #if ENABLED(LIN_ADVANCE) - uint32_t la_advance_rate; // The rate at which steps are added whilst accelerating - uint8_t la_scaling; // Scale ISR frequency down and step frequency up by 2 ^ la_scaling - uint16_t max_adv_steps, // Max advance steps to get cruising speed pressure - final_adv_steps; // Advance steps for exit speed pressure + #if ENABLED(SMOOTH_LIN_ADVANCE) + bool use_advance_lead; + #else + uint32_t la_advance_rate; // The rate at which steps are added whilst accelerating + uint8_t la_scaling; // Scale ISR frequency down and step frequency up by 2 ^ la_scaling + uint16_t max_adv_steps, // Max advance steps to get cruising speed pressure + final_adv_steps; // Advance steps for exit speed pressure + #endif #endif uint32_t nominal_rate, // The nominal step rate for this block in step_events/sec @@ -1041,6 +1056,14 @@ class Planner { */ static block_t* get_current_block(); + /** + * Get a planned upcoming block from the buffer. + * Return nullptr if the buffer doesn't have the `current + offset` yet. + * + * WARNING: Called from Stepper ISR context! + */ + static block_t* get_future_block(const uint8_t offset); + /** * "Release" the current block so its slot can be reused. * Called when the current block is no longer needed. diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp index a6714b3e6a..feb433041e 100644 --- a/Marlin/src/module/settings.cpp +++ b/Marlin/src/module/settings.cpp @@ -500,7 +500,12 @@ typedef struct SettingsDataStruct { // // LIN_ADVANCE // - float planner_extruder_advance_K[DISTINCT_E]; // M900 K planner.extruder_advance_K + #if ENABLED(LIN_ADVANCE) + float planner_extruder_advance_K[DISTINCT_E]; // M900 K planner.extruder_advance_K + #if ENABLED(SMOOTH_LIN_ADVANCE) + float stepper_extruder_advance_tau[DISTINCT_E]; // M900 U stepper.extruder_advance_tau + #endif + #endif // // HAS_MOTOR_CURRENT_PWM @@ -1554,13 +1559,13 @@ void MarlinSettings::postprocess() { // Linear Advance // { - _FIELD_TEST(planner_extruder_advance_K); - #if ENABLED(LIN_ADVANCE) + _FIELD_TEST(planner_extruder_advance_K); EEPROM_WRITE(planner.extruder_advance_K); - #else - dummyf = 0; - for (uint8_t q = DISTINCT_E; q--;) EEPROM_WRITE(dummyf); + #if ENABLED(SMOOTH_LIN_ADVANCE) + _FIELD_TEST(stepper_extruder_advance_tau); + EEPROM_WRITE(stepper.extruder_advance_tau); + #endif #endif } @@ -2633,15 +2638,27 @@ void MarlinSettings::postprocess() { // // Linear Advance // + #if ENABLED(LIN_ADVANCE) { float extruder_advance_K[DISTINCT_E]; _FIELD_TEST(planner_extruder_advance_K); EEPROM_READ(extruder_advance_K); - #if ENABLED(LIN_ADVANCE) - if (!validating) - COPY(planner.extruder_advance_K, extruder_advance_K); + if (!validating) + COPY(planner.extruder_advance_K, extruder_advance_K); + #if ENABLED(SMOOTH_LIN_ADVANCE) + _FIELD_TEST(stepper_extruder_advance_tau); + float tau[DISTINCT_E]; + EEPROM_READ(tau); + if (!validating) { + #if ENABLED(DISTINCT_E_FACTORS) + EXTRUDER_LOOP() stepper.set_advance_tau(tau[e], e); + #else + stepper.set_advance_tau(tau[0]); + #endif + } #endif } + #endif // // Motor Current PWM @@ -3742,6 +3759,15 @@ void MarlinSettings::reset() { #else planner.extruder_advance_K[0] = ADVANCE_K; #endif + #if ENABLED(SMOOTH_LIN_ADVANCE) + #if ENABLED(DISTINCT_E_FACTORS) + constexpr float linAdvanceTau[] = ADVANCE_TAU; + EXTRUDER_LOOP() + stepper.set_advance_tau(linAdvanceTau[_MAX(uint8_t(e), COUNT(linAdvanceTau) - 1)], e); + #else + stepper.set_advance_tau(ADVANCE_TAU); + #endif + #endif #endif // diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 1340cd9157..cc042181c6 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -256,10 +256,15 @@ uint32_t Stepper::advance_divisor = 0, #if ENABLED(LIN_ADVANCE) hal_timer_t Stepper::nextAdvanceISR = LA_ADV_NEVER, Stepper::la_interval = LA_ADV_NEVER; - int32_t Stepper::la_delta_error = 0, + #if HAS_ROUGH_LIN_ADVANCE + int32_t Stepper::la_delta_error = 0, Stepper::la_dividend = 0, Stepper::la_advance_steps = 0; - bool Stepper::la_active = false; + bool Stepper::la_active = false; + #else + uint32_t Stepper::curr_step_rate, + Stepper::curr_timer_tick = 0; + #endif #endif #if ENABLED(NONLINEAR_EXTRUSION) @@ -1521,6 +1526,10 @@ void Stepper::isr() { static hal_timer_t nextMainISR = 0; // Interval until the next main Stepper Pulse phase (0 = Now) + #if ENABLED(SMOOTH_LIN_ADVANCE) + static hal_timer_t smoothLinAdvISR = 0; + #endif + // Program timer compare for the maximum period, so it does NOT // flag an interrupt while this ISR is running - So changes from small // periods to big periods are respected and the timer does not reset to 0 @@ -1594,6 +1603,9 @@ void Stepper::isr() { // ^== Time critical. NOTHING besides pulse generation should be above here!!! if (!nextMainISR) nextMainISR = block_phase_isr(); // Manage acc/deceleration, get next block + #if ENABLED(SMOOTH_LIN_ADVANCE) + if (!smoothLinAdvISR) smoothLinAdvISR = smooth_lin_adv_isr(); // Manage la + #endif #if ENABLED(BABYSTEPPING) if (is_babystep) // Avoid ANY stepping too soon after baby-stepping @@ -1609,6 +1621,7 @@ void Stepper::isr() { TERN_(INPUT_SHAPING_Y, NOMORE(interval, ShapingQueue::peek_y())); // Time until next input shaping echo for Y TERN_(INPUT_SHAPING_Z, NOMORE(interval, ShapingQueue::peek_z())); // Time until next input shaping echo for Z TERN_(LIN_ADVANCE, NOMORE(interval, nextAdvanceISR)); // Come back early for Linear Advance? + TERN_(SMOOTH_LIN_ADVANCE, NOMORE(interval, smoothLinAdvISR)); // Come back early for Linear Advance rate update? TERN_(BABYSTEPPING, NOMORE(interval, nextBabystepISR)); // Come back early for Babystepping? // @@ -1621,6 +1634,7 @@ void Stepper::isr() { nextMainISR -= interval; TERN_(HAS_ZV_SHAPING, ShapingQueue::decrement_delays(interval)); TERN_(LIN_ADVANCE, if (nextAdvanceISR != LA_ADV_NEVER) nextAdvanceISR -= interval); + TERN_(SMOOTH_LIN_ADVANCE, if (smoothLinAdvISR != LA_ADV_NEVER) smoothLinAdvISR -= interval); TERN_(BABYSTEPPING, if (nextBabystepISR != BABYSTEP_NEVER) nextBabystepISR -= interval); } // standard motion control @@ -2000,15 +2014,18 @@ void Stepper::pulse_phase_isr() { #if ANY(HAS_E0_STEP, MIXING_EXTRUDER) PULSE_PREP(E); + #endif - #if ENABLED(LIN_ADVANCE) - if (la_active && step_needed.e) { - // don't actually step here, but do subtract movements steps - // from the linear advance step count - step_needed.e = false; - la_advance_steps--; - } - #endif + #if HAS_ROUGH_LIN_ADVANCE + if (la_active && step_needed.e) { + // don't actually step here, but do subtract movements steps + // from the linear advance step count + step_needed.e = false; + la_advance_steps--; + } + #elif ENABLED(SMOOTH_LIN_ADVANCE) + // Extruder steps are exclusively managed by the LA isr + step_needed.e = false; #endif #if HAS_ZV_SHAPING @@ -2458,7 +2475,7 @@ hal_timer_t Stepper::block_phase_isr() { calc_nonlinear_e(acc_step_rate << oversampling_factor); #endif - #if ENABLED(LIN_ADVANCE) + #if HAS_ROUGH_LIN_ADVANCE if (la_active) { const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0; la_interval = calc_timer_interval((acc_step_rate + la_step_rate) >> current_block->la_scaling); @@ -2487,6 +2504,7 @@ hal_timer_t Stepper::block_phase_isr() { else cutter.apply_power(0); } #endif + TERN_(SMOOTH_LIN_ADVANCE, curr_step_rate = acc_step_rate;) } // Are we in Deceleration phase ? else if (step_events_completed >= decelerate_start) { @@ -2523,7 +2541,7 @@ hal_timer_t Stepper::block_phase_isr() { calc_nonlinear_e(step_rate << oversampling_factor); #endif - #if ENABLED(LIN_ADVANCE) + #if HAS_ROUGH_LIN_ADVANCE if (la_active) { const uint32_t la_step_rate = la_advance_steps > current_block->final_adv_steps ? current_block->la_advance_rate : 0; if (la_step_rate != step_rate) { @@ -2561,7 +2579,7 @@ hal_timer_t Stepper::block_phase_isr() { } } #endif - + TERN_(SMOOTH_LIN_ADVANCE, curr_step_rate = step_rate;) } else { // Must be in cruise phase otherwise @@ -2571,13 +2589,14 @@ hal_timer_t Stepper::block_phase_isr() { ticks_nominal = calc_multistep_timer_interval(current_block->nominal_rate << oversampling_factor); // Prepare for deceleration IF_DISABLED(S_CURVE_ACCELERATION, acc_step_rate = current_block->nominal_rate); + TERN_(SMOOTH_LIN_ADVANCE, curr_step_rate = current_block->nominal_rate;) deceleration_time = ticks_nominal / 2; #if ENABLED(NONLINEAR_EXTRUSION) calc_nonlinear_e(current_block->nominal_rate << oversampling_factor); #endif - #if ENABLED(LIN_ADVANCE) + #if HAS_ROUGH_LIN_ADVANCE if (la_active) la_interval = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling); #endif @@ -2712,7 +2731,8 @@ hal_timer_t Stepper::block_phase_isr() { step_event_count = current_block->step_event_count << oversampling_factor; // Initialize Bresenham delta errors to 1/2 - delta_error = TERN_(LIN_ADVANCE, la_delta_error =) -int32_t(step_event_count); + delta_error = -int32_t(step_event_count); + TERN_(HAS_ROUGH_LIN_ADVANCE, la_delta_error = delta_error); // Calculate Bresenham dividends and divisors advance_dividend = (current_block->steps << 1).asLong(); @@ -2762,12 +2782,12 @@ hal_timer_t Stepper::block_phase_isr() { E_TERN_(stepper_extruder = current_block->extruder); // Initialize the trapezoid generator from the current block. - #if ENABLED(LIN_ADVANCE) - la_active = (current_block->la_advance_rate != 0); + #if HAS_ROUGH_LIN_ADVANCE #if DISABLED(MIXING_EXTRUDER) && E_STEPPERS > 1 // If the now active extruder wasn't in use during the last move, its pressure is most likely gone. if (stepper_extruder != last_moved_extruder) la_advance_steps = 0; #endif + la_active = (current_block->la_advance_rate != 0); if (la_active) { // Apply LA scaling and discount the effect of frequency scaling la_dividend = (advance_dividend.e << current_block->la_scaling) << oversampling_factor; @@ -2849,10 +2869,14 @@ hal_timer_t Stepper::block_phase_isr() { #endif #if ENABLED(LIN_ADVANCE) - if (la_active) { - const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0; - la_interval = calc_timer_interval((current_block->initial_rate + la_step_rate) >> current_block->la_scaling); - } + #if ENABLED(SMOOTH_LIN_ADVANCE) + curr_timer_tick = 0; + #else + if (la_active) { + const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0; + la_interval = calc_timer_interval((current_block->initial_rate + la_step_rate) >> current_block->la_scaling); + } + #endif #endif } } // !current_block @@ -2863,17 +2887,176 @@ hal_timer_t Stepper::block_phase_isr() { #if ENABLED(LIN_ADVANCE) + #if ENABLED(SMOOTH_LIN_ADVANCE) + + float Stepper::extruder_advance_tau[DISTINCT_E], + Stepper::extruder_advance_tau_ticks[DISTINCT_E], + Stepper::extruder_advance_alpha[DISTINCT_E]; + + void Stepper::set_la_interval(const int32_t rate) { + if (rate == 0) { + la_interval = LA_ADV_NEVER; + } + else { + const bool forward_e = rate > 0; + la_interval = calc_timer_interval(uint32_t(ABS(rate))); + if (forward_e != motor_direction(E_AXIS)) { + last_direction_bits.toggle(E_AXIS); + count_direction.e = -count_direction.e; + DIR_WAIT_BEFORE(); + E_APPLY_DIR(forward_e, false); + TERN_(FT_MOTION, last_set_direction = last_direction_bits); + DIR_WAIT_AFTER(); + } + } + } + + #if ENABLED(INPUT_SHAPING_E_SYNC) + + constexpr uint16_t IS_COMPENSATION_BUFFER_SIZE = uint16_t(float(SMOOTH_LIN_ADV_HZ) / float(SHAPING_MIN_FREQ) / 2.0f + 0.5f); + + typedef struct { + xy_float_t buffer[IS_COMPENSATION_BUFFER_SIZE]; + uint16_t index; + } DelayBuffer; + + DelayBuffer delayBuffer; + + void add_to_buffer(xy_float_t input) { + delayBuffer.buffer[delayBuffer.index++] = input; + if (delayBuffer.index == IS_COMPENSATION_BUFFER_SIZE) + delayBuffer.index = 0; + } + + xy_float_t lookback(shaping_time_t t /* in stepper timer ticks */) { + constexpr float ADV_TICKS_PER_STEPPER_TICKS = float(SMOOTH_LIN_ADV_HZ) / (STEPPER_TIMER_RATE); + uint32_t delay_steps = t * ADV_TICKS_PER_STEPPER_TICKS + 0.5f; // Convert time to steps + uint16_t past_i; + if (delay_steps>= IS_COMPENSATION_BUFFER_SIZE) { + // this means the buffer is too small. TODO: how to inform user? + past_i = delayBuffer.index; + } + else { + past_i = (delayBuffer.index + IS_COMPENSATION_BUFFER_SIZE - delay_steps) % IS_COMPENSATION_BUFFER_SIZE; + } + return delayBuffer.buffer[past_i]; + } + + #endif // INPUT_SHAPING_E_SYNC + + float lookahead(uint32_t t) { + for (uint8_t i = 0; block_t *block = Planner::get_future_block(i); i++) { + if (block->is_sync()) continue; + if (t <= block->acceleration_time) { + if (!block->use_advance_lead) return 0.0f; + uint32_t rate = STEP_MULTIPLY(t, block->acceleration_rate) + block->initial_rate; + NOMORE(rate, block->nominal_rate); + return rate * block->e_step_ratio; + } + t -= block->acceleration_time; + + if (t <= block->cruise_time) { + if (!block->use_advance_lead) return 0.0f; + return block->cruise_rate * block->e_step_ratio; + } + t -= block->cruise_time; + + if (t <= block->deceleration_time) { + if (!block->use_advance_lead) return 0.0f; + uint32_t rate = STEP_MULTIPLY(t, block->acceleration_rate); + if (rate < block->cruise_rate) { + rate = block->cruise_rate - rate; + NOLESS(rate, block->final_rate); + } + else + rate = block->final_rate; + return rate * block->e_step_ratio; + } + t -= block->deceleration_time; + } + return 0.0f; + } + + hal_timer_t Stepper::smooth_lin_adv_isr() { + float target_adv_steps = 0; + if (current_block) { + const uint32_t t = extruder_advance_tau_ticks[0] + curr_timer_tick; + target_adv_steps = lookahead(t) * Planner::extruder_advance_K[0]; + } + else { + curr_step_rate = 0; + } + static float last_target_adv_steps = 0; + constexpr float dt_inv = SMOOTH_LIN_ADV_HZ; + float la_step_rate = (target_adv_steps - last_target_adv_steps) * dt_inv; + last_target_adv_steps = target_adv_steps; + + static float smoothed_vals[SMOOTH_LIN_ADV_EXP_ORDER] = {0}; + for (uint8_t i = 0; i < SMOOTH_LIN_ADV_EXP_ORDER; i++) { + // Approximate gaussian smoothing via higher order exponential smoothing + la_step_rate = extruder_advance_alpha[0] * la_step_rate + (1 - extruder_advance_alpha[0]) * smoothed_vals[i]; + smoothed_vals[i] = la_step_rate; + } + const float planned_step_rate = current_block ? curr_step_rate * current_block->e_step_ratio : 0; + float total_step_rate = la_step_rate + planned_step_rate; + + #if ENABLED(INPUT_SHAPING_E_SYNC) + + xy_float_t pre_shaping_rate = xy_float_t({0, 0}), + first_pulse_rate = xy_float_t({0, 0}); + float unshaped_rate_e = total_step_rate; + if (current_block) { + const float xy_length = TERN0(INPUT_SHAPING_X, current_block->steps.x) + TERN0(INPUT_SHAPING_Y, current_block->steps.y); + if (xy_length > 0) { + unshaped_rate_e = 0; + pre_shaping_rate = xy_float_t({ + TERN0(INPUT_SHAPING_X, total_step_rate * current_block->steps.x / xy_length), + TERN0(INPUT_SHAPING_Y, total_step_rate * current_block->steps.y / xy_length) + }); + first_pulse_rate = xy_float_t({ + TERN0(INPUT_SHAPING_X, pre_shaping_rate.x * Stepper::shaping_x.factor1 / 128.0f), + TERN0(INPUT_SHAPING_Y, pre_shaping_rate.y * Stepper::shaping_y.factor1 / 128.0f) + }); + } + } + const xy_float_t second_pulse_rate = { + TERN0(INPUT_SHAPING_X, lookback(ShapingQueue::get_delay_x()).x * Stepper::shaping_x.factor2 / 128.0f), + TERN0(INPUT_SHAPING_Y, lookback(ShapingQueue::get_delay_y()).y * Stepper::shaping_y.factor2 / 128.0f) + }; + add_to_buffer(pre_shaping_rate); + + const float x = TERN0(INPUT_SHAPING_X, first_pulse_rate.x + second_pulse_rate.x), + y = TERN0(INPUT_SHAPING_Y, first_pulse_rate.y + second_pulse_rate.y); + + total_step_rate = unshaped_rate_e + x + y; + + #endif // INPUT_SHAPING_E_SYNC + + set_la_interval(total_step_rate); + + curr_timer_tick += SMOOTH_LIN_ADV_INTERVAL; + return SMOOTH_LIN_ADV_INTERVAL; + } + #endif // SMOOTH_LIN_ADVANCE + // Timer interrupt for E. LA_steps is set in the main routine void Stepper::advance_isr() { // Apply Bresenham algorithm so that linear advance can piggy back on // the acceleration and speed values calculated in block_phase_isr(). // This helps keep LA in sync with, for example, S_CURVE_ACCELERATION. - la_delta_error += la_dividend; - const bool e_step_needed = la_delta_error >= 0; + #if HAS_ROUGH_LIN_ADVANCE + la_delta_error += la_dividend; + const bool e_step_needed = la_delta_error >= 0; + #else + constexpr bool e_step_needed = true; + #endif + if (e_step_needed) { count_position.e += count_direction.e; - la_advance_steps += count_direction.e; - la_delta_error -= advance_divisor; + #if HAS_ROUGH_LIN_ADVANCE + la_advance_steps += count_direction.e; + la_delta_error -= advance_divisor; + #endif // Set the STEP pulse ON E_STEP_WRITE(TERN(MIXING_EXTRUDER, mixer.get_next_stepper(), stepper_extruder), STEP_STATE_E); diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 306b2151b5..83ada9202e 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -241,18 +241,21 @@ constexpr ena_mask_t enable_overlap[] = { static bool dequeue_x() { SHAPING_QUEUE_DEQUEUE(x) } static bool empty_x() { return head_x == tail; } static uint16_t free_count_x() { return _free_count_x; } + static uint16_t get_delay_x() { return delay_x; } #endif #if ENABLED(INPUT_SHAPING_Y) static shaping_time_t peek_y() { return _peek_y; } static bool dequeue_y() { SHAPING_QUEUE_DEQUEUE(y) } static bool empty_y() { return head_y == tail; } static uint16_t free_count_y() { return _free_count_y; } + static uint16_t get_delay_y() { return delay_y; } #endif #if ENABLED(INPUT_SHAPING_Z) static shaping_time_t peek_z() { return _peek_z; } static bool dequeue_z() { SHAPING_QUEUE_DEQUEUE(z) } static bool empty_z() { return head_z == tail; } static uint16_t free_count_z() { return _free_count_z; } + static uint16_t get_delay_z() { return delay_z; } #endif static void purge() { const auto st = shaping_time_t(-1); @@ -292,6 +295,7 @@ constexpr ena_mask_t enable_overlap[] = { class Stepper { friend class Max7219; friend class FTMotion; + friend class MarlinSettings; friend void stepperTask(void *); public: @@ -348,6 +352,16 @@ class Stepper { static constexpr bool adaptive_step_smoothing_enabled = true; #endif + #if ENABLED(SMOOTH_LIN_ADVANCE) + static void set_advance_tau(const_float_t tau, const uint8_t e=E_INDEX_N(active_extruder)) { + extruder_advance_tau[e] = tau; + extruder_advance_tau_ticks[e] = tau * (STEPPER_TIMER_RATE); // i.e., <= STEPPER_TIMER_RATE / 2 + // α=1−exp(−dt/τ) + extruder_advance_alpha[e] = 1.0f - expf(-(SMOOTH_LIN_ADV_INTERVAL) * (SMOOTH_LIN_ADV_EXP_ORDER) / extruder_advance_tau_ticks[e]); + } + static float get_advance_tau(const uint8_t e=E_INDEX_N(active_extruder)) { return extruder_advance_tau[e]; } + #endif + private: static block_t* current_block; // A pointer to the block currently being traced @@ -434,11 +448,20 @@ class Stepper { #if ENABLED(LIN_ADVANCE) static constexpr hal_timer_t LA_ADV_NEVER = HAL_TIMER_TYPE_MAX; static hal_timer_t nextAdvanceISR, - la_interval; // Interval between ISR calls for LA - static int32_t la_delta_error, // Analogue of delta_error.e for E steps in LA ISR - la_dividend, // Analogue of advance_dividend.e for E steps in LA ISR - la_advance_steps; // Count of steps added to increase nozzle pressure - static bool la_active; // Whether linear advance is used on the present segment. + la_interval; // Interval between ISR calls for LA + #if ENABLED(SMOOTH_LIN_ADVANCE) + static uint32_t curr_timer_tick, // Current tick relative to block start + curr_step_rate; // Current motion step rate + static float extruder_advance_tau[DISTINCT_E], // Smoothing time; also the lookahead time of the smoother + extruder_advance_tau_ticks[DISTINCT_E], // Same as extruder_advance_tau but in in stepper timer ticks + extruder_advance_alpha[DISTINCT_E]; // The smoothing factor of each stage of the high-order exponential + // smoothing filter (calculated from tau) + #else + static int32_t la_delta_error, // Analogue of delta_error.e for E steps in LA ISR + la_dividend, // Analogue of advance_dividend.e for E steps in LA ISR + la_advance_steps; // Count of steps added to increase nozzle pressure + static bool la_active; // Whether linear advance is used on the present segment + #endif #endif #if ENABLED(NONLINEAR_EXTRUSION) @@ -504,6 +527,10 @@ class Stepper { #if ENABLED(LIN_ADVANCE) // The Linear advance ISR phase static void advance_isr(); + #if ENABLED(SMOOTH_LIN_ADVANCE) + static void set_la_interval(const int32_t rate); + static hal_timer_t smooth_lin_adv_isr(); + #endif #endif #if ENABLED(BABYSTEPPING) @@ -558,7 +585,7 @@ class Stepper { current_block = nullptr; axis_did_move.reset(); planner.release_current_block(); - TERN_(LIN_ADVANCE, la_interval = nextAdvanceISR = LA_ADV_NEVER); + TERN_(HAS_ROUGH_LIN_ADVANCE, la_interval = nextAdvanceISR = LA_ADV_NEVER); } // Quickly stop all steppers diff --git a/buildroot/tests/LPC1768 b/buildroot/tests/LPC1768 index 3a08af8ae8..cd911a9060 100755 --- a/buildroot/tests/LPC1768 +++ b/buildroot/tests/LPC1768 @@ -77,21 +77,19 @@ opt_set MOTHERBOARD BOARD_BTT_SKR_V1_4 SERIAL_PORT -1 \ Z_STEPPER_ALIGN_ITERATIONS 10 DEFAULT_STEPPER_TIMEOUT_SEC 0 \ SLOWDOWN_DIVISOR 16 SDCARD_CONNECTION ONBOARD BLOCK_BUFFER_SIZE 64 \ CHOPPER_TIMING CHOPPER_DEFAULT_24V MMU_SERIAL_PORT 0 -opt_enable PIDTEMPBED S_CURVE_ACCELERATION \ - USE_PROBE_FOR_Z_HOMING BLTOUCH FILAMENT_RUNOUT_SENSOR \ - AUTO_BED_LEVELING_BILINEAR RESTORE_LEVELING_AFTER_G28 \ - EXTRAPOLATE_BEYOND_GRID LCD_BED_LEVELING MESH_EDIT_MENU Z_SAFE_HOMING \ - EEPROM_SETTINGS EEPROM_AUTO_INIT NOZZLE_PARK_FEATURE SDSUPPORT \ - SPEAKER CR10_STOCKDISPLAY QUICK_HOME BLTOUCH_FORCE_SW_MODE \ - Z_STEPPER_AUTO_ALIGN INPUT_SHAPING_X INPUT_SHAPING_Y SHAPING_MENU \ - ADAPTIVE_STEP_SMOOTHING LCD_INFO_MENU STATUS_MESSAGE_SCROLLING \ - SET_PROGRESS_MANUALLY M73_REPORT SHOW_REMAINING_TIME \ - PRINT_PROGRESS_SHOW_DECIMALS AUTO_REPORT_SD_STATUS USE_BIG_EDIT_FONT \ - BABYSTEPPING BABYSTEP_WITHOUT_HOMING BABYSTEP_ALWAYS_AVAILABLE \ - DOUBLECLICK_FOR_Z_BABYSTEPPING BABYSTEP_DISPLAY_TOTAL LIN_ADVANCE \ - BEZIER_CURVE_SUPPORT EMERGENCY_PARSER ADVANCED_PAUSE_FEATURE \ - TMC_DEBUG HOST_ACTION_COMMANDS HOST_PAUSE_M76 HOST_PROMPT_SUPPORT \ - HOST_STATUS_NOTIFICATIONS MMU_DEBUG +opt_enable PIDTEMPBED FILAMENT_RUNOUT_SENSOR NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE \ + AUTO_BED_LEVELING_BILINEAR EXTRAPOLATE_BEYOND_GRID RESTORE_LEVELING_AFTER_G28 LCD_BED_LEVELING MESH_EDIT_MENU \ + BLTOUCH BLTOUCH_FORCE_SW_MODE USE_PROBE_FOR_Z_HOMING Z_SAFE_HOMING QUICK_HOME Z_STEPPER_AUTO_ALIGN \ + SDSUPPORT AUTO_REPORT_SD_STATUS \ + EEPROM_SETTINGS EEPROM_AUTO_INIT \ + CR10_STOCKDISPLAY USE_BIG_EDIT_FONT SPEAKER LCD_INFO_MENU STATUS_MESSAGE_SCROLLING \ + INPUT_SHAPING_X INPUT_SHAPING_Y SHAPING_MENU \ + BEZIER_CURVE_SUPPORT \ + LIN_ADVANCE SMOOTH_LIN_ADVANCE INPUT_SHAPING_E_SYNC \ + TMC_DEBUG ADAPTIVE_STEP_SMOOTHING \ + SET_PROGRESS_MANUALLY M73_REPORT SHOW_REMAINING_TIME PRINT_PROGRESS_SHOW_DECIMALS \ + BABYSTEPPING BABYSTEP_WITHOUT_HOMING BABYSTEP_ALWAYS_AVAILABLE DOUBLECLICK_FOR_Z_BABYSTEPPING BABYSTEP_DISPLAY_TOTAL \ + EMERGENCY_PARSER HOST_ACTION_COMMANDS HOST_PAUSE_M76 HOST_PROMPT_SUPPORT HOST_STATUS_NOTIFICATIONS MMU_DEBUG opt_disable Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN FILAMENT_LOAD_UNLOAD_GCODES \ PARK_HEAD_ON_PAUSE exec_test $1 $2 "BigTreeTech SKR 1.4 | MMU2" "$3" diff --git a/buildroot/tests/LPC1769 b/buildroot/tests/LPC1769 index f29bbb5656..5356d3e186 100755 --- a/buildroot/tests/LPC1769 +++ b/buildroot/tests/LPC1769 @@ -81,20 +81,20 @@ opt_set MOTHERBOARD BOARD_BTT_SKR_V1_4_TURBO SERIAL_PORT -1 \ SLOWDOWN_DIVISOR 16 SDCARD_CONNECTION ONBOARD BLOCK_BUFFER_SIZE 64 \ CHOPPER_TIMING CHOPPER_DEFAULT_24V MMU_SERIAL_PORT 0 \ Z_MIN_ENDSTOP_HIT_STATE HIGH -opt_enable PIDTEMPBED S_CURVE_ACCELERATION \ - USE_PROBE_FOR_Z_HOMING BLTOUCH FILAMENT_RUNOUT_SENSOR \ - AUTO_BED_LEVELING_BILINEAR RESTORE_LEVELING_AFTER_G28 \ - EXTRAPOLATE_BEYOND_GRID LCD_BED_LEVELING MESH_EDIT_MENU Z_SAFE_HOMING \ - EEPROM_SETTINGS EEPROM_AUTO_INIT NOZZLE_PARK_FEATURE SDSUPPORT \ - SPEAKER CR10_STOCKDISPLAY QUICK_HOME BLTOUCH_FORCE_SW_MODE \ - Z_STEPPER_AUTO_ALIGN INPUT_SHAPING_X INPUT_SHAPING_Y SHAPING_MENU \ - ADAPTIVE_STEP_SMOOTHING LCD_INFO_MENU STATUS_MESSAGE_SCROLLING \ +opt_enable PIDTEMPBED \ + FILAMENT_RUNOUT_SENSOR NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE \ + BLTOUCH BLTOUCH_FORCE_SW_MODE USE_PROBE_FOR_Z_HOMING Z_SAFE_HOMING QUICK_HOME Z_STEPPER_AUTO_ALIGN \ + AUTO_BED_LEVELING_BILINEAR EXTRAPOLATE_BEYOND_GRID RESTORE_LEVELING_AFTER_G28 LCD_BED_LEVELING MESH_EDIT_MENU \ + EEPROM_SETTINGS EEPROM_AUTO_INIT \ + SDSUPPORT CR10_STOCKDISPLAY SPEAKER LCD_INFO_MENU STATUS_MESSAGE_SCROLLING \ + INPUT_SHAPING_X INPUT_SHAPING_Y SHAPING_MENU \ + BEZIER_CURVE_SUPPORT LIN_ADVANCE \ + TMC_DEBUG S_CURVE_ACCELERATION ADAPTIVE_STEP_SMOOTHING \ SET_PROGRESS_MANUALLY M73_REPORT SHOW_REMAINING_TIME \ PRINT_PROGRESS_SHOW_DECIMALS AUTO_REPORT_SD_STATUS USE_BIG_EDIT_FONT \ BABYSTEPPING BABYSTEP_WITHOUT_HOMING BABYSTEP_ALWAYS_AVAILABLE \ - DOUBLECLICK_FOR_Z_BABYSTEPPING BABYSTEP_DISPLAY_TOTAL LIN_ADVANCE \ - BEZIER_CURVE_SUPPORT EMERGENCY_PARSER ADVANCED_PAUSE_FEATURE \ - TMC_DEBUG HOST_ACTION_COMMANDS HOST_PAUSE_M76 HOST_PROMPT_SUPPORT HOST_STATUS_NOTIFICATIONS \ + DOUBLECLICK_FOR_Z_BABYSTEPPING BABYSTEP_DISPLAY_TOTAL \ + EMERGENCY_PARSER HOST_ACTION_COMMANDS HOST_PAUSE_M76 HOST_PROMPT_SUPPORT HOST_STATUS_NOTIFICATIONS \ MMU3_SPOOL_JOIN_CONSUMES_ALL_FILAMENT MMU_MENUS MMU_DEBUG opt_disable Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN FILAMENT_LOAD_UNLOAD_GCODES PARK_HEAD_ON_PAUSE exec_test $1 $2 "BigTreeTech SKR 1.4 Turbo | MMU3" "$3"