diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 219cfe42c7..2d816d9e46 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -305,6 +305,18 @@ #endif +/** + * Differential Extruder + * + * The X and E steppers work together to create a differential drive system. + * Simple : E steps = X + E ; X steps = X (E drives a loop, X stays the same) + * Balanced: E steps = X + E/2 ; X steps = X - E/2 (Dual loop system) + */ +//#define DIFFERENTIAL_EXTRUDER +#if ENABLED(DIFFERENTIAL_EXTRUDER) + //#define BALANCED_DIFFERENTIAL_EXTRUDER +#endif + /** * Switching Toolhead * diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index fc31f9901b..474e51a7ec 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -934,6 +934,19 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L #endif #endif +/** + * Differential Extruder requirements + */ +#if ENABLED(DIFFERENTIAL_EXTRUDER) + #if EXTRUDERS != 1 + #error "DIFFERENTIAL EXTRUDER currently requires a single extruder (EXTRUDERS = 1)." + #elif !IS_FULL_CARTESIAN + #error "DIFFERENTIAL EXTRUDER requires standard Cartesian kinematics." + #elif !defined(CPU_32_BIT) + #error "DIFFERENTIAL EXTRUDER requires a 32-bit CPU." + #endif +#endif + /** * Generic Switching Toolhead requirements */ diff --git a/Marlin/src/module/ft_motion/resonance_generator.cpp b/Marlin/src/module/ft_motion/resonance_generator.cpp index f1b14ab72a..bfc2f9263d 100644 --- a/Marlin/src/module/ft_motion/resonance_generator.cpp +++ b/Marlin/src/module/ft_motion/resonance_generator.cpp @@ -33,7 +33,7 @@ ftm_resonance_test_params_t ResonanceGenerator::rt_params; // Resonance test bool ResonanceGenerator::active = false; // Resonance test active bool ResonanceGenerator::done = false; // Resonance test done -float ResonanceGenerator::rt_time = FTM_TS; // Resonance test timer +float ResonanceGenerator::rt_time = FTM_TS; // Resonance test timer float ResonanceGenerator::timeline = 0.0f; ResonanceGenerator::ResonanceGenerator() {} diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index d45ef3dc9a..b6d79dfac7 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -2725,6 +2725,70 @@ hal_timer_t Stepper::block_phase_isr() { // Based on the oversampling factor, do the calculations step_event_count = current_block->step_event_count << oversampling_factor; + #if ENABLED(DIFFERENTIAL_EXTRUDER) + + // X and E work together as a differential mechanism: + // When X and E move in the same direction they move the print carriage, + // and when X and E move in opposite directions they cause extrusion. + + // NOTE: All calculations performed per block, preserving Bresenham timing coordination + if (current_block->steps.x > 0 || current_block->steps.e > 0) { + // Calculate signed step counts based on directions + const int32_t x_signed_steps = current_block->direction_bits.x ? current_block->steps.x : -int32_t(current_block->steps.x), + e_signed_steps = current_block->direction_bits.e ? current_block->steps.e : -int32_t(current_block->steps.e); + + #if ENABLED(BALANCED_DIFFERENTIAL_EXTRUDER) + + // BALANCED DIFFERENTIAL EXTRUDER: X stepper drives one loop belt + // and E stepper drives another loop belt. Two belts mesh at the extruder + // X stepper: X - E/2 (carriage movement - half extrusion) + // E stepper: X + E/2 (carriage movement + half extrusion) + // Net extrusion: (X + E/2) - (X - E/2) = E + // (This will work great once I figure out what to do when E/2 is not integer!) + + // Split extrusion 50/50 between X and E steppers + const int32_t half_e_steps = e_signed_steps / 2; + + // X stepper: X movement - half E extrusion + const int32_t new_x_steps = x_signed_steps - half_e_steps; + current_block->steps.x = ABS(new_x_steps); + + // Update X axis direction + current_block->direction_bits.x = new_x_steps >= 0; + + // E stepper: X movement + half E extrusion + const int32_t new_e_steps = x_signed_steps + half_e_steps; + current_block->steps.e = ABS(new_e_steps); + + // Update E axis direction + current_block->direction_bits.e = new_e_steps >= 0; + + #else // !BALANCED_DIFFERENTIAL_EXTRUDER + + // SIMPLE SINGLE-LOOP DIFFERENTIAL EXTRUDER: X stepper drives the carriage as usual, + // while E stepper drives a loop belt that's meshed around the extruder. + // X stepper: X only (carriage movement) + // E stepper: X + E (carriage movement + extrusion) + // Net extrusion: (X + E) - X = E + + // Calculate net E steps (X movement + extrusion) + const int32_t net_e_steps = x_signed_steps + e_signed_steps; + + // Update the block with new E step count + current_block->steps.e = ABS(net_e_steps); + + // Update direction bit for E axis + current_block->direction_bits.e = net_e_steps >= 0; + + #endif // !BALANCED_DIFFERENTIAL_EXTRUDER + + // NOTE: DO NOT modify the step_event_count! This would change XYZ timing! + // Bresenham distributes X and E steps over the time base. + + } + + #endif // DIFFERENTIAL_EXTRUDER + // Initialize Bresenham delta errors to 1/2 delta_error = -int32_t(step_event_count); TERN_(HAS_ROUGH_LIN_ADVANCE, la_delta_error = delta_error); @@ -3322,7 +3386,7 @@ void Stepper::init() { } void Stepper::set_shaping_frequency(const AxisEnum axis, const float freq) { - // Enabling or disabling shaping whilst moving can result in lost steps + // Enabling or disabling shaping while moving can result in lost steps planner.synchronize(); const bool was_on = hal.isr_state(); diff --git a/buildroot/tests/STM32F103RE_btt b/buildroot/tests/STM32F103RE_btt index 2c46272625..21c6045a71 100755 --- a/buildroot/tests/STM32F103RE_btt +++ b/buildroot/tests/STM32F103RE_btt @@ -13,5 +13,5 @@ restore_configs opt_set MOTHERBOARD BOARD_BTT_SKR_E3_DIP \ SERIAL_PORT 1 SERIAL_PORT_2 -1 \ X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2130 -opt_enable SERIAL_DMA ULTI_CONTROLLER -exec_test $1 $2 "BTT SKR E3 DIP 1.0 | Mixed TMC Drivers" "$3" +opt_enable SERIAL_DMA ULTI_CONTROLLER DIFFERENTIAL_EXTRUDER BALANCED_DIFFERENTIAL_EXTRUDER +exec_test $1 $2 "BTT SKR E3 DIP 1.0 | Mixed TMC Drivers | Differential Extruder" "$3"