From 5ae45bab1826fc9c743d62a52f55cea5fe124b6b Mon Sep 17 00:00:00 2001 From: studiodyne <42887851+studiodyne@users.noreply.github.com> Date: Mon, 27 Apr 2020 12:59:52 +0200 Subject: [PATCH] Adjustable XY_FREQUENCY_LIMIT (#17583) --- Marlin/Configuration_adv.h | 14 +++-- Marlin/src/gcode/config/M200-M205.cpp | 5 ++ Marlin/src/lcd/language/language_en.h | 2 + Marlin/src/lcd/language/language_fr.h | 2 + Marlin/src/lcd/menu/menu_advanced.cpp | 12 +++-- Marlin/src/module/planner.cpp | 76 ++++++++++++--------------- Marlin/src/module/planner.h | 32 ++++++----- 7 files changed, 82 insertions(+), 61 deletions(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index e01f71d7e..7a125b06d 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -784,10 +784,16 @@ #define SLOWDOWN_DIVISOR 2 #endif -// Frequency limit -// See nophead's blog for more info -// Not working O -//#define XY_FREQUENCY_LIMIT 15 +/** + * XY Frequency limit + * Reduce resonance by limiting the frequency of small zigzag infill moves. + * See http://hydraraptor.blogspot.com/2010/12/frequency-limit.html + * Use M201 F G to change limits at runtime. + */ +//#define XY_FREQUENCY_LIMIT 10 // (Hz) Maximum frequency of small zigzag infill moves. Set with M201 F. +#ifdef XY_FREQUENCY_LIMIT + #define XY_FREQUENCY_MIN_PERCENT 5 // (percent) Minimum FR percentage to apply. Set with M201 G. +#endif // Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end // of the buffer and all stops. This should not be much greater than zero and should only be changed diff --git a/Marlin/src/gcode/config/M200-M205.cpp b/Marlin/src/gcode/config/M200-M205.cpp index 30e6f0f56..b717dc336 100644 --- a/Marlin/src/gcode/config/M200-M205.cpp +++ b/Marlin/src/gcode/config/M200-M205.cpp @@ -60,6 +60,11 @@ void GcodeSuite::M201() { const int8_t target_extruder = get_target_extruder_from_command(); if (target_extruder < 0) return; + #ifdef XY_FREQUENCY_LIMIT + if (parser.seenval('F')) planner.set_frequency_limit(parser.value_byte()); + if (parser.seenval('G')) planner.xy_freq_min_speed_factor = constrain(parser.value_float(), 1, 100) / 100; + #endif + LOOP_XYZE(i) { if (parser.seen(axis_codes[i])) { const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i); diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index e9cd043e6..f9fb84cc8 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -301,6 +301,8 @@ namespace Language_en { PROGMEM Language_Str MSG_AMAX_EN = _UxGT("Amax *"); PROGMEM Language_Str MSG_A_RETRACT = _UxGT("A-Retract"); PROGMEM Language_Str MSG_A_TRAVEL = _UxGT("A-Travel"); + PROGMEM Language_Str MSG_XY_FREQUENCY_LIMIT = _UxGT("Frequency max"); + PROGMEM Language_Str MSG_XY_FREQUENCY_FEEDRATE = _UxGT("Feed min"); PROGMEM Language_Str MSG_STEPS_PER_MM = _UxGT("Steps/mm"); PROGMEM Language_Str MSG_A_STEPS = LCD_STR_A _UxGT("steps/mm"); PROGMEM Language_Str MSG_B_STEPS = LCD_STR_B _UxGT("steps/mm"); diff --git a/Marlin/src/lcd/language/language_fr.h b/Marlin/src/lcd/language/language_fr.h index 23258b901..3daac87a2 100644 --- a/Marlin/src/lcd/language/language_fr.h +++ b/Marlin/src/lcd/language/language_fr.h @@ -262,6 +262,8 @@ namespace Language_fr { PROGMEM Language_Str MSG_ACCELERATION = _UxGT("Accélération"); PROGMEM Language_Str MSG_A_RETRACT = _UxGT("Acc.rétraction"); PROGMEM Language_Str MSG_A_TRAVEL = _UxGT("Acc.course"); + PROGMEM Language_Str MSG_XY_FREQUENCY_LIMIT = _UxGT("Fréquence max"); + PROGMEM Language_Str MSG_XY_FREQUENCY_FEEDRATE = _UxGT("Vitesse min"); PROGMEM Language_Str MSG_STEPS_PER_MM = _UxGT("Pas/mm"); PROGMEM Language_Str MSG_A_STEPS = LCD_STR_A _UxGT(" pas/mm"); PROGMEM Language_Str MSG_B_STEPS = LCD_STR_B _UxGT(" pas/mm"); diff --git a/Marlin/src/lcd/menu/menu_advanced.cpp b/Marlin/src/lcd/menu/menu_advanced.cpp index 166442079..0322ad1ff 100644 --- a/Marlin/src/lcd/menu/menu_advanced.cpp +++ b/Marlin/src/lcd/menu/menu_advanced.cpp @@ -405,9 +405,9 @@ void menu_cancelobject(); #endif #define EDIT_AMAX(Q,L) EDIT_ITEM_FAST(long5_25, MSG_AMAX_##Q, &planner.settings.max_acceleration_mm_per_s2[_AXIS(Q)], L, max_accel_edit_scaled[_AXIS(Q)], []{ planner.reset_acceleration_rates(); }) - EDIT_AMAX(A,100); - EDIT_AMAX(B,100); - EDIT_AMAX(C, 10); + EDIT_AMAX(A, 100); + EDIT_AMAX(B, 100); + EDIT_AMAX(C, 10); #if ENABLED(DISTINCT_E_FACTORS) EDIT_ITEM_FAST(long5_25, MSG_AMAX_E, &planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(active_extruder)], 100, max_accel_edit_scaled.e, []{ planner.reset_acceleration_rates(); }); @@ -417,6 +417,12 @@ void menu_cancelobject(); EDIT_ITEM_FAST(long5_25, MSG_AMAX_E, &planner.settings.max_acceleration_mm_per_s2[E_AXIS], 100, max_accel_edit_scaled.e, []{ planner.reset_acceleration_rates(); }); #endif + #ifdef XY_FREQUENCY_LIMIT + EDIT_ITEM(uint16_3, MSG_XY_FREQUENCY_LIMIT, &planner.xy_freq_limit_hz, 0, 100, refresh_frequency_limit(), true); + editable.uint8 = ROUND(planner.xy_freq_min_speed_factor * 255 * 100); // percent to u8 + EDIT_ITEM(percent, MSG_XY_FREQUENCY_FEEDRATE, &editable.uint8, 3, 255, []{ planner.set_min_speed_factor_u8(editable.uint8); }, true); + #endif + END_MENU(); } diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 0643ba1cb..ad9dffe4e 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -113,7 +113,7 @@ Planner planner; - // public: +// public: /** * A ring buffer of moves described in steps @@ -200,10 +200,9 @@ float Planner::previous_nominal_speed_sqr; #endif #ifdef XY_FREQUENCY_LIMIT - // Old direction bits. Used for speed calculations - unsigned char Planner::old_direction_bits = 0; - // Segment times (in µs). Used for speed calculations - xy_ulong_t Planner::axis_segment_time_us[3] = { { MAX_FREQ_TIME_US + 1, MAX_FREQ_TIME_US + 1 } }; + int8_t Planner::xy_freq_limit_hz = XY_FREQUENCY_LIMIT; + float Planner::xy_freq_min_speed_factor = (XY_FREQUENCY_MIN_PERCENT) * 0.01f; + int32_t Planner::xy_freq_min_interval_us = LROUND(1000000.0 / (XY_FREQUENCY_LIMIT)); #endif #if ENABLED(LIN_ADVANCE) @@ -2006,7 +2005,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, // Slow down when the buffer starts to empty, rather than wait at the corner for a buffer refill #if EITHER(SLOWDOWN, ULTRA_LCD) || defined(XY_FREQUENCY_LIMIT) // Segment time im micro seconds - uint32_t segment_time_us = LROUND(1000000.0f / inverse_secs); + int32_t segment_time_us = LROUND(1000000.0f / inverse_secs); #endif #if ENABLED(SLOWDOWN) @@ -2014,9 +2013,10 @@ bool Planner::_populate_block(block_t * const block, bool split_move, #define SLOWDOWN_DIVISOR 2 #endif if (WITHIN(moves_queued, 2, (BLOCK_BUFFER_SIZE) / (SLOWDOWN_DIVISOR) - 1)) { - if (segment_time_us < settings.min_segment_time_us) { - // buffer is draining, add extra time. The amount of time added increases if the buffer is still emptied more. - const uint32_t nst = segment_time_us + LROUND(2 * (settings.min_segment_time_us - segment_time_us) / moves_queued); + const int32_t time_diff = settings.min_segment_time_us - segment_time_us; + if (time_diff > 0) { + // Buffer is draining so add extra time. The amount of time added increases if the buffer is still emptied more. + const int32_t nst = segment_time_us + LROUND(2 * time_diff / moves_queued); inverse_secs = 1000000.0f / nst; #if defined(XY_FREQUENCY_LIMIT) || HAS_SPI_LCD segment_time_us = nst; @@ -2072,42 +2072,36 @@ bool Planner::_populate_block(block_t * const block, bool split_move, } #endif - // Max segment time in µs. #ifdef XY_FREQUENCY_LIMIT - // Check and limit the xy direction change frequency - const unsigned char direction_change = block->direction_bits ^ old_direction_bits; - old_direction_bits = block->direction_bits; - segment_time_us = LROUND((float)segment_time_us / speed_factor); + static uint8_t old_direction_bits; // = 0 - uint32_t xs0 = axis_segment_time_us[0].x, - xs1 = axis_segment_time_us[1].x, - xs2 = axis_segment_time_us[2].x, - ys0 = axis_segment_time_us[0].y, - ys1 = axis_segment_time_us[1].y, - ys2 = axis_segment_time_us[2].y; + if (xy_freq_limit_hz) { + // Check and limit the xy direction change frequency + const uint8_t direction_change = block->direction_bits ^ old_direction_bits; + old_direction_bits = block->direction_bits; + segment_time_us = LROUND(float(segment_time_us) / speed_factor); - if (TEST(direction_change, X_AXIS)) { - xs2 = axis_segment_time_us[2].x = xs1; - xs1 = axis_segment_time_us[1].x = xs0; - xs0 = 0; + static int32_t xs0, xs1, xs2, ys0, ys1, ys2; + if (segment_time_us > xy_freq_min_interval_us) + xs2 = xs1 = ys2 = ys1 = xy_freq_min_interval_us; + else { + xs2 = xs1; xs1 = xs0; + ys2 = ys1; ys1 = ys0; + } + xs0 = TEST(direction_change, X_AXIS) ? segment_time_us : xy_freq_min_interval_us; + ys0 = TEST(direction_change, Y_AXIS) ? segment_time_us : xy_freq_min_interval_us; + + if (segment_time_us < xy_freq_min_interval_us) { + const int32_t least_xy_segment_time = _MIN(_MAX(xs0, xs1, xs2), _MAX(ys0, ys1, ys2)); + if (least_xy_segment_time < xy_freq_min_interval_us) { + float freq_xy_feedrate = (speed_factor * least_xy_segment_time) / xy_freq_min_interval_us; + NOLESS(freq_xy_feedrate, xy_freq_min_speed_factor); + NOMORE(speed_factor, freq_xy_feedrate); + } + } } - xs0 = axis_segment_time_us[0].x = xs0 + segment_time_us; - if (TEST(direction_change, Y_AXIS)) { - ys2 = axis_segment_time_us[2].y = axis_segment_time_us[1].y; - ys1 = axis_segment_time_us[1].y = axis_segment_time_us[0].y; - ys0 = 0; - } - ys0 = axis_segment_time_us[0].y = ys0 + segment_time_us; - - const uint32_t max_x_segment_time = _MAX(xs0, xs1, xs2), - max_y_segment_time = _MAX(ys0, ys1, ys2), - min_xy_segment_time = _MIN(max_x_segment_time, max_y_segment_time); - if (min_xy_segment_time < MAX_FREQ_TIME_US) { - const float low_sf = speed_factor * min_xy_segment_time / (MAX_FREQ_TIME_US); - NOMORE(speed_factor, low_sf); - } #endif // XY_FREQUENCY_LIMIT // Correct the speed @@ -2832,7 +2826,7 @@ void Planner::set_max_jerk(const AxisEnum axis, float targetValue) { const bool was_enabled = stepper.suspend(); #endif - millis_t bbru = block_buffer_runtime_us; + uint32_t bbru = block_buffer_runtime_us; #ifdef __AVR__ // Reenable Stepper ISR @@ -2844,7 +2838,7 @@ void Planner::set_max_jerk(const AxisEnum axis, float targetValue) { // Doesn't matter because block_buffer_runtime_us is already too small an estimation. bbru >>= 10; // limit to about a minute. - NOMORE(bbru, 0xFFFFul); + NOMORE(bbru, 0x0000FFFFUL); return bbru; } diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index bf585320d..e9658f2c6 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -352,6 +352,23 @@ class Planner { #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT) static bool abort_on_endstop_hit; #endif + #ifdef XY_FREQUENCY_LIMIT + static int8_t xy_freq_limit_hz; // Minimum XY frequency setting + static float xy_freq_min_speed_factor; // Minimum speed factor setting + static int32_t xy_freq_min_interval_us; // Minimum segment time based on xy_freq_limit_hz + static inline void refresh_frequency_limit() { + //xy_freq_min_interval_us = xy_freq_limit_hz ?: LROUND(1000000.0f / xy_freq_limit_hz); + if (xy_freq_limit_hz) + xy_freq_min_interval_us = LROUND(1000000.0f / xy_freq_limit_hz); + } + static inline void set_min_speed_factor_u8(const uint8_t v255) { + xy_freq_min_speed_factor = float(ui8_to_percent(v255)) / 100; + } + static inline void set_frequency_limit(const uint8_t hz) { + xy_freq_limit_hz = constrain(hz, 0, 100); + refresh_frequency_limit(); + } + #endif private: @@ -375,23 +392,12 @@ class Planner { #endif #if ENABLED(DISABLE_INACTIVE_EXTRUDER) - /** - * Counters to manage disabling inactive extruders - */ + // Counters to manage disabling inactive extruders static uint8_t g_uc_extruder_last_move[EXTRUDERS]; - #endif // DISABLE_INACTIVE_EXTRUDER - - #ifdef XY_FREQUENCY_LIMIT - // Used for the frequency limit - #define MAX_FREQ_TIME_US (uint32_t)(1000000.0 / XY_FREQUENCY_LIMIT) - // Old direction bits. Used for speed calculations - static unsigned char old_direction_bits; - // Segment times (in µs). Used for speed calculations - static xy_ulong_t axis_segment_time_us[3]; #endif #if HAS_SPI_LCD - volatile static uint32_t block_buffer_runtime_us; //Theoretical block buffer runtime in µs + volatile static uint32_t block_buffer_runtime_us; // Theoretical block buffer runtime in µs #endif public: