diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 203825e76b..4d6ed12e99 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -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 /** diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h index 9fc7e99bf5..645725237b 100644 --- a/Marlin/src/core/language.h +++ b/Marlin/src/core/language.h @@ -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 F Z)" diff --git a/Marlin/src/gcode/feature/nonlinear/M592.cpp b/Marlin/src/gcode/feature/nonlinear/M592.cpp new file mode 100644 index 0000000000..dc8c1e1e58 --- /dev/null +++ b/Marlin/src/gcode/feature/nonlinear/M592.cpp @@ -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 . + * + */ + +#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 Linear coefficient (default 0.0) + * B Quadratic coefficient (default 0.0) + * C 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 diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index fd2a78d1fd..4902cebde2 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -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 diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index f9ee81f2eb..5e6e400cf1 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -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 Y Z E L". (Requires ADVANCED_PAUSE_FEATURE) * M603 - Configure filament change: "M603 T U L". (Requires ADVANCED_PAUSE_FEATURE) @@ -1106,6 +1107,11 @@ private: 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); diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index b31d10b068..0e9ef07eb3 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -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 */ diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 6b645fa133..9b8b268927 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -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 diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp index a4820ae900..7abe276561 100644 --- a/Marlin/src/module/settings.cpp +++ b/Marlin/src/module/settings.cpp @@ -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. @@ -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!"); @@ -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 // @@ -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 // @@ -3396,7 +3417,6 @@ void MarlinSettings::reset() { // // Heated Bed PID // - #if ENABLED(PIDTEMPBED) thermalManager.temp_bed.pid.set(DEFAULT_bedKp, DEFAULT_bedKi, DEFAULT_bedKd); #endif @@ -3404,7 +3424,6 @@ void MarlinSettings::reset() { // // Heated Chamber PID // - #if ENABLED(PIDTEMPCHAMBER) thermalManager.temp_chamber.pid.set(DEFAULT_chamberKp, DEFAULT_chamberKi, DEFAULT_chamberKd); #endif @@ -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) @@ -3598,6 +3616,11 @@ void MarlinSettings::reset() { // TERN_(FT_MOTION, fxdTiCtrl.set_defaults()); + // + // Nonlinear Extrusion + // + TERN_(NONLINEAR_EXTRUSION, stepper.ne.reset()); + // // Input Shaping // @@ -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 // diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 9bafe7443d..6077154cd2 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -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) @@ -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) { @@ -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; @@ -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; @@ -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); @@ -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 @@ -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; diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 7dbb6b8b5a..4312cfaa8b 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -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 // @@ -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 @@ -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; @@ -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); diff --git a/buildroot/tests/STM32F103RC_btt b/buildroot/tests/STM32F103RC_btt index 8df20740c4..b9fff2b6c5 100755 --- a/buildroot/tests/STM32F103RC_btt +++ b/buildroot/tests/STM32F103RC_btt @@ -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 diff --git a/ini/features.ini b/ini/features.ini index 1a3546e575..c89f2fea41 100644 --- a/ini/features.ini +++ b/ini/features.ini @@ -312,6 +312,7 @@ CONTROLLER_FAN_EDITABLE = build_src_filter=+ GCODE_MACROS = build_src_filter=+ GRADIENT_MIX = build_src_filter=+ +NONLINEAR_EXTRUSION = build_src_filter=+ OTA_FIRMWARE_UPDATE = build_src_filter=+ HAS_SAVED_POSITIONS = build_src_filter=+ + PARK_HEAD_ON_PAUSE = build_src_filter=+