mirror of
https://github.com/MarlinFirmware/Marlin.git
synced 2026-01-06 06:37:43 -07:00
✨ SMOOTH_LIN_ADVANCE (#27710)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
parent
b97b09413f
commit
f6c8915545
16 changed files with 504 additions and 106 deletions
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue