Skip to content

Commit

Permalink
✨ Nonlinear Extrusion Control (M592) (MarlinFirmware#26127)
Browse files Browse the repository at this point in the history
  • Loading branch information
abortz authored and EvilGremlin committed Oct 26, 2023
1 parent c2964ee commit 087df23
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 8 deletions.
8 changes: 8 additions & 0 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -2275,6 +2275,14 @@
//#define EXPERIMENTAL_I2S_LA // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz.
#endif

/**
* Nonlinear Extrusion Control
*
* Control extrusion rate based on instantaneous extruder velocity. Can be used to correct for
* underextrusion at high extruder speeds that are otherwise well-behaved (i.e., not skipping).
*/
//#define NONLINEAR_EXTRUSION

// @section leveling

/**
Expand Down
1 change: 1 addition & 0 deletions Marlin/src/core/language.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@
#define STR_CHAMBER_PID "Chamber PID"
#define STR_STEPS_PER_UNIT "Steps per unit"
#define STR_LINEAR_ADVANCE "Linear Advance"
#define STR_NONLINEAR_EXTRUSION "Nonlinear Extrusion"
#define STR_CONTROLLER_FAN "Controller Fan"
#define STR_STEPPER_MOTOR_CURRENTS "Stepper motor currents"
#define STR_RETRACT_S_F_Z "Retract (S<length> F<feedrate> Z<lift>)"
Expand Down
51 changes: 51 additions & 0 deletions Marlin/src/gcode/feature/nonlinear/M592.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

#include "../../../inc/MarlinConfig.h"

#if ENABLED(NONLINEAR_EXTRUSION)

#include "../../gcode.h"
#include "../../../module/stepper.h"

void GcodeSuite::M592_report(const bool forReplay/*=true*/) {
report_heading(forReplay, F(STR_NONLINEAR_EXTRUSION));
SERIAL_ECHOLNPGM(" M593 A", stepper.ne.A, " B", stepper.ne.B, " C", stepper.ne.C);
}

/**
* M592: Get or set nonlinear extrusion parameters
* A<factor> Linear coefficient (default 0.0)
* B<factor> Quadratic coefficient (default 0.0)
* C<factor> Constant coefficient (default 1.0)
*
* Adjusts the amount of extrusion based on the instantaneous velocity of extrusion, as a multiplier.
* The amount of extrusion is multiplied by max(C, C + A*v + B*v^2) where v is extruder velocity in mm/s.
* Only adjusts forward extrusions, since those are the ones affected by backpressure.
*/
void GcodeSuite::M592() {
if (parser.seenval('A')) stepper.ne.A = parser.value_float();
if (parser.seenval('B')) stepper.ne.B = parser.value_float();
if (parser.seenval('C')) stepper.ne.C = parser.value_float();
}

#endif // NONLINEAR_EXTRUSION
4 changes: 4 additions & 0 deletions Marlin/src/gcode/gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
case 575: M575(); break; // M575: Set serial baudrate
#endif

#if ENABLED(NONLINEAR_EXTRUSION)
case 592: M592(); break; // M592: Nonlinear Extrusion control
#endif

#if HAS_ZV_SHAPING
case 593: M593(); break; // M593: Input Shaping control
#endif
Expand Down
6 changes: 6 additions & 0 deletions Marlin/src/gcode/gcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@
* M554 - Get or set IP gateway. (Requires enabled Ethernet port)
* M569 - Enable stealthChop on an axis. (Requires at least one _DRIVER_TYPE to be TMC2130/2160/2208/2209/5130/5160)
* M575 - Change the serial baud rate. (Requires BAUD_RATE_GCODE)
* M592 - Get or set nonlinear extrusion parameters. (Requires NONLINEAR_EXTRUSION)
* M593 - Get or set input shaping parameters. (Requires INPUT_SHAPING_[XY])
* M600 - Pause for filament change: "M600 X<pos> Y<pos> Z<raise> E<first_retract> L<later_retract>". (Requires ADVANCED_PAUSE_FEATURE)
* M603 - Configure filament change: "M603 T<tool> U<unload_length> L<load_length>". (Requires ADVANCED_PAUSE_FEATURE)
Expand Down Expand Up @@ -1106,6 +1107,11 @@ class GcodeSuite {
static void M575();
#endif

#if ENABLED(NONLINEAR_EXTRUSION)
static void M592();
static void M592_report(const bool forReplay=true);
#endif

#if HAS_ZV_SHAPING
static void M593();
static void M593_report(const bool forReplay=true);
Expand Down
13 changes: 13 additions & 0 deletions Marlin/src/inc/SanityCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,19 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
#endif
#endif

/**
* Nonlinear Extrusion requirements
*/
#if ENABLED(NONLINEAR_EXTRUSION)
#if DISABLED(ADAPTIVE_STEP_SMOOTHING)
#error "ADAPTIVE_STEP_SMOOTHING is required for NONLINEAR_EXTRUSION."
#elif HAS_MULTI_EXTRUDER
#error "NONLINEAR_EXTRUSION doesn't currently support multi-extruder setups."
#elif DISABLED(CPU_32_BIT)
#error "NONLINEAR_EXTRUSION requires a 32-bit CPU."
#endif
#endif

/**
* Special tool-changing options
*/
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/module/planner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2005,7 +2005,7 @@ bool Planner::_populate_block(
#if HAS_EXTRUDERS
dm.e = (dist.e > 0);
const float esteps_float = dist.e * e_factor[extruder];
const uint32_t esteps = ABS(esteps_float) + 0.5f;
const uint32_t esteps = ABS(esteps_float);
#else
constexpr uint32_t esteps = 0;
#endif
Expand Down
36 changes: 32 additions & 4 deletions Marlin/src/module/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
*/

// Change EEPROM version if the structure changes
#define EEPROM_VERSION "V88"
#define EEPROM_VERSION "V89"
#define EEPROM_OFFSET 100

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

//
// Nonlinear Extrusion
//
#if ENABLED(NONLINEAR_EXTRUSION)
ne_coeff_t stepper_ne; // M592 A B C
#endif

} SettingsData;

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

//
// Nonlinear Extrusion
//
#if ENABLED(NONLINEAR_EXTRUSION)
EEPROM_WRITE(stepper.ne);
#endif

//
// Report final CRC and Data Size
//
Expand Down Expand Up @@ -2803,6 +2817,13 @@ void MarlinSettings::postprocess() {
EEPROM_READ(hotend_idle.cfg);
#endif

//
// Nonlinear Extrusion
//
#if ENABLED(NONLINEAR_EXTRUSION)
EEPROM_READ(stepper.ne);
#endif

//
// Validate Final Size and CRC
//
Expand Down Expand Up @@ -3396,15 +3417,13 @@ void MarlinSettings::reset() {
//
// Heated Bed PID
//

#if ENABLED(PIDTEMPBED)
thermalManager.temp_bed.pid.set(DEFAULT_bedKp, DEFAULT_bedKi, DEFAULT_bedKd);
#endif

//
// Heated Chamber PID
//

#if ENABLED(PIDTEMPCHAMBER)
thermalManager.temp_chamber.pid.set(DEFAULT_chamberKp, DEFAULT_chamberKi, DEFAULT_chamberKd);
#endif
Expand Down Expand Up @@ -3456,7 +3475,6 @@ void MarlinSettings::reset() {
//
// Volumetric & Filament Size
//

#if DISABLED(NO_VOLUMETRICS)
parser.volumetric_enabled = ENABLED(VOLUMETRIC_DEFAULT_ON);
for (uint8_t q = 0; q < COUNT(planner.filament_size); ++q)
Expand Down Expand Up @@ -3598,6 +3616,11 @@ void MarlinSettings::reset() {
//
TERN_(FT_MOTION, fxdTiCtrl.set_defaults());

//
// Nonlinear Extrusion
//
TERN_(NONLINEAR_EXTRUSION, stepper.ne.reset());

//
// Input Shaping
//
Expand Down Expand Up @@ -3867,6 +3890,11 @@ void MarlinSettings::reset() {
//
TERN_(FT_MOTION, gcode.M493_report(forReplay));

//
// Nonlinear Extrusion
//
TERN_(NONLINEAR_EXTRUSION, gcode.M592_report(forReplay));

//
// Input Shaping
//
Expand Down
55 changes: 53 additions & 2 deletions Marlin/src/module/stepper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,13 @@ uint32_t Stepper::advance_divisor = 0,
bool Stepper::la_active = false;
#endif

#if ENABLED(NONLINEAR_EXTRUSION)
ne_coeff_t Stepper::ne;
ne_fix_t Stepper::ne_fix;
int32_t Stepper::ne_edividend;
uint32_t Stepper::ne_scale;
#endif

#if HAS_ZV_SHAPING
shaping_time_t ShapingQueue::now = 0;
#if ANY(MCU_LPC1768, MCU_LPC1769) && DISABLED(NO_LPC_ETHERNET_BUFFER)
Expand Down Expand Up @@ -2191,6 +2198,16 @@ hal_timer_t Stepper::calc_timer_interval(uint32_t step_rate) {
#endif // !CPU_32_BIT
}

#if ENABLED(NONLINEAR_EXTRUSION)
void Stepper::calc_nonlinear_e(uint32_t step_rate) {
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
int32_t vd = (((int64_t)ne_fix.A * velocity) >> 24) + (((((int64_t)ne_fix.B * velocity) >> 24) * velocity) >> 24);
NOLESS(vd, 0);

advance_dividend.e = (uint64_t(ne_fix.C + vd) * ne_edividend) >> 24;
}
#endif

// Get the timer interval and the number of loops to perform per tick
hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {

Expand Down Expand Up @@ -2318,6 +2335,10 @@ hal_timer_t Stepper::block_phase_isr() {
interval = calc_multistep_timer_interval(acc_step_rate << oversampling_factor);
acceleration_time += interval;

#if ENABLED(NONLINEAR_EXTRUSION)
calc_nonlinear_e(acc_step_rate << oversampling_factor);
#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;
Expand Down Expand Up @@ -2388,6 +2409,10 @@ hal_timer_t Stepper::block_phase_isr() {
interval = calc_multistep_timer_interval(step_rate << oversampling_factor);
deceleration_time += interval;

#if ENABLED(NONLINEAR_EXTRUSION)
calc_nonlinear_e(step_rate << oversampling_factor);
#endif

#if ENABLED(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;
Expand Down Expand Up @@ -2436,6 +2461,10 @@ hal_timer_t Stepper::block_phase_isr() {
// step_rate to timer interval and loops for the nominal speed
ticks_nominal = calc_multistep_timer_interval(current_block->nominal_rate << oversampling_factor);

#if ENABLED(NONLINEAR_EXTRUSION)
calc_nonlinear_e(current_block->nominal_rate << oversampling_factor);
#endif

#if ENABLED(LIN_ADVANCE)
if (la_active)
la_interval = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling);
Expand Down Expand Up @@ -2636,10 +2665,13 @@ hal_timer_t Stepper::block_phase_isr() {
acceleration_time = deceleration_time = 0;

#if ENABLED(ADAPTIVE_STEP_SMOOTHING)
oversampling_factor = 0; // Assume no axis smoothing (via oversampling)
// Nonlinear Extrusion needs at least 2x oversampling to permit increase of E step rate
// Otherwise assume no axis smoothing (via oversampling)
oversampling_factor = TERN(NONLINEAR_EXTRUSION, 1, 0);

// Decide if axis smoothing is possible
uint32_t max_rate = current_block->nominal_rate; // Get the step event rate
if (TERN1(DWIN_LCD_PROUI, hmiData.adaptiveStepSmoothing)) {
uint32_t max_rate = current_block->nominal_rate; // Get the step event rate
while (max_rate < MIN_STEP_ISR_FREQUENCY) { // As long as more ISRs are possible...
max_rate <<= 1; // Try to double the rate
if (max_rate < MIN_STEP_ISR_FREQUENCY) // Don't exceed the estimated ISR limit
Expand Down Expand Up @@ -2755,10 +2787,29 @@ hal_timer_t Stepper::block_phase_isr() {
acc_step_rate = current_block->initial_rate;
#endif

#if ENABLED(NONLINEAR_EXTRUSION)
ne_edividend = advance_dividend.e;
const float scale = (float(ne_edividend) / advance_divisor) * planner.mm_per_step[E_AXIS_N(current_block->extruder)];
ne_scale = (1L << 24) * scale;
if (current_block->direction_bits.e) {
ne_fix.A = (1L << 24) * ne.A;
ne_fix.B = (1L << 24) * ne.B;
ne_fix.C = (1L << 24) * ne.C;
}
else {
ne_fix.A = ne_fix.B = 0;
ne_fix.C = (1L << 24);
}
#endif

// Calculate the initial timer interval
interval = calc_multistep_timer_interval(current_block->initial_rate << oversampling_factor);
acceleration_time += interval;

#if ENABLED(NONLINEAR_EXTRUSION)
calc_nonlinear_e(current_block->initial_rate << oversampling_factor);
#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;
Expand Down
19 changes: 19 additions & 0 deletions Marlin/src/module/stepper.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ constexpr ena_mask_t enable_overlap[] = {

#endif // HAS_ZV_SHAPING

#if ENABLED(NONLINEAR_EXTRUSION)
typedef struct { float A, B, C; void reset() { A = B = 0.0f; C = 1.0f; } } ne_coeff_t;
typedef struct { int32_t A, B, C; } ne_fix_t;
#endif

//
// Stepper class definition
//
Expand Down Expand Up @@ -326,6 +331,10 @@ class Stepper {
static bool frozen; // Set this flag to instantly freeze motion
#endif

#if ENABLED(NONLINEAR_EXTRUSION)
static ne_coeff_t ne;
#endif

private:

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

#if ENABLED(NONLINEAR_EXTRUSION)
static int32_t ne_edividend;
static uint32_t ne_scale;
static ne_fix_t ne_fix;
#endif

#if ENABLED(BABYSTEPPING)
static constexpr hal_timer_t BABYSTEP_NEVER = HAL_TIMER_TYPE_MAX;
static hal_timer_t nextBabystepISR;
Expand Down Expand Up @@ -660,6 +675,10 @@ class Stepper {
// Calculate timing interval and steps-per-ISR for the given step rate
static hal_timer_t calc_multistep_timer_interval(uint32_t step_rate);

#if ENABLED(NONLINEAR_EXTRUSION)
static void calc_nonlinear_e(uint32_t step_rate);
#endif

#if ENABLED(S_CURVE_ACCELERATION)
static void _calc_bezier_curve_coeffs(const int32_t v0, const int32_t v1, const uint32_t av);
static int32_t _eval_bezier_curve(const uint32_t curr_step);
Expand Down
2 changes: 1 addition & 1 deletion buildroot/tests/STM32F103RC_btt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ set -e
restore_configs
opt_set MOTHERBOARD BOARD_BTT_SKR_MINI_E3_V1_0 SERIAL_PORT 1 SERIAL_PORT_2 -1 \
X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2209 Z_DRIVER_TYPE TMC2209 E0_DRIVER_TYPE TMC2209
opt_enable CR10_STOCKDISPLAY PINS_DEBUGGING Z_IDLE_HEIGHT FT_MOTION FT_MOTION_MENU
opt_enable CR10_STOCKDISPLAY PINS_DEBUGGING Z_IDLE_HEIGHT FT_MOTION FT_MOTION_MENU ADAPTIVE_STEP_SMOOTHING NONLINEAR_EXTRUSION
exec_test $1 $2 "BigTreeTech SKR Mini E3 1.0 - TMC2209 HW Serial, FT_MOTION" "$3"

# clean up
Expand Down
Loading

0 comments on commit 087df23

Please sign in to comment.