Adjustable XY_FREQUENCY_LIMIT (#17583)

This commit is contained in:
studiodyne 2020-04-27 12:59:52 +02:00 committed by GitHub
parent 65daf3ba40
commit 5ae45bab18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 61 deletions

View file

@ -784,10 +784,16 @@
#define SLOWDOWN_DIVISOR 2 #define SLOWDOWN_DIVISOR 2
#endif #endif
// Frequency limit /**
// See nophead's blog for more info * XY Frequency limit
// Not working O * Reduce resonance by limiting the frequency of small zigzag infill moves.
//#define XY_FREQUENCY_LIMIT 15 * See http://hydraraptor.blogspot.com/2010/12/frequency-limit.html
* Use M201 F<freq> G<min%> to change limits at runtime.
*/
//#define XY_FREQUENCY_LIMIT 10 // (Hz) Maximum frequency of small zigzag infill moves. Set with M201 F<hertz>.
#ifdef XY_FREQUENCY_LIMIT
#define XY_FREQUENCY_MIN_PERCENT 5 // (percent) Minimum FR percentage to apply. Set with M201 G<min%>.
#endif
// Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end // 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 // of the buffer and all stops. This should not be much greater than zero and should only be changed

View file

@ -60,6 +60,11 @@ void GcodeSuite::M201() {
const int8_t target_extruder = get_target_extruder_from_command(); const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return; 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) { LOOP_XYZE(i) {
if (parser.seen(axis_codes[i])) { if (parser.seen(axis_codes[i])) {
const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i); const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i);

View file

@ -301,6 +301,8 @@ namespace Language_en {
PROGMEM Language_Str MSG_AMAX_EN = _UxGT("Amax *"); PROGMEM Language_Str MSG_AMAX_EN = _UxGT("Amax *");
PROGMEM Language_Str MSG_A_RETRACT = _UxGT("A-Retract"); PROGMEM Language_Str MSG_A_RETRACT = _UxGT("A-Retract");
PROGMEM Language_Str MSG_A_TRAVEL = _UxGT("A-Travel"); 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_STEPS_PER_MM = _UxGT("Steps/mm");
PROGMEM Language_Str MSG_A_STEPS = LCD_STR_A _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"); PROGMEM Language_Str MSG_B_STEPS = LCD_STR_B _UxGT("steps/mm");

View file

@ -262,6 +262,8 @@ namespace Language_fr {
PROGMEM Language_Str MSG_ACCELERATION = _UxGT("Accélération"); PROGMEM Language_Str MSG_ACCELERATION = _UxGT("Accélération");
PROGMEM Language_Str MSG_A_RETRACT = _UxGT("Acc.rétraction"); PROGMEM Language_Str MSG_A_RETRACT = _UxGT("Acc.rétraction");
PROGMEM Language_Str MSG_A_TRAVEL = _UxGT("Acc.course"); 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_STEPS_PER_MM = _UxGT("Pas/mm");
PROGMEM Language_Str MSG_A_STEPS = LCD_STR_A _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"); PROGMEM Language_Str MSG_B_STEPS = LCD_STR_B _UxGT(" pas/mm");

View file

@ -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(); }); 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 #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(); END_MENU();
} }

View file

@ -200,10 +200,9 @@ float Planner::previous_nominal_speed_sqr;
#endif #endif
#ifdef XY_FREQUENCY_LIMIT #ifdef XY_FREQUENCY_LIMIT
// Old direction bits. Used for speed calculations int8_t Planner::xy_freq_limit_hz = XY_FREQUENCY_LIMIT;
unsigned char Planner::old_direction_bits = 0; float Planner::xy_freq_min_speed_factor = (XY_FREQUENCY_MIN_PERCENT) * 0.01f;
// Segment times (in µs). Used for speed calculations int32_t Planner::xy_freq_min_interval_us = LROUND(1000000.0 / (XY_FREQUENCY_LIMIT));
xy_ulong_t Planner::axis_segment_time_us[3] = { { MAX_FREQ_TIME_US + 1, MAX_FREQ_TIME_US + 1 } };
#endif #endif
#if ENABLED(LIN_ADVANCE) #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 // 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) #if EITHER(SLOWDOWN, ULTRA_LCD) || defined(XY_FREQUENCY_LIMIT)
// Segment time im micro seconds // 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 #endif
#if ENABLED(SLOWDOWN) #if ENABLED(SLOWDOWN)
@ -2014,9 +2013,10 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
#define SLOWDOWN_DIVISOR 2 #define SLOWDOWN_DIVISOR 2
#endif #endif
if (WITHIN(moves_queued, 2, (BLOCK_BUFFER_SIZE) / (SLOWDOWN_DIVISOR) - 1)) { if (WITHIN(moves_queued, 2, (BLOCK_BUFFER_SIZE) / (SLOWDOWN_DIVISOR) - 1)) {
if (segment_time_us < settings.min_segment_time_us) { const int32_t time_diff = settings.min_segment_time_us - segment_time_us;
// buffer is draining, add extra time. The amount of time added increases if the buffer is still emptied more. if (time_diff > 0) {
const uint32_t nst = segment_time_us + LROUND(2 * (settings.min_segment_time_us - segment_time_us) / moves_queued); // 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; inverse_secs = 1000000.0f / nst;
#if defined(XY_FREQUENCY_LIMIT) || HAS_SPI_LCD #if defined(XY_FREQUENCY_LIMIT) || HAS_SPI_LCD
segment_time_us = nst; segment_time_us = nst;
@ -2072,42 +2072,36 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
} }
#endif #endif
// Max segment time in µs.
#ifdef XY_FREQUENCY_LIMIT #ifdef XY_FREQUENCY_LIMIT
static uint8_t old_direction_bits; // = 0
if (xy_freq_limit_hz) {
// Check and limit the xy direction change frequency // Check and limit the xy direction change frequency
const unsigned char direction_change = block->direction_bits ^ old_direction_bits; const uint8_t direction_change = block->direction_bits ^ old_direction_bits;
old_direction_bits = block->direction_bits; old_direction_bits = block->direction_bits;
segment_time_us = LROUND((float)segment_time_us / speed_factor); segment_time_us = LROUND(float(segment_time_us) / speed_factor);
uint32_t xs0 = axis_segment_time_us[0].x, static int32_t xs0, xs1, xs2, ys0, ys1, ys2;
xs1 = axis_segment_time_us[1].x, if (segment_time_us > xy_freq_min_interval_us)
xs2 = axis_segment_time_us[2].x, xs2 = xs1 = ys2 = ys1 = xy_freq_min_interval_us;
ys0 = axis_segment_time_us[0].y, else {
ys1 = axis_segment_time_us[1].y, xs2 = xs1; xs1 = xs0;
ys2 = axis_segment_time_us[2].y; ys2 = ys1; ys1 = ys0;
if (TEST(direction_change, X_AXIS)) {
xs2 = axis_segment_time_us[2].x = xs1;
xs1 = axis_segment_time_us[1].x = xs0;
xs0 = 0;
} }
xs0 = axis_segment_time_us[0].x = xs0 + segment_time_us; 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 (TEST(direction_change, Y_AXIS)) { if (segment_time_us < xy_freq_min_interval_us) {
ys2 = axis_segment_time_us[2].y = axis_segment_time_us[1].y; const int32_t least_xy_segment_time = _MIN(_MAX(xs0, xs1, xs2), _MAX(ys0, ys1, ys2));
ys1 = axis_segment_time_us[1].y = axis_segment_time_us[0].y; if (least_xy_segment_time < xy_freq_min_interval_us) {
ys0 = 0; 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);
}
}
} }
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 #endif // XY_FREQUENCY_LIMIT
// Correct the speed // Correct the speed
@ -2832,7 +2826,7 @@ void Planner::set_max_jerk(const AxisEnum axis, float targetValue) {
const bool was_enabled = stepper.suspend(); const bool was_enabled = stepper.suspend();
#endif #endif
millis_t bbru = block_buffer_runtime_us; uint32_t bbru = block_buffer_runtime_us;
#ifdef __AVR__ #ifdef __AVR__
// Reenable Stepper ISR // 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. // Doesn't matter because block_buffer_runtime_us is already too small an estimation.
bbru >>= 10; bbru >>= 10;
// limit to about a minute. // limit to about a minute.
NOMORE(bbru, 0xFFFFul); NOMORE(bbru, 0x0000FFFFUL);
return bbru; return bbru;
} }

View file

@ -352,6 +352,23 @@ class Planner {
#if ENABLED(SD_ABORT_ON_ENDSTOP_HIT) #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT)
static bool abort_on_endstop_hit; static bool abort_on_endstop_hit;
#endif #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: private:
@ -375,19 +392,8 @@ class Planner {
#endif #endif
#if ENABLED(DISABLE_INACTIVE_EXTRUDER) #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]; 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 #endif
#if HAS_SPI_LCD #if HAS_SPI_LCD