diff --git a/.travis.yml b/.travis.yml index 15b6991ee..496b616aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,10 +76,12 @@ script: - build_marlin # # Test 3 extruders on RUMBA (can use any board with >=3 extruders defined) + # Include a test for LIN_ADVANCE here also # - opt_set MOTHERBOARD BOARD_RUMBA - opt_set EXTRUDERS 3 - opt_set TEMP_SENSOR_2 1 + - opt_enable_adv LIN_ADVANCE - build_marlin # # Test PIDTEMPBED diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 952da0fa7..43b51115e 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -445,6 +445,15 @@ #define D_FILAMENT 2.85 #endif +// Implementation of a linear pressure control +// Assumption: advance = k * (delta velocity) +// K=0 means advance disabled. A good value for a gregs wade extruder will be around K=75 +//#define LIN_ADVANCE + +#if ENABLED(LIN_ADVANCE) + #define LIN_ADVANCE_K 75 +#endif + // @section leveling // Default mesh area is an area with an inset margin on the print area. diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 4fe165e4b..691721fe0 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -6475,6 +6475,16 @@ inline void gcode_M503() { #endif // DUAL_X_CARRIAGE +#if ENABLED(LIN_ADVANCE) + /** + * M905: Set advance factor + */ + inline void gcode_M905() { + stepper.synchronize(); + stepper.advance_M905(); + } +#endif + /** * M907: Set digital trimpot motor current using axis codes X, Y, Z, E, B, S */ @@ -7347,6 +7357,12 @@ void process_next_command() { gcode_M605(); break; #endif // DUAL_X_CARRIAGE + + #if ENABLED(LIN_ADVANCE) + case 905: // M905 Set advance factor. + gcode_M905(); + break; + #endif case 907: // M907 Set digital trimpot motor current using axis codes. gcode_M907(); diff --git a/Marlin/SanityCheck.h b/Marlin/SanityCheck.h index a913cb1b0..4110d6705 100644 --- a/Marlin/SanityCheck.h +++ b/Marlin/SanityCheck.h @@ -351,6 +351,13 @@ #endif // AUTO_BED_LEVELING_FEATURE +/** + * Advance Extrusion + */ +#if ENABLED(ADVANCE) && ENABLED(LIN_ADVANCE) + #error You can enable ADVANCE or LIN_ADVANCE, but not both. +#endif + /** * Filament Width Sensor */ @@ -358,7 +365,6 @@ #error "FILAMENT_WIDTH_SENSOR requires a FILWIDTH_PIN to be defined." #endif - /** * ULTIPANEL encoder */ diff --git a/Marlin/planner.cpp b/Marlin/planner.cpp index bd60d75a1..699987f15 100644 --- a/Marlin/planner.cpp +++ b/Marlin/planner.cpp @@ -1050,7 +1050,23 @@ void Planner::check_axes_activity() { for (int i = 0; i < NUM_AXIS; i++) previous_speed[i] = current_speed[i]; previous_nominal_speed = block->nominal_speed; - #if ENABLED(ADVANCE) + #if ENABLED(LIN_ADVANCE) + + // bse == allsteps: A problem occurs when there's a very tiny move before a retract. + // In this case, the retract and the move will be executed together. + // This leads to an enormous number of advance steps due to a huge e_acceleration. + // The math is correct, but you don't want a retract move done with advance! + // So this situation is filtered out here. + if (!bse || (!bsx && !bsy && !bsz) || stepper.get_advance_k() == 0 || bse == allsteps) { + block->use_advance_lead = false; + } + else { + block->use_advance_lead = true; + block->e_speed_multiplier8 = (block->steps[E_AXIS] << 8) / block->step_event_count; + } + + #elif ENABLED(ADVANCE) + // Calculate advance rate if (!bse || (!bsx && !bsy && !bsz)) { block->advance_rate = 0; @@ -1069,7 +1085,8 @@ void Planner::check_axes_activity() { SERIAL_ECHOPGM("advance rate :"); SERIAL_ECHOLN(block->advance_rate/256.0); */ - #endif // ADVANCE + + #endif // ADVANCE or LIN_ADVANCE calculate_trapezoid_for_block(block, block->entry_speed / block->nominal_speed, safe_speed / block->nominal_speed); diff --git a/Marlin/planner.h b/Marlin/planner.h index 07de37134..51219743e 100644 --- a/Marlin/planner.h +++ b/Marlin/planner.h @@ -64,7 +64,11 @@ typedef struct { unsigned char direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) - #if ENABLED(ADVANCE) + // Advance extrusion + #if ENABLED(LIN_ADVANCE) + bool use_advance_lead; + int e_speed_multiplier8; // Factorised by 2^8 to avoid float + #elif ENABLED(ADVANCE) long advance_rate; volatile long initial_advance; volatile long final_advance; diff --git a/Marlin/stepper.cpp b/Marlin/stepper.cpp index f8e8a853c..c9d49cd12 100644 --- a/Marlin/stepper.cpp +++ b/Marlin/stepper.cpp @@ -89,13 +89,24 @@ long Stepper::counter_X = 0, volatile unsigned long Stepper::step_events_completed = 0; // The number of step events executed in the current block -#if ENABLED(ADVANCE) +#if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) + unsigned char Stepper::old_OCR0A; - long Stepper::final_advance = 0, - Stepper::old_advance = 0, - Stepper::e_steps[EXTRUDERS], - Stepper::advance_rate, - Stepper::advance; + volatile unsigned char Stepper::eISR_Rate = 200; // Keep the ISR at a low rate until needed + + #if ENABLED(LIN_ADVANCE) + volatile int Stepper::e_steps[EXTRUDERS]; + int Stepper::extruder_advance_k = LIN_ADVANCE_K, + Stepper::final_estep_rate, + Stepper::current_estep_rate[EXTRUDERS], + Stepper::current_adv_steps[EXTRUDERS]; + #else + long Stepper::e_steps[EXTRUDERS], + Stepper::final_advance = 0, + Stepper::old_advance = 0, + Stepper::advance_rate, + Stepper::advance; + #endif #endif long Stepper::acceleration_time, Stepper::deceleration_time; @@ -344,14 +355,32 @@ void Stepper::isr() { customizedSerial.checkRx(); // Check for serial chars. #endif - #if ENABLED(ADVANCE) + #if ENABLED(LIN_ADVANCE) + + counter_E += current_block->steps[E_AXIS]; + if (counter_E > 0) { + counter_E -= current_block->step_event_count; + count_position[E_AXIS] += count_direction[E_AXIS]; + e_steps[current_block->active_extruder] += motor_direction(E_AXIS) ? -1 : 1; + } + + if (current_block->use_advance_lead) { + int delta_adv_steps; //Maybe a char would be enough? + delta_adv_steps = (((long)extruder_advance_k * current_estep_rate[current_block->active_extruder]) >> 9) - current_adv_steps[current_block->active_extruder]; + e_steps[current_block->active_extruder] += delta_adv_steps; + current_adv_steps[current_block->active_extruder] += delta_adv_steps; + } + + #elif ENABLED(ADVANCE) + counter_E += current_block->steps[E_AXIS]; if (counter_E > 0) { counter_E -= current_block->step_event_count; e_steps[current_block->active_extruder] += motor_direction(E_AXIS) ? -1 : 1; } - #endif //ADVANCE + #endif // ADVANCE or LIN_ADVANCE + #define _COUNTER(AXIS) counter_## AXIS #define _APPLY_STEP(AXIS) AXIS ##_APPLY_STEP #define _INVERT_STEP_PIN(AXIS) INVERT_## AXIS ##_STEP_PIN @@ -363,7 +392,7 @@ void Stepper::isr() { STEP_ADD(X); STEP_ADD(Y); STEP_ADD(Z); - #if DISABLED(ADVANCE) + #if DISABLED(ADVANCE) && DISABLED(LIN_ADVANCE) STEP_ADD(E); #endif @@ -377,13 +406,19 @@ void Stepper::isr() { STEP_IF_COUNTER(X); STEP_IF_COUNTER(Y); STEP_IF_COUNTER(Z); - #if DISABLED(ADVANCE) + #if DISABLED(ADVANCE) && DISABLED(LIN_ADVANCE) STEP_IF_COUNTER(E); #endif step_events_completed++; if (step_events_completed >= current_block->step_event_count) break; } + + #if ENABLED(LIN_ADVANCE) + // If we have esteps to execute, fire the next ISR "now" + if (e_steps[current_block->active_extruder]) OCR0A = TCNT0 + 2; + #endif + // Calculate new timer value unsigned short timer, step_rate; if (step_events_completed <= (unsigned long)current_block->accelerate_until) { @@ -399,7 +434,12 @@ void Stepper::isr() { OCR1A = timer; acceleration_time += timer; - #if ENABLED(ADVANCE) + #if ENABLED(LIN_ADVANCE) + + if (current_block->use_advance_lead) + current_estep_rate[current_block->active_extruder] = ((unsigned long)acc_step_rate * current_block->e_speed_multiplier8) >> 8; + + #elif ENABLED(ADVANCE) advance += advance_rate * step_loops; //NOLESS(advance, current_block->advance); @@ -408,7 +448,11 @@ void Stepper::isr() { e_steps[current_block->active_extruder] += ((advance >> 8) - old_advance); old_advance = advance >> 8; - #endif //ADVANCE + #endif // ADVANCE or LIN_ADVANCE + + #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) + eISR_Rate = (timer >> 2) / abs(e_steps[current_block->active_extruder]); + #endif } else if (step_events_completed > (unsigned long)current_block->decelerate_after) { MultiU24X32toH16(step_rate, deceleration_time, current_block->acceleration_rate); @@ -424,8 +468,14 @@ void Stepper::isr() { timer = calc_timer(step_rate); OCR1A = timer; deceleration_time += timer; + + #if ENABLED(LIN_ADVANCE) + + if (current_block->use_advance_lead) + current_estep_rate[current_block->active_extruder] = ((unsigned long)step_rate * current_block->e_speed_multiplier8) >> 8; + + #elif ENABLED(ADVANCE) - #if ENABLED(ADVANCE) advance -= advance_rate * step_loops; NOLESS(advance, final_advance); @@ -433,9 +483,24 @@ void Stepper::isr() { uint32_t advance_whole = advance >> 8; e_steps[current_block->active_extruder] += advance_whole - old_advance; old_advance = advance_whole; - #endif //ADVANCE + + #endif // ADVANCE or LIN_ADVANCE + + #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) + eISR_Rate = (timer >> 2) / abs(e_steps[current_block->active_extruder]); + #endif } else { + + #if ENABLED(LIN_ADVANCE) + + if (current_block->use_advance_lead) + current_estep_rate[current_block->active_extruder] = final_estep_rate; + + eISR_Rate = (OCR1A_nominal >> 2) / abs(e_steps[current_block->active_extruder]); + + #endif + OCR1A = OCR1A_nominal; // ensure we're running at the correct step rate, even if we just came off an acceleration step_loops = step_loops_nominal; @@ -451,13 +516,15 @@ void Stepper::isr() { } } -#if ENABLED(ADVANCE) +#if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) + // Timer interrupt for E. e_steps is set in the main routine; // Timer 0 is shared with millies ISR(TIMER0_COMPA_vect) { Stepper::advance_isr(); } void Stepper::advance_isr() { - old_OCR0A += 52; // ~10kHz interrupt (250000 / 26 = 9615kHz) + + old_OCR0A += eISR_Rate; OCR0A = old_OCR0A; #define STEP_E_ONCE(INDEX) \ @@ -474,22 +541,21 @@ void Stepper::isr() { E## INDEX ##_STEP_WRITE(!INVERT_E_STEP_PIN); \ } - // Step all E steppers that have steps, up to 4 steps per interrupt - for (unsigned char i = 0; i < 4; i++) { - STEP_E_ONCE(0); - #if EXTRUDERS > 1 - STEP_E_ONCE(1); - #if EXTRUDERS > 2 - STEP_E_ONCE(2); - #if EXTRUDERS > 3 - STEP_E_ONCE(3); - #endif + // Step all E steppers that have steps + STEP_E_ONCE(0); + #if EXTRUDERS > 1 + STEP_E_ONCE(1); + #if EXTRUDERS > 2 + STEP_E_ONCE(2); + #if EXTRUDERS > 3 + STEP_E_ONCE(3); #endif #endif - } + #endif + } -#endif // ADVANCE +#endif // ADVANCE or LIN_ADVANCE void Stepper::init() { @@ -656,14 +722,28 @@ void Stepper::init() { TCNT1 = 0; ENABLE_STEPPER_DRIVER_INTERRUPT(); - #if ENABLED(ADVANCE) + #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) + + #if ENABLED(LIN_ADVANCE) + + for (int i = 0; i < EXTRUDERS; i++) { + e_steps[i] = 0; + current_adv_steps[i] = 0; + } + + #elif ENABLED(ADVANCE) + + for (uint8_t i = 0; i < EXTRUDERS; i++) e_steps[i] = 0; + + #endif + #if defined(TCCR0A) && defined(WGM01) CBI(TCCR0A, WGM01); CBI(TCCR0A, WGM00); #endif - for (uint8_t i = 0; i < EXTRUDERS; i++) e_steps[i] = 0; SBI(TIMSK0, OCIE0A); - #endif //ADVANCE + + #endif // ADVANCE or LIN_ADVANCE endstops.enable(true); // Start with endstops active. After homing they can be disabled sei(); @@ -1040,3 +1120,14 @@ void Stepper::microstep_readings() { SERIAL_PROTOCOLLN(digitalRead(E1_MS2_PIN)); #endif } + +#if ENABLED(LIN_ADVANCE) + + void Stepper::advance_M905() { + if (code_seen('K')) extruder_advance_k = code_value(); + SERIAL_ECHO_START; + SERIAL_ECHOPAIR("Advance factor: ", extruder_advance_k); + SERIAL_EOL; + } + +#endif // LIN_ADVANCE diff --git a/Marlin/stepper.h b/Marlin/stepper.h index 1aebe366c..279db1807 100644 --- a/Marlin/stepper.h +++ b/Marlin/stepper.h @@ -22,7 +22,7 @@ /** * stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors - * Part of Grbl + * Derived from Grbl * * Copyright (c) 2009-2011 Simen Svale Skogsrud * @@ -90,10 +90,6 @@ class Stepper { static bool performing_homing; #endif - #if ENABLED(ADVANCE) - static long e_steps[EXTRUDERS]; - #endif - private: static unsigned char last_direction_bits; // The next stepping-bits to be output @@ -107,10 +103,23 @@ class Stepper { static long counter_X, counter_Y, counter_Z, counter_E; static volatile unsigned long step_events_completed; // The number of step events executed in the current block - #if ENABLED(ADVANCE) + #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) static unsigned char old_OCR0A; - static long advance_rate, advance, old_advance, final_advance; - #endif + static volatile unsigned char eISR_Rate; + #if ENABLED(LIN_ADVANCE) + static volatile int e_steps[EXTRUDERS]; + static int extruder_advance_k; + static int final_estep_rate; + static int current_estep_rate[EXTRUDERS]; // Actual extruder speed [steps/s] + static int current_adv_steps[EXTRUDERS]; // The amount of current added esteps due to advance. + // i.e., the current amount of pressure applied + // to the spring (=filament). + #else + static long e_steps[EXTRUDERS]; + static long advance_rate, advance, final_advance; + static long old_advance; + #endif + #endif // ADVANCE or LIN_ADVANCE static long acceleration_time, deceleration_time; //unsigned long accelerate_until, decelerate_after, acceleration_rate, initial_rate, final_rate, nominal_rate; @@ -156,7 +165,7 @@ class Stepper { static void isr(); - #if ENABLED(ADVANCE) + #if ENABLED(ADVANCE) || ENABLED(LIN_ADVANCE) static void advance_isr(); #endif @@ -246,6 +255,11 @@ class Stepper { return endstops_trigsteps[axis] / planner.axis_steps_per_mm[axis]; } + #if ENABLED(LIN_ADVANCE) + void advance_M905(); + FORCE_INLINE int get_advance_k() { return extruder_advance_k; } + #endif + private: static FORCE_INLINE unsigned short calc_timer(unsigned short step_rate) { @@ -315,6 +329,13 @@ class Stepper { acc_step_rate = current_block->initial_rate; acceleration_time = calc_timer(acc_step_rate); OCR1A = acceleration_time; + + #if ENABLED(LIN_ADVANCE) + if (current_block->use_advance_lead) { + current_estep_rate[current_block->active_extruder] = ((unsigned long)acc_step_rate * current_block->e_speed_multiplier8) >> 8; + final_estep_rate = (current_block->nominal_rate * current_block->e_speed_multiplier8) >> 8; + } + #endif // SERIAL_ECHO_START; // SERIAL_ECHOPGM("advance :");