Skip to content

Commit e9b9d63

Browse files
authored
✨ Nonlinear Extrusion Control (M592) (MarlinFirmware#26127)
1 parent 6d301a2 commit e9b9d63

File tree

12 files changed

+190
-8
lines changed

12 files changed

+190
-8
lines changed

Marlin/Configuration_adv.h

+8
Original file line numberDiff line numberDiff line change
@@ -2275,6 +2275,14 @@
22752275
//#define EXPERIMENTAL_I2S_LA // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz.
22762276
#endif
22772277

2278+
/**
2279+
* Nonlinear Extrusion Control
2280+
*
2281+
* Control extrusion rate based on instantaneous extruder velocity. Can be used to correct for
2282+
* underextrusion at high extruder speeds that are otherwise well-behaved (i.e., not skipping).
2283+
*/
2284+
//#define NONLINEAR_EXTRUSION
2285+
22782286
// @section leveling
22792287

22802288
/**

Marlin/src/core/language.h

+1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@
301301
#define STR_CHAMBER_PID "Chamber PID"
302302
#define STR_STEPS_PER_UNIT "Steps per unit"
303303
#define STR_LINEAR_ADVANCE "Linear Advance"
304+
#define STR_NONLINEAR_EXTRUSION "Nonlinear Extrusion"
304305
#define STR_CONTROLLER_FAN "Controller Fan"
305306
#define STR_STEPPER_MOTOR_CURRENTS "Stepper motor currents"
306307
#define STR_RETRACT_S_F_Z "Retract (S<length> F<feedrate> Z<lift>)"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Marlin 3D Printer Firmware
3+
* Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4+
*
5+
* Based on Sprinter and grbl.
6+
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
#include "../../../inc/MarlinConfig.h"
24+
25+
#if ENABLED(NONLINEAR_EXTRUSION)
26+
27+
#include "../../gcode.h"
28+
#include "../../../module/stepper.h"
29+
30+
void GcodeSuite::M592_report(const bool forReplay/*=true*/) {
31+
report_heading(forReplay, F(STR_NONLINEAR_EXTRUSION));
32+
SERIAL_ECHOLNPGM(" M593 A", stepper.ne.A, " B", stepper.ne.B, " C", stepper.ne.C);
33+
}
34+
35+
/**
36+
* M592: Get or set nonlinear extrusion parameters
37+
* A<factor> Linear coefficient (default 0.0)
38+
* B<factor> Quadratic coefficient (default 0.0)
39+
* C<factor> Constant coefficient (default 1.0)
40+
*
41+
* Adjusts the amount of extrusion based on the instantaneous velocity of extrusion, as a multiplier.
42+
* The amount of extrusion is multiplied by max(C, C + A*v + B*v^2) where v is extruder velocity in mm/s.
43+
* Only adjusts forward extrusions, since those are the ones affected by backpressure.
44+
*/
45+
void GcodeSuite::M592() {
46+
if (parser.seenval('A')) stepper.ne.A = parser.value_float();
47+
if (parser.seenval('B')) stepper.ne.B = parser.value_float();
48+
if (parser.seenval('C')) stepper.ne.C = parser.value_float();
49+
}
50+
51+
#endif // NONLINEAR_EXTRUSION

Marlin/src/gcode/gcode.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
935935
case 575: M575(); break; // M575: Set serial baudrate
936936
#endif
937937

938+
#if ENABLED(NONLINEAR_EXTRUSION)
939+
case 592: M592(); break; // M592: Nonlinear Extrusion control
940+
#endif
941+
938942
#if HAS_ZV_SHAPING
939943
case 593: M593(); break; // M593: Input Shaping control
940944
#endif

Marlin/src/gcode/gcode.h

+6
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@
259259
* M554 - Get or set IP gateway. (Requires enabled Ethernet port)
260260
* M569 - Enable stealthChop on an axis. (Requires at least one _DRIVER_TYPE to be TMC2130/2160/2208/2209/5130/5160)
261261
* M575 - Change the serial baud rate. (Requires BAUD_RATE_GCODE)
262+
* M592 - Get or set nonlinear extrusion parameters. (Requires NONLINEAR_EXTRUSION)
262263
* M593 - Get or set input shaping parameters. (Requires INPUT_SHAPING_[XY])
263264
* M600 - Pause for filament change: "M600 X<pos> Y<pos> Z<raise> E<first_retract> L<later_retract>". (Requires ADVANCED_PAUSE_FEATURE)
264265
* M603 - Configure filament change: "M603 T<tool> U<unload_length> L<load_length>". (Requires ADVANCED_PAUSE_FEATURE)
@@ -1106,6 +1107,11 @@ class GcodeSuite {
11061107
static void M575();
11071108
#endif
11081109

1110+
#if ENABLED(NONLINEAR_EXTRUSION)
1111+
static void M592();
1112+
static void M592_report(const bool forReplay=true);
1113+
#endif
1114+
11091115
#if HAS_ZV_SHAPING
11101116
static void M593();
11111117
static void M593_report(const bool forReplay=true);

Marlin/src/inc/SanityCheck.h

+13
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,19 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
858858
#endif
859859
#endif
860860

861+
/**
862+
* Nonlinear Extrusion requirements
863+
*/
864+
#if ENABLED(NONLINEAR_EXTRUSION)
865+
#if DISABLED(ADAPTIVE_STEP_SMOOTHING)
866+
#error "ADAPTIVE_STEP_SMOOTHING is required for NONLINEAR_EXTRUSION."
867+
#elif HAS_MULTI_EXTRUDER
868+
#error "NONLINEAR_EXTRUSION doesn't currently support multi-extruder setups."
869+
#elif DISABLED(CPU_32_BIT)
870+
#error "NONLINEAR_EXTRUSION requires a 32-bit CPU."
871+
#endif
872+
#endif
873+
861874
/**
862875
* Special tool-changing options
863876
*/

Marlin/src/module/planner.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -2005,7 +2005,7 @@ bool Planner::_populate_block(
20052005
#if HAS_EXTRUDERS
20062006
dm.e = (dist.e > 0);
20072007
const float esteps_float = dist.e * e_factor[extruder];
2008-
const uint32_t esteps = ABS(esteps_float) + 0.5f;
2008+
const uint32_t esteps = ABS(esteps_float);
20092009
#else
20102010
constexpr uint32_t esteps = 0;
20112011
#endif

Marlin/src/module/settings.cpp

+32-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
*/
3737

3838
// Change EEPROM version if the structure changes
39-
#define EEPROM_VERSION "V88"
39+
#define EEPROM_VERSION "V89"
4040
#define EEPROM_OFFSET 100
4141

4242
// Check the integrity of data offsets.
@@ -634,6 +634,13 @@ typedef struct SettingsDataStruct {
634634
hotend_idle_settings_t hotend_idle_config; // M86 S T E B
635635
#endif
636636

637+
//
638+
// Nonlinear Extrusion
639+
//
640+
#if ENABLED(NONLINEAR_EXTRUSION)
641+
ne_coeff_t stepper_ne; // M592 A B C
642+
#endif
643+
637644
} SettingsData;
638645

639646
//static_assert(sizeof(SettingsData) <= MARLIN_EEPROM_SIZE, "EEPROM too small to contain SettingsData!");
@@ -1729,6 +1736,13 @@ void MarlinSettings::postprocess() {
17291736
EEPROM_WRITE(hotend_idle.cfg);
17301737
#endif
17311738

1739+
//
1740+
// Nonlinear Extrusion
1741+
//
1742+
#if ENABLED(NONLINEAR_EXTRUSION)
1743+
EEPROM_WRITE(stepper.ne);
1744+
#endif
1745+
17321746
//
17331747
// Report final CRC and Data Size
17341748
//
@@ -2803,6 +2817,13 @@ void MarlinSettings::postprocess() {
28032817
EEPROM_READ(hotend_idle.cfg);
28042818
#endif
28052819

2820+
//
2821+
// Nonlinear Extrusion
2822+
//
2823+
#if ENABLED(NONLINEAR_EXTRUSION)
2824+
EEPROM_READ(stepper.ne);
2825+
#endif
2826+
28062827
//
28072828
// Validate Final Size and CRC
28082829
//
@@ -3396,15 +3417,13 @@ void MarlinSettings::reset() {
33963417
//
33973418
// Heated Bed PID
33983419
//
3399-
34003420
#if ENABLED(PIDTEMPBED)
34013421
thermalManager.temp_bed.pid.set(DEFAULT_bedKp, DEFAULT_bedKi, DEFAULT_bedKd);
34023422
#endif
34033423

34043424
//
34053425
// Heated Chamber PID
34063426
//
3407-
34083427
#if ENABLED(PIDTEMPCHAMBER)
34093428
thermalManager.temp_chamber.pid.set(DEFAULT_chamberKp, DEFAULT_chamberKi, DEFAULT_chamberKd);
34103429
#endif
@@ -3456,7 +3475,6 @@ void MarlinSettings::reset() {
34563475
//
34573476
// Volumetric & Filament Size
34583477
//
3459-
34603478
#if DISABLED(NO_VOLUMETRICS)
34613479
parser.volumetric_enabled = ENABLED(VOLUMETRIC_DEFAULT_ON);
34623480
for (uint8_t q = 0; q < COUNT(planner.filament_size); ++q)
@@ -3598,6 +3616,11 @@ void MarlinSettings::reset() {
35983616
//
35993617
TERN_(FT_MOTION, fxdTiCtrl.set_defaults());
36003618

3619+
//
3620+
// Nonlinear Extrusion
3621+
//
3622+
TERN_(NONLINEAR_EXTRUSION, stepper.ne.reset());
3623+
36013624
//
36023625
// Input Shaping
36033626
//
@@ -3867,6 +3890,11 @@ void MarlinSettings::reset() {
38673890
//
38683891
TERN_(FT_MOTION, gcode.M493_report(forReplay));
38693892

3893+
//
3894+
// Nonlinear Extrusion
3895+
//
3896+
TERN_(NONLINEAR_EXTRUSION, gcode.M592_report(forReplay));
3897+
38703898
//
38713899
// Input Shaping
38723900
//

Marlin/src/module/stepper.cpp

+53-2
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,13 @@ uint32_t Stepper::advance_divisor = 0,
245245
bool Stepper::la_active = false;
246246
#endif
247247

248+
#if ENABLED(NONLINEAR_EXTRUSION)
249+
ne_coeff_t Stepper::ne;
250+
ne_fix_t Stepper::ne_fix;
251+
int32_t Stepper::ne_edividend;
252+
uint32_t Stepper::ne_scale;
253+
#endif
254+
248255
#if HAS_ZV_SHAPING
249256
shaping_time_t ShapingQueue::now = 0;
250257
#if ANY(MCU_LPC1768, MCU_LPC1769) && DISABLED(NO_LPC_ETHERNET_BUFFER)
@@ -2191,6 +2198,16 @@ hal_timer_t Stepper::calc_timer_interval(uint32_t step_rate) {
21912198
#endif // !CPU_32_BIT
21922199
}
21932200

2201+
#if ENABLED(NONLINEAR_EXTRUSION)
2202+
void Stepper::calc_nonlinear_e(uint32_t step_rate) {
2203+
const uint32_t velocity = ne_scale * step_rate; // Scale step_rate first so all intermediate values stay in range of 8.24 fixed point math
2204+
int32_t vd = (((int64_t)ne_fix.A * velocity) >> 24) + (((((int64_t)ne_fix.B * velocity) >> 24) * velocity) >> 24);
2205+
NOLESS(vd, 0);
2206+
2207+
advance_dividend.e = (uint64_t(ne_fix.C + vd) * ne_edividend) >> 24;
2208+
}
2209+
#endif
2210+
21942211
// Get the timer interval and the number of loops to perform per tick
21952212
hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {
21962213

@@ -2318,6 +2335,10 @@ hal_timer_t Stepper::block_phase_isr() {
23182335
interval = calc_multistep_timer_interval(acc_step_rate << oversampling_factor);
23192336
acceleration_time += interval;
23202337

2338+
#if ENABLED(NONLINEAR_EXTRUSION)
2339+
calc_nonlinear_e(acc_step_rate << oversampling_factor);
2340+
#endif
2341+
23212342
#if ENABLED(LIN_ADVANCE)
23222343
if (la_active) {
23232344
const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0;
@@ -2388,6 +2409,10 @@ hal_timer_t Stepper::block_phase_isr() {
23882409
interval = calc_multistep_timer_interval(step_rate << oversampling_factor);
23892410
deceleration_time += interval;
23902411

2412+
#if ENABLED(NONLINEAR_EXTRUSION)
2413+
calc_nonlinear_e(step_rate << oversampling_factor);
2414+
#endif
2415+
23912416
#if ENABLED(LIN_ADVANCE)
23922417
if (la_active) {
23932418
const uint32_t la_step_rate = la_advance_steps > current_block->final_adv_steps ? current_block->la_advance_rate : 0;
@@ -2436,6 +2461,10 @@ hal_timer_t Stepper::block_phase_isr() {
24362461
// step_rate to timer interval and loops for the nominal speed
24372462
ticks_nominal = calc_multistep_timer_interval(current_block->nominal_rate << oversampling_factor);
24382463

2464+
#if ENABLED(NONLINEAR_EXTRUSION)
2465+
calc_nonlinear_e(current_block->nominal_rate << oversampling_factor);
2466+
#endif
2467+
24392468
#if ENABLED(LIN_ADVANCE)
24402469
if (la_active)
24412470
la_interval = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling);
@@ -2636,10 +2665,13 @@ hal_timer_t Stepper::block_phase_isr() {
26362665
acceleration_time = deceleration_time = 0;
26372666

26382667
#if ENABLED(ADAPTIVE_STEP_SMOOTHING)
2639-
oversampling_factor = 0; // Assume no axis smoothing (via oversampling)
2668+
// Nonlinear Extrusion needs at least 2x oversampling to permit increase of E step rate
2669+
// Otherwise assume no axis smoothing (via oversampling)
2670+
oversampling_factor = TERN(NONLINEAR_EXTRUSION, 1, 0);
2671+
26402672
// Decide if axis smoothing is possible
2641-
uint32_t max_rate = current_block->nominal_rate; // Get the step event rate
26422673
if (TERN1(DWIN_LCD_PROUI, hmiData.adaptiveStepSmoothing)) {
2674+
uint32_t max_rate = current_block->nominal_rate; // Get the step event rate
26432675
while (max_rate < MIN_STEP_ISR_FREQUENCY) { // As long as more ISRs are possible...
26442676
max_rate <<= 1; // Try to double the rate
26452677
if (max_rate < MIN_STEP_ISR_FREQUENCY) // Don't exceed the estimated ISR limit
@@ -2755,10 +2787,29 @@ hal_timer_t Stepper::block_phase_isr() {
27552787
acc_step_rate = current_block->initial_rate;
27562788
#endif
27572789

2790+
#if ENABLED(NONLINEAR_EXTRUSION)
2791+
ne_edividend = advance_dividend.e;
2792+
const float scale = (float(ne_edividend) / advance_divisor) * planner.mm_per_step[E_AXIS_N(current_block->extruder)];
2793+
ne_scale = (1L << 24) * scale;
2794+
if (current_block->direction_bits.e) {
2795+
ne_fix.A = (1L << 24) * ne.A;
2796+
ne_fix.B = (1L << 24) * ne.B;
2797+
ne_fix.C = (1L << 24) * ne.C;
2798+
}
2799+
else {
2800+
ne_fix.A = ne_fix.B = 0;
2801+
ne_fix.C = (1L << 24);
2802+
}
2803+
#endif
2804+
27582805
// Calculate the initial timer interval
27592806
interval = calc_multistep_timer_interval(current_block->initial_rate << oversampling_factor);
27602807
acceleration_time += interval;
27612808

2809+
#if ENABLED(NONLINEAR_EXTRUSION)
2810+
calc_nonlinear_e(current_block->initial_rate << oversampling_factor);
2811+
#endif
2812+
27622813
#if ENABLED(LIN_ADVANCE)
27632814
if (la_active) {
27642815
const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0;

Marlin/src/module/stepper.h

+19
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,11 @@ constexpr ena_mask_t enable_overlap[] = {
284284

285285
#endif // HAS_ZV_SHAPING
286286

287+
#if ENABLED(NONLINEAR_EXTRUSION)
288+
typedef struct { float A, B, C; void reset() { A = B = 0.0f; C = 1.0f; } } ne_coeff_t;
289+
typedef struct { int32_t A, B, C; } ne_fix_t;
290+
#endif
291+
287292
//
288293
// Stepper class definition
289294
//
@@ -326,6 +331,10 @@ class Stepper {
326331
static bool frozen; // Set this flag to instantly freeze motion
327332
#endif
328333

334+
#if ENABLED(NONLINEAR_EXTRUSION)
335+
static ne_coeff_t ne;
336+
#endif
337+
329338
private:
330339

331340
static block_t* current_block; // A pointer to the block currently being traced
@@ -416,6 +425,12 @@ class Stepper {
416425
static bool la_active; // Whether linear advance is used on the present segment.
417426
#endif
418427

428+
#if ENABLED(NONLINEAR_EXTRUSION)
429+
static int32_t ne_edividend;
430+
static uint32_t ne_scale;
431+
static ne_fix_t ne_fix;
432+
#endif
433+
419434
#if ENABLED(BABYSTEPPING)
420435
static constexpr hal_timer_t BABYSTEP_NEVER = HAL_TIMER_TYPE_MAX;
421436
static hal_timer_t nextBabystepISR;
@@ -660,6 +675,10 @@ class Stepper {
660675
// Calculate timing interval and steps-per-ISR for the given step rate
661676
static hal_timer_t calc_multistep_timer_interval(uint32_t step_rate);
662677

678+
#if ENABLED(NONLINEAR_EXTRUSION)
679+
static void calc_nonlinear_e(uint32_t step_rate);
680+
#endif
681+
663682
#if ENABLED(S_CURVE_ACCELERATION)
664683
static void _calc_bezier_curve_coeffs(const int32_t v0, const int32_t v1, const uint32_t av);
665684
static int32_t _eval_bezier_curve(const uint32_t curr_step);

buildroot/tests/STM32F103RC_btt

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ set -e
1212
restore_configs
1313
opt_set MOTHERBOARD BOARD_BTT_SKR_MINI_E3_V1_0 SERIAL_PORT 1 SERIAL_PORT_2 -1 \
1414
X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2209 Z_DRIVER_TYPE TMC2209 E0_DRIVER_TYPE TMC2209
15-
opt_enable CR10_STOCKDISPLAY PINS_DEBUGGING Z_IDLE_HEIGHT FT_MOTION FT_MOTION_MENU
15+
opt_enable CR10_STOCKDISPLAY PINS_DEBUGGING Z_IDLE_HEIGHT FT_MOTION FT_MOTION_MENU ADAPTIVE_STEP_SMOOTHING NONLINEAR_EXTRUSION
1616
exec_test $1 $2 "BigTreeTech SKR Mini E3 1.0 - TMC2209 HW Serial, FT_MOTION" "$3"
1717

1818
# clean up

0 commit comments

Comments
 (0)