New Continuous Filament Mixer (#12098)

This commit is contained in:
AnHardt 2018-10-16 10:38:57 +02:00 committed by Scott Lahteine
parent 2d2cd628c6
commit f56968ba0b
14 changed files with 296 additions and 236 deletions

View File

@ -905,8 +905,8 @@ void setup() {
lcd_bootscreen(); lcd_bootscreen();
#endif #endif
#if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1 #if ENABLED(MIXING_EXTRUDER)
mixing_tools_init(); mixer.init();
#endif #endif
#if ENABLED(BLTOUCH) #if ENABLED(BLTOUCH)

View File

@ -142,11 +142,8 @@ void FWRetract::retract(const bool retracting
set_destination_from_current(); set_destination_from_current();
#if ENABLED(RETRACT_SYNC_MIXING) #if ENABLED(RETRACT_SYNC_MIXING)
float old_mixing_factor[MIXING_STEPPERS]; uint8_t old_mixing_tool = mixer.get_current_v_tool();
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) { mixer.T(MIXER_AUTORETRACT_TOOL);
old_mixing_factor[i] = mixing_factor[i];
mixing_factor[i] = RECIPROCAL(MIXING_STEPPERS);
}
#endif #endif
if (retracting) { if (retracting) {
@ -196,7 +193,7 @@ void FWRetract::retract(const bool retracting
} }
#if ENABLED(RETRACT_SYNC_MIXING) #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 #endif
feedrate_mm_s = old_feedrate_mm_s; // Restore original feedrate feedrate_mm_s = old_feedrate_mm_s; // Restore original feedrate

View File

@ -24,81 +24,90 @@
#if ENABLED(MIXING_EXTRUDER) #if ENABLED(MIXING_EXTRUDER)
#if ENABLED(DIRECT_MIXING_IN_G1) //#define MIXER_NORMALIZER_DEBUG
#include "../gcode/parser.h" #ifdef MIXER_NORMALIZER_DEBUG
#include "../core/serial.h"
#endif #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() { // Used in Stepper
// Virtual Tools 0, 1, 2, 3 = Filament 1, 2, 3, 4, etc. int_fast8_t Mixer::runner = 0;
for (uint8_t t = 0; t < MIXING_VIRTUAL_TOOLS && t < MIXING_STEPPERS; t++) mixer_color_t Mixer::s_color[MIXING_STEPPERS];
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) mixer_accu_t Mixer::accu[MIXING_STEPPERS] = { 0 };
mixing_virtual_tool_mix[t][i] = (t == i) ? 1.0 : 0.0;
// Remaining virtual tools are 100% filament 1 void Mixer::normalize(const uint8_t tool_index) {
#if MIXING_STEPPERS < MIXING_VIRTUAL_TOOLS float cmax = 0;
for (uint8_t t = MIXING_STEPPERS; t < MIXING_VIRTUAL_TOOLS; t++) #ifdef MIXER_NORMALIZER_DEBUG
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) float csum = 0;
mixing_virtual_tool_mix[t][i] = (i == 0) ? 1.0 : 0.0; #endif
MIXER_STEPPER_LOOP(i) {
cmax = max(cmax, M163_collector[i]);
#ifdef MIXER_NORMALIZER_DEBUG
csum += M163_collector[i];
#endif #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() { #ifdef MIXER_NORMALIZER_DEBUG
float mix_total = 0.0; csum = 0;
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) mix_total += mixing_factor[i]; SERIAL_ECHOPGM("Mixer: Normalizing to : [ ");
// Scale all values if they don't add up to ~1.0 MIXER_STEPPER_LOOP(i) {
if (!NEAR(mix_total, 1.0)) { SERIAL_ECHO(uint16_t(color[tool_index][i]));
SERIAL_PROTOCOLLNPGM("Warning: Mix factors must add up to 1.0. Scaling."); SERIAL_CHAR(' ');
const float inverse_sum = RECIPROCAL(mix_total); csum += color[tool_index][i];
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) mixing_factor[i] *= inverse_sum; }
} 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) // called at boot
// Get mixing parameters from the GCode void Mixer::init( void ) {
// The total "must" be 1.0 (but it will be normalized) // Virtual Tools 0, 1, 2, 3 = Filament 1, 2, 3, 4, etc.
// If no mix factors are given, the old mix is preserved // Every virtual tool gets a pure filament
void gcode_get_mix() { for (uint8_t t = 0; t < MIXING_VIRTUAL_TOOLS && t < MIXING_STEPPERS; t++)
const char mixing_codes[] = { 'A', 'B' MIXER_STEPPER_LOOP(i)
#if MIXING_STEPPERS > 2 color[t][i] = (t == i) ? COLOR_A_MASK : 0;
, 'C'
#if MIXING_STEPPERS > 3 // Remaining virtual tools are 100% filament 1
, 'D' #if MIXING_STEPPERS < MIXING_VIRTUAL_TOOLS
#if MIXING_STEPPERS > 4 for (uint8_t t = MIXING_STEPPERS; t < MIXING_VIRTUAL_TOOLS; t++)
, 'H' MIXER_STEPPER_LOOP(i)
#if MIXING_STEPPERS > 5 color[t][i] = (i == 0) ? COLOR_A_MASK : 0;
, 'I' #endif
#endif // MIXING_STEPPERS > 5
#endif // MIXING_STEPPERS > 4 #if ENABLED(RETRACT_SYNC_MIXING)
#endif // MIXING_STEPPERS > 3 // AUTORETRACT_TOOL gets the same amount of all filaments
#endif // MIXING_STEPPERS > 2 MIXER_STEPPER_LOOP(i)
}; color[MIXER_AUTORETRACT_TOOL][i] = COLOR_A_MASK;
byte mix_bits = 0; #endif
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) {
if (parser.seenval(mixing_codes[i])) { ZERO(M163_collector);
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
#endif // MIXING_EXTRUDER #endif // MIXING_EXTRUDER

View File

@ -19,23 +19,88 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#pragma once
#ifndef __MIXING_H__
#define __MIXING_H__
#include "../inc/MarlinConfig.h" #include "../inc/MarlinConfig.h"
extern float mixing_factor[MIXING_STEPPERS]; // Reciprocal of mix proportion. 0.0 = off, otherwise >= 1.0. #ifdef __AVR__
#define MIXER_ACCU_SIGNED
#if MIXING_VIRTUAL_TOOLS > 1 typedef uint8_t mixer_color_t;
extern float mixing_virtual_tool_mix[MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS]; typedef int8_t mixer_accu_t;
void mixing_tools_init(); #else
typedef uint_fast16_t mixer_color_t;
typedef uint_fast16_t mixer_accu_t;
#endif #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) #ifndef MIXING_VIRTUAL_TOOLS
void gcode_get_mix(); #define MIXING_VIRTUAL_TOOLS 1
#endif #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;

View File

@ -643,7 +643,7 @@ void GcodeSuite::G33() {
if (verbose_level != 0) { // !dry run if (verbose_level != 0) { // !dry run
// normalise angles to least squares // Normalize angles to least-squares
if (_angle_results) { if (_angle_results) {
float a_sum = 0.0; float a_sum = 0.0;
LOOP_XYZ(axis) a_sum += delta_tower_angle_trim[axis]; LOOP_XYZ(axis) a_sum += delta_tower_angle_trim[axis];

View File

@ -30,7 +30,7 @@
/** /**
* M163: Set a single mix factor for a mixing extruder * M163: Set a single mix factor for a mixing extruder
* This is called "weight" by some systems. * 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 * S[index] The channel index to set
* P[float] The mix value * P[float] The mix value
@ -38,24 +38,23 @@
void GcodeSuite::M163() { void GcodeSuite::M163() {
const int mix_index = parser.intval('S'); const int mix_index = parser.intval('S');
if (mix_index < MIXING_STEPPERS) 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. * 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 * S[index] The virtual tool to store
*/ */
void GcodeSuite::M164() { void GcodeSuite::M164() {
normalize_mix();
#if MIXING_VIRTUAL_TOOLS > 1 #if MIXING_VIRTUAL_TOOLS > 1
const int tool_index = parser.intval('S', -1); const int tool_index = parser.intval('S', -1);
if (WITHIN(tool_index, 0, MIXING_VIRTUAL_TOOLS - 1)) { #else
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) constexpr int tool_index = 0;
mixing_virtual_tool_mix[tool_index][i] = mixing_factor[i];
}
#endif #endif
if (WITHIN(tool_index, 0, MIXING_VIRTUAL_TOOLS - 1))
mixer.normalize(tool_index);
} }
#if ENABLED(DIRECT_MIXING_IN_G1) #if ENABLED(DIRECT_MIXING_IN_G1)
@ -63,7 +62,7 @@ void GcodeSuite::M164() {
/** /**
* M165: Set multiple mix factors for a mixing extruder. * M165: Set multiple mix factors for a mixing extruder.
* Factors that are left out will be set to 0. * 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 * A[factor] Mix factor for extruder stepper 1
* B[factor] Mix factor for extruder stepper 2 * B[factor] Mix factor for extruder stepper 2
@ -72,7 +71,39 @@ void GcodeSuite::M164() {
* H[factor] Mix factor for extruder stepper 5 * H[factor] Mix factor for extruder stepper 5
* I[factor] Mix factor for extruder stepper 6 * 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 #endif // DIRECT_MIXING_IN_G1

View File

@ -36,10 +36,6 @@ GcodeSuite gcode;
#include "../module/printcounter.h" #include "../module/printcounter.h"
#endif #endif
#if ENABLED(DIRECT_MIXING_IN_G1)
#include "../feature/mixing.h"
#endif
#include "../Marlin.h" // for idle() and suspend_auto_report #include "../Marlin.h" // for idle() and suspend_auto_report
uint8_t GcodeSuite::target_extruder; uint8_t GcodeSuite::target_extruder;
@ -113,7 +109,7 @@ void GcodeSuite::get_destination_from_command() {
// Get ABCDHI mixing factors // Get ABCDHI mixing factors
#if ENABLED(MIXING_EXTRUDER) && ENABLED(DIRECT_MIXING_IN_G1) #if ENABLED(MIXING_EXTRUDER) && ENABLED(DIRECT_MIXING_IN_G1)
gcode_get_mix(); M165();
#endif #endif
} }
@ -441,9 +437,7 @@ void GcodeSuite::process_parsed_command(
#if ENABLED(MIXING_EXTRUDER) #if ENABLED(MIXING_EXTRUDER)
case 163: M163(); break; // M163: Set a component weight for 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
case 164: M164(); break; // M164: Save current mix as a virtual extruder
#endif
#if ENABLED(DIRECT_MIXING_IN_G1) #if ENABLED(DIRECT_MIXING_IN_G1)
case 165: M165(); break; // M165: Set multiple mix weights case 165: M165(); break; // M165: Set multiple mix weights
#endif #endif

View File

@ -146,8 +146,8 @@
* M150 - Set Status LED Color as R<red> U<green> B<blue> P<bright>. Values 0-255. (Requires BLINKM, RGB_LED, RGBW_LED, NEOPIXEL_LED, or PCA9632). * M150 - Set Status LED Color as R<red> U<green> B<blue> P<bright>. Values 0-255. (Requires BLINKM, RGB_LED, RGBW_LED, NEOPIXEL_LED, or PCA9632).
* M155 - Auto-report temperatures with interval of S<seconds>. (Requires AUTO_REPORT_TEMPERATURES) * M155 - Auto-report temperatures with interval of S<seconds>. (Requires AUTO_REPORT_TEMPERATURES)
* M163 - Set a single proportion for a mixing extruder. (Requires MIXING_EXTRUDER) * 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) * 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 wuth parameters ABCDHI. (Requires MIXING_EXTRUDER and DIRECT_MIXING_IN_G1) * 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! ** * 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. ** * Rxxx Wait for bed current temp to reach target temp. ** Waits for heating or cooling. **
* M200 - Set filament diameter, D<diameter>, setting E axis units to cubic. (Use S0 to revert to linear units.) * M200 - Set filament diameter, D<diameter>, setting E axis units to cubic. (Use S0 to revert to linear units.)
@ -581,9 +581,7 @@ private:
#if ENABLED(MIXING_EXTRUDER) #if ENABLED(MIXING_EXTRUDER)
static void M163(); static void M163();
#if MIXING_VIRTUAL_TOOLS > 1 static void M164();
static void M164();
#endif
#if ENABLED(DIRECT_MIXING_IN_G1) #if ENABLED(DIRECT_MIXING_IN_G1)
static void M165(); static void M165();
#endif #endif

View File

@ -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." #error "Please select either MIXING_EXTRUDER or SWITCHING_EXTRUDER, not both."
#elif ENABLED(SINGLENOZZLE) #elif ENABLED(SINGLENOZZLE)
#error "MIXING_EXTRUDER is incompatible with SINGLENOZZLE." #error "MIXING_EXTRUDER is incompatible with SINGLENOZZLE."
#elif ENABLED(LIN_ADVANCE)
#error "MIXING_EXTRUDER is incompatible with LIN_ADVANCE."
#endif #endif
#endif #endif

View File

@ -1749,10 +1749,8 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
// Bail if this is a zero-length block // Bail if this is a zero-length block
if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return false; 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) #if ENABLED(MIXING_EXTRUDER)
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) MIXER_POPULATE_BLOCK();
block->mix_steps[i] = mixing_factor[i] * esteps;
#endif #endif
#if FAN_COUNT > 0 #if FAN_COUNT > 0
@ -1765,7 +1763,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
#endif #endif
#if EXTRUDERS > 1 #if EXTRUDERS > 1
block->active_extruder = extruder; block->extruder = extruder;
#endif #endif
#if ENABLED(AUTO_POWER_CONTROL) #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 // Calculate and limit speed in mm/sec for each axis
float current_speed[NUM_AXIS], speed_factor = 1.0f; // factor <1 decreases speed float current_speed[NUM_AXIS], speed_factor = 1.0f; // factor <1 decreases speed
LOOP_XYZE(i) { 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; float delta_mm_i = 0;
if (i == E_AXIS) { if (i == E_AXIS && mixer.get_current_v_tool() == MIXER_AUTORETRACT_TOOL)
for (uint8_t s = 0; s < MIXING_STEPPERS; s++) { delta_mm_i = delta_mm[i] / MIXING_STEPPERS;
const float delta_mm_s = mixing_factor[s] * delta_mm[i]; else
if (ABS(delta_mm_s) > ABS(delta_mm_i)) delta_mm_i = delta_mm_s; delta_mm_i = delta_mm[i];
}
}
else delta_mm_i = delta_mm[i];
#else #else
const float delta_mm_i = delta_mm[i]; const float delta_mm_i = delta_mm[i];
#endif #endif

View File

@ -47,6 +47,10 @@
#include "../feature/fwretract.h" #include "../feature/fwretract.h"
#endif #endif
#if ENABLED(MIXING_EXTRUDER)
#include "../feature/mixing.h"
#endif
enum BlockFlagBit : char { enum BlockFlagBit : char {
// Recalculate trapezoids on entry junction. For optimization. // Recalculate trapezoids on entry junction. For optimization.
BLOCK_BIT_RECALCULATE, BLOCK_BIT_RECALCULATE,
@ -104,11 +108,11 @@ typedef struct {
uint32_t step_event_count; // The number of step events required to complete this block uint32_t step_event_count; // The number of step events required to complete this block
#if EXTRUDERS > 1 #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 #endif
#if ENABLED(MIXING_EXTRUDER) #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 #endif
// Settings for the trapezoid generator // Settings for the trapezoid generator

View File

@ -103,6 +103,10 @@
#include <SPI.h> #include <SPI.h>
#endif #endif
#if ENABLED(MIXING_EXTRUDER)
#include "../feature/mixing.h"
#endif
Stepper stepper; // Singleton Stepper stepper; // Singleton
// public: // 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::decelerate_after, // The point from where we need to start decelerating
Stepper::step_event_count; // The total event count for the current block Stepper::step_event_count; // The total event count for the current block
#if ENABLED(MIXING_EXTRUDER) #if EXTRUDERS > 1 || ENABLED(MIXING_EXTRUDER)
int32_t Stepper::delta_error_m[MIXING_STEPPERS]; uint8_t Stepper::stepper_extruder;
uint32_t Stepper::advance_dividend_m[MIXING_STEPPERS], #else
Stepper::advance_divisor_m; constexpr uint8_t Stepper::stepper_extruder;
#elif EXTRUDERS > 1
uint8_t Stepper::active_extruder; // Active extruder
#endif #endif
#if ENABLED(S_CURVE_ACCELERATION) #if ENABLED(S_CURVE_ACCELERATION)
@ -301,7 +303,7 @@ int8_t Stepper::count_direction[NUM_AXIS] = { 0, 0, 0, 0 };
#endif #endif
#if DISABLED(MIXING_EXTRUDER) #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 #endif
void Stepper::wake_up() { void Stepper::wake_up() {
@ -340,21 +342,23 @@ void Stepper::set_directions() {
#if DISABLED(LIN_ADVANCE) #if DISABLED(LIN_ADVANCE)
#if ENABLED(MIXING_EXTRUDER) #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)) { 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; count_direction[E_AXIS] = -1;
} }
else { else {
MIXING_STEPPERS_LOOP(j) NORM_E_DIR(j); MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
count_direction[E_AXIS] = 1; count_direction[E_AXIS] = 1;
} }
#else #else
if (motor_direction(E_AXIS)) { if (motor_direction(E_AXIS)) {
REV_E_DIR(active_extruder); REV_E_DIR(stepper_extruder);
count_direction[E_AXIS] = -1; count_direction[E_AXIS] = -1;
} }
else { else {
NORM_E_DIR(active_extruder); NORM_E_DIR(stepper_extruder);
count_direction[E_AXIS] = 1; count_direction[E_AXIS] = 1;
} }
#endif #endif
@ -1387,39 +1391,27 @@ void Stepper::stepper_pulse_phase_isr() {
PULSE_START(Z); PULSE_START(Z);
#endif #endif
// Pulse E/Mixing extruders // Pulse Extruders
#if ENABLED(LIN_ADVANCE) // Tick the E axis, correct error term and update position
// 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]; delta_error[E_AXIS] += advance_dividend[E_AXIS];
if (delta_error[E_AXIS] >= 0) { if (delta_error[E_AXIS] >= 0) {
count_position[E_AXIS] += count_direction[E_AXIS]; count_position[E_AXIS] += count_direction[E_AXIS];
delta_error[E_AXIS] -= advance_divisor; #if ENABLED(LIN_ADVANCE)
// 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];
delta_error[E_AXIS] -= advance_divisor; 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;
// Tick the counters used for this mix in proper proportion #else // !LIN_ADVANCE && MIXING_EXTRUDER
MIXING_STEPPERS_LOOP(j) { // Don't adjust delta_error[E_AXIS] here!
// Step mixing steppers (proportionally) // Being positive is the criteria for ending the pulse.
delta_error_m[j] += advance_dividend_m[j]; E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
// Step when the counter goes over zero #endif
if (delta_error_m[j] >= 0) E_STEP_WRITE(j, !INVERT_E_STEP_PIN); }
} #else // !LIN_ADVANCE && !MIXING_EXTRUDER
#if HAS_E_STEP
#else // !MIXING_EXTRUDER
PULSE_START(E); PULSE_START(E);
#endif #endif
#endif // !LIN_ADVANCE #endif
#if MINIMUM_STEPPER_PULSE #if MINIMUM_STEPPER_PULSE
// Just wait for the requested pulse duration // Just wait for the requested pulse duration
@ -1442,11 +1434,9 @@ void Stepper::stepper_pulse_phase_isr() {
#if DISABLED(LIN_ADVANCE) #if DISABLED(LIN_ADVANCE)
#if ENABLED(MIXING_EXTRUDER) #if ENABLED(MIXING_EXTRUDER)
MIXING_STEPPERS_LOOP(j) { if (delta_error[E_AXIS] >= 0) {
if (delta_error_m[j] >= 0) { delta_error[E_AXIS] -= advance_divisor;
delta_error_m[j] -= advance_divisor_m; E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN);
E_STEP_WRITE(j, INVERT_E_STEP_PIN);
}
} }
#else // !MIXING_EXTRUDER #else // !MIXING_EXTRUDER
PULSE_STOP(E); PULSE_STOP(E);
@ -1717,27 +1707,18 @@ uint32_t Stepper::stepper_block_phase_isr() {
decelerate_after = current_block->decelerate_after << oversampling; decelerate_after = current_block->decelerate_after << oversampling;
#if ENABLED(MIXING_EXTRUDER) #if ENABLED(MIXING_EXTRUDER)
const uint32_t e_steps = ( MIXER_STEPPER_SETUP();
#if ENABLED(LIN_ADVANCE) #endif
current_block->steps[E_AXIS]
#else #if EXTRUDERS > 1
step_event_count stepper_extruder = current_block->extruder;
#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;
#endif #endif
// Initialize the trapezoid generator from the current block. // Initialize the trapezoid generator from the current block.
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
#if DISABLED(MIXING_EXTRUDER) && E_STEPPERS > 1 #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 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 #endif
if ((LA_use_advance_lead = current_block->use_advance_lead)) { if ((LA_use_advance_lead = current_block->use_advance_lead)) {
@ -1751,15 +1732,15 @@ uint32_t Stepper::stepper_block_phase_isr() {
#endif #endif
if (current_block->direction_bits != last_direction_bits if (current_block->direction_bits != last_direction_bits
#if DISABLED(MIXING_EXTRUDER) #if DISABLED(MIXING_EXTRUDER)
|| active_extruder != last_moved_extruder || stepper_extruder != last_moved_extruder
#endif #endif
) { ) {
last_direction_bits = current_block->direction_bits; last_direction_bits = current_block->direction_bits;
#if DISABLED(MIXING_EXTRUDER) && EXTRUDERS > 1
last_moved_extruder = active_extruder;
#endif
set_directions(); set_directions();
#if EXTRUDERS > 1
last_moved_extruder = stepper_extruder;
#endif
} }
// At this point, we must ensure the movement about to execute isn't // 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; interval = LA_ADV_NEVER;
#if ENABLED(MIXING_EXTRUDER) #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) if (LA_steps >= 0)
MIXING_STEPPERS_LOOP(j) NORM_E_DIR(j); MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
else else
MIXING_STEPPERS_LOOP(j) REV_E_DIR(j); MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
#else #else
if (LA_steps >= 0) if (LA_steps >= 0)
NORM_E_DIR(active_extruder); NORM_E_DIR(stepper_extruder);
else else
REV_E_DIR(active_extruder); REV_E_DIR(stepper_extruder);
#endif #endif
// Get the timer count and estimate the end of the pulse // 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 // Set the STEP pulse ON
#if ENABLED(MIXING_EXTRUDER) #if ENABLED(MIXING_EXTRUDER)
MIXING_STEPPERS_LOOP(j) { E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
// 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 #else
E_STEP_WRITE(active_extruder, !INVERT_E_STEP_PIN); E_STEP_WRITE(stepper_extruder, !INVERT_E_STEP_PIN);
#endif #endif
// Enforce a minimum duration for STEP pulse ON // Enforce a minimum duration for STEP pulse ON
@ -1871,14 +1849,9 @@ uint32_t Stepper::stepper_block_phase_isr() {
// Set the STEP pulse OFF // Set the STEP pulse OFF
#if ENABLED(MIXING_EXTRUDER) #if ENABLED(MIXING_EXTRUDER)
MIXING_STEPPERS_LOOP(j) { E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN);
if (delta_error_m[j] >= 0) {
delta_error_m[j] -= advance_divisor_m;
E_STEP_WRITE(j, INVERT_E_STEP_PIN);
}
}
#else #else
E_STEP_WRITE(active_extruder, INVERT_E_STEP_PIN); E_STEP_WRITE(stepper_extruder, INVERT_E_STEP_PIN);
#endif #endif
// For minimum pulse time wait before looping // 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 endstops.enable(true); // Start with endstops active. After homing they can be disabled
sei(); sei();
set_directions(); // Init directions to last_direction_bits = 0
} }
/** /**

View File

@ -150,7 +150,12 @@
#define ISR_E_STEPPER_CYCLES ISR_STEPPER_CYCLES #define ISR_E_STEPPER_CYCLES ISR_STEPPER_CYCLES
// If linear advance is disabled, then the loop also handles them // 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_START_MIXING_STEPPER_CYCLES ((MIXING_STEPPERS) * (ISR_START_STEPPER_CYCLES))
#define ISR_MIXING_STEPPER_CYCLES ((MIXING_STEPPERS) * (ISR_STEPPER_CYCLES)) #define ISR_MIXING_STEPPER_CYCLES ((MIXING_STEPPERS) * (ISR_STEPPER_CYCLES))
#else #else
@ -188,7 +193,12 @@
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
// Estimate the minimum LA loop time // 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)) #define MIN_ISR_LA_LOOP_CYCLES ((MIXING_STEPPERS) * (ISR_STEPPER_CYCLES))
#else #else
#define MIN_ISR_LA_LOOP_CYCLES ISR_STEPPER_CYCLES #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 decelerate_after, // The point from where we need to start decelerating
step_event_count; // The total event count for the current block step_event_count; // The total event count for the current block
// Mixing extruder mix delta_errors for bresenham tracing #if EXTRUDERS > 1 || ENABLED(MIXING_EXTRUDER)
#if ENABLED(MIXING_EXTRUDER) static uint8_t stepper_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;
#else #else
static constexpr uint8_t active_extruder = 0; static constexpr uint8_t stepper_extruder = 0;
#endif #endif
#if ENABLED(S_CURVE_ACCELERATION) #if ENABLED(S_CURVE_ACCELERATION)

View File

@ -356,19 +356,6 @@ inline void invalid_extruder_error(const uint8_t e) {
SERIAL_ECHOLNPGM(MSG_INVALID_EXTRUDER); 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) #if ENABLED(DUAL_X_CARRIAGE)
inline void dualx_tool_change(const uint8_t tmp_extruder, bool &no_move) { 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. * 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*/) { 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 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()) 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 #endif
#if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1 #if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1
if (tmp_extruder >= MIXING_VIRTUAL_TOOLS)
mixing_tool_change(tmp_extruder); 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 #else // !MIXING_EXTRUDER || MIXING_VIRTUAL_TOOLS <= 1