Add volumetric extrusion limit (#17017)
This commit is contained in:
		| @@ -2993,9 +2993,21 @@ | ||||
|    * Activate to make volumetric extrusion the default method, | ||||
|    * with DEFAULT_NOMINAL_FILAMENT_DIA as the default diameter. | ||||
|    * | ||||
|    * M200 D0 to disable, M200 Dn to set a new diameter. | ||||
|    * M200 D0 to disable, M200 Dn to set a new diameter (and enable volumetric). | ||||
|    * M200 S0/S1 to disable/enable volumetric extrusion. | ||||
|    */ | ||||
|   //#define VOLUMETRIC_DEFAULT_ON | ||||
|  | ||||
|   //#define VOLUMETRIC_EXTRUDER_LIMIT | ||||
|   #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|     /** | ||||
|      * Default volumetric extrusion limit in cubic mm per second (mm^3/sec). | ||||
|      * This factory setting applies to all extruders. | ||||
|      * Use 'M200 [T<extruder>] L<limit>' to override and 'M502' to reset. | ||||
|      * A non-zero value activates Volume-based Extrusion Limiting. | ||||
|      */ | ||||
|     #define DEFAULT_VOLUMETRIC_EXTRUDER_LIMIT 0.00      // (mm^3/sec) | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -30,21 +30,42 @@ | ||||
|    * M200: Set filament diameter and set E axis units to cubic units | ||||
|    * | ||||
|    *    T<extruder> - Optional extruder number. Current extruder if omitted. | ||||
|    *    D<linear> - Diameter of the filament. Use "D0" to switch back to linear units on the E axis. | ||||
|    *    D<linear>   - Set filament diameter and enable. D0 disables volumetric. | ||||
|    *    S<bool>     - Turn volumetric ON or OFF. | ||||
|    *    L<float>    - Volumetric extruder limit (in mm^3/sec). L0 disables the limit. | ||||
|    */ | ||||
|   void GcodeSuite::M200() { | ||||
|  | ||||
|     const int8_t target_extruder = get_target_extruder_from_command(); | ||||
|     if (target_extruder < 0) return; | ||||
|  | ||||
|     if (parser.seen('D')) { | ||||
|       // setting any extruder filament size disables volumetric on the assumption that | ||||
|       // slicers either generate in extruder values as cubic mm or as as filament feeds | ||||
|       // for all extruders | ||||
|     bool vol_enable = parser.volumetric_enabled, | ||||
|          can_enable = true; | ||||
|  | ||||
|     if (parser.seenval('D')) { | ||||
|       const float dval = parser.value_linear_units(); | ||||
|       if ( (parser.volumetric_enabled = (dval != 0)) ) | ||||
|       if (dval) { // Set filament size for volumetric calculation | ||||
|         planner.set_filament_size(target_extruder, dval); | ||||
|         vol_enable = true;    // Dn = enable for compatibility | ||||
|       } | ||||
|       else | ||||
|         can_enable = false;   // D0 = disable for compatibility | ||||
|     } | ||||
|  | ||||
|     // Enable or disable with S1 / S0 | ||||
|     parser.volumetric_enabled = can_enable && parser.boolval('S', vol_enable); | ||||
|  | ||||
|     #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|       if (parser.seenval('L')) { | ||||
|         // Set volumetric limit (in mm^3/sec) | ||||
|         const float lval = parser.value_float(); | ||||
|         if (WITHIN(lval, 0, 20)) | ||||
|           planner.set_volumetric_extruder_limit(target_extruder, lval); | ||||
|         else | ||||
|           SERIAL_ECHOLNPGM("?L value out of range (0-20)."); | ||||
|       } | ||||
|     #endif | ||||
|      | ||||
|     planner.calculate_volumetric_multipliers(); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -260,6 +260,13 @@ | ||||
|   #define MAX_AUTORETRACT 99 | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Provide a DEFAULT_VOLUMETRIC_EXTRUDER_LIMIT in case NO_VOLUMETRICS is enabled | ||||
|  */ | ||||
| #ifndef DEFAULT_VOLUMETRIC_EXTRUDER_LIMIT | ||||
|   #define DEFAULT_VOLUMETRIC_EXTRUDER_LIMIT 0.00 | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * LCD Contrast for Graphical Displays | ||||
|  */ | ||||
|   | ||||
| @@ -1479,6 +1479,17 @@ static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Volumetric Extruder Limit | ||||
|  */ | ||||
| #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|   #if ENABLED(NO_VOLUMETRICS) | ||||
|     #error "VOLUMETRIC_EXTRUDER_LIMIT requires NO_VOLUMETRICS to be disabled." | ||||
|   #elif MIN_STEPS_PER_SEGMENT > 1 | ||||
|     #error "VOLUMETRIC_EXTRUDER_LIMIT is not compatible with MIN_STEPS_PER_SEGMENT greater than 1." | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * ULTIPANEL encoder | ||||
|  */ | ||||
|   | ||||
| @@ -317,6 +317,8 @@ namespace Language_en { | ||||
|   PROGMEM Language_Str MSG_MOTION                          = _UxGT("Motion"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT                        = _UxGT("Filament"); | ||||
|   PROGMEM Language_Str MSG_VOLUMETRIC_ENABLED              = _UxGT("E in mm³"); | ||||
|   PROGMEM Language_Str MSG_VOLUMETRIC_LIMIT                = _UxGT("E Limit in mm³"); | ||||
|   PROGMEM Language_Str MSG_VOLUMETRIC_LIMIT_E              = _UxGT("E Limit *"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_DIAM                   = _UxGT("Fil. Dia."); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_DIAM_E                 = _UxGT("Fil. Dia. *"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_UNLOAD                 = _UxGT("Unload mm"); | ||||
|   | ||||
| @@ -117,6 +117,14 @@ void menu_cancelobject(); | ||||
|     #if DISABLED(NO_VOLUMETRICS) | ||||
|       EDIT_ITEM(bool, MSG_VOLUMETRIC_ENABLED, &parser.volumetric_enabled, planner.calculate_volumetric_multipliers); | ||||
|  | ||||
|       #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|         EDIT_ITEM_FAST(float42_52, MSG_VOLUMETRIC_LIMIT, &planner.volumetric_extruder_limit[active_extruder], 0.0f, 20.0f, planner.calculate_volumetric_extruder_limits); | ||||
|         #if EXTRUDERS > 1 | ||||
|           LOOP_L_N(n, EXTRUDERS) | ||||
|             EDIT_ITEM_FAST_N(float42_52, n, MSG_VOLUMETRIC_LIMIT_E, &planner.volumetric_extruder_limit[n], 0.0f, 20.00f, planner.calculate_volumetric_extruder_limits); | ||||
|         #endif | ||||
|       #endif | ||||
|  | ||||
|       if (parser.volumetric_enabled) { | ||||
|         EDIT_ITEM_FAST(float43, MSG_FILAMENT_DIAM, &planner.filament_size[active_extruder], 1.5f, 3.25f, planner.calculate_volumetric_multipliers); | ||||
|         #if EXTRUDERS > 1 | ||||
|   | ||||
| @@ -37,7 +37,7 @@ | ||||
|  */ | ||||
|  | ||||
| // Change EEPROM version if the structure changes | ||||
| #define EEPROM_VERSION "V79" | ||||
| #define EEPROM_VERSION "V80" | ||||
| #define EEPROM_OFFSET 100 | ||||
|  | ||||
| // Check the integrity of data offsets. | ||||
| @@ -320,8 +320,9 @@ typedef struct SettingsDataStruct { | ||||
|   // | ||||
|   // !NO_VOLUMETRIC | ||||
|   // | ||||
|   bool parser_volumetric_enabled;                       // M200 D  parser.volumetric_enabled | ||||
|   bool parser_volumetric_enabled;                       // M200 S  parser.volumetric_enabled | ||||
|   float planner_filament_size[EXTRUDERS];               // M200 T D  planner.filament_size[] | ||||
|   float planner_volumetric_extruder_limit[EXTRUDERS];   // M200 T L  planner.volumetric_extruder_limit[] | ||||
|  | ||||
|   // | ||||
|   // HAS_TRINAMIC_CONFIG | ||||
| @@ -935,12 +936,20 @@ void MarlinSettings::postprocess() { | ||||
|  | ||||
|         EEPROM_WRITE(parser.volumetric_enabled); | ||||
|         EEPROM_WRITE(planner.filament_size); | ||||
|         #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|           EEPROM_WRITE(planner.volumetric_extruder_limit); | ||||
|         #else | ||||
|           dummyf = DEFAULT_VOLUMETRIC_EXTRUDER_LIMIT; | ||||
|           for (uint8_t q = EXTRUDERS; q--;) EEPROM_WRITE(dummyf); | ||||
|         #endif | ||||
|  | ||||
|       #else | ||||
|  | ||||
|         const bool volumetric_enabled = false; | ||||
|         dummyf = DEFAULT_NOMINAL_FILAMENT_DIA; | ||||
|         EEPROM_WRITE(volumetric_enabled); | ||||
|         dummyf = DEFAULT_NOMINAL_FILAMENT_DIA; | ||||
|         for (uint8_t q = EXTRUDERS; q--;) EEPROM_WRITE(dummyf); | ||||
|         dummyf = DEFAULT_VOLUMETRIC_EXTRUDER_LIMIT; | ||||
|         for (uint8_t q = EXTRUDERS; q--;) EEPROM_WRITE(dummyf); | ||||
|  | ||||
|       #endif | ||||
| @@ -1787,6 +1796,9 @@ void MarlinSettings::postprocess() { | ||||
|         struct { | ||||
|           bool volumetric_enabled; | ||||
|           float filament_size[EXTRUDERS]; | ||||
|           #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|             float volumetric_extruder_limit[EXTRUDERS]; | ||||
|           #endif | ||||
|         } storage; | ||||
|  | ||||
|         _FIELD_TEST(parser_volumetric_enabled); | ||||
| @@ -1796,6 +1808,9 @@ void MarlinSettings::postprocess() { | ||||
|           if (!validating) { | ||||
|             parser.volumetric_enabled = storage.volumetric_enabled; | ||||
|             COPY(planner.filament_size, storage.filament_size); | ||||
|             #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|               COPY(planner.volumetric_extruder_limit, storage.volumetric_extruder_limit); | ||||
|             #endif | ||||
|           } | ||||
|         #endif | ||||
|       } | ||||
| @@ -2598,6 +2613,10 @@ void MarlinSettings::reset() { | ||||
|     parser.volumetric_enabled = ENABLED(VOLUMETRIC_DEFAULT_ON); | ||||
|     LOOP_L_N(q, COUNT(planner.filament_size)) | ||||
|       planner.filament_size[q] = DEFAULT_NOMINAL_FILAMENT_DIA; | ||||
|     #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|       LOOP_L_N(q, COUNT(planner.volumetric_extruder_limit)) | ||||
|         planner.volumetric_extruder_limit[q] = DEFAULT_VOLUMETRIC_EXTRUDER_LIMIT; | ||||
|     #endif | ||||
|   #endif | ||||
|  | ||||
|   endstops.enable_globally(ENABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)); | ||||
| @@ -2750,7 +2769,7 @@ void MarlinSettings::reset() { | ||||
|  | ||||
|     SERIAL_EOL(); | ||||
|  | ||||
|     #if DISABLED(NO_VOLUMETRICS) | ||||
|     #if EXTRUDERS && DISABLED(NO_VOLUMETRICS) | ||||
|  | ||||
|       /** | ||||
|        * Volumetric extrusion M200 | ||||
| @@ -2765,20 +2784,26 @@ void MarlinSettings::reset() { | ||||
|  | ||||
|       #if EXTRUDERS == 1 | ||||
|         CONFIG_ECHO_START(); | ||||
|         SERIAL_ECHOLNPAIR("  M200 D", LINEAR_UNIT(planner.filament_size[0])); | ||||
|       #elif EXTRUDERS | ||||
|         SERIAL_ECHOLNPAIR("  M200 S", int(parser.volumetric_enabled) | ||||
|                               , " D", LINEAR_UNIT(planner.filament_size[0]), | ||||
|                               #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|                                 , " L", LINEAR_UNIT(planner.volumetric_extruder_limit[0]) | ||||
|                               #endif | ||||
|                          ); | ||||
|       #else | ||||
|         LOOP_L_N(i, EXTRUDERS) { | ||||
|           CONFIG_ECHO_START(); | ||||
|           SERIAL_ECHOPGM("  M200"); | ||||
|           if (i) SERIAL_ECHOPAIR_P(SP_T_STR, int(i)); | ||||
|           SERIAL_ECHOLNPAIR(" D", LINEAR_UNIT(planner.filament_size[i])); | ||||
|           SERIAL_ECHOLNPAIR("  M200 T", int(i) | ||||
|                                 , " D", LINEAR_UNIT(planner.filament_size[i]) | ||||
|                                 #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|                                   , " L", LINEAR_UNIT(planner.volumetric_extruder_limit[i]) | ||||
|                                 #endif | ||||
|                            ); | ||||
|         } | ||||
|         CONFIG_ECHO_START(); | ||||
|         SERIAL_ECHOLNPAIR("  M200 S", int(parser.volumetric_enabled)); | ||||
|       #endif | ||||
|  | ||||
|       if (!parser.volumetric_enabled) | ||||
|         CONFIG_ECHO_MSG("  M200 D0"); | ||||
|  | ||||
|     #endif // !NO_VOLUMETRICS | ||||
|     #endif // EXTRUDERS && !NO_VOLUMETRICS | ||||
|  | ||||
|     CONFIG_ECHO_HEADING("Steps per unit:"); | ||||
|     report_M92(!forReplay); | ||||
|   | ||||
| @@ -171,6 +171,11 @@ float Planner::steps_to_mm[XYZE_N];             // (mm) Millimeters per step | ||||
|         Planner::volumetric_multiplier[EXTRUDERS];  // Reciprocal of cross-sectional area of filament (in mm^2). Pre-calculated to reduce computation in the planner | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|   float Planner::volumetric_extruder_limit[EXTRUDERS],          // max mm^3/sec the extruder is able to handle | ||||
|         Planner::volumetric_extruder_feedrate_limit[EXTRUDERS]; // pre calculated extruder feedrate limit based on volumetric_extruder_limit; pre-calculated to reduce computation in the planner | ||||
| #endif | ||||
|  | ||||
| #if HAS_LEVELING | ||||
|   bool Planner::leveling_active = false; // Flag that auto bed leveling is enabled | ||||
|   #if ABL_PLANAR | ||||
| @@ -1407,10 +1412,28 @@ void Planner::check_axes_activity() { | ||||
|       volumetric_multiplier[i] = calculate_volumetric_multiplier(filament_size[i]); | ||||
|       refresh_e_factor(i); | ||||
|     } | ||||
|     #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|       calculate_volumetric_extruder_limits(); // update volumetric_extruder_limits as well. | ||||
|     #endif | ||||
|   } | ||||
|  | ||||
| #endif // !NO_VOLUMETRICS | ||||
|  | ||||
| #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|  | ||||
|   /** | ||||
|    * Convert volumetric based limits into pre calculated extruder feedrate limits. | ||||
|    */ | ||||
|   void Planner::calculate_volumetric_extruder_limit(const uint8_t e) { | ||||
|     const float &lim = volumetric_extruder_limit[e], &siz = filament_size[e]; | ||||
|     volumetric_extruder_feedrate_limit[e] = (lim && siz) ? lim / CIRCLE_AREA(siz * 0.5f) : 0; | ||||
|   } | ||||
|   void Planner::calculate_volumetric_extruder_limits() { | ||||
|     LOOP_L_N(e, EXTRUDERS) calculate_volumetric_extruder_limit(e); | ||||
|   } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(FILAMENT_WIDTH_SENSOR) | ||||
|   /** | ||||
|    * Convert the ratio value given by the filament width sensor | ||||
| @@ -2077,10 +2100,33 @@ bool Planner::_populate_block(block_t * const block, bool split_move, | ||||
|         if (mixer.get_current_vtool() == MIXER_AUTORETRACT_TOOL) | ||||
|           current_speed.e *= MIXING_STEPPERS; | ||||
|       #endif | ||||
|  | ||||
|       const feedRate_t cs = ABS(current_speed.e), | ||||
|                    max_fr = settings.max_feedrate_mm_s[E_AXIS_N(extruder)] | ||||
|                             * TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1); | ||||
|       if (cs > max_fr) NOMORE(speed_factor, max_fr / cs); | ||||
|  | ||||
|       if (cs > max_fr) NOMORE(speed_factor, max_fr / cs); //respect max feedrate on any movement (doesn't matter if E axes only or not) | ||||
|        | ||||
|       #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|         const feedRate_t max_vfr = volumetric_extruder_feedrate_limit[extruder]  | ||||
|                                    * TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1); | ||||
|  | ||||
|         // TODO: Doesn't work properly for joined segments. Set MIN_STEPS_PER_SEGMENT 1 as workaround. | ||||
|  | ||||
|         if (block->steps.a || block->steps.b || block->steps.c) { | ||||
|  | ||||
|           if (max_vfr > 0 && cs > max_vfr) { | ||||
|             NOMORE(speed_factor, max_vfr / cs); // respect volumetric extruder limit (if any) | ||||
|             /* <-- add a slash to enable | ||||
|             SERIAL_ECHOPAIR("volumetric extruder limit enforced: ", (cs * CIRCLE_AREA(filament_size[extruder] * 0.5f))); | ||||
|             SERIAL_ECHOPAIR(" mm^3/s (", cs); | ||||
|             SERIAL_ECHOPAIR(" mm/s) limited to ", (max_vfr * CIRCLE_AREA(filament_size[extruder] * 0.5f))); | ||||
|             SERIAL_ECHOPAIR(" mm^3/s (", max_vfr); | ||||
|             SERIAL_ECHOLNPGM(" mm/s)"); | ||||
|             //*/ | ||||
|           } | ||||
|         } | ||||
|       #endif | ||||
|     } | ||||
|   #endif | ||||
|  | ||||
|   | ||||
| @@ -333,6 +333,11 @@ class Planner { | ||||
|                                                       // May be auto-adjusted by a filament width sensor | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|       static float volumetric_extruder_limit[EXTRUDERS],          // Maximum mm^3/sec the extruder can handle | ||||
|                    volumetric_extruder_feedrate_limit[EXTRUDERS]; // Feedrate limit (mm/s) calculated from volume limit | ||||
|     #endif | ||||
|  | ||||
|     static planner_settings_t settings; | ||||
|  | ||||
|     #if ENABLED(LASER_POWER_INLINE) | ||||
| @@ -473,9 +478,6 @@ class Planner { | ||||
|     // Manage fans, paste pressure, etc. | ||||
|     static void check_axes_activity(); | ||||
|  | ||||
|     // Update multipliers based on new diameter measurements | ||||
|     static void calculate_volumetric_multipliers(); | ||||
|  | ||||
|     #if ENABLED(FILAMENT_WIDTH_SENSOR) | ||||
|       void apply_filament_width_sensor(const int8_t encoded_ratio); | ||||
|  | ||||
| @@ -489,8 +491,18 @@ class Planner { | ||||
|  | ||||
|     #if DISABLED(NO_VOLUMETRICS) | ||||
|  | ||||
|       // Update multipliers based on new diameter measurements | ||||
|       static void calculate_volumetric_multipliers(); | ||||
|    | ||||
|       #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|         // Update pre calculated extruder feedrate limits based on volumetric values | ||||
|         static void calculate_volumetric_extruder_limit(const uint8_t e); | ||||
|         static void calculate_volumetric_extruder_limits(); | ||||
|       #endif | ||||
|  | ||||
|       FORCE_INLINE static void set_filament_size(const uint8_t e, const float &v) { | ||||
|         filament_size[e] = v; | ||||
|         if (v > 0) volumetric_area_nominal = CIRCLE_AREA(v * 0.5); //TODO: should it be per extruder | ||||
|         // make sure all extruders have some sane value for the filament size | ||||
|         LOOP_L_N(i, COUNT(filament_size)) | ||||
|           if (!filament_size[i]) filament_size[i] = DEFAULT_NOMINAL_FILAMENT_DIA; | ||||
| @@ -498,6 +510,13 @@ class Planner { | ||||
|  | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) | ||||
|       FORCE_INLINE static void set_volumetric_extruder_limit(const uint8_t e, const float &v) { | ||||
|         volumetric_extruder_limit[e] = v; | ||||
|         calculate_volumetric_extruder_limit(e); | ||||
|       } | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) | ||||
|  | ||||
|       /** | ||||
|   | ||||
		Reference in New Issue
	
	Block a user