From 9c201c4dd64026584628f7673f59d90a767d77b8 Mon Sep 17 00:00:00 2001 From: 3d-gussner <3d.gussner@gmail.com> Date: Sat, 20 May 2017 17:41:50 +0200 Subject: [PATCH] Split LIN_ADVANCE from TMC2103 support Check: https://github.com/prusa3d/Prusa-Firmware/pull/93 and https://github.com/madhunm/Prusa-Firmware/tree/MK2/Firmware --- Firmware/Configuration.h | 2 +- Firmware/ConfigurationStore.cpp | 38 +++ Firmware/Marlin_main.cpp | 49 ++++ Firmware/language_all.cpp | 10 + Firmware/language_all.h | 4 + Firmware/language_en.h | 5 + Firmware/planner.cpp | 99 ++++++- Firmware/planner.h | 12 + Firmware/stepper.cpp | 273 +++++++++++++++++- Firmware/stepper.h | 23 ++ Firmware/stepper_indirection.h | 119 ++++++++ Firmware/ultralcd.cpp | 5 + .../variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h | 43 +++ 13 files changed, 672 insertions(+), 10 deletions(-) create mode 100644 Firmware/stepper_indirection.h diff --git a/Firmware/Configuration.h b/Firmware/Configuration.h index 9fb430d09..f1fd9dfd2 100644 --- a/Firmware/Configuration.h +++ b/Firmware/Configuration.h @@ -5,7 +5,7 @@ #include "Configuration_prusa.h" // Firmware version -#define FW_version "3.0.11" +#define FW_version "3.0.11-L01" #define FW_PRUSA3D_MAGIC "PRUSA3DFW" #define FW_PRUSA3D_MAGIC_LEN 10 diff --git a/Firmware/ConfigurationStore.cpp b/Firmware/ConfigurationStore.cpp index f7f64094c..989dcb2a0 100644 --- a/Firmware/ConfigurationStore.cpp +++ b/Firmware/ConfigurationStore.cpp @@ -124,6 +124,19 @@ void Config_StoreSettings() EEPROM_WRITE_VAR(i, filament_size[2]); #endif #endif +// + // Linear Advance + // + + #if defined(LIN_ADVANCE) + EEPROM_WRITE_VAR(i, extruder_advance_k); + EEPROM_WRITE_VAR(i, advance_ed_ratio); + #else + dummy = 0.0f; + EEPROM_WRITE_VAR(i, dummy); + EEPROM_WRITE_VAR(i, dummy); + #endif + /*MYSERIAL.print("Top address used:\n"); MYSERIAL.print(i); MYSERIAL.print("\n"); @@ -259,6 +272,14 @@ void Config_PrintSettings() SERIAL_ECHOLNPGM("Filament settings: Disabled"); } #endif + + #if defined(LIN_ADVANCE) + SERIAL_ECHO_START; + SERIAL_ECHOLNPGM("Linear Advance:"); + SERIAL_ECHOPAIR(" M900 K", extruder_advance_k); + SERIAL_ECHOPAIR(" R", advance_ed_ratio); + SERIAL_ECHO('\n'); + #endif } #endif @@ -349,6 +370,18 @@ void Config_RetrieveSettings() #endif calculate_volumetric_multipliers(); // Call updatePID (similar to when we have processed M301) + // + // Linear Advance + // + + #if defined(LIN_ADVANCE) + EEPROM_READ_VAR(i, extruder_advance_k); + EEPROM_READ_VAR(i, advance_ed_ratio); + #else + EEPROM_READ_VAR(i, dummy); + EEPROM_READ_VAR(i, dummy); + #endif + updatePID(); SERIAL_ECHO_START; SERIAL_ECHOLNPGM("Stored settings retrieved"); @@ -432,6 +465,11 @@ void Config_ResetDefault() #endif #endif calculate_volumetric_multipliers(); + +#if defined(LIN_ADVANCE) + extruder_advance_k = LIN_ADVANCE_K; + advance_ed_ratio = LIN_ADVANCE_E_D_RATIO; +#endif SERIAL_ECHO_START; SERIAL_ECHOLNPGM("Hardcoded Default Settings Loaded"); diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 685f788e1..fcfae087e 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -75,6 +75,10 @@ #define VERSION_STRING "1.0.2" +#ifdef AUTOMATIC_CURRENT_CONTROL + bool auto_current_control = 0; +#endif + #include "ultralcd.h" @@ -208,6 +212,7 @@ // M928 - Start SD logging (M928 filename.g) - ended by M29 // M999 - Restart after being stopped by error +// M900 - Set and/or Get advance K factor and WH/D ratio (Requires LIN_ADVANCE) //Stepper Movement Variables //=========================================================================== @@ -1598,6 +1603,22 @@ void get_command() #endif //SDSUPPORT } +#define WITHIN(V,L,H) ((V) >= (L) && (V) <= (H)) +#define NUMERIC(a) WITHIN(a, '0', '9') +#define NUMERIC_SIGNED(a) (NUMERIC(a) || (a) == '-') + +static char *current_command, // The command currently being executed + *current_command_args, // The address where arguments begin + *seen_pointer; // Set by code_seen(), used by the code_value functions + +inline bool code_has_value() { + int i = 1; + char c = seen_pointer[i]; + while (c == ' ') c = seen_pointer[++i]; + if (c == '-' || c == '+') c = seen_pointer[++i]; + if (c == '.') c = seen_pointer[++i]; + return NUMERIC(c); +} // Return True if a character was found static inline bool code_seen(char code) { return (strchr_pointer = strchr(CMDBUFFER_CURRENT_STRING, code)) != NULL; } @@ -1606,6 +1627,7 @@ static inline float code_value() { return strtod(strchr_pointer+1, NULL); static inline long code_value_long() { return strtol(strchr_pointer+1, NULL, 10); } static inline int16_t code_value_short() { return int16_t(strtol(strchr_pointer+1, NULL, 10)); }; static inline uint8_t code_value_uint8() { return uint8_t(strtol(strchr_pointer+1, NULL, 10)); }; +static inline bool code_value_bool() { return !code_has_value() || code_value_uint8() > 0; } #define DEFINE_PGM_READ_ANY(type, reader) \ static inline type pgm_read_any(const type *p) \ @@ -5335,6 +5357,33 @@ case 404: //M404 Enter the nominal filament width (3mm, 1.75mm ) N<3.0> or disp if(lcd_commands_type == 0) lcd_commands_type = LCD_COMMAND_LONG_PAUSE_RESUME; } break; + + case 900: { + + st_synchronize(); + + const float newK = code_seen('K') ? code_value() : -1; + if (newK >= 0) extruder_advance_k = newK; + + float newR = code_seen('R') ? code_value() : -1; + if (newR < 0) { + const float newD = code_seen('D') ? code_value() : -1, + newW = code_seen('W') ? code_value() : -1, + newH = code_seen('H') ? code_value() : -1; + if (newD >= 0 && newW >= 0 && newH >= 0) + newR = newD ? (newW * newH) / (sq(newD * 0.5) * M_PI) : 0; + } + if (newR >= 0) advance_ed_ratio = newR; + + SERIAL_ECHO_START; + SERIAL_ECHOPAIR("Advance K=", extruder_advance_k); + SERIAL_ECHOPGM(" E/D="); + const float ratio = advance_ed_ratio; + if (ratio) SERIAL_ECHO(ratio); else SERIAL_ECHOPGM("Auto"); + SERIAL_ECHO('\n'); + + } + break; case 907: // M907 Set digital trimpot motor current using axis codes. { diff --git a/Firmware/language_all.cpp b/Firmware/language_all.cpp index eddc4ce88..19f5b7812 100644 --- a/Firmware/language_all.cpp +++ b/Firmware/language_all.cpp @@ -27,6 +27,11 @@ const char * const MSG_ADJUSTZ_LANG_TABLE[LANG_NUM] PROGMEM = { MSG_ADJUSTZ_NL }; +const char MSG_ADVANCE_K_EN[] PROGMEM = "Advance K"; +const char * const MSG_ADVANCE_K_LANG_TABLE[1] PROGMEM = { + MSG_ADVANCE_K_EN +}; + const char MSG_AMAX_EN[] PROGMEM = "Amax "; const char * const MSG_AMAX_LANG_TABLE[1] PROGMEM = { MSG_AMAX_EN @@ -918,6 +923,11 @@ const char * const MSG_E_CAL_KNOB_LANG_TABLE[LANG_NUM] PROGMEM = { MSG_E_CAL_KNOB_NL }; +const char MSG_E_D_RATIO_EN[] PROGMEM = "E-D Ratio"; +const char * const MSG_E_D_RATIO_LANG_TABLE[1] PROGMEM = { + MSG_E_D_RATIO_EN +}; + const char MSG_Enqueing_EN[] PROGMEM = "enqueing \""; const char * const MSG_Enqueing_LANG_TABLE[1] PROGMEM = { MSG_Enqueing_EN diff --git a/Firmware/language_all.h b/Firmware/language_all.h index c6c27b7c6..22c8a882e 100644 --- a/Firmware/language_all.h +++ b/Firmware/language_all.h @@ -30,6 +30,8 @@ extern const char* const MSG_ACTIVE_EXTRUDER_LANG_TABLE[1]; #define MSG_ACTIVE_EXTRUDER LANG_TABLE_SELECT_EXPLICIT(MSG_ACTIVE_EXTRUDER_LANG_TABLE, 0) extern const char* const MSG_ADJUSTZ_LANG_TABLE[LANG_NUM]; #define MSG_ADJUSTZ LANG_TABLE_SELECT(MSG_ADJUSTZ_LANG_TABLE) +extern const char* const MSG_ADVANCE_K_LANG_TABLE[1]; +#define MSG_ADVANCE_K LANG_TABLE_SELECT_EXPLICIT(MSG_ADVANCE_K_LANG_TABLE, 0) extern const char* const MSG_AMAX_LANG_TABLE[1]; #define MSG_AMAX LANG_TABLE_SELECT_EXPLICIT(MSG_AMAX_LANG_TABLE, 0) extern const char* const MSG_AUTHOR_LANG_TABLE[1]; @@ -176,6 +178,8 @@ extern const char* const MSG_EXTERNAL_RESET_LANG_TABLE[1]; #define MSG_EXTERNAL_RESET LANG_TABLE_SELECT_EXPLICIT(MSG_EXTERNAL_RESET_LANG_TABLE, 0) extern const char* const MSG_E_CAL_KNOB_LANG_TABLE[LANG_NUM]; #define MSG_E_CAL_KNOB LANG_TABLE_SELECT(MSG_E_CAL_KNOB_LANG_TABLE) +extern const char* const MSG_E_D_RATIO_LANG_TABLE[1]; +#define MSG_E_D_RATIO LANG_TABLE_SELECT_EXPLICIT(MSG_E_D_RATIO_LANG_TABLE, 0) extern const char* const MSG_Enqueing_LANG_TABLE[1]; #define MSG_Enqueing LANG_TABLE_SELECT_EXPLICIT(MSG_Enqueing_LANG_TABLE, 0) extern const char* const MSG_FACTOR_LANG_TABLE[1]; diff --git a/Firmware/language_en.h b/Firmware/language_en.h index d1d4bc423..060eea9e8 100644 --- a/Firmware/language_en.h +++ b/Firmware/language_en.h @@ -46,6 +46,11 @@ #define MSG_REFRESH "\xF8" "Refresh" #define MSG_WATCH "Info screen" #define MSG_TUNE "Tune" + +//Linear Advance option +#define MSG_ADVANCE_K "Advance K" +#define MSG_E_D_RATIO "E-D Ratio" + #define MSG_PAUSE_PRINT "Pause print" #define MSG_RESUME_PRINT "Resume print" #define MSG_STOP_PRINT "Stop print" diff --git a/Firmware/planner.cpp b/Firmware/planner.cpp index bdfc4ca21..6686d08d5 100644 --- a/Firmware/planner.cpp +++ b/Firmware/planner.cpp @@ -57,12 +57,22 @@ #include "temperature.h" #include "ultralcd.h" #include "language.h" +#include "Configuration_prusa.h" #ifdef MESH_BED_LEVELING #include "mesh_bed_leveling.h" #include "mesh_bed_calibration.h" #endif +#define UNEAR_ZERO(x) ((x) < 0.000001) + +#if defined(LIN_ADVANCE) + float extruder_advance_k = LIN_ADVANCE_K, + advance_ed_ratio = LIN_ADVANCE_E_D_RATIO, + position_float[NUM_AXIS] = { 0 }; +#endif + + //=========================================================================== //=============================public variables ============================ //=========================================================================== @@ -411,6 +421,11 @@ void plan_init() { block_buffer_head = 0; block_buffer_tail = 0; memset(position, 0, sizeof(position)); // clear position + + #if defined(LIN_ADVANCE) + memset(position_float, 0, sizeof(position_float)); + #endif + previous_speed[0] = 0.0; previous_speed[1] = 0.0; previous_speed[2] = 0.0; @@ -675,13 +690,32 @@ void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate #else target[Z_AXIS] = lround(z*axis_steps_per_unit[Z_AXIS]); #endif // ENABLE_MESH_BED_LEVELING + + #if defined(LIN_ADVANCE) + const float mm_D_float = sqrt(sq(target[X_AXIS] - position_float[X_AXIS]) + sq(target[Y_AXIS] - position_float[Y_AXIS])); + #endif + + long de = target[E_AXIS] - position[E_AXIS]; + target[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); + + #if defined(LIN_ADVANCE) + float de_float = target[E_AXIS] - position_float[E_AXIS]; + #endif + #ifdef PREVENT_DANGEROUS_EXTRUDE if(target[E_AXIS]!=position[E_AXIS]) { if(degHotend(active_extruder)axis_steps_per_unit[E_AXIS]*EXTRUDE_MAXLENGTH) { position[E_AXIS]=target[E_AXIS]; //behave as if the move really took place, but ignore E part + + #if defined(LIN_ADVANCE) + position_float[E_AXIS] = e; + de_float = 0; + #endif + SERIAL_ECHO_START; SERIAL_ECHOLNRPGM(MSG_ERR_LONG_EXTRUDE_STOP); } @@ -1111,6 +1151,41 @@ Having the real displacement of the head, we can calculate the total movement le memcpy(previous_speed, current_speed, sizeof(previous_speed)); // previous_speed[] = current_speed[] previous_nominal_speed = block->nominal_speed; previous_safe_speed = safe_speed; + + #if defined(LIN_ADVANCE) + + // + // Use LIN_ADVANCE for blocks if all these are true: + // + // esteps : We have E steps todo (a printing move) + // + // block->steps[X_AXIS] || block->steps[Y_AXIS] : We have a movement in XY direction (i.e., not retract / prime). + // + // extruder_advance_k : There is an advance factor set. + // + // block->steps[E_AXIS] != block->step_event_count : A problem occurs if the move before a retract is too small. + // In that case, the retract and move will be executed together. + // This leads to too many advance steps due to a huge e_acceleration. + // The math is good, but we must avoid retract moves with advance! + // de_float > 0.0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves) + // + + float tmp1[] = DEFAULT_AXIS_STEPS_PER_UNIT; + + + block->use_advance_lead = delta_mm[E_AXIS] + && (block->steps_x || block->steps_y) + && extruder_advance_k + && (uint32_t)delta_mm[E_AXIS] != block->step_event_count + && de_float > 0.0; + if (block->use_advance_lead) + block->abs_adv_steps_multiplier8 = lround( + extruder_advance_k + * (UNEAR_ZERO(advance_ed_ratio) ? de_float / mm_D_float : advance_ed_ratio) // Use the fixed ratio, if set + * (block->nominal_speed / (float)block->nominal_rate) + * tmp1[E_AXIS] * 256.0 + ); + #endif // Precalculate the division, so when all the trapezoids in the planner queue get recalculated, the division is not repeated. block->speed_factor = block->nominal_rate / block->nominal_speed; @@ -1121,6 +1196,13 @@ Having the real displacement of the head, we can calculate the total movement le // Update position memcpy(position, target, sizeof(target)); // position[] = target[] + + #if defined(LIN_ADVANCE) + position_float[X_AXIS] = target[X_AXIS]; + position_float[Y_AXIS] = target[Y_AXIS]; + position_float[Z_AXIS] = target[Z_AXIS]; + position_float[E_AXIS] = target[E_AXIS]; + #endif // Recalculate the trapezoids to maximize speed at the segment transitions while respecting // the machine limits (maximum acceleration and maximum jerk). @@ -1178,8 +1260,16 @@ void plan_set_position(float x, float y, float z, const float &e) #else position[Z_AXIS] = lround(z*axis_steps_per_unit[Z_AXIS]); #endif // ENABLE_MESH_BED_LEVELING - position[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); - st_set_position(position[X_AXIS], position[Y_AXIS], position[Z_AXIS], position[E_AXIS]); + position[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); + + #if defined(LIN_ADVANCE) + position_float[X_AXIS] = position[X_AXIS]; + position_float[Y_AXIS] = position[Y_AXIS]; + position_float[Z_AXIS] = position[Z_AXIS]; + position_float[E_AXIS] = position[E_AXIS]; + #endif + + st_set_position(position_float[X_AXIS], position_float[Y_AXIS], position_float[Z_AXIS], position_float[E_AXIS]); previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. previous_speed[0] = 0.0; previous_speed[1] = 0.0; @@ -1197,7 +1287,12 @@ void plan_set_z_position(const float &z) void plan_set_e_position(const float &e) { position[E_AXIS] = lround(e*axis_steps_per_unit[E_AXIS]); + + #if defined(LIN_ADVANCE) + position_float[E_AXIS] = e; + #endif st_set_e_position(position[E_AXIS]); + previous_speed[E_AXIS] = 0.0; } #ifdef PREVENT_DANGEROUS_EXTRUDE diff --git a/Firmware/planner.h b/Firmware/planner.h index 30194952d..1ceb34d79 100644 --- a/Firmware/planner.h +++ b/Firmware/planner.h @@ -42,6 +42,12 @@ enum BlockFlag { BLOCK_FLAG_START_FROM_FULL_HALT = 4, }; + #if defined(LIN_ADVANCE) + extern float extruder_advance_k; + extern float advance_ed_ratio; + extern float position_float[NUM_AXIS]; + #endif + // This struct is used when buffering the setup for each linear movement "nominal" values are as specified in // the source g-code and may never actually be reached if acceleration management is active. typedef struct { @@ -55,6 +61,12 @@ typedef struct { // accelerate_until and decelerate_after are set by calculate_trapezoid_for_block() and they need to be synchronized with the stepper interrupt controller. long accelerate_until; // The index of the step event on which to stop acceleration long decelerate_after; // The index of the step event on which to start decelerating + + // Advance extrusion + #if defined(LIN_ADVANCE) + bool use_advance_lead; + uint32_t abs_adv_steps_multiplier8; // Factorised by 2^8 to avoid float + #endif // Fields used by the motion planner to manage acceleration // float speed_x, speed_y, speed_z, speed_e; // Nominal mm/sec for each axis diff --git a/Firmware/stepper.cpp b/Firmware/stepper.cpp index b92f54ee1..8cb9f9cc3 100644 --- a/Firmware/stepper.cpp +++ b/Firmware/stepper.cpp @@ -88,6 +88,48 @@ int8_t SilentMode; volatile long count_position[NUM_AXIS] = { 0, 0, 0, 0}; volatile signed char count_direction[NUM_AXIS] = { 1, 1, 1, 1}; + +#if defined(LIN_ADVANCE) + + constexpr uint16_t ADV_NEVER = 65535; + + uint16_t nextMainISR = 0, + nextAdvanceISR = ADV_NEVER, + eISR_Rate = ADV_NEVER; + + #if defined(LIN_ADVANCE) + volatile int e_steps[EXTRUDERS]; + int final_estep_rate, + current_estep_rate[EXTRUDERS], + current_adv_steps[EXTRUDERS]; + #endif + + #define ADV_RATE(T, L) (e_steps[TOOL_E_INDEX] ? (T) * (L) / abs(e_steps[TOOL_E_INDEX]) : ADV_NEVER) + +#endif + +// Macros for bit masks +#define TEST(n,b) (((n)&_BV(b))!=0) +#define SBI(n,b) (n |= _BV(b)) +#define CBI(n,b) (n &= ~_BV(b)) +#define SET_BIT(n,b,value) (n) ^= ((-value)^(n)) & (_BV(b)) + +unsigned char last_direction_bits = 0; + +// +// The direction of a single motor +// +static FORCE_INLINE bool motor_direction(AxisEnum axis) { return TEST(last_direction_bits, axis); } + +#define TOOL_E_INDEX current_block->active_extruder + +// Macros to contrain values +#define NOLESS(v,n) do{ if (v < n) v = n; }while(0) +#define NOMORE(v,n) do{ if (v > n) v = n; }while(0) + + +#define _ENABLE_ISRs() do { cli(); SBI(TIMSK0, OCIE0B); ENABLE_STEPPER_DRIVER_INTERRUPT(); } while(0) + //=========================================================================== //=============================functions ============================ //=========================================================================== @@ -320,12 +362,50 @@ FORCE_INLINE void trapezoid_generator_reset() { // SERIAL_ECHOPGM("final advance :"); // SERIAL_ECHOLN(current_block->final_advance/256.0); + #if defined(LIN_ADVANCE) + if (current_block->use_advance_lead) { + current_estep_rate[current_block->active_extruder] = ((unsigned long)acc_step_rate * current_block->abs_adv_steps_multiplier8) >> 17; + final_estep_rate = (current_block->nominal_rate * current_block->abs_adv_steps_multiplier8) >> 17; + } + #endif + } +/** + * Stepper Driver Interrupt + * + * Directly pulses the stepper motors at high frequency. + * Timer 1 runs at a base frequency of 2MHz, with this ISR using OCR1A compare mode. + * + * OCR1A Frequency + * 1 2 MHz + * 50 40 KHz + * 100 20 KHz - capped max rate + * 200 10 KHz - nominal max rate + * 2000 1 KHz - sleep rate + * 4000 500 Hz - init rate + */ +ISR(TIMER1_COMPA_vect) { + #if defined(LIN_ADVANCE) + advance_isr_scheduler(); + #else + isr(); + #endif +} + + // "The Stepper Driver Interrupt" - This timer interrupt is the workhorse. // It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. -ISR(TIMER1_COMPA_vect) +void isr() { + +#ifndef LIN_ADVANCE + // Disable Timer0 ISRs and enable global ISR again to capture UART events (incoming chars) + CBI(TIMSK0, OCIE0B); // Temperature ISR + DISABLE_STEPPER_DRIVER_INTERRUPT(); + sei(); + #endif + // If there is no current block, attempt to pop one from the buffer if (current_block == NULL) { // Anything in the buffer? @@ -344,12 +424,15 @@ ISR(TIMER1_COMPA_vect) if(current_block->steps_z > 0) { enable_z(); OCR1A = 2000; //1ms wait + _ENABLE_ISRs(); return; } #endif } else { OCR1A=2000; // 1kHz. + _ENABLE_ISRs(); + return; } } @@ -515,10 +598,11 @@ ISR(TIMER1_COMPA_vect) } #endif +#ifndef LIN_ADVANCE if ((out_bits & (1 << E_AXIS)) != 0) { // -direction //AKU -#ifdef SNMM + #ifdef SNMM if (snmm_extruder == 0 || snmm_extruder == 2) { NORM_E_DIR(); @@ -527,14 +611,14 @@ ISR(TIMER1_COMPA_vect) { REV_E_DIR(); } -#else + #else REV_E_DIR(); -#endif // SNMM + #endif // SNMM count_direction[E_AXIS] = -1; } else { // +direction -#ifdef SNMM + #ifdef SNMM if (snmm_extruder == 0 || snmm_extruder == 2) { REV_E_DIR(); @@ -543,16 +627,29 @@ ISR(TIMER1_COMPA_vect) { NORM_E_DIR(); } -#else + #else NORM_E_DIR(); -#endif // SNMM + #endif // SNMM count_direction[E_AXIS] = 1; } +#endif for(uint8_t i=0; i < step_loops; i++) { // Take multiple steps per interrupt (For high speed moves) #ifndef AT90USB MSerial.checkRx(); // Check for serial chars. #endif + + #if defined(LIN_ADVANCE) + counter_e += current_block->steps_e; + if (counter_e > 0) { + counter_e -= current_block->step_event_count; + #ifndef MIXING_EXTRUDER + // Don't step E here for mixing extruder + count_position[E_AXIS] += count_direction[E_AXIS]; + motor_direction(E_AXIS) ? --e_steps[TOOL_E_INDEX] : ++e_steps[TOOL_E_INDEX]; + #endif + } + #endif //LIN_ADVANCE counter_x += current_block->steps_x; if (counter_x > 0) { @@ -596,6 +693,7 @@ ISR(TIMER1_COMPA_vect) #endif } + #ifndef LIN_ADVANCE counter_e += current_block->steps_e; if (counter_e > 0) { WRITE_E_STEP(!INVERT_E_STEP_PIN); @@ -603,6 +701,7 @@ ISR(TIMER1_COMPA_vect) count_position[E_AXIS]+=count_direction[E_AXIS]; WRITE_E_STEP(INVERT_E_STEP_PIN); } + #endif step_events_completed += 1; if(step_events_completed >= current_block->step_event_count) break; } @@ -622,6 +721,15 @@ ISR(TIMER1_COMPA_vect) timer = calc_timer(acc_step_rate); OCR1A = timer; acceleration_time += timer; + + #if defined(LIN_ADVANCE) + + if (current_block->use_advance_lead) + { + current_estep_rate[TOOL_E_INDEX] = ((uint32_t)acc_step_rate * current_block->abs_adv_steps_multiplier8) >> 17; + } + eISR_Rate = ADV_RATE(timer, step_loops); + #endif } else if (step_events_completed > (unsigned long int)current_block->decelerate_after) { MultiU24X24toH16(step_rate, deceleration_time, current_block->acceleration_rate); @@ -641,21 +749,161 @@ ISR(TIMER1_COMPA_vect) timer = calc_timer(step_rate); OCR1A = timer; deceleration_time += timer; + + #if defined(LIN_ADVANCE) + + if (current_block->use_advance_lead) + { + current_estep_rate[TOOL_E_INDEX] = ((uint32_t)step_rate * current_block->abs_adv_steps_multiplier8) >> 17; + } + eISR_Rate = ADV_RATE(timer, step_loops); + #endif } else { 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; } + + #ifndef LIN_ADVANCE + NOLESS(OCR1A, TCNT1 + 16); + #endif // If current block is finished, reset pointer if (step_events_completed >= current_block->step_event_count) { current_block = NULL; plan_discard_current_block(); } + + #ifndef LIN_ADVANCE + _ENABLE_ISRs(); // re-enable ISRs + #endif } } + +#if defined LIN_ADVANCE + + #define CYCLES_EATEN_E (E_STEPPERS * 5) + #define EXTRA_CYCLES_E (STEP_PULSE_CYCLES - (CYCLES_EATEN_E)) + + // Timer interrupt for E. e_steps is set in the main routine; + + void advance_isr() + { + + nextAdvanceISR = eISR_Rate; + + #define SET_E_STEP_DIR(INDEX) \ + if (e_steps[INDEX]) E## INDEX ##_DIR_WRITE(e_steps[INDEX] < 0 ? INVERT_E## INDEX ##_DIR : !INVERT_E## INDEX ##_DIR) + + #define START_E_PULSE(INDEX) \ + if (e_steps[INDEX]) E## INDEX ##_STEP_WRITE(!INVERT_E_STEP_PIN) + + #define STOP_E_PULSE(INDEX) \ + if (e_steps[INDEX]) { \ + e_steps[INDEX] < 0 ? ++e_steps[INDEX] : --e_steps[INDEX]; \ + E## INDEX ##_STEP_WRITE(INVERT_E_STEP_PIN); \ + } + + SET_E_STEP_DIR(0); + #if E_STEPPERS > 1 + SET_E_STEP_DIR(1); + #if E_STEPPERS > 2 + SET_E_STEP_DIR(2); + #if E_STEPPERS > 3 + SET_E_STEP_DIR(3); + #endif + #endif + #endif + + // Step all E steppers that have steps + for (uint8_t i = step_loops; i--;) { + + #if EXTRA_CYCLES_E > 20 + uint32_t pulse_start = TCNT0; + #endif + + START_E_PULSE(0); + #if E_STEPPERS > 1 + START_E_PULSE(1); + #if E_STEPPERS > 2 + START_E_PULSE(2); + #if E_STEPPERS > 3 + START_E_PULSE(3); + #endif + #endif + #endif + + // For minimum pulse time wait before stopping pulses + #if EXTRA_CYCLES_E > 20 + while (EXTRA_CYCLES_E > (uint32_t)(TCNT0 - pulse_start) * (INT0_PRESCALER)) { /* nada */ } + pulse_start = TCNT0; + #elif EXTRA_CYCLES_E > 0 + DELAY_NOPS(EXTRA_CYCLES_E); + #endif + + STOP_E_PULSE(0); + #if E_STEPPERS > 1 + STOP_E_PULSE(1); + #if E_STEPPERS > 2 + STOP_E_PULSE(2); + #if E_STEPPERS > 3 + STOP_E_PULSE(3); + #endif + #endif + #endif + + // For minimum pulse time wait before looping + #if EXTRA_CYCLES_E > 20 + if (i) while (EXTRA_CYCLES_E > (uint32_t)(TCNT0 - pulse_start) * (INT0_PRESCALER)) { /* nada */ } + #elif EXTRA_CYCLES_E > 0 + if (i) DELAY_NOPS(EXTRA_CYCLES_E); + #endif + + } // steps_loop + } + + void advance_isr_scheduler() { + // Disable Timer0 ISRs and enable global ISR again to capture UART events (incoming chars) + CBI(TIMSK0, OCIE0B); // Temperature ISR + DISABLE_STEPPER_DRIVER_INTERRUPT(); + sei(); + + // Run main stepping ISR if flagged + if (!nextMainISR) isr(); + + // Run Advance stepping ISR if flagged + if (!nextAdvanceISR) advance_isr(); + + // Is the next advance ISR scheduled before the next main ISR? + if (nextAdvanceISR <= nextMainISR) { + // Set up the next interrupt + OCR1A = nextAdvanceISR; + // New interval for the next main ISR + if (nextMainISR) nextMainISR -= nextAdvanceISR; + // Will call Stepper::advance_isr on the next interrupt + nextAdvanceISR = 0; + } + else { + // The next main ISR comes first + OCR1A = nextMainISR; + // New interval for the next advance ISR, if any + if (nextAdvanceISR && nextAdvanceISR != ADV_NEVER) + nextAdvanceISR -= nextMainISR; + // Will call Stepper::isr on the next interrupt + nextMainISR = 0; + } + + // Don't run the ISR faster than possible + NOLESS(OCR1A, TCNT1 + 16); + + // Restore original ISR settings + _ENABLE_ISRs(); + } + +#endif // ADVANCE or LIN_ADVANCE + void st_init() { digipot_init(); //Initialize Digipot Motor Current @@ -843,6 +1091,17 @@ void st_init() OCR1A = 0x4000; TCNT1 = 0; ENABLE_STEPPER_DRIVER_INTERRUPT(); + + #ifdef LIN_ADVANCE + #if defined(TCCR0A) && defined(WGM01) + TCCR0A &= ~(1< 2 #define WRITE_E_STEP(v) { if(current_block->active_extruder == 2) { WRITE(E2_STEP_PIN, v); } else { if(current_block->active_extruder == 1) { WRITE(E1_STEP_PIN, v); } else { WRITE(E0_STEP_PIN, v); }}} @@ -41,6 +42,28 @@ extern bool abort_on_endstop_hit; #endif + #if defined(LIN_ADVANCE) + extern uint16_t nextMainISR, nextAdvanceISR, eISR_Rate; + #define _NEXT_ISR(T) nextMainISR = T + #if defined(LIN_ADVANCE) + extern volatile int e_steps[EXTRUDERS]; + extern int final_estep_rate; + extern int current_estep_rate[EXTRUDERS]; // Actual extruder speed [steps/s] + extern 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). + #endif +#else + #define _NEXT_ISR(T) OCR1A = T +#endif // ADVANCE or LIN_ADVANCE + +#if defined(LIN_ADVANCE) + extern void advance_isr(); + extern void advance_isr_scheduler(); +#endif + extern unsigned char last_direction_bits; // The next stepping-bits to be output + + // Initialize and start the stepper motor subsystem void st_init(); diff --git a/Firmware/stepper_indirection.h b/Firmware/stepper_indirection.h new file mode 100644 index 000000000..a540980c3 --- /dev/null +++ b/Firmware/stepper_indirection.h @@ -0,0 +1,119 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + stepper_indirection.h - stepper motor driver indirection macros + to allow some stepper functions to be done via SPI/I2c instead of direct pin manipulation + Part of Marlin + + Copyright (c) 2015 Dominik Wenger + + Marlin is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Marlin is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Marlin. If not, see . +*/ + +#ifndef STEPPER_INDIRECTION_H +#define STEPPER_INDIRECTION_H + +#if defined(HAVE_TMC2130) + #include "TMC2130Stepper.h" + void tmc2130_init(); +#endif + +// X Stepper + #if defined(HAVE_TMC2130) && defined(X_IS_TMC2130) + extern TMC2130Stepper stepperX; + #endif + +#define X_ENABLE_INIT SET_OUTPUT(X_ENABLE_PIN) +#define X_ENABLE_WRITE(STATE) WRITE(X_ENABLE_PIN,STATE) +#define X_ENABLE_READ READ(X_ENABLE_PIN) + +#define X_DIR_INIT SET_OUTPUT(X_DIR_PIN) +#define X_DIR_WRITE(STATE) WRITE(X_DIR_PIN,STATE) +#define X_DIR_READ READ(X_DIR_PIN) + +#define X_STEP_INIT SET_OUTPUT(X_STEP_PIN) +#define X_STEP_WRITE(STATE) WRITE(X_STEP_PIN,STATE) +#define X_STEP_READ READ(X_STEP_PIN) + +// Y Stepper + #if defined(HAVE_TMC2130) && defined(Y_IS_TMC2130) + extern TMC2130Stepper stepperY; + #endif + +#define Y_ENABLE_INIT SET_OUTPUT(Y_ENABLE_PIN) +#define Y_ENABLE_WRITE(STATE) WRITE(Y_ENABLE_PIN,STATE) +#define Y_ENABLE_READ READ(Y_ENABLE_PIN) + +#define Y_DIR_INIT SET_OUTPUT(Y_DIR_PIN) +#define Y_DIR_WRITE(STATE) WRITE(Y_DIR_PIN,STATE) +#define Y_DIR_READ READ(Y_DIR_PIN) + +#define Y_STEP_INIT SET_OUTPUT(Y_STEP_PIN) +#define Y_STEP_WRITE(STATE) WRITE(Y_STEP_PIN,STATE) +#define Y_STEP_READ READ(Y_STEP_PIN) + +// Z Stepper + #if defined(HAVE_TMC2130) && defined(Z_IS_TMC2130) + extern TMC2130Stepper stepperZ; + #endif + +#define Z_ENABLE_INIT SET_OUTPUT(Z_ENABLE_PIN) +#define Z_ENABLE_WRITE(STATE) WRITE(Z_ENABLE_PIN,STATE) +#define Z_ENABLE_READ READ(Z_ENABLE_PIN) + +#define Z_DIR_INIT SET_OUTPUT(Z_DIR_PIN) +#define Z_DIR_WRITE(STATE) WRITE(Z_DIR_PIN,STATE) +#define Z_DIR_READ READ(Z_DIR_PIN) + +#define Z_STEP_INIT SET_OUTPUT(Z_STEP_PIN) +#define Z_STEP_WRITE(STATE) WRITE(Z_STEP_PIN,STATE) +#define Z_STEP_READ READ(Z_STEP_PIN) + +// E0 Stepper + #if defined(HAVE_TMC2130) && defined(E0_IS_TMC2130) + extern TMC2130Stepper stepperE0; + #endif +#define E0_ENABLE_INIT SET_OUTPUT(E0_ENABLE_PIN) +#define E0_ENABLE_WRITE(STATE) WRITE(E0_ENABLE_PIN,STATE) +#define E0_ENABLE_READ READ(E0_ENABLE_PIN) + +#define E0_DIR_INIT SET_OUTPUT(E0_DIR_PIN) +#define E0_DIR_WRITE(STATE) WRITE(E0_DIR_PIN,STATE) +#define E0_DIR_READ READ(E0_DIR_PIN) + +#define E0_STEP_INIT SET_OUTPUT(E0_STEP_PIN) +#define E0_STEP_WRITE(STATE) WRITE(E0_STEP_PIN,STATE) +#define E0_STEP_READ READ(E0_STEP_PIN) + +#endif // STEPPER_INDIRECTION_H diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index 8ac34d93d..f7f45c78f 100644 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -3720,6 +3720,11 @@ static void lcd_tune_menu() } else { MENU_ITEM(function, MSG_SILENT_MODE_ON, lcd_silent_mode_set_tune); } + + #if defined(LIN_ADVANCE) + MENU_ITEM_EDIT(float3, MSG_ADVANCE_K, &extruder_advance_k, 0, 999); + MENU_ITEM_EDIT(float3, MSG_E_D_RATIO, &advance_ed_ratio, 0, 999); + #endif END_MENU(); } diff --git a/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h b/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h index 55dd6b9b4..11a0c5d91 100644 --- a/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h @@ -28,6 +28,49 @@ GENERAL SETTINGS //#define E3D_PT100_BED_NO_AMP +// Linear Advance feature - EXPERIMENTAL! +/** + * Implementation of linear pressure control + * + * Assumption: advance = k * (delta velocity) + * K=0 means advance disabled. + * See Marlin documentation for calibration instructions. + */ +#define LIN_ADVANCE + +#if defined(LIN_ADVANCE) + #define LIN_ADVANCE_K 75 + + /** + * Some Slicers produce Gcode with randomly jumping extrusion widths occasionally. + * For example within a 0.4mm perimeter it may produce a single segment of 0.05mm width. + * While this is harmless for normal printing (the fluid nature of the filament will + * close this very, very tiny gap), it throws off the LIN_ADVANCE pressure adaption. + * + * For this case LIN_ADVANCE_E_D_RATIO can be used to set the extrusion:distance ratio + * to a fixed value. Note that using a fixed ratio will lead to wrong nozzle pressures + * if the slicer is using variable widths or layer heights within one print! + * + * This option sets the default E:D ratio at startup. Use `M900` to override this value. + * + * Example: `M900 W0.4 H0.2 D1.75`, where: + * - W is the extrusion width in mm + * - H is the layer height in mm + * - D is the filament diameter in mm + * + * Example: `M900 R0.0458` to set the ratio directly. + * + * Set to 0 to auto-detect the ratio based on given Gcode G1 print moves. + * + * Slic3r (including Prusa Slic3r) produces Gcode compatible with the automatic mode. + * Cura (as of this writing) may produce Gcode incompatible with the automatic mode. + */ + #define LIN_ADVANCE_E_D_RATIO 0.033260135 // The calculated ratio (or 0) according to the formula W * H / ((D / 2) ^ 2 * PI) + // Example: 0.4 * 0.2 / ((1.75 / 2) ^ 2 * PI) = 0.033260135 +#endif + + + /*------------------------------------ AXIS SETTINGS *------------------------------------*/