SMOOTH_LIN_ADVANCE (#27710)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
David Buezas 2025-04-20 07:18:45 +02:00 committed by GitHub
parent b97b09413f
commit f6c8915545
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 504 additions and 106 deletions

View file

@ -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
/**

View file

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

View file

@ -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
}

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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)");

View file

@ -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

View file

@ -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
//

View file

@ -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;

View file

@ -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.

View file

@ -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
//

View file

@ -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);

View file

@ -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
// α=1exp(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

View file

@ -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"

View file

@ -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"