️ Optimize Smooth Linear Advance (via fixed-point) (#27818)

This commit is contained in:
David Buezas 2025-05-13 23:14:04 +02:00 committed by GitHub
parent 4de6d655ac
commit 12fdde24d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 265 additions and 201 deletions

View file

@ -2366,11 +2366,11 @@
* Higher k and higher XY acceleration may require larger ADVANCE_TAU to avoid skipping steps. * Higher k and higher XY acceleration may require larger ADVANCE_TAU to avoid skipping steps.
*/ */
#if ENABLED(DISTINCT_E_FACTORS) #if ENABLED(DISTINCT_E_FACTORS)
#define ADVANCE_TAU { 0.01 } // (s) Smoothing time to reduce extruder acceleration, per extruder #define ADVANCE_TAU { 0.02 } // (s) Smoothing time to reduce extruder acceleration, per extruder
#else #else
#define ADVANCE_TAU 0.01 // (s) Smoothing time to reduce extruder acceleration #define ADVANCE_TAU 0.02 // (s) Smoothing time to reduce extruder acceleration
#endif #endif
#define SMOOTH_LIN_ADV_HZ 5000 // (Hz) How often to update extruder speed #define SMOOTH_LIN_ADV_HZ 1000 // (Hz) How often to update extruder speed
#define INPUT_SHAPING_E_SYNC // Synchronize the extruder-shaped XY axes (to increase precision) #define INPUT_SHAPING_E_SYNC // Synchronize the extruder-shaped XY axes (to increase precision)
#endif #endif
#endif #endif

View file

@ -29,7 +29,7 @@
#include "../../../module/stepper.h" #include "../../../module/stepper.h"
#if ENABLED(ADVANCE_K_EXTRA) #if ENABLED(ADVANCE_K_EXTRA)
float other_extruder_advance_K[DISTINCT_E]; float other_extruder_advance_K[EXTRUDERS];
uint8_t lin_adv_slot = 0; uint8_t lin_adv_slot = 0;
#endif #endif
@ -62,17 +62,17 @@ void GcodeSuite::M900() {
} }
#endif #endif
float &kref = planner.extruder_advance_K[E_INDEX_N(tool_index)], newK = kref; const float oldK = planner.get_advance_k(tool_index);
const float oldK = newK; float newK = oldK;
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
const float oldU = stepper.get_advance_tau(E_INDEX_N(tool_index)); const float oldU = stepper.get_advance_tau(tool_index);
float newU = oldU; float newU = oldU;
#endif #endif
#if ENABLED(ADVANCE_K_EXTRA) #if ENABLED(ADVANCE_K_EXTRA)
float &lref = other_extruder_advance_K[E_INDEX_N(tool_index)]; float &lref = other_extruder_advance_K[tool_index];
const bool old_slot = TEST(lin_adv_slot, tool_index), // Each tool uses 1 bit to store its current slot (0 or 1) const bool old_slot = TEST(lin_adv_slot, tool_index), // Each tool uses 1 bit to store its current slot (0 or 1)
new_slot = parser.boolval('S', old_slot); // The new slot (0 or 1) to set for the tool (default = no change) new_slot = parser.boolval('S', old_slot); // The new slot (0 or 1) to set for the tool (default = no change)
@ -125,9 +125,9 @@ void GcodeSuite::M900() {
if (newK != oldK || TERN0(SMOOTH_LIN_ADVANCE, newU != oldU)) { if (newK != oldK || TERN0(SMOOTH_LIN_ADVANCE, newU != oldU)) {
planner.synchronize(); planner.synchronize();
if (newK != oldK) kref = newK; if (newK != oldK) planner.set_advance_k(newK, tool_index);
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
if (newU != oldU) stepper.set_advance_tau(newU); if (newU != oldU) stepper.set_advance_tau(newU, tool_index);
#endif #endif
} }
@ -136,11 +136,11 @@ void GcodeSuite::M900() {
#if ENABLED(ADVANCE_K_EXTRA) #if ENABLED(ADVANCE_K_EXTRA)
#if DISABLED(DISTINCT_E_FACTORS) #if DISABLED(DISTINCT_E_FACTORS)
SERIAL_ECHOLNPGM("Advance S", new_slot, " K", kref, "(S", !new_slot, " K", lref, ")"); SERIAL_ECHOLNPGM("Advance S", new_slot, " K", newK, "(S", !new_slot, " K", lref, ")");
#else #else
EXTRUDER_LOOP() { EXTRUDER_LOOP() {
const bool slot = TEST(lin_adv_slot, e); const bool slot = TEST(lin_adv_slot, e);
SERIAL_ECHOLNPGM("Advance T", e, " S", slot, " K", planner.extruder_advance_K[e], SERIAL_ECHOLNPGM("Advance T", e, " S", slot, " K", planner.get_advance_k(e),
"(S", !slot, " K", other_extruder_advance_K[e], ")"); "(S", !slot, " K", other_extruder_advance_K[e], ")");
} }
#endif #endif
@ -149,14 +149,14 @@ void GcodeSuite::M900() {
SERIAL_ECHO_START(); SERIAL_ECHO_START();
#if DISABLED(DISTINCT_E_FACTORS) #if DISABLED(DISTINCT_E_FACTORS)
SERIAL_ECHOPGM("Advance K=", planner.extruder_advance_K[0]); SERIAL_ECHOPGM("Advance K=", planner.get_advance_k());
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
SERIAL_ECHOPGM(" TAU=", stepper.get_advance_tau()); SERIAL_ECHOPGM(" TAU=", stepper.get_advance_tau());
#endif #endif
SERIAL_EOL(); SERIAL_EOL();
#else #else
SERIAL_ECHOPGM("Advance K"); SERIAL_ECHOPGM("Advance K");
EXTRUDER_LOOP() SERIAL_ECHO(C(' '), C('0' + e), C(':'), planner.extruder_advance_K[e]); EXTRUDER_LOOP() SERIAL_ECHO(C(' '), C('0' + e), C(':'), planner.get_advance_k(e));
SERIAL_EOL(); SERIAL_EOL();
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
SERIAL_ECHOPGM("Advance TAU"); SERIAL_ECHOPGM("Advance TAU");
@ -174,23 +174,21 @@ void GcodeSuite::M900_report(const bool forReplay/*=true*/) {
TERN_(MARLIN_SMALL_BUILD, return); TERN_(MARLIN_SMALL_BUILD, return);
report_heading(forReplay, F(STR_LINEAR_ADVANCE)); report_heading(forReplay, F(STR_LINEAR_ADVANCE));
#if DISABLED(DISTINCT_E_FACTORS) DISTINCT_E_LOOP() {
report_echo_start(forReplay); report_echo_start(forReplay);
SERIAL_ECHOPGM(" M900 K", planner.extruder_advance_K[0]); SERIAL_ECHOPGM(
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(DISTINCT_E_FACTORS)
SERIAL_ECHOPGM(" M900 U", stepper.get_advance_tau()); " M900 T", e, " K"
#endif
SERIAL_EOL();
#else #else
EXTRUDER_LOOP() { " M900 K"
report_echo_start(forReplay); #endif
SERIAL_ECHOPGM(" M900 T", e, " K", planner.extruder_advance_K[e]); );
SERIAL_ECHO(planner.get_advance_k(e));
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
SERIAL_ECHOPGM(" U", stepper.get_advance_tau(e)); SERIAL_ECHOPGM(" U", stepper.get_advance_tau(e));
#endif #endif
SERIAL_EOL(); SERIAL_EOL();
} }
#endif
} }
#endif // LIN_ADVANCE #endif // LIN_ADVANCE

View file

@ -3725,7 +3725,7 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
* Check per-axis initializers for errors * Check per-axis initializers for errors
*/ */
#define __PLUS_TEST(I,A) && (sanity_arr_##A[_MIN(I,signed(COUNT(sanity_arr_##A)-1))] > 0) #define __PLUS_TEST(I,A) && (sanity_arr_##A[ALIM(I,sanity_arr_##A)] > 0)
#define _PLUS_TEST(A) (1 REPEAT2(14,__PLUS_TEST,A)) #define _PLUS_TEST(A) (1 REPEAT2(14,__PLUS_TEST,A))
#if HAS_MULTI_EXTRUDER #if HAS_MULTI_EXTRUDER
#define _EXTRA_NOTE " (Did you forget to enable DISTINCT_E_FACTORS?)" #define _EXTRA_NOTE " (Did you forget to enable DISTINCT_E_FACTORS?)"

View file

@ -2398,10 +2398,13 @@ void JyersDWIN::menuItemHandler(const uint8_t menu, const uint8_t item, bool dra
case MOTION_LA: case MOTION_LA:
if (draw) { if (draw) {
drawMenuItem(row, ICON_MaxAccelerated, GET_TEXT_F(MSG_ADVANCE_K)); drawMenuItem(row, ICON_MaxAccelerated, GET_TEXT_F(MSG_ADVANCE_K));
drawFloat(planner.extruder_advance_K[0], row, false, 100); drawFloat(planner.get_advance_k(), row, false, 100);
} }
else else {
modifyValue(planner.extruder_advance_K[0], 0, 10, 100); static float k = planner.get_advance_k();
modifyValue(k, 0, 10, 100, []{ planner.set_advance_k(k); });
}
break; break;
#endif #endif
} }
@ -2914,10 +2917,12 @@ void JyersDWIN::menuItemHandler(const uint8_t menu, const uint8_t item, bool dra
case ADVANCED_LA: case ADVANCED_LA:
if (draw) { if (draw) {
drawMenuItem(row, ICON_MaxAccelerated, GET_TEXT_F(MSG_ADVANCE_K)); drawMenuItem(row, ICON_MaxAccelerated, GET_TEXT_F(MSG_ADVANCE_K));
drawFloat(planner.extruder_advance_K[0], row, false, 100); drawFloat(planner.get_advance_k(), row, false, 100);
}
else {
static float k = planner.get_advance_k();
modifyValue(k, 0, 10, 100, []{ planner.set_advance_k(k); });
} }
else
modifyValue(planner.extruder_advance_K[0], 0, 10, 100);
break; break;
#endif #endif
@ -3925,10 +3930,12 @@ void JyersDWIN::menuItemHandler(const uint8_t menu, const uint8_t item, bool dra
case TUNE_LA: case TUNE_LA:
if (draw) { if (draw) {
drawMenuItem(row, ICON_MaxAccelerated, GET_TEXT_F(MSG_ADVANCE_K)); drawMenuItem(row, ICON_MaxAccelerated, GET_TEXT_F(MSG_ADVANCE_K));
drawFloat(planner.extruder_advance_K[0], row, false, 100); drawFloat(planner.get_advance_k(), row, false, 100);
}
else {
static float k = planner.get_advance_k();
modifyValue(k, 0, 10, 100, []{ planner.set_advance_k(k); });
} }
else
modifyValue(planner.extruder_advance_K[0], 0, 10, 100);
break; break;
#endif #endif

View file

@ -2686,7 +2686,8 @@ void applyMaxAccel() { planner.set_max_acceleration(hmiValue.axis, menuData.valu
#endif #endif
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
void setLA_K() { setPFloatOnClick(0, 10, 3); } void applyLA_K() { planner.set_advance_k(menuData.value / MINUNITMULT); }
void setLA_K() { setPFloatOnClick(0, 10, 3, applyLA_K); }
#endif #endif
#if HAS_X_AXIS #if HAS_X_AXIS
@ -3545,7 +3546,7 @@ void drawTuneMenu() {
EDIT_ITEM(ICON_JDmm, MSG_JUNCTION_DEVIATION, onDrawPFloat3Menu, setJDmm, &planner.junction_deviation_mm); EDIT_ITEM(ICON_JDmm, MSG_JUNCTION_DEVIATION, onDrawPFloat3Menu, setJDmm, &planner.junction_deviation_mm);
#endif #endif
#if ENABLED(PROUI_ITEM_ADVK) #if ENABLED(PROUI_ITEM_ADVK)
EDIT_ITEM(ICON_MaxAccelerated, MSG_ADVANCE_K, onDrawPFloat3Menu, setLA_K, &planner.extruder_advance_K[0]); EDIT_ITEM(ICON_MaxAccelerated, MSG_ADVANCE_K, onDrawPFloat3Menu, setLA_K, &planner.get_advance_k());
#endif #endif
#if HAS_LOCKSCREEN #if HAS_LOCKSCREEN
MENU_ITEM(ICON_Lock, MSG_LOCKSCREEN, onDrawMenuItem, dwinLockScreen); MENU_ITEM(ICON_Lock, MSG_LOCKSCREEN, onDrawMenuItem, dwinLockScreen);
@ -3683,7 +3684,7 @@ void drawMotionMenu() {
MENU_ITEM(ICON_Homing, MSG_HOMING_FEEDRATE, onDrawSubMenu, drawHomingFRMenu); MENU_ITEM(ICON_Homing, MSG_HOMING_FEEDRATE, onDrawSubMenu, drawHomingFRMenu);
#endif #endif
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
EDIT_ITEM(ICON_MaxAccelerated, MSG_ADVANCE_K, onDrawPFloat3Menu, setLA_K, &planner.extruder_advance_K[0]); EDIT_ITEM(ICON_MaxAccelerated, MSG_ADVANCE_K, onDrawPFloat3Menu, setLA_K, &planner.get_advance_k());
#endif #endif
#if ENABLED(SHAPING_MENU) #if ENABLED(SHAPING_MENU)
MENU_ITEM(ICON_InputShaping, MSG_INPUT_SHAPING, onDrawSubMenu, drawInputShaping_menu); MENU_ITEM(ICON_InputShaping, MSG_INPUT_SHAPING, onDrawSubMenu, drawInputShaping_menu);

View file

@ -643,12 +643,12 @@ namespace ExtUI {
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
float getLinearAdvance_mm_mm_s(const extruder_t extruder) { float getLinearAdvance_mm_mm_s(const extruder_t extruder) {
return (extruder < EXTRUDERS) ? planner.extruder_advance_K[E_INDEX_N(extruder - E0)] : 0; return (extruder < EXTRUDERS) ? planner.get_advance_k(E_INDEX_N(extruder - E0)) : 0;
} }
void setLinearAdvance_mm_mm_s(const_float_t value, const extruder_t extruder) { void setLinearAdvance_mm_mm_s(const_float_t value, const extruder_t extruder) {
if (extruder < EXTRUDERS) if (extruder < EXTRUDERS)
planner.extruder_advance_K[E_INDEX_N(extruder - E0)] = constrain(value, 0, 10); planner.set_advance_k(constrain(value, 0, 10), E_INDEX_N(extruder - E0));
} }
#endif #endif

View file

@ -117,10 +117,13 @@ void menu_backlash();
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
#if DISABLED(DISTINCT_E_FACTORS) #if DISABLED(DISTINCT_E_FACTORS)
EDIT_ITEM(float42_52, MSG_ADVANCE_K, &planner.extruder_advance_K[0], 0, 10); editable.decimal = planner.get_advance_k();
EDIT_ITEM(float42_52, MSG_ADVANCE_K, &editable.decimal, 0.0f, 10.0f, []{ planner.set_advance_k(editable.decimal); });
#else #else
EXTRUDER_LOOP() EXTRUDER_LOOP() {
EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10); editable.decimal = planner.get_advance_k(e);
EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &editable.decimal, 0.0f, 10.0f, []{ planner.set_advance_k(editable.decimal, MenuItemBase::itemIndex); });
}
#endif #endif
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
#if DISABLED(DISTINCT_E_FACTORS) #if DISABLED(DISTINCT_E_FACTORS)
@ -745,15 +748,19 @@ void menu_advanced_settings() {
#endif #endif
#if HAS_ADV_FILAMENT_MENU #if HAS_ADV_FILAMENT_MENU
SUBMENU(MSG_FILAMENT, menu_advanced_filament);
#endif
#if ENABLED(LIN_ADVANCE) && DISABLED(HAS_ADV_FILAMENT_MENU) SUBMENU(MSG_FILAMENT, menu_advanced_filament);
#elif ENABLED(LIN_ADVANCE)
#if DISABLED(DISTINCT_E_FACTORS) #if DISABLED(DISTINCT_E_FACTORS)
EDIT_ITEM(float42_52, MSG_ADVANCE_K, &planner.extruder_advance_K[0], 0, 10); editable.decimal = planner.get_advance_k();
EDIT_ITEM(float42_52, MSG_ADVANCE_K, &editable.decimal, 0.0f, 10.0f, []{ planner.set_advance_k(editable.decimal); });
#else #else
EXTRUDER_LOOP() EXTRUDER_LOOP() {
EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10); editable.decimal = planner.get_advance_k(e);
EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &editable.decimal, 0.0f, 10.0f, []{ planner.set_advance_k(editable.decimal, MenuItemBase::itemIndex); });
}
#endif #endif
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
#if DISABLED(DISTINCT_E_FACTORS) #if DISABLED(DISTINCT_E_FACTORS)
@ -766,7 +773,8 @@ void menu_advanced_settings() {
} }
#endif #endif
#endif #endif
#endif
#endif // LIN_ADVANCE && !HAS_ADV_FILAMENT_MENU
// M540 S - Abort on endstop hit when SD printing // M540 S - Abort on endstop hit when SD printing
#if ENABLED(SD_ABORT_ON_ENDSTOP_HIT) #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT)

View file

@ -216,10 +216,13 @@ void menu_tune() {
// //
#if ENABLED(LIN_ADVANCE) && DISABLED(SLIM_LCD_MENUS) #if ENABLED(LIN_ADVANCE) && DISABLED(SLIM_LCD_MENUS)
#if DISABLED(DISTINCT_E_FACTORS) #if DISABLED(DISTINCT_E_FACTORS)
EDIT_ITEM(float42_52, MSG_ADVANCE_K, &planner.extruder_advance_K[0], 0, 10); editable.decimal = planner.get_advance_k();
EDIT_ITEM(float42_52, MSG_ADVANCE_K, &editable.decimal, 0.0f, 10.0f, []{ planner.set_advance_k(editable.decimal); });
#else #else
EXTRUDER_LOOP() EXTRUDER_LOOP() {
EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10); editable.decimal = planner.get_advance_k(e);
EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &editable.decimal, 0.0f, 10.0f, []{ planner.set_advance_k(editable.decimal, MenuItemBase::itemIndex); });
}
#endif #endif
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
#if DISABLED(DISTINCT_E_FACTORS) #if DISABLED(DISTINCT_E_FACTORS)

View file

@ -894,7 +894,7 @@ void RTS::handleData() {
break; break;
case 4: // Go to Advanced Settings case 4: // Go to Advanced Settings
TERN_(LIN_ADVANCE, sendData(planner.extruder_advance_K[0] * 100, Advance_K_VP)); TERN_(LIN_ADVANCE, sendData(planner.get_advance_k() * 100, Advance_K_VP));
gotoPage(ID_AdvWarn_L, ID_AdvWarn_D); gotoPage(ID_AdvWarn_L, ID_AdvWarn_D);
break; break;
@ -1292,7 +1292,7 @@ void RTS::handleData() {
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
case 7: // Confirm case 7: // Confirm
sendData(planner.extruder_advance_K[0] * 100, Advance_K_VP); sendData(planner.get_advance_k() * 100, Advance_K_VP);
gotoPage(ID_Advanced_L, ID_Advanced_D); gotoPage(ID_Advanced_L, ID_Advanced_D);
break; break;
#endif #endif
@ -1350,7 +1350,7 @@ void RTS::handleData() {
#endif #endif
case A_Retract: planner.settings.retract_acceleration = recdat.data[0]; break; case A_Retract: planner.settings.retract_acceleration = recdat.data[0]; break;
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
case Advance_K: planner.extruder_advance_K[0] = float(recdat.data[0]) / 100.0f; break; case Advance_K: planner.set_advance_k(float(recdat.data[0]) / 100.0f); break;
#endif #endif
#endif #endif
case Accel: planner.settings.acceleration = recdat.data[0]; break; case Accel: planner.settings.acceleration = recdat.data[0]; break;

View file

@ -181,44 +181,51 @@ void Touch::touch(touch_control_t *control) {
case SLIDER: hold(control); ui.encoderPosition = (x - control->x) * control->data / control->width; break; case SLIDER: hold(control); ui.encoderPosition = (x - control->x) * control->data / control->width; break;
case INCREASE: hold(control, repeat_delay - 5); TERN(AUTO_BED_LEVELING_UBL, ui.external_control ? bedlevel.encoder_diff++ : ui.encoderPosition++, ui.encoderPosition++); break; case INCREASE: hold(control, repeat_delay - 5); TERN(AUTO_BED_LEVELING_UBL, ui.external_control ? bedlevel.encoder_diff++ : ui.encoderPosition++, ui.encoderPosition++); break;
case DECREASE: hold(control, repeat_delay - 5); TERN(AUTO_BED_LEVELING_UBL, ui.external_control ? bedlevel.encoder_diff-- : ui.encoderPosition--, ui.encoderPosition--); break; case DECREASE: hold(control, repeat_delay - 5); TERN(AUTO_BED_LEVELING_UBL, ui.external_control ? bedlevel.encoder_diff-- : ui.encoderPosition--, ui.encoderPosition--); break;
case HEATER: case HEATER: {
int8_t heater;
heater = control->data;
ui.clear_for_drawing(); ui.clear_for_drawing();
const int8_t heater = control->data;
switch (heater) {
default: // Hotend
#if HAS_HOTEND #if HAS_HOTEND
if (heater >= 0) { // HotEnd #define HOTEND_HEATER(N) TERN0(HAS_MULTI_HOTEND, N)
#if HOTENDS == 1 TERN_(HAS_MULTI_HOTEND, MenuItemBase::itemIndex = heater);
MenuItem_int3::action(GET_TEXT_F(MSG_NOZZLE), &thermalManager.temp_hotend[0].target, 0, thermalManager.hotend_max_target(0), []{ thermalManager.start_watching_hotend(0); }); MenuItem_int3::action(GET_TEXT_F(TERN(HAS_MULTI_HOTEND, MSG_NOZZLE_N, MSG_NOZZLE)),
#else &thermalManager.temp_hotend[HOTEND_HEATER(heater)].target, 0, thermalManager.hotend_max_target(HOTEND_HEATER(heater)),
MenuItemBase::itemIndex = heater; []{ thermalManager.start_watching_hotend(HOTEND_HEATER(MenuItemBase::itemIndex)); }
MenuItem_int3::action(GET_TEXT_F(MSG_NOZZLE_N), &thermalManager.temp_hotend[heater].target, 0, thermalManager.hotend_max_target(heater), []{ thermalManager.start_watching_hotend(MenuItemBase::itemIndex); }); );
#endif
}
#endif #endif
break;
#if HAS_HEATED_BED #if HAS_HEATED_BED
else if (heater == H_BED) { case H_BED:
MenuItem_int3::action(GET_TEXT_F(MSG_BED), &thermalManager.temp_bed.target, 0, BED_MAX_TARGET, thermalManager.start_watching_bed); MenuItem_int3::action(GET_TEXT_F(MSG_BED), &thermalManager.temp_bed.target, 0, BED_MAX_TARGET, thermalManager.start_watching_bed);
} break;
#endif
#if HAS_HEATED_CHAMBER
else if (heater == H_CHAMBER) {
MenuItem_int3::action(GET_TEXT_F(MSG_CHAMBER), &thermalManager.temp_chamber.target, 0, CHAMBER_MAX_TARGET, thermalManager.start_watching_chamber);
}
#endif
#if HAS_COOLER
else if (heater == H_COOLER) {
MenuItem_int3::action(GET_TEXT_F(MSG_COOLER), &thermalManager.temp_cooler.target, 0, COOLER_MAX_TARGET, thermalManager.start_watching_cooler);
}
#endif #endif
#if HAS_HEATED_CHAMBER
case H_CHAMBER:
MenuItem_int3::action(GET_TEXT_F(MSG_CHAMBER), &thermalManager.temp_chamber.target, 0, CHAMBER_MAX_TARGET, thermalManager.start_watching_chamber);
break; break;
case FAN: #endif
#if HAS_COOLER
case H_COOLER:
MenuItem_int3::action(GET_TEXT_F(MSG_COOLER), &thermalManager.temp_cooler.target, 0, COOLER_MAX_TARGET, thermalManager.start_watching_cooler);
break;
#endif
} // switch
} break;
case FAN: {
ui.clear_for_drawing(); ui.clear_for_drawing();
static uint8_t fan, fan_speed; static uint8_t fan, fan_speed;
fan = 0; fan = 0;
fan_speed = thermalManager.fan_speed[fan]; fan_speed = thermalManager.fan_speed[fan];
MenuItem_percent::action(GET_TEXT_F(MSG_FIRST_FAN_SPEED), &fan_speed, 0, 255, []{ thermalManager.set_fan_speed(fan, fan_speed); TERN_(LASER_SYNCHRONOUS_M106_M107, planner.buffer_sync_block(BLOCK_BIT_SYNC_FANS));}); MenuItem_percent::action(GET_TEXT_F(MSG_FIRST_FAN_SPEED), &fan_speed, 0, 255, []{ thermalManager.set_fan_speed(fan, fan_speed); TERN_(LASER_SYNCHRONOUS_M106_M107, planner.buffer_sync_block(BLOCK_BIT_SYNC_FANS));});
break; } break;
case FEEDRATE: case FEEDRATE:
ui.clear_for_drawing(); ui.clear_for_drawing();
MenuItem_int3::action(GET_TEXT_F(MSG_SPEED), &feedrate_percentage, SPEED_EDIT_MIN, SPEED_EDIT_MAX); MenuItem_int3::action(GET_TEXT_F(MSG_SPEED), &feedrate_percentage, SPEED_EDIT_MIN, SPEED_EDIT_MAX);
@ -228,11 +235,10 @@ void Touch::touch(touch_control_t *control) {
case FLOWRATE: case FLOWRATE:
ui.clear_for_drawing(); ui.clear_for_drawing();
MenuItemBase::itemIndex = control->data; MenuItemBase::itemIndex = control->data;
#if EXTRUDERS == 1 MenuItem_int3::action(GET_TEXT_F(TERN(HAS_MULTI_EXTRUDER, MSG_FLOW_N, MSG_FLOW)),
MenuItem_int3::action(GET_TEXT_F(MSG_FLOW), &planner.flow_percentage[MenuItemBase::itemIndex], FLOW_EDIT_MIN, FLOW_EDIT_MAX, []{ planner.refresh_e_factor(MenuItemBase::itemIndex); }); &planner.flow_percentage[MenuItemBase::itemIndex], FLOW_EDIT_MIN, FLOW_EDIT_MAX,
#else []{ planner.refresh_e_factor(MenuItemBase::itemIndex); }
MenuItem_int3::action(GET_TEXT_F(MSG_FLOW_N), &planner.flow_percentage[MenuItemBase::itemIndex], FLOW_EDIT_MIN, FLOW_EDIT_MAX, []{ planner.refresh_e_factor(MenuItemBase::itemIndex); }); );
#endif
break; break;
#endif #endif

View file

@ -236,6 +236,9 @@ float Planner::previous_nominal_speed;
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
float Planner::extruder_advance_K[DISTINCT_E]; // Initialized by settings.load float Planner::extruder_advance_K[DISTINCT_E]; // Initialized by settings.load
#if ENABLED(SMOOTH_LIN_ADVANCE)
uint32_t Planner::extruder_advance_K_q27[DISTINCT_E];
#endif
#endif #endif
#if HAS_POSITION_FLOAT #if HAS_POSITION_FLOAT
@ -2457,7 +2460,7 @@ bool Planner::_populate_block(
block->acceleration_steps_per_s2 = accel; block->acceleration_steps_per_s2 = accel;
block->acceleration = accel / steps_per_mm; block->acceleration = accel / steps_per_mm;
#if DISABLED(S_CURVE_ACCELERATION) #if DISABLED(S_CURVE_ACCELERATION)
block->acceleration_rate = uint32_t(accel * (float(1UL << 24) / (STEPPER_TIMER_RATE))); block->acceleration_rate = uint32_t(accel * (float(_BV32(24)) / (STEPPER_TIMER_RATE)));
#endif #endif
#if HAS_ROUGH_LIN_ADVANCE #if HAS_ROUGH_LIN_ADVANCE
@ -2478,7 +2481,13 @@ bool Planner::_populate_block(
} }
#elif ENABLED(SMOOTH_LIN_ADVANCE) #elif ENABLED(SMOOTH_LIN_ADVANCE)
block->use_advance_lead = use_advance_lead; 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; const uint32_t ratio = (uint64_t(block->steps.e) * _BV32(30)) / block->step_event_count;
block->e_step_ratio_q30 = block->direction_bits.e ? ratio : -ratio;
#if ENABLED(INPUT_SHAPING_E_SYNC)
const uint32_t xy_steps = TERN0(INPUT_SHAPING_X, block->steps.x) + TERN0(INPUT_SHAPING_Y, block->steps.y);
block->xy_length_inv_q30 = xy_steps ? (_BV32(30) / xy_steps) : 0;
#endif
#endif #endif
// Formula for the average speed over a 1 step worth of distance if starting from zero and // Formula for the average speed over a 1 step worth of distance if starting from zero and

View file

@ -247,7 +247,10 @@ typedef struct PlannerBlock {
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
uint32_t cruise_time; // Cruise time in STEP timer counts uint32_t cruise_time; // Cruise time in STEP timer counts
float e_step_ratio; int32_t e_step_ratio_q30; // Ratio of e steps to block steps.
#if ENABLED(INPUT_SHAPING_E_SYNC)
uint32_t xy_length_inv_q30; // inverse of block->steps.x + block.steps.y
#endif
#endif #endif
#if ANY(S_CURVE_ACCELERATION, SMOOTH_LIN_ADVANCE) #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 uint32_t cruise_rate, // The actual cruise rate to use, between end of the acceleration phase and start of deceleration phase
@ -359,9 +362,8 @@ typedef struct PlannerSettings {
#if ENABLED(EDITABLE_STEPS_PER_UNIT) #if ENABLED(EDITABLE_STEPS_PER_UNIT)
float axis_steps_per_mm[DISTINCT_AXES]; float axis_steps_per_mm[DISTINCT_AXES];
#else #else
#define _DLIM(I) ALIM(I, _dasu) #define _DASU(N) _dasu[ALIM(N, _dasu)],
#define _DASU(N) _dasu[_DLIM(N)], #define _EASU(N) _dasu[ALIM(E_AXIS + N, _dasu)],
#define _EASU(N) _dasu[_DLIM(E_AXIS + N)],
static constexpr float axis_steps_per_mm[DISTINCT_AXES] = { static constexpr float axis_steps_per_mm[DISTINCT_AXES] = {
REPEAT(NUM_AXES, _DASU) REPEAT(NUM_AXES, _DASU)
TERN_(HAS_EXTRUDERS, REPEAT(DISTINCT_E, _EASU)) TERN_(HAS_EXTRUDERS, REPEAT(DISTINCT_E, _EASU))
@ -526,6 +528,23 @@ class Planner {
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
static float extruder_advance_K[DISTINCT_E]; static float extruder_advance_K[DISTINCT_E];
static void set_advance_k(const_float_t k, const uint8_t e=active_extruder) {
UNUSED(e);
extruder_advance_K[E_INDEX_N(e)] = k;
#if ENABLED(SMOOTH_LIN_ADVANCE)
extruder_advance_K_q27[E_INDEX_N(e)] = k * (1UL << 27);
#endif
}
static float get_advance_k(const uint8_t e=active_extruder) {
UNUSED(e);
return extruder_advance_K[E_INDEX_N(e)];
}
#if ENABLED(SMOOTH_LIN_ADVANCE)
static uint32_t get_advance_k_q27(const uint8_t e=active_extruder) {
UNUSED(e);
return extruder_advance_K_q27[E_INDEX_N(e)];
}
#endif
#endif #endif
/** /**
@ -602,6 +621,10 @@ class Planner {
volatile static uint32_t block_buffer_runtime_us; // Theoretical block buffer runtime in µs volatile static uint32_t block_buffer_runtime_us; // Theoretical block buffer runtime in µs
#endif #endif
#if ENABLED(SMOOTH_LIN_ADVANCE)
static uint32_t extruder_advance_K_q27[DISTINCT_E];
#endif
public: public:
/** /**

View file

@ -126,7 +126,7 @@
#endif #endif
#if ENABLED(ADVANCE_K_EXTRA) #if ENABLED(ADVANCE_K_EXTRA)
extern float other_extruder_advance_K[DISTINCT_E]; extern float other_extruder_advance_K[EXTRUDERS];
#endif #endif
#if HAS_MULTI_EXTRUDER #if HAS_MULTI_EXTRUDER
@ -2641,18 +2641,14 @@ void MarlinSettings::postprocess() {
_FIELD_TEST(planner_extruder_advance_K); _FIELD_TEST(planner_extruder_advance_K);
EEPROM_READ(extruder_advance_K); EEPROM_READ(extruder_advance_K);
if (!validating) if (!validating)
COPY(planner.extruder_advance_K, extruder_advance_K); DISTINCT_E_LOOP() planner.set_advance_k(extruder_advance_K[e], e);
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
_FIELD_TEST(stepper_extruder_advance_tau); _FIELD_TEST(stepper_extruder_advance_tau);
float tau[DISTINCT_E]; float tau[DISTINCT_E];
EEPROM_READ(tau); EEPROM_READ(tau);
if (!validating) { if (!validating)
#if ENABLED(DISTINCT_E_FACTORS) DISTINCT_E_LOOP() stepper.set_advance_tau(tau[e], e);
EXTRUDER_LOOP() stepper.set_advance_tau(tau[e], e);
#else
stepper.set_advance_tau(tau[0]);
#endif
}
#endif #endif
} }
#endif #endif
@ -3615,21 +3611,23 @@ void MarlinSettings::reset() {
#if ENABLED(DISTINCT_E_FACTORS) #if ENABLED(DISTINCT_E_FACTORS)
constexpr float linAdvanceK[] = ADVANCE_K; constexpr float linAdvanceK[] = ADVANCE_K;
EXTRUDER_LOOP() {
const float a = linAdvanceK[ALIM(e, linAdvanceK)];
planner.extruder_advance_K[e] = a;
TERN_(ADVANCE_K_EXTRA, other_extruder_advance_K[e] = a);
}
#else
planner.extruder_advance_K[0] = ADVANCE_K;
#endif
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
#if ENABLED(DISTINCT_E_FACTORS)
constexpr float linAdvanceTau[] = ADVANCE_TAU; constexpr float linAdvanceTau[] = ADVANCE_TAU;
EXTRUDER_LOOP() #endif
stepper.set_advance_tau(linAdvanceTau[ALIM(e, linAdvanceTau)], e);
#else EXTRUDER_LOOP() {
stepper.set_advance_tau(ADVANCE_TAU); const float k = linAdvanceK[ALIM(e, linAdvanceK)];
planner.set_advance_k(k, e);
TERN_(SMOOTH_LIN_ADVANCE, stepper.set_advance_tau(linAdvanceTau[ALIM(e, linAdvanceTau)], e));
TERN_(ADVANCE_K_EXTRA, other_extruder_advance_K[e] = k);
}
#else // !DISTINCT_E_FACTORS
planner.set_advance_k(ADVANCE_K);
TERN_(SMOOTH_LIN_ADVANCE, stepper.set_advance_tau(ADVANCE_TAU));
#if ENABLED(ADVANCE_K_EXTRA)
EXTRUDER_LOOP() other_extruder_advance_K[e] = ADVANCE_K;
#endif #endif
#endif #endif

View file

@ -1510,6 +1510,10 @@ HAL_STEP_TIMER_ISR() {
#define STEP_MULTIPLY(A,B) MultiU24X32toH16(A, B) #define STEP_MULTIPLY(A,B) MultiU24X32toH16(A, B)
#endif #endif
#if ENABLED(SMOOTH_LIN_ADVANCE)
FORCE_INLINE static constexpr int32_t MULT_Q(uint8_t q, int32_t x, int32_t y) { return (int64_t(x) * y) >> q; }
#endif
void Stepper::isr() { void Stepper::isr() {
static hal_timer_t nextMainISR = 0; // Interval until the next main Stepper Pulse phase (0 = Now) static hal_timer_t nextMainISR = 0; // Interval until the next main Stepper Pulse phase (0 = Now)
@ -2877,9 +2881,9 @@ hal_timer_t Stepper::block_phase_isr() {
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
float Stepper::extruder_advance_tau[DISTINCT_E], float Stepper::extruder_advance_tau[DISTINCT_E];
Stepper::extruder_advance_tau_ticks[DISTINCT_E], uint32_t Stepper::extruder_advance_tau_ticks[DISTINCT_E],
Stepper::extruder_advance_alpha[DISTINCT_E]; Stepper::extruder_advance_alpha_q30[DISTINCT_E];
void Stepper::set_la_interval(const int32_t rate) { void Stepper::set_la_interval(const int32_t rate) {
if (rate == 0) { if (rate == 0) {
@ -2904,116 +2908,118 @@ hal_timer_t Stepper::block_phase_isr() {
constexpr uint16_t IS_COMPENSATION_BUFFER_SIZE = uint16_t(float(SMOOTH_LIN_ADV_HZ) / float(SHAPING_MIN_FREQ) / 2.0f + 0.5f); constexpr uint16_t IS_COMPENSATION_BUFFER_SIZE = uint16_t(float(SMOOTH_LIN_ADV_HZ) / float(SHAPING_MIN_FREQ) / 2.0f + 0.5f);
typedef struct { typedef struct {
xy_float_t buffer[IS_COMPENSATION_BUFFER_SIZE]; xy_long_t buffer[IS_COMPENSATION_BUFFER_SIZE];
uint16_t index; uint16_t index;
FORCE_INLINE void add(const xy_long_t &input) {
buffer[index] = input;
if (++index == IS_COMPENSATION_BUFFER_SIZE) index = 0;
}
FORCE_INLINE xy_long_t past_item(const uint16_t n) {
const int16_t i = int16_t(index) - n;
return buffer[i >= 0 ? i : i + IS_COMPENSATION_BUFFER_SIZE];
}
} DelayBuffer; } DelayBuffer;
DelayBuffer delayBuffer; DelayBuffer delayBuffer;
void add_to_buffer(xy_float_t input) { xy_long_t smooth_lin_adv_lookback(const shaping_time_t stepper_ticks) {
delayBuffer.buffer[delayBuffer.index++] = input; constexpr uint32_t ADV_TICKS_PER_STEPPER_TICKS_Q30 = (uint64_t(SMOOTH_LIN_ADV_HZ) * _BV32(30)) / STEPPER_TIMER_RATE;
if (delayBuffer.index == IS_COMPENSATION_BUFFER_SIZE) const uint16_t delay_steps = MULT_Q(30, stepper_ticks, ADV_TICKS_PER_STEPPER_TICKS_Q30);
delayBuffer.index = 0; return delayBuffer.past_item(delay_steps);
}
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 #endif // INPUT_SHAPING_E_SYNC
float lookahead(uint32_t t) { int32_t smooth_lin_adv_lookahead(uint32_t stepper_ticks) {
for (uint8_t i = 0; block_t *block = planner.get_future_block(i); i++) { for (uint8_t i = 0; block_t *block = planner.get_future_block(i); i++) {
if (block->is_sync()) continue; if (block->is_sync()) continue;
if (t <= block->acceleration_time) { if (stepper_ticks <= block->acceleration_time) {
if (!block->use_advance_lead) return 0.0f; if (!block->use_advance_lead) return 0;
uint32_t rate = STEP_MULTIPLY(t, block->acceleration_rate) + block->initial_rate; uint32_t rate = STEP_MULTIPLY(stepper_ticks, block->acceleration_rate) + block->initial_rate;
NOMORE(rate, block->nominal_rate); NOMORE(rate, block->nominal_rate);
return rate * block->e_step_ratio; return MULT_Q(30, rate, block->e_step_ratio_q30);
} }
t -= block->acceleration_time; stepper_ticks -= block->acceleration_time;
if (t <= block->cruise_time) { if (stepper_ticks <= block->cruise_time) {
if (!block->use_advance_lead) return 0.0f; if (!block->use_advance_lead) return 0;
return block->cruise_rate * block->e_step_ratio; return MULT_Q(30, block->cruise_rate, block->e_step_ratio_q30);
} }
t -= block->cruise_time; stepper_ticks -= block->cruise_time;
if (t <= block->deceleration_time) { if (stepper_ticks <= block->deceleration_time) {
if (!block->use_advance_lead) return 0.0f; if (!block->use_advance_lead) return 0;
uint32_t rate = STEP_MULTIPLY(t, block->acceleration_rate); uint32_t rate = STEP_MULTIPLY(stepper_ticks, block->acceleration_rate);
if (rate < block->cruise_rate) { if (rate < block->cruise_rate) {
rate = block->cruise_rate - rate; rate = block->cruise_rate - rate;
NOLESS(rate, block->final_rate); NOLESS(rate, block->final_rate);
} }
else else
rate = block->final_rate; rate = block->final_rate;
return rate * block->e_step_ratio; return MULT_Q(30, rate, block->e_step_ratio_q30);
} }
t -= block->deceleration_time; stepper_ticks -= block->deceleration_time;
} }
return 0.0f; return 0;
} }
hal_timer_t Stepper::smooth_lin_adv_isr() { hal_timer_t Stepper::smooth_lin_adv_isr() {
float target_adv_steps = 0; int32_t target_adv_steps = 0;
if (current_block) { if (current_block) {
const uint32_t t = extruder_advance_tau_ticks[0] + curr_timer_tick; const uint32_t stepper_ticks = extruder_advance_tau_ticks[E_INDEX_N(active_extruder)] + curr_timer_tick;
target_adv_steps = lookahead(t) * planner.extruder_advance_K[0]; target_adv_steps = MULT_Q(27, smooth_lin_adv_lookahead(stepper_ticks), planner.get_advance_k_q27());
} }
else { else {
curr_step_rate = 0; curr_step_rate = 0;
} }
static float last_target_adv_steps = 0; static int32_t last_target_adv_steps = 0;
constexpr float dt_inv = SMOOTH_LIN_ADV_HZ; constexpr uint16_t dt_inv = SMOOTH_LIN_ADV_HZ;
float la_step_rate = (target_adv_steps - last_target_adv_steps) * dt_inv; int32_t la_step_rate = (target_adv_steps - last_target_adv_steps) * dt_inv;
last_target_adv_steps = target_adv_steps; last_target_adv_steps = target_adv_steps;
static float smoothed_vals[SMOOTH_LIN_ADV_EXP_ORDER] = {0}; static int32_t smoothed_vals[SMOOTH_LIN_ADV_EXP_ORDER] = {0};
for (uint8_t i = 0; i < SMOOTH_LIN_ADV_EXP_ORDER; i++) { for (uint8_t i = 0; i < SMOOTH_LIN_ADV_EXP_ORDER; i++) {
// Approximate gaussian smoothing via higher order exponential smoothing // 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] += MULT_Q(30, la_step_rate - smoothed_vals[i], extruder_advance_alpha_q30[E_INDEX_N(active_extruder)]);
smoothed_vals[i] = la_step_rate; la_step_rate = smoothed_vals[i];
} }
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; const int32_t planned_step_rate = current_block
? MULT_Q(30, curr_step_rate, current_block->e_step_ratio_q30)
: 0;
int32_t total_step_rate = la_step_rate + planned_step_rate;
#if ENABLED(INPUT_SHAPING_E_SYNC) #if ENABLED(INPUT_SHAPING_E_SYNC)
xy_float_t pre_shaping_rate = xy_float_t({0, 0}), xy_long_t pre_shaping_rate = xy_long_t({0, 0}),
first_pulse_rate = xy_float_t({0, 0}); first_pulse_rate = xy_long_t({0, 0});
float unshaped_rate_e = total_step_rate; int32_t unshaped_rate_e = total_step_rate;
if (current_block) { if (current_block) {
const float xy_length = TERN0(INPUT_SHAPING_X, current_block->steps.x) + TERN0(INPUT_SHAPING_Y, current_block->steps.y); if (current_block->xy_length_inv_q30 > 0) {
if (xy_length > 0) {
unshaped_rate_e = 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), pre_shaping_rate = xy_long_t({
TERN0(INPUT_SHAPING_X, MULT_Q(30, total_step_rate * current_block->steps.x, current_block->xy_length_inv_q30)),
TERN0(INPUT_SHAPING_Y, MULT_Q(30, total_step_rate * current_block->steps.y, current_block->xy_length_inv_q30))
});
first_pulse_rate = xy_long_t({
TERN0(INPUT_SHAPING_X, (pre_shaping_rate.x * Stepper::shaping_x.factor1) >> 7),
TERN0(INPUT_SHAPING_Y, (pre_shaping_rate.y * Stepper::shaping_y.factor1) >> 7)
});
}
}
const xy_long_t second_pulse_rate = {
TERN0(INPUT_SHAPING_X, (smooth_lin_adv_lookback(ShapingQueue::get_delay_x()).x * Stepper::shaping_x.factor2)) >> 7,
TERN0(INPUT_SHAPING_Y, (smooth_lin_adv_lookback(ShapingQueue::get_delay_y()).y * Stepper::shaping_y.factor2)) >> 7
};
delayBuffer.add(pre_shaping_rate);
const int32_t 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); y = TERN0(INPUT_SHAPING_Y, first_pulse_rate.y + second_pulse_rate.y);
total_step_rate = unshaped_rate_e + x + y; total_step_rate = unshaped_rate_e + x + y;
@ -3025,6 +3031,7 @@ hal_timer_t Stepper::block_phase_isr() {
curr_timer_tick += SMOOTH_LIN_ADV_INTERVAL; curr_timer_tick += SMOOTH_LIN_ADV_INTERVAL;
return SMOOTH_LIN_ADV_INTERVAL; return SMOOTH_LIN_ADV_INTERVAL;
} }
#endif // SMOOTH_LIN_ADVANCE #endif // SMOOTH_LIN_ADVANCE
// Timer interrupt for E. LA_steps is set in the main routine // Timer interrupt for E. LA_steps is set in the main routine

View file

@ -352,13 +352,18 @@ class Stepper {
#endif #endif
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
static void set_advance_tau(const_float_t tau, const uint8_t e=E_INDEX_N(active_extruder)) { static float extruder_advance_tau[DISTINCT_E]; // Smoothing time; also the lookahead time of the smoother
extruder_advance_tau[e] = tau; static void set_advance_tau(const_float_t tau, const uint8_t e=active_extruder) {
extruder_advance_tau_ticks[e] = tau * (STEPPER_TIMER_RATE); // i.e., <= STEPPER_TIMER_RATE / 2 const uint8_t i = E_INDEX_N(e);
extruder_advance_tau[i] = tau;
extruder_advance_tau_ticks[i] = tau * STEPPER_TIMER_RATE;
// α=1exp(dt/τ) // α=1exp(dt/τ)
extruder_advance_alpha[e] = 1.0f - expf(-(SMOOTH_LIN_ADV_INTERVAL) * (SMOOTH_LIN_ADV_EXP_ORDER) / extruder_advance_tau_ticks[e]); const float alpha_float = 1.0f - expf(-float(SMOOTH_LIN_ADV_INTERVAL) * (SMOOTH_LIN_ADV_EXP_ORDER) / extruder_advance_tau_ticks[i]);
extruder_advance_alpha_q30[i] = int32_t(alpha_float * _BV32(30));
}
static float get_advance_tau(const uint8_t e=active_extruder) {
return extruder_advance_tau[E_INDEX_N(e)];
} }
static float get_advance_tau(const uint8_t e=E_INDEX_N(active_extruder)) { return extruder_advance_tau[e]; }
#endif #endif
private: private:
@ -451,9 +456,8 @@ class Stepper {
#if ENABLED(SMOOTH_LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE)
static uint32_t curr_timer_tick, // Current tick relative to block start static uint32_t curr_timer_tick, // Current tick relative to block start
curr_step_rate; // Current motion step rate curr_step_rate; // Current motion step rate
static float extruder_advance_tau[DISTINCT_E], // Smoothing time; also the lookahead time of the smoother static uint32_t extruder_advance_tau_ticks[DISTINCT_E], // Same as extruder_advance_tau but in in stepper timer ticks
extruder_advance_tau_ticks[DISTINCT_E], // Same as extruder_advance_tau but in in stepper timer ticks extruder_advance_alpha_q30[DISTINCT_E]; // The smoothing factor of each stage of the high-order exponential
extruder_advance_alpha[DISTINCT_E]; // The smoothing factor of each stage of the high-order exponential
// smoothing filter (calculated from tau) // smoothing filter (calculated from tau)
#else #else
static int32_t la_delta_error, // Analogue of delta_error.e for E steps in LA ISR static int32_t la_delta_error, // Analogue of delta_error.e for E steps in LA ISR

View file

@ -1587,7 +1587,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
#endif #endif
// Migrate Linear Advance K factor to the new extruder // Migrate Linear Advance K factor to the new extruder
TERN_(LIN_ADVANCE, planner.extruder_advance_K[active_extruder] = planner.extruder_advance_K[migration_extruder]); TERN_(LIN_ADVANCE, planner.set_advance_k(planner.get_advance_k(migration_extruder), active_extruder));
// Temporary migration toolchange_settings restored on exit. i.e., before next tool_change(). // Temporary migration toolchange_settings restored on exit. i.e., before next tool_change().
#if defined(MIGRATION_FS_EXTRA_PRIME) \ #if defined(MIGRATION_FS_EXTRA_PRIME) \