From f56968ba0b8d4eaccbc9b0c7834cf0cd2f5872d8 Mon Sep 17 00:00:00 2001 From: AnHardt Date: Tue, 16 Oct 2018 10:38:57 +0200 Subject: [PATCH] New Continuous Filament Mixer (#12098) --- Marlin/src/Marlin.cpp | 4 +- Marlin/src/feature/fwretract.cpp | 9 +- Marlin/src/feature/mixing.cpp | 137 +++++++++-------- Marlin/src/feature/mixing.h | 89 +++++++++-- Marlin/src/gcode/calibrate/G33.cpp | 2 +- Marlin/src/gcode/feature/mixing/M163-M165.cpp | 51 +++++-- Marlin/src/gcode/gcode.cpp | 10 +- Marlin/src/gcode/gcode.h | 8 +- Marlin/src/inc/SanityCheck.h | 2 - Marlin/src/module/planner.cpp | 21 ++- Marlin/src/module/planner.h | 8 +- Marlin/src/module/stepper.cpp | 139 +++++++----------- Marlin/src/module/stepper.h | 27 ++-- Marlin/src/module/tool_change.cpp | 25 ++-- 14 files changed, 296 insertions(+), 236 deletions(-) diff --git a/Marlin/src/Marlin.cpp b/Marlin/src/Marlin.cpp index a78cdf2a6e..33b683d739 100644 --- a/Marlin/src/Marlin.cpp +++ b/Marlin/src/Marlin.cpp @@ -905,8 +905,8 @@ void setup() { lcd_bootscreen(); #endif - #if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1 - mixing_tools_init(); + #if ENABLED(MIXING_EXTRUDER) + mixer.init(); #endif #if ENABLED(BLTOUCH) diff --git a/Marlin/src/feature/fwretract.cpp b/Marlin/src/feature/fwretract.cpp index 7cb29a970b..b272bdeda9 100644 --- a/Marlin/src/feature/fwretract.cpp +++ b/Marlin/src/feature/fwretract.cpp @@ -142,11 +142,8 @@ void FWRetract::retract(const bool retracting set_destination_from_current(); #if ENABLED(RETRACT_SYNC_MIXING) - float old_mixing_factor[MIXING_STEPPERS]; - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) { - old_mixing_factor[i] = mixing_factor[i]; - mixing_factor[i] = RECIPROCAL(MIXING_STEPPERS); - } + uint8_t old_mixing_tool = mixer.get_current_v_tool(); + mixer.T(MIXER_AUTORETRACT_TOOL); #endif if (retracting) { @@ -196,7 +193,7 @@ void FWRetract::retract(const bool retracting } #if ENABLED(RETRACT_SYNC_MIXING) - COPY(mixing_factor, old_mixing_factor); // Restore original mixing factor + mixer.T(old_mixing_tool); // Restore original mixing tool #endif feedrate_mm_s = old_feedrate_mm_s; // Restore original feedrate diff --git a/Marlin/src/feature/mixing.cpp b/Marlin/src/feature/mixing.cpp index 37a6bdc5e7..0713dca56f 100644 --- a/Marlin/src/feature/mixing.cpp +++ b/Marlin/src/feature/mixing.cpp @@ -24,81 +24,90 @@ #if ENABLED(MIXING_EXTRUDER) -#if ENABLED(DIRECT_MIXING_IN_G1) - #include "../gcode/parser.h" +//#define MIXER_NORMALIZER_DEBUG +#ifdef MIXER_NORMALIZER_DEBUG + #include "../core/serial.h" #endif -float mixing_factor[MIXING_STEPPERS]; // Reciprocal of mix proportion. 0.0 = off, otherwise <= 1.0. (Array must sum to 1.0.) +#include "mixing.h" -#if MIXING_VIRTUAL_TOOLS > 1 +Mixer mixer; - float mixing_virtual_tool_mix[MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS]; +// Used up to Planner level +uint_fast8_t Mixer::selected_v_tool = 0; +float Mixer::M163_collector[MIXING_STEPPERS]; // mix proportion. 0.0 = off, otherwise <= COLOR_A_MASK. +mixer_color_t Mixer::color[NR_MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS]; - void mixing_tools_init() { - // Virtual Tools 0, 1, 2, 3 = Filament 1, 2, 3, 4, etc. - for (uint8_t t = 0; t < MIXING_VIRTUAL_TOOLS && t < MIXING_STEPPERS; t++) - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) - mixing_virtual_tool_mix[t][i] = (t == i) ? 1.0 : 0.0; +// Used in Stepper +int_fast8_t Mixer::runner = 0; +mixer_color_t Mixer::s_color[MIXING_STEPPERS]; +mixer_accu_t Mixer::accu[MIXING_STEPPERS] = { 0 }; - // Remaining virtual tools are 100% filament 1 - #if MIXING_STEPPERS < MIXING_VIRTUAL_TOOLS - for (uint8_t t = MIXING_STEPPERS; t < MIXING_VIRTUAL_TOOLS; t++) - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) - mixing_virtual_tool_mix[t][i] = (i == 0) ? 1.0 : 0.0; +void Mixer::normalize(const uint8_t tool_index) { + float cmax = 0; + #ifdef MIXER_NORMALIZER_DEBUG + float csum = 0; + #endif + MIXER_STEPPER_LOOP(i) { + cmax = max(cmax, M163_collector[i]); + #ifdef MIXER_NORMALIZER_DEBUG + csum += M163_collector[i]; #endif - - // Initialize mixing to tool 0 color - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) - mixing_factor[i] = mixing_virtual_tool_mix[0][i]; } + #ifdef MIXER_NORMALIZER_DEBUG + SERIAL_ECHOPGM("Mixer: Relation before normalizing: [ "); + MIXER_STEPPER_LOOP(i) { + SERIAL_ECHO_F(M163_collector[i] / csum, 3); + SERIAL_CHAR(' '); + } + SERIAL_ECHOPGM("]\n"); + #endif -#endif // MIXING_VIRTUAL_TOOLS > 1 + // Scale all values so their maximum is COLOR_A_MASK + const float inverse_max = RECIPROCAL(cmax); + MIXER_STEPPER_LOOP(i) + color[tool_index][i] = M163_collector[i] * COLOR_A_MASK * inverse_max; -void normalize_mix() { - float mix_total = 0.0; - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) mix_total += mixing_factor[i]; - // Scale all values if they don't add up to ~1.0 - if (!NEAR(mix_total, 1.0)) { - SERIAL_PROTOCOLLNPGM("Warning: Mix factors must add up to 1.0. Scaling."); - const float inverse_sum = RECIPROCAL(mix_total); - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) mixing_factor[i] *= inverse_sum; - } + #ifdef MIXER_NORMALIZER_DEBUG + csum = 0; + SERIAL_ECHOPGM("Mixer: Normalizing to : [ "); + MIXER_STEPPER_LOOP(i) { + SERIAL_ECHO(uint16_t(color[tool_index][i])); + SERIAL_CHAR(' '); + csum += color[tool_index][i]; + } + SERIAL_ECHOLNPGM("]"); + SERIAL_ECHOPGM("Mixer: Relation after normalizing: [ "); + MIXER_STEPPER_LOOP(i) { + SERIAL_ECHO_F(uint16_t(color[tool_index][i]) / csum, 3); + SERIAL_CHAR(' '); + } + SERIAL_ECHOLNPGM("]"); + #endif } -#if ENABLED(DIRECT_MIXING_IN_G1) - // Get mixing parameters from the GCode - // The total "must" be 1.0 (but it will be normalized) - // If no mix factors are given, the old mix is preserved - void gcode_get_mix() { - const char mixing_codes[] = { 'A', 'B' - #if MIXING_STEPPERS > 2 - , 'C' - #if MIXING_STEPPERS > 3 - , 'D' - #if MIXING_STEPPERS > 4 - , 'H' - #if MIXING_STEPPERS > 5 - , 'I' - #endif // MIXING_STEPPERS > 5 - #endif // MIXING_STEPPERS > 4 - #endif // MIXING_STEPPERS > 3 - #endif // MIXING_STEPPERS > 2 - }; - byte mix_bits = 0; - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) { - if (parser.seenval(mixing_codes[i])) { - SBI(mix_bits, i); - mixing_factor[i] = MAX(parser.value_float(), 0.0); - } - } - // If any mixing factors were included, clear the rest - // If none were included, preserve the last mix - if (mix_bits) { - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) - if (!TEST(mix_bits, i)) mixing_factor[i] = 0.0; - normalize_mix(); - } - } -#endif +// called at boot +void Mixer::init( void ) { + // Virtual Tools 0, 1, 2, 3 = Filament 1, 2, 3, 4, etc. + // Every virtual tool gets a pure filament + for (uint8_t t = 0; t < MIXING_VIRTUAL_TOOLS && t < MIXING_STEPPERS; t++) + MIXER_STEPPER_LOOP(i) + color[t][i] = (t == i) ? COLOR_A_MASK : 0; + + // Remaining virtual tools are 100% filament 1 + #if MIXING_STEPPERS < MIXING_VIRTUAL_TOOLS + for (uint8_t t = MIXING_STEPPERS; t < MIXING_VIRTUAL_TOOLS; t++) + MIXER_STEPPER_LOOP(i) + color[t][i] = (i == 0) ? COLOR_A_MASK : 0; + #endif + + #if ENABLED(RETRACT_SYNC_MIXING) + // AUTORETRACT_TOOL gets the same amount of all filaments + MIXER_STEPPER_LOOP(i) + color[MIXER_AUTORETRACT_TOOL][i] = COLOR_A_MASK; + #endif + + ZERO(M163_collector); +} #endif // MIXING_EXTRUDER diff --git a/Marlin/src/feature/mixing.h b/Marlin/src/feature/mixing.h index fff240c0e0..1a675e4d79 100644 --- a/Marlin/src/feature/mixing.h +++ b/Marlin/src/feature/mixing.h @@ -19,23 +19,88 @@ * along with this program. If not, see . * */ - -#ifndef __MIXING_H__ -#define __MIXING_H__ +#pragma once #include "../inc/MarlinConfig.h" -extern float mixing_factor[MIXING_STEPPERS]; // Reciprocal of mix proportion. 0.0 = off, otherwise >= 1.0. - -#if MIXING_VIRTUAL_TOOLS > 1 - extern float mixing_virtual_tool_mix[MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS]; - void mixing_tools_init(); +#ifdef __AVR__ + #define MIXER_ACCU_SIGNED + typedef uint8_t mixer_color_t; + typedef int8_t mixer_accu_t; +#else + typedef uint_fast16_t mixer_color_t; + typedef uint_fast16_t mixer_accu_t; #endif -void normalize_mix(); +#define COLOR_A_MASK _BV(sizeof(mixer_color_t) * 8 - 1) // 0x80 or 0x8000 +#define COLOR_MASK (COLOR_A_MASK - 1) // 0x7F or 0x7FFF -#if ENABLED(DIRECT_MIXING_IN_G1) - void gcode_get_mix(); +#ifndef MIXING_VIRTUAL_TOOLS + #define MIXING_VIRTUAL_TOOLS 1 #endif -#endif // __MIXING_H__ +#ifdef RETRACT_SYNC_MIXING + #define NR_MIXING_VIRTUAL_TOOLS (MIXING_VIRTUAL_TOOLS + 1) + #define MIXER_AUTORETRACT_TOOL MIXING_VIRTUAL_TOOLS +#else + #define NR_MIXING_VIRTUAL_TOOLS (MIXING_VIRTUAL_TOOLS) +#endif + +#define MIXER_STEPPER_LOOP(VAR) \ + for (uint_fast8_t VAR = 0; VAR < MIXING_STEPPERS; VAR++) + +#define MIXER_BLOCK_DEFINITION mixer_color_t b_color[MIXING_STEPPERS] +#define MIXER_POPULATE_BLOCK() mixer.populate_block(block->b_color) +#define MIXER_STEPPER_SETUP() mixer.stepper_setup(current_block->b_color) + +class Mixer { + public: + + static void init(void); // Populate colors at boot time + + // Used up to Planner level + static void normalize(const uint8_t tool_index); + FORCE_INLINE static uint8_t get_current_v_tool(void) { return selected_v_tool; } + FORCE_INLINE static void T(const uint_fast8_t c) { selected_v_tool = c; } + FORCE_INLINE static void set_M163_collector(const uint8_t c, const float f) { M163_collector[c] = f; } + + // Used when dealing with blocks + FORCE_INLINE static void populate_block(mixer_color_t b_color[]) { + uint_fast8_t j = get_current_v_tool(); + MIXER_STEPPER_LOOP(i) b_color[i] = color[j][i]; + } + FORCE_INLINE static void stepper_setup(mixer_color_t b_color[]) { MIXER_STEPPER_LOOP(i) s_color[i] = b_color[i]; } + + // Used in Stepper + FORCE_INLINE static uint8_t get_stepper(void) { return runner; } + FORCE_INLINE static uint8_t get_next_stepper(void) { + do { + if (--runner < 0) runner = MIXING_STEPPERS - 1; + accu[runner] += s_color[runner]; + if ( + #ifdef MIXER_ACCU_SIGNED + accu[runner] < 0 + #else + accu[runner] & COLOR_A_MASK + #endif + ) { + accu[runner] &= COLOR_MASK; + return runner; + } + } while( true ); + } + + private: + + // Used up to Planner level + static uint_fast8_t selected_v_tool; + static float M163_collector[MIXING_STEPPERS]; + static mixer_color_t color[NR_MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS]; + + // Used in Stepper + static int_fast8_t runner; + static mixer_color_t s_color[MIXING_STEPPERS]; + static mixer_accu_t accu[MIXING_STEPPERS]; +}; + +extern Mixer mixer; diff --git a/Marlin/src/gcode/calibrate/G33.cpp b/Marlin/src/gcode/calibrate/G33.cpp index 047874a9f9..7ec76c1126 100644 --- a/Marlin/src/gcode/calibrate/G33.cpp +++ b/Marlin/src/gcode/calibrate/G33.cpp @@ -643,7 +643,7 @@ void GcodeSuite::G33() { if (verbose_level != 0) { // !dry run - // normalise angles to least squares + // Normalize angles to least-squares if (_angle_results) { float a_sum = 0.0; LOOP_XYZ(axis) a_sum += delta_tower_angle_trim[axis]; diff --git a/Marlin/src/gcode/feature/mixing/M163-M165.cpp b/Marlin/src/gcode/feature/mixing/M163-M165.cpp index 3fa478b834..478aa95a18 100644 --- a/Marlin/src/gcode/feature/mixing/M163-M165.cpp +++ b/Marlin/src/gcode/feature/mixing/M163-M165.cpp @@ -30,7 +30,7 @@ /** * M163: Set a single mix factor for a mixing extruder * This is called "weight" by some systems. - * The 'P' values must sum to 1.0 or must be followed by M164 to normalize them. + * Must be followed by M164 to normalize and commit them. * * S[index] The channel index to set * P[float] The mix value @@ -38,24 +38,23 @@ void GcodeSuite::M163() { const int mix_index = parser.intval('S'); if (mix_index < MIXING_STEPPERS) - mixing_factor[mix_index] = MAX(parser.floatval('P'), 0.0); + mixer.set_M163_collector(mix_index, MAX(parser.floatval('P'), 0.0)); } /** * M164: Normalize and commit the mix. - * If 'S' is given store as a virtual tool. (Requires MIXING_VIRTUAL_TOOLS > 1) + * If 'S' is given store as a virtual tool. Else in T0. * * S[index] The virtual tool to store */ void GcodeSuite::M164() { - normalize_mix(); #if MIXING_VIRTUAL_TOOLS > 1 const int tool_index = parser.intval('S', -1); - if (WITHIN(tool_index, 0, MIXING_VIRTUAL_TOOLS - 1)) { - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) - mixing_virtual_tool_mix[tool_index][i] = mixing_factor[i]; - } + #else + constexpr int tool_index = 0; #endif + if (WITHIN(tool_index, 0, MIXING_VIRTUAL_TOOLS - 1)) + mixer.normalize(tool_index); } #if ENABLED(DIRECT_MIXING_IN_G1) @@ -63,7 +62,7 @@ void GcodeSuite::M164() { /** * M165: Set multiple mix factors for a mixing extruder. * Factors that are left out will be set to 0. - * All factors should sum to 1.0, but they will be normalized regardless. + * All factors will be normalized and stored in the current v-tool. * * A[factor] Mix factor for extruder stepper 1 * B[factor] Mix factor for extruder stepper 2 @@ -72,7 +71,39 @@ void GcodeSuite::M164() { * H[factor] Mix factor for extruder stepper 5 * I[factor] Mix factor for extruder stepper 6 */ - void GcodeSuite::M165() { gcode_get_mix(); } + void GcodeSuite::M165() { + // Get mixing parameters from the GCode + // The total "must" be 1.0 (but it will be normalized) + // If no mix factors are given, the old mix is preserved + const char mixing_codes[] = { 'A', 'B' + #if MIXING_STEPPERS > 2 + , 'C' + #if MIXING_STEPPERS > 3 + , 'D' + #if MIXING_STEPPERS > 4 + , 'H' + #if MIXING_STEPPERS > 5 + , 'I' + #endif // MIXING_STEPPERS > 5 + #endif // MIXING_STEPPERS > 4 + #endif // MIXING_STEPPERS > 3 + #endif // MIXING_STEPPERS > 2 + }; + uint8_t mix_bits = 0; + MIXER_STEPPER_LOOP(i) { + if (parser.seenval(mixing_codes[i])) { + SBI(mix_bits, i); + mixer.set_M163_collector(i, MAX(parser.value_float(), 0.0f)); + } + } + // If any mixing factors were included, clear the rest + // If none were included, preserve the last mix + if (mix_bits) { + MIXER_STEPPER_LOOP(i) + if (!TEST(mix_bits, i)) mixer.set_M163_collector(i, 0.0f); + mixer.normalize(mixer.get_current_v_tool()); + } + } #endif // DIRECT_MIXING_IN_G1 diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 868a103436..0185676821 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -36,10 +36,6 @@ GcodeSuite gcode; #include "../module/printcounter.h" #endif -#if ENABLED(DIRECT_MIXING_IN_G1) - #include "../feature/mixing.h" -#endif - #include "../Marlin.h" // for idle() and suspend_auto_report uint8_t GcodeSuite::target_extruder; @@ -113,7 +109,7 @@ void GcodeSuite::get_destination_from_command() { // Get ABCDHI mixing factors #if ENABLED(MIXING_EXTRUDER) && ENABLED(DIRECT_MIXING_IN_G1) - gcode_get_mix(); + M165(); #endif } @@ -441,9 +437,7 @@ void GcodeSuite::process_parsed_command( #if ENABLED(MIXING_EXTRUDER) case 163: M163(); break; // M163: Set a component weight for mixing extruder - #if MIXING_VIRTUAL_TOOLS > 1 - case 164: M164(); break; // M164: Save current mix as a virtual extruder - #endif + case 164: M164(); break; // M164: Save current mix as a virtual extruder #if ENABLED(DIRECT_MIXING_IN_G1) case 165: M165(); break; // M165: Set multiple mix weights #endif diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index 81d560d905..fea79c2103 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -146,8 +146,8 @@ * M150 - Set Status LED Color as R U B P. Values 0-255. (Requires BLINKM, RGB_LED, RGBW_LED, NEOPIXEL_LED, or PCA9632). * M155 - Auto-report temperatures with interval of S. (Requires AUTO_REPORT_TEMPERATURES) * M163 - Set a single proportion for a mixing extruder. (Requires MIXING_EXTRUDER) - * M164 - Commit the mix (Req. MIXING_EXTRUDER) and optionally save as a virtual tool (Req. MIXING_VIRTUAL_TOOLS > 1) - * M165 - Set the mix for a mixing extruder wuth parameters ABCDHI. (Requires MIXING_EXTRUDER and DIRECT_MIXING_IN_G1) + * M164 - Commit the mix (Req. MIXING_EXTRUDER) and optionally save as a virtual tool (Requires MIXING_EXTRUDER) + * M165 - Set the mix for a mixing extruder with parameters ABCDHI. (Requires MIXING_EXTRUDER and DIRECT_MIXING_IN_G1) * M190 - Sxxx Wait for bed current temp to reach target temp. ** Waits only when heating! ** * Rxxx Wait for bed current temp to reach target temp. ** Waits for heating or cooling. ** * M200 - Set filament diameter, D, setting E axis units to cubic. (Use S0 to revert to linear units.) @@ -581,9 +581,7 @@ private: #if ENABLED(MIXING_EXTRUDER) static void M163(); - #if MIXING_VIRTUAL_TOOLS > 1 - static void M164(); - #endif + static void M164(); #if ENABLED(DIRECT_MIXING_IN_G1) static void M165(); #endif diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 0f16ab6a9e..67db332521 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -705,8 +705,6 @@ static_assert(X_MAX_LENGTH >= X_BED_SIZE && Y_MAX_LENGTH >= Y_BED_SIZE, #error "Please select either MIXING_EXTRUDER or SWITCHING_EXTRUDER, not both." #elif ENABLED(SINGLENOZZLE) #error "MIXING_EXTRUDER is incompatible with SINGLENOZZLE." - #elif ENABLED(LIN_ADVANCE) - #error "MIXING_EXTRUDER is incompatible with LIN_ADVANCE." #endif #endif diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 7d1f30f5d8..68302a2aa8 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -1749,10 +1749,8 @@ bool Planner::_populate_block(block_t * const block, bool split_move, // Bail if this is a zero-length block if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return false; - // For a mixing extruder, get a magnified esteps for each #if ENABLED(MIXING_EXTRUDER) - for (uint8_t i = 0; i < MIXING_STEPPERS; i++) - block->mix_steps[i] = mixing_factor[i] * esteps; + MIXER_POPULATE_BLOCK(); #endif #if FAN_COUNT > 0 @@ -1765,7 +1763,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, #endif #if EXTRUDERS > 1 - block->active_extruder = extruder; + block->extruder = extruder; #endif #if ENABLED(AUTO_POWER_CONTROL) @@ -2066,15 +2064,14 @@ bool Planner::_populate_block(block_t * const block, bool split_move, // Calculate and limit speed in mm/sec for each axis float current_speed[NUM_AXIS], speed_factor = 1.0f; // factor <1 decreases speed LOOP_XYZE(i) { - #if ENABLED(MIXING_EXTRUDER) + #if ENABLED(MIXING_EXTRUDER) && ENABLED(RETRACT_SYNC_MIXING) + // In worst case, only one extruder running, no change is needed. + // In best case, all extruders run the same amount, we can divide by MIXING_STEPPERS float delta_mm_i = 0; - if (i == E_AXIS) { - for (uint8_t s = 0; s < MIXING_STEPPERS; s++) { - const float delta_mm_s = mixing_factor[s] * delta_mm[i]; - if (ABS(delta_mm_s) > ABS(delta_mm_i)) delta_mm_i = delta_mm_s; - } - } - else delta_mm_i = delta_mm[i]; + if (i == E_AXIS && mixer.get_current_v_tool() == MIXER_AUTORETRACT_TOOL) + delta_mm_i = delta_mm[i] / MIXING_STEPPERS; + else + delta_mm_i = delta_mm[i]; #else const float delta_mm_i = delta_mm[i]; #endif diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index d3a0b1ee96..791ebe31f3 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -47,6 +47,10 @@ #include "../feature/fwretract.h" #endif +#if ENABLED(MIXING_EXTRUDER) + #include "../feature/mixing.h" +#endif + enum BlockFlagBit : char { // Recalculate trapezoids on entry junction. For optimization. BLOCK_BIT_RECALCULATE, @@ -104,11 +108,11 @@ typedef struct { uint32_t step_event_count; // The number of step events required to complete this block #if EXTRUDERS > 1 - uint8_t active_extruder; // The extruder to move (if E move) + uint8_t extruder; // The extruder to move (if E move) #endif #if ENABLED(MIXING_EXTRUDER) - uint32_t mix_steps[MIXING_STEPPERS]; // Scaled steps[E_AXIS] for the mixing steppers + MIXER_BLOCK_DEFINITION; // Normalized color for the mixing steppers #endif // Settings for the trapezoid generator diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index e080bfb347..a49b6173fc 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -103,6 +103,10 @@ #include #endif +#if ENABLED(MIXING_EXTRUDER) + #include "../feature/mixing.h" +#endif + Stepper stepper; // Singleton // public: @@ -158,12 +162,10 @@ uint32_t Stepper::advance_dividend[XYZE] = { 0 }, Stepper::decelerate_after, // The point from where we need to start decelerating Stepper::step_event_count; // The total event count for the current block -#if ENABLED(MIXING_EXTRUDER) - int32_t Stepper::delta_error_m[MIXING_STEPPERS]; - uint32_t Stepper::advance_dividend_m[MIXING_STEPPERS], - Stepper::advance_divisor_m; -#elif EXTRUDERS > 1 - uint8_t Stepper::active_extruder; // Active extruder +#if EXTRUDERS > 1 || ENABLED(MIXING_EXTRUDER) + uint8_t Stepper::stepper_extruder; +#else + constexpr uint8_t Stepper::stepper_extruder; #endif #if ENABLED(S_CURVE_ACCELERATION) @@ -301,7 +303,7 @@ int8_t Stepper::count_direction[NUM_AXIS] = { 0, 0, 0, 0 }; #endif #if DISABLED(MIXING_EXTRUDER) - #define E_APPLY_STEP(v,Q) E_STEP_WRITE(active_extruder, v) + #define E_APPLY_STEP(v,Q) E_STEP_WRITE(stepper_extruder, v) #endif void Stepper::wake_up() { @@ -340,21 +342,23 @@ void Stepper::set_directions() { #if DISABLED(LIN_ADVANCE) #if ENABLED(MIXING_EXTRUDER) + // Because this is valid for the whole block we don't know + // what e-steppers will step. Likely all. Set all. if (motor_direction(E_AXIS)) { - MIXING_STEPPERS_LOOP(j) REV_E_DIR(j); + MIXER_STEPPER_LOOP(j) REV_E_DIR(j); count_direction[E_AXIS] = -1; } else { - MIXING_STEPPERS_LOOP(j) NORM_E_DIR(j); + MIXER_STEPPER_LOOP(j) NORM_E_DIR(j); count_direction[E_AXIS] = 1; } #else if (motor_direction(E_AXIS)) { - REV_E_DIR(active_extruder); + REV_E_DIR(stepper_extruder); count_direction[E_AXIS] = -1; } else { - NORM_E_DIR(active_extruder); + NORM_E_DIR(stepper_extruder); count_direction[E_AXIS] = 1; } #endif @@ -1387,39 +1391,27 @@ void Stepper::stepper_pulse_phase_isr() { PULSE_START(Z); #endif - // Pulse E/Mixing extruders - #if ENABLED(LIN_ADVANCE) - // Tick the E axis, correct error term and update position + // Pulse Extruders + // Tick the E axis, correct error term and update position + #if ENABLED(LIN_ADVANCE) || ENABLED(MIXING_EXTRUDER) delta_error[E_AXIS] += advance_dividend[E_AXIS]; if (delta_error[E_AXIS] >= 0) { count_position[E_AXIS] += count_direction[E_AXIS]; - delta_error[E_AXIS] -= advance_divisor; - - // Don't step E here - But remember the number of steps to perform - motor_direction(E_AXIS) ? --LA_steps : ++LA_steps; - } - #else // !LIN_ADVANCE - use linear interpolation for E also - #if ENABLED(MIXING_EXTRUDER) - - // Tick the E axis - delta_error[E_AXIS] += advance_dividend[E_AXIS]; - if (delta_error[E_AXIS] >= 0) { - count_position[E_AXIS] += count_direction[E_AXIS]; + #if ENABLED(LIN_ADVANCE) delta_error[E_AXIS] -= advance_divisor; - } - - // Tick the counters used for this mix in proper proportion - MIXING_STEPPERS_LOOP(j) { - // Step mixing steppers (proportionally) - delta_error_m[j] += advance_dividend_m[j]; - // Step when the counter goes over zero - if (delta_error_m[j] >= 0) E_STEP_WRITE(j, !INVERT_E_STEP_PIN); - } - - #else // !MIXING_EXTRUDER + // Don't step E here - But remember the number of steps to perform + motor_direction(E_AXIS) ? --LA_steps : ++LA_steps; + #else // !LIN_ADVANCE && MIXING_EXTRUDER + // Don't adjust delta_error[E_AXIS] here! + // Being positive is the criteria for ending the pulse. + E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN); + #endif + } + #else // !LIN_ADVANCE && !MIXING_EXTRUDER + #if HAS_E_STEP PULSE_START(E); #endif - #endif // !LIN_ADVANCE + #endif #if MINIMUM_STEPPER_PULSE // Just wait for the requested pulse duration @@ -1442,11 +1434,9 @@ void Stepper::stepper_pulse_phase_isr() { #if DISABLED(LIN_ADVANCE) #if ENABLED(MIXING_EXTRUDER) - MIXING_STEPPERS_LOOP(j) { - if (delta_error_m[j] >= 0) { - delta_error_m[j] -= advance_divisor_m; - E_STEP_WRITE(j, INVERT_E_STEP_PIN); - } + if (delta_error[E_AXIS] >= 0) { + delta_error[E_AXIS] -= advance_divisor; + E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN); } #else // !MIXING_EXTRUDER PULSE_STOP(E); @@ -1717,27 +1707,18 @@ uint32_t Stepper::stepper_block_phase_isr() { decelerate_after = current_block->decelerate_after << oversampling; #if ENABLED(MIXING_EXTRUDER) - const uint32_t e_steps = ( - #if ENABLED(LIN_ADVANCE) - current_block->steps[E_AXIS] - #else - step_event_count - #endif - ); - MIXING_STEPPERS_LOOP(i) { - delta_error_m[i] = -int32_t(e_steps); - advance_dividend_m[i] = current_block->mix_steps[i] << 1; - } - advance_divisor_m = e_steps << 1; - #elif EXTRUDERS > 1 - active_extruder = current_block->active_extruder; + MIXER_STEPPER_SETUP(); + #endif + + #if EXTRUDERS > 1 + stepper_extruder = current_block->extruder; #endif // Initialize the trapezoid generator from the current block. #if ENABLED(LIN_ADVANCE) #if DISABLED(MIXING_EXTRUDER) && E_STEPPERS > 1 // If the now active extruder wasn't in use during the last move, its pressure is most likely gone. - if (active_extruder != last_moved_extruder) LA_current_adv_steps = 0; + if (stepper_extruder != last_moved_extruder) LA_current_adv_steps = 0; #endif if ((LA_use_advance_lead = current_block->use_advance_lead)) { @@ -1751,15 +1732,15 @@ uint32_t Stepper::stepper_block_phase_isr() { #endif if (current_block->direction_bits != last_direction_bits - #if DISABLED(MIXING_EXTRUDER) - || active_extruder != last_moved_extruder - #endif + #if DISABLED(MIXING_EXTRUDER) + || stepper_extruder != last_moved_extruder + #endif ) { last_direction_bits = current_block->direction_bits; - #if DISABLED(MIXING_EXTRUDER) && EXTRUDERS > 1 - last_moved_extruder = active_extruder; - #endif set_directions(); + #if EXTRUDERS > 1 + last_moved_extruder = stepper_extruder; + #endif } // At this point, we must ensure the movement about to execute isn't @@ -1827,15 +1808,17 @@ uint32_t Stepper::stepper_block_phase_isr() { interval = LA_ADV_NEVER; #if ENABLED(MIXING_EXTRUDER) + // We don't know which steppers will be stepped because LA loop follows, + // with potentially multiple steps. Set all. if (LA_steps >= 0) - MIXING_STEPPERS_LOOP(j) NORM_E_DIR(j); + MIXER_STEPPER_LOOP(j) NORM_E_DIR(j); else - MIXING_STEPPERS_LOOP(j) REV_E_DIR(j); + MIXER_STEPPER_LOOP(j) REV_E_DIR(j); #else if (LA_steps >= 0) - NORM_E_DIR(active_extruder); + NORM_E_DIR(stepper_extruder); else - REV_E_DIR(active_extruder); + REV_E_DIR(stepper_extruder); #endif // Get the timer count and estimate the end of the pulse @@ -1848,14 +1831,9 @@ uint32_t Stepper::stepper_block_phase_isr() { // Set the STEP pulse ON #if ENABLED(MIXING_EXTRUDER) - MIXING_STEPPERS_LOOP(j) { - // Step mixing steppers (proportionally) - delta_error_m[j] += advance_dividend_m[j]; - // Step when the counter goes over zero - if (delta_error_m[j] >= 0) E_STEP_WRITE(j, !INVERT_E_STEP_PIN); - } + E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN); #else - E_STEP_WRITE(active_extruder, !INVERT_E_STEP_PIN); + E_STEP_WRITE(stepper_extruder, !INVERT_E_STEP_PIN); #endif // Enforce a minimum duration for STEP pulse ON @@ -1871,14 +1849,9 @@ uint32_t Stepper::stepper_block_phase_isr() { // Set the STEP pulse OFF #if ENABLED(MIXING_EXTRUDER) - MIXING_STEPPERS_LOOP(j) { - if (delta_error_m[j] >= 0) { - delta_error_m[j] -= advance_divisor_m; - E_STEP_WRITE(j, INVERT_E_STEP_PIN); - } - } + E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN); #else - E_STEP_WRITE(active_extruder, INVERT_E_STEP_PIN); + E_STEP_WRITE(stepper_extruder, INVERT_E_STEP_PIN); #endif // For minimum pulse time wait before looping @@ -2106,8 +2079,6 @@ void Stepper::init() { endstops.enable(true); // Start with endstops active. After homing they can be disabled sei(); - - set_directions(); // Init directions to last_direction_bits = 0 } /** diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 95f28dc914..b9e7ead995 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -150,7 +150,12 @@ #define ISR_E_STEPPER_CYCLES ISR_STEPPER_CYCLES // If linear advance is disabled, then the loop also handles them -#if DISABLED(LIN_ADVANCE) && ENABLED(MIXING_EXTRUDER) +#if DISABLED(LIN_ADVANCE) && ENABLED(MIXING_EXTRUDER) // ToDo: ??? + // HELP ME: What is what? + // Directions are set up for MIXING_STEPPERS - like before. + // Finding the right stepper may last up to MIXING_STEPPERS loops in get_next_stepper(). + // These loops are a bit faster than advancing a bresenham counter. + // Always only one e-stepper is stepped. #define ISR_START_MIXING_STEPPER_CYCLES ((MIXING_STEPPERS) * (ISR_START_STEPPER_CYCLES)) #define ISR_MIXING_STEPPER_CYCLES ((MIXING_STEPPERS) * (ISR_STEPPER_CYCLES)) #else @@ -188,7 +193,12 @@ #if ENABLED(LIN_ADVANCE) // Estimate the minimum LA loop time - #if ENABLED(MIXING_EXTRUDER) + #if ENABLED(MIXING_EXTRUDER) // ToDo: ??? + // HELP ME: What is what? + // Directions are set up for MIXING_STEPPERS - like before. + // Finding the right stepper may last up to MIXING_STEPPERS loops in get_next_stepper(). + // These loops are a bit faster than advancing a bresenham counter. + // Always only one e-stepper is stepped. #define MIN_ISR_LA_LOOP_CYCLES ((MIXING_STEPPERS) * (ISR_STEPPER_CYCLES)) #else #define MIN_ISR_LA_LOOP_CYCLES ISR_STEPPER_CYCLES @@ -292,17 +302,10 @@ class Stepper { decelerate_after, // The point from where we need to start decelerating step_event_count; // The total event count for the current block - // Mixing extruder mix delta_errors for bresenham tracing - #if ENABLED(MIXING_EXTRUDER) - static int32_t delta_error_m[MIXING_STEPPERS]; - static uint32_t advance_dividend_m[MIXING_STEPPERS], - advance_divisor_m; - #define MIXING_STEPPERS_LOOP(VAR) \ - for (uint8_t VAR = 0; VAR < MIXING_STEPPERS; VAR++) - #elif EXTRUDERS > 1 - static uint8_t active_extruder; + #if EXTRUDERS > 1 || ENABLED(MIXING_EXTRUDER) + static uint8_t stepper_extruder; #else - static constexpr uint8_t active_extruder = 0; + static constexpr uint8_t stepper_extruder = 0; #endif #if ENABLED(S_CURVE_ACCELERATION) diff --git a/Marlin/src/module/tool_change.cpp b/Marlin/src/module/tool_change.cpp index 6d1695870d..57ad09dc0d 100644 --- a/Marlin/src/module/tool_change.cpp +++ b/Marlin/src/module/tool_change.cpp @@ -356,19 +356,6 @@ inline void invalid_extruder_error(const uint8_t e) { SERIAL_ECHOLNPGM(MSG_INVALID_EXTRUDER); } -#if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1 - - inline void mixing_tool_change(const uint8_t tmp_extruder) { - if (tmp_extruder >= MIXING_VIRTUAL_TOOLS) - return invalid_extruder_error(tmp_extruder); - - // T0-Tnnn: Switch virtual tool by changing the mix - for (uint8_t j = 0; j < MIXING_STEPPERS; j++) - mixing_factor[j] = mixing_virtual_tool_mix[tmp_extruder][j]; - } - -#endif // MIXING_EXTRUDER && MIXING_VIRTUAL_TOOLS > 1 - #if ENABLED(DUAL_X_CARRIAGE) inline void dualx_tool_change(const uint8_t tmp_extruder, bool &no_move) { @@ -467,7 +454,9 @@ inline void invalid_extruder_error(const uint8_t e) { * previous tool out of the way and the new tool into place. */ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool no_move/*=false*/) { - planner.synchronize(); + #if DISABLED(MIXING_EXTRUDER) + planner.synchronize(); + #endif #if ENABLED(DUAL_X_CARRIAGE) // Only T0 allowed if the Printer is in DXC_DUPLICATION_MODE or DXC_SCALED_DUPLICATION_MODE if (tmp_extruder != 0 && dxc_is_duplicating()) @@ -481,8 +470,12 @@ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool n #endif #if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1 - - mixing_tool_change(tmp_extruder); + if (tmp_extruder >= MIXING_VIRTUAL_TOOLS) + return invalid_extruder_error(tmp_extruder); + // T0-Tnnn: Switch virtual tool by changing the index to the mix + mixer.T(uint_fast8_t(tmp_extruder)); + UNUSED(fr_mm_s); + UNUSED(no_move); #else // !MIXING_EXTRUDER || MIXING_VIRTUAL_TOOLS <= 1