diff --git a/Marlin/Conditionals_LCD.h b/Marlin/Conditionals_LCD.h index c9d44b1be3..2cd7504326 100644 --- a/Marlin/Conditionals_LCD.h +++ b/Marlin/Conditionals_LCD.h @@ -380,12 +380,6 @@ #define HAS_DEBUG_MENU (ENABLED(ULTIPANEL) && ENABLED(LCD_PROGRESS_BAR_TEST)) -// MK2 Multiplexer forces SINGLENOZZLE and kills DISABLE_INACTIVE_EXTRUDER -#if ENABLED(MK2_MULTIPLEXER) - #define SINGLENOZZLE - #undef DISABLE_INACTIVE_EXTRUDER -#endif - /** * Extruders have some combination of stepper motors and hotends * so we separate these concepts into the defines: @@ -396,25 +390,17 @@ * E_MANUAL - Number of E steppers for LCD move options * */ -#if ENABLED(SINGLENOZZLE) || ENABLED(MIXING_EXTRUDER) // One hotend, one thermistor, no XY offset - #define HOTENDS 1 - #undef TEMP_SENSOR_1_AS_REDUNDANT - #undef HOTEND_OFFSET_X - #undef HOTEND_OFFSET_Y -#else // Two hotends - #define HOTENDS EXTRUDERS -#endif - #if ENABLED(SWITCHING_EXTRUDER) // One stepper for every two EXTRUDERS #if EXTRUDERS > 4 #define E_STEPPERS 3 - #define E_MANUAL 3 #elif EXTRUDERS > 2 #define E_STEPPERS 2 - #define E_MANUAL 2 #else #define E_STEPPERS 1 #endif + #if DISABLED(SWITCHING_NOZZLE) + #define HOTENDS E_STEPPERS + #endif #define E_MANUAL EXTRUDERS #elif ENABLED(MIXING_EXTRUDER) #define E_STEPPERS MIXING_STEPPERS @@ -424,6 +410,28 @@ #define E_MANUAL EXTRUDERS #endif +// No inactive extruders with MK2_MULTIPLEXER or SWITCHING_NOZZLE +#if ENABLED(MK2_MULTIPLEXER) || ENABLED(SWITCHING_NOZZLE) + #undef DISABLE_INACTIVE_EXTRUDER +#endif + +// MK2 Multiplexer forces SINGLENOZZLE +#if ENABLED(MK2_MULTIPLEXER) + #define SINGLENOZZLE +#endif + +#if ENABLED(SINGLENOZZLE) || ENABLED(MIXING_EXTRUDER) // One hotend, one thermistor, no XY offset + #undef HOTENDS + #define HOTENDS 1 + #undef TEMP_SENSOR_1_AS_REDUNDANT + #undef HOTEND_OFFSET_X + #undef HOTEND_OFFSET_Y +#endif + +#ifndef HOTENDS + #define HOTENDS EXTRUDERS +#endif + #define DO_SWITCH_EXTRUDER (ENABLED(SWITCHING_EXTRUDER) && (DISABLED(SWITCHING_NOZZLE) || SWITCHING_EXTRUDER_SERVO_NR != SWITCHING_NOZZLE_SERVO_NR)) /** diff --git a/Marlin/Conditionals_post.h b/Marlin/Conditionals_post.h index 2e91e44097..924ac46ba6 100644 --- a/Marlin/Conditionals_post.h +++ b/Marlin/Conditionals_post.h @@ -205,15 +205,6 @@ #define MAX_AUTORETRACT 99 #endif -/** - * MAX_STEP_FREQUENCY differs for TOSHIBA - */ -#if ENABLED(CONFIG_STEPPERS_TOSHIBA) - #define MAX_STEP_FREQUENCY 10000 // Max step frequency for Toshiba Stepper Controllers -#else - #define MAX_STEP_FREQUENCY 40000 // Max step frequency for Ultimaker (5000 pps / half step) -#endif - // MS1 MS2 Stepper Driver Microstepping mode table #define MICROSTEP1 LOW,LOW #if ENABLED(HEROIC_STEPPER_DRIVERS) @@ -616,24 +607,27 @@ #define HAS_X2_ENABLE (PIN_EXISTS(X2_ENABLE)) #define HAS_X2_DIR (PIN_EXISTS(X2_DIR)) #define HAS_X2_STEP (PIN_EXISTS(X2_STEP)) -#define HAS_Y_MICROSTEPS (PIN_EXISTS(Y_MS1)) +#define HAS_X2_MICROSTEPS (PIN_EXISTS(X2_MS1)) #define HAS_Y_ENABLE (PIN_EXISTS(Y_ENABLE)) #define HAS_Y_DIR (PIN_EXISTS(Y_DIR)) #define HAS_Y_STEP (PIN_EXISTS(Y_STEP)) -#define HAS_Z_MICROSTEPS (PIN_EXISTS(Z_MS1)) +#define HAS_Y_MICROSTEPS (PIN_EXISTS(Y_MS1)) #define HAS_Y2_ENABLE (PIN_EXISTS(Y2_ENABLE)) #define HAS_Y2_DIR (PIN_EXISTS(Y2_DIR)) #define HAS_Y2_STEP (PIN_EXISTS(Y2_STEP)) +#define HAS_Y2_MICROSTEPS (PIN_EXISTS(Y2_MS1)) #define HAS_Z_ENABLE (PIN_EXISTS(Z_ENABLE)) #define HAS_Z_DIR (PIN_EXISTS(Z_DIR)) #define HAS_Z_STEP (PIN_EXISTS(Z_STEP)) +#define HAS_Z_MICROSTEPS (PIN_EXISTS(Z_MS1)) #define HAS_Z2_ENABLE (PIN_EXISTS(Z2_ENABLE)) #define HAS_Z2_DIR (PIN_EXISTS(Z2_DIR)) #define HAS_Z2_STEP (PIN_EXISTS(Z2_STEP)) +#define HAS_Z2_MICROSTEPS (PIN_EXISTS(Z2_MS1)) // Extruder steppers and solenoids #define HAS_E0_ENABLE (PIN_EXISTS(E0_ENABLE)) @@ -984,6 +978,7 @@ #define PLANNER_LEVELING (OLDSCHOOL_ABL || ENABLED(MESH_BED_LEVELING) || UBL_SEGMENTED || ENABLED(SKEW_CORRECTION)) #define HAS_PROBING_PROCEDURE (HAS_ABL || ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)) #define HAS_UBL_AND_CURVES (ENABLED(AUTO_BED_LEVELING_UBL) && !PLANNER_LEVELING && (ENABLED(ARC_SUPPORT) || ENABLED(BEZIER_CURVE_SUPPORT))) +#define HAS_FEEDRATE_SCALING (ENABLED(SCARA_FEEDRATE_SCALING) || ENABLED(DELTA_FEEDRATE_SCALING)) #if ENABLED(AUTO_BED_LEVELING_UBL) #undef LCD_BED_LEVELING @@ -1247,9 +1242,6 @@ #define MANUAL_PROBE_HEIGHT Z_HOMING_HEIGHT #endif -// Stepper pulse duration, in cycles -#define STEP_PULSE_CYCLES ((MINIMUM_STEPPER_PULSE) * CYCLES_PER_MICROSECOND) - // Updated G92 behavior shifts the workspace #define HAS_POSITION_SHIFT DISABLED(NO_WORKSPACE_OFFSETS) // The home offset also shifts the coordinate space diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 7e8822f901..f5ab5c0d35 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -722,7 +722,7 @@ #if ENABLED(BABYSTEPPING) //#define BABYSTEP_XY // Also enable X/Y Babystepping. Not supported on DELTA! #define BABYSTEP_INVERT_Z false // Change if Z babysteps should go the other way - #define BABYSTEP_MULTIPLICATOR 1 // Babysteps are very small. Increase for faster motion. + #define BABYSTEP_MULTIPLICATOR 25 // Babysteps are very small. Increase for faster motion. #define BABYSTEP_ZPROBE_OFFSET // Enable to combine M851 and Babystepping #define DOUBLECLICK_FOR_Z_BABYSTEPPING // Double-click on the Status Screen for Z Babystepping. #define DOUBLECLICK_MAX_INTERVAL 1250 // Maximum interval between clicks, in milliseconds. diff --git a/Marlin/HAL.h b/Marlin/HAL.h index e4c2f805a2..bb985881c9 100644 --- a/Marlin/HAL.h +++ b/Marlin/HAL.h @@ -48,10 +48,14 @@ // Bracket code that shouldn't be interrupted #ifndef CRITICAL_SECTION_START - #define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli(); - #define CRITICAL_SECTION_END SREG = _sreg; + #define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli() + #define CRITICAL_SECTION_END SREG = _sreg #endif +#define ISRS_ENABLED() TEST(SREG, SREG_I) +#define ENABLE_ISRS() sei() +#define DISABLE_ISRS() cli() + // -------------------------------------------------------------------------- // Types // -------------------------------------------------------------------------- @@ -89,22 +93,17 @@ inline uint8_t HAL_get_reset_source(void) { return MCUSR; } #define STEP_TIMER_NUM 1 #define TEMP_TIMER_NUM 0 -#define PULSE_TIMER_NUM TEMP_TIMER_NUM - -#define HAL_STEPPER_TIMER_RATE HAL_TIMER_RATE -#define HAL_TICKS_PER_US ((HAL_STEPPER_TIMER_RATE) / 1000000) // Cannot be of type double -#define STEPPER_TIMER_PRESCALE 8 -#define STEP_TIMER_MIN_INTERVAL 8 // minimum time in µs between stepper interrupts +#define PULSE_TIMER_NUM STEP_TIMER_NUM #define TEMP_TIMER_FREQUENCY ((F_CPU) / 64.0 / 256.0) -#define TIMER_OCR_1 OCR1A -#define TIMER_COUNTER_1 TCNT1 +#define STEPPER_TIMER_RATE HAL_TIMER_RATE +#define STEPPER_TIMER_PRESCALE 8 +#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // Cannot be of type double -#define TIMER_OCR_0 OCR0A -#define TIMER_COUNTER_0 TCNT0 - -#define PULSE_TIMER_PRESCALE 8 +#define PULSE_TIMER_RATE STEPPER_TIMER_RATE // frequency of pulse timer +#define PULSE_TIMER_PRESCALE STEPPER_TIMER_PRESCALE +#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US #define ENABLE_STEPPER_DRIVER_INTERRUPT() SBI(TIMSK1, OCIE1A) #define DISABLE_STEPPER_DRIVER_INTERRUPT() CBI(TIMSK1, OCIE1A) @@ -114,7 +113,42 @@ inline uint8_t HAL_get_reset_source(void) { return MCUSR; } #define DISABLE_TEMPERATURE_INTERRUPT() CBI(TIMSK0, OCIE0B) #define TEMPERATURE_ISR_ENABLED() TEST(TIMSK0, OCIE0B) -#define HAL_timer_start(timer_num, frequency) +FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { + UNUSED(frequency); + switch (timer_num) { + case STEP_TIMER_NUM: + // waveform generation = 0100 = CTC + SET_WGM(1, CTC_OCRnA); + + // output mode = 00 (disconnected) + SET_COMA(1, NORMAL); + + // Set the timer pre-scaler + // Generally we use a divider of 8, resulting in a 2MHz timer + // frequency on a 16MHz MCU. If you are going to change this, be + // sure to regenerate speed_lookuptable.h with + // create_speed_lookuptable.py + SET_CS(1, PRESCALER_8); // CS 2 = 1/8 prescaler + + // Init Stepper ISR to 122 Hz for quick starting + // (F_CPU) / (STEPPER_TIMER_PRESCALE) / frequency + OCR1A = 0x4000; + TCNT1 = 0; + break; + + case TEMP_TIMER_NUM: + // Use timer0 for temperature measurement + // Interleave temperature interrupt with millies interrupt + OCR0B = 128; + break; + } +} + +#define TIMER_OCR_1 OCR1A +#define TIMER_COUNTER_1 TCNT1 + +#define TIMER_OCR_0 OCR0A +#define TIMER_COUNTER_0 TCNT0 #define _CAT(a, ...) a ## __VA_ARGS__ #define HAL_timer_set_compare(timer, compare) (_CAT(TIMER_OCR_, timer) = compare) @@ -148,7 +182,6 @@ void TIMER1_COMPA_vect (void) { \ A("lds r16, %[timsk1]") /* 2 Load into R0 the stepper timer Interrupt mask register [TIMSK1] */ \ A("andi r16,~%[msk1]") /* 1 Disable the stepper ISR */ \ A("sts %[timsk1], r16") /* 2 And set the new value */ \ - A("sei") /* 1 Enable global interrupts - stepper and temperature ISRs are disabled, so no risk of reentry or being preempted by the temperature ISR */ \ A("push r16") /* 2 Save TIMSK1 into stack */ \ A("in r16, 0x3B") /* 1 Get RAMPZ register */ \ A("push r16") /* 2 Save RAMPZ into stack */ \ @@ -258,7 +291,7 @@ void TIMER0_COMPB_vect (void) { \ A("out 0x3B, r16") /* 1 Restore RAMPZ register to its original value */ \ A("pop r16") /* 2 Get the original TIMSK0 value but with temperature ISR disabled */ \ A("ori r16,%[msk0]") /* 1 Enable temperature ISR */ \ - A("cli") /* 1 Disable global interrupts - We must do this, as we will reenable the temperature ISR, and we don´t want to reenter this handler until the current one is done */ \ + A("cli") /* 1 Disable global interrupts - We must do this, as we will reenable the temperature ISR, and we don't want to reenter this handler until the current one is done */ \ A("sts %[timsk0], r16") /* 2 And restore the old value */ \ A("pop r16") /* 2 Get the old SREG */ \ A("out __SREG__, r16") /* 1 And restore the SREG value */ \ diff --git a/Marlin/I2CPositionEncoder.cpp b/Marlin/I2CPositionEncoder.cpp index ce7029237b..285ba0d66f 100644 --- a/Marlin/I2CPositionEncoder.cpp +++ b/Marlin/I2CPositionEncoder.cpp @@ -181,7 +181,7 @@ if (errPrstIdx >= I2CPE_ERR_PRST_ARRAY_SIZE) { float sumP = 0; LOOP_L_N(i, I2CPE_ERR_PRST_ARRAY_SIZE) sumP += errPrst[i]; - const int32_t errorP = int32_t(sumP * (1.0 / (I2CPE_ERR_PRST_ARRAY_SIZE))); + const int32_t errorP = int32_t(sumP * (1.0f / (I2CPE_ERR_PRST_ARRAY_SIZE))); SERIAL_ECHO(axis_codes[encoderAxis]); SERIAL_ECHOPAIR(" - err detected: ", errorP * planner.steps_to_mm[encoderAxis]); SERIAL_ECHOLNPGM("mm; correcting!"); diff --git a/Marlin/I2CPositionEncoder.h b/Marlin/I2CPositionEncoder.h index 7e5513423f..a0e8a6199a 100644 --- a/Marlin/I2CPositionEncoder.h +++ b/Marlin/I2CPositionEncoder.h @@ -134,16 +134,12 @@ nextErrorCountTime = 0, lastErrorTime; - //double positionMm; //calculate - #if ENABLED(I2CPE_ERR_ROLLING_AVERAGE) uint8_t errIdx = 0, errPrstIdx = 0; int err[I2CPE_ERR_ARRAY_SIZE] = { 0 }, errPrst[I2CPE_ERR_PRST_ARRAY_SIZE] = { 0 }; #endif - //float positionMm; //calculate - public: void init(const uint8_t address, const AxisEnum axis); void reset(); diff --git a/Marlin/Marlin.h b/Marlin/Marlin.h index efb73d172a..ef4c0f2b06 100644 --- a/Marlin/Marlin.h +++ b/Marlin/Marlin.h @@ -60,10 +60,10 @@ extern const char axis_codes[XYZE]; #if HAS_X2_ENABLE #define enable_X() do{ X_ENABLE_WRITE( X_ENABLE_ON); X2_ENABLE_WRITE( X_ENABLE_ON); }while(0) - #define disable_X() do{ X_ENABLE_WRITE(!X_ENABLE_ON); X2_ENABLE_WRITE(!X_ENABLE_ON); axis_known_position[X_AXIS] = false; }while(0) + #define disable_X() do{ X_ENABLE_WRITE(!X_ENABLE_ON); X2_ENABLE_WRITE(!X_ENABLE_ON); CBI(axis_known_position, X_AXIS); }while(0) #elif HAS_X_ENABLE #define enable_X() X_ENABLE_WRITE( X_ENABLE_ON) - #define disable_X() do{ X_ENABLE_WRITE(!X_ENABLE_ON); axis_known_position[X_AXIS] = false; }while(0) + #define disable_X() do{ X_ENABLE_WRITE(!X_ENABLE_ON); CBI(axis_known_position, X_AXIS); }while(0) #else #define enable_X() NOOP #define disable_X() NOOP @@ -71,10 +71,10 @@ extern const char axis_codes[XYZE]; #if HAS_Y2_ENABLE #define enable_Y() do{ Y_ENABLE_WRITE( Y_ENABLE_ON); Y2_ENABLE_WRITE(Y_ENABLE_ON); }while(0) - #define disable_Y() do{ Y_ENABLE_WRITE(!Y_ENABLE_ON); Y2_ENABLE_WRITE(!Y_ENABLE_ON); axis_known_position[Y_AXIS] = false; }while(0) + #define disable_Y() do{ Y_ENABLE_WRITE(!Y_ENABLE_ON); Y2_ENABLE_WRITE(!Y_ENABLE_ON); CBI(axis_known_position, Y_AXIS); }while(0) #elif HAS_Y_ENABLE #define enable_Y() Y_ENABLE_WRITE( Y_ENABLE_ON) - #define disable_Y() do{ Y_ENABLE_WRITE(!Y_ENABLE_ON); axis_known_position[Y_AXIS] = false; }while(0) + #define disable_Y() do{ Y_ENABLE_WRITE(!Y_ENABLE_ON); CBI(axis_known_position, Y_AXIS); }while(0) #else #define enable_Y() NOOP #define disable_Y() NOOP @@ -82,10 +82,10 @@ extern const char axis_codes[XYZE]; #if HAS_Z2_ENABLE #define enable_Z() do{ Z_ENABLE_WRITE( Z_ENABLE_ON); Z2_ENABLE_WRITE(Z_ENABLE_ON); }while(0) - #define disable_Z() do{ Z_ENABLE_WRITE(!Z_ENABLE_ON); Z2_ENABLE_WRITE(!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }while(0) + #define disable_Z() do{ Z_ENABLE_WRITE(!Z_ENABLE_ON); Z2_ENABLE_WRITE(!Z_ENABLE_ON); CBI(axis_known_position, Z_AXIS); }while(0) #elif HAS_Z_ENABLE #define enable_Z() Z_ENABLE_WRITE( Z_ENABLE_ON) - #define disable_Z() do{ Z_ENABLE_WRITE(!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }while(0) + #define disable_Z() do{ Z_ENABLE_WRITE(!Z_ENABLE_ON); CBI(axis_known_position, Z_AXIS); }while(0) #else #define enable_Z() NOOP #define disable_Z() NOOP @@ -220,11 +220,16 @@ inline void reset_stepper_timeout() { previous_move_ms = millis(); } extern float feedrate_mm_s; extern int16_t feedrate_percentage; -#define MMS_SCALED(MM_S) ((MM_S)*feedrate_percentage*0.01) +#define MMS_SCALED(MM_S) ((MM_S)*feedrate_percentage*0.01f) + +extern bool axis_relative_modes[XYZE]; + +extern uint8_t axis_homed, axis_known_position; + +constexpr uint8_t xyz_bits = _BV(X_AXIS) | _BV(Y_AXIS) | _BV(Z_AXIS); +FORCE_INLINE bool all_axes_homed() { return (axis_homed & xyz_bits) == xyz_bits; } +FORCE_INLINE bool all_axes_known() { return (axis_known_position & xyz_bits) == xyz_bits; } -extern bool axis_relative_modes[]; -extern bool axis_known_position[XYZ]; -extern bool axis_homed[XYZ]; extern volatile bool wait_for_heatup; #if HAS_RESUME_CONTINUE @@ -316,22 +321,15 @@ void report_current_position(); void recalc_delta_settings(); float delta_safe_distance_from_top(); - #if ENABLED(DELTA_FAST_SQRT) - float Q_rsqrt(const float number); - #define _SQRT(n) (1.0f / Q_rsqrt(n)) - #else - #define _SQRT(n) SQRT(n) - #endif - // Macro to obtain the Z position of an individual tower - #define DELTA_Z(V,T) V[Z_AXIS] + _SQRT( \ + #define DELTA_Z(V,T) V[Z_AXIS] + SQRT( \ delta_diagonal_rod_2_tower[T] - HYPOT2( \ delta_tower[T][X_AXIS] - V[X_AXIS], \ delta_tower[T][Y_AXIS] - V[Y_AXIS] \ ) \ ) - #define DELTA_IK(V) do { \ + #define DELTA_IK(V) do { \ delta[A_AXIS] = DELTA_Z(V, A_AXIS); \ delta[B_AXIS] = DELTA_Z(V, B_AXIS); \ delta[C_AXIS] = DELTA_Z(V, C_AXIS); \ @@ -370,11 +368,6 @@ void report_current_position(); void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, const element_2d_fn fn); #endif -#if ENABLED(AUTO_BED_LEVELING_UBL) - typedef struct { double A, B, D; } linear_fit; - linear_fit* lsf_linear_fit(double x[], double y[], double z[], const int); -#endif - #if HAS_LEVELING bool leveling_is_valid(); void set_bed_leveling_enabled(const bool enable=true); @@ -468,10 +461,10 @@ void prepare_move_to_destination(); /** * Blocking movement and shorthand functions */ -void do_blocking_move_to(const float rx, const float ry, const float rz, const float &fr_mm_s=0.0); -void do_blocking_move_to_x(const float &rx, const float &fr_mm_s=0.0); -void do_blocking_move_to_z(const float &rz, const float &fr_mm_s=0.0); -void do_blocking_move_to_xy(const float &rx, const float &ry, const float &fr_mm_s=0.0); +void do_blocking_move_to(const float rx, const float ry, const float rz, const float &fr_mm_s=0); +void do_blocking_move_to_x(const float &rx, const float &fr_mm_s=0); +void do_blocking_move_to_z(const float &rz, const float &fr_mm_s=0); +void do_blocking_move_to_xy(const float &rx, const float &ry, const float &fr_mm_s=0); #if ENABLED(ARC_SUPPORT) void plan_arc(const float(&cart)[XYZE], const float(&offset)[2], const bool clockwise); @@ -531,8 +524,8 @@ void do_blocking_move_to_xy(const float &rx, const float &ry, const float &fr_mm // Return true if the given position is within the machine bounds. inline bool position_is_reachable(const float &rx, const float &ry) { // Add 0.001 margin to deal with float imprecision - return WITHIN(rx, X_MIN_POS - 0.001, X_MAX_POS + 0.001) - && WITHIN(ry, Y_MIN_POS - 0.001, Y_MAX_POS + 0.001); + return WITHIN(rx, X_MIN_POS - 0.001f, X_MAX_POS + 0.001f) + && WITHIN(ry, Y_MIN_POS - 0.001f, Y_MAX_POS + 0.001f); } #if HAS_BED_PROBE @@ -545,8 +538,8 @@ void do_blocking_move_to_xy(const float &rx, const float &ry, const float &fr_mm */ inline bool position_is_reachable_by_probe(const float &rx, const float &ry) { return position_is_reachable(rx - (X_PROBE_OFFSET_FROM_EXTRUDER), ry - (Y_PROBE_OFFSET_FROM_EXTRUDER)) - && WITHIN(rx, MIN_PROBE_X - 0.001, MAX_PROBE_X + 0.001) - && WITHIN(ry, MIN_PROBE_Y - 0.001, MAX_PROBE_Y + 0.001); + && WITHIN(rx, MIN_PROBE_X - 0.001f, MAX_PROBE_X + 0.001f) + && WITHIN(ry, MIN_PROBE_Y - 0.001f, MAX_PROBE_Y + 0.001f); } #endif diff --git a/Marlin/MarlinSerial.cpp b/Marlin/MarlinSerial.cpp index 83bc49edc4..86d4c440ef 100644 --- a/Marlin/MarlinSerial.cpp +++ b/Marlin/MarlinSerial.cpp @@ -28,6 +28,7 @@ * Modified 28 September 2010 by Mark Sproul * Modified 14 February 2016 by Andreas Hardtung (added tx buffer) * Modified 01 October 2017 by Eduardo José Tagle (added XON/XOFF) + * Modified 10 June 2018 by Eduardo José Tagle (See #10991) */ // Disable HardwareSerial.cpp to support chips without a UART (Attiny, etc.) @@ -55,140 +56,312 @@ ring_buffer_r rx_buffer = { { 0 }, 0, 0 }; #if TX_BUFFER_SIZE > 0 ring_buffer_t tx_buffer = { { 0 }, 0, 0 }; - static bool _written; #endif + static bool _written; #endif #if ENABLED(SERIAL_XON_XOFF) - constexpr uint8_t XON_XOFF_CHAR_SENT = 0x80; // XON / XOFF Character was sent - constexpr uint8_t XON_XOFF_CHAR_MASK = 0x1F; // XON / XOFF character to send + constexpr uint8_t XON_XOFF_CHAR_SENT = 0x80, // XON / XOFF Character was sent + XON_XOFF_CHAR_MASK = 0x1F; // XON / XOFF character to send // XON / XOFF character definitions - constexpr uint8_t XON_CHAR = 17; - constexpr uint8_t XOFF_CHAR = 19; + constexpr uint8_t XON_CHAR = 17, XOFF_CHAR = 19; uint8_t xon_xoff_state = XON_XOFF_CHAR_SENT | XON_CHAR; #endif - void clear_command_queue(); - #if ENABLED(SERIAL_STATS_DROPPED_RX) uint8_t rx_dropped_bytes = 0; #endif + #if ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS) + uint8_t rx_buffer_overruns = 0; + #endif + + #if ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS) + uint8_t rx_framing_errors = 0; + #endif + #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) ring_buffer_pos_t rx_max_enqueued = 0; #endif + // A SW memory barrier, to ensure GCC does not overoptimize loops + #define sw_barrier() asm volatile("": : :"memory"); + #if ENABLED(EMERGENCY_PARSER) #include "emergency_parser.h" #endif + // "Atomically" read the RX head index value without disabling interrupts: + // This MUST be called with RX interrupts enabled, and CAN'T be called + // from the RX ISR itself! + FORCE_INLINE ring_buffer_pos_t atomic_read_rx_head() { + #if RX_BUFFER_SIZE > 256 + // Keep reading until 2 consecutive reads return the same value, + // meaning there was no update in-between caused by an interrupt. + // This works because serial RX interrupts happen at a slower rate + // than successive reads of a variable, so 2 consecutive reads with + // the same value means no interrupt updated it. + ring_buffer_pos_t vold, vnew = rx_buffer.head; + sw_barrier(); + do { + vold = vnew; + vnew = rx_buffer.head; + sw_barrier(); + } while (vold != vnew); + return vnew; + #else + // With an 8bit index, reads are always atomic. No need for special handling + return rx_buffer.head; + #endif + } + + #if RX_BUFFER_SIZE > 256 + static volatile bool rx_tail_value_not_stable = false; + static volatile uint16_t rx_tail_value_backup = 0; + #endif + + // Set RX tail index, taking into account the RX ISR could interrupt + // the write to this variable in the middle - So a backup strategy + // is used to ensure reads of the correct values. + // -Must NOT be called from the RX ISR - + FORCE_INLINE void atomic_set_rx_tail(ring_buffer_pos_t value) { + #if RX_BUFFER_SIZE > 256 + // Store the new value in the backup + rx_tail_value_backup = value; + sw_barrier(); + // Flag we are about to change the true value + rx_tail_value_not_stable = true; + sw_barrier(); + // Store the new value + rx_buffer.tail = value; + sw_barrier(); + // Signal the new value is completely stored into the value + rx_tail_value_not_stable = false; + sw_barrier(); + #else + rx_buffer.tail = value; + #endif + } + + // Get the RX tail index, taking into account the read could be + // interrupting in the middle of the update of that index value + // -Called from the RX ISR - + FORCE_INLINE ring_buffer_pos_t atomic_read_rx_tail() { + #if RX_BUFFER_SIZE > 256 + // If the true index is being modified, return the backup value + if (rx_tail_value_not_stable) return rx_tail_value_backup; + #endif + // The true index is stable, return it + return rx_buffer.tail; + } + + // (called with RX interrupts disabled) FORCE_INLINE void store_rxd_char() { - const ring_buffer_pos_t h = rx_buffer.head, - i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); + // Get the tail - Nothing can alter its value while this ISR is executing, but there's + // a chance that this ISR interrupted the main process while it was updating the index. + // The backup mechanism ensures the correct value is always returned. + const ring_buffer_pos_t t = atomic_read_rx_tail(); + + // Get the head pointer - This ISR is the only one that modifies its value, so it's safe to read here + ring_buffer_pos_t h = rx_buffer.head; + + // Get the next element + ring_buffer_pos_t i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); + + // This must read the M_UCSRxA register before reading the received byte to detect error causes + #if ENABLED(SERIAL_STATS_DROPPED_RX) + if (TEST(M_UCSRxA, M_DORx) && !++rx_dropped_bytes) --rx_dropped_bytes; + #endif + + #if ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS) + if (TEST(M_UCSRxA, M_DORx) && !++rx_buffer_overruns) --rx_buffer_overruns; + #endif + + #if ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS) + if (TEST(M_UCSRxA, M_FEx) && !++rx_framing_errors) --rx_framing_errors; + #endif + + // Read the character from the USART + uint8_t c = M_UDRx; + + #if ENABLED(EMERGENCY_PARSER) + emergency_parser.update(c); + #endif // If the character is to be stored at the index just before the tail - // (such that the head would advance to the current tail), the buffer is - // critical, so don't write the character or advance the head. - const char c = M_UDRx; - if (i != rx_buffer.tail) { + // (such that the head would advance to the current tail), the RX FIFO is + // full, so don't write the character or advance the head. + if (i != t) { rx_buffer.buffer[h] = c; - rx_buffer.head = i; - } - else { - #if ENABLED(SERIAL_STATS_DROPPED_RX) - if (!++rx_dropped_bytes) ++rx_dropped_bytes; - #endif + h = i; } + #if ENABLED(SERIAL_STATS_DROPPED_RX) + else if (!++rx_dropped_bytes) --rx_dropped_bytes; + #endif #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) - // calculate count of bytes stored into the RX buffer - ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(rx_buffer.head - rx_buffer.tail) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); + // Calculate count of bytes stored into the RX buffer + const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); + // Keep track of the maximum count of enqueued bytes NOLESS(rx_max_enqueued, rx_count); #endif #if ENABLED(SERIAL_XON_XOFF) - - // for high speed transfers, we can use XON/XOFF protocol to do - // software handshake and avoid overruns. + // If the last char that was sent was an XON if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XON_CHAR) { - // calculate count of bytes stored into the RX buffer - ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(rx_buffer.head - rx_buffer.tail) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); + // Bytes stored into the RX buffer + const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); - // if we are above 12.5% of RX buffer capacity, send XOFF before - // we run out of RX buffer space .. We need 325 bytes @ 250kbits/s to - // let the host react and stop sending bytes. This translates to 13mS - // propagation time. + // If over 12.5% of RX buffer capacity, send XOFF before running out of + // RX buffer space .. 325 bytes @ 250kbits/s needed to let the host react + // and stop sending bytes. This translates to 13mS propagation time. if (rx_count >= (RX_BUFFER_SIZE) / 8) { - // If TX interrupts are disabled and data register is empty, - // just write the byte to the data register and be done. This - // shortcut helps significantly improve the effective datarate - // at high (>500kbit/s) bitrates, where interrupt overhead - // becomes a slowdown. - if (!TEST(M_UCSRxB, M_UDRIEx) && TEST(M_UCSRxA, M_UDREx)) { - // Send an XOFF character - M_UDRx = XOFF_CHAR; - // clear the TXC bit -- "can be cleared by writing a one to its bit - // location". This makes sure flush() won't return until the bytes - // actually got written - SBI(M_UCSRxA, M_TXCx); - // And remember it was sent - xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT; + + // At this point, definitely no TX interrupt was executing, since the TX ISR can't be preempted. + // Don't enable the TX interrupt here as a means to trigger the XOFF char, because if it happens + // to be in the middle of trying to disable the RX interrupt in the main program, eventually the + // enabling of the TX interrupt could be undone. The ONLY reliable thing this can do to ensure + // the sending of the XOFF char is to send it HERE AND NOW. + + // About to send the XOFF char + xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT; + + // Wait until the TX register becomes empty and send it - Here there could be a problem + // - While waiting for the TX register to empty, the RX register could receive a new + // character. This must also handle that situation! + while (!TEST(M_UCSRxA, M_UDREx)) { + + if (TEST(M_UCSRxA,M_RXCx)) { + // A char arrived while waiting for the TX buffer to be empty - Receive and process it! + + i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); + + // Read the character from the USART + c = M_UDRx; + + #if ENABLED(EMERGENCY_PARSER) + emergency_parser.update(c); + #endif + + // If the character is to be stored at the index just before the tail + // (such that the head would advance to the current tail), the FIFO is + // full, so don't write the character or advance the head. + if (i != t) { + rx_buffer.buffer[h] = c; + h = i; + } + #if ENABLED(SERIAL_STATS_DROPPED_RX) + else if (!++rx_dropped_bytes) --rx_dropped_bytes; + #endif + } + sw_barrier(); } - else { - // TX interrupts disabled, but buffer still not empty ... or - // TX interrupts enabled. Reenable TX ints and schedule XOFF - // character to be sent - #if TX_BUFFER_SIZE > 0 - SBI(M_UCSRxB, M_UDRIEx); - xon_xoff_state = XOFF_CHAR; - #else - // We are not using TX interrupts, we will have to send this manually - while (!TEST(M_UCSRxA, M_UDREx)) {/* nada */} - M_UDRx = XOFF_CHAR; - // And remember we already sent it - xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT; - #endif + + M_UDRx = XOFF_CHAR; + + // Clear the TXC bit -- "can be cleared by writing a one to its bit + // location". This makes sure flush() won't return until the bytes + // actually got written + SBI(M_UCSRxA, M_TXCx); + + // At this point there could be a race condition between the write() function + // and this sending of the XOFF char. This interrupt could happen between the + // wait to be empty TX buffer loop and the actual write of the character. Since + // the TX buffer is full because it's sending the XOFF char, the only way to be + // sure the write() function will succeed is to wait for the XOFF char to be + // completely sent. Since an extra character could be received during the wait + // it must also be handled! + while (!TEST(M_UCSRxA, M_UDREx)) { + + if (TEST(M_UCSRxA,M_RXCx)) { + // A char arrived while waiting for the TX buffer to be empty - Receive and process it! + + i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); + + // Read the character from the USART + c = M_UDRx; + + #if ENABLED(EMERGENCY_PARSER) + emergency_parser.update(c); + #endif + + // If the character is to be stored at the index just before the tail + // (such that the head would advance to the current tail), the FIFO is + // full, so don't write the character or advance the head. + if (i != t) { + rx_buffer.buffer[h] = c; + h = i; + } + #if ENABLED(SERIAL_STATS_DROPPED_RX) + else if (!++rx_dropped_bytes) --rx_dropped_bytes; + #endif + } + sw_barrier(); } + + // At this point everything is ready. The write() function won't + // have any issues writing to the UART TX register if it needs to! } } #endif // SERIAL_XON_XOFF - #if ENABLED(EMERGENCY_PARSER) - emergency_parser.update(c); - #endif + // Store the new head value - The main loop will retry until the value is stable + rx_buffer.head = h; } #if TX_BUFFER_SIZE > 0 + // (called with TX irqs disabled) FORCE_INLINE void _tx_udr_empty_irq(void) { - // If interrupts are enabled, there must be more data in the output - // buffer. + + // Read positions + uint8_t t = tx_buffer.tail; + const uint8_t h = tx_buffer.head; #if ENABLED(SERIAL_XON_XOFF) - // Do a priority insertion of an XON/XOFF char, if needed. - const uint8_t state = xon_xoff_state; - if (!(state & XON_XOFF_CHAR_SENT)) { - M_UDRx = state & XON_XOFF_CHAR_MASK; - xon_xoff_state = state | XON_XOFF_CHAR_SENT; + // If an XON char is pending to be sent, do it now + if (xon_xoff_state == XON_CHAR) { + + // Send the character + M_UDRx = XON_CHAR; + + // clear the TXC bit -- "can be cleared by writing a one to its bit + // location". This makes sure flush() won't return until the bytes + // actually got written + SBI(M_UCSRxA, M_TXCx); + + // Remember we sent it. + xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT; + + // If nothing else to transmit, just disable TX interrupts. + if (h == t) CBI(M_UCSRxB, M_UDRIEx); // (Non-atomic, could be reenabled by the main program, but eventually this will succeed) + + return; } - else #endif - { // Send the next byte - const uint8_t t = tx_buffer.tail, c = tx_buffer.buffer[t]; - tx_buffer.tail = (t + 1) & (TX_BUFFER_SIZE - 1); - M_UDRx = c; + + // If nothing to transmit, just disable TX interrupts. This could + // happen as the result of the non atomicity of the disabling of RX + // interrupts that could end reenabling TX interrupts as a side effect. + if (h == t) { + CBI(M_UCSRxB, M_UDRIEx); // (Non-atomic, could be reenabled by the main program, but eventually this will succeed) + return; } - // clear the TXC bit -- "can be cleared by writing a one to its bit - // location". This makes sure flush() won't return until the bytes - // actually got written + // There is something to TX, Send the next byte + const uint8_t c = tx_buffer.buffer[t]; + t = (t + 1) & (TX_BUFFER_SIZE - 1); + M_UDRx = c; + tx_buffer.tail = t; + + // Clear the TXC bit (by writing a one to its bit location). + // Ensures flush() won't return until the bytes are actually written/ SBI(M_UCSRxA, M_TXCx); - // Disable interrupts if the buffer is empty - if (tx_buffer.head == tx_buffer.tail) - CBI(M_UCSRxB, M_UDRIEx); + // Disable interrupts if there is nothing to transmit following this byte + if (h == t) CBI(M_UCSRxB, M_UDRIEx); // (Non-atomic, could be reenabled by the main program, but eventually this will succeed) } #ifdef M_USARTx_UDRE_vect @@ -232,8 +405,8 @@ SBI(M_UCSRxB, M_RXCIEx); #if TX_BUFFER_SIZE > 0 CBI(M_UCSRxB, M_UDRIEx); - _written = false; #endif + _written = false; } void MarlinSerial::end() { @@ -243,177 +416,179 @@ CBI(M_UCSRxB, M_UDRIEx); } - void MarlinSerial::checkRx(void) { - if (TEST(M_UCSRxA, M_RXCx)) { - CRITICAL_SECTION_START; - store_rxd_char(); - CRITICAL_SECTION_END; - } - } - int MarlinSerial::peek(void) { - CRITICAL_SECTION_START; - const int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail]; - CRITICAL_SECTION_END; - return v; + const ring_buffer_pos_t h = atomic_read_rx_head(), t = rx_buffer.tail; + return h == t ? -1 : rx_buffer.buffer[t]; } int MarlinSerial::read(void) { - int v; - CRITICAL_SECTION_START; - const ring_buffer_pos_t t = rx_buffer.tail; - if (rx_buffer.head == t) - v = -1; - else { - v = rx_buffer.buffer[t]; - rx_buffer.tail = (ring_buffer_pos_t)(t + 1) & (RX_BUFFER_SIZE - 1); + const ring_buffer_pos_t h = atomic_read_rx_head(); - #if ENABLED(SERIAL_XON_XOFF) - if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) { - // Get count of bytes in the RX buffer - ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(rx_buffer.head - rx_buffer.tail) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); - // When below 10% of RX buffer capacity, send XON before - // running out of RX buffer bytes - if (rx_count < (RX_BUFFER_SIZE) / 10) { - xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT; - CRITICAL_SECTION_END; // End critical section before returning! - writeNoHandshake(XON_CHAR); - return v; - } - } - #endif + // Read the tail. Main thread owns it, so it is safe to directly read it + ring_buffer_pos_t t = rx_buffer.tail; + + // If nothing to read, return now + if (h == t) return -1; + + // Get the next char + const int v = rx_buffer.buffer[t]; + t = (ring_buffer_pos_t)(t + 1) & (RX_BUFFER_SIZE - 1); + + // Advance tail - Making sure the RX ISR will always get an stable value, even + // if it interrupts the writing of the value of that variable in the middle. + atomic_set_rx_tail(t); + + #if ENABLED(SERIAL_XON_XOFF) + // If the XOFF char was sent, or about to be sent... + if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) { + // Get count of bytes in the RX buffer + const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); + if (rx_count < (RX_BUFFER_SIZE) / 10) { + #if TX_BUFFER_SIZE > 0 + // Signal we want an XON character to be sent. + xon_xoff_state = XON_CHAR; + // Enable TX ISR. Non atomic, but it will eventually enable them + SBI(M_UCSRxB, M_UDRIEx); + #else + // If not using TX interrupts, we must send the XON char now + xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT; + while (!TEST(M_UCSRxA, M_UDREx)) sw_barrier(); + M_UDRx = XON_CHAR; + #endif + } } - CRITICAL_SECTION_END; + #endif + return v; } ring_buffer_pos_t MarlinSerial::available(void) { - CRITICAL_SECTION_START; - const ring_buffer_pos_t h = rx_buffer.head, t = rx_buffer.tail; - CRITICAL_SECTION_END; + const ring_buffer_pos_t h = atomic_read_rx_head(), t = rx_buffer.tail; return (ring_buffer_pos_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1); } void MarlinSerial::flush(void) { - // Don't change this order of operations. If the RX interrupt occurs between - // reading rx_buffer_head and updating rx_buffer_tail, the previous rx_buffer_head - // may be written to rx_buffer_tail, making the buffer appear full rather than empty. - CRITICAL_SECTION_START; - rx_buffer.head = rx_buffer.tail = 0; - clear_command_queue(); - CRITICAL_SECTION_END; + + // Set the tail to the head: + // - Read the RX head index in a safe way. (See atomic_read_rx_head.) + // - Set the tail, making sure the RX ISR will always get a stable value, even + // if it interrupts the writing of the value of that variable in the middle. + atomic_set_rx_tail(atomic_read_rx_head()); #if ENABLED(SERIAL_XON_XOFF) + // If the XOFF char was sent, or about to be sent... if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) { - xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT; - writeNoHandshake(XON_CHAR); + #if TX_BUFFER_SIZE > 0 + // Signal we want an XON character to be sent. + xon_xoff_state = XON_CHAR; + // Enable TX ISR. Non atomic, but it will eventually enable it. + SBI(M_UCSRxB, M_UDRIEx); + #else + // If not using TX interrupts, we must send the XON char now + xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT; + while (!TEST(M_UCSRxA, M_UDREx)) sw_barrier(); + M_UDRx = XON_CHAR; + #endif } #endif } #if TX_BUFFER_SIZE > 0 - uint8_t MarlinSerial::availableForWrite(void) { - CRITICAL_SECTION_START; - const uint8_t h = tx_buffer.head, t = tx_buffer.tail; - CRITICAL_SECTION_END; - return (uint8_t)(TX_BUFFER_SIZE + h - t) & (TX_BUFFER_SIZE - 1); - } - void MarlinSerial::write(const uint8_t c) { - #if ENABLED(SERIAL_XON_XOFF) - const uint8_t state = xon_xoff_state; - if (!(state & XON_XOFF_CHAR_SENT)) { - // Send 2 chars: XON/XOFF, then a user-specified char - writeNoHandshake(state & XON_XOFF_CHAR_MASK); - xon_xoff_state = state | XON_XOFF_CHAR_SENT; - } - #endif - writeNoHandshake(c); - } - - void MarlinSerial::writeNoHandshake(const uint8_t c) { _written = true; - CRITICAL_SECTION_START; - bool emty = (tx_buffer.head == tx_buffer.tail); - CRITICAL_SECTION_END; - // If the buffer and the data register is empty, just write the byte - // to the data register and be done. This shortcut helps - // significantly improve the effective datarate at high (> - // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. - if (emty && TEST(M_UCSRxA, M_UDREx)) { - CRITICAL_SECTION_START; - M_UDRx = c; - SBI(M_UCSRxA, M_TXCx); - CRITICAL_SECTION_END; + // If the TX interrupts are disabled and the data register + // is empty, just write the byte to the data register and + // be done. This shortcut helps significantly improve the + // effective datarate at high (>500kbit/s) bitrates, where + // interrupt overhead becomes a slowdown. + // Yes, there is a race condition between the sending of the + // XOFF char at the RX ISR, but it is properly handled there + if (!TEST(M_UCSRxB, M_UDRIEx) && TEST(M_UCSRxA, M_UDREx)) { + M_UDRx = c; + + // clear the TXC bit -- "can be cleared by writing a one to its bit + // location". This makes sure flush() won't return until the bytes + // actually got written + SBI(M_UCSRxA, M_TXCx); return; } + const uint8_t i = (tx_buffer.head + 1) & (TX_BUFFER_SIZE - 1); - // If the output buffer is full, there's nothing for it other than to - // wait for the interrupt handler to empty it a bit - while (i == tx_buffer.tail) { - if (!TEST(SREG, SREG_I)) { - // Interrupts are disabled, so we'll have to poll the data - // register empty flag ourselves. If it is set, pretend an - // interrupt has happened and call the handler to free up - // space for us. - if (TEST(M_UCSRxA, M_UDREx)) - _tx_udr_empty_irq(); - } - else { - // nop, the interrupt handler will free up space for us + // If global interrupts are disabled (as the result of being called from an ISR)... + if (!ISRS_ENABLED()) { + + // Make room by polling if it is possible to transmit, and do so! + while (i == tx_buffer.tail) { + + // If we can transmit another byte, do it. + if (TEST(M_UCSRxA, M_UDREx)) _tx_udr_empty_irq(); + + // Make sure compiler rereads tx_buffer.tail + sw_barrier(); } } + else { + // Interrupts are enabled, just wait until there is space + while (i == tx_buffer.tail) { sw_barrier(); } + } + // Store new char. head is always safe to move tx_buffer.buffer[tx_buffer.head] = c; - { CRITICAL_SECTION_START; - tx_buffer.head = i; - SBI(M_UCSRxB, M_UDRIEx); - CRITICAL_SECTION_END; - } - return; + tx_buffer.head = i; + + // Enable TX ISR - Non atomic, but it will eventually enable TX ISR + SBI(M_UCSRxB, M_UDRIEx); } void MarlinSerial::flushTX(void) { - // TX - // If we have never written a byte, no need to flush. This special - // case is needed since there is no way to force the TXC (transmit - // complete) bit to 1 during initialization - if (!_written) - return; + // No bytes written, no need to flush. This special case is needed since there's + // no way to force the TXC (transmit complete) bit to 1 during initialization. + if (!_written) return; - while (TEST(M_UCSRxB, M_UDRIEx) || !TEST(M_UCSRxA, M_TXCx)) { - if (!TEST(SREG, SREG_I) && TEST(M_UCSRxB, M_UDRIEx)) - // Interrupts are globally disabled, but the DR empty - // interrupt should be enabled, so poll the DR empty flag to - // prevent deadlock + // If global interrupts are disabled (as the result of being called from an ISR)... + if (!ISRS_ENABLED()) { + + // Wait until everything was transmitted - We must do polling, as interrupts are disabled + while (tx_buffer.head != tx_buffer.tail || !TEST(M_UCSRxA, M_TXCx)) { + + // If there is more space, send an extra character if (TEST(M_UCSRxA, M_UDREx)) _tx_udr_empty_irq(); + + sw_barrier(); + } + } - // If we get here, nothing is queued anymore (DRIE is disabled) and - // the hardware finished tranmission (TXC is set). + else { + // Wait until everything was transmitted + while (tx_buffer.head != tx_buffer.tail || !TEST(M_UCSRxA, M_TXCx)) sw_barrier(); + } + + // At this point nothing is queued anymore (DRIE is disabled) and + // the hardware finished transmission (TXC is set). } #else // TX_BUFFER_SIZE == 0 void MarlinSerial::write(const uint8_t c) { - #if ENABLED(SERIAL_XON_XOFF) - // Do a priority insertion of an XON/XOFF char, if needed. - const uint8_t state = xon_xoff_state; - if (!(state & XON_XOFF_CHAR_SENT)) { - writeNoHandshake(state & XON_XOFF_CHAR_MASK); - xon_xoff_state = state | XON_XOFF_CHAR_SENT; - } - #endif - writeNoHandshake(c); - } - - void MarlinSerial::writeNoHandshake(uint8_t c) { - while (!TEST(M_UCSRxA, M_UDREx)) {/* nada */} + _written = true; + while (!TEST(M_UCSRxA, M_UDREx)) sw_barrier(); M_UDRx = c; } + void MarlinSerial::flushTX(void) { + // No bytes written, no need to flush. This special case is needed since there's + // no way to force the TXC (transmit complete) bit to 1 during initialization. + if (!_written) return; + + // Wait until everything was transmitted + while (!TEST(M_UCSRxA, M_TXCx)) sw_barrier(); + + // At this point nothing is queued anymore (DRIE is disabled) and + // the hardware finished transmission (TXC is set). + } #endif // TX_BUFFER_SIZE == 0 /** @@ -437,13 +612,9 @@ } void MarlinSerial::print(long n, int base) { - if (base == 0) - write(n); + if (base == 0) write(n); else if (base == 10) { - if (n < 0) { - print('-'); - n = -n; - } + if (n < 0) { print('-'); n = -n; } printNumber(n, 10); } else diff --git a/Marlin/MarlinSerial.h b/Marlin/MarlinSerial.h index 96071f800c..d8bd5346c6 100644 --- a/Marlin/MarlinSerial.h +++ b/Marlin/MarlinSerial.h @@ -26,10 +26,11 @@ * * Modified 28 September 2010 by Mark Sproul * Modified 14 February 2016 by Andreas Hardtung (added tx buffer) + * Modified 01 October 2017 by Eduardo José Tagle (added XON/XOFF) */ -#ifndef MARLINSERIAL_H -#define MARLINSERIAL_H +#ifndef _MARLINSERIAL_H_ +#define _MARLINSERIAL_H_ #include "MarlinConfig.h" @@ -59,6 +60,9 @@ #define M_TXCx SERIAL_REGNAME(TXC,SERIAL_PORT,) #define M_RXCIEx SERIAL_REGNAME(RXCIE,SERIAL_PORT,) #define M_UDREx SERIAL_REGNAME(UDRE,SERIAL_PORT,) +#define M_FEx SERIAL_REGNAME(FE,SERIAL_PORT,) +#define M_DORx SERIAL_REGNAME(DOR,SERIAL_PORT,) +#define M_UPEx SERIAL_REGNAME(UPE,SERIAL_PORT,) #define M_UDRIEx SERIAL_REGNAME(UDRIE,SERIAL_PORT,) #define M_UDRx SERIAL_REGNAME(UDR,SERIAL_PORT,) #define M_UBRRxH SERIAL_REGNAME(UBRR,SERIAL_PORT,H) @@ -97,11 +101,19 @@ extern uint8_t rx_dropped_bytes; #endif + #if ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS) + extern uint8_t rx_buffer_overruns; + #endif + + #if ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS) + extern uint8_t rx_framing_errors; + #endif + #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) extern ring_buffer_pos_t rx_max_enqueued; #endif - class MarlinSerial { //: public Stream + class MarlinSerial { public: MarlinSerial() {}; @@ -111,27 +123,25 @@ static int read(void); static void flush(void); static ring_buffer_pos_t available(void); - static void checkRx(void); static void write(const uint8_t c); - #if TX_BUFFER_SIZE > 0 - static uint8_t availableForWrite(void); - static void flushTX(void); - #endif - static void writeNoHandshake(const uint8_t c); + static void flushTX(void); #if ENABLED(SERIAL_STATS_DROPPED_RX) FORCE_INLINE static uint32_t dropped() { return rx_dropped_bytes; } #endif + #if ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS) + FORCE_INLINE static uint32_t buffer_overruns() { return rx_buffer_overruns; } + #endif + + #if ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS) + FORCE_INLINE static uint32_t framing_errors() { return rx_framing_errors; } + #endif + #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) FORCE_INLINE static ring_buffer_pos_t rxMaxEnqueued() { return rx_max_enqueued; } #endif - private: - static void printNumber(unsigned long, const uint8_t); - static void printFloat(double, uint8_t); - - public: FORCE_INLINE static void write(const char* str) { while (*str) write(*str++); } FORCE_INLINE static void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); } FORCE_INLINE static void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); } @@ -155,6 +165,11 @@ static void println(unsigned long, int = DEC); static void println(double, int = 2); static void println(void); + operator bool() { return true; } + + private: + static void printNumber(unsigned long, const uint8_t); + static void printFloat(double, uint8_t); }; extern MarlinSerial customizedSerial; @@ -166,4 +181,4 @@ extern HardwareSerial bluetoothSerial; #endif -#endif // MARLINSERIAL_H +#endif // _MARLINSERIAL_H_ diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 1ccf7ff5cb..3897f79577 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -378,7 +378,7 @@ uint8_t marlin_debug_flags = DEBUG_NONE; * Used by 'buffer_line_to_current_position' to do a move after changing it. * Used by 'SYNC_PLAN_POSITION_KINEMATIC' to update 'planner.position'. */ -float current_position[XYZE] = { 0.0 }; +float current_position[XYZE] = { 0 }; /** * Cartesian Destination @@ -386,7 +386,7 @@ float current_position[XYZE] = { 0.0 }; * and expected by functions like 'prepare_move_to_destination'. * Set with 'gcode_get_destination' or 'set_destination_from_current'. */ -float destination[XYZE] = { 0.0 }; +float destination[XYZE] = { 0 }; /** * axis_homed @@ -397,7 +397,7 @@ float destination[XYZE] = { 0.0 }; * Flags that the position is known in each linear axis. Set when homed. * Cleared whenever a stepper powers off, potentially losing its position. */ -bool axis_homed[XYZ] = { false }, axis_known_position[XYZ] = { false }; +uint8_t axis_homed, axis_known_position; // = 0 /** * GCode line number handling. Hosts may opt to include line numbers when @@ -446,12 +446,12 @@ static const float homing_feedrate_mm_s[] PROGMEM = { }; FORCE_INLINE float homing_feedrate(const AxisEnum a) { return pgm_read_float(&homing_feedrate_mm_s[a]); } -float feedrate_mm_s = MMM_TO_MMS(1500.0); +float feedrate_mm_s = MMM_TO_MMS(1500.0f); static float saved_feedrate_mm_s; int16_t feedrate_percentage = 100, saved_feedrate_percentage; // Initialized by settings.load() -bool axis_relative_modes[] = AXIS_RELATIVE_MODES; +bool axis_relative_modes[XYZE] = AXIS_RELATIVE_MODES; #if HAS_WORKSPACE_OFFSET #if HAS_POSITION_SHIFT @@ -1220,7 +1220,7 @@ inline void get_serial_commands() { if (job_recovery_commands_count) { if (_enqueuecommand(job_recovery_commands[job_recovery_commands_index])) { ++job_recovery_commands_index; - if (!--job_recovery_commands_count) job_recovery_phase = JOB_RECOVERY_IDLE; + if (!--job_recovery_commands_count) job_recovery_phase = JOB_RECOVERY_DONE; } return true; } @@ -1372,7 +1372,7 @@ bool get_target_extruder_from_command(const uint16_t code) { #endif #if ENABLED(DELTA) - switch(axis) { + switch (axis) { #if HAS_SOFTWARE_ENDSTOPS case X_AXIS: case Y_AXIS: @@ -1428,7 +1428,8 @@ static void set_axis_is_at_home(const AxisEnum axis) { } #endif - axis_known_position[axis] = axis_homed[axis] = true; + SBI(axis_known_position, axis); + SBI(axis_homed, axis); #if HAS_POSITION_SHIFT position_shift[axis] = 0; @@ -1570,7 +1571,7 @@ inline void buffer_line_to_destination(const float &fr_mm_s) { /** * Calculate delta, start a line, and set current_position to destination */ - void prepare_uninterpolated_move_to_destination(const float fr_mm_s=0.0) { + void prepare_uninterpolated_move_to_destination(const float fr_mm_s=0) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS("prepare_uninterpolated_move_to_destination", destination); #endif @@ -1753,13 +1754,13 @@ void clean_up_after_endstop_or_probe_move() { bool axis_unhomed_error(const bool x/*=true*/, const bool y/*=true*/, const bool z/*=true*/) { #if ENABLED(HOME_AFTER_DEACTIVATE) - const bool xx = x && !axis_known_position[X_AXIS], - yy = y && !axis_known_position[Y_AXIS], - zz = z && !axis_known_position[Z_AXIS]; + const bool xx = x && !TEST(axis_known_position, X_AXIS), + yy = y && !TEST(axis_known_position, Y_AXIS), + zz = z && !TEST(axis_known_position, Z_AXIS); #else - const bool xx = x && !axis_homed[X_AXIS], - yy = y && !axis_homed[Y_AXIS], - zz = z && !axis_homed[Z_AXIS]; + const bool xx = x && !TEST(axis_homed, X_AXIS), + yy = y && !TEST(axis_homed, Y_AXIS), + zz = z && !TEST(axis_homed, Z_AXIS); #endif if (xx || yy || zz) { SERIAL_ECHO_START(); @@ -2110,7 +2111,7 @@ void clean_up_after_endstop_or_probe_move() { // For beds that fall when Z is powered off only raise for trusted Z #if ENABLED(UNKNOWN_Z_NO_RAISE) - const bool unknown_condition = axis_known_position[Z_AXIS]; + const bool unknown_condition = TEST(axis_known_position, Z_AXIS); #else constexpr float unknown_condition = true; #endif @@ -2241,7 +2242,6 @@ void clean_up_after_endstop_or_probe_move() { if (probe_triggered && set_bltouch_deployed(false)) return true; #endif - // Clear endstop flags endstops.hit_on_purpose(); // Get Z where the steppers were interrupted @@ -2271,13 +2271,21 @@ void clean_up_after_endstop_or_probe_move() { // Stop the probe before it goes too low to prevent damage. // If Z isn't known then probe to -10mm. - const float z_probe_low_point = axis_known_position[Z_AXIS] ? -zprobe_zoffset + Z_PROBE_LOW_POINT : -10.0; + const float z_probe_low_point = TEST(axis_known_position, Z_AXIS) ? -zprobe_zoffset + Z_PROBE_LOW_POINT : -10.0; // Double-probing does a fast probe followed by a slow probe #if MULTIPLE_PROBING == 2 // Do a first probe at the fast speed - if (do_probe_move(z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_FAST))) return NAN; + if (do_probe_move(z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_FAST))) { + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + SERIAL_ECHOLNPGM("FAST Probe fail!"); + DEBUG_POS("<<< run_z_probe", current_position); + } + #endif + return NAN; + } float first_probe_z = current_position[Z_AXIS]; @@ -2308,7 +2316,15 @@ void clean_up_after_endstop_or_probe_move() { #endif // move down slowly to find bed - if (do_probe_move(z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_SLOW))) return NAN; + if (do_probe_move(z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_SLOW))) { + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + SERIAL_ECHOLNPGM("SLOW Probe fail!"); + DEBUG_POS("<<< run_z_probe", current_position); + } + #endif + return NAN; + } #if MULTIPLE_PROBING > 2 probes_total += current_position[Z_AXIS]; @@ -2319,7 +2335,7 @@ void clean_up_after_endstop_or_probe_move() { #if MULTIPLE_PROBING > 2 // Return the average value of all probes - return probes_total * (1.0 / (MULTIPLE_PROBING)); + const float measured_z = probes_total * (1.0f / (MULTIPLE_PROBING)); #elif MULTIPLE_PROBING == 2 @@ -2333,18 +2349,20 @@ void clean_up_after_endstop_or_probe_move() { #endif // Return a weighted average of the fast and slow probes - return (z2 * 3.0 + first_probe_z * 2.0) * 0.2; + const float measured_z = (z2 * 3.0 + first_probe_z * 2.0) * 0.2; #else // Return the single probe result - return current_position[Z_AXIS]; + const float measured_z = current_position[Z_AXIS]; #endif #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS("<<< run_z_probe", current_position); #endif + + return measured_z; } /** @@ -2414,10 +2432,6 @@ void clean_up_after_endstop_or_probe_move() { SERIAL_EOL(); } - #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< probe_pt"); - #endif - feedrate_mm_s = old_feedrate_mm_s; if (isnan(measured_z)) { @@ -2426,6 +2440,10 @@ void clean_up_after_endstop_or_probe_move() { SERIAL_ERRORLNPGM(MSG_ERR_PROBING_FAILED); } + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< probe_pt"); + #endif + return measured_z; } @@ -2913,15 +2931,20 @@ void clean_up_after_endstop_or_probe_move() { /** * Home an individual linear axis */ -static void do_homing_move(const AxisEnum axis, const float distance, const float fr_mm_s=0.0) { +static void do_homing_move(const AxisEnum axis, const float distance, const float fr_mm_s=0) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) { SERIAL_ECHOPAIR(">>> do_homing_move(", axis_codes[axis]); SERIAL_ECHOPAIR(", ", distance); - SERIAL_ECHOPAIR(", ", fr_mm_s); - SERIAL_ECHOPAIR(" [", fr_mm_s ? fr_mm_s : homing_feedrate(axis)); - SERIAL_ECHOLNPGM("])"); + SERIAL_ECHOPGM(", "); + if (fr_mm_s) + SERIAL_ECHO(fr_mm_s); + else { + SERIAL_ECHOPAIR("[", homing_feedrate(axis)); + SERIAL_CHAR(']'); + } + SERIAL_ECHOLNPGM(")"); } #endif @@ -2965,6 +2988,7 @@ static void do_homing_move(const AxisEnum axis, const float distance, const floa // Tell the planner the axis is at 0 current_position[axis] = 0; + // Do the move, which is required to hit an endstop #if IS_SCARA SYNC_PLAN_POSITION_KINEMATIC(); current_position[axis] = distance; @@ -2991,7 +3015,7 @@ static void do_homing_move(const AxisEnum axis, const float distance, const floa #endif } - endstops.hit_on_purpose(); + endstops.validate_homing_move(); // Re-enable stealthChop if used. Disable diag1 pin on driver. #if ENABLED(SENSORLESS_HOMING) @@ -3019,8 +3043,6 @@ static void do_homing_move(const AxisEnum axis, const float distance, const floa * before updating the current position. */ -#define HOMEAXIS(A) homeaxis(_AXIS(A)) - static void homeaxis(const AxisEnum axis) { #if IS_SCARA @@ -3040,11 +3062,12 @@ static void homeaxis(const AxisEnum axis) { } #endif - const int axis_home_dir = + const int axis_home_dir = ( #if ENABLED(DUAL_X_CARRIAGE) - (axis == X_AXIS) ? x_home_dir(active_extruder) : + axis == X_AXIS ? x_home_dir(active_extruder) : #endif - home_dir(axis); + home_dir(axis) + ); // Homing Z towards the bed? Deploy the Z probe or endstop. #if HOMING_Z_WITH_PROBE @@ -3052,21 +3075,27 @@ static void homeaxis(const AxisEnum axis) { #endif // Set flags for X, Y, Z motor locking - #if ENABLED(X_DUAL_ENDSTOPS) - if (axis == X_AXIS) stepper.set_homing_flag_x(true); - #endif - #if ENABLED(Y_DUAL_ENDSTOPS) - if (axis == Y_AXIS) stepper.set_homing_flag_y(true); - #endif - #if ENABLED(Z_DUAL_ENDSTOPS) - if (axis == Z_AXIS) stepper.set_homing_flag_z(true); + #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS) + switch (axis) { + #if ENABLED(X_DUAL_ENDSTOPS) + case X_AXIS: + #endif + #if ENABLED(Y_DUAL_ENDSTOPS) + case Y_AXIS: + #endif + #if ENABLED(Z_DUAL_ENDSTOPS) + case Z_AXIS: + #endif + stepper.set_homing_dual_axis(true); + default: break; + } #endif // Fast move towards endstop until triggered #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("Home 1 Fast:"); #endif - do_homing_move(axis, 1.5 * max_length(axis) * axis_home_dir); + do_homing_move(axis, 1.5f * max_length(axis) * axis_home_dir); // When homing Z with probe respect probe clearance const float bump = axis_home_dir * ( @@ -3102,34 +3131,32 @@ static void homeaxis(const AxisEnum axis) { const bool pos_dir = axis_home_dir > 0; #if ENABLED(X_DUAL_ENDSTOPS) if (axis == X_AXIS) { - const bool lock_x1 = pos_dir ? (endstops.x_endstop_adj > 0) : (endstops.x_endstop_adj < 0); const float adj = ABS(endstops.x_endstop_adj); - if (lock_x1) stepper.set_x_lock(true); else stepper.set_x2_lock(true); + if (pos_dir ? (endstops.x_endstop_adj > 0) : (endstops.x_endstop_adj < 0)) stepper.set_x_lock(true); else stepper.set_x2_lock(true); do_homing_move(axis, pos_dir ? -adj : adj); - if (lock_x1) stepper.set_x_lock(false); else stepper.set_x2_lock(false); - stepper.set_homing_flag_x(false); + stepper.set_x_lock(false); + stepper.set_x2_lock(false); } #endif #if ENABLED(Y_DUAL_ENDSTOPS) if (axis == Y_AXIS) { - const bool lock_y1 = pos_dir ? (endstops.y_endstop_adj > 0) : (endstops.y_endstop_adj < 0); const float adj = ABS(endstops.y_endstop_adj); - if (lock_y1) stepper.set_y_lock(true); else stepper.set_y2_lock(true); + if (pos_dir ? (endstops.y_endstop_adj > 0) : (endstops.y_endstop_adj < 0)) stepper.set_y_lock(true); else stepper.set_y2_lock(true); do_homing_move(axis, pos_dir ? -adj : adj); - if (lock_y1) stepper.set_y_lock(false); else stepper.set_y2_lock(false); - stepper.set_homing_flag_y(false); + stepper.set_y_lock(false); + stepper.set_y2_lock(false); } #endif #if ENABLED(Z_DUAL_ENDSTOPS) if (axis == Z_AXIS) { - const bool lock_z1 = pos_dir ? (endstops.z_endstop_adj > 0) : (endstops.z_endstop_adj < 0); const float adj = ABS(endstops.z_endstop_adj); - if (lock_z1) stepper.set_z_lock(true); else stepper.set_z2_lock(true); + if (pos_dir ? (endstops.z_endstop_adj > 0) : (endstops.z_endstop_adj < 0)) stepper.set_z_lock(true); else stepper.set_z2_lock(true); do_homing_move(axis, pos_dir ? -adj : adj); - if (lock_z1) stepper.set_z_lock(false); else stepper.set_z2_lock(false); - stepper.set_homing_flag_z(false); + stepper.set_z_lock(false); + stepper.set_z2_lock(false); } #endif + stepper.set_homing_dual_axis(false); #endif #if IS_SCARA @@ -3173,8 +3200,7 @@ static void homeaxis(const AxisEnum axis) { // Clear retracted status if homing the Z axis #if ENABLED(FWRETRACT) - if (axis == Z_AXIS) - fwretract.hop_amount = 0.0; + if (axis == Z_AXIS) fwretract.hop_amount = 0.0; #endif #if ENABLED(DEBUG_LEVELING_FEATURE) @@ -3250,7 +3276,7 @@ void gcode_get_destination() { destination[i] = current_position[i]; } - if (parser.linearval('F') > 0.0) + if (parser.linearval('F') > 0) feedrate_mm_s = MMM_TO_MMS(parser.value_feedrate()); #if ENABLED(PRINTCOUNTER) @@ -3400,19 +3426,19 @@ inline void gcode_G0_G1( relative_mode = relative_mode_backup; #endif - float arc_offset[2] = { 0.0, 0.0 }; + float arc_offset[2] = { 0, 0 }; if (parser.seenval('R')) { const float r = parser.value_linear_units(), p1 = current_position[X_AXIS], q1 = current_position[Y_AXIS], p2 = destination[X_AXIS], q2 = destination[Y_AXIS]; if (r && (p2 != p1 || q2 != q1)) { - const float e = clockwise ^ (r < 0) ? -1 : 1, // clockwise -1/1, counterclockwise 1/-1 - dx = p2 - p1, dy = q2 - q1, // X and Y differences - d = HYPOT(dx, dy), // Linear distance between the points - h = SQRT(sq(r) - sq(d * 0.5)), // Distance to the arc pivot-point - mx = (p1 + p2) * 0.5, my = (q1 + q2) * 0.5, // Point between the two points - sx = -dy / d, sy = dx / d, // Slope of the perpendicular bisector - cx = mx + e * h * sx, cy = my + e * h * sy; // Pivot-point of the arc + const float e = clockwise ^ (r < 0) ? -1 : 1, // clockwise -1/1, counterclockwise 1/-1 + dx = p2 - p1, dy = q2 - q1, // X and Y differences + d = HYPOT(dx, dy), // Linear distance between the points + h = SQRT(sq(r) - sq(d * 0.5f)), // Distance to the arc pivot-point + mx = (p1 + p2) * 0.5f, my = (q1 + q2) * 0.5f, // Point between the two points + sx = -dy / d, sy = dx / d, // Slope of the perpendicular bisector + cx = mx + e * h * sx, cy = my + e * h * sy; // Pivot-point of the arc arc_offset[0] = cx - p1; arc_offset[1] = cy - q1; } @@ -3699,7 +3725,9 @@ inline void gcode_G4() { #endif do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * home_dir(Y_AXIS), fr_mm_s); - endstops.hit_on_purpose(); // clear endstop hit flags + + endstops.validate_homing_move(); + current_position[X_AXIS] = current_position[Y_AXIS] = 0.0; #if ENABLED(SENSORLESS_HOMING) @@ -3755,9 +3783,17 @@ inline void gcode_G4() { SERIAL_ECHOPGM(" (Aligned With"); #endif #if Y_PROBE_OFFSET_FROM_EXTRUDER > 0 - SERIAL_ECHOPGM("-Back"); + #if IS_SCARA + SERIAL_ECHOPGM("-Distal"); + #else + SERIAL_ECHOPGM("-Back"); + #endif #elif Y_PROBE_OFFSET_FROM_EXTRUDER < 0 - SERIAL_ECHOPGM("-Front"); + #if IS_SCARA + SERIAL_ECHOPGM("-Proximal"); + #else + SERIAL_ECHOPGM("-Front"); + #endif #elif X_PROBE_OFFSET_FROM_EXTRUDER != 0 SERIAL_ECHOPGM("-Center"); #endif @@ -3871,7 +3907,7 @@ inline void gcode_G4() { * A delta can only safely home all axes at the same time * This is like quick_home_xy() but for 3 towers. */ - inline bool home_delta() { + inline void home_delta() { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS(">>> home_delta", current_position); #endif @@ -3895,22 +3931,13 @@ inline void gcode_G4() { delta_sensorless_homing(false); #endif - // If an endstop was not hit, then damage can occur if homing is continued. - // This can occur if the delta height not set correctly. - if (!(endstops.trigger_state() & (_BV(X_MAX) | _BV(Y_MAX) | _BV(Z_MAX)))) { - LCD_MESSAGEPGM(MSG_ERR_HOMING_FAILED); - SERIAL_ERROR_START(); - SERIAL_ERRORLNPGM(MSG_ERR_HOMING_FAILED); - return false; - } - - endstops.hit_on_purpose(); // clear endstop hit flags + endstops.validate_homing_move(); // At least one carriage has reached the top. // Now re-home each carriage separately. - HOMEAXIS(A); - HOMEAXIS(B); - HOMEAXIS(C); + homeaxis(A_AXIS); + homeaxis(B_AXIS); + homeaxis(C_AXIS); // Set all carriages to their home positions // Do this here all at once for Delta, because @@ -3923,8 +3950,6 @@ inline void gcode_G4() { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS("<<< home_delta", current_position); #endif - - return true; } #endif // DELTA @@ -3943,7 +3968,7 @@ inline void gcode_G4() { inline void home_z_safely() { // Disallow Z homing if X or Y are unknown - if (!axis_known_position[X_AXIS] || !axis_known_position[Y_AXIS]) { + if (!TEST(axis_known_position, X_AXIS) || !TEST(axis_known_position, Y_AXIS)) { LCD_MESSAGEPGM(MSG_ERR_Z_HOMING); SERIAL_ECHO_START(); SERIAL_ECHOLNPGM(MSG_ERR_Z_HOMING); @@ -3984,7 +4009,7 @@ inline void gcode_G4() { #endif do_blocking_move_to_xy(destination[X_AXIS], destination[Y_AXIS]); - HOMEAXIS(Z); + homeaxis(Z_AXIS); } else { LCD_MESSAGEPGM(MSG_ZPROBE_OUT); @@ -4013,6 +4038,8 @@ inline void gcode_G4() { * None Home to all axes with no parameters. * With QUICK_HOME enabled XY will home together, then Z. * + * O Home only if position is unknown + * * Rn Raise by n mm/inches before homing * * Cartesian parameters @@ -4026,11 +4053,34 @@ inline void gcode_G28(const bool always_home_all) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) { - SERIAL_ECHOLNPGM(">>> gcode_G28"); + SERIAL_ECHOLNPGM(">>> G28"); log_machine_info(); } #endif + #if ENABLED(MARLIN_DEV_MODE) + if (parser.seen('S')) { + LOOP_XYZ(a) set_axis_is_at_home((AxisEnum)a); + SYNC_PLAN_POSITION_KINEMATIC(); + SERIAL_ECHOLNPGM("Simulated Homing"); + report_current_position(); + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< G28"); + #endif + return; + } + #endif + + if (all_axes_known() && parser.boolval('O')) { // home only if needed + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + SERIAL_ECHOLNPGM("> homing not needed, skip"); + SERIAL_ECHOLNPGM("<<< G28"); + } + #endif + return; + } + // Wait for planner moves to finish! planner.synchronize(); @@ -4051,6 +4101,11 @@ inline void gcode_G28(const bool always_home_all) { workspace_plane = PLANE_XY; #endif + #if ENABLED(BLTOUCH) + bltouch_command(BLTOUCH_RESET); + set_bltouch_deployed(false); + #endif + // Always home with tool 0 active #if HOTENDS > 1 #if DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE) @@ -4085,13 +4140,13 @@ inline void gcode_G28(const bool always_home_all) { #if Z_HOME_DIR > 0 // If homing away from BED do Z first - if (home_all || homeZ) HOMEAXIS(Z); + if (home_all || homeZ) homeaxis(Z_AXIS); #endif const float z_homing_height = ( #if ENABLED(UNKNOWN_Z_NO_RAISE) - !axis_known_position[Z_AXIS] ? 0 : + !TEST(axis_known_position, Z_AXIS) ? 0 : #endif (parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT) ); @@ -4123,7 +4178,7 @@ inline void gcode_G28(const bool always_home_all) { #if ENABLED(CODEPENDENT_XY_HOMING) || homeX #endif - ) HOMEAXIS(Y); + ) homeaxis(Y_AXIS); #endif @@ -4138,14 +4193,14 @@ inline void gcode_G28(const bool always_home_all) { // Always home the 2nd (right) extruder first active_extruder = 1; - HOMEAXIS(X); + homeaxis(X_AXIS); // Remember this extruder's position for later tool change inactive_extruder_x_pos = current_position[X_AXIS]; // Home the 1st (left) extruder active_extruder = 0; - HOMEAXIS(X); + homeaxis(X_AXIS); // Consider the active extruder to be parked COPY(raised_parked_position, current_position); @@ -4154,14 +4209,14 @@ inline void gcode_G28(const bool always_home_all) { #else - HOMEAXIS(X); + homeaxis(X_AXIS); #endif } // Home Y (after X) #if DISABLED(HOME_Y_BEFORE_X) - if (home_all || homeY) HOMEAXIS(Y); + if (home_all || homeY) homeaxis(Y_AXIS); #endif // Home Z last if homing towards the bed @@ -4170,7 +4225,7 @@ inline void gcode_G28(const bool always_home_all) { #if ENABLED(Z_SAFE_HOMING) home_z_safely(); #else - HOMEAXIS(Z); + homeaxis(Z_AXIS); #endif #if HOMING_Z_WITH_PROBE && defined(Z_AFTER_PROBING) @@ -4222,7 +4277,7 @@ inline void gcode_G28(const bool always_home_all) { #endif #if ENABLED(DEBUG_LEVELING_FEATURE) - if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< gcode_G28"); + if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< G28"); #endif } // G28 @@ -4416,7 +4471,7 @@ void home_all_axes() { gcode_G28(true); } reset_bed_level(); break; - } // switch(state) + } // switch (state) if (state == MeshNext) { SERIAL_PROTOCOLPAIR("MBL G29 point ", MIN(mbl_probe_index, GRID_MAX_POINTS)); @@ -4448,6 +4503,8 @@ void home_all_axes() { gcode_G28(true); } * * Enhanced G29 Auto Bed Leveling Probe Routine * + * O Auto-level only if needed + * * D Dry-Run mode. Just evaluate the bed Topology - Don't apply * or alter the bed level data. Useful to check the topology * after a first run of G29. @@ -4554,6 +4611,16 @@ void home_all_axes() { gcode_G28(true); } // Don't allow auto-leveling without homing first if (axis_unhomed_error()) return; + if (!no_action && planner.leveling_active && parser.boolval('O')) { // Auto-level only if needed + #if ENABLED(DEBUG_LEVELING_FEATURE) + if (DEBUGGING(LEVELING)) { + SERIAL_ECHOLNPGM("> Auto-level not needed, skip"); + SERIAL_ECHOLNPGM("<<< G29"); + } + #endif + return; + } + // Define local vars 'static' for manual probing, 'auto' otherwise #if ENABLED(PROBE_MANUALLY) #define ABL_VAR static @@ -4636,6 +4703,10 @@ void home_all_axes() { gcode_G28(true); } */ if (!g29_in_progress) { + #if ENABLED(DUAL_X_CARRIAGE) + if (active_extruder != 0) tool_change(0); + #endif + #if ENABLED(PROBE_MANUALLY) || ENABLED(AUTO_BED_LEVELING_LINEAR) abl_probe_index = -1; #endif @@ -4666,8 +4737,8 @@ void home_all_axes() { gcode_G28(true); } if (!isnan(rx) && !isnan(ry)) { // Get nearest i / j from rx / ry - i = (rx - bilinear_start[X_AXIS] + 0.5 * xGridSpacing) / xGridSpacing; - j = (ry - bilinear_start[Y_AXIS] + 0.5 * yGridSpacing) / yGridSpacing; + i = (rx - bilinear_start[X_AXIS] + 0.5f * xGridSpacing) / xGridSpacing; + j = (ry - bilinear_start[Y_AXIS] + 0.5f * yGridSpacing) / yGridSpacing; i = constrain(i, 0, GRID_MAX_POINTS_X - 1); j = constrain(j, 0, GRID_MAX_POINTS_Y - 1); } @@ -5447,12 +5518,10 @@ void home_all_axes() { gcode_G28(true); } float lcd_probe_pt(const float &rx, const float &ry); - bool ac_home() { + void ac_home() { endstops.enable(true); - if (!home_delta()) - return false; + home_delta(); endstops.not_homing(); - return true; } void ac_setup(const bool reset_bed) { @@ -5562,7 +5631,7 @@ void home_all_axes() { gcode_G28(true); } S2 += sq(z_pt[rad]); N++; } - return round(SQRT(S2 / N) * 1000.0) / 1000.0 + 0.00001; + return LROUND(SQRT(S2 / N) * 1000.0) / 1000.0 + 0.00001; } } return 0.00001; @@ -5622,7 +5691,7 @@ void home_all_axes() { gcode_G28(true); } } if (_7p_calibration) { // probe extra center points - const float start = _7p_9_center ? _CA + _7P_STEP / 3.0 : _7p_6_center ? _CA : __C, + const float start = _7p_9_center ? float(_CA) + _7P_STEP / 3.0 : _7p_6_center ? float(_CA) : float(__C), steps = _7p_9_center ? _4P_STEP / 3.0 : _7p_6_center ? _7P_STEP : _4P_STEP; I_LOOP_CAL_PT(rad, start, steps) { const float a = RADIANS(210 + (360 / NPP) * (rad - 1)), @@ -5654,8 +5723,8 @@ void home_all_axes() { gcode_G28(true); } const float z_temp = calibration_probe(cos(a) * r, sin(a) * r, stow_after_each, set_up); if (isnan(z_temp)) return false; // split probe point to neighbouring calibration points - z_pt[uint8_t(round(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cos(RADIANS(interpol * 90))); - z_pt[uint8_t(round(rad - interpol)) % NPP + 1] += z_temp * sq(sin(RADIANS(interpol * 90))); + z_pt[uint8_t(LROUND(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cos(RADIANS(interpol * 90))); + z_pt[uint8_t(LROUND(rad - interpol)) % NPP + 1] += z_temp * sq(sin(RADIANS(interpol * 90))); } zig_zag = !zig_zag; } @@ -5736,7 +5805,7 @@ void home_all_axes() { gcode_G28(true); } float h_fac = 0.0; h_fac = r_quot / (2.0 / 3.0); - h_fac = 1.0 / h_fac; // (2/3)/CR + h_fac = 1.0f / h_fac; // (2/3)/CR return h_fac; } @@ -5905,8 +5974,7 @@ void home_all_axes() { gcode_G28(true); } ac_setup(!_0p_calibration && !_1p_calibration); - if (!_0p_calibration) - if (!ac_home()) return; + if (!_0p_calibration) ac_home(); do { // start iterations @@ -6058,9 +6126,9 @@ void home_all_axes() { gcode_G28(true); } char mess[21]; strcpy_P(mess, PSTR("Calibration sd:")); if (zero_std_dev_min < 1) - sprintf_P(&mess[15], PSTR("0.%03i"), (int)round(zero_std_dev_min * 1000.0)); + sprintf_P(&mess[15], PSTR("0.%03i"), (int)LROUND(zero_std_dev_min * 1000.0)); else - sprintf_P(&mess[15], PSTR("%03i.x"), (int)round(zero_std_dev_min)); + sprintf_P(&mess[15], PSTR("%03i.x"), (int)LROUND(zero_std_dev_min)); lcd_setstatus(mess); print_calibration_settings(_endstop_results, _angle_results); serialprintPGM(save_message); @@ -6094,12 +6162,12 @@ void home_all_axes() { gcode_G28(true); } strcpy_P(mess, enddryrun); strcpy_P(&mess[11], PSTR(" sd:")); if (zero_std_dev < 1) - sprintf_P(&mess[15], PSTR("0.%03i"), (int)round(zero_std_dev * 1000.0)); + sprintf_P(&mess[15], PSTR("0.%03i"), (int)LROUND(zero_std_dev * 1000.0)); else - sprintf_P(&mess[15], PSTR("%03i.x"), (int)round(zero_std_dev)); + sprintf_P(&mess[15], PSTR("%03i.x"), (int)LROUND(zero_std_dev)); lcd_setstatus(mess); } - if (!ac_home()) return; + ac_home(); } while (((zero_std_dev < test_precision && iterations < 31) || iterations <= force_iterations) && zero_std_dev > calibration_precision); @@ -6463,12 +6531,12 @@ inline void gcode_G92() { delay_for_power_down(); } else { - int16_t ocr_val = (spindle_laser_power - (SPEED_POWER_INTERCEPT)) * (1.0 / (SPEED_POWER_SLOPE)); // convert RPM to PWM duty cycle + int16_t ocr_val = (spindle_laser_power - (SPEED_POWER_INTERCEPT)) * (1.0f / (SPEED_POWER_SLOPE)); // convert RPM to PWM duty cycle NOMORE(ocr_val, 255); // limit to max the Atmel PWM will support if (spindle_laser_power <= SPEED_POWER_MIN) - ocr_val = (SPEED_POWER_MIN - (SPEED_POWER_INTERCEPT)) * (1.0 / (SPEED_POWER_SLOPE)); // minimum setting + ocr_val = (SPEED_POWER_MIN - (SPEED_POWER_INTERCEPT)) * (1.0f / (SPEED_POWER_SLOPE)); // minimum setting if (spindle_laser_power >= SPEED_POWER_MAX) - ocr_val = (SPEED_POWER_MAX - (SPEED_POWER_INTERCEPT)) * (1.0 / (SPEED_POWER_SLOPE)); // limit to max RPM + ocr_val = (SPEED_POWER_MAX - (SPEED_POWER_INTERCEPT)) * (1.0f / (SPEED_POWER_SLOPE)); // limit to max RPM if (SPINDLE_LASER_PWM_INVERT) ocr_val = 255 - ocr_val; WRITE(SPINDLE_LASER_ENABLE_PIN, SPINDLE_LASER_ENABLE_INVERT); // turn spindle on (active low) analogWrite(SPINDLE_LASER_PWM_PIN, ocr_val & 0xFF); // only write low byte @@ -7022,6 +7090,9 @@ inline void gcode_M17() { * M23: Open a file */ inline void gcode_M23() { + #if ENABLED(POWER_LOSS_RECOVERY) + card.removeJobRecoveryFile(); + #endif // Simplify3D includes the size, so zero out all spaces (#7227) for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; card.openFile(parser.string_arg, true); @@ -7031,16 +7102,22 @@ inline void gcode_M17() { * M24: Start or Resume SD Print */ inline void gcode_M24() { - #if ENABLED(POWER_LOSS_RECOVERY) - card.removeJobRecoveryFile(); - #endif - #if ENABLED(PARK_HEAD_ON_PAUSE) resume_print(); #endif + #if ENABLED(POWER_LOSS_RECOVERY) + if (parser.seenval('S')) card.setIndex(parser.value_long()); + #endif + card.startFileprint(); - print_job_timer.start(); + + #if ENABLED(POWER_LOSS_RECOVERY) + if (parser.seenval('T')) + print_job_timer.resume(parser.value_long()); + else + #endif + print_job_timer.start(); } /** @@ -7203,6 +7280,11 @@ static bool pin_is_protected(const pin_t pin) { return false; } +inline void protected_pin_err() { + SERIAL_ERROR_START(); + SERIAL_ERRORLNPGM(MSG_ERR_PROTECTED_PIN); +} + /** * M42: Change pin status via GCode * @@ -7216,11 +7298,7 @@ inline void gcode_M42() { const pin_t pin_number = parser.byteval('P', LED_PIN); if (pin_number < 0) return; - if (pin_is_protected(pin_number)) { - SERIAL_ERROR_START(); - SERIAL_ERRORLNPGM(MSG_ERR_PROTECTED_PIN); - return; - } + if (pin_is_protected(pin_number)) return protected_pin_err(); pinMode(pin_number, OUTPUT); digitalWrite(pin_number, pin_status); @@ -7246,21 +7324,21 @@ inline void gcode_M42() { #include "pinsDebug.h" inline void toggle_pins() { - const bool I_flag = parser.boolval('I'); + const bool ignore_protection = parser.boolval('I'); const int repeat = parser.intval('R', 1), start = parser.intval('S'), end = parser.intval('L', NUM_DIGITAL_PINS - 1), wait = parser.intval('W', 500); for (uint8_t pin = start; pin <= end; pin++) { - //report_pin_state_extended(pin, I_flag, false); + //report_pin_state_extended(pin, ignore_protection, false); - if (!I_flag && pin_is_protected(pin)) { - report_pin_state_extended(pin, I_flag, true, "Untouched "); + if (!ignore_protection && pin_is_protected(pin)) { + report_pin_state_extended(pin, ignore_protection, true, "Untouched "); SERIAL_EOL(); } else { - report_pin_state_extended(pin, I_flag, true, "Pulsing "); + report_pin_state_extended(pin, ignore_protection, true, "Pulsing "); #if AVR_AT90USB1286_FAMILY // Teensy IDEs don't know about these pins so must use FASTIO if (pin == TEENSY_E2) { SET_OUTPUT(TEENSY_E2); @@ -7479,7 +7557,7 @@ inline void gcode_M42() { SERIAL_PROTOCOLLNPGM("Watching pins"); byte pin_state[last_pin - first_pin + 1]; for (pin_t pin = first_pin; pin <= last_pin; pin++) { - if (pin_is_protected(pin) && !ignore_protection) continue; + if (!ignore_protection && pin_is_protected(pin)) continue; pinMode(pin, INPUT_PULLUP); delay(1); /* @@ -7497,7 +7575,7 @@ inline void gcode_M42() { for (;;) { for (pin_t pin = first_pin; pin <= last_pin; pin++) { - if (pin_is_protected(pin) && !ignore_protection) continue; + if (!ignore_protection && pin_is_protected(pin)) continue; const byte val = /* IS_ANALOG(pin) @@ -7607,7 +7685,7 @@ inline void gcode_M42() { setup_for_endstop_or_probe_move(); - double mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples]; + float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples]; // Move to the first point, deploy, and probe const float t = probe_pt(X_probe_location, Y_probe_location, raise_after, verbose_level); @@ -7638,7 +7716,7 @@ inline void gcode_M42() { } for (uint8_t l = 0; l < n_legs - 1; l++) { - double delta_angle; + float delta_angle; if (schizoid_flag) // The points of a 5 point star are 72 degrees apart. We need to @@ -7695,7 +7773,7 @@ inline void gcode_M42() { /** * Get the current mean for the data points we have so far */ - double sum = 0.0; + float sum = 0.0; for (uint8_t j = 0; j <= n; j++) sum += sample_set[j]; mean = sum / (n + 1); @@ -8045,7 +8123,7 @@ inline void gcode_M109() { #define TEMP_CONDITIONS (wants_to_cool ? thermalManager.isCoolingHotend(target_extruder) : thermalManager.isHeatingHotend(target_extruder)) #endif - float target_temp = -1.0, old_temp = 9999.0; + float target_temp = -1, old_temp = 9999; bool wants_to_cool = false; wait_for_heatup = true; millis_t now, next_temp_ms = 0, next_cool_check_ms = 0; @@ -8124,7 +8202,7 @@ inline void gcode_M109() { // break after MIN_COOLING_SLOPE_TIME seconds // if the temperature did not drop at least MIN_COOLING_SLOPE_DEG if (!next_cool_check_ms || ELAPSED(now, next_cool_check_ms)) { - if (old_temp - temp < MIN_COOLING_SLOPE_DEG) break; + if (old_temp - temp < float(MIN_COOLING_SLOPE_DEG)) break; next_cool_check_ms = now + 1000UL * MIN_COOLING_SLOPE_TIME; old_temp = temp; } @@ -8270,7 +8348,7 @@ inline void gcode_M109() { // Break after MIN_COOLING_SLOPE_TIME_BED seconds // if the temperature did not drop at least MIN_COOLING_SLOPE_DEG_BED if (!next_cool_check_ms || ELAPSED(now, next_cool_check_ms)) { - if (old_temp - temp < MIN_COOLING_SLOPE_DEG_BED) break; + if (old_temp - temp < float(MIN_COOLING_SLOPE_DEG_BED)) break; next_cool_check_ms = now + 1000UL * MIN_COOLING_SLOPE_TIME_BED; old_temp = temp; } @@ -8329,6 +8407,23 @@ inline void gcode_M111() { } else { SERIAL_ECHOPGM(MSG_DEBUG_OFF); + #if !defined(__AVR__) || !defined(USBCON) + #if ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS) + SERIAL_ECHOPAIR("\nBuffer Overruns: ", customizedSerial.buffer_overruns()); + #endif + + #if ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS) + SERIAL_ECHOPAIR("\nFraming Errors: ", customizedSerial.framing_errors()); + #endif + + #if ENABLED(SERIAL_STATS_DROPPED_RX) + SERIAL_ECHOPAIR("\nDropped bytes: ", customizedSerial.dropped()); + #endif + + #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) + SERIAL_ECHOPAIR("\nMax RX Queue Size: ", customizedSerial.rxMaxEnqueued()); + #endif + #endif // !__AVR__ || !USBCON } SERIAL_EOL(); } @@ -8564,9 +8659,11 @@ inline void gcode_M92() { if (parser.seen(axis_codes[i])) { if (i == E_AXIS) { const float value = parser.value_per_axis_unit((AxisEnum)(E_AXIS + TARGET_EXTRUDER)); - if (value < 20.0) { + if (value < 20) { float factor = planner.axis_steps_per_mm[E_AXIS + TARGET_EXTRUDER] / value; // increase e constants if M92 E14 is given for netfab. - planner.max_jerk[E_AXIS] *= factor; + #if DISABLED(JUNCTION_DEVIATION) + planner.max_jerk[E_AXIS] *= factor; + #endif planner.max_feedrate_mm_s[E_AXIS + TARGET_EXTRUDER] *= factor; planner.max_acceleration_steps_per_s2[E_AXIS + TARGET_EXTRUDER] *= factor; } @@ -8980,7 +9077,7 @@ inline void gcode_M121() { endstops.enable_globally(false); } // setting any extruder filament size disables volumetric on the assumption that // slicers either generate in extruder values as cubic mm or as as filament feeds // for all extruders - if ( (parser.volumetric_enabled = (parser.value_linear_units() != 0.0)) ) + if ( (parser.volumetric_enabled = (parser.value_linear_units() != 0)) ) planner.set_filament_size(target_extruder, parser.value_linear_units()); } planner.calculate_volumetric_multipliers(); @@ -9067,28 +9164,43 @@ inline void gcode_M204() { /** * M205: Set Advanced Settings * + * B = Min Segment Time (µs) * S = Min Feed Rate (units/s) * T = Min Travel Feed Rate (units/s) - * B = Min Segment Time (µs) * X = Max X Jerk (units/sec^2) * Y = Max Y Jerk (units/sec^2) * Z = Max Z Jerk (units/sec^2) * E = Max E Jerk (units/sec^2) + * J = Junction Deviation (mm) (Requires JUNCTION_DEVIATION) */ inline void gcode_M205() { + if (parser.seen('B')) planner.min_segment_time_us = parser.value_ulong(); if (parser.seen('S')) planner.min_feedrate_mm_s = parser.value_linear_units(); if (parser.seen('T')) planner.min_travel_feedrate_mm_s = parser.value_linear_units(); - if (parser.seen('B')) planner.min_segment_time_us = parser.value_ulong(); - if (parser.seen('X')) planner.max_jerk[X_AXIS] = parser.value_linear_units(); - if (parser.seen('Y')) planner.max_jerk[Y_AXIS] = parser.value_linear_units(); - if (parser.seen('Z')) { - planner.max_jerk[Z_AXIS] = parser.value_linear_units(); - #if HAS_MESH - if (planner.max_jerk[Z_AXIS] <= 0.1) - SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses."); - #endif - } - if (parser.seen('E')) planner.max_jerk[E_AXIS] = parser.value_linear_units(); + #if ENABLED(JUNCTION_DEVIATION) + if (parser.seen('J')) { + const float junc_dev = parser.value_linear_units(); + if (WITHIN(junc_dev, 0.01f, 0.3f)) { + planner.junction_deviation_mm = junc_dev; + planner.recalculate_max_e_jerk(); + } + else { + SERIAL_ERROR_START(); + SERIAL_ERRORLNPGM("?J out of range (0.01 to 0.3)"); + } + } + #else + if (parser.seen('X')) planner.max_jerk[X_AXIS] = parser.value_linear_units(); + if (parser.seen('Y')) planner.max_jerk[Y_AXIS] = parser.value_linear_units(); + if (parser.seen('Z')) { + planner.max_jerk[Z_AXIS] = parser.value_linear_units(); + #if HAS_MESH + if (planner.max_jerk[Z_AXIS] <= 0.1f) + SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses."); + #endif + } + if (parser.seen('E')) planner.max_jerk[E_AXIS] = parser.value_linear_units(); + #endif } #if HAS_M206_COMMAND @@ -9406,30 +9518,21 @@ inline void gcode_M221() { */ inline void gcode_M226() { if (parser.seen('P')) { - const int pin = parser.value_int(), - pin_state = parser.intval('S', -1); // required pin state - default is inverted - - if (WITHIN(pin_state, -1, 1) && pin > -1 && !pin_is_protected(pin)) { - - int target = LOW; - - planner.synchronize(); - - pinMode(pin, INPUT); - switch (pin_state) { - case 1: - target = HIGH; - break; - case 0: - target = LOW; - break; - case -1: - target = !digitalRead(pin); - break; + const int pin = parser.value_int(), pin_state = parser.intval('S', -1); + if (WITHIN(pin_state, -1, 1) && pin > -1) { + if (pin_is_protected(pin)) + protected_pin_err(); + else { + int target = LOW; + planner.synchronize(); + pinMode(pin, INPUT); + switch (pin_state) { + case 1: target = HIGH; break; + case 0: target = LOW; break; + case -1: target = !digitalRead(pin); break; + } + while (digitalRead(pin) != target) idle(); } - - while (digitalRead(pin) != target) idle(); - } // pin_state -1 0 1 && pin > -1 } // parser.seen('P') } @@ -10643,9 +10746,9 @@ inline void gcode_M502() { /** * M701: Load filament * - * T[extruder] - Optional extruder number. Current extruder if omitted. - * Z[distance] - Move the Z axis by this distance - * L[distance] - Extrude distance for insertion (positive value) (manual reload) + * T - Optional extruder number. Current extruder if omitted. + * Z - Move the Z axis by this distance + * L - Extrude distance for insertion (positive value) (manual reload) * * Default values are used for omitted arguments. */ @@ -10702,10 +10805,10 @@ inline void gcode_M502() { /** * M702: Unload filament * - * T[extruder] - Optional extruder number. If omitted, current extruder + * T - Optional extruder number. If omitted, current extruder * (or ALL extruders with FILAMENT_UNLOAD_ALL_EXTRUDERS). - * Z[distance] - Move the Z axis by this distance - * U[distance] - Retract distance for removal (manual reload) + * Z - Move the Z axis by this distance + * U - Retract distance for removal (manual reload) * * Default values are used for omitted arguments. */ @@ -10774,6 +10877,38 @@ inline void gcode_M502() { #endif // FILAMENT_LOAD_UNLOAD_GCODES +#if ENABLED(MAX7219_GCODE) + /** + * M7219: Control the Max7219 LED matrix + * + * I - Initialize (clear) the matrix + * C - Set a column to the 8-bit value V + * R - Set a row to the 8-bit value V + * X - X position of an LED to set or toggle + * Y - Y position of an LED to set or toggle + * V - The 8-bit value or on/off state to set + */ + inline void gcode_M7219() { + if (parser.seen('I')) + Max7219_Clear(); + else if (parser.seenval('R')) { + const uint8_t r = parser.value_int(); + Max7219_Set_Row(r, parser.byteval('V')); + } + else if (parser.seenval('C')) { + const uint8_t c = parser.value_int(); + Max7219_Set_Column(c, parser.byteval('V')); + } + else if (parser.seenval('X') || parser.seenval('Y')) { + const uint8_t x = parser.byteval('X'), y = parser.byteval('Y'); + if (parser.seenval('V')) + Max7219_LED_Set(x, y, parser.boolval('V')); + else + Max7219_LED_Toggle(x, y); + } + } +#endif // MAX7219_GCODE + #if ENABLED(LIN_ADVANCE) /** * M900: Get or Set Linear Advance K-factor @@ -11147,61 +11282,73 @@ inline void gcode_M502() { bool report = true; const uint8_t index = parser.byteval('I'); LOOP_XYZ(i) if (parser.seen(axis_codes[i])) { - const int8_t value = (int8_t)constrain(parser.value_int(), -63, 64); + const int8_t value = (int8_t)constrain(parser.value_int(), -64, 63); report = false; switch (i) { - case X_AXIS: - #if ENABLED(X_IS_TMC2130) || ENABLED(IS_TRAMS) - if (index == 0) TMC_SET_SGT(X); - #endif - #if ENABLED(X2_IS_TMC2130) - if (index == 1) TMC_SET_SGT(X2); - #endif - break; - case Y_AXIS: - #if ENABLED(Y_IS_TMC2130) || ENABLED(IS_TRAMS) - if (index == 0) TMC_SET_SGT(Y); - #endif - #if ENABLED(Y2_IS_TMC2130) - if (index == 1) TMC_SET_SGT(Y2); - #endif - break; - case Z_AXIS: - #if ENABLED(Z_IS_TMC2130) || ENABLED(IS_TRAMS) - if (index == 0) TMC_SET_SGT(Z); - #endif - #if ENABLED(Z2_IS_TMC2130) - if (index == 1) TMC_SET_SGT(Z2); - #endif - break; + #if X_SENSORLESS + case X_AXIS: + #if ENABLED(X_IS_TMC2130) || ENABLED(IS_TRAMS) + if (index == 0) TMC_SET_SGT(X); + #endif + #if ENABLED(X2_IS_TMC2130) + if (index == 1) TMC_SET_SGT(X2); + #endif + break; + #endif + #if Y_SENSORLESS + case Y_AXIS: + #if ENABLED(Y_IS_TMC2130) || ENABLED(IS_TRAMS) + if (index == 0) TMC_SET_SGT(Y); + #endif + #if ENABLED(Y2_IS_TMC2130) + if (index == 1) TMC_SET_SGT(Y2); + #endif + break; + #endif + #if Z_SENSORLESS + case Z_AXIS: + #if ENABLED(Z_IS_TMC2130) || ENABLED(IS_TRAMS) + if (index == 0) TMC_SET_SGT(Z); + #endif + #if ENABLED(Z2_IS_TMC2130) + if (index == 1) TMC_SET_SGT(Z2); + #endif + break; + #endif } } if (report) LOOP_XYZ(i) switch (i) { - case X_AXIS: - #if ENABLED(X_IS_TMC2130) || ENABLED(IS_TRAMS) - TMC_SAY_SGT(X); - #endif - #if ENABLED(X2_IS_TMC2130) - TMC_SAY_SGT(X2); - #endif - break; - case Y_AXIS: - #if ENABLED(Y_IS_TMC2130) || ENABLED(IS_TRAMS) - TMC_SAY_SGT(Y); - #endif - #if ENABLED(Y2_IS_TMC2130) - TMC_SAY_SGT(Y2); - #endif - break; - case Z_AXIS: - #if ENABLED(Z_IS_TMC2130) || ENABLED(IS_TRAMS) - TMC_SAY_SGT(Z); - #endif - #if ENABLED(Z2_IS_TMC2130) - TMC_SAY_SGT(Z2); - #endif - break; + #if X_SENSORLESS + case X_AXIS: + #if ENABLED(X_IS_TMC2130) || ENABLED(IS_TRAMS) + TMC_SAY_SGT(X); + #endif + #if ENABLED(X2_IS_TMC2130) + TMC_SAY_SGT(X2); + #endif + break; + #endif + #if Y_SENSORLESS + case Y_AXIS: + #if ENABLED(Y_IS_TMC2130) || ENABLED(IS_TRAMS) + TMC_SAY_SGT(Y); + #endif + #if ENABLED(Y2_IS_TMC2130) + TMC_SAY_SGT(Y2); + #endif + break; + #endif + #if Z_SENSORLESS + case Z_AXIS: + #if ENABLED(Z_IS_TMC2130) || ENABLED(IS_TRAMS) + TMC_SAY_SGT(Z); + #endif + #if ENABLED(Z2_IS_TMC2130) + TMC_SAY_SGT(Z2); + #endif + break; + #endif } } #endif // SENSORLESS_HOMING @@ -11214,7 +11361,7 @@ inline void gcode_M502() { const uint16_t _rms = parser.seenval('S') ? parser.value_int() : CALIBRATION_CURRENT, _z = parser.seenval('Z') ? parser.value_linear_units() : CALIBRATION_EXTRA_HEIGHT; - if (!axis_known_position[Z_AXIS]) { + if (!TEST(axis_known_position, Z_AXIS)) { SERIAL_ECHOLNPGM("\nPlease home Z axis first"); return; } @@ -12417,6 +12564,10 @@ void process_parsed_command() { case 702: gcode_M702(); break; // M702: Unload Filament #endif + #if ENABLED(MAX7219_GCODE) + case 7219: gcode_M7219(); break; // M7219: Set LEDs, columns, and rows + #endif + #if ENABLED(DEBUG_GCODE_PARSER) case 800: parser.debug(); break; // M800: GCode Parser Test for M #endif @@ -12501,8 +12652,6 @@ void process_next_command() { #endif } - reset_stepper_timeout(); // Keep steppers powered - // Parse the next command in the queue parser.parse(current_command); process_parsed_command(); @@ -12709,30 +12858,9 @@ void ok_to_send() { delta_diagonal_rod_2_tower[B_AXIS] = sq(delta_diagonal_rod + drt[B_AXIS]); delta_diagonal_rod_2_tower[C_AXIS] = sq(delta_diagonal_rod + drt[C_AXIS]); update_software_endstops(Z_AXIS); - axis_homed[X_AXIS] = axis_homed[Y_AXIS] = axis_homed[Z_AXIS] = false; + axis_homed = 0; } - #if ENABLED(DELTA_FAST_SQRT) - /** - * Fast inverse sqrt from Quake III Arena - * See: https://en.wikipedia.org/wiki/Fast_inverse_square_root - */ - float Q_rsqrt(const float number) { - long i; - float x2, y; - const float threehalfs = 1.5f; - x2 = number * 0.5f; - y = number; - i = * ( long * ) &y; // evil floating point bit level hacking - i = 0x5F3759DF - ( i >> 1 ); // what the f***? - y = * ( float * ) &i; - y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration - // y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed - return y; - } - - #endif - /** * Delta Inverse Kinematics * @@ -12747,9 +12875,6 @@ void ok_to_send() { * * - Disable the home_offset (M206) and/or position_shift (G92) * features to remove up to 12 float additions. - * - * - Use a fast-inverse-sqrt function and add the reciprocal. - * (see above) */ #define DELTA_DEBUG(VAR) do { \ @@ -12815,7 +12940,7 @@ void ok_to_send() { * * The result is stored in the cartes[] array. */ - void forward_kinematics_DELTA(float z1, float z2, float z3) { + void forward_kinematics_DELTA(const float &z1, const float &z2, const float &z3) { // Create a vector in old coordinates along x axis of new coordinate const float p12[] = { delta_tower[B_AXIS][X_AXIS] - delta_tower[A_AXIS][X_AXIS], @@ -12823,11 +12948,11 @@ void ok_to_send() { z2 - z1 }, - // Get the Magnitude of vector. - d = SQRT(sq(p12[0]) + sq(p12[1]) + sq(p12[2])), + // Get the reciprocal of Magnitude of vector. + d2 = sq(p12[0]) + sq(p12[1]) + sq(p12[2]), inv_d = RSQRT(d2), - // Create unit vector by dividing by magnitude. - ex[3] = { p12[0] / d, p12[1] / d, p12[2] / d }, + // Create unit vector by multiplying by the inverse of the magnitude. + ex[3] = { p12[0] * inv_d, p12[1] * inv_d, p12[2] * inv_d }, // Get the vector from the origin of the new system to the third point. p13[3] = { @@ -12846,11 +12971,11 @@ void ok_to_send() { // variable that will be the unit vector after we scale it. float ey[3] = { p13[0] - iex[0], p13[1] - iex[1], p13[2] - iex[2] }; - // The magnitude of Y component - const float j = SQRT(sq(ey[0]) + sq(ey[1]) + sq(ey[2])); + // The magnitude and the inverse of the magnitude of Y component + const float j2 = sq(ey[0]) + sq(ey[1]) + sq(ey[2]), inv_j = RSQRT(j2); // Convert to a unit vector - ey[0] /= j; ey[1] /= j; ey[2] /= j; + ey[0] *= inv_j; ey[1] *= inv_j; ey[2] *= inv_j; // The cross product of the unit x and y is the unit z // float[] ez = vectorCrossProd(ex, ey); @@ -12861,8 +12986,8 @@ void ok_to_send() { }, // We now have the d, i and j values defined in Wikipedia. // Plug them into the equations defined in Wikipedia for Xnew, Ynew and Znew - Xnew = (delta_diagonal_rod_2_tower[A_AXIS] - delta_diagonal_rod_2_tower[B_AXIS] + sq(d)) / (d * 2), - Ynew = ((delta_diagonal_rod_2_tower[A_AXIS] - delta_diagonal_rod_2_tower[C_AXIS] + HYPOT2(i, j)) / 2 - i * Xnew) / j, + Xnew = (delta_diagonal_rod_2_tower[A_AXIS] - delta_diagonal_rod_2_tower[B_AXIS] + d2) * inv_d * 0.5, + Ynew = ((delta_diagonal_rod_2_tower[A_AXIS] - delta_diagonal_rod_2_tower[C_AXIS] + sq(i) + j2) * 0.5 - i * Xnew) * inv_j, Znew = SQRT(delta_diagonal_rod_2_tower[A_AXIS] - HYPOT2(Xnew, Ynew)); // Start from the origin of the old coordinates and add vectors in the @@ -12870,10 +12995,10 @@ void ok_to_send() { // in the old system. cartes[X_AXIS] = delta_tower[A_AXIS][X_AXIS] + ex[0] * Xnew + ey[0] * Ynew - ez[0] * Znew; cartes[Y_AXIS] = delta_tower[A_AXIS][Y_AXIS] + ex[1] * Xnew + ey[1] * Ynew - ez[1] * Znew; - cartes[Z_AXIS] = z1 + ex[2] * Xnew + ey[2] * Ynew - ez[2] * Znew; + cartes[Z_AXIS] = z1 + ex[2] * Xnew + ey[2] * Ynew - ez[2] * Znew; } - void forward_kinematics_DELTA(float point[ABC]) { + void forward_kinematics_DELTA(const float (&point)[ABC]) { forward_kinematics_DELTA(point[A_AXIS], point[B_AXIS], point[C_AXIS]); } @@ -12969,7 +13094,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { NOLESS(segments, 1); // The approximate length of each segment - const float inv_segments = 1.0 / float(segments), + const float inv_segments = 1.0f / float(segments), cartesian_segment_mm = cartesian_mm * inv_segments, segment_distance[XYZE] = { xdiff * inv_segments, @@ -13155,7 +13280,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { * but may produce jagged lines. Try 0.5mm, 1.0mm, and 2.0mm * and compare the difference. */ - #define SCARA_MIN_SEGMENT_LENGTH 0.5 + #define SCARA_MIN_SEGMENT_LENGTH 0.5f #endif /** @@ -13204,14 +13329,14 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { // For SCARA enforce a minimum segment size #if IS_SCARA - NOMORE(segments, cartesian_mm * (1.0 / SCARA_MIN_SEGMENT_LENGTH)); + NOMORE(segments, cartesian_mm * (1.0f / float(SCARA_MIN_SEGMENT_LENGTH))); #endif // At least one segment is required NOLESS(segments, 1); // The approximate length of each segment - const float inv_segments = 1.0 / float(segments), + const float inv_segments = 1.0f / float(segments), segment_distance[XYZE] = { xdiff * inv_segments, ydiff * inv_segments, @@ -13219,7 +13344,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { ediff * inv_segments }; - #if DISABLED(SCARA_FEEDRATE_SCALING) + #if !HAS_FEEDRATE_SCALING const float cartesian_segment_mm = cartesian_mm * inv_segments; #endif @@ -13227,22 +13352,25 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { SERIAL_ECHOPAIR("mm=", cartesian_mm); SERIAL_ECHOPAIR(" seconds=", seconds); SERIAL_ECHOPAIR(" segments=", segments); - #if DISABLED(SCARA_FEEDRATE_SCALING) - SERIAL_ECHOLNPAIR(" segment_mm=", cartesian_segment_mm); - #else - SERIAL_EOL(); + #if !HAS_FEEDRATE_SCALING + SERIAL_ECHOPAIR(" segment_mm=", cartesian_segment_mm); #endif + SERIAL_EOL(); //*/ - #if ENABLED(SCARA_FEEDRATE_SCALING) + #if HAS_FEEDRATE_SCALING // SCARA needs to scale the feed rate from mm/s to degrees/s // i.e., Complete the angular vector in the given time. const float segment_length = cartesian_mm * inv_segments, - inv_segment_length = 1.0 / segment_length, // 1/mm/segs + inv_segment_length = 1.0f / segment_length, // 1/mm/segs inverse_secs = inv_segment_length * _feedrate_mm_s; float oldA = planner.position_float[A_AXIS], - oldB = planner.position_float[B_AXIS]; + oldB = planner.position_float[B_AXIS] + #if ENABLED(DELTA_FEEDRATE_SCALING) + , oldC = planner.position_float[C_AXIS] + #endif + ; /* SERIAL_ECHOPGM("Scaled kinematic move: "); @@ -13251,7 +13379,11 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { SERIAL_ECHOPAIR(") _feedrate_mm_s=", _feedrate_mm_s); SERIAL_ECHOPAIR(" inverse_secs=", inverse_secs); SERIAL_ECHOPAIR(" oldA=", oldA); - SERIAL_ECHOLNPAIR(" oldB=", oldB); + SERIAL_ECHOPAIR(" oldB=", oldB); + #if ENABLED(DELTA_FEEDRATE_SCALING) + SERIAL_ECHOPAIR(" oldC=", oldC); + #endif + SERIAL_EOL(); safe_delay(5); //*/ #endif @@ -13292,6 +13424,19 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { safe_delay(5); //*/ oldA = delta[A_AXIS]; oldB = delta[B_AXIS]; + #elif ENABLED(DELTA_FEEDRATE_SCALING) + // For DELTA scale the feed rate from Effector mm/s to Carriage mm/s + // i.e., Complete the linear vector in the given time. + if (!planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], raw[E_AXIS], SQRT(sq(delta[A_AXIS] - oldA) + sq(delta[B_AXIS] - oldB) + sq(delta[C_AXIS] - oldC)) * inverse_secs, active_extruder)) + break; + /* + SERIAL_ECHO(segments); + SERIAL_ECHOPAIR(": X=", raw[X_AXIS]); SERIAL_ECHOPAIR(" Y=", raw[Y_AXIS]); + SERIAL_ECHOPAIR(" A=", delta[A_AXIS]); SERIAL_ECHOPAIR(" B=", delta[B_AXIS]); SERIAL_ECHOPAIR(" C=", delta[C_AXIS]); + SERIAL_ECHOLNPAIR(" F", SQRT(sq(delta[A_AXIS] - oldA) + sq(delta[B_AXIS] - oldB) + sq(delta[C_AXIS] - oldC)) * inverse_secs * 60); + safe_delay(5); + //*/ + oldA = delta[A_AXIS]; oldB = delta[B_AXIS]; oldC = delta[C_AXIS]; #else if (!planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], raw[E_AXIS], _feedrate_mm_s, active_extruder, cartesian_segment_mm)) break; @@ -13299,16 +13444,31 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { } // Ensure last segment arrives at target location. - #if ENABLED(SCARA_FEEDRATE_SCALING) + #if HAS_FEEDRATE_SCALING inverse_kinematics(rtarget); ADJUST_DELTA(rtarget); + #endif + + #if ENABLED(SCARA_FEEDRATE_SCALING) const float diff2 = HYPOT2(delta[A_AXIS] - oldA, delta[B_AXIS] - oldB); if (diff2) { planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], rtarget[Z_AXIS], rtarget[E_AXIS], SQRT(diff2) * inverse_secs, active_extruder); /* SERIAL_ECHOPAIR("final: A=", delta[A_AXIS]); SERIAL_ECHOPAIR(" B=", delta[B_AXIS]); SERIAL_ECHOPAIR(" adiff=", delta[A_AXIS] - oldA); SERIAL_ECHOPAIR(" bdiff=", delta[B_AXIS] - oldB); - SERIAL_ECHOLNPAIR(" F", (SQRT(diff2) * inverse_secs) * 60); + SERIAL_ECHOLNPAIR(" F", SQRT(diff2) * inverse_secs * 60); + SERIAL_EOL(); + safe_delay(5); + //*/ + } + #elif ENABLED(DELTA_FEEDRATE_SCALING) + const float diff2 = sq(delta[A_AXIS] - oldA) + sq(delta[B_AXIS] - oldB) + sq(delta[C_AXIS] - oldC); + if (diff2) { + planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], rtarget[E_AXIS], SQRT(diff2) * inverse_secs, active_extruder); + /* + SERIAL_ECHOPAIR("final: A=", delta[A_AXIS]); SERIAL_ECHOPAIR(" B=", delta[B_AXIS]); SERIAL_ECHOPAIR(" C=", delta[C_AXIS]); + SERIAL_ECHOPAIR(" adiff=", delta[A_AXIS] - oldA); SERIAL_ECHOPAIR(" bdiff=", delta[B_AXIS] - oldB); SERIAL_ECHOPAIR(" cdiff=", delta[C_AXIS] - oldC); + SERIAL_ECHOLNPAIR(" F", SQRT(diff2) * inverse_secs * 60); SERIAL_EOL(); safe_delay(5); //*/ @@ -13545,7 +13705,7 @@ void prepare_move_to_destination() { const float flat_mm = radius * angular_travel, mm_of_travel = linear_travel ? HYPOT(flat_mm, linear_travel) : ABS(flat_mm); - if (mm_of_travel < 0.001) return; + if (mm_of_travel < 0.001f) return; uint16_t segments = FLOOR(mm_of_travel / (MM_PER_ARC_SEGMENT)); NOLESS(segments, 1); @@ -13582,7 +13742,7 @@ void prepare_move_to_destination() { linear_per_segment = linear_travel / segments, extruder_per_segment = extruder_travel / segments, sin_T = theta_per_segment, - cos_T = 1 - 0.5 * sq(theta_per_segment); // Small angle approximation + cos_T = 1 - 0.5f * sq(theta_per_segment); // Small angle approximation // Initialize the linear axis raw[l_axis] = current_position[l_axis]; @@ -13594,12 +13754,16 @@ void prepare_move_to_destination() { millis_t next_idle_ms = millis() + 200UL; - #if ENABLED(SCARA_FEEDRATE_SCALING) + #if HAS_FEEDRATE_SCALING // SCARA needs to scale the feed rate from mm/s to degrees/s - const float inv_segment_length = 1.0 / (MM_PER_ARC_SEGMENT), + const float inv_segment_length = 1.0f / (MM_PER_ARC_SEGMENT), inverse_secs = inv_segment_length * fr_mm_s; float oldA = planner.position_float[A_AXIS], - oldB = planner.position_float[B_AXIS]; + oldB = planner.position_float[B_AXIS] + #if ENABLED(DELTA_FEEDRATE_SCALING) + , oldC = planner.position_float[C_AXIS] + #endif + ; #endif #if N_ARC_CORRECTION > 1 @@ -13645,14 +13809,23 @@ void prepare_move_to_destination() { clamp_to_software_endstops(raw); + #if HAS_FEEDRATE_SCALING + inverse_kinematics(raw); + ADJUST_DELTA(raw); + #endif + #if ENABLED(SCARA_FEEDRATE_SCALING) // For SCARA scale the feed rate from mm/s to degrees/s // i.e., Complete the angular vector in the given time. - inverse_kinematics(raw); - ADJUST_DELTA(raw); if (!planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], raw[Z_AXIS], raw[E_AXIS], HYPOT(delta[A_AXIS] - oldA, delta[B_AXIS] - oldB) * inverse_secs, active_extruder)) break; oldA = delta[A_AXIS]; oldB = delta[B_AXIS]; + #elif ENABLED(DELTA_FEEDRATE_SCALING) + // For DELTA scale the feed rate from Effector mm/s to Carriage mm/s + // i.e., Complete the linear vector in the given time. + if (!planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], raw[E_AXIS], SQRT(sq(delta[A_AXIS] - oldA) + sq(delta[B_AXIS] - oldB) + sq(delta[C_AXIS] - oldC)) * inverse_secs, active_extruder)) + break; + oldA = delta[A_AXIS]; oldB = delta[B_AXIS]; oldC = delta[C_AXIS]; #elif HAS_UBL_AND_CURVES float pos[XYZ] = { raw[X_AXIS], raw[Y_AXIS], raw[Z_AXIS] }; planner.apply_leveling(pos); @@ -13665,12 +13838,19 @@ void prepare_move_to_destination() { } // Ensure last segment arrives at target location. - #if ENABLED(SCARA_FEEDRATE_SCALING) + #if HAS_FEEDRATE_SCALING inverse_kinematics(cart); ADJUST_DELTA(cart); + #endif + + #if ENABLED(SCARA_FEEDRATE_SCALING) const float diff2 = HYPOT2(delta[A_AXIS] - oldA, delta[B_AXIS] - oldB); if (diff2) planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], cart[Z_AXIS], cart[E_AXIS], SQRT(diff2) * inverse_secs, active_extruder); + #elif ENABLED(DELTA_FEEDRATE_SCALING) + const float diff2 = sq(delta[A_AXIS] - oldA) + sq(delta[B_AXIS] - oldB) + sq(delta[C_AXIS] - oldC); + if (diff2) + planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], cart[E_AXIS], SQRT(diff2) * inverse_secs, active_extruder); #elif HAS_UBL_AND_CURVES float pos[XYZ] = { cart[X_AXIS], cart[Y_AXIS], cart[Z_AXIS] }; planner.apply_leveling(pos); @@ -14005,8 +14185,16 @@ void manage_inactivity(const bool ignore_stepper_queue/*=false*/) { && !planner.has_blocks_queued() ) { #if ENABLED(SWITCHING_EXTRUDER) - const bool oldstatus = E0_ENABLE_READ; - enable_E0(); + bool oldstatus; + switch (active_extruder) { + default: oldstatus = E0_ENABLE_READ; enable_E0(); break; + #if E_STEPPERS > 1 + case 2: case 3: oldstatus = E1_ENABLE_READ; enable_E1(); break; + #if E_STEPPERS > 2 + case 4: oldstatus = E2_ENABLE_READ; enable_E2(); break; + #endif // E_STEPPERS > 2 + #endif // E_STEPPERS > 1 + } #else // !SWITCHING_EXTRUDER bool oldstatus; switch (active_extruder) { @@ -14031,11 +14219,19 @@ void manage_inactivity(const bool ignore_stepper_queue/*=false*/) { planner.buffer_line_kinematic(current_position, MMM_TO_MMS(EXTRUDER_RUNOUT_SPEED), active_extruder); current_position[E_AXIS] = olde; planner.set_e_position_mm(olde); - planner.synchronize(); + #if ENABLED(SWITCHING_EXTRUDER) - E0_ENABLE_WRITE(oldstatus); - #else + switch (active_extruder) { + default: oldstatus = E0_ENABLE_WRITE(oldstatus); break; + #if E_STEPPERS > 1 + case 2: case 3: oldstatus = E1_ENABLE_WRITE(oldstatus); break; + #if E_STEPPERS > 2 + case 4: oldstatus = E2_ENABLE_WRITE(oldstatus); break; + #endif // E_STEPPERS > 2 + #endif // E_STEPPERS > 1 + } + #else // !SWITCHING_EXTRUDER switch (active_extruder) { case 0: E0_ENABLE_WRITE(oldstatus); break; #if E_STEPPERS > 1 @@ -14088,7 +14284,7 @@ void idle( ) { #if ENABLED(MAX7219_DEBUG) Max7219_idle_tasks(); - #endif // MAX7219_DEBUG + #endif lcd_update(); @@ -14438,7 +14634,7 @@ void setup() { #endif #if ENABLED(POWER_LOSS_RECOVERY) - do_print_job_recovery(); + check_print_job_recovery(); #endif #if ENABLED(USE_WATCHDOG) @@ -14479,6 +14675,9 @@ void loop() { for (uint8_t i = 0; i < FAN_COUNT; i++) fanSpeeds[i] = 0; #endif wait_for_heatup = false; + #if ENABLED(POWER_LOSS_RECOVERY) + card.removeJobRecoveryFile(); + #endif } #endif diff --git a/Marlin/Max7219_Debug_LEDs.cpp b/Marlin/Max7219_Debug_LEDs.cpp index ad2cf3effc..eba1ffcd27 100644 --- a/Marlin/Max7219_Debug_LEDs.cpp +++ b/Marlin/Max7219_Debug_LEDs.cpp @@ -22,11 +22,9 @@ /** * This module is off by default, but can be enabled to facilitate the display of - * extra debug information during code development. It assumes the existence of a - * Max7219 LED Matrix. A suitable device can be obtained on eBay similar to this: - * http://www.ebay.com/itm/191781645249 for under $2.00 including shipping. + * extra debug information during code development. * - * Just connect up +5v and GND to give it power, then connect up the pins assigned + * Just connect up 5V and GND to give it power, then connect up the pins assigned * in Configuration_adv.h. For example, on the Re-ARM you could use: * * #define MAX7219_CLK_PIN 77 @@ -35,26 +33,14 @@ * * Max7219_init() is called automatically at startup, and then there are a number of * support functions available to control the LEDs in the 8x8 grid. - * - * void Max7219_init(); - * void Max7219_PutByte(uint8_t data); - * void Max7219(uint8_t reg, uint8_t data); - * void Max7219_LED_On(uint8_t col, uint8_t row); - * void Max7219_LED_Off(uint8_t col, uint8_t row); - * void Max7219_LED_Toggle(uint8_t col, uint8_t row); - * void Max7219_Clear_Row(uint8_t row); - * void Max7219_Clear_Column(uint8_t col); - * void Max7219_Set_Row(uint8_t row, uint8_t val); - * void Max7219_Set_2_Rows(uint8_t row, uint16_t val); - * void Max7219_Set_4_Rows(uint8_t row, uint32_t val); - * void Max7219_Set_Column(uint8_t col, uint8_t val); - * void Max7219_idle_tasks(); */ #include "MarlinConfig.h" #if ENABLED(MAX7219_DEBUG) +#define MAX7219_ERRORS // Disable to save 406 bytes of Program Memory + #include "Max7219_Debug_LEDs.h" #include "planner.h" @@ -64,11 +50,43 @@ static uint8_t LEDs[8] = { 0 }; +#ifndef MAX7219_ROTATE + #define MAX7219_ROTATE 0 +#endif +#define _ROT ((MAX7219_ROTATE + 360) % 360) +#if _ROT == 0 + #define _ROW_ y + #define _COL_ x + #define XOR_7219(x, y) LEDs[y] ^= _BV(7 - x) + #define BIT_7219(x, y) TEST(LEDs[y], 7 - x) + #define SEND_7219(R,V) Max7219(max7219_reg_digit0 + R, V) +#elif _ROT == 90 + #define _ROW_ x + #define _COL_ y + #define XOR_7219(x, y) LEDs[x] ^= _BV(y) + #define BIT_7219(x, y) TEST(LEDs[x], y) + #define SEND_7219(R,V) Max7219(max7219_reg_digit0 + R, V) +#elif _ROT == 180 + #define _ROW_ y + #define _COL_ x + #define XOR_7219(x, y) LEDs[y] ^= _BV(x) + #define BIT_7219(x, y) TEST(LEDs[y], x) + #define SEND_7219(R,V) Max7219(max7219_reg_digit7 - R, V) +#elif _ROT == 270 + #define _ROW_ x + #define _COL_ y + #define XOR_7219(x, y) LEDs[x] ^= _BV(7 - y) + #define BIT_7219(x, y) TEST(LEDs[x], 7 - y) + #define SEND_7219(R,V) Max7219(max7219_reg_digit7 - R, V) +#else + #error "MAX7219_ROTATE must be a multiple of +/- 90°." +#endif + // Delay for 0.1875µs (16MHz AVR) or 0.15µs (20MHz AVR) #define SIG_DELAY() DELAY_NS(188) void Max7219_PutByte(uint8_t data) { - CRITICAL_SECTION_START + CRITICAL_SECTION_START; for (uint8_t i = 8; i--;) { SIG_DELAY(); WRITE(MAX7219_CLK_PIN, LOW); // tick @@ -79,12 +97,12 @@ void Max7219_PutByte(uint8_t data) { SIG_DELAY(); data <<= 1; } - CRITICAL_SECTION_END + CRITICAL_SECTION_END; } void Max7219(const uint8_t reg, const uint8_t data) { SIG_DELAY(); - CRITICAL_SECTION_START + CRITICAL_SECTION_START; WRITE(MAX7219_LOAD_PIN, LOW); // begin SIG_DELAY(); Max7219_PutByte(reg); // specify register @@ -94,126 +112,170 @@ void Max7219(const uint8_t reg, const uint8_t data) { WRITE(MAX7219_LOAD_PIN, LOW); // and tell the chip to load the data SIG_DELAY(); WRITE(MAX7219_LOAD_PIN, HIGH); - CRITICAL_SECTION_END + CRITICAL_SECTION_END; SIG_DELAY(); } -void Max7219_LED_Set(const uint8_t col, const uint8_t row, const bool on) { - if (row > 7 || col > 7) { - SERIAL_ECHOPAIR("??? Max7219_LED_Set(", (int)row); - SERIAL_ECHOPAIR(",", (int)col); - SERIAL_ECHOLNPGM(")"); - return; +#if ENABLED(MAX7219_NUMERIC) + + // Draw an integer with optional leading zeros and optional decimal point + void Max7219_Print(const uint8_t start, int16_t value, uint8_t size, const bool leadzero=false, bool dec=false) { + constexpr uint8_t led_numeral[10] = { 0x7E, 0x60, 0x6D, 0x79, 0x63, 0x5B, 0x5F, 0x70, 0x7F, 0x7A }, + led_decimal = 0x80, led_minus = 0x01; + + bool blank = false, neg = value < 0; + if (neg) value *= -1; + while (size--) { + const bool minus = neg && blank; + if (minus) neg = false; + Max7219( + max7219_reg_digit0 + start + size, + minus ? led_minus : blank ? 0x00 : led_numeral[value % 10] | (dec ? led_decimal : 0x00) + ); + value /= 10; + if (!value && !leadzero) blank = true; + dec = false; + } } - if (TEST(LEDs[col], row) == on) return; // if LED is already on/off, leave alone - if (on) SBI(LEDs[col], row); else CBI(LEDs[col], row); - Max7219(8 - col, LEDs[col]); + + // Draw a float with a decimal point and optional digits + void Max7219_Print(const uint8_t start, const float value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false) { + if (pre_size) Max7219_Print(start, value, pre_size, leadzero, !!post_size); + if (post_size) { + const int16_t after = ABS(value) * (10 ^ post_size); + Max7219_Print(start + pre_size, after, post_size, true); + } + } + +#endif // MAX7219_NUMERIC + +inline void Max7219_Error(const char * const func, const int32_t v1, const int32_t v2=-1) { + #if ENABLED(MAX7219_ERRORS) + SERIAL_ECHOPGM("??? "); + serialprintPGM(func); + SERIAL_CHAR('('); + SERIAL_ECHO(v1); + if (v2 > 0) SERIAL_ECHOPAIR(", ", v2); + SERIAL_CHAR(')'); + SERIAL_EOL(); + #else + UNUSED(func); UNUSED(v1); UNUSED(v2); + #endif } -void Max7219_LED_On(const uint8_t col, const uint8_t row) { - if (row > 7 || col > 7) { - SERIAL_ECHOPAIR("??? Max7219_LED_On(", (int)col); - SERIAL_ECHOPAIR(",", (int)row); - SERIAL_ECHOLNPGM(")"); - return; - } - Max7219_LED_Set(col, row, true); +inline uint8_t flipped(const uint8_t bits) { + uint8_t outbits = 0; + for (uint8_t b = 0; b < 8; b++) + if (bits & _BV(b)) outbits |= _BV(7 - b); + return outbits; } -void Max7219_LED_Off(const uint8_t col, const uint8_t row) { - if (row > 7 || col > 7) { - SERIAL_ECHOPAIR("??? Max7219_LED_Off(", (int)row); - SERIAL_ECHOPAIR(",", (int)col); - SERIAL_ECHOLNPGM(")"); - return; - } - Max7219_LED_Set(col, row, false); +// Modify a single LED bit and send the changed line +void Max7219_LED_Set(const uint8_t x, const uint8_t y, const bool on) { + if (x > 7 || y > 7) return Max7219_Error(PSTR("Max7219_LED_Set"), x, y); + if (BIT_7219(x, y) == on) return; + XOR_7219(x, y); + SEND_7219(_ROW_, LEDs[_ROW_]); } -void Max7219_LED_Toggle(const uint8_t col, const uint8_t row) { - if (row > 7 || col > 7) { - SERIAL_ECHOPAIR("??? Max7219_LED_Toggle(", (int)row); - SERIAL_ECHOPAIR(",", (int)col); - SERIAL_ECHOLNPGM(")"); - return; - } - if (TEST(LEDs[row], col)) - Max7219_LED_Off(col, row); - else - Max7219_LED_On(col, row); +void Max7219_LED_On(const uint8_t x, const uint8_t y) { + if (x > 7 || y > 7) return Max7219_Error(PSTR("Max7219_LED_On"), x, y); + Max7219_LED_Set(x, y, true); } -void Max7219_Clear_Column(const uint8_t col) { - if (col > 7) { - SERIAL_ECHOPAIR("??? Max7219_Clear_Column(", (int)col); - SERIAL_ECHOLNPGM(")"); - return; - } - LEDs[col] = 0; - Max7219(8 - col, LEDs[col]); +void Max7219_LED_Off(const uint8_t x, const uint8_t y) { + if (x > 7 || y > 7) return Max7219_Error(PSTR("Max7219_LED_Off"), x, y); + Max7219_LED_Set(x, y, false); } -void Max7219_Clear_Row(const uint8_t row) { - if (row > 7) { - SERIAL_ECHOPAIR("??? Max7219_Clear_Row(", (int)row); - SERIAL_ECHOLNPGM(")"); - return; - } - for (uint8_t c = 0; c <= 7; c++) - Max7219_LED_Off(c, row); +void Max7219_LED_Toggle(const uint8_t x, const uint8_t y) { + if (x > 7 || y > 7) return Max7219_Error(PSTR("Max7219_LED_Toggle"), x, y); + Max7219_LED_Set(x, y, !BIT_7219(x, y)); } -void Max7219_Set_Row(const uint8_t row, const uint8_t val) { - if (row > 7) { - SERIAL_ECHOPAIR("??? Max7219_Set_Row(", (int)row); - SERIAL_ECHOPAIR(",", (int)val); - SERIAL_ECHOLNPGM(")"); - return; - } - for (uint8_t b = 0; b <= 7; b++) - if (TEST(val, b)) - Max7219_LED_On(7 - b, row); - else - Max7219_LED_Off(7 - b, row); +inline void _Max7219_Set_Reg(const uint8_t reg, const uint8_t val) { + LEDs[reg] = val; + SEND_7219(reg, val); } -void Max7219_Set_2_Rows(const uint8_t row, const uint16_t val) { - if (row > 6) { - SERIAL_ECHOPAIR("??? Max7219_Set_2_Rows(", (int)row); - SERIAL_ECHOPAIR(",", (int)val); - SERIAL_ECHOLNPGM(")"); - return; - } - Max7219_Set_Row(row + 1, (val >> 8) & 0xFF); - Max7219_Set_Row(row + 0, (val ) & 0xFF); +void Max7219_Set_Row(const uint8_t _ROW_, const uint8_t val) { + if (_ROW_ > 7) return Max7219_Error(PSTR("Max7219_Set_Row"), _ROW_); + #if _ROT == 90 + for (uint8_t _COL_ = 0; _COL_ <= 7; _COL_++) Max7219_LED_Set(7 - _COL_, _ROW_, TEST(val, _COL_)); + #elif _ROT == 180 + _Max7219_Set_Reg(_ROW_, flipped(val)); + #elif _ROT == 270 + for (uint8_t _COL_ = 0; _COL_ <= 7; _COL_++) Max7219_LED_Set(_COL_, _ROW_, TEST(val, _COL_)); + #else + _Max7219_Set_Reg(_ROW_, val); + #endif } -void Max7219_Set_4_Rows(const uint8_t row, const uint32_t val) { - if (row > 4) { - SERIAL_ECHOPAIR("??? Max7219_Set_4_Rows(", (int)row); - SERIAL_ECHOPAIR(",", (long)val); - SERIAL_ECHOLNPGM(")"); - return; - } - Max7219_Set_Row(row + 3, (val >> 24) & 0xFF); - Max7219_Set_Row(row + 2, (val >> 16) & 0xFF); - Max7219_Set_Row(row + 1, (val >> 8) & 0xFF); - Max7219_Set_Row(row + 0, (val ) & 0xFF); +void Max7219_Clear_Row(const uint8_t _ROW_) { + if (_ROW_ > 7) return Max7219_Error(PSTR("Max7219_Clear_Row"), _ROW_); + #if _ROT == 90 || _ROT == 270 + for (uint8_t _COL_ = 0; _COL_ <= 7; _COL_++) Max7219_LED_Off(_COL_, _ROW_); + #else + _Max7219_Set_Reg(_ROW_, 0); + #endif } -void Max7219_Set_Column(const uint8_t col, const uint8_t val) { - if (col > 7) { - SERIAL_ECHOPAIR("??? Max7219_Column(", (int)col); - SERIAL_ECHOPAIR(",", (int)val); - SERIAL_ECHOLNPGM(")"); - return; - } - LEDs[col] = val; - Max7219(8 - col, LEDs[col]); +void Max7219_Set_Column(const uint8_t _COL_, const uint8_t val) { + if (_COL_ > 7) return Max7219_Error(PSTR("Max7219_Set_Column"), _COL_); + #if _ROT == 90 + _Max7219_Set_Reg(_COL_, val); + #elif _ROT == 180 + for (uint8_t _ROW_ = 0; _ROW_ <= 7; _ROW_++) Max7219_LED_Set(_COL_, _ROW_, TEST(val, _ROW_)); + #elif _ROT == 270 + _Max7219_Set_Reg(_COL_, flipped(val)); + #else + for (uint8_t _ROW_ = 0; _ROW_ <= 7; _ROW_++) Max7219_LED_Set(_COL_, _ROW_, TEST(val, _ROW_)); + #endif +} + +void Max7219_Clear_Column(const uint8_t _COL_) { + if (_COL_ > 7) return Max7219_Error(PSTR("Max7219_Clear_Column"), _COL_); + #if _ROT == 90 || _ROT == 270 + _Max7219_Set_Reg(_COL_, 0); + #else + for (uint8_t _ROW_ = 0; _ROW_ <= 7; _ROW_++) Max7219_LED_Off(_COL_, _ROW_); + #endif +} + +void Max7219_Clear() { + for (uint8_t r = 0; r < 8; r++) _Max7219_Set_Reg(r, 0); +} + +void Max7219_Set_2_Rows(const uint8_t y, uint16_t val) { + if (y > 6) return Max7219_Error(PSTR("Max7219_Set_2_Rows"), y, val); + Max7219_Set_Row(y + 0, val & 0xFF); val >>= 8; + Max7219_Set_Row(y + 1, val & 0xFF); +} + +void Max7219_Set_4_Rows(const uint8_t y, uint32_t val) { + if (y > 4) return Max7219_Error(PSTR("Max7219_Set_4_Rows"), y, val); + Max7219_Set_Row(y + 0, val & 0xFF); val >>= 8; + Max7219_Set_Row(y + 1, val & 0xFF); val >>= 8; + Max7219_Set_Row(y + 2, val & 0xFF); val >>= 8; + Max7219_Set_Row(y + 3, val & 0xFF); +} + +void Max7219_Set_2_Columns(const uint8_t x, uint16_t val) { + if (x > 6) return Max7219_Error(PSTR("Max7219_Set_2_Columns"), x, val); + Max7219_Set_Column(x + 0, val & 0xFF); val >>= 8; + Max7219_Set_Column(x + 1, val & 0xFF); +} + +void Max7219_Set_4_Columns(const uint8_t x, uint32_t val) { + if (x > 4) return Max7219_Error(PSTR("Max7219_Set_4_Columns"), x, val); + Max7219_Set_Column(x + 0, val & 0xFF); val >>= 8; + Max7219_Set_Column(x + 1, val & 0xFF); val >>= 8; + Max7219_Set_Column(x + 2, val & 0xFF); val >>= 8; + Max7219_Set_Column(x + 3, val & 0xFF); } void Max7219_register_setup() { - //initiation of the max 7219 + // Initialize the Max7219 Max7219(max7219_reg_scanLimit, 0x07); Max7219(max7219_reg_decodeMode, 0x00); // using an led matrix (not digits) Max7219(max7219_reg_shutdown, 0x01); // not in shutdown mode @@ -222,135 +284,169 @@ void Max7219_register_setup() { // range: 0x00 to 0x0F } -void Max7219_init() { - uint8_t i, x, y; +#ifdef MAX7219_INIT_TEST +#if (MAX7219_INIT_TEST + 0) == 2 + inline void Max7219_spiral(const bool on, const uint16_t del) { + constexpr int8_t way[] = { 1, 0, 0, 1, -1, 0, 0, -1 }; + int8_t px = 0, py = 0, dir = 0; + for (uint8_t i = 64; i--;) { + Max7219_LED_Set(px, py, on); + delay(del); + const int8_t x = px + way[dir], y = py + way[dir + 1]; + if (!WITHIN(x, 0, 7) || !WITHIN(y, 0, 7) || BIT_7219(x, y) == on) dir = (dir + 2) & 0x7; + px += way[dir]; py += way[dir + 1]; + } + } + +#else + + inline void Max7219_colset(const uint8_t x, const bool on) { + for (uint8_t y = 0; y <= 7; y++) Max7219_LED_Set(x, y, on); + } + inline void Max7219_sweep(const int8_t dir, const uint16_t ms, const bool on) { + uint8_t x = dir > 0 ? 0 : 7; + for (uint8_t i = 8; i--; x += dir) { + Max7219_Set_Column(x, on ? 0xFF : 0x00); + delay(ms); + } + } + +#endif +#endif // MAX7219_INIT_TEST + +void Max7219_init() { SET_OUTPUT(MAX7219_DIN_PIN); SET_OUTPUT(MAX7219_CLK_PIN); - OUT_WRITE(MAX7219_LOAD_PIN, HIGH); delay(1); Max7219_register_setup(); - for (i = 0; i <= 7; i++) { // empty registers, turn all LEDs off + for (uint8_t i = 0; i <= 7; i++) { // Empty registers to turn all LEDs off LEDs[i] = 0x00; - Max7219(i + 1, 0); + Max7219(max7219_reg_digit0 + i, 0); } - for (x = 0; x <= 7; x++) // Do an aesthetically pleasing pattern to fully test - for (y = 0; y <= 7; y++) { // the Max7219 module and LEDs. First, turn them - Max7219_LED_On(y, x); // all on. - delay(3); - } - - for (x = 0; x <= 7; x++) // Now, turn them all off. - for (y = 0; y <= 7; y++) { - Max7219_LED_Off(y, x); - delay(3); // delay() is OK here. Max7219_init() is only called from - } // setup() and nothing is running yet. - - delay(150); - - for (x = 8; x--;) // Now, do the same thing from the opposite direction - for (y = 0; y <= 7; y++) { - Max7219_LED_On(y, x); - delay(2); - } - - for (x = 8; x--;) - for (y = 0; y <= 7; y++) { - Max7219_LED_Off(y, x); - delay(2); - } + #ifdef MAX7219_INIT_TEST + #if (MAX7219_INIT_TEST + 0) == 2 + Max7219_spiral(true, 8); + delay(150); + Max7219_spiral(false, 8); + #else + // Do an aesthetically-pleasing pattern to fully test the Max7219 module and LEDs. + // Light up and turn off columns, both forward and backward. + Max7219_sweep(1, 20, true); + Max7219_sweep(1, 20, false); + delay(150); + Max7219_sweep(-1, 20, true); + Max7219_sweep(-1, 20, false); + #endif + #endif } /** - * These are sample debug features to demonstrate the usage of the 8x8 LED Matrix for debug purposes. - * There is very little CPU burden added to the system by displaying information within the idle() - * task. - * - * But with that said, if your debugging can be facilitated by making calls into the library from - * other places in the code, feel free to do it. The CPU burden for a few calls to toggle an LED - * or clear a row is not very significant. + * This code demonstrates some simple debugging using a single 8x8 LED Matrix. If your feature could + * benefit from matrix display, add its code here. Very little processing is required, so the 7219 is + * ideal for debugging when realtime feedback is important but serial output can't be used. */ + +// Apply changes to update a marker +inline void Max7219_Mark16(const uint8_t y, const uint8_t v1, const uint8_t v2) { + Max7219_LED_Off(v1 & 0x7, y + (v1 >= 8)); + Max7219_LED_On(v2 & 0x7, y + (v2 >= 8)); +} + +// Apply changes to update a tail-to-head range +inline void Max7219_Range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh, const uint8_t nh) { + if (ot != nt) for (uint8_t n = ot & 0xF; n != (nt & 0xF) && n != (nh & 0xF); n = (n + 1) & 0xF) + Max7219_LED_Off(n & 0x7, y + (n >= 8)); + if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 0xF) + Max7219_LED_On(n & 0x7, y + (n >= 8)); +} + +// Apply changes to update a quantity +inline void Max7219_Quantity16(const uint8_t y, const uint8_t ov, const uint8_t nv) { + for (uint8_t i = MIN(nv, ov); i < MAX(nv, ov); i++) + Max7219_LED_Set(i >> 1, y + (i & 1), nv >= ov); +} + void Max7219_idle_tasks() { - #if MAX7219_DEBUG_STEPPER_HEAD || MAX7219_DEBUG_STEPPER_TAIL || MAX7219_DEBUG_STEPPER_QUEUE - CRITICAL_SECTION_START - #if MAX7219_DEBUG_STEPPER_HEAD || MAX7219_DEBUG_STEPPER_QUEUE + #define MAX7219_USE_HEAD (defined(MAX7219_DEBUG_PLANNER_HEAD) || defined(MAX7219_DEBUG_PLANNER_QUEUE)) + #define MAX7219_USE_TAIL (defined(MAX7219_DEBUG_PLANNER_TAIL) || defined(MAX7219_DEBUG_PLANNER_QUEUE)) + #if MAX7219_USE_HEAD || MAX7219_USE_TAIL + CRITICAL_SECTION_START; + #if MAX7219_USE_HEAD const uint8_t head = planner.block_buffer_head; #endif - #if MAX7219_DEBUG_STEPPER_TAIL || MAX7219_DEBUG_STEPPER_QUEUE + #if MAX7219_USE_TAIL const uint8_t tail = planner.block_buffer_tail; #endif - CRITICAL_SECTION_END + CRITICAL_SECTION_END; #endif - static uint16_t refresh_cnt = 0; // The Max7219 circuit boards available for several dollars on eBay - if (refresh_cnt++ > 50000) { // are vulnerable to electrical noise, especially with long wires - Max7219_register_setup(); // next to high current wires. If the display becomes corrupted due - Max7219_LED_Toggle(7, 0); // to electrical noise, this will fix it within a couple of seconds. + #if ENABLED(MAX7219_DEBUG_PRINTER_ALIVE) + static uint8_t refresh_cnt; // = 0 + constexpr uint16_t refresh_limit = 5; + static millis_t next_blink = 0; + const millis_t ms = millis(); + const bool do_blink = ELAPSED(ms, next_blink); + #else + static uint16_t refresh_cnt; // = 0 + constexpr bool do_blink = true; + constexpr uint16_t refresh_limit = 50000; + #endif + + // Some Max7219 units are vulnerable to electrical noise, especially + // with long wires next to high current wires. If the display becomes + // corrupted, this will fix it within a couple seconds. + if (do_blink && ++refresh_cnt >= refresh_limit) { refresh_cnt = 0; + Max7219_register_setup(); } #if ENABLED(MAX7219_DEBUG_PRINTER_ALIVE) - static millis_t next_blink = 0; - if (ELAPSED(millis(), next_blink)) { + if (do_blink) { Max7219_LED_Toggle(7, 7); - next_blink = millis() + 750; + next_blink = ms + 1000; } #endif - #ifdef MAX7219_DEBUG_STEPPER_HEAD - static int16_t last_head_cnt = 0; - if (last_head_cnt != head) { - if (last_head_cnt < 8) - Max7219_LED_Off(MAX7219_DEBUG_STEPPER_HEAD, last_head_cnt); - else - Max7219_LED_Off(MAX7219_DEBUG_STEPPER_HEAD + 1, last_head_cnt - 8); + #if defined(MAX7219_DEBUG_PLANNER_HEAD) && defined(MAX7219_DEBUG_PLANNER_TAIL) && MAX7219_DEBUG_PLANNER_HEAD == MAX7219_DEBUG_PLANNER_TAIL + static int16_t last_head_cnt = 0xF, last_tail_cnt = 0xF; + + if (last_head_cnt != head || last_tail_cnt != tail) { + Max7219_Range16(MAX7219_DEBUG_PLANNER_HEAD, last_tail_cnt, tail, last_head_cnt, head); last_head_cnt = head; - if (head < 8) - Max7219_LED_On(MAX7219_DEBUG_STEPPER_HEAD, head); - else - Max7219_LED_On(MAX7219_DEBUG_STEPPER_HEAD + 1, head - 8); - } - #endif - - #ifdef MAX7219_DEBUG_STEPPER_TAIL - static int16_t last_tail_cnt = 0; - if (last_tail_cnt != tail) { - if (last_tail_cnt < 8) - Max7219_LED_Off(MAX7219_DEBUG_STEPPER_TAIL, last_tail_cnt); - else - Max7219_LED_Off(MAX7219_DEBUG_STEPPER_TAIL + 1, last_tail_cnt - 8); - last_tail_cnt = tail; - if (tail < 8) - Max7219_LED_On(MAX7219_DEBUG_STEPPER_TAIL, tail); - else - Max7219_LED_On(MAX7219_DEBUG_STEPPER_TAIL + 1, tail - 8); } + + #else + + #ifdef MAX7219_DEBUG_PLANNER_HEAD + static int16_t last_head_cnt = 0x1; + if (last_head_cnt != head) { + Max7219_Mark16(MAX7219_DEBUG_PLANNER_HEAD, last_head_cnt, head); + last_head_cnt = head; + } + #endif + + #ifdef MAX7219_DEBUG_PLANNER_TAIL + static int16_t last_tail_cnt = 0x1; + if (last_tail_cnt != tail) { + Max7219_Mark16(MAX7219_DEBUG_PLANNER_TAIL, last_tail_cnt, tail); + last_tail_cnt = tail; + } + #endif + #endif - #ifdef MAX7219_DEBUG_STEPPER_QUEUE + #ifdef MAX7219_DEBUG_PLANNER_QUEUE static int16_t last_depth = 0; - int16_t current_depth = head - tail; - if (current_depth != last_depth) { // usually, no update will be needed. - if (current_depth < 0) current_depth += BLOCK_BUFFER_SIZE; - NOMORE(current_depth, BLOCK_BUFFER_SIZE); - NOMORE(current_depth, 16); // if the BLOCK_BUFFER_SIZE is greater than 16, two lines - // of LEDs is enough to see if the buffer is draining - - const uint8_t st = MIN(current_depth, last_depth), - en = MAX(current_depth, last_depth); - if (current_depth < last_depth) - for (uint8_t i = st; i <= en; i++) // clear the highest order LEDs - Max7219_LED_Off(MAX7219_DEBUG_STEPPER_QUEUE + (i & 1), i / 2); - else - for (uint8_t i = st; i <= en; i++) // set the LEDs to current depth - Max7219_LED_On(MAX7219_DEBUG_STEPPER_QUEUE + (i & 1), i / 2); - + const int16_t current_depth = (head - tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1) & 0xF; + if (current_depth != last_depth) { + Max7219_Quantity16(MAX7219_DEBUG_PLANNER_QUEUE, last_depth, current_depth); last_depth = current_depth; } #endif diff --git a/Marlin/Max7219_Debug_LEDs.h b/Marlin/Max7219_Debug_LEDs.h index 3beccb0ea8..f00f231749 100644 --- a/Marlin/Max7219_Debug_LEDs.h +++ b/Marlin/Max7219_Debug_LEDs.h @@ -22,11 +22,9 @@ /** * This module is off by default, but can be enabled to facilitate the display of - * extra debug information during code development. It assumes the existence of a - * Max7219 LED Matrix. A suitable device can be obtained on eBay similar to this: - * http://www.ebay.com/itm/191781645249 for under $2.00 including shipping. + * extra debug information during code development. * - * Just connect up +5v and GND to give it power, then connect up the pins assigned + * Just connect up 5V and GND to give it power, then connect up the pins assigned * in Configuration_adv.h. For example, on the Re-ARM you could use: * * #define MAX7219_CLK_PIN 77 @@ -35,28 +33,13 @@ * * Max7219_init() is called automatically at startup, and then there are a number of * support functions available to control the LEDs in the 8x8 grid. - * - * void Max7219_init(); - * void Max7219_PutByte(uint8_t data); - * void Max7219(uint8_t reg, uint8_t data); - * void Max7219_LED_Set(uint8_t row, uint8_t col, bool on); - * void Max7219_LED_On(uint8_t col, uint8_t row); - * void Max7219_LED_Off(uint8_t col, uint8_t row); - * void Max7219_LED_Toggle(uint8_t row, uint8_t col); - * void Max7219_Clear_Row(uint8_t row); - * void Max7219_Clear_Column(uint8_t col); - * void Max7219_Set_Row(uint8_t row, uint8_t val); - * void Max7219_Set_2_Rows(uint8_t row, uint16_t val); - * void Max7219_Set_4_Rows(uint8_t row, uint32_t val); - * void Max7219_Set_Column(uint8_t col, uint8_t val); - * void Max7219_idle_tasks(); */ #ifndef __MAX7219_DEBUG_LEDS_H__ #define __MAX7219_DEBUG_LEDS_H__ // -// define max7219 registers +// MAX7219 registers // #define max7219_reg_noop 0x00 #define max7219_reg_digit0 0x01 @@ -68,23 +51,36 @@ #define max7219_reg_digit6 0x07 #define max7219_reg_digit7 0x08 -#define max7219_reg_intensity 0x0A -#define max7219_reg_displayTest 0x0F #define max7219_reg_decodeMode 0x09 +#define max7219_reg_intensity 0x0A #define max7219_reg_scanLimit 0x0B #define max7219_reg_shutdown 0x0C +#define max7219_reg_displayTest 0x0F void Max7219_init(); void Max7219_PutByte(uint8_t data); + +// Set a single register (e.g., a whole native row) void Max7219(const uint8_t reg, const uint8_t data); -void Max7219_LED_Set(const uint8_t row, const uint8_t col, const bool on); -void Max7219_LED_On(const uint8_t row, const uint8_t col); -void Max7219_LED_Off(const uint8_t row, const uint8_t col); -void Max7219_LED_Toggle(const uint8_t row, const uint8_t col); -void Max7219_Clear_Row(const uint8_t row); -void Max7219_Clear_Column(const uint8_t col); -void Max7219_Set_Row(const uint8_t row, const uint8_t val); + +// Set a single LED by XY coordinate +void Max7219_LED_Set(const uint8_t x, const uint8_t y, const bool on); +void Max7219_LED_On(const uint8_t x, const uint8_t y); +void Max7219_LED_Off(const uint8_t x, const uint8_t y); +void Max7219_LED_Toggle(const uint8_t x, const uint8_t y); + +// Set all 8 LEDs in a single column void Max7219_Set_Column(const uint8_t col, const uint8_t val); +void Max7219_Clear_Column(const uint8_t col); + +// Set all 8 LEDs in a single row +void Max7219_Set_Row(const uint8_t row, const uint8_t val); +void Max7219_Clear_Row(const uint8_t row); + +// Quickly clear the whole matrix +void Max7219_Clear(); + +// Apply custom code to update the matrix void Max7219_idle_tasks(); #endif // __MAX7219_DEBUG_LEDS_H__ diff --git a/Marlin/SanityCheck.h b/Marlin/SanityCheck.h index bf6d449cb7..1b9832ebc6 100644 --- a/Marlin/SanityCheck.h +++ b/Marlin/SanityCheck.h @@ -54,7 +54,9 @@ /** * Warnings for old configurations */ -#if !defined(X_BED_SIZE) || !defined(Y_BED_SIZE) +#ifndef MOTHERBOARD + #error "MOTHERBOARD is required. Please update your configuration." +#elif !defined(X_BED_SIZE) || !defined(Y_BED_SIZE) #error "X_BED_SIZE and Y_BED_SIZE are now required! Please update your configuration." #elif WATCH_TEMP_PERIOD > 500 #error "WATCH_TEMP_PERIOD now uses seconds instead of milliseconds." @@ -273,6 +275,20 @@ #error "FILAMENT_CHANGE_LOAD_LENGTH is now FILAMENT_CHANGE_FAST_LOAD_LENGTH. Please update your configuration." #elif ENABLED(LEVEL_BED_CORNERS) && !defined(LEVEL_CORNERS_INSET) #error "LEVEL_BED_CORNERS requires a LEVEL_CORNERS_INSET value. Please update your Configuration.h." +#elif defined(BEZIER_JERK_CONTROL) + #error "BEZIER_JERK_CONTROL is now S_CURVE_ACCELERATION. Please update your configuration." +#elif defined(JUNCTION_DEVIATION_FACTOR) + #error "JUNCTION_DEVIATION_FACTOR is now JUNCTION_DEVIATION_MM. Please update your configuration." +#elif defined(JUNCTION_ACCELERATION_FACTOR) + #error "JUNCTION_ACCELERATION_FACTOR is obsolete. Delete it from Configuration_adv.h." +#elif defined(JUNCTION_ACCELERATION) + #error "JUNCTION_ACCELERATION is obsolete. Delete it from Configuration_adv.h." +#elif defined(MAX7219_DEBUG_STEPPER_HEAD) + #error "MAX7219_DEBUG_STEPPER_HEAD is now MAX7219_DEBUG_PLANNER_HEAD. Please update your configuration." +#elif defined(MAX7219_DEBUG_STEPPER_TAIL) + #error "MAX7219_DEBUG_STEPPER_TAIL is now MAX7219_DEBUG_PLANNER_TAIL. Please update your configuration." +#elif defined(MAX7219_DEBUG_STEPPER_QUEUE) + #error "MAX7219_DEBUG_STEPPER_QUEUE is now MAX7219_DEBUG_PLANNER_QUEUE. Please update your configuration." #endif #define BOARD_MKS_13 -47 @@ -920,15 +936,11 @@ static_assert(X_MAX_LENGTH >= X_BED_SIZE && Y_MAX_LENGTH >= Y_BED_SIZE, */ #if ENABLED(Z_SAFE_HOMING) #if HAS_BED_PROBE - static_assert(WITHIN(Z_SAFE_HOMING_X_POINT, MIN_PROBE_X, MAX_PROBE_X), - "Z_SAFE_HOMING_X_POINT is outside the probe region."); - static_assert(WITHIN(Z_SAFE_HOMING_Y_POINT, MIN_PROBE_Y, MAX_PROBE_Y), - "Z_SAFE_HOMING_Y_POINT is outside the probe region."); + static_assert(WITHIN(Z_SAFE_HOMING_X_POINT, MIN_PROBE_X, MAX_PROBE_X), "Z_SAFE_HOMING_X_POINT is outside the probe region."); + static_assert(WITHIN(Z_SAFE_HOMING_Y_POINT, MIN_PROBE_Y, MAX_PROBE_Y), "Z_SAFE_HOMING_Y_POINT is outside the probe region."); #else - static_assert(WITHIN(Z_SAFE_HOMING_X_POINT, X_MIN_POS, X_MAX_POS), - "Z_SAFE_HOMING_X_POINT can't be reached by the nozzle."); - static_assert(WITHIN(Z_SAFE_HOMING_Y_POINT, Y_MIN_POS, Y_MAX_POS), - "Z_SAFE_HOMING_Y_POINT can't be reached by the nozzle."); + static_assert(WITHIN(Z_SAFE_HOMING_X_POINT, X_MIN_POS, X_MAX_POS), "Z_SAFE_HOMING_X_POINT can't be reached by the nozzle."); + static_assert(WITHIN(Z_SAFE_HOMING_Y_POINT, Y_MIN_POS, Y_MAX_POS), "Z_SAFE_HOMING_Y_POINT can't be reached by the nozzle."); #endif #endif // Z_SAFE_HOMING @@ -1482,6 +1494,8 @@ static_assert(X_MAX_LENGTH >= X_BED_SIZE && Y_MAX_LENGTH >= Y_BED_SIZE, #error "SENSORLESS_HOMING requires Z_MIN_ENDSTOP_INVERTING and ENDSTOPPULLUP_ZMIN when homing to Z_MIN." #elif Z_SENSORLESS && Z_HOME_DIR == 1 && (DISABLED(Z_MAX_ENDSTOP_INVERTING) || DISABLED(ENDSTOPPULLUP_ZMAX)) #error "SENSORLESS_HOMING requires Z_MAX_ENDSTOP_INVERTING and ENDSTOPPULLUP_ZMAX when homing to Z_MAX." + #elif ENABLED(ENDSTOP_NOISE_FILTER) + #error "SENSORLESS_HOMING is incompatible with ENDSTOP_NOISE_FILTER." #endif #endif diff --git a/Marlin/Version.h b/Marlin/Version.h index 6ba8222395..47ad81d421 100644 --- a/Marlin/Version.h +++ b/Marlin/Version.h @@ -35,7 +35,7 @@ /** * Marlin release version identifier */ - #define SHORT_BUILD_VERSION "1.1.8-R6" + #define SHORT_BUILD_VERSION "1.1.8-R7" /** * Verbose version identifier which should contain a reference to the location @@ -48,7 +48,7 @@ * here we define this default string as the date where the latest release * version was tagged. */ - #define STRING_DISTRIBUTION_DATE "2018-05-26" + #define STRING_DISTRIBUTION_DATE "2018-07-07" /** * Required minimum Configuration.h and Configuration_adv.h file versions. diff --git a/Marlin/boards.h b/Marlin/boards.h index a7aceff081..18912cedab 100644 --- a/Marlin/boards.h +++ b/Marlin/boards.h @@ -156,6 +156,6 @@ #define BOARD_TEENSY2 84 // Teensy++2.0 (AT90USB1286) - CLI compile: HARDWARE_MOTHERBOARD=84 make #define BOARD_5DPRINT 88 // 5DPrint D8 Driver Board -#define MB(board) (MOTHERBOARD==BOARD_##board) +#define MB(board) (defined(BOARD_##board) && MOTHERBOARD==BOARD_##board) #endif // __BOARDS_H diff --git a/Marlin/cardreader.cpp b/Marlin/cardreader.cpp index 72eaa26940..5c4ff5e7d0 100644 --- a/Marlin/cardreader.cpp +++ b/Marlin/cardreader.cpp @@ -98,8 +98,8 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m createFilename(dosFilename, p); // Allocate enough stack space for the full path to a folder, trailing slash, and nul - bool prepend_is_empty = (prepend[0] == '\0'); - int len = (prepend_is_empty ? 1 : strlen(prepend)) + strlen(dosFilename) + 1 + 1; + const bool prepend_is_empty = (!prepend || prepend[0] == '\0'); + const int len = (prepend_is_empty ? 1 : strlen(prepend)) + strlen(dosFilename) + 1 + 1; char path[len]; // Append the FOLDERNAME12/ to the passed string. @@ -501,9 +501,13 @@ void CardReader::checkautostart() { if (!cardOK) initsd(); - if (cardOK) { + if (cardOK + #if ENABLED(POWER_LOSS_RECOVERY) + && !jobRecoverFileExists() // Don't run auto#.g when a resume file exists + #endif + ) { char autoname[10]; - sprintf_P(autoname, PSTR("auto%i.g"), autostart_index); + sprintf_P(autoname, PSTR("auto%i.g"), int(autostart_index)); dir_t p; root.rewind(); while (root.readDir(&p, NULL) > 0) { @@ -582,9 +586,8 @@ const char* CardReader::diveToFile(SdFile*& curDir, const char * const path, con while (dirname_start) { char * const dirname_end = strchr(dirname_start, '/'); if (dirname_end <= dirname_start) break; - - char dosSubdirname[FILENAME_LENGTH]; const uint8_t len = dirname_end - dirname_start; + char dosSubdirname[len + 1]; strncpy(dosSubdirname, dirname_start, len); dosSubdirname[len] = 0; @@ -898,11 +901,7 @@ void CardReader::printingHasFinished() { sdprinting = false; #if ENABLED(POWER_LOSS_RECOVERY) - openJobRecoveryFile(false); - job_recovery_info.valid_head = job_recovery_info.valid_foot = 0; - (void)saveJobRecoveryInfo(); - closeJobRecoveryFile(); - job_recovery_commands_count = 0; + removeJobRecoveryFile(); #endif #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND) @@ -948,20 +947,24 @@ void CardReader::printingHasFinished() { SERIAL_PROTOCOLCHAR('.'); SERIAL_EOL(); } - else + else if (!read) SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, job_recovery_file_name); } void CardReader::closeJobRecoveryFile() { jobRecoveryFile.close(); } bool CardReader::jobRecoverFileExists() { - return jobRecoveryFile.open(&root, job_recovery_file_name, O_READ); + const bool exists = jobRecoveryFile.open(&root, job_recovery_file_name, O_READ); + if (exists) jobRecoveryFile.close(); + return exists; } int16_t CardReader::saveJobRecoveryInfo() { jobRecoveryFile.seekSet(0); const int16_t ret = jobRecoveryFile.write(&job_recovery_info, sizeof(job_recovery_info)); - if (ret == -1) SERIAL_PROTOCOLLNPGM("Power-loss file write failed."); + #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) + if (ret == -1) SERIAL_PROTOCOLLNPGM("Power-loss file write failed."); + #endif return ret; } @@ -970,10 +973,15 @@ void CardReader::printingHasFinished() { } void CardReader::removeJobRecoveryFile() { - if (jobRecoveryFile.remove(&root, job_recovery_file_name)) - SERIAL_PROTOCOLLNPGM("Power-loss file deleted."); - else - SERIAL_PROTOCOLLNPGM("Power-loss file delete failed."); + job_recovery_info.valid_head = job_recovery_info.valid_foot = job_recovery_commands_count = 0; + if (jobRecoverFileExists()) { + closefile(); + removeFile(job_recovery_file_name); + #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) + SERIAL_PROTOCOLPGM("Power-loss file delete"); + serialprintPGM(jobRecoverFileExists() ? PSTR(" failed.\n") : PSTR("d.\n")); + #endif + } } #endif // POWER_LOSS_RECOVERY diff --git a/Marlin/cardreader.h b/Marlin/cardreader.h index dcdc2c48da..15275c0729 100644 --- a/Marlin/cardreader.h +++ b/Marlin/cardreader.h @@ -117,7 +117,7 @@ public: public: bool saving, logging, sdprinting, cardOK, filenameIsDir; char filename[FILENAME_LENGTH], longFilename[LONG_FILENAME_LENGTH]; - int autostart_index; + int8_t autostart_index; private: SdFile root, workDir, workDirParents[MAX_DIR_DEPTH]; uint8_t workDirDepth; diff --git a/Marlin/configuration_store.cpp b/Marlin/configuration_store.cpp index 66926643b1..57d9de0e92 100644 --- a/Marlin/configuration_store.cpp +++ b/Marlin/configuration_store.cpp @@ -37,7 +37,7 @@ */ // Change EEPROM version if the structure changes -#define EEPROM_VERSION "V54" +#define EEPROM_VERSION "V55" #define EEPROM_OFFSET 100 // Check the integrity of data offsets. @@ -97,16 +97,17 @@ typedef struct SettingsDataStruct { // uint8_t esteppers; // XYZE_N - XYZ + uint32_t planner_max_acceleration_mm_per_s2[XYZE_N], // M201 XYZE planner.max_acceleration_mm_per_s2[XYZE_N] + planner_min_segment_time_us; // M205 B planner.min_segment_time_us float planner_axis_steps_per_mm[XYZE_N], // M92 XYZE planner.axis_steps_per_mm[XYZE_N] - planner_max_feedrate_mm_s[XYZE_N]; // M203 XYZE planner.max_feedrate_mm_s[XYZE_N] - uint32_t planner_max_acceleration_mm_per_s2[XYZE_N]; // M201 XYZE planner.max_acceleration_mm_per_s2[XYZE_N] - float planner_acceleration, // M204 P planner.acceleration + planner_max_feedrate_mm_s[XYZE_N], // M203 XYZE planner.max_feedrate_mm_s[XYZE_N] + planner_acceleration, // M204 P planner.acceleration planner_retract_acceleration, // M204 R planner.retract_acceleration planner_travel_acceleration, // M204 T planner.travel_acceleration planner_min_feedrate_mm_s, // M205 S planner.min_feedrate_mm_s - planner_min_travel_feedrate_mm_s; // M205 T planner.min_travel_feedrate_mm_s - uint32_t planner_min_segment_time_us; // M205 B planner.min_segment_time_us - float planner_max_jerk[XYZE]; // M205 XYZE planner.max_jerk[XYZE] + planner_min_travel_feedrate_mm_s, // M205 T planner.min_travel_feedrate_mm_s + planner_max_jerk[XYZE], // M205 XYZE planner.max_jerk[XYZE] + planner_junction_deviation_mm; // M205 J planner.junction_deviation_mm float home_offset[XYZ]; // M206 XYZ @@ -318,6 +319,10 @@ void MarlinSettings::postprocess() { fwretract.refresh_autoretract(); #endif + #if ENABLED(JUNCTION_DEVIATION) && ENABLED(LIN_ADVANCE) + planner.recalculate_max_e_jerk(); + #endif + // Refresh steps_to_mm with the reciprocal of axis_steps_per_mm // and init stepper.count[], planner.position[] with current_position planner.refresh_positioning(); @@ -397,7 +402,7 @@ void MarlinSettings::postprocess() { * M500 - Store Configuration */ bool MarlinSettings::save() { - float dummy = 0.0f; + float dummy = 0; char ver[4] = "ERR"; uint16_t working_crc = 0; @@ -416,17 +421,25 @@ void MarlinSettings::postprocess() { const uint8_t esteppers = COUNT(planner.axis_steps_per_mm) - XYZ; EEPROM_WRITE(esteppers); + EEPROM_WRITE(planner.max_acceleration_mm_per_s2); + EEPROM_WRITE(planner.min_segment_time_us); EEPROM_WRITE(planner.axis_steps_per_mm); EEPROM_WRITE(planner.max_feedrate_mm_s); - EEPROM_WRITE(planner.max_acceleration_mm_per_s2); - EEPROM_WRITE(planner.acceleration); EEPROM_WRITE(planner.retract_acceleration); EEPROM_WRITE(planner.travel_acceleration); EEPROM_WRITE(planner.min_feedrate_mm_s); EEPROM_WRITE(planner.min_travel_feedrate_mm_s); - EEPROM_WRITE(planner.min_segment_time_us); - EEPROM_WRITE(planner.max_jerk); + + #if ENABLED(JUNCTION_DEVIATION) + const float planner_max_jerk[] = { float(DEFAULT_XJERK), float(DEFAULT_YJERK), float(DEFAULT_ZJERK), float(DEFAULT_EJERK) }; + EEPROM_WRITE(planner_max_jerk); + EEPROM_WRITE(planner.junction_deviation_mm); + #else + EEPROM_WRITE(planner.max_jerk); + dummy = 0.02f; + EEPROM_WRITE(dummy); + #endif _FIELD_TEST(home_offset); @@ -468,7 +481,7 @@ void MarlinSettings::postprocess() { EEPROM_WRITE(mesh_num_y); EEPROM_WRITE(mbl.z_values); #else // For disabled MBL write a default mesh - dummy = 0.0f; + dummy = 0; const uint8_t mesh_num_x = 3, mesh_num_y = 3; EEPROM_WRITE(dummy); // z_offset EEPROM_WRITE(mesh_num_x); @@ -490,7 +503,7 @@ void MarlinSettings::postprocess() { #if ABL_PLANAR EEPROM_WRITE(planner.bed_level_matrix); #else - dummy = 0.0; + dummy = 0; for (uint8_t q = 9; q--;) EEPROM_WRITE(dummy); #endif @@ -514,7 +527,7 @@ void MarlinSettings::postprocess() { // For disabled Bilinear Grid write an empty 3x3 grid const uint8_t grid_max_x = 3, grid_max_y = 3; const int bilinear_start[2] = { 0 }, bilinear_grid_spacing[2] = { 0 }; - dummy = 0.0f; + dummy = 0; EEPROM_WRITE(grid_max_x); EEPROM_WRITE(grid_max_y); EEPROM_WRITE(bilinear_grid_spacing); @@ -552,7 +565,7 @@ void MarlinSettings::postprocess() { _FIELD_TEST(x_endstop_adj); // Write dual endstops in X, Y, Z order. Unused = 0.0 - dummy = 0.0f; + dummy = 0; #if ENABLED(X_DUAL_ENDSTOPS) EEPROM_WRITE(endstops.x_endstop_adj); // 1 float #else @@ -604,7 +617,7 @@ void MarlinSettings::postprocess() { { dummy = DUMMY_PID_VALUE; // When read, will not change the existing value EEPROM_WRITE(dummy); // Kp - dummy = 0.0f; + dummy = 0; for (uint8_t q = 3; q--;) EEPROM_WRITE(dummy); // Ki, Kd, Kc } @@ -850,7 +863,7 @@ void MarlinSettings::postprocess() { #if ENABLED(LIN_ADVANCE) EEPROM_WRITE(planner.extruder_advance_K); #else - dummy = 0.0f; + dummy = 0; EEPROM_WRITE(dummy); #endif @@ -872,7 +885,7 @@ void MarlinSettings::postprocess() { #if ENABLED(CNC_COORDINATE_SYSTEMS) EEPROM_WRITE(coordinate_system); // 27 floats #else - dummy = 0.0f; + dummy = 0; for (uint8_t q = MAX_COORDINATE_SYSTEMS * XYZ; q--;) EEPROM_WRITE(dummy); #endif @@ -887,7 +900,7 @@ void MarlinSettings::postprocess() { EEPROM_WRITE(planner.xz_skew_factor); EEPROM_WRITE(planner.yz_skew_factor); #else - dummy = 0.0f; + dummy = 0; for (uint8_t q = 3; q--;) EEPROM_WRITE(dummy); #endif @@ -907,7 +920,7 @@ void MarlinSettings::postprocess() { EEPROM_WRITE(dummy); } #else - dummy = 0.0f; + dummy = 0; for (uint8_t q = MAX_EXTRUDERS * 2; q--;) EEPROM_WRITE(dummy); #endif @@ -994,17 +1007,20 @@ void MarlinSettings::postprocess() { // Get only the number of E stepper parameters previously stored // Any steppers added later are set to their defaults - const float def1[] = DEFAULT_AXIS_STEPS_PER_UNIT, def2[] = DEFAULT_MAX_FEEDRATE; - const uint32_t def3[] = DEFAULT_MAX_ACCELERATION; - float tmp1[XYZ + esteppers], tmp2[XYZ + esteppers]; - uint32_t tmp3[XYZ + esteppers]; - EEPROM_READ(tmp1); - EEPROM_READ(tmp2); - EEPROM_READ(tmp3); + const uint32_t def1[] = DEFAULT_MAX_ACCELERATION; + const float def2[] = DEFAULT_AXIS_STEPS_PER_UNIT, def3[] = DEFAULT_MAX_FEEDRATE; + + uint32_t tmp1[XYZ + esteppers]; + EEPROM_READ(tmp1); // max_acceleration_mm_per_s2 + EEPROM_READ(planner.min_segment_time_us); + + float tmp2[XYZ + esteppers], tmp3[XYZ + esteppers]; + EEPROM_READ(tmp2); // axis_steps_per_mm + EEPROM_READ(tmp3); // max_feedrate_mm_s if (!validating) LOOP_XYZE_N(i) { - planner.axis_steps_per_mm[i] = i < XYZ + esteppers ? tmp1[i] : def1[i < COUNT(def1) ? i : COUNT(def1) - 1]; - planner.max_feedrate_mm_s[i] = i < XYZ + esteppers ? tmp2[i] : def2[i < COUNT(def2) ? i : COUNT(def2) - 1]; - planner.max_acceleration_mm_per_s2[i] = i < XYZ + esteppers ? tmp3[i] : def3[i < COUNT(def3) ? i : COUNT(def3) - 1]; + planner.max_acceleration_mm_per_s2[i] = i < XYZ + esteppers ? tmp1[i] : def1[i < COUNT(def1) ? i : COUNT(def1) - 1]; + planner.axis_steps_per_mm[i] = i < XYZ + esteppers ? tmp2[i] : def2[i < COUNT(def2) ? i : COUNT(def2) - 1]; + planner.max_feedrate_mm_s[i] = i < XYZ + esteppers ? tmp3[i] : def3[i < COUNT(def3) ? i : COUNT(def3) - 1]; } EEPROM_READ(planner.acceleration); @@ -1012,8 +1028,14 @@ void MarlinSettings::postprocess() { EEPROM_READ(planner.travel_acceleration); EEPROM_READ(planner.min_feedrate_mm_s); EEPROM_READ(planner.min_travel_feedrate_mm_s); - EEPROM_READ(planner.min_segment_time_us); - EEPROM_READ(planner.max_jerk); + + #if ENABLED(JUNCTION_DEVIATION) + for (uint8_t q = 4; q--;) EEPROM_READ(dummy); + EEPROM_READ(planner.junction_deviation_mm); + #else + EEPROM_READ(planner.max_jerk); + EEPROM_READ(dummy); + #endif // // Home Offset (M206) @@ -1596,7 +1618,7 @@ void MarlinSettings::postprocess() { } #endif - int16_t MarlinSettings::meshes_start_index() { + uint16_t MarlinSettings::meshes_start_index() { return (datasize() + EEPROM_OFFSET + 32) & 0xFFF8; // Pad the end of configuration data so it can float up // or down a little bit without disrupting the mesh data } @@ -1699,16 +1721,21 @@ void MarlinSettings::reset() { planner.max_acceleration_mm_per_s2[i] = pgm_read_dword_near(&tmp3[i < COUNT(tmp3) ? i : COUNT(tmp3) - 1]); } + planner.min_segment_time_us = DEFAULT_MINSEGMENTTIME; planner.acceleration = DEFAULT_ACCELERATION; planner.retract_acceleration = DEFAULT_RETRACT_ACCELERATION; planner.travel_acceleration = DEFAULT_TRAVEL_ACCELERATION; planner.min_feedrate_mm_s = DEFAULT_MINIMUMFEEDRATE; planner.min_travel_feedrate_mm_s = DEFAULT_MINTRAVELFEEDRATE; - planner.min_segment_time_us = DEFAULT_MINSEGMENTTIME; - planner.max_jerk[X_AXIS] = DEFAULT_XJERK; - planner.max_jerk[Y_AXIS] = DEFAULT_YJERK; - planner.max_jerk[Z_AXIS] = DEFAULT_ZJERK; - planner.max_jerk[E_AXIS] = DEFAULT_EJERK; + + #if ENABLED(JUNCTION_DEVIATION) + planner.junction_deviation_mm = float(JUNCTION_DEVIATION_MM); + #else + planner.max_jerk[X_AXIS] = DEFAULT_XJERK; + planner.max_jerk[Y_AXIS] = DEFAULT_YJERK; + planner.max_jerk[Z_AXIS] = DEFAULT_ZJERK; + planner.max_jerk[E_AXIS] = DEFAULT_EJERK; + #endif #if HAS_HOME_OFFSET ZERO(home_offset); @@ -1804,7 +1831,7 @@ void MarlinSettings::reset() { HOTEND_LOOP() #endif { - PID_PARAM(Kp, e) = DEFAULT_Kp; + PID_PARAM(Kp, e) = float(DEFAULT_Kp); PID_PARAM(Ki, e) = scalePID_i(DEFAULT_Ki); PID_PARAM(Kd, e) = scalePID_d(DEFAULT_Kd); #if ENABLED(PID_EXTRUSION_SCALING) @@ -1873,7 +1900,7 @@ void MarlinSettings::reset() { #endif #if ENABLED(ADVANCED_PAUSE_FEATURE) - for (uint8_t e = 0; e < E_STEPPERS; e++) { + for (uint8_t e = 0; e < EXTRUDERS; e++) { filament_change_unload_length[e] = FILAMENT_CHANGE_UNLOAD_LENGTH; filament_change_load_length[e] = FILAMENT_CHANGE_FAST_LOAD_LENGTH; } @@ -1892,12 +1919,12 @@ void MarlinSettings::reset() { #define CONFIG_ECHO_START do{ if (!forReplay) SERIAL_ECHO_START(); }while(0) #if HAS_TRINAMIC - void say_M906() { SERIAL_ECHOPGM(" M906 "); } + void say_M906() { SERIAL_ECHOPGM(" M906"); } #if ENABLED(HYBRID_THRESHOLD) - void say_M913() { SERIAL_ECHOPGM(" M913 "); } + void say_M913() { SERIAL_ECHOPGM(" M913"); } #endif #if ENABLED(SENSORLESS_HOMING) - void say_M914() { SERIAL_ECHOPGM(" M914 "); } + void say_M914() { SERIAL_ECHOPGM(" M914"); } #endif #endif @@ -2077,16 +2104,32 @@ void MarlinSettings::reset() { if (!forReplay) { CONFIG_ECHO_START; - SERIAL_ECHOLNPGM("Advanced: S T B X Z E"); + SERIAL_ECHOPGM("Advanced: B S T"); + #if ENABLED(JUNCTION_DEVIATION) + SERIAL_ECHOPGM(" J"); + #else + SERIAL_ECHOPGM(" X Y Z"); + #endif + #if DISABLED(JUNCTION_DEVIATION) || ENABLED(LIN_ADVANCE) + SERIAL_ECHOPGM(" E"); + #endif + SERIAL_EOL(); } CONFIG_ECHO_START; - SERIAL_ECHOPAIR(" M205 S", LINEAR_UNIT(planner.min_feedrate_mm_s)); + SERIAL_ECHOPAIR(" M205 B", LINEAR_UNIT(planner.min_segment_time_us)); + SERIAL_ECHOPAIR(" S", LINEAR_UNIT(planner.min_feedrate_mm_s)); SERIAL_ECHOPAIR(" T", LINEAR_UNIT(planner.min_travel_feedrate_mm_s)); - SERIAL_ECHOPAIR(" B", planner.min_segment_time_us); - SERIAL_ECHOPAIR(" X", LINEAR_UNIT(planner.max_jerk[X_AXIS])); - SERIAL_ECHOPAIR(" Y", LINEAR_UNIT(planner.max_jerk[Y_AXIS])); - SERIAL_ECHOPAIR(" Z", LINEAR_UNIT(planner.max_jerk[Z_AXIS])); - SERIAL_ECHOLNPAIR(" E", LINEAR_UNIT(planner.max_jerk[E_AXIS])); + + #if ENABLED(JUNCTION_DEVIATION) + SERIAL_ECHOPAIR(" J", LINEAR_UNIT(planner.junction_deviation_mm)); + #else + SERIAL_ECHOPAIR(" X", LINEAR_UNIT(planner.max_jerk[X_AXIS])); + SERIAL_ECHOPAIR(" Y", LINEAR_UNIT(planner.max_jerk[Y_AXIS])); + SERIAL_ECHOPAIR(" Z", LINEAR_UNIT(planner.max_jerk[Z_AXIS])); + SERIAL_ECHOPAIR(" E", LINEAR_UNIT(planner.max_jerk[E_AXIS])); + #endif + + SERIAL_EOL(); #if HAS_M206_COMMAND if (!forReplay) { @@ -2161,7 +2204,7 @@ void MarlinSettings::reset() { SERIAL_ECHOPAIR(" G29 S3 X", (int)px + 1); SERIAL_ECHOPAIR(" Y", (int)py + 1); SERIAL_ECHOPGM(" Z"); - SERIAL_PROTOCOL_F(LINEAR_UNIT(mbl.z_values[px][py]), 5); + SERIAL_ECHO_F(LINEAR_UNIT(mbl.z_values[px][py]), 5); SERIAL_EOL(); } } @@ -2185,10 +2228,10 @@ void MarlinSettings::reset() { for (uint8_t py = 0; py < GRID_MAX_POINTS_Y; py++) { for (uint8_t px = 0; px < GRID_MAX_POINTS_X; px++) { CONFIG_ECHO_START; - SERIAL_ECHOPAIR(" G29 W I", (int)px + 1); - SERIAL_ECHOPAIR(" J", (int)py + 1); + SERIAL_ECHOPAIR(" G29 W I", (int)px); + SERIAL_ECHOPAIR(" J", (int)py); SERIAL_ECHOPGM(" Z"); - SERIAL_PROTOCOL_F(LINEAR_UNIT(z_values[px][py]), 5); + SERIAL_ECHO_F(LINEAR_UNIT(z_values[px][py]), 5); SERIAL_EOL(); } } @@ -2392,49 +2435,56 @@ void MarlinSettings::reset() { SERIAL_ECHOLNPGM("Stepper driver current:"); } CONFIG_ECHO_START; - #if X_IS_TRINAMIC + #if X_IS_TRINAMIC || Y_IS_TRINAMIC || Z_IS_TRINAMIC say_M906(); - SERIAL_ECHOLNPAIR("X", stepperX.getCurrent()); #endif - #if X2_IS_TRINAMIC - say_M906(); - SERIAL_ECHOLNPAIR("I1 X", stepperX2.getCurrent()); + #if X_IS_TRINAMIC + SERIAL_ECHOPAIR(" X", stepperX.getCurrent()); #endif #if Y_IS_TRINAMIC - say_M906(); - SERIAL_ECHOLNPAIR("Y", stepperY.getCurrent()); - #endif - #if Y2_IS_TRINAMIC - say_M906(); - SERIAL_ECHOLNPAIR("I1 Y", stepperY2.getCurrent()); + SERIAL_ECHOPAIR(" Y", stepperY.getCurrent()); #endif #if Z_IS_TRINAMIC + SERIAL_ECHOPAIR(" Z", stepperZ.getCurrent()); + #endif + #if X_IS_TRINAMIC || Y_IS_TRINAMIC || Z_IS_TRINAMIC + SERIAL_EOL(); + #endif + #if X2_IS_TRINAMIC || Y2_IS_TRINAMIC || Z2_IS_TRINAMIC say_M906(); - SERIAL_ECHOLNPAIR("Z", stepperZ.getCurrent()); + SERIAL_ECHOPGM(" I1"); + #endif + #if X2_IS_TRINAMIC + SERIAL_ECHOPAIR(" X", stepperX2.getCurrent()); + #endif + #if Y2_IS_TRINAMIC + SERIAL_ECHOPAIR(" Y", stepperY2.getCurrent()); #endif #if Z2_IS_TRINAMIC - say_M906(); - SERIAL_ECHOLNPAIR("I1 Z", stepperZ2.getCurrent()); + SERIAL_ECHOPAIR(" Z", stepperZ2.getCurrent()); + #endif + #if X2_IS_TRINAMIC || Y2_IS_TRINAMIC || Z2_IS_TRINAMIC + SERIAL_EOL(); #endif #if E0_IS_TRINAMIC say_M906(); - SERIAL_ECHOLNPAIR("T0 E", stepperE0.getCurrent()); + SERIAL_ECHOLNPAIR(" T0 E", stepperE0.getCurrent()); #endif #if E_STEPPERS > 1 && E1_IS_TRINAMIC say_M906(); - SERIAL_ECHOLNPAIR("T1 E", stepperE1.getCurrent()); + SERIAL_ECHOLNPAIR(" T1 E", stepperE1.getCurrent()); #endif #if E_STEPPERS > 2 && E2_IS_TRINAMIC say_M906(); - SERIAL_ECHOLNPAIR("T2 E", stepperE2.getCurrent()); + SERIAL_ECHOLNPAIR(" T2 E", stepperE2.getCurrent()); #endif #if E_STEPPERS > 3 && E3_IS_TRINAMIC say_M906(); - SERIAL_ECHOLNPAIR("T3 E", stepperE3.getCurrent()); + SERIAL_ECHOLNPAIR(" T3 E", stepperE3.getCurrent()); #endif #if E_STEPPERS > 4 && E4_IS_TRINAMIC say_M906(); - SERIAL_ECHOLNPAIR("T4 E", stepperE4.getCurrent()); + SERIAL_ECHOLNPAIR(" T4 E", stepperE4.getCurrent()); #endif SERIAL_EOL(); @@ -2447,49 +2497,56 @@ void MarlinSettings::reset() { SERIAL_ECHOLNPGM("Hybrid Threshold:"); } CONFIG_ECHO_START; - #if X_IS_TRINAMIC + #if X_IS_TRINAMIC || Y_IS_TRINAMIC || Z_IS_TRINAMIC say_M913(); - SERIAL_ECHOLNPAIR("X", TMC_GET_PWMTHRS(X, X)); #endif - #if X2_IS_TRINAMIC - say_M913(); - SERIAL_ECHOLNPAIR("I1 X", TMC_GET_PWMTHRS(X, X2)); + #if X_IS_TRINAMIC + SERIAL_ECHOPAIR(" X", TMC_GET_PWMTHRS(X, X)); #endif #if Y_IS_TRINAMIC - say_M913(); - SERIAL_ECHOLNPAIR("Y", TMC_GET_PWMTHRS(Y, Y)); - #endif - #if Y2_IS_TRINAMIC - say_M913(); - SERIAL_ECHOLNPAIR("I1 Y", TMC_GET_PWMTHRS(Y, Y2)); + SERIAL_ECHOPAIR(" Y", TMC_GET_PWMTHRS(Y, Y)); #endif #if Z_IS_TRINAMIC + SERIAL_ECHOPAIR(" Z", TMC_GET_PWMTHRS(Z, Z)); + #endif + #if X_IS_TRINAMIC || Y_IS_TRINAMIC || Z_IS_TRINAMIC + SERIAL_EOL(); + #endif + #if X2_IS_TRINAMIC || Y2_IS_TRINAMIC || Z2_IS_TRINAMIC say_M913(); - SERIAL_ECHOLNPAIR("Z", TMC_GET_PWMTHRS(Z, Z)); + SERIAL_ECHOPGM(" I1"); + #endif + #if X2_IS_TRINAMIC + SERIAL_ECHOPAIR(" X", TMC_GET_PWMTHRS(X, X2)); + #endif + #if Y2_IS_TRINAMIC + SERIAL_ECHOPAIR(" Y", TMC_GET_PWMTHRS(Y, Y2)); #endif #if Z2_IS_TRINAMIC - say_M913(); - SERIAL_ECHOLNPAIR("I1 Z", TMC_GET_PWMTHRS(Z, Z2)); + SERIAL_ECHOPAIR(" Z", TMC_GET_PWMTHRS(Z, Z2)); + #endif + #if X2_IS_TRINAMIC || Y2_IS_TRINAMIC || Z2_IS_TRINAMIC + SERIAL_EOL(); #endif #if E0_IS_TRINAMIC say_M913(); - SERIAL_ECHOLNPAIR("T0 E", TMC_GET_PWMTHRS(E, E0)); + SERIAL_ECHOLNPAIR(" T0 E", TMC_GET_PWMTHRS(E, E0)); #endif #if E_STEPPERS > 1 && E1_IS_TRINAMIC say_M913(); - SERIAL_ECHOLNPAIR("T1 E", TMC_GET_PWMTHRS(E, E1)); + SERIAL_ECHOLNPAIR(" T1 E", TMC_GET_PWMTHRS(E, E1)); #endif #if E_STEPPERS > 2 && E2_IS_TRINAMIC say_M913(); - SERIAL_ECHOLNPAIR("T2 E", TMC_GET_PWMTHRS(E, E2)); + SERIAL_ECHOLNPAIR(" T2 E", TMC_GET_PWMTHRS(E, E2)); #endif #if E_STEPPERS > 3 && E3_IS_TRINAMIC say_M913(); - SERIAL_ECHOLNPAIR("T3 E", TMC_GET_PWMTHRS(E, E3)); + SERIAL_ECHOLNPAIR(" T3 E", TMC_GET_PWMTHRS(E, E3)); #endif #if E_STEPPERS > 4 && E4_IS_TRINAMIC say_M913(); - SERIAL_ECHOLNPAIR("T4 E", TMC_GET_PWMTHRS(E, E4)); + SERIAL_ECHOLNPAIR(" T4 E", TMC_GET_PWMTHRS(E, E4)); #endif SERIAL_EOL(); #endif // HYBRID_THRESHOLD @@ -2503,38 +2560,42 @@ void MarlinSettings::reset() { SERIAL_ECHOLNPGM("Sensorless homing threshold:"); } CONFIG_ECHO_START; - #ifdef X_HOMING_SENSITIVITY - #if ENABLED(X_IS_TMC2130) || ENABLED(IS_TRAMS) - say_M914(); - SERIAL_ECHOLNPAIR("X", stepperX.sgt()); + #define HAS_X_SENSORLESS (defined(X_HOMING_SENSITIVITY) && (ENABLED(X_IS_TMC2130) || ENABLED(IS_TRAMS))) + #define HAS_Y_SENSORLESS (defined(Y_HOMING_SENSITIVITY) && (ENABLED(Y_IS_TMC2130) || ENABLED(IS_TRAMS))) + #define HAS_Z_SENSORLESS (defined(Z_HOMING_SENSITIVITY) && (ENABLED(Z_IS_TMC2130) || ENABLED(IS_TRAMS))) + #if HAS_X_SENSORLESS || HAS_Y_SENSORLESS || HAS_Z_SENSORLESS + say_M914(); + #if HAS_X_SENSORLESS + SERIAL_ECHOPAIR(" X", stepperX.sgt()); #endif - #if ENABLED(X2_IS_TMC2130) - say_M914(); - SERIAL_ECHOLNPAIR("I1 X", stepperX2.sgt()); + #if HAS_Y_SENSORLESS + SERIAL_ECHOPAIR(" Y", stepperY.sgt()); #endif + #if HAS_Z_SENSORLESS + SERIAL_ECHOPAIR(" Z", stepperZ.sgt()); + #endif + SERIAL_EOL(); #endif - #ifdef Y_HOMING_SENSITIVITY - #if ENABLED(Y_IS_TMC2130) || ENABLED(IS_TRAMS) - say_M914(); - SERIAL_ECHOLNPAIR("Y", stepperY.sgt()); + + #define HAS_X2_SENSORLESS (defined(X_HOMING_SENSITIVITY) && ENABLED(X2_IS_TMC2130)) + #define HAS_Y2_SENSORLESS (defined(Y_HOMING_SENSITIVITY) && ENABLED(Y2_IS_TMC2130)) + #define HAS_Z2_SENSORLESS (defined(Z_HOMING_SENSITIVITY) && ENABLED(Z2_IS_TMC2130)) + #if HAS_X2_SENSORLESS || HAS_Y2_SENSORLESS || HAS_Z2_SENSORLESS + say_M914(); + SERIAL_ECHOPGM(" I1"); + #if HAS_X2_SENSORLESS + SERIAL_ECHOPAIR(" X", stepperX2.sgt()); #endif - #if ENABLED(Y2_IS_TMC2130) - say_M914(); - SERIAL_ECHOLNPAIR("I1 Y", stepperY2.sgt()); + #if HAS_Y2_SENSORLESS + SERIAL_ECHOPAIR(" Y", stepperY2.sgt()); #endif + #if HAS_Z2_SENSORLESS + SERIAL_ECHOPAIR(" Z", stepperZ2.sgt()); + #endif + SERIAL_EOL(); #endif - #ifdef Z_HOMING_SENSITIVITY - #if ENABLED(Z_IS_TMC2130) || ENABLED(IS_TRAMS) - say_M914(); - SERIAL_ECHOLNPAIR("Z", stepperZ.sgt()); - #endif - #if ENABLED(Z2_IS_TMC2130) - say_M914(); - SERIAL_ECHOLNPAIR("I1 Z", stepperZ2.sgt()); - #endif - #endif - SERIAL_EOL(); - #endif + + #endif // SENSORLESS_HOMING #endif // HAS_TRINAMIC diff --git a/Marlin/configuration_store.h b/Marlin/configuration_store.h index 2bba57930e..f7b50e0920 100644 --- a/Marlin/configuration_store.h +++ b/Marlin/configuration_store.h @@ -53,8 +53,8 @@ class MarlinSettings { #if ENABLED(AUTO_BED_LEVELING_UBL) // Eventually make these available if any leveling system // That can store is enabled - static int16_t meshes_start_index(); - FORCE_INLINE static int16_t meshes_end_index() { return meshes_end; } + static uint16_t meshes_start_index(); + FORCE_INLINE static uint16_t meshes_end_index() { return meshes_end; } static uint16_t calc_num_meshes(); static int mesh_slot_offset(const int8_t slot); static void store_mesh(const int8_t slot); @@ -84,8 +84,8 @@ class MarlinSettings { #if ENABLED(AUTO_BED_LEVELING_UBL) // Eventually make these available if any leveling system // That can store is enabled - static constexpr int16_t meshes_end = E2END - 128; // 128 is a placeholder for the size of the MAT; the MAT will always - // live at the very end of the eeprom + static constexpr uint16_t meshes_end = E2END - 128; // 128 is a placeholder for the size of the MAT; the MAT will always + // live at the very end of the eeprom #endif diff --git a/Marlin/digipot_mcp4018.cpp b/Marlin/digipot_mcp4018.cpp index e29e929e93..5871fdb99c 100644 --- a/Marlin/digipot_mcp4018.cpp +++ b/Marlin/digipot_mcp4018.cpp @@ -89,7 +89,7 @@ static void i2c_send(const uint8_t channel, const byte v) { // This is for the MCP4018 I2C based digipot void digipot_i2c_set_current(uint8_t channel, float current) { - i2c_send(channel, current_to_wiper(MIN(MAX(current, 0.0f), float(DIGIPOT_A4988_MAX_CURRENT)))); + i2c_send(channel, current_to_wiper(MIN(MAX(current, 0), float(DIGIPOT_A4988_MAX_CURRENT)))); } void digipot_i2c_init() { diff --git a/Marlin/digipot_mcp4451.cpp b/Marlin/digipot_mcp4451.cpp index 8e372220cb..fed84b2645 100644 --- a/Marlin/digipot_mcp4451.cpp +++ b/Marlin/digipot_mcp4451.cpp @@ -50,7 +50,7 @@ static void i2c_send(const byte addr, const byte a, const byte b) { // This is for the MCP4451 I2C based digipot void digipot_i2c_set_current(uint8_t channel, float current) { - current = MIN((float) MAX(current, 0.0f), DIGIPOT_I2C_MAX_CURRENT); + current = MIN((float) MAX(current, 0), DIGIPOT_I2C_MAX_CURRENT); // these addresses are specific to Azteeg X3 Pro, can be set to others, // In this case first digipot is at address A0=0, A1= 0, second one is at A0=0, A1= 1 byte addr = 0x2C; // channel 0-3 diff --git a/Marlin/endstop_interrupts.h b/Marlin/endstop_interrupts.h index 65f0d1a5bd..62c2ea8532 100644 --- a/Marlin/endstop_interrupts.h +++ b/Marlin/endstop_interrupts.h @@ -41,7 +41,7 @@ #include "macros.h" // One ISR for all EXT-Interrupts -void endstop_ISR(void) { endstops.check_possible_change(); } +void endstop_ISR(void) { endstops.update(); } /** * Patch for pins_arduino.h (...\Arduino\hardware\arduino\avr\variants\mega\pins_arduino.h) diff --git a/Marlin/endstops.cpp b/Marlin/endstops.cpp index f122b27bbc..3cffff1832 100644 --- a/Marlin/endstops.cpp +++ b/Marlin/endstops.cpp @@ -35,12 +35,6 @@ #include "endstop_interrupts.h" #endif -#if HAS_BED_PROBE - #define ENDSTOPS_ENABLED (enabled || z_probe_enabled) -#else - #define ENDSTOPS_ENABLED enabled -#endif - Endstops endstops; // public: @@ -49,9 +43,9 @@ bool Endstops::enabled, Endstops::enabled_globally; // Initialized by settings.l volatile uint8_t Endstops::hit_state; Endstops::esbits_t Endstops::live_state = 0; + #if ENABLED(ENDSTOP_NOISE_FILTER) - Endstops::esbits_t Endstops::old_live_state, - Endstops::validated_live_state; + Endstops::esbits_t Endstops::validated_live_state; uint8_t Endstops::endstop_poll_count; #endif @@ -195,9 +189,6 @@ void Endstops::init() { } // Endstops::init -// Called from ISR. A change was detected. Find out what happened! -void Endstops::check_possible_change() { if (ENDSTOPS_ENABLED) update(); } - // Called from ISR: Poll endstop state if required void Endstops::poll() { @@ -205,8 +196,10 @@ void Endstops::poll() { run_monitor(); // report changes in endstop status #endif - #if DISABLED(ENDSTOP_INTERRUPTS_FEATURE) || ENABLED(ENDSTOP_NOISE_FILTER) - if (ENDSTOPS_ENABLED) update(); + #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE) && ENABLED(ENDSTOP_NOISE_FILTER) + if (endstop_poll_count) update(); + #elif DISABLED(ENDSTOP_INTERRUPTS_FEATURE) || ENABLED(ENDSTOP_NOISE_FILTER) + update(); #endif } @@ -214,7 +207,7 @@ void Endstops::enable_globally(const bool onoff) { enabled_globally = enabled = onoff; #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE) - if (onoff) update(); // If enabling, update state now + update(); #endif } @@ -223,7 +216,7 @@ void Endstops::enable(const bool onoff) { enabled = onoff; #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE) - if (onoff) update(); // If enabling, update state now + update(); #endif } @@ -232,17 +225,23 @@ void Endstops::not_homing() { enabled = enabled_globally; #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE) - if (enabled) update(); // If enabling, update state now + update(); #endif } +// If the last move failed to trigger an endstop, call kill +void Endstops::validate_homing_move() { + if (!trigger_state()) kill(PSTR(MSG_ERR_HOMING_FAILED)); + hit_on_purpose(); +} + // Enable / disable endstop z-probe checking #if HAS_BED_PROBE - void Endstops::enable_z_probe(bool onoff) { + void Endstops::enable_z_probe(const bool onoff) { z_probe_enabled = onoff; #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE) - if (enabled) update(); // If enabling, update state now + update(); #endif } #endif @@ -369,10 +368,12 @@ void Endstops::M119() { // Check endstops - Could be called from ISR! void Endstops::update() { - // UPDATE_ENDSTOP_BIT: set the current endstop bits for an endstop to its status + #if DISABLED(ENDSTOP_NOISE_FILTER) + if (!abort_enabled()) return; + #endif + #define UPDATE_ENDSTOP_BIT(AXIS, MINMAX) SET_BIT_TO(live_state, _ENDSTOP(AXIS, MINMAX), (READ(_ENDSTOP_PIN(AXIS, MINMAX)) != _ENDSTOP_INVERTING(AXIS, MINMAX))) - // COPY_BIT: copy the value of SRC_BIT to DST_BIT in DST - #define COPY_BIT(DST, SRC_BIT, DST_BIT) SET_BIT_TO(DST, DST_BIT, TEST(DST, SRC_BIT)) + #define COPY_LIVE_STATE(SRC_BIT, DST_BIT) SET_BIT_TO(live_state, DST_BIT, TEST(live_state, SRC_BIT)) #if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && !(CORE_IS_XY || CORE_IS_XZ) // If G38 command is active check Z_MIN_PROBE for ALL movement @@ -407,162 +408,142 @@ void Endstops::update() { #endif /** - * Check and update endstops according to conditions + * Check and update endstops */ - if (stepper.axis_is_moving(X_AXIS)) { - if (stepper.motor_direction(X_AXIS_HEAD)) { // -direction - #if HAS_X_MIN - #if ENABLED(X_DUAL_ENDSTOPS) - UPDATE_ENDSTOP_BIT(X, MIN); - #if HAS_X2_MIN - UPDATE_ENDSTOP_BIT(X2, MIN); - #else - COPY_BIT(live_state, X_MIN, X2_MIN); - #endif - #else - if (X_MIN_TEST) UPDATE_ENDSTOP_BIT(X, MIN); - #endif + #if HAS_X_MIN + #if ENABLED(X_DUAL_ENDSTOPS) + UPDATE_ENDSTOP_BIT(X, MIN); + #if HAS_X2_MIN + UPDATE_ENDSTOP_BIT(X2, MIN); + #else + COPY_LIVE_STATE(X_MIN, X2_MIN); #endif - } - else { // +direction - #if HAS_X_MAX - #if ENABLED(X_DUAL_ENDSTOPS) - UPDATE_ENDSTOP_BIT(X, MAX); - #if HAS_X2_MAX - UPDATE_ENDSTOP_BIT(X2, MAX); - #else - COPY_BIT(live_state, X_MAX, X2_MAX); - #endif - #else - if (X_MAX_TEST) UPDATE_ENDSTOP_BIT(X, MAX); - #endif - #endif - } - } + #else + UPDATE_ENDSTOP_BIT(X, MIN); + #endif + #endif - if (stepper.axis_is_moving(Y_AXIS)) { - if (stepper.motor_direction(Y_AXIS_HEAD)) { // -direction - #if HAS_Y_MIN - #if ENABLED(Y_DUAL_ENDSTOPS) - UPDATE_ENDSTOP_BIT(Y, MIN); - #if HAS_Y2_MIN - UPDATE_ENDSTOP_BIT(Y2, MIN); - #else - COPY_BIT(live_state, Y_MIN, Y2_MIN); - #endif - #else - UPDATE_ENDSTOP_BIT(Y, MIN); - #endif + #if HAS_X_MAX + #if ENABLED(X_DUAL_ENDSTOPS) + UPDATE_ENDSTOP_BIT(X, MAX); + #if HAS_X2_MAX + UPDATE_ENDSTOP_BIT(X2, MAX); + #else + COPY_LIVE_STATE(X_MAX, X2_MAX); #endif - } - else { // +direction - #if HAS_Y_MAX - #if ENABLED(Y_DUAL_ENDSTOPS) - UPDATE_ENDSTOP_BIT(Y, MAX); - #if HAS_Y2_MAX - UPDATE_ENDSTOP_BIT(Y2, MAX); - #else - COPY_BIT(live_state, Y_MAX, Y2_MAX); - #endif - #else - UPDATE_ENDSTOP_BIT(Y, MAX); - #endif - #endif - } - } + #else + UPDATE_ENDSTOP_BIT(X, MAX); + #endif + #endif - if (stepper.axis_is_moving(Z_AXIS)) { - if (stepper.motor_direction(Z_AXIS_HEAD)) { // Z -direction. Gantry down, bed up. - #if HAS_Z_MIN - #if ENABLED(Z_DUAL_ENDSTOPS) - UPDATE_ENDSTOP_BIT(Z, MIN); - #if HAS_Z2_MIN - UPDATE_ENDSTOP_BIT(Z2, MIN); - #else - COPY_BIT(live_state, Z_MIN, Z2_MIN); - #endif - #else - #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN) - if (z_probe_enabled) UPDATE_ENDSTOP_BIT(Z, MIN); - #else - UPDATE_ENDSTOP_BIT(Z, MIN); - #endif - #endif + #if HAS_Y_MIN + #if ENABLED(Y_DUAL_ENDSTOPS) + UPDATE_ENDSTOP_BIT(Y, MIN); + #if HAS_Y2_MIN + UPDATE_ENDSTOP_BIT(Y2, MIN); + #else + COPY_LIVE_STATE(Y_MIN, Y2_MIN); #endif + #else + UPDATE_ENDSTOP_BIT(Y, MIN); + #endif + #endif - // When closing the gap check the enabled probe - #if ENABLED(Z_MIN_PROBE_ENDSTOP) - if (z_probe_enabled) UPDATE_ENDSTOP_BIT(Z, MIN_PROBE); + #if HAS_Y_MAX + #if ENABLED(Y_DUAL_ENDSTOPS) + UPDATE_ENDSTOP_BIT(Y, MAX); + #if HAS_Y2_MAX + UPDATE_ENDSTOP_BIT(Y2, MAX); + #else + COPY_LIVE_STATE(Y_MAX, Y2_MAX); #endif - } - else { // Z +direction. Gantry up, bed down. - #if HAS_Z_MAX - // Check both Z dual endstops - #if ENABLED(Z_DUAL_ENDSTOPS) - UPDATE_ENDSTOP_BIT(Z, MAX); - #if HAS_Z2_MAX - UPDATE_ENDSTOP_BIT(Z2, MAX); - #else - COPY_BIT(live_state, Z_MAX, Z2_MAX); - #endif - // If this pin is not hijacked for the bed probe - // then it belongs to the Z endstop - #elif DISABLED(Z_MIN_PROBE_ENDSTOP) || Z_MAX_PIN != Z_MIN_PROBE_PIN - UPDATE_ENDSTOP_BIT(Z, MAX); - #endif - #endif - } - } + #else + UPDATE_ENDSTOP_BIT(Y, MAX); + #endif + #endif + + #if HAS_Z_MIN + #if ENABLED(Z_DUAL_ENDSTOPS) + UPDATE_ENDSTOP_BIT(Z, MIN); + #if HAS_Z2_MIN + UPDATE_ENDSTOP_BIT(Z2, MIN); + #else + COPY_LIVE_STATE(Z_MIN, Z2_MIN); + #endif + #elif ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN) + UPDATE_ENDSTOP_BIT(Z, MIN); + #elif Z_HOME_DIR < 0 + UPDATE_ENDSTOP_BIT(Z, MIN); + #endif + #endif + + // When closing the gap check the enabled probe + #if ENABLED(Z_MIN_PROBE_ENDSTOP) + UPDATE_ENDSTOP_BIT(Z, MIN_PROBE); + #endif + + #if HAS_Z_MAX + // Check both Z dual endstops + #if ENABLED(Z_DUAL_ENDSTOPS) + UPDATE_ENDSTOP_BIT(Z, MAX); + #if HAS_Z2_MAX + UPDATE_ENDSTOP_BIT(Z2, MAX); + #else + COPY_LIVE_STATE(Z_MAX, Z2_MAX); + #endif + #elif DISABLED(Z_MIN_PROBE_ENDSTOP) || Z_MAX_PIN != Z_MIN_PROBE_PIN + // If this pin isn't the bed probe it's the Z endstop + UPDATE_ENDSTOP_BIT(Z, MAX); + #endif + #endif - // All endstops were updated. #if ENABLED(ENDSTOP_NOISE_FILTER) - if (old_live_state != live_state) { // We detected a change. Reinit the timeout - /** - * Filtering out noise on endstops requires a delayed decision. Let's assume, due to noise, - * that 50% of endstop signal samples are good and 50% are bad (assuming normal distribution - * of random noise). Then the first sample has a 50% chance to be good or bad. The 2nd sample - * also has a 50% chance to be good or bad. The chances of 2 samples both being bad becomes - * 50% of 50%, or 25%. That was the previous implementation of Marlin endstop handling. It - * reduces chances of bad readings in half, at the cost of 1 extra sample period, but chances - * still exist. The only way to reduce them further is to increase the number of samples. - * To reduce the chance to 1% (1/128th) requires 7 samples (adding 7ms of delay). - */ + /** + * Filtering out noise on endstops requires a delayed decision. Let's assume, due to noise, + * that 50% of endstop signal samples are good and 50% are bad (assuming normal distribution + * of random noise). Then the first sample has a 50% chance to be good or bad. The 2nd sample + * also has a 50% chance to be good or bad. The chances of 2 samples both being bad becomes + * 50% of 50%, or 25%. That was the previous implementation of Marlin endstop handling. It + * reduces chances of bad readings in half, at the cost of 1 extra sample period, but chances + * still exist. The only way to reduce them further is to increase the number of samples. + * To reduce the chance to 1% (1/128th) requires 7 samples (adding 7ms of delay). + */ + static esbits_t old_live_state; + if (old_live_state != live_state) { endstop_poll_count = 7; old_live_state = live_state; } else if (endstop_poll_count && !--endstop_poll_count) validated_live_state = live_state; - #else - - // Lets accept the new endstop values as valid - We assume hardware filtering of lines - esbits_t validated_live_state = live_state; + if (!abort_enabled()) return; #endif - // Endstop readings are validated in validated_live_state - // Test the current status of an endstop - #define TEST_ENDSTOP(ENDSTOP) (TEST(validated_live_state, ENDSTOP)) + #define TEST_ENDSTOP(ENDSTOP) (TEST(state(), ENDSTOP)) // Record endstop was hit #define _ENDSTOP_HIT(AXIS, MINMAX) SBI(hit_state, _ENDSTOP(AXIS, MINMAX)) // Call the endstop triggered routine for single endstops #define PROCESS_ENDSTOP(AXIS,MINMAX) do { \ - if (TEST_ENDSTOP(_ENDSTOP(AXIS, MINMAX))) { \ - _ENDSTOP_HIT(AXIS, MINMAX); \ - planner.endstop_triggered(_AXIS(AXIS)); \ - } \ - }while(0) + if (TEST_ENDSTOP(_ENDSTOP(AXIS, MINMAX))) { \ + _ENDSTOP_HIT(AXIS, MINMAX); \ + planner.endstop_triggered(_AXIS(AXIS)); \ + } \ + }while(0) - // Call the endstop triggered routine for single endstops + // Call the endstop triggered routine for dual endstops #define PROCESS_DUAL_ENDSTOP(AXIS1, AXIS2, MINMAX) do { \ - if (TEST_ENDSTOP(_ENDSTOP(AXIS1, MINMAX)) || TEST_ENDSTOP(_ENDSTOP(AXIS2, MINMAX))) { \ - _ENDSTOP_HIT(AXIS1, MINMAX); \ + const byte dual_hit = TEST_ENDSTOP(_ENDSTOP(AXIS1, MINMAX)) | (TEST_ENDSTOP(_ENDSTOP(AXIS2, MINMAX)) << 1); \ + if (dual_hit) { \ + _ENDSTOP_HIT(AXIS1, MINMAX); \ + /* if not performing home or if both endstops were trigged during homing... */ \ + if (!stepper.homing_dual_axis || dual_hit == 0x3) \ planner.endstop_triggered(_AXIS(AXIS1)); \ - } \ - }while(0) + } \ + }while(0) #if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && !(CORE_IS_XY || CORE_IS_XZ) // If G38 command is active check Z_MIN_PROBE for ALL movement @@ -627,6 +608,8 @@ void Endstops::update() { #else #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN) if (z_probe_enabled) PROCESS_ENDSTOP(Z, MIN); + #elif ENABLED(Z_MIN_PROBE_ENDSTOP) + if (!z_probe_enabled) PROCESS_ENDSTOP(Z, MIN); #else PROCESS_ENDSTOP(Z, MIN); #endif diff --git a/Marlin/endstops.h b/Marlin/endstops.h index 8f38992d7d..a6ea580d30 100644 --- a/Marlin/endstops.h +++ b/Marlin/endstops.h @@ -69,9 +69,9 @@ class Endstops { private: static esbits_t live_state; static volatile uint8_t hit_state; // Use X_MIN, Y_MIN, Z_MIN and Z_MIN_PROBE as BIT index + #if ENABLED(ENDSTOP_NOISE_FILTER) - static esbits_t old_live_state, // Old endstop value for debouncing and denoising - validated_live_state; // The validated (accepted as true) endstop bits + static esbits_t validated_live_state; static uint8_t endstop_poll_count; // Countdown from threshold for polling #endif @@ -84,10 +84,15 @@ class Endstops { static void init(); /** - * A change was detected or presumed to be in endstops pins. Find out what - * changed, if anything. Called from ISR contexts + * Are endstops or the probe set to abort the move? */ - static void check_possible_change(); + FORCE_INLINE static bool abort_enabled() { + return (enabled + #if HAS_BED_PROBE + || z_probe_enabled + #endif + ); + } /** * Periodic call to poll endstops if required. Called from temperature ISR @@ -95,7 +100,9 @@ class Endstops { static void poll(); /** - * Update the endstops bits from the pins + * Update endstops bits from the pins. Apply filtering to get a verified state. + * If abort_enabled() and moving towards a triggered switch, abort the current move. + * Called from ISR contexts. */ static void update(); @@ -107,7 +114,15 @@ class Endstops { /** * Get current endstops state */ - FORCE_INLINE static esbits_t state() { return live_state; } + FORCE_INLINE static esbits_t state() { + return + #if ENABLED(ENDSTOP_NOISE_FILTER) + validated_live_state + #else + live_state + #endif + ; + } /** * Report endstop hits to serial. Called from loop(). @@ -128,13 +143,16 @@ class Endstops { // Disable / Enable endstops based on ENSTOPS_ONLY_FOR_HOMING and global enable static void not_homing(); + // If the last move failed to trigger an endstop, call kill + static void validate_homing_move(); + // Clear endstops (i.e., they were hit intentionally) to suppress the report FORCE_INLINE static void hit_on_purpose() { hit_state = 0; } // Enable / disable endstop z-probe checking #if HAS_BED_PROBE static volatile bool z_probe_enabled; - static void enable_z_probe(bool onoff=true); + static void enable_z_probe(const bool onoff=true); #endif // Debugging of endstops diff --git a/Marlin/fwretract.cpp b/Marlin/fwretract.cpp index c77337b12d..997e49a7f9 100644 --- a/Marlin/fwretract.cpp +++ b/Marlin/fwretract.cpp @@ -153,7 +153,8 @@ void FWRetract::retract(const bool retracting else { // If a hop was done and Z hasn't changed, undo the Z hop if (hop_amount) { - destination[Z_AXIS] -= hop_amount; // Move back down by the total hop amount + current_position[Z_AXIS] += hop_amount; // Restore the actual Z position + SYNC_PLAN_POSITION_KINEMATIC(); // Unspoof the position planner feedrate_mm_s = planner.max_feedrate_mm_s[Z_AXIS]; // Z feedrate to max prepare_move_to_destination(); // Lower Z, set_current_to_destination hop_amount = 0.0; // Clear the hop amount diff --git a/Marlin/language_cz.h b/Marlin/language_cz.h index 95f3cdfba0..3002390b38 100644 --- a/Marlin/language_cz.h +++ b/Marlin/language_cz.h @@ -146,8 +146,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("UBL Postupne") #define MSG_LED_CONTROL _UxGT("LED Nastaveni") -#define MSG_LEDS_ON _UxGT("Svetla Zap") -#define MSG_LEDS_OFF _UxGT("Svetla Vyp") +#define MSG_LEDS _UxGT("Svetla") #define MSG_LED_PRESETS _UxGT("Svetla Predvolby") #define MSG_SET_LEDS_RED _UxGT("Cervena") #define MSG_SET_LEDS_ORANGE _UxGT("Oranzova") diff --git a/Marlin/language_cz_utf8.h b/Marlin/language_cz_utf8.h index 49ea9bd17e..f962d41a80 100644 --- a/Marlin/language_cz_utf8.h +++ b/Marlin/language_cz_utf8.h @@ -149,8 +149,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("UBL Postupně") #define MSG_LED_CONTROL _UxGT("LED Nastavení") -#define MSG_LEDS_ON _UxGT("Světla Zap") -#define MSG_LEDS_OFF _UxGT("Světla Vyp") +#define MSG_LEDS _UxGT("Světla") #define MSG_LED_PRESETS _UxGT("Světla Předvolby") #define MSG_SET_LEDS_RED _UxGT("Červená") #define MSG_SET_LEDS_ORANGE _UxGT("Oranžová") diff --git a/Marlin/language_de.h b/Marlin/language_de.h index e2d11f6fc9..c662ec25a2 100644 --- a/Marlin/language_de.h +++ b/Marlin/language_de.h @@ -311,8 +311,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("Schrittweises UBL") #define MSG_LED_CONTROL _UxGT("LED Kontrolle") -#define MSG_LEDS_ON _UxGT("Licht an") -#define MSG_LEDS_OFF _UxGT("Licht aus") +#define MSG_LEDS _UxGT("Licht") #define MSG_LED_PRESETS _UxGT("Licht Einstellungen") #define MSG_SET_LEDS_RED _UxGT("Rot") #define MSG_SET_LEDS_ORANGE _UxGT("Orange") diff --git a/Marlin/language_en.h b/Marlin/language_en.h index 03483d6148..1f5ad92508 100644 --- a/Marlin/language_en.h +++ b/Marlin/language_en.h @@ -371,11 +371,8 @@ #ifndef MSG_LED_CONTROL #define MSG_LED_CONTROL _UxGT("LED Control") #endif -#ifndef MSG_LEDS_ON - #define MSG_LEDS_ON _UxGT("Lights On") -#endif -#ifndef MSG_LEDS_OFF - #define MSG_LEDS_OFF _UxGT("Lights Off") +#ifndef MSG_LEDS + #define MSG_LEDS _UxGT("Lights") #endif #ifndef MSG_LED_PRESETS #define MSG_LED_PRESETS _UxGT("Light Presets") @@ -540,6 +537,9 @@ #ifndef MSG_VE_JERK #define MSG_VE_JERK _UxGT("Ve-jerk") #endif +#ifndef MSG_JUNCTION_DEVIATION + #define MSG_JUNCTION_DEVIATION _UxGT("Junction Dev") +#endif #ifndef MSG_VELOCITY #define MSG_VELOCITY _UxGT("Velocity") #endif @@ -666,6 +666,9 @@ #ifndef MSG_STOP_PRINT #define MSG_STOP_PRINT _UxGT("Stop print") #endif +#ifndef MSG_POWER_LOSS_RECOVERY + #define MSG_POWER_LOSS_RECOVERY _UxGT("Power-Loss Recovery") +#endif #ifndef MSG_CARD_MENU #define MSG_CARD_MENU _UxGT("Print from SD") #endif diff --git a/Marlin/language_eu.h b/Marlin/language_eu.h index d6e51bc250..c28eaa6f94 100644 --- a/Marlin/language_eu.h +++ b/Marlin/language_eu.h @@ -142,8 +142,7 @@ //#define MSG_UBL_Z_OFFSET_STOPPED _UxGT("Z-Offset Stopped") //#define MSG_UBL_STEP_BY_STEP_MENU _UxGT("Step-By-Step UBL") #define MSG_LED_CONTROL _UxGT("LED ezarpenak") -#define MSG_LEDS_ON _UxGT("Argiak piztu") -#define MSG_LEDS_OFF _UxGT("Argiak itzali") +#define MSG_LEDS _UxGT("Argiak") #define MSG_LED_PRESETS _UxGT("Argi aurrehautaketak") #define MSG_SET_LEDS_RED _UxGT("Gorria") #define MSG_SET_LEDS_ORANGE _UxGT("Laranja") diff --git a/Marlin/language_fr.h b/Marlin/language_fr.h index 5da9084169..20d542433a 100644 --- a/Marlin/language_fr.h +++ b/Marlin/language_fr.h @@ -144,8 +144,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("UBL Pas a pas") #define MSG_LED_CONTROL _UxGT("Controle LED") -#define MSG_LEDS_ON _UxGT("Lumiere ON") -#define MSG_LEDS_OFF _UxGT("Lumiere OFF") +#define MSG_LEDS _UxGT("Lumiere") #define MSG_LED_PRESETS _UxGT("Preregl. LED.") #define MSG_SET_LEDS_RED _UxGT("Rouge") #define MSG_SET_LEDS_ORANGE _UxGT("Orange") diff --git a/Marlin/language_fr_utf8.h b/Marlin/language_fr_utf8.h index a54fb76c23..8365242255 100644 --- a/Marlin/language_fr_utf8.h +++ b/Marlin/language_fr_utf8.h @@ -145,8 +145,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("UBL Pas à pas") #define MSG_LED_CONTROL _UxGT("Contrôle LED") -#define MSG_LEDS_ON _UxGT("Lumière ON") -#define MSG_LEDS_OFF _UxGT("Lumière OFF") +#define MSG_LEDS _UxGT("Lumière") #define MSG_LED_PRESETS _UxGT("Préregl. LED") #define MSG_SET_LEDS_RED _UxGT("Rouge") #define MSG_SET_LEDS_ORANGE _UxGT("Orange") diff --git a/Marlin/language_it.h b/Marlin/language_it.h index 1439d3e5c6..e3b83312bb 100644 --- a/Marlin/language_it.h +++ b/Marlin/language_it.h @@ -56,13 +56,13 @@ #define MSG_HOME_OFFSETS_APPLIED _UxGT("Offset applicato") #define MSG_SET_ORIGIN _UxGT("Imposta Origine") #define MSG_PREHEAT_1 _UxGT("Preriscalda PLA") -#define MSG_PREHEAT_1_N _UxGT("Prerisc.PLA ") +#define MSG_PREHEAT_1_N _UxGT("Preris.PLA ") #define MSG_PREHEAT_1_ALL MSG_PREHEAT_1_N _UxGT("Tutto") #define MSG_PREHEAT_1_END MSG_PREHEAT_1_N _UxGT("Ugello") #define MSG_PREHEAT_1_BEDONLY MSG_PREHEAT_1_N _UxGT("Piatto") #define MSG_PREHEAT_1_SETTINGS MSG_PREHEAT_1_N _UxGT("conf") #define MSG_PREHEAT_2 _UxGT("Preriscalda ABS") -#define MSG_PREHEAT_2_N _UxGT("Prerisc.ABS ") +#define MSG_PREHEAT_2_N _UxGT("Preris.ABS ") #define MSG_PREHEAT_2_ALL MSG_PREHEAT_2_N _UxGT("Tutto") #define MSG_PREHEAT_2_END MSG_PREHEAT_2_N _UxGT("Ugello") #define MSG_PREHEAT_2_BEDONLY MSG_PREHEAT_2_N _UxGT("Piatto") @@ -144,8 +144,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("UBL passo passo") #define MSG_LED_CONTROL _UxGT("Controllo LED") -#define MSG_LEDS_ON _UxGT("Luci On") -#define MSG_LEDS_OFF _UxGT("Luci Off") +#define MSG_LEDS _UxGT("Luci") #define MSG_LED_PRESETS _UxGT("Preset luci") #define MSG_SET_LEDS_RED _UxGT("Rosso") #define MSG_SET_LEDS_ORANGE _UxGT("Arancione") diff --git a/Marlin/language_pt-br.h b/Marlin/language_pt-br.h index 8793202517..4d9c64261c 100644 --- a/Marlin/language_pt-br.h +++ b/Marlin/language_pt-br.h @@ -145,8 +145,7 @@ #define MSG_UBL_Z_OFFSET_STOPPED _UxGT("Compensacao Z parou") #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("UBL passo a passo") #define MSG_LED_CONTROL _UxGT("Controle do LED") -#define MSG_LEDS_ON _UxGT("Luz Acesa") -#define MSG_LEDS_OFF _UxGT("Luz Apagada") +#define MSG_LEDS _UxGT("Luz") #define MSG_LED_PRESETS _UxGT("Configuracao da Luz") #define MSG_SET_LEDS_RED _UxGT("Luz Vermelha") #define MSG_SET_LEDS_ORANGE _UxGT("Luz Laranja") diff --git a/Marlin/language_pt-br_utf8.h b/Marlin/language_pt-br_utf8.h index 2e481e4810..b297b7b4b0 100644 --- a/Marlin/language_pt-br_utf8.h +++ b/Marlin/language_pt-br_utf8.h @@ -147,8 +147,7 @@ #define MSG_UBL_Z_OFFSET_STOPPED _UxGT("Compensação Z parou") #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("UBL passo a passo") #define MSG_LED_CONTROL _UxGT("Controle do LED") -#define MSG_LEDS_ON _UxGT("Luz Acesa") -#define MSG_LEDS_OFF _UxGT("Luz Apagada") +#define MSG_LEDS _UxGT("Luz") #define MSG_LED_PRESETS _UxGT("Configuração da Luz") #define MSG_SET_LEDS_RED _UxGT("Luz Vermelha") #define MSG_SET_LEDS_ORANGE _UxGT("Luz Laranja") diff --git a/Marlin/language_ru.h b/Marlin/language_ru.h index b7235ebb1d..0e54d6be0c 100644 --- a/Marlin/language_ru.h +++ b/Marlin/language_ru.h @@ -145,8 +145,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("Пошаговое UBL") #define MSG_LED_CONTROL _UxGT("Настройки LED") -#define MSG_LEDS_ON _UxGT("Включить подсветку") -#define MSG_LEDS_OFF _UxGT("Выключить подсветку") +#define MSG_LEDS _UxGT("Подсветку") #define MSG_LED_PRESETS _UxGT("Предустановки света") #define MSG_SET_LEDS_RED _UxGT("Красный свет") #define MSG_SET_LEDS_ORANGE _UxGT("Оранжевый свет") diff --git a/Marlin/language_sk_utf8.h b/Marlin/language_sk_utf8.h index a7ce610c56..3b43007e54 100644 --- a/Marlin/language_sk_utf8.h +++ b/Marlin/language_sk_utf8.h @@ -43,6 +43,7 @@ #define MSG_SD_INSERTED _UxGT("Karta vložená") #define MSG_SD_REMOVED _UxGT("Karta vybratá") #define MSG_LCD_ENDSTOPS _UxGT("Endstopy") // max 8 znakov +#define MSG_LCD_SOFT_ENDSTOPS _UxGT("Soft.endstopy") #define MSG_MAIN _UxGT("Hlavná ponuka") #define MSG_AUTOSTART _UxGT("Autoštart") #define MSG_DISABLE_STEPPERS _UxGT("Uvolniť motory") @@ -148,8 +149,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("UBL Postupne") #define MSG_LED_CONTROL _UxGT("Nastavenie LED") -#define MSG_LEDS_ON _UxGT("Zapnúť svetlo") -#define MSG_LEDS_OFF _UxGT("Vypnúť svetlo") +#define MSG_LEDS _UxGT("Svetlo") #define MSG_LED_PRESETS _UxGT("Prednastavené farby") #define MSG_SET_LEDS_RED _UxGT("Červená") #define MSG_SET_LEDS_ORANGE _UxGT("Oranžová") @@ -208,6 +208,7 @@ #define MSG_VC_JERK _UxGT("Vz-skok") #endif #define MSG_VE_JERK _UxGT("Ve-skok") +#define MSG_JUNCTION_DEVIATION _UxGT("Junction Dev") #define MSG_VELOCITY _UxGT("Rýchlosť") #define MSG_VMAX _UxGT("Vmax ") #define MSG_VMIN _UxGT("Vmin") @@ -255,8 +256,9 @@ #define MSG_CARD_MENU _UxGT("Tlačiť z SD") #define MSG_NO_CARD _UxGT("Žiadna SD karta") #define MSG_DWELL _UxGT("Spím...") -#define MSG_USERWAIT _UxGT("Čakám...") +#define MSG_USERWAIT _UxGT("Kliknutím pokrač.") #define MSG_PRINT_PAUSED _UxGT("Tlač pozastavená") +#define MSG_PRINTING _UxGT("Tlačím...") #define MSG_PRINT_ABORTED _UxGT("Tlač zrušená") #define MSG_NO_MOVE _UxGT("Žiadny pohyb.") #define MSG_KILLED _UxGT("PRERUŠENÉ. ") @@ -318,6 +320,7 @@ #define MSG_DELTA_SETTINGS _UxGT("Delta nastavenia") #define MSG_DELTA_AUTO_CALIBRATE _UxGT("Autokalibrácia") #define MSG_DELTA_HEIGHT_CALIBRATE _UxGT("Nast.výšku delty") +#define MSG_DELTA_Z_OFFSET_CALIBRATE _UxGT("Offset sondy Z") #define MSG_DELTA_DIAG_ROD _UxGT("Diag rameno") #define MSG_DELTA_HEIGHT _UxGT("Výška") #define MSG_DELTA_RADIUS _UxGT("Polomer") diff --git a/Marlin/language_zh_CN.h b/Marlin/language_zh_CN.h index c62bbc77b2..3a1a065d5e 100644 --- a/Marlin/language_zh_CN.h +++ b/Marlin/language_zh_CN.h @@ -141,8 +141,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("一步步UBL") // "Step-By-Step UBL" #define MSG_LED_CONTROL _UxGT("灯管控制") // "LED Control") -#define MSG_LEDS_ON _UxGT("灯亮") // "Lights On") -#define MSG_LEDS_OFF _UxGT("灯灭") // "Lights Off") +#define MSG_LEDS _UxGT("灯") // "Lights") #define MSG_LED_PRESETS _UxGT("灯预置") // "Light Presets") #define MSG_SET_LEDS_RED _UxGT("红") // "Red") #define MSG_SET_LEDS_ORANGE _UxGT("橙") // "Orange") diff --git a/Marlin/language_zh_TW.h b/Marlin/language_zh_TW.h index 4eb420f423..2a0399e358 100644 --- a/Marlin/language_zh_TW.h +++ b/Marlin/language_zh_TW.h @@ -141,8 +141,7 @@ #define MSG_UBL_STEP_BY_STEP_MENU _UxGT("一步步UBL") // "Step-By-Step UBL" #define MSG_LED_CONTROL _UxGT("灯管控制") // "LED Control") -#define MSG_LEDS_ON _UxGT("灯亮") // "Lights On") -#define MSG_LEDS_OFF _UxGT("灯灭") // "Lights Off") +#define MSG_LEDS _UxGT("灯") // "Lights") #define MSG_LED_PRESETS _UxGT("灯预置") // "Light Presets") #define MSG_SET_LEDS_RED _UxGT("红") // "Red") #define MSG_SET_LEDS_ORANGE _UxGT("橙") // "Orange") diff --git a/Marlin/macros.h b/Marlin/macros.h index 4d9865a57e..45c4334ae2 100644 --- a/Marlin/macros.h +++ b/Marlin/macros.h @@ -81,15 +81,14 @@ #define IS_POWER_OF_2(x) ((x) && !((x) & ((x) - 1))) // Macros for maths shortcuts -#ifndef M_PI - #define M_PI 3.14159265358979323846 -#endif -#define RADIANS(d) ((d)*M_PI/180.0) -#define DEGREES(r) ((r)*180.0/M_PI) +#undef M_PI +#define M_PI 3.14159265358979323846f +#define RADIANS(d) ((d)*M_PI/180.0f) +#define DEGREES(r) ((r)*180.0f/M_PI) #define HYPOT2(x,y) (sq(x)+sq(y)) -#define CIRCLE_AREA(R) (M_PI * sq(R)) -#define CIRCLE_CIRC(R) (2.0 * M_PI * (R)) +#define CIRCLE_AREA(R) (M_PI * sq(float(R))) +#define CIRCLE_CIRC(R) (2 * M_PI * (float(R))) #define SIGN(a) ((a>0)-(a<0)) #define IS_POWER_OF_2(x) ((x) && !((x) & ((x) - 1))) @@ -162,8 +161,8 @@ #define PENDING(NOW,SOON) ((long)(NOW-(SOON))<0) #define ELAPSED(NOW,SOON) (!PENDING(NOW,SOON)) -#define MMM_TO_MMS(MM_M) ((MM_M)/60.0) -#define MMS_TO_MMM(MM_S) ((MM_S)*60.0) +#define MMM_TO_MMS(MM_M) ((MM_M)/60.0f) +#define MMS_TO_MMM(MM_S) ((MM_S)*60.0f) #define NOOP do{} while(0) @@ -212,23 +211,24 @@ #define MAX4(a, b, c, d) MAX(MAX3(a, b, c), d) #define MAX5(a, b, c, d, e) MAX(MAX4(a, b, c, d), e) -#define UNEAR_ZERO(x) ((x) < 0.000001) -#define NEAR_ZERO(x) WITHIN(x, -0.000001, 0.000001) +#define UNEAR_ZERO(x) ((x) < 0.000001f) +#define NEAR_ZERO(x) WITHIN(x, -0.000001f, 0.000001f) #define NEAR(x,y) NEAR_ZERO((x)-(y)) -#define RECIPROCAL(x) (NEAR_ZERO(x) ? 0.0 : 1.0 / (x)) -#define FIXFLOAT(f) (f + (f < 0.0 ? -0.00005 : 0.00005)) +#define RECIPROCAL(x) (NEAR_ZERO(x) ? 0.0f : 1.0f / (x)) +#define FIXFLOAT(f) (f + (f < 0.0f ? -0.00005f : 0.00005f)) // // Maths macros that can be overridden by HAL // -#define ATAN2(y, x) atan2(y, x) -#define POW(x, y) pow(x, y) -#define SQRT(x) sqrt(x) -#define CEIL(x) ceil(x) -#define FLOOR(x) floor(x) -#define LROUND(x) lround(x) -#define FMOD(x, y) fmod(x, y) +#define ATAN2(y, x) atan2f(y, x) +#define POW(x, y) powf(x, y) +#define SQRT(x) sqrtf(x) +#define RSQRT(x) (1 / sqrtf(x)) +#define CEIL(x) ceilf(x) +#define FLOOR(x) floorf(x) +#define LROUND(x) lroundf(x) +#define FMOD(x, y) fmodf(x, y) #define HYPOT(x,y) SQRT(HYPOT2(x,y)) #endif //__MACROS_H diff --git a/Marlin/mesh_bed_leveling.h b/Marlin/mesh_bed_leveling.h index f3a374af59..cb95ad733f 100644 --- a/Marlin/mesh_bed_leveling.h +++ b/Marlin/mesh_bed_leveling.h @@ -72,22 +72,22 @@ public: } static int8_t cell_index_x(const float &x) { - int8_t cx = (x - (MESH_MIN_X)) * (1.0 / (MESH_X_DIST)); + int8_t cx = (x - (MESH_MIN_X)) * (1.0f / (MESH_X_DIST)); return constrain(cx, 0, (GRID_MAX_POINTS_X) - 2); } static int8_t cell_index_y(const float &y) { - int8_t cy = (y - (MESH_MIN_Y)) * (1.0 / (MESH_Y_DIST)); + int8_t cy = (y - (MESH_MIN_Y)) * (1.0f / (MESH_Y_DIST)); return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 2); } static int8_t probe_index_x(const float &x) { - int8_t px = (x - (MESH_MIN_X) + 0.5 * (MESH_X_DIST)) * (1.0 / (MESH_X_DIST)); + int8_t px = (x - (MESH_MIN_X) + 0.5f * (MESH_X_DIST)) * (1.0f / (MESH_X_DIST)); return WITHIN(px, 0, GRID_MAX_POINTS_X - 1) ? px : -1; } static int8_t probe_index_y(const float &y) { - int8_t py = (y - (MESH_MIN_Y) + 0.5 * (MESH_Y_DIST)) * (1.0 / (MESH_Y_DIST)); + int8_t py = (y - (MESH_MIN_Y) + 0.5f * (MESH_Y_DIST)) * (1.0f / (MESH_Y_DIST)); return WITHIN(py, 0, GRID_MAX_POINTS_Y - 1) ? py : -1; } diff --git a/Marlin/parser.h b/Marlin/parser.h index 4184191506..949c489cd0 100644 --- a/Marlin/parser.h +++ b/Marlin/parser.h @@ -39,6 +39,8 @@ #include "serial.h" #endif +#define strtof strtod + /** * GCode parser * @@ -194,15 +196,15 @@ public: if (c == '\0' || c == ' ') break; if (c == 'E' || c == 'e') { *e = '\0'; - const float ret = strtod(value_ptr, NULL); + const float ret = strtof(value_ptr, NULL); *e = c; return ret; } ++e; } - return strtod(value_ptr, NULL); + return strtof(value_ptr, NULL); } - return 0.0; + return 0; } // Code value as a long or ulong diff --git a/Marlin/pins_5DPRINT.h b/Marlin/pins_5DPRINT.h index 0e8b5f6019..415cc23bce 100755 --- a/Marlin/pins_5DPRINT.h +++ b/Marlin/pins_5DPRINT.h @@ -132,7 +132,9 @@ #define HEATER_0_PIN 15 // C5 #define HEATER_BED_PIN 14 // C4 -#define FAN_PIN 16 // C6 PWM3A +#ifndef FAN_PIN + #define FAN_PIN 16 // C6 PWM3A +#endif // // Misc. Functions diff --git a/Marlin/pins_ANET_10.h b/Marlin/pins_ANET_10.h index e8393c4c72..bdd277e35c 100644 --- a/Marlin/pins_ANET_10.h +++ b/Marlin/pins_ANET_10.h @@ -133,7 +133,10 @@ // #define HEATER_0_PIN 13 // (extruder) #define HEATER_BED_PIN 12 // (bed) -#define FAN_PIN 4 + +#ifndef FAN_PIN + #define FAN_PIN 4 +#endif // // Misc. Functions diff --git a/Marlin/pins_AZTEEG_X3_PRO.h b/Marlin/pins_AZTEEG_X3_PRO.h index 1010320679..fa17120651 100644 --- a/Marlin/pins_AZTEEG_X3_PRO.h +++ b/Marlin/pins_AZTEEG_X3_PRO.h @@ -24,22 +24,29 @@ * AZTEEG_X3_PRO (Arduino Mega) pin assignments */ +#ifndef __AVR_ATmega2560__ + #error "Oops! Make sure you have 'Arduino Mega 2560' selected from the 'Tools -> Boards' menu." +#endif + #if HOTENDS > 5 || E_STEPPERS > 5 #error "Azteeg X3 Pro supports up to 5 hotends / E-steppers. Comment out this line to continue." #endif -#if ENABLED(CASE_LIGHT_ENABLE) && !PIN_EXISTS(CASE_LIGHT) - #define CASE_LIGHT_PIN 44 // Define before RAMPS pins include -#endif - #define BOARD_NAME "Azteeg X3 Pro" -#include "pins_RAMPS.h" - -#ifndef __AVR_ATmega2560__ - #error "Oops! Make sure you have 'Arduino Mega 2560' selected from the 'Tools -> Boards' menu." +// +// RAMPS pins overrides +// +#if ENABLED(CASE_LIGHT_ENABLE) && !PIN_EXISTS(CASE_LIGHT) + #define CASE_LIGHT_PIN 44 #endif +#ifndef FAN_PIN + #define FAN_PIN 6 +#endif + +#include "pins_RAMPS.h" + // DIGIPOT slave addresses #define DIGIPOT_I2C_ADDRESS_A 0x2C // unshifted slave address for first DIGIPOT 0x2C (0x58 <- 0x2C << 1) #define DIGIPOT_I2C_ADDRESS_B 0x2E // unshifted slave address for second DIGIPOT 0x2E (0x5C <- 0x2E << 1) @@ -116,9 +123,6 @@ #define HEATER_6_PIN 6 #define HEATER_7_PIN 11 -#undef FAN_PIN -#define FAN_PIN 6 // Part Cooling System - #ifndef CONTROLLER_FAN_PIN #define CONTROLLER_FAN_PIN 4 // Pin used for the fan to cool motherboard (-1 to disable) #endif diff --git a/Marlin/pins_BRAINWAVE.h b/Marlin/pins_BRAINWAVE.h index 932619769b..a43c6be95c 100644 --- a/Marlin/pins_BRAINWAVE.h +++ b/Marlin/pins_BRAINWAVE.h @@ -115,7 +115,9 @@ #define HEATER_0_PIN 32 // A4 Extruder #define HEATER_BED_PIN 18 // E6 Bed -#define FAN_PIN 31 // A3 Fan +#ifndef FAN_PIN + #define FAN_PIN 31 // A3 Fan +#endif // // Misc. Functions diff --git a/Marlin/pins_BRAINWAVE_PRO.h b/Marlin/pins_BRAINWAVE_PRO.h index 872d868e10..49c8ab6b59 100644 --- a/Marlin/pins_BRAINWAVE_PRO.h +++ b/Marlin/pins_BRAINWAVE_PRO.h @@ -125,7 +125,9 @@ // #define HEATER_0_PIN 27 // B7 #define HEATER_BED_PIN 26 // B6 Bed -#define FAN_PIN 16 // C6 Fan, PWM3A +#ifndef FAN_PIN + #define FAN_PIN 16 // C6 Fan, PWM3A +#endif // // Misc. Functions diff --git a/Marlin/pins_CHEAPTRONIC.h b/Marlin/pins_CHEAPTRONIC.h index 6d1e45d870..e8f2c5f67e 100644 --- a/Marlin/pins_CHEAPTRONIC.h +++ b/Marlin/pins_CHEAPTRONIC.h @@ -69,8 +69,8 @@ // // Heaters / Fans // -#define HEATER_0_PIN 19 // EXTRUDER 1 -#define HEATER_1_PIN 23 // EXTRUDER 2 +#define HEATER_0_PIN 19 // EXTRUDER 1 +#define HEATER_1_PIN 23 // EXTRUDER 2 #define HEATER_BED_PIN 22 // diff --git a/Marlin/pins_CHEAPTRONICv2.h b/Marlin/pins_CHEAPTRONICv2.h index e6840abeb2..eea57d23ec 100644 --- a/Marlin/pins_CHEAPTRONICv2.h +++ b/Marlin/pins_CHEAPTRONICv2.h @@ -31,6 +31,7 @@ #endif #define BOARD_NAME "Cheaptronic v2.0" + // // Limit Switches // @@ -80,10 +81,32 @@ // // Heaters / Fans // -#define HEATER_0_PIN 6 -#define HEATER_1_PIN 7 -#define HEATER_2_PIN 8 -#define HEATER_BED_PIN 9 +#define HEATER_0_PIN 6 +#define HEATER_1_PIN 7 +#define HEATER_2_PIN 8 +#define HEATER_BED_PIN 9 +#ifndef FAN_PIN + #define FAN_PIN 3 +#endif +#define FAN2_PIN 58 // additional fan or light control output + +// +// Other board specific pins +// +#ifndef FIL_RUNOUT_PIN + #define FIL_RUNOUT_PIN 37 // board input labeled as F-DET +#endif +#define Z_MIN_PROBE_PIN 36 // additional external board input labeled as E-SENS (should be used for Z-probe) +#define LED_PIN 13 +#define SPINDLE_ENABLE_PIN 4 // additional PWM pin 1 at JP1 connector - should be used for laser control too +#define EXT_2 5 // additional PWM pin 2 at JP1 connector +#define EXT_3 2 // additional PWM pin 3 at JP1 connector +#define PS_ON_PIN 45 +#define KILL_PIN 46 + +#ifndef FILWIDTH_PIN + #define FILWIDTH_PIN 11 // shared with TEMP_3 analog input +#endif // // LCD @@ -105,23 +128,3 @@ #define BTN_EN1 11 #define BTN_EN2 12 #define BTN_ENC 43 - -// -// Other board specific pins -// -#ifndef FIL_RUNOUT_PIN - #define FIL_RUNOUT_PIN 37 // board input labeled as F-DET -#endif -#define Z_MIN_PROBE_PIN 36 // additional external board input labeled as E-SENS (should be used for Z-probe) -#define LED_PIN 13 -#define SPINDLE_ENABLE_PIN 4 // additional PWM pin 1 at JP1 connector - should be used for laser control too -#define EXT_2 5 // additional PWM pin 2 at JP1 connector -#define EXT_3 2 // additional PWM pin 3 at JP1 connector -#define FAN_PIN 3 -#define FAN2_PIN 58 // additional fan or light control output -#define PS_ON_PIN 45 -#define KILL_PIN 46 - -#ifndef FILWIDTH_PIN - #define FILWIDTH_PIN 11 // shared with TEMP_3 analog input -#endif diff --git a/Marlin/pins_CNCONTROLS_11.h b/Marlin/pins_CNCONTROLS_11.h index 8535288ece..0a32d04a18 100644 --- a/Marlin/pins_CNCONTROLS_11.h +++ b/Marlin/pins_CNCONTROLS_11.h @@ -65,7 +65,9 @@ #define HEATER_3_PIN 46 #define HEATER_BED_PIN 2 -//#define FAN_PIN 7 // common PWM pin for all tools +#ifndef FAN_PIN + //#define FAN_PIN 7 // common PWM pin for all tools +#endif #define ORIG_E0_AUTO_FAN_PIN 7 #define ORIG_E1_AUTO_FAN_PIN 7 diff --git a/Marlin/pins_CNCONTROLS_12.h b/Marlin/pins_CNCONTROLS_12.h index 9a849916f0..f073b19c96 100644 --- a/Marlin/pins_CNCONTROLS_12.h +++ b/Marlin/pins_CNCONTROLS_12.h @@ -65,7 +65,9 @@ #define HEATER_3_PIN 3 #define HEATER_BED_PIN 24 -#define FAN_PIN 5 // 5 is PWMtool3 -> 7 is common PWM pin for all tools +#ifndef FAN_PIN + #define FAN_PIN 5 // 5 is PWMtool3 -> 7 is common PWM pin for all tools +#endif #define ORIG_E0_AUTO_FAN_PIN 7 #define ORIG_E1_AUTO_FAN_PIN 7 @@ -124,4 +126,4 @@ //#define UI2 37 #define STAT_LED_BLUE_PIN -1 -#define STAT_LED_RED_PIN 10 // TOOL_0_PWM_PIN +#define STAT_LED_RED_PIN 10 // TOOL_0_PWM_PIN diff --git a/Marlin/pins_EINSY_RAMBO.h b/Marlin/pins_EINSY_RAMBO.h index b86e1530ac..b3c1d1caa0 100644 --- a/Marlin/pins_EINSY_RAMBO.h +++ b/Marlin/pins_EINSY_RAMBO.h @@ -117,7 +117,9 @@ #define HEATER_0_PIN 3 #define HEATER_BED_PIN 4 -#define FAN_PIN 8 +#ifndef FAN_PIN + #define FAN_PIN 8 +#endif #define FAN1_PIN 6 // diff --git a/Marlin/pins_EINSY_RETRO.h b/Marlin/pins_EINSY_RETRO.h index 4b46427f30..df4f1c20be 100644 --- a/Marlin/pins_EINSY_RETRO.h +++ b/Marlin/pins_EINSY_RETRO.h @@ -134,7 +134,9 @@ #define HEATER_0_PIN 3 #define HEATER_BED_PIN 4 -#define FAN_PIN 8 +#ifndef FAN_PIN + #define FAN_PIN 8 +#endif #define FAN1_PIN 6 // diff --git a/Marlin/pins_ELEFU_3.h b/Marlin/pins_ELEFU_3.h index 12631a901f..79a4bce712 100644 --- a/Marlin/pins_ELEFU_3.h +++ b/Marlin/pins_ELEFU_3.h @@ -90,7 +90,9 @@ #define HEATER_2_PIN 17 // 12V PWM3 #define HEATER_BED_PIN 44 // DOUBLE 12V PWM -#define FAN_PIN 16 // 5V PWM +#ifndef FAN_PIN + #define FAN_PIN 16 // 5V PWM +#endif // // Misc. Functions diff --git a/Marlin/pins_GEN3_MONOLITHIC.h b/Marlin/pins_GEN3_MONOLITHIC.h index dae4046fd2..5d64e94290 100644 --- a/Marlin/pins_GEN3_MONOLITHIC.h +++ b/Marlin/pins_GEN3_MONOLITHIC.h @@ -68,11 +68,11 @@ // #define X_STEP_PIN 15 #define X_DIR_PIN 18 -#define X_ENABLE_PIN 24 // actually uses Y_enable_pin +#define X_ENABLE_PIN 24 // actually uses Y_enable_pin #define Y_STEP_PIN 23 #define Y_DIR_PIN 22 -#define Y_ENABLE_PIN 24 // shared with X_enable_pin +#define Y_ENABLE_PIN 24 // shared with X_enable_pin #define Z_STEP_PIN 27 #define Z_DIR_PIN 28 @@ -95,7 +95,6 @@ // // Misc. Functions // -#define PS_ON_PIN 14 // Alex, does this work on the card? +#define PS_ON_PIN 14 // Alex, does this work on the card? // Alex extras from Gen3+ - diff --git a/Marlin/pins_GEN7_12.h b/Marlin/pins_GEN7_12.h index 3bc38d7ee7..1afaae9abc 100644 --- a/Marlin/pins_GEN7_12.h +++ b/Marlin/pins_GEN7_12.h @@ -112,8 +112,8 @@ #define HEATER_0_PIN 4 #define HEATER_BED_PIN 3 -#if GEN7_VERSION < 13 // Gen7 v1.3 removed the fan pin - #define FAN_PIN 31 +#if !defined(FAN_PIN) && GEN7_VERSION < 13 // Gen7 v1.3 removed the fan pin + #define FAN_PIN 31 #endif // diff --git a/Marlin/pins_GT2560_REV_A.h b/Marlin/pins_GT2560_REV_A.h index 73b32bb40c..63f166ab32 100644 --- a/Marlin/pins_GT2560_REV_A.h +++ b/Marlin/pins_GT2560_REV_A.h @@ -81,7 +81,9 @@ #define HEATER_0_PIN 2 #define HEATER_1_PIN 3 #define HEATER_BED_PIN 4 -#define FAN_PIN 7 +#ifndef FAN_PIN + #define FAN_PIN 7 +#endif // // Misc. Functions diff --git a/Marlin/pins_LEAPFROG.h b/Marlin/pins_LEAPFROG.h index a7fffa5325..9a31520e8a 100644 --- a/Marlin/pins_LEAPFROG.h +++ b/Marlin/pins_LEAPFROG.h @@ -47,21 +47,21 @@ #define X_DIR_PIN 63 #define X_ENABLE_PIN 29 -#define Y_STEP_PIN 14 // A6 -#define Y_DIR_PIN 15 // A0 +#define Y_STEP_PIN 14 // A6 +#define Y_DIR_PIN 15 // A0 #define Y_ENABLE_PIN 39 -#define Z_STEP_PIN 31 // A2 -#define Z_DIR_PIN 32 // A6 -#define Z_ENABLE_PIN 30 // A1 +#define Z_STEP_PIN 31 // A2 +#define Z_DIR_PIN 32 // A6 +#define Z_ENABLE_PIN 30 // A1 -#define E0_STEP_PIN 34 // 34 -#define E0_DIR_PIN 35 // 35 -#define E0_ENABLE_PIN 33 // 33 +#define E0_STEP_PIN 34 // 34 +#define E0_DIR_PIN 35 // 35 +#define E0_ENABLE_PIN 33 // 33 -#define E1_STEP_PIN 37 // 37 -#define E1_DIR_PIN 40 // 40 -#define E1_ENABLE_PIN 36 // 36 +#define E1_STEP_PIN 37 // 37 +#define E1_DIR_PIN 40 // 40 +#define E1_ENABLE_PIN 36 // 36 // // Temperature Sensors @@ -74,9 +74,9 @@ // Heaters / Fans // #define HEATER_0_PIN 9 -#define HEATER_1_PIN 8 // 12 -#define HEATER_2_PIN 11 // 13 -#define HEATER_BED_PIN 10 // 14/15 +#define HEATER_1_PIN 8 // 12 +#define HEATER_2_PIN 11 // 13 +#define HEATER_BED_PIN 10 // 14/15 #define FAN_PIN 7 diff --git a/Marlin/pins_MEGACONTROLLER.h b/Marlin/pins_MEGACONTROLLER.h index b2631901be..709dc413be 100644 --- a/Marlin/pins_MEGACONTROLLER.h +++ b/Marlin/pins_MEGACONTROLLER.h @@ -62,17 +62,17 @@ // // Steppers // -#define X_STEP_PIN 62 // A8 -#define X_DIR_PIN 63 // A9 -#define X_ENABLE_PIN 61 // A7 +#define X_STEP_PIN 62 // A8 +#define X_DIR_PIN 63 // A9 +#define X_ENABLE_PIN 61 // A7 -#define Y_STEP_PIN 65 // A11 -#define Y_DIR_PIN 66 // A12 -#define Y_ENABLE_PIN 64 // A10 +#define Y_STEP_PIN 65 // A11 +#define Y_DIR_PIN 66 // A12 +#define Y_ENABLE_PIN 64 // A10 -#define Z_STEP_PIN 68 // A14 -#define Z_DIR_PIN 69 // A15 -#define Z_ENABLE_PIN 67 // A13 +#define Z_STEP_PIN 68 // A14 +#define Z_DIR_PIN 69 // A15 +#define Z_ENABLE_PIN 67 // A13 #define E0_STEP_PIN 23 #define E0_DIR_PIN 24 @@ -112,7 +112,9 @@ #define HEATER_1_PIN 34 #define HEATER_BED_PIN 28 -#define FAN_PIN 39 +#ifndef FAN_PIN + #define FAN_PIN 39 +#endif #define FAN1_PIN 35 #define FAN2_PIN 36 diff --git a/Marlin/pins_MEGATRONICS.h b/Marlin/pins_MEGATRONICS.h index acf1da0160..8b608eff88 100644 --- a/Marlin/pins_MEGATRONICS.h +++ b/Marlin/pins_MEGATRONICS.h @@ -53,13 +53,13 @@ #define X_DIR_PIN 28 #define X_ENABLE_PIN 24 -#define Y_STEP_PIN 60 // A6 -#define Y_DIR_PIN 61 // A7 +#define Y_STEP_PIN 60 // A6 +#define Y_DIR_PIN 61 // A7 #define Y_ENABLE_PIN 22 -#define Z_STEP_PIN 54 // A0 -#define Z_DIR_PIN 55 // A1 -#define Z_ENABLE_PIN 56 // A2 +#define Z_STEP_PIN 54 // A0 +#define Z_DIR_PIN 55 // A1 +#define Z_ENABLE_PIN 56 // A2 #define E0_STEP_PIN 31 #define E0_DIR_PIN 32 @@ -87,7 +87,9 @@ #define HEATER_1_PIN 8 #define HEATER_BED_PIN 10 -#define FAN_PIN 7 // IO pin. Buffer needed +#ifndef FAN_PIN + #define FAN_PIN 7 // IO pin. Buffer needed +#endif // // Misc. Functions diff --git a/Marlin/pins_MEGATRONICS_2.h b/Marlin/pins_MEGATRONICS_2.h index 2ae436674b..9bd8b4506d 100644 --- a/Marlin/pins_MEGATRONICS_2.h +++ b/Marlin/pins_MEGATRONICS_2.h @@ -101,7 +101,9 @@ #define HEATER_1_PIN 8 #define HEATER_BED_PIN 10 -#define FAN_PIN 7 +#ifndef FAN_PIN + #define FAN_PIN 7 +#endif #define FAN1_PIN 6 // diff --git a/Marlin/pins_MEGATRONICS_3.h b/Marlin/pins_MEGATRONICS_3.h index c638b27865..119c63a585 100644 --- a/Marlin/pins_MEGATRONICS_3.h +++ b/Marlin/pins_MEGATRONICS_3.h @@ -118,7 +118,9 @@ #define HEATER_2_PIN 8 #define HEATER_BED_PIN 10 -#define FAN_PIN 6 +#ifndef FAN_PIN + #define FAN_PIN 6 +#endif #define FAN1_PIN 7 // diff --git a/Marlin/pins_MIGHTYBOARD_REVE.h b/Marlin/pins_MIGHTYBOARD_REVE.h index 9a2fb539b2..989533d53e 100644 --- a/Marlin/pins_MIGHTYBOARD_REVE.h +++ b/Marlin/pins_MIGHTYBOARD_REVE.h @@ -134,14 +134,14 @@ // 2 E4 CS2 // 78 E2 SCK // -#define THERMO_SCK_PIN 78 // E2 -#define THERMO_DO_PIN 3 // E5 -#define THERMO_CS1 5 // E3 -#define THERMO_CS2 2 // E4 +#define THERMO_SCK_PIN 78 // E2 +#define THERMO_DO_PIN 3 // E5 +#define THERMO_CS1 5 // E3 +#define THERMO_CS2 2 // E4 -#define MAX6675_SS THERMO_CS1 -#define MAX6675_SCK_PIN THERMO_SCK_PIN -#define MAX6675_DO_PIN THERMO_DO_PIN +#define MAX6675_SS THERMO_CS1 +#define MAX6675_SCK_PIN THERMO_SCK_PIN +#define MAX6675_DO_PIN THERMO_DO_PIN // // Augmentation for auto-assigning plugs // @@ -149,10 +149,10 @@ // 2 extruders or 1 extruder and a heated bed. // With no heated bed, an additional 24V fan is possible. // -#define MOSFET_A_PIN 6 // H3 -#define MOSFET_B_PIN 11 // B5 - Rev A of this file had this pin assigned to 9 -#define MOSFET_C_PIN 45 // L4 -#define MOSFET_D_PIN 44 // L5 +#define MOSFET_A_PIN 6 // H3 +#define MOSFET_B_PIN 11 // B5 - Rev A of this file had this pin assigned to 9 +#define MOSFET_C_PIN 45 // L4 +#define MOSFET_D_PIN 44 // L5 #if HOTENDS > 1 #if TEMP_SENSOR_BED @@ -172,23 +172,24 @@ #define HEATER_0_PIN MOSFET_A_PIN #if ENABLED(IS_EFB) // Hotend, Fan, Bed - #define FAN_PIN MOSFET_B_PIN - #define HEATER_BED_PIN MOSFET_C_PIN + #define HEATER_BED_PIN MOSFET_C_PIN #elif ENABLED(IS_EEF) // Hotend, Hotend, Fan - #define HEATER_1_PIN MOSFET_B_PIN - #define FAN_PIN MOSFET_C_PIN + #define HEATER_1_PIN MOSFET_B_PIN #elif ENABLED(IS_EEB) // Hotend, Hotend, Bed - #define HEATER_1_PIN MOSFET_B_PIN - #define HEATER_BED_PIN MOSFET_C_PIN + #define HEATER_1_PIN MOSFET_B_PIN + #define HEATER_BED_PIN MOSFET_C_PIN #elif ENABLED(IS_EFF) // Hotend, Fan, Fan - #define FAN_PIN MOSFET_B_PIN - #define FAN1_PIN MOSFET_C_PIN -#elif ENABLED(IS_SF) // Spindle, Fan - #define FAN_PIN MOSFET_C_PIN + #define FAN1_PIN MOSFET_C_PIN #endif #ifndef FAN_PIN - #define FAN_PIN MOSFET_D_PIN + #if ENABLED(IS_EFB) || ENABLED(IS_EFF) // Hotend, Fan, Bed or Hotend, Fan, Fan + #define FAN_PIN MOSFET_B_PIN + #elif ENABLED(IS_EEF) || ENABLED(IS_SF) // Hotend, Hotend, Fan or Spindle, Fan + #define FAN_PIN MOSFET_C_PIN + #else + #define FAN_PIN MOSFET_D_PIN + #endif #endif // @@ -200,37 +201,37 @@ // // Misc. Functions // -#define LED_PIN 13 // B7 -#define CUTOFF_RESET_PIN 16 // H1 -#define CUTOFF_TEST_PIN 17 // H0 -#define CASE_LIGHT_PIN 44 // L5 MUST BE HARDWARE PWM +#define LED_PIN 13 // B7 +#define CUTOFF_RESET_PIN 16 // H1 +#define CUTOFF_TEST_PIN 17 // H0 +#define CASE_LIGHT_PIN 44 // L5 MUST BE HARDWARE PWM // // LCD / Controller // #ifdef REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER - #define LCD_PINS_RS 33 // C4: LCD-STROBE - #define LCD_PINS_ENABLE 72 // J2: LEFT - #define LCD_PINS_D4 35 // C2: LCD-CLK - #define LCD_PINS_D5 32 // C5: RLED - #define LCD_PINS_D6 34 // C3: LCD-DATA - #define LCD_PINS_D7 31 // C6: GLED + #define LCD_PINS_RS 33 // C4: LCD-STROBE + #define LCD_PINS_ENABLE 72 // J2: LEFT + #define LCD_PINS_D4 35 // C2: LCD-CLK + #define LCD_PINS_D5 32 // C5: RLED + #define LCD_PINS_D6 34 // C3: LCD-DATA + #define LCD_PINS_D7 31 // C6: GLED - #define BTN_EN2 75 // J4, UP - #define BTN_EN1 73 // J3, DOWN + #define BTN_EN2 75 // J4, UP + #define BTN_EN1 73 // J3, DOWN //STOP button connected as KILL_PIN - #define KILL_PIN 14 // J1, RIGHT + #define KILL_PIN 14 // J1, RIGHT //KILL - not connected - #define BEEPER_PIN 8 // H5, SD_WP + #define BEEPER_PIN 8 // H5, SD_WP - #define BTN_CENTER 15 // J0 - #define BTN_ENC BTN_CENTER + #define BTN_CENTER 15 // J0 + #define BTN_ENC BTN_CENTER //on board leds - #define STAT_LED_RED_LED SERVO0_PIN // C1 (1280-EX1, DEBUG2) - #define STAT_LED_BLUE_PIN SERVO1_PIN // C0 (1280-EX2, DEBUG3) + #define STAT_LED_RED_LED SERVO0_PIN // C1 (1280-EX1, DEBUG2) + #define STAT_LED_BLUE_PIN SERVO1_PIN // C0 (1280-EX2, DEBUG3) #else // Replicator uses a 3-wire SR controller with HD44780 @@ -238,18 +239,18 @@ // #define SAV_3DLCD - #define SR_DATA_PIN 34 // C3 - #define SR_CLK_PIN 35 // C2 - #define SR_STROBE_PIN 33 // C4 + #define SR_DATA_PIN 34 // C3 + #define SR_CLK_PIN 35 // C2 + #define SR_STROBE_PIN 33 // C4 - #define BTN_UP 75 // J4 - #define BTN_DOWN 73 // J3 - #define BTN_LEFT 72 // J2 - #define BTN_RIGHT 14 // J1 - #define BTN_CENTER 15 // J0 - #define BTN_ENC BTN_CENTER + #define BTN_UP 75 // J4 + #define BTN_DOWN 73 // J3 + #define BTN_LEFT 72 // J2 + #define BTN_RIGHT 14 // J1 + #define BTN_CENTER 15 // J0 + #define BTN_ENC BTN_CENTER - #define BEEPER_PIN 4 // G5 + #define BEEPER_PIN 4 // G5 #define STAT_LED_RED_PIN 32 // C5 #define STAT_LED_BLUE_PIN 31 // C6 (Actually green) @@ -259,8 +260,8 @@ // // SD Card // -#define SDSS 53 // B0 -#define SD_DETECT_PIN 9 // H6 +#define SDSS 53 // B0 +#define SD_DETECT_PIN 9 // H6 #define MAX_PIN THERMO_SCK_PIN @@ -272,8 +273,6 @@ #define SPINDLE_DIR_PIN 67 // K5 - - // Check if all pins are defined in mega/pins_arduino.h #include static_assert(NUM_DIGITAL_PINS > MAX_PIN, "add missing pins to [arduino dir]/hardware/arduino/avr/variants/mega/pins_arduino.h based on fastio.h" diff --git a/Marlin/pins_MINIRAMBO.h b/Marlin/pins_MINIRAMBO.h index 609c0c867c..89335ea8d4 100644 --- a/Marlin/pins_MINIRAMBO.h +++ b/Marlin/pins_MINIRAMBO.h @@ -106,7 +106,9 @@ #endif #define HEATER_BED_PIN 4 -#define FAN_PIN 8 +#ifndef FAN_PIN + #define FAN_PIN 8 +#endif #define FAN1_PIN 6 // diff --git a/Marlin/pins_MINITRONICS.h b/Marlin/pins_MINITRONICS.h index fe2c9e0b54..f5fdac21d2 100644 --- a/Marlin/pins_MINITRONICS.h +++ b/Marlin/pins_MINITRONICS.h @@ -57,13 +57,13 @@ #define X_DIR_PIN 47 #define X_ENABLE_PIN 49 -#define Y_STEP_PIN 39 // A6 -#define Y_DIR_PIN 40 // A0 +#define Y_STEP_PIN 39 // A6 +#define Y_DIR_PIN 40 // A0 #define Y_ENABLE_PIN 38 -#define Z_STEP_PIN 42 // A2 -#define Z_DIR_PIN 43 // A6 -#define Z_ENABLE_PIN 41 // A1 +#define Z_STEP_PIN 42 // A2 +#define Z_DIR_PIN 43 // A6 +#define Z_ENABLE_PIN 41 // A1 #define E0_STEP_PIN 45 #define E0_DIR_PIN 44 @@ -83,11 +83,13 @@ // // Heaters / Fans // -#define HEATER_0_PIN 7 // EXTRUDER 1 -#define HEATER_1_PIN 8 // EXTRUDER 2 -#define HEATER_BED_PIN 3 // BED +#define HEATER_0_PIN 7 // EXTRUDER 1 +#define HEATER_1_PIN 8 // EXTRUDER 2 +#define HEATER_BED_PIN 3 // BED -#define FAN_PIN 9 +#ifndef FAN_PIN + #define FAN_PIN 9 +#endif // // Misc. Functions @@ -122,7 +124,7 @@ #define BTN_EN2 -1 #define BTN_ENC -1 - #define SD_DETECT_PIN -1 // Minitronics doesn't use this + #define SD_DETECT_PIN -1 // Minitronics doesn't use this #endif // diff --git a/Marlin/pins_MKS_GEN_13.h b/Marlin/pins_MKS_GEN_13.h index 70228e2f1f..e0e916b878 100644 --- a/Marlin/pins_MKS_GEN_13.h +++ b/Marlin/pins_MKS_GEN_13.h @@ -33,7 +33,7 @@ #error "MKS GEN 1.3/1.4 supports up to 2 hotends / E-steppers. Comment out this line to continue." #endif -#define BOARD_NAME "MKS GEN > v1.3" +#define BOARD_NAME "MKS GEN >= v1.3" // // Heaters / Fans diff --git a/Marlin/pins_MKS_GEN_L.h b/Marlin/pins_MKS_GEN_L.h index dd4568e152..59c5bcf481 100644 --- a/Marlin/pins_MKS_GEN_L.h +++ b/Marlin/pins_MKS_GEN_L.h @@ -36,4 +36,17 @@ // Power outputs EFBF or EFBE #define MOSFET_D_PIN 7 +// +// CS Pins wired to avoid conflict with the LCD +// See https://www.thingiverse.com/asset:66604 +// + +#ifndef X_CS_PIN + #define X_CS_PIN 59 +#endif + +#ifndef Y_CS_PIN + #define Y_CS_PIN 63 +#endif + #include "pins_RAMPS.h" diff --git a/Marlin/pins_OMCA.h b/Marlin/pins_OMCA.h index 8715efcd44..6ee6fabe6b 100644 --- a/Marlin/pins_OMCA.h +++ b/Marlin/pins_OMCA.h @@ -108,13 +108,13 @@ #define E0_DIR_PIN 21 #define E0_ENABLE_PIN 10 -#define E1_STEP_PIN -1 // 21 -#define E1_DIR_PIN -1 // 20 -#define E1_ENABLE_PIN -1 // 19 +#define E1_STEP_PIN -1 // 21 +#define E1_DIR_PIN -1 // 20 +#define E1_ENABLE_PIN -1 // 19 -#define E2_STEP_PIN -1 // 21 -#define E2_DIR_PIN -1 // 20 -#define E2_ENABLE_PIN -1 // 18 +#define E2_STEP_PIN -1 // 21 +#define E2_DIR_PIN -1 // 20 +#define E2_ENABLE_PIN -1 // 18 // // Temperature Sensors @@ -126,10 +126,12 @@ // // Heaters / Fans // -#define HEATER_0_PIN 3 // DONE PWM on RIGHT connector +#define HEATER_0_PIN 3 // DONE PWM on RIGHT connector #define HEATER_BED_PIN 4 -#define FAN_PIN 14 // PWM on MIDDLE connector +#ifndef FAN_PIN + #define FAN_PIN 14 // PWM on MIDDLE connector +#endif // // Misc. Functions @@ -145,4 +147,4 @@ #define __GS 18 #define __GD 13 -#define UNUSED_PWM 14 // PWM on LEFT connector +#define UNUSED_PWM 14 // PWM on LEFT connector diff --git a/Marlin/pins_OMCA_A.h b/Marlin/pins_OMCA_A.h index 3686973c95..777c80b22f 100644 --- a/Marlin/pins_OMCA_A.h +++ b/Marlin/pins_OMCA_A.h @@ -107,12 +107,12 @@ #define E0_DIR_PIN 27 #define E0_ENABLE_PIN 24 -#define E1_STEP_PIN -1 // 19 -#define E1_DIR_PIN -1 // 18 +#define E1_STEP_PIN -1 // 19 +#define E1_DIR_PIN -1 // 18 #define E1_ENABLE_PIN 24 -#define E2_STEP_PIN -1 // 17 -#define E2_DIR_PIN -1 // 16 +#define E2_STEP_PIN -1 // 17 +#define E2_DIR_PIN -1 // 16 #define E2_ENABLE_PIN 24 // @@ -125,7 +125,9 @@ // #define HEATER_0_PIN 4 -#define FAN_PIN 3 +#ifndef FAN_PIN + #define FAN_PIN 3 +#endif // // Misc. Functions diff --git a/Marlin/pins_PRINTRBOARD.h b/Marlin/pins_PRINTRBOARD.h index 28392fb8df..42632d174a 100644 --- a/Marlin/pins_PRINTRBOARD.h +++ b/Marlin/pins_PRINTRBOARD.h @@ -110,8 +110,9 @@ #define HEATER_2_PIN 45 // F7 #define HEATER_BED_PIN 14 // C4 PWM3C - -#define FAN_PIN 16 // C6 PWM3A +#ifndef FAN_PIN + #define FAN_PIN 16 // C6 PWM3A +#endif // // Misc. Functions diff --git a/Marlin/pins_PRINTRBOARD_REVF.h b/Marlin/pins_PRINTRBOARD_REVF.h index 359a8b85db..8239a91586 100644 --- a/Marlin/pins_PRINTRBOARD_REVF.h +++ b/Marlin/pins_PRINTRBOARD_REVF.h @@ -190,7 +190,9 @@ #endif #endif -#define FAN_PIN 16 // C6 PWM3A +#ifndef FAN_PIN + #define FAN_PIN 16 // C6 PWM3A +#endif // // LCD / Controller diff --git a/Marlin/pins_RAMBO.h b/Marlin/pins_RAMBO.h index bcc1b4f62d..f2ea3d33bd 100644 --- a/Marlin/pins_RAMBO.h +++ b/Marlin/pins_RAMBO.h @@ -127,7 +127,9 @@ #define HEATER_2_PIN 6 #define HEATER_BED_PIN 3 -#define FAN_PIN 8 +#ifndef FAN_PIN + #define FAN_PIN 8 +#endif #define FAN1_PIN 6 #define FAN2_PIN 2 diff --git a/Marlin/pins_RAMPS_OLD.h b/Marlin/pins_RAMPS_OLD.h index 951e3b000b..da047e2b0a 100644 --- a/Marlin/pins_RAMPS_OLD.h +++ b/Marlin/pins_RAMPS_OLD.h @@ -88,11 +88,15 @@ #if ENABLED(RAMPS_V_1_0) #define HEATER_0_PIN 12 #define HEATER_BED_PIN -1 - #define FAN_PIN 11 + #ifndef FAN_PIN + #define FAN_PIN 11 + #endif #else // RAMPS_V_1_1 or RAMPS_V_1_2 #define HEATER_0_PIN 10 #define HEATER_BED_PIN 8 - #define FAN_PIN 9 + #ifndef FAN_PIN + #define FAN_PIN 9 + #endif #endif // diff --git a/Marlin/pins_RIGIDBOARD.h b/Marlin/pins_RIGIDBOARD.h index 5bd06e40eb..e985963505 100644 --- a/Marlin/pins_RIGIDBOARD.h +++ b/Marlin/pins_RIGIDBOARD.h @@ -36,8 +36,8 @@ // // MOSFET changes // -#define RAMPS_D10_PIN 9 // EXTRUDER 1 -#define MOSFET_D_PIN 12 // EXTRUDER 2 or FAN +#define RAMPS_D10_PIN 9 // EXTRUDER 1 +#define MOSFET_D_PIN 12 // EXTRUDER 2 or FAN #include "pins_RAMPS.h" @@ -74,9 +74,9 @@ // SPI for Max6675 or Max31855 Thermocouple #undef MAX6675_SS #if DISABLED(SDSUPPORT) - #define MAX6675_SS 53 // Don't use pin 53 if there is even the remote possibility of using Display/SD card + #define MAX6675_SS 53 // Don't use pin 53 if there is even the remote possibility of using Display/SD card #else - #define MAX6675_SS 49 // Don't use pin 49 as this is tied to the switch inside the SD card socket to detect if there is an SD card present + #define MAX6675_SS 49 // Don't use pin 49 as this is tied to the switch inside the SD card socket to detect if there is an SD card present #endif // @@ -85,8 +85,9 @@ #undef HEATER_BED_PIN #define HEATER_BED_PIN 10 -#undef FAN_PIN -#define FAN_PIN 8 // Same as RAMPS_13_EEF +#ifndef FAN_PIN + #define FAN_PIN 8 // Same as RAMPS_13_EEF +#endif // // Misc. Functions diff --git a/Marlin/pins_RIGIDBOARD_V2.h b/Marlin/pins_RIGIDBOARD_V2.h index dfac7051d5..2075892ab8 100644 --- a/Marlin/pins_RIGIDBOARD_V2.h +++ b/Marlin/pins_RIGIDBOARD_V2.h @@ -39,12 +39,12 @@ // Channels available for DAC, For Rigidboard there are 4 #define DAC_STEPPER_ORDER { 0, 1, 2, 3 } -#define DAC_STEPPER_SENSE 0.05 // sense resistors on rigidboard stepper chips are .05 value +#define DAC_STEPPER_SENSE 0.05 // sense resistors on rigidboard stepper chips are .05 value #define DAC_STEPPER_ADDRESS 0 -#define DAC_STEPPER_MAX 4096 // was 5000 but max allowable value is actually 4096 -#define DAC_STEPPER_VREF 1 // internal Vref, gain 2x = 4.096V -#define DAC_STEPPER_GAIN 1 // value of 1 here sets gain of 2 -#define DAC_DISABLE_PIN 42 // set low to enable DAC +#define DAC_STEPPER_MAX 4096 // was 5000 but max allowable value is actually 4096 +#define DAC_STEPPER_VREF 1 // internal Vref, gain 2x = 4.096V +#define DAC_STEPPER_GAIN 1 // value of 1 here sets gain of 2 +#define DAC_DISABLE_PIN 42 // set low to enable DAC #define DAC_OR_ADDRESS 0x01 #ifndef DAC_MOTOR_CURRENT_DEFAULT diff --git a/Marlin/pins_SANGUINOLOLU_11.h b/Marlin/pins_SANGUINOLOLU_11.h index ed4a521e7e..0d9af5b5a8 100644 --- a/Marlin/pins_SANGUINOLOLU_11.h +++ b/Marlin/pins_SANGUINOLOLU_11.h @@ -100,7 +100,7 @@ #define Z_ENABLE_PIN 26 #define E0_ENABLE_PIN 14 - #if ENABLED(LCD_I2C_PANELOLU2) + #if !defined(FAN_PIN) && ENABLED(LCD_I2C_PANELOLU2) #define FAN_PIN 4 // Uses Transistor1 (PWM) on Panelolu2's Sanguino Adapter Board to drive the fan #endif @@ -114,7 +114,7 @@ #endif -#if MB(AZTEEG_X1) || MB(STB_11) || ENABLED(IS_MELZI) +#if !defined(FAN_PIN) && (MB(AZTEEG_X1) || MB(STB_11) || ENABLED(IS_MELZI)) #define FAN_PIN 4 // Works for Panelolu2 too #endif diff --git a/Marlin/pins_SAV_MKI.h b/Marlin/pins_SAV_MKI.h index 1241ee083f..8945594495 100644 --- a/Marlin/pins_SAV_MKI.h +++ b/Marlin/pins_SAV_MKI.h @@ -114,7 +114,9 @@ #define HEATER_0_PIN 15 // C5 PWM3B - Extruder #define HEATER_BED_PIN 14 // C4 PWM3C - Bed -#define FAN_PIN 16 // C6 PWM3A +#ifndef FAN_PIN + #define FAN_PIN 16 // C6 PWM3A +#endif // // Misc. Functions diff --git a/Marlin/pins_SCOOVO_X9H.h b/Marlin/pins_SCOOVO_X9H.h index c0852bf310..ea6685def4 100644 --- a/Marlin/pins_SCOOVO_X9H.h +++ b/Marlin/pins_SCOOVO_X9H.h @@ -91,7 +91,7 @@ #define E1_MS2_PIN 64 #define DIGIPOTSS_PIN 38 -#define DIGIPOT_CHANNELS {4,5,3,0,1} // X Y Z E0 E1 digipot channels to stepper driver mapping +#define DIGIPOT_CHANNELS {4,5,3,0,1} // X Y Z E0 E1 digipot channels to stepper driver mapping // // Temperature Sensors @@ -106,7 +106,9 @@ #define HEATER_1_PIN 7 #define HEATER_BED_PIN 3 -#define FAN_PIN 8 +#ifndef FAN_PIN + #define FAN_PIN 8 +#endif #define FAN1_PIN 6 #define FAN2_PIN 2 diff --git a/Marlin/pins_SETHI.h b/Marlin/pins_SETHI.h index 293373a6d7..801560e5e3 100644 --- a/Marlin/pins_SETHI.h +++ b/Marlin/pins_SETHI.h @@ -98,12 +98,13 @@ #define HEATER_0_PIN 4 #define HEATER_BED_PIN 3 - -#if GEN7_VERSION >= 13 - // Gen7 v1.3 removed the fan pin - #define FAN_PIN -1 -#else - #define FAN_PIN 31 +#ifndef FAN_PIN + #if GEN7_VERSION >= 13 + // Gen7 v1.3 removed the fan pin + #define FAN_PIN -1 + #else + #define FAN_PIN 31 + #endif #endif // diff --git a/Marlin/pins_SILVER_GATE.h b/Marlin/pins_SILVER_GATE.h index ca96a427c1..005c4c6eca 100644 --- a/Marlin/pins_SILVER_GATE.h +++ b/Marlin/pins_SILVER_GATE.h @@ -56,7 +56,9 @@ #define FIL_RUNOUT_PIN 34 // X_MAX unless overridden #endif -#define FAN_PIN 5 +#ifndef FAN_PIN + #define FAN_PIN 5 +#endif #define HEATER_0_PIN 7 diff --git a/Marlin/pins_TEENSY2.h b/Marlin/pins_TEENSY2.h index 16fcd46f10..736be6f73e 100644 --- a/Marlin/pins_TEENSY2.h +++ b/Marlin/pins_TEENSY2.h @@ -149,7 +149,9 @@ // #define HEATER_0_PIN 15 // C5 PWM3B Extruder #define HEATER_BED_PIN 14 // C4 PWM3C -#define FAN_PIN 16 // C6 PWM3A Fan +#ifndef FAN_PIN + #define FAN_PIN 16 // C6 PWM3A Fan +#endif // // Misc. Functions diff --git a/Marlin/pins_TEENSYLU.h b/Marlin/pins_TEENSYLU.h index 33f78b2305..e733b98287 100755 --- a/Marlin/pins_TEENSYLU.h +++ b/Marlin/pins_TEENSYLU.h @@ -127,7 +127,9 @@ #define HEATER_0_PIN 15 // C5 PWM3B - Extruder #define HEATER_BED_PIN 14 // C4 PWM3C -#define FAN_PIN 16 // C6 PWM3A +#ifndef FAN_PIN + #define FAN_PIN 16 // C6 PWM3A +#endif // // Misc. Functions diff --git a/Marlin/pins_TRIGORILLA_13.h b/Marlin/pins_TRIGORILLA_13.h index e876da4671..0907eaf286 100644 --- a/Marlin/pins_TRIGORILLA_13.h +++ b/Marlin/pins_TRIGORILLA_13.h @@ -29,8 +29,9 @@ #endif #define IS_RAMPS_EFB -#define RAMPS_D9_PIN 44 -#define ORIG_E0_AUTO_FAN_PIN RAMPS_D9_PIN +#define RAMPS_D9_PIN 44 +#define FAN2_PIN 9 +#define ORIG_E0_AUTO_FAN_PIN 9 #include "pins_RAMPS_13.h" diff --git a/Marlin/pins_TRIGORILLA_14.h b/Marlin/pins_TRIGORILLA_14.h index 3903e1dba5..70b0a930a9 100644 --- a/Marlin/pins_TRIGORILLA_14.h +++ b/Marlin/pins_TRIGORILLA_14.h @@ -30,11 +30,7 @@ #define IS_RAMPS_EFB -#include "pins_RAMPS_13.h" +#define FAN2_PIN 44 +#define ORIG_E0_AUTO_FAN_PIN 44 -#define FAN2_PIN 44 - -#undef E1_STEP_PIN -#undef E1_DIR_PIN -#undef E1_ENABLE_PIN -#undef E1_CS_PIN +#include "pins_RAMPS.h" diff --git a/Marlin/pins_ULTIMAIN_2.h b/Marlin/pins_ULTIMAIN_2.h index c52b20c3e3..ea7a8325c0 100644 --- a/Marlin/pins_ULTIMAIN_2.h +++ b/Marlin/pins_ULTIMAIN_2.h @@ -93,7 +93,9 @@ #define HEATER_1_PIN 3 #define HEATER_BED_PIN 4 -#define FAN_PIN 7 +#ifndef FAN_PIN + #define FAN_PIN 7 +#endif // // Misc. Functions diff --git a/Marlin/pins_ULTIMAKER.h b/Marlin/pins_ULTIMAKER.h index 0d1a65431f..4b6488aede 100644 --- a/Marlin/pins_ULTIMAKER.h +++ b/Marlin/pins_ULTIMAKER.h @@ -99,7 +99,9 @@ #define HEATER_1_PIN 3 #define HEATER_BED_PIN 4 -#define FAN_PIN 7 +#ifndef FAN_PIN + #define FAN_PIN 7 +#endif // // Misc. Functions diff --git a/Marlin/planner.cpp b/Marlin/planner.cpp index ffbee93ad5..7a73c50f6d 100644 --- a/Marlin/planner.cpp +++ b/Marlin/planner.cpp @@ -83,7 +83,7 @@ // Delay for delivery of first block to the stepper ISR, if the queue contains 2 or // fewer movements. The delay is measured in milliseconds, and must be less than 250ms -#define BLOCK_DELAY_FOR_1ST_MOVE 50 +#define BLOCK_DELAY_FOR_1ST_MOVE 100 Planner planner; @@ -93,15 +93,38 @@ Planner planner; * A ring buffer of moves described in steps */ block_t Planner::block_buffer[BLOCK_BUFFER_SIZE]; -volatile uint8_t Planner::block_buffer_head, // Index of the next block to be pushed - Planner::block_buffer_tail; // Index of the busy block, if any -uint16_t Planner::cleaning_buffer_counter; // A counter to disable queuing of blocks -uint8_t Planner::delay_before_delivering, // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks - Planner::block_buffer_planned; // Index of the optimally planned block +volatile uint8_t Planner::block_buffer_head, // Index of the next block to be pushed + Planner::block_buffer_nonbusy, // Index of the first non-busy block + Planner::block_buffer_planned, // Index of the optimally planned block + Planner::block_buffer_tail; // Index of the busy block, if any +uint16_t Planner::cleaning_buffer_counter; // A counter to disable queuing of blocks +uint8_t Planner::delay_before_delivering; // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks -float Planner::max_feedrate_mm_s[XYZE_N], // Max speeds in mm per second - Planner::axis_steps_per_mm[XYZE_N], - Planner::steps_to_mm[XYZE_N]; +uint32_t Planner::max_acceleration_mm_per_s2[XYZE_N], // (mm/s^2) M201 XYZE + Planner::max_acceleration_steps_per_s2[XYZE_N], // (steps/s^2) Derived from mm_per_s2 + Planner::min_segment_time_us; // (µs) M205 B + +float Planner::max_feedrate_mm_s[XYZE_N], // (mm/s) M203 XYZE - Max speeds + Planner::axis_steps_per_mm[XYZE_N], // (steps) M92 XYZE - Steps per millimeter + Planner::steps_to_mm[XYZE_N], // (mm) Millimeters per step + Planner::min_feedrate_mm_s, // (mm/s) M205 S - Minimum linear feedrate + Planner::acceleration, // (mm/s^2) M204 S - Normal acceleration. DEFAULT ACCELERATION for all printing moves. + Planner::retract_acceleration, // (mm/s^2) M204 R - Retract acceleration. Filament pull-back and push-forward while standing still in the other axes + Planner::travel_acceleration, // (mm/s^2) M204 T - Travel acceleration. DEFAULT ACCELERATION for all NON printing moves. + Planner::min_travel_feedrate_mm_s; // (mm/s) M205 T - Minimum travel feedrate + +#if ENABLED(JUNCTION_DEVIATION) + float Planner::junction_deviation_mm; // (mm) M205 J + #if ENABLED(LIN_ADVANCE) + #if ENABLED(DISTINCT_E_FACTORS) + float Planner::max_e_jerk[EXTRUDERS]; // Calculated from junction_deviation_mm + #else + float Planner::max_e_jerk; + #endif + #endif +#else + float Planner::max_jerk[XYZE]; // (mm/s^2) M205 XYZE - The largest speed change requiring no acceleration. +#endif #if ENABLED(ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED) bool Planner::abort_on_endstop_hit = false; @@ -109,31 +132,21 @@ float Planner::max_feedrate_mm_s[XYZE_N], // Max speeds in mm per second #if ENABLED(DISTINCT_E_FACTORS) uint8_t Planner::last_extruder = 0; // Respond to extruder change + #define _EINDEX (E_AXIS + active_extruder) +#else + #define _EINDEX E_AXIS #endif int16_t Planner::flow_percentage[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(100); // Extrusion factor for each extruder -float Planner::e_factor[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(1.0); // The flow percentage and volumetric multiplier combine to scale E movement +float Planner::e_factor[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(1.0f); // The flow percentage and volumetric multiplier combine to scale E movement #if DISABLED(NO_VOLUMETRICS) float Planner::filament_size[EXTRUDERS], // diameter of filament (in millimeters), typically around 1.75 or 2.85, 0 disables the volumetric calculations for the extruder - Planner::volumetric_area_nominal = CIRCLE_AREA((DEFAULT_NOMINAL_FILAMENT_DIA) * 0.5), // Nominal cross-sectional area + Planner::volumetric_area_nominal = CIRCLE_AREA(float(DEFAULT_NOMINAL_FILAMENT_DIA) * 0.5f), // Nominal cross-sectional area Planner::volumetric_multiplier[EXTRUDERS]; // Reciprocal of cross-sectional area of filament (in mm^2). Pre-calculated to reduce computation in the planner #endif -uint32_t Planner::max_acceleration_steps_per_s2[XYZE_N], - Planner::max_acceleration_mm_per_s2[XYZE_N]; // Use M201 to override by software - -uint32_t Planner::min_segment_time_us; - -// Initialized by settings.load() -float Planner::min_feedrate_mm_s, - Planner::acceleration, // Normal acceleration mm/s^2 DEFAULT ACCELERATION for all printing moves. M204 SXXXX - Planner::retract_acceleration, // Retract acceleration mm/s^2 filament pull-back and push-forward while standing still in the other axes M204 TXXXX - Planner::travel_acceleration, // Travel acceleration mm/s^2 DEFAULT ACCELERATION for all NON printing moves. M204 MXXXX - Planner::max_jerk[XYZE], // The largest speed change requiring no acceleration - Planner::min_travel_feedrate_mm_s; - #if HAS_LEVELING bool Planner::leveling_active = false; // Flag that auto bed leveling is enabled #if ABL_PLANAR @@ -164,7 +177,7 @@ float Planner::min_feedrate_mm_s, #if ENABLED(AUTOTEMP) float Planner::autotemp_max = 250, Planner::autotemp_min = 210, - Planner::autotemp_factor = 0.1; + Planner::autotemp_factor = 0.1f; bool Planner::autotemp_enabled = false; #endif @@ -212,156 +225,96 @@ void Planner::init() { ZERO(position_float); #endif ZERO(previous_speed); - previous_nominal_speed_sqr = 0.0; + previous_nominal_speed_sqr = 0; #if ABL_PLANAR bed_level_matrix.set_to_identity(); #endif clear_block_buffer(); - block_buffer_planned = 0; delay_before_delivering = 0; } -#if ENABLED(BEZIER_JERK_CONTROL) +#if ENABLED(S_CURVE_ACCELERATION) - // This routine, for AVR, returns 0x1000000 / d, but trying to get the inverse as - // fast as possible. A fast converging iterative Newton-Raphson method is able to - // reach full precision in just 1 iteration, and takes 211 cycles (worst case, mean - // case is less, up to 30 cycles for small divisors), instead of the 500 cycles a - // normal division would take. - // - // Inspired by the following page, - // https://stackoverflow.com/questions/27801397/newton-raphson-division-with-big-integers - // - // Suppose we want to calculate - // floor(2 ^ k / B) where B is a positive integer - // Then - // B must be <= 2^k, otherwise, the quotient is 0. - // - // The Newton - Raphson iteration for x = B / 2 ^ k yields: - // q[n + 1] = q[n] * (2 - q[n] * B / 2 ^ k) - // - // We can rearrange it as: - // q[n + 1] = q[n] * (2 ^ (k + 1) - q[n] * B) >> k - // - // Each iteration of this kind requires only integer multiplications - // and bit shifts. - // Does it converge to floor(2 ^ k / B) ?: Not necessarily, but, in - // the worst case, it eventually alternates between floor(2 ^ k / B) - // and ceiling(2 ^ k / B)). - // So we can use some not-so-clever test to see if we are in this - // case, and extract floor(2 ^ k / B). - // Lastly, a simple but important optimization for this approach is to - // truncate multiplications (i.e.calculate only the higher bits of the - // product) in the early iterations of the Newton - Raphson method.The - // reason to do so, is that the results of the early iterations are far - // from the quotient, and it doesn't matter to perform them inaccurately. - // Finally, we should pick a good starting value for x. Knowing how many - // digits the divisor has, we can estimate it: - // - // 2^k / x = 2 ^ log2(2^k / x) - // 2^k / x = 2 ^(log2(2^k)-log2(x)) - // 2^k / x = 2 ^(k*log2(2)-log2(x)) - // 2^k / x = 2 ^ (k-log2(x)) - // 2^k / x >= 2 ^ (k-floor(log2(x))) - // floor(log2(x)) simply is the index of the most significant bit set. - // - // If we could improve this estimation even further, then the number of - // iterations can be dropped quite a bit, thus saving valuable execution time. - // The paper "Software Integer Division" by Thomas L.Rodeheffer, Microsoft - // Research, Silicon Valley,August 26, 2008, that is available at - // https://www.microsoft.com/en-us/research/wp-content/uploads/2008/08/tr-2008-141.pdf - // suggests , for its integer division algorithm, that using a table to supply the - // first 8 bits of precision, and due to the quadratic convergence nature of the - // Newton-Raphon iteration, then just 2 iterations should be enough to get - // maximum precision of the division. - // If we precompute values of inverses for small denominator values, then - // just one Newton-Raphson iteration is enough to reach full precision - // We will use the top 9 bits of the denominator as index. - // - // The AVR assembly function is implementing the following C code, included - // here as reference: - // - // uint32_t get_period_inverse(uint32_t d) { - // static const uint8_t inv_tab[256] = { - // 255,253,252,250,248,246,244,242,240,238,236,234,233,231,229,227, - // 225,224,222,220,218,217,215,213,212,210,208,207,205,203,202,200, - // 199,197,195,194,192,191,189,188,186,185,183,182,180,179,178,176, - // 175,173,172,170,169,168,166,165,164,162,161,160,158,157,156,154, - // 153,152,151,149,148,147,146,144,143,142,141,139,138,137,136,135, - // 134,132,131,130,129,128,127,126,125,123,122,121,120,119,118,117, - // 116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101, - // 100,99,98,97,96,95,94,93,92,91,90,89,88,88,87,86, - // 85,84,83,82,81,80,80,79,78,77,76,75,74,74,73,72, - // 71,70,70,69,68,67,66,66,65,64,63,62,62,61,60,59, - // 59,58,57,56,56,55,54,53,53,52,51,50,50,49,48,48, - // 47,46,46,45,44,43,43,42,41,41,40,39,39,38,37,37, - // 36,35,35,34,33,33,32,32,31,30,30,29,28,28,27,27, - // 26,25,25,24,24,23,22,22,21,21,20,19,19,18,18,17, - // 17,16,15,15,14,14,13,13,12,12,11,10,10,9,9,8, - // 8,7,7,6,6,5,5,4,4,3,3,2,2,1,0,0 - // }; - // - // // For small denominators, it is cheaper to directly store the result, - // // because those denominators would require 2 Newton-Raphson iterations - // // to converge to the required result precision. For bigger ones, just - // // ONE Newton-Raphson iteration is enough to get maximum precision! - // static const uint32_t small_inv_tab[111] PROGMEM = { - // 16777216,16777216,8388608,5592405,4194304,3355443,2796202,2396745,2097152,1864135,1677721,1525201,1398101,1290555,1198372,1118481, - // 1048576,986895,932067,883011,838860,798915,762600,729444,699050,671088,645277,621378,599186,578524,559240,541200, - // 524288,508400,493447,479349,466033,453438,441505,430185,419430,409200,399457,390167,381300,372827,364722,356962, - // 349525,342392,335544,328965,322638,316551,310689,305040,299593,294337,289262,284359,279620,275036,270600,266305, - // 262144,258111,254200,250406,246723,243148,239674,236298,233016,229824,226719,223696,220752,217885,215092,212369, - // 209715,207126,204600,202135,199728,197379,195083,192841,190650,188508,186413,184365,182361,180400,178481,176602, - // 174762,172960,171196,169466,167772,166111,164482,162885,161319,159783,158275,156796,155344,153919,152520 - // }; - // - // // For small divisors, it is best to directly retrieve the results - // if (d <= 110) - // return pgm_read_dword(&small_inv_tab[d]); - // - // // Compute initial estimation of 0x1000000/x - - // // Get most significant bit set on divider - // uint8_t idx = 0; - // uint32_t nr = d; - // if (!(nr & 0xFF0000)) { - // nr <<= 8; - // idx += 8; - // if (!(nr & 0xFF0000)) { - // nr <<= 8; - // idx += 8; - // } - // } - // if (!(nr & 0xF00000)) { - // nr <<= 4; - // idx += 4; - // } - // if (!(nr & 0xC00000)) { - // nr <<= 2; - // idx += 2; - // } - // if (!(nr & 0x800000)) { - // nr <<= 1; - // idx += 1; - // } - // - // // Isolate top 9 bits of the denominator, to be used as index into the initial estimation table - // uint32_t tidx = nr >> 15; // top 9 bits. bit8 is always set - // uint32_t ie = inv_tab[tidx & 0xFF] + 256; // Get the table value. bit9 is always set - // uint32_t x = idx <= 8 ? (ie >> (8 - idx)) : (ie << (idx - 8)); // Position the estimation at the proper place - // - // // Now, refine estimation by newton-raphson. 1 iteration is enough - // x = uint32_t((x * uint64_t((1 << 25) - x * d)) >> 24); - // - // // Estimate remainder - // uint32_t r = (1 << 24) - x * d; - // - // // Check if we must adjust result - // if (r >= d) x++; - // - // // x holds the proper estimation - // return uint32_t(x); - // } - // + /** + * This routine returns 0x1000000 / d, getting the inverse as fast as possible. + * A fast-converging iterative Newton-Raphson method can reach full precision in + * just 1 iteration, and takes 211 cycles (worst case; the mean case is less, up + * to 30 cycles for small divisors), instead of the 500 cycles a normal division + * would take. + * + * Inspired by the following page: + * https://stackoverflow.com/questions/27801397/newton-raphson-division-with-big-integers + * + * Suppose we want to calculate floor(2 ^ k / B) where B is a positive integer + * Then, B must be <= 2^k, otherwise, the quotient is 0. + * + * The Newton - Raphson iteration for x = B / 2 ^ k yields: + * q[n + 1] = q[n] * (2 - q[n] * B / 2 ^ k) + * + * This can be rearranged to: + * q[n + 1] = q[n] * (2 ^ (k + 1) - q[n] * B) >> k + * + * Each iteration requires only integer multiplications and bit shifts. + * It doesn't necessarily converge to floor(2 ^ k / B) but in the worst case + * it eventually alternates between floor(2 ^ k / B) and ceil(2 ^ k / B). + * So it checks for this case and extracts floor(2 ^ k / B). + * + * A simple but important optimization for this approach is to truncate + * multiplications (i.e., calculate only the higher bits of the product) in the + * early iterations of the Newton - Raphson method. This is done so the results + * of the early iterations are far from the quotient. Then it doesn't matter if + * they are done inaccurately. + * It's important to pick a good starting value for x. Knowing how many + * digits the divisor has, it can be estimated: + * + * 2^k / x = 2 ^ log2(2^k / x) + * 2^k / x = 2 ^(log2(2^k)-log2(x)) + * 2^k / x = 2 ^(k*log2(2)-log2(x)) + * 2^k / x = 2 ^ (k-log2(x)) + * 2^k / x >= 2 ^ (k-floor(log2(x))) + * floor(log2(x)) is simply the index of the most significant bit set. + * + * If this estimation can be improved even further the number of iterations can be + * reduced a lot, saving valuable execution time. + * The paper "Software Integer Division" by Thomas L.Rodeheffer, Microsoft + * Research, Silicon Valley,August 26, 2008, available at + * https://www.microsoft.com/en-us/research/wp-content/uploads/2008/08/tr-2008-141.pdf + * suggests, for its integer division algorithm, using a table to supply the first + * 8 bits of precision, then, due to the quadratic convergence nature of the + * Newton-Raphon iteration, just 2 iterations should be enough to get maximum + * precision of the division. + * By precomputing values of inverses for small denominator values, just one + * Newton-Raphson iteration is enough to reach full precision. + * This code uses the top 9 bits of the denominator as index. + * + * The AVR assembly function implements this C code using the data below: + * + * // For small divisors, it is best to directly retrieve the results + * if (d <= 110) return pgm_read_dword(&small_inv_tab[d]); + * + * // Compute initial estimation of 0x1000000/x - + * // Get most significant bit set on divider + * uint8_t idx = 0; + * uint32_t nr = d; + * if (!(nr & 0xFF0000)) { + * nr <<= 8; idx += 8; + * if (!(nr & 0xFF0000)) { nr <<= 8; idx += 8; } + * } + * if (!(nr & 0xF00000)) { nr <<= 4; idx += 4; } + * if (!(nr & 0xC00000)) { nr <<= 2; idx += 2; } + * if (!(nr & 0x800000)) { nr <<= 1; idx += 1; } + * + * // Isolate top 9 bits of the denominator, to be used as index into the initial estimation table + * uint32_t tidx = nr >> 15, // top 9 bits. bit8 is always set + * ie = inv_tab[tidx & 0xFF] + 256, // Get the table value. bit9 is always set + * x = idx <= 8 ? (ie >> (8 - idx)) : (ie << (idx - 8)); // Position the estimation at the proper place + * + * x = uint32_t((x * uint64_t(_BV(25) - x * d)) >> 24); // Refine estimation by newton-raphson. 1 iteration is enough + * const uint32_t r = _BV(24) - x * d; // Estimate remainder + * if (r >= d) x++; // Check whether to adjust result + * return uint32_t(x); // x holds the proper estimation + * + */ static uint32_t get_period_inverse(uint32_t d) { static const uint8_t inv_tab[256] PROGMEM = { @@ -397,13 +350,12 @@ void Planner::init() { }; // For small divisors, it is best to directly retrieve the results - if (d <= 110) - return pgm_read_dword(&small_inv_tab[d]); + if (d <= 110) return pgm_read_dword(&small_inv_tab[d]); - register uint8_t r8 = d & 0xFF; - register uint8_t r9 = (d >> 8) & 0xFF; - register uint8_t r10 = (d >> 16) & 0xFF; - register uint8_t r2,r3,r4,r5,r6,r7,r11,r12,r13,r14,r15,r16,r17,r18; + register uint8_t r8 = d & 0xFF, + r9 = (d >> 8) & 0xFF, + r10 = (d >> 16) & 0xFF, + r2,r3,r4,r5,r6,r7,r11,r12,r13,r14,r15,r16,r17,r18; register const uint8_t* ptab = inv_tab; __asm__ __volatile__( @@ -726,13 +678,20 @@ void Planner::init() { // Return the result return r11 | (uint16_t(r12) << 8) | (uint32_t(r13) << 16); } -#endif // BEZIER_JERK_CONTROL + +#endif // S_CURVE_ACCELERATION #define MINIMAL_STEP_RATE 120 /** * Calculate trapezoid parameters, multiplying the entry- and exit-speeds * by the provided factors. + ** + * ############ VERY IMPORTANT ############ + * NOTE that the PRECONDITION to call this function is that the block is + * NOT BUSY and it is marked as RECALCULATE. That WARRANTIES the Stepper ISR + * is not and will not use the block while we modify it, so it is safe to + * alter its values. */ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &entry_factor, const float &exit_factor) { @@ -743,7 +702,7 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e NOLESS(initial_rate, uint32_t(MINIMAL_STEP_RATE)); NOLESS(final_rate, uint32_t(MINIMAL_STEP_RATE)); - #if ENABLED(BEZIER_JERK_CONTROL) + #if ENABLED(S_CURVE_ACCELERATION) uint32_t cruise_rate = initial_rate; #endif @@ -764,49 +723,38 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e accelerate_steps = MIN(uint32_t(MAX(accelerate_steps_float, 0)), block->step_event_count); plateau_steps = 0; - #if ENABLED(BEZIER_JERK_CONTROL) + #if ENABLED(S_CURVE_ACCELERATION) // We won't reach the cruising rate. Let's calculate the speed we will reach cruise_rate = final_speed(initial_rate, accel, accelerate_steps); #endif } - #if ENABLED(BEZIER_JERK_CONTROL) + #if ENABLED(S_CURVE_ACCELERATION) else // We have some plateau time, so the cruise rate will be the nominal rate cruise_rate = block->nominal_rate; #endif - // block->accelerate_until = accelerate_steps; - // block->decelerate_after = accelerate_steps+plateau_steps; - - #if ENABLED(BEZIER_JERK_CONTROL) + #if ENABLED(S_CURVE_ACCELERATION) // Jerk controlled speed requires to express speed versus time, NOT steps - uint32_t acceleration_time = ((float)(cruise_rate - initial_rate) / accel) * (HAL_STEPPER_TIMER_RATE), - deceleration_time = ((float)(cruise_rate - final_rate) / accel) * (HAL_STEPPER_TIMER_RATE); + uint32_t acceleration_time = ((float)(cruise_rate - initial_rate) / accel) * (STEPPER_TIMER_RATE), + deceleration_time = ((float)(cruise_rate - final_rate) / accel) * (STEPPER_TIMER_RATE); // And to offload calculations from the ISR, we also calculate the inverse of those times here uint32_t acceleration_time_inverse = get_period_inverse(acceleration_time); uint32_t deceleration_time_inverse = get_period_inverse(deceleration_time); - #endif - // Fill variables used by the stepper in a critical section - const bool was_enabled = STEPPER_ISR_ENABLED(); - if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT(); - - // Don't update variables if block is busy: It is being interpreted by the planner - if (!TEST(block->flag, BLOCK_BIT_BUSY)) { - block->accelerate_until = accelerate_steps; - block->decelerate_after = accelerate_steps + plateau_steps; - block->initial_rate = initial_rate; - #if ENABLED(BEZIER_JERK_CONTROL) - block->acceleration_time = acceleration_time; - block->deceleration_time = deceleration_time; - block->acceleration_time_inverse = acceleration_time_inverse; - block->deceleration_time_inverse = deceleration_time_inverse; - block->cruise_rate = cruise_rate; - #endif - block->final_rate = final_rate; - } - if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT(); + // Store new block parameters + block->accelerate_until = accelerate_steps; + block->decelerate_after = accelerate_steps + plateau_steps; + block->initial_rate = initial_rate; + #if ENABLED(S_CURVE_ACCELERATION) + block->acceleration_time = acceleration_time; + block->deceleration_time = deceleration_time; + block->acceleration_time_inverse = acceleration_time_inverse; + block->deceleration_time_inverse = deceleration_time_inverse; + block->cruise_rate = cruise_rate; + #endif + block->final_rate = final_rate; } /* PLANNER SPEED DEFINITION @@ -857,7 +805,7 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the planner buffer that don't change with the addition of a new block, as describe above. In addition, this block can never be less than block_buffer_tail and will always be pushed forward and maintain - this requirement when encountered by the plan_discard_current_block() routine during a cycle. + this requirement when encountered by the Planner::discard_current_block() routine during a cycle. NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't @@ -894,12 +842,26 @@ void Planner::reverse_pass_kernel(block_t* const current, const block_t * const const float new_entry_speed_sqr = TEST(current->flag, BLOCK_BIT_NOMINAL_LENGTH) ? max_entry_speed_sqr - : MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next ? next->entry_speed_sqr : sq(MINIMUM_PLANNER_SPEED), current->millimeters)); + : MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next ? next->entry_speed_sqr : sq(float(MINIMUM_PLANNER_SPEED)), current->millimeters)); if (current->entry_speed_sqr != new_entry_speed_sqr) { - current->entry_speed_sqr = new_entry_speed_sqr; - // Need to recalculate the block speed + // Need to recalculate the block speed - Mark it now, so the stepper + // ISR does not consume the block before being recalculated SBI(current->flag, BLOCK_BIT_RECALCULATE); + + // But there is an inherent race condition here, as the block may have + // become BUSY just before being marked RECALCULATE, so check for that! + if (stepper.is_block_busy(current)) { + // Block became busy. Clear the RECALCULATE flag (no point in + // recalculating BUSY blocks). And don't set its speed, as it can't + // be updated at this time. + CBI(current->flag, BLOCK_BIT_RECALCULATE); + } + else { + // Block is not BUSY so this is ahead of the Stepper ISR: + // Just Set the new entry speed. + current->entry_speed_sqr = new_entry_speed_sqr; + } } } } @@ -925,12 +887,11 @@ void Planner::reverse_pass() { // Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last // block in buffer. Cease planning when the last optimal planned or tail pointer is reached. // NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan. - block_t *current; const block_t *next = NULL; while (block_index != planned_block_index) { // Perform the reverse pass - current = &block_buffer[block_index]; + block_t *current = &block_buffer[block_index]; // Only consider non sync blocks if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) { @@ -940,6 +901,18 @@ void Planner::reverse_pass() { // Advance to the next block_index = prev_block_index(block_index); + + // The ISR could advance the block_buffer_planned while we were doing the reverse pass. + // We must try to avoid using an already consumed block as the last one - So follow + // changes to the pointer and make sure to limit the loop to the currently busy block + while (planned_block_index != block_buffer_planned) { + + // If we reached the busy block or an already processed block, break the loop now + if (block_index == planned_block_index) return; + + // Advance the pointer, following the busy block + planned_block_index = next_block_index(planned_block_index); + } } } @@ -959,14 +932,28 @@ void Planner::forward_pass_kernel(const block_t* const previous, block_t* const // If true, current block is full-acceleration and we can move the planned pointer forward. if (new_entry_speed_sqr < current->entry_speed_sqr) { - // Always <= max_entry_speed_sqr. Backward pass sets this. - current->entry_speed_sqr = new_entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this. - - // Set optimal plan pointer. - block_buffer_planned = block_index; - - // And mark we need to recompute the trapezoidal shape + // Mark we need to recompute the trapezoidal shape, and do it now, + // so the stepper ISR does not consume the block before being recalculated SBI(current->flag, BLOCK_BIT_RECALCULATE); + + // But there is an inherent race condition here, as the block maybe + // became BUSY, just before it was marked as RECALCULATE, so check + // if that is the case! + if (stepper.is_block_busy(current)) { + // Block became busy. Clear the RECALCULATE flag (no point in + // recalculating BUSY blocks and don't set its speed, as it can't + // be updated at this time. + CBI(current->flag, BLOCK_BIT_RECALCULATE); + } + else { + // Block is not BUSY, we won the race against the Stepper ISR: + + // Always <= max_entry_speed_sqr. Backward pass sets this. + current->entry_speed_sqr = new_entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this. + + // Set optimal plan pointer. + block_buffer_planned = block_index; + } } } @@ -1003,7 +990,13 @@ void Planner::forward_pass() { // Skip SYNC blocks if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) { - forward_pass_kernel(previous, current, block_index); + // If there's no previous block or the previous block is not + // BUSY (thus, modifiable) run the forward_pass_kernel. Otherwise, + // the previous block became BUSY, so assume the current block's + // entry speed can't be altered (since that would also require + // updating the exit speed of the previous block). + if (!previous || !stepper.is_block_busy(previous)) + forward_pass_kernel(previous, current, block_index); previous = current; } // Advance to the previous @@ -1018,16 +1011,15 @@ void Planner::forward_pass() { */ void Planner::recalculate_trapezoids() { // The tail may be changed by the ISR so get a local copy. - uint8_t block_index = block_buffer_tail; - - // As there could be a sync block in the head of the queue, and the next loop must not - // recalculate the head block (as it needs to be specially handled), scan backwards until - // we find the first non SYNC block - uint8_t head_block_index = block_buffer_head; + uint8_t block_index = block_buffer_tail, + head_block_index = block_buffer_head; + // Since there could be a sync block in the head of the queue, and the + // next loop must not recalculate the head block (as it needs to be + // specially handled), scan backwards to the first non-SYNC block. while (head_block_index != block_index) { // Go back (head always point to the first free block) - uint8_t prev_index = prev_block_index(head_block_index); + const uint8_t prev_index = prev_block_index(head_block_index); // Get the pointer to the block block_t *prev = &block_buffer[prev_index]; @@ -1037,7 +1029,7 @@ void Planner::recalculate_trapezoids() { // Examine the previous block. This and all following are SYNC blocks head_block_index = prev_index; - }; + } // Go from the tail (currently executed block) to the first block, without including it) block_t *current = NULL, *next = NULL; @@ -1053,18 +1045,34 @@ void Planner::recalculate_trapezoids() { if (current) { // Recalculate if current block entry or exit junction speed has changed. if (TEST(current->flag, BLOCK_BIT_RECALCULATE) || TEST(next->flag, BLOCK_BIT_RECALCULATE)) { - // NOTE: Entry and exit factors always > 0 by all previous logic operations. - const float current_nominal_speed = SQRT(current->nominal_speed_sqr), - nomr = 1.0 / current_nominal_speed; - calculate_trapezoid_for_block(current, current_entry_speed * nomr, next_entry_speed * nomr); - #if ENABLED(LIN_ADVANCE) - if (current->use_advance_lead) { - const float comp = current->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS]; - current->max_adv_steps = current_nominal_speed * comp; - current->final_adv_steps = next_entry_speed * comp; - } - #endif - CBI(current->flag, BLOCK_BIT_RECALCULATE); // Reset current only to ensure next trapezoid is computed + + // Mark the current block as RECALCULATE, to protect it from the Stepper ISR running it. + // Note that due to the above condition, there's a chance the current block isn't marked as + // RECALCULATE yet, but the next one is. That's the reason for the following line. + SBI(current->flag, BLOCK_BIT_RECALCULATE); + + // But there is an inherent race condition here, as the block maybe + // became BUSY, just before it was marked as RECALCULATE, so check + // if that is the case! + if (!stepper.is_block_busy(current)) { + // Block is not BUSY, we won the race against the Stepper ISR: + + // NOTE: Entry and exit factors always > 0 by all previous logic operations. + const float current_nominal_speed = SQRT(current->nominal_speed_sqr), + nomr = 1.0f / current_nominal_speed; + calculate_trapezoid_for_block(current, current_entry_speed * nomr, next_entry_speed * nomr); + #if ENABLED(LIN_ADVANCE) + if (current->use_advance_lead) { + const float comp = current->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS]; + current->max_adv_steps = current_nominal_speed * comp; + current->final_adv_steps = next_entry_speed * comp; + } + #endif + } + + // Reset current only to ensure next trapezoid is computed - The + // stepper is free to use the block from now on. + CBI(current->flag, BLOCK_BIT_RECALCULATE); } } @@ -1077,16 +1085,32 @@ void Planner::recalculate_trapezoids() { // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. if (next) { - const float next_nominal_speed = SQRT(next->nominal_speed_sqr), - nomr = 1.0 / next_nominal_speed; - calculate_trapezoid_for_block(next, next_entry_speed * nomr, (MINIMUM_PLANNER_SPEED) * nomr); - #if ENABLED(LIN_ADVANCE) - if (next->use_advance_lead) { - const float comp = next->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS]; - next->max_adv_steps = next_nominal_speed * comp; - next->final_adv_steps = (MINIMUM_PLANNER_SPEED) * comp; - } - #endif + + // Mark the next(last) block as RECALCULATE, to prevent the Stepper ISR running it. + // As the last block is always recalculated here, there is a chance the block isn't + // marked as RECALCULATE yet. That's the reason for the following line. + SBI(next->flag, BLOCK_BIT_RECALCULATE); + + // But there is an inherent race condition here, as the block maybe + // became BUSY, just before it was marked as RECALCULATE, so check + // if that is the case! + if (!stepper.is_block_busy(current)) { + // Block is not BUSY, we won the race against the Stepper ISR: + + const float next_nominal_speed = SQRT(next->nominal_speed_sqr), + nomr = 1.0f / next_nominal_speed; + calculate_trapezoid_for_block(next, next_entry_speed * nomr, float(MINIMUM_PLANNER_SPEED) * nomr); + #if ENABLED(LIN_ADVANCE) + if (next->use_advance_lead) { + const float comp = next->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS]; + next->max_adv_steps = next_nominal_speed * comp; + next->final_adv_steps = (MINIMUM_PLANNER_SPEED) * comp; + } + #endif + } + + // Reset next only to ensure its trapezoid is computed - The stepper is free to use + // the block from now on. CBI(next->flag, BLOCK_BIT_RECALCULATE); } } @@ -1121,7 +1145,7 @@ void Planner::recalculate() { float t = autotemp_min + high * autotemp_factor; t = constrain(t, autotemp_min, autotemp_max); - if (t < oldt) t = t * (1 - (AUTOTEMP_OLDWEIGHT)) + oldt * (AUTOTEMP_OLDWEIGHT); + if (t < oldt) t = t * (1 - float(AUTOTEMP_OLDWEIGHT)) + oldt * float(AUTOTEMP_OLDWEIGHT); oldt = t; thermalManager.setTargetHotend(t, 0); } @@ -1276,7 +1300,7 @@ void Planner::check_axes_activity() { * Return 1.0 with volumetric off or a diameter of 0.0. */ inline float calculate_volumetric_multiplier(const float &diameter) { - return (parser.volumetric_enabled && diameter) ? 1.0 / CIRCLE_AREA(diameter * 0.5) : 1.0; + return (parser.volumetric_enabled && diameter) ? 1.0f / CIRCLE_AREA(diameter * 0.5) : 1.0; } /** @@ -1300,12 +1324,12 @@ void Planner::check_axes_activity() { */ void Planner::calculate_volumetric_for_width_sensor(const int8_t encoded_ratio) { // Reconstitute the nominal/measured ratio - const float nom_meas_ratio = 1.0 + 0.01 * encoded_ratio, + const float nom_meas_ratio = 1 + 0.01f * encoded_ratio, ratio_2 = sq(nom_meas_ratio); volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] = parser.volumetric_enabled - ? ratio_2 / CIRCLE_AREA(filament_width_nominal * 0.5) // Volumetric uses a true volumetric multiplier - : ratio_2; // Linear squares the ratio, which scales the volume + ? ratio_2 / CIRCLE_AREA(filament_width_nominal * 0.5f) // Volumetric uses a true volumetric multiplier + : ratio_2; // Linear squares the ratio, which scales the volume refresh_e_factor(FILAMENT_SENSOR_EXTRUDER_NUM); } @@ -1427,7 +1451,7 @@ void Planner::quick_stop() { if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT(); // Drop all queue entries - block_buffer_planned = block_buffer_head = block_buffer_tail; + block_buffer_nonbusy = block_buffer_planned = block_buffer_head = block_buffer_tail; // Restart the block delay for the first movement - As the queue was // forced to empty, there's no risk the ISR will touch this. @@ -1526,9 +1550,9 @@ bool Planner::_buffer_steps(const int32_t (&target)[XYZE] // Fill the block with the specified movement if (!_populate_block(block, false, target - #if HAS_POSITION_FLOAT - , target_float - #endif + #if HAS_POSITION_FLOAT + , target_float + #endif , fr_mm_s, extruder, millimeters )) { // Movement was not queued, probably because it was too short. @@ -1649,7 +1673,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, if (de < 0) SBI(dm, E_AXIS); const float esteps_float = de * e_factor[extruder]; - const uint32_t esteps = ABS(esteps_float) + 0.5; + const uint32_t esteps = ABS(esteps_float) + 0.5f; // Clear all flags, including the "busy" bit block->flag = 0x00; @@ -1688,10 +1712,16 @@ 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 step_event_count for each + // 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_event_count[i] = mixing_factor[i] * block->step_event_count; + block->mix_steps[i] = mixing_factor[i] * ( + #if ENABLED(LIN_ADVANCE) + esteps + #else + block->step_event_count + #endif + ); #endif #if FAN_COUNT > 0 @@ -1898,18 +1928,19 @@ bool Planner::_populate_block(block_t * const block, bool split_move, else block->millimeters = millimeters; - const float inverse_millimeters = 1.0 / block->millimeters; // Inverse millimeters to remove multiple divides + const float inverse_millimeters = 1.0f / block->millimeters; // Inverse millimeters to remove multiple divides // Calculate inverse time for this move. No divide by zero due to previous checks. // Example: At 120mm/s a 60mm move takes 0.5s. So this will give 2.0. float inverse_secs = fr_mm_s * inverse_millimeters; - const uint8_t moves_queued = movesplanned(); + // Get the number of non busy movements in queue (non busy means that they can be altered) + const uint8_t moves_queued = nonbusy_movesplanned(); // Slow down when the buffer starts to empty, rather than wait at the corner for a buffer refill #if ENABLED(SLOWDOWN) || ENABLED(ULTRA_LCD) || defined(XY_FREQUENCY_LIMIT) // Segment time im micro seconds - uint32_t segment_time_us = LROUND(1000000.0 / inverse_secs); + uint32_t segment_time_us = LROUND(1000000.0f / inverse_secs); #endif #if ENABLED(SLOWDOWN) @@ -1917,7 +1948,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, if (segment_time_us < min_segment_time_us) { // buffer is draining, add extra time. The amount of time added increases if the buffer is still emptied more. const uint32_t nst = segment_time_us + LROUND(2 * (min_segment_time_us - segment_time_us) / moves_queued); - inverse_secs = 1000000.0 / nst; + inverse_secs = 1000000.0f / nst; #if defined(XY_FREQUENCY_LIMIT) || ENABLED(ULTRA_LCD) segment_time_us = nst; #endif @@ -1957,7 +1988,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, while (filwidth_delay_dist >= MMD_MM) filwidth_delay_dist -= MMD_MM; // Convert into an index into the measurement array - filwidth_delay_index[0] = int8_t(filwidth_delay_dist * 0.1); + filwidth_delay_index[0] = int8_t(filwidth_delay_dist * 0.1f); // If the index has changed (must have gone forward)... if (filwidth_delay_index[0] != filwidth_delay_index[1]) { @@ -1973,7 +2004,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, #endif // Calculate and limit speed in mm/sec for each axis - float current_speed[NUM_AXIS], speed_factor = 1.0; // factor <1 decreases speed + float current_speed[NUM_AXIS], speed_factor = 1.0f; // factor <1 decreases speed LOOP_XYZE(i) { const float cs = ABS((current_speed[i] = delta_mm[i] * inverse_secs)); #if ENABLED(DISTINCT_E_FACTORS) @@ -2021,7 +2052,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, #endif // XY_FREQUENCY_LIMIT // Correct the speed - if (speed_factor < 1.0) { + if (speed_factor < 1.0f) { LOOP_XYZE(i) current_speed[i] *= speed_factor; block->nominal_rate *= speed_factor; block->nominal_speed_sqr = block->nominal_speed_sqr * sq(speed_factor); @@ -2056,6 +2087,17 @@ bool Planner::_populate_block(block_t * const block, bool split_move, accel = CEIL((esteps ? acceleration : travel_acceleration) * steps_per_mm); #if ENABLED(LIN_ADVANCE) + + #if ENABLED(JUNCTION_DEVIATION) + #if ENABLED(DISTINCT_E_FACTORS) + #define MAX_E_JERK max_e_jerk[extruder] + #else + #define MAX_E_JERK max_e_jerk + #endif + #else + #define MAX_E_JERK max_jerk[E_AXIS] + #endif + /** * * Use LIN_ADVANCE for blocks if all these are true: @@ -2083,13 +2125,12 @@ bool Planner::_populate_block(block_t * const block, bool split_move, // Check for unusual high e_D ratio to detect if a retract move was combined with the last print move due to min. steps per segment. Never execute this with advance! // This assumes no one will use a retract length of 0mm < retr_length < ~0.2mm and no one will print 100mm wide lines using 3mm filament or 35mm wide lines using 1.75mm filament. - if (block->e_D_ratio > 3.0) + if (block->e_D_ratio > 3.0f) block->use_advance_lead = false; else { - const uint32_t max_accel_steps_per_s2 = max_jerk[E_AXIS] / (extruder_advance_K * block->e_D_ratio) * steps_per_mm; + const uint32_t max_accel_steps_per_s2 = MAX_E_JERK / (extruder_advance_K * block->e_D_ratio) * steps_per_mm; #if ENABLED(LA_DEBUG) - if (accel > max_accel_steps_per_s2) - SERIAL_ECHOLNPGM("Acceleration limited."); + if (accel > max_accel_steps_per_s2) SERIAL_ECHOLNPGM("Acceleration limited."); #endif NOMORE(accel, max_accel_steps_per_s2); } @@ -2118,12 +2159,12 @@ bool Planner::_populate_block(block_t * const block, bool split_move, } block->acceleration_steps_per_s2 = accel; block->acceleration = accel / steps_per_mm; - #if DISABLED(BEZIER_JERK_CONTROL) - block->acceleration_rate = (uint32_t)(accel * (4096.0 * 4096.0 / (HAL_STEPPER_TIMER_RATE))); + #if DISABLED(S_CURVE_ACCELERATION) + block->acceleration_rate = (uint32_t)(accel * (4096.0f * 4096.0f / (STEPPER_TIMER_RATE))); #endif #if ENABLED(LIN_ADVANCE) if (block->use_advance_lead) { - block->advance_speed = (HAL_STEPPER_TIMER_RATE) / (extruder_advance_K * block->e_D_ratio * block->acceleration * axis_steps_per_mm[E_AXIS_N]); + block->advance_speed = (STEPPER_TIMER_RATE) / (extruder_advance_K * block->e_D_ratio * block->acceleration * axis_steps_per_mm[E_AXIS_N]); #if ENABLED(LA_DEBUG) if (extruder_advance_K * block->e_D_ratio * block->acceleration * 2 < SQRT(block->nominal_speed_sqr) * block->e_D_ratio) SERIAL_ECHOLNPGM("More than 2 steps per eISR loop executed."); @@ -2172,21 +2213,13 @@ bool Planner::_populate_block(block_t * const block, bool split_move, already calculated in a different place. */ // Unit vector of previous path line segment - static float previous_unit_vec[ - #if ENABLED(JUNCTION_DEVIATION_INCLUDE_E) - XYZE - #else - XYZ - #endif - ]; + static float previous_unit_vec[XYZE]; float unit_vec[] = { delta_mm[A_AXIS] * inverse_millimeters, delta_mm[B_AXIS] * inverse_millimeters, - delta_mm[C_AXIS] * inverse_millimeters - #if ENABLED(JUNCTION_DEVIATION_INCLUDE_E) - , delta_mm[E_AXIS] * inverse_millimeters - #endif + delta_mm[C_AXIS] * inverse_millimeters, + delta_mm[E_AXIS] * inverse_millimeters }; // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. @@ -2196,31 +2229,38 @@ bool Planner::_populate_block(block_t * const block, bool split_move, float junction_cos_theta = -previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] -previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] -previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] - #if ENABLED(JUNCTION_DEVIATION_INCLUDE_E) - -previous_unit_vec[E_AXIS] * unit_vec[E_AXIS] - #endif + -previous_unit_vec[E_AXIS] * unit_vec[E_AXIS] ; // NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta). - if (junction_cos_theta > 0.999999) { + if (junction_cos_theta > 0.999999f) { // For a 0 degree acute junction, just set minimum junction speed. - vmax_junction_sqr = sq(MINIMUM_PLANNER_SPEED); + vmax_junction_sqr = sq(float(MINIMUM_PLANNER_SPEED)); } else { - NOLESS(junction_cos_theta, -0.999999); // Check for numerical round-off to avoid divide by zero. - const float sin_theta_d2 = SQRT(0.5 * (1.0 - junction_cos_theta)); // Trig half angle identity. Always positive. + NOLESS(junction_cos_theta, -0.999999f); // Check for numerical round-off to avoid divide by zero. - // TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the - // two junctions. However, this shouldn't be a significant problem except in extreme circumstances. - vmax_junction_sqr = (JUNCTION_ACCELERATION_FACTOR * JUNCTION_DEVIATION_FACTOR * sin_theta_d2) / (1.0 - sin_theta_d2); - if (block->millimeters < 1.0) { + // Convert delta vector to unit vector + float junction_unit_vec[XYZE] = { + unit_vec[X_AXIS] - previous_unit_vec[X_AXIS], + unit_vec[Y_AXIS] - previous_unit_vec[Y_AXIS], + unit_vec[Z_AXIS] - previous_unit_vec[Z_AXIS], + unit_vec[E_AXIS] - previous_unit_vec[E_AXIS] + }; + normalize_junction_vector(junction_unit_vec); + + const float junction_acceleration = limit_value_by_axis_maximum(block->acceleration, junction_unit_vec), + sin_theta_d2 = SQRT(0.5f * (1.0f - junction_cos_theta)); // Trig half angle identity. Always positive. + + vmax_junction_sqr = (junction_acceleration * junction_deviation_mm * sin_theta_d2) / (1.0f - sin_theta_d2); + if (block->millimeters < 1) { // Fast acos approximation, minus the error bar to be safe - const float junction_theta = (RADIANS(-40) * sq(junction_cos_theta) - RADIANS(50)) * junction_cos_theta + RADIANS(90) - 0.18; + const float junction_theta = (RADIANS(-40) * sq(junction_cos_theta) - RADIANS(50)) * junction_cos_theta + RADIANS(90) - 0.18f; // If angle is greater than 135 degrees (octagon), find speed for approximate arc if (junction_theta > RADIANS(135)) { - const float limit_sqr = block->millimeters / (RADIANS(180) - junction_theta) * JUNCTION_ACCELERATION_FACTOR; + const float limit_sqr = block->millimeters / (RADIANS(180) - junction_theta) * junction_acceleration; NOMORE(vmax_junction_sqr, limit_sqr); } } @@ -2230,7 +2270,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, vmax_junction_sqr = MIN3(vmax_junction_sqr, block->nominal_speed_sqr, previous_nominal_speed_sqr); } else // Init entry speed to zero. Assume it starts from rest. Planner will correct this later. - vmax_junction_sqr = 0.0; + vmax_junction_sqr = 0; COPY(previous_unit_vec, unit_vec); @@ -2321,11 +2361,11 @@ bool Planner::_populate_block(block_t * const block, bool split_move, block->max_entry_speed_sqr = vmax_junction_sqr; // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. - const float v_allowable_sqr = max_allowable_speed_sqr(-block->acceleration, sq(MINIMUM_PLANNER_SPEED), block->millimeters); + const float v_allowable_sqr = max_allowable_speed_sqr(-block->acceleration, sq(float(MINIMUM_PLANNER_SPEED)), block->millimeters); // If we are trying to add a split block, start with the // max. allowed speed to avoid an interrupted first move. - block->entry_speed_sqr = !split_move ? sq(MINIMUM_PLANNER_SPEED) : MIN(vmax_junction_sqr, v_allowable_sqr); + block->entry_speed_sqr = !split_move ? sq(float(MINIMUM_PLANNER_SPEED)) : MIN(vmax_junction_sqr, v_allowable_sqr); // Initialize planner efficiency flags // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. @@ -2483,10 +2523,7 @@ bool Planner::buffer_segment(const float &a, const float &b, const float &c, con void Planner::_set_position_mm(const float &a, const float &b, const float &c, const float &e) { #if ENABLED(DISTINCT_E_FACTORS) - #define _EINDEX (E_AXIS + active_extruder) last_extruder = active_extruder; - #else - #define _EINDEX E_AXIS #endif position[A_AXIS] = LROUND(a * axis_steps_per_mm[A_AXIS]), position[B_AXIS] = LROUND(b * axis_steps_per_mm[B_AXIS]), @@ -2498,9 +2535,13 @@ void Planner::_set_position_mm(const float &a, const float &b, const float &c, c position_float[C_AXIS] = c; position_float[E_AXIS] = e; #endif - previous_nominal_speed_sqr = 0.0; // Resets planner junction speeds. Assumes start from rest. - ZERO(previous_speed); - buffer_sync_block(); + if (has_blocks_queued()) { + //previous_nominal_speed_sqr = 0.0; // Reset planner junction speeds. Assume start from rest. + //ZERO(previous_speed); + buffer_sync_block(); + } + else + stepper.set_position(position[A_AXIS], position[B_AXIS], position[C_AXIS], position[E_AXIS]); } void Planner::set_position_mm_kinematic(const float (&cart)[XYZE]) { @@ -2532,8 +2573,12 @@ void Planner::set_position_mm(const AxisEnum axis, const float &v) { #if HAS_POSITION_FLOAT position_float[axis] = v; #endif - previous_speed[axis] = 0.0; - buffer_sync_block(); + if (has_blocks_queued()) { + //previous_speed[axis] = 0.0; + buffer_sync_block(); + } + else + stepper.set_position(axis, position[axis]); } // Recalculate the steps/s^2 acceleration rates, based on the mm/s^2 @@ -2549,11 +2594,14 @@ void Planner::reset_acceleration_rates() { if (AXIS_CONDITION) NOLESS(highest_rate, max_acceleration_steps_per_s2[i]); } cutoff_long = 4294967295UL / highest_rate; // 0xFFFFFFFFUL + #if ENABLED(JUNCTION_DEVIATION) && ENABLED(LIN_ADVANCE) + recalculate_max_e_jerk(); + #endif } // Recalculate position, steps_to_mm if axis_steps_per_mm changes! void Planner::refresh_positioning() { - LOOP_XYZE_N(i) steps_to_mm[i] = 1.0 / axis_steps_per_mm[i]; + LOOP_XYZE_N(i) steps_to_mm[i] = 1.0f / axis_steps_per_mm[i]; set_position_mm_kinematic(current_position); reset_acceleration_rates(); } diff --git a/Marlin/planner.h b/Marlin/planner.h index 1c4f2a1e8e..fd06be588c 100644 --- a/Marlin/planner.h +++ b/Marlin/planner.h @@ -49,9 +49,6 @@ enum BlockFlagBit : char { // from a safe speed (in consideration of jerking from zero speed). BLOCK_BIT_NOMINAL_LENGTH, - // The block is busy, being interpreted by the stepper ISR - BLOCK_BIT_BUSY, - // The block is segment 2+ of a longer move BLOCK_BIT_CONTINUED, @@ -62,7 +59,6 @@ enum BlockFlagBit : char { enum BlockFlag : char { BLOCK_FLAG_RECALCULATE = _BV(BLOCK_BIT_RECALCULATE), BLOCK_FLAG_NOMINAL_LENGTH = _BV(BLOCK_BIT_NOMINAL_LENGTH), - BLOCK_FLAG_BUSY = _BV(BLOCK_BIT_BUSY), BLOCK_FLAG_CONTINUED = _BV(BLOCK_BIT_CONTINUED), BLOCK_FLAG_SYNC_POSITION = _BV(BLOCK_BIT_SYNC_POSITION) }; @@ -78,7 +74,7 @@ enum BlockFlag : char { */ typedef struct { - uint8_t flag; // Block flags (See BlockFlag enum above) + volatile uint8_t flag; // Block flags (See BlockFlag enum above) - Modified by ISR and main thread! // Fields used by the motion planner to manage acceleration float nominal_speed_sqr, // The nominal speed for this block in (mm/sec)^2 @@ -103,18 +99,18 @@ typedef struct { uint8_t active_extruder; // The extruder to move (if E move) #if ENABLED(MIXING_EXTRUDER) - uint32_t mix_event_count[MIXING_STEPPERS]; // Scaled step_event_count for the mixing steppers + uint32_t mix_steps[MIXING_STEPPERS]; // Scaled steps[E_AXIS] for the mixing steppers #endif // Settings for the trapezoid generator uint32_t accelerate_until, // The index of the step event on which to stop acceleration decelerate_after; // The index of the step event on which to start decelerating - #if ENABLED(BEZIER_JERK_CONTROL) - uint32_t cruise_rate; // The actual cruise rate to use, between end of the acceleration phase and start of deceleration phase - uint32_t acceleration_time, // Acceleration time and deceleration time in STEP timer counts - deceleration_time; - uint32_t acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used + #if ENABLED(S_CURVE_ACCELERATION) + uint32_t cruise_rate, // The actual cruise rate to use, between end of the acceleration phase and start of deceleration phase + acceleration_time, // Acceleration time and deceleration time in STEP timer counts + deceleration_time, + acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used deceleration_time_inverse; #else uint32_t acceleration_rate; // The acceleration rate used for acceleration calculation @@ -125,7 +121,7 @@ typedef struct { // Advance extrusion #if ENABLED(LIN_ADVANCE) bool use_advance_lead; - uint16_t advance_speed, // Timer value for extruder speed offset + uint16_t advance_speed, // STEP timer value for extruder speed offset ISR max_adv_steps, // max. advance steps to get cruising speed pressure (not always nominal_speed!) final_adv_steps; // advance steps due to exit speed float e_D_ratio; @@ -148,7 +144,7 @@ typedef struct { } block_t; -#define HAS_POSITION_FLOAT (ENABLED(LIN_ADVANCE) || ENABLED(SCARA_FEEDRATE_SCALING)) +#define HAS_POSITION_FLOAT (ENABLED(LIN_ADVANCE) || HAS_FEEDRATE_SCALING) #define BLOCK_MOD(n) ((n)&(BLOCK_BUFFER_SIZE-1)) @@ -170,11 +166,12 @@ class Planner { */ static block_t block_buffer[BLOCK_BUFFER_SIZE]; static volatile uint8_t block_buffer_head, // Index of the next block to be pushed + block_buffer_nonbusy, // Index of the first non busy block + block_buffer_planned, // Index of the optimally planned block block_buffer_tail; // Index of the busy block, if any static uint16_t cleaning_buffer_counter; // A counter to disable queuing of blocks - static uint8_t delay_before_delivering, // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks - block_buffer_planned; // Index of the optimally planned block - + static uint8_t delay_before_delivering; // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks + #if ENABLED(DISTINCT_E_FACTORS) static uint8_t last_extruder; // Respond to extruder change @@ -191,19 +188,30 @@ class Planner { // May be auto-adjusted by a filament width sensor #endif - static float max_feedrate_mm_s[XYZE_N], // Max speeds in mm per second - axis_steps_per_mm[XYZE_N], - steps_to_mm[XYZE_N]; - static uint32_t max_acceleration_steps_per_s2[XYZE_N], - max_acceleration_mm_per_s2[XYZE_N]; // Use M201 to override + static uint32_t max_acceleration_mm_per_s2[XYZE_N], // (mm/s^2) M201 XYZE + max_acceleration_steps_per_s2[XYZE_N], // (steps/s^2) Derived from mm_per_s2 + min_segment_time_us; // (µs) M205 B + static float max_feedrate_mm_s[XYZE_N], // (mm/s) M203 XYZE - Max speeds + axis_steps_per_mm[XYZE_N], // (steps) M92 XYZE - Steps per millimeter + steps_to_mm[XYZE_N], // (mm) Millimeters per step + min_feedrate_mm_s, // (mm/s) M205 S - Minimum linear feedrate + acceleration, // (mm/s^2) M204 S - Normal acceleration. DEFAULT ACCELERATION for all printing moves. + retract_acceleration, // (mm/s^2) M204 R - Retract acceleration. Filament pull-back and push-forward while standing still in the other axes + travel_acceleration, // (mm/s^2) M204 T - Travel acceleration. DEFAULT ACCELERATION for all NON printing moves. + min_travel_feedrate_mm_s; // (mm/s) M205 T - Minimum travel feedrate - static uint32_t min_segment_time_us; // Use 'M205 B<µs>' to override - static float min_feedrate_mm_s, - acceleration, // Normal acceleration mm/s^2 DEFAULT ACCELERATION for all printing moves. M204 SXXXX - retract_acceleration, // Retract acceleration mm/s^2 filament pull-back and push-forward while standing still in the other axes M204 TXXXX - travel_acceleration, // Travel acceleration mm/s^2 DEFAULT ACCELERATION for all NON printing moves. M204 MXXXX - max_jerk[XYZE], // The largest speed change requiring no acceleration - min_travel_feedrate_mm_s; + #if ENABLED(JUNCTION_DEVIATION) + static float junction_deviation_mm; // (mm) M205 J + #if ENABLED(LIN_ADVANCE) + #if ENABLED(DISTINCT_E_FACTORS) + static float max_e_jerk[EXTRUDERS]; // Calculated from junction_deviation_mm + #else + static float max_e_jerk; + #endif + #endif + #else + static float max_jerk[XYZE]; // (mm/s^2) M205 XYZE - The largest speed change requiring no acceleration. + #endif #if HAS_LEVELING static bool leveling_active; // Flag that bed leveling is enabled @@ -311,7 +319,7 @@ class Planner { static void refresh_positioning(); FORCE_INLINE static void refresh_e_factor(const uint8_t e) { - e_factor[e] = (flow_percentage[e] * 0.01 + e_factor[e] = (flow_percentage[e] * 0.01f #if DISABLED(NO_VOLUMETRICS) * volumetric_multiplier[e] #endif @@ -349,19 +357,19 @@ class Planner { * Returns 0.0 if Z is past the specified 'Fade Height'. */ inline static float fade_scaling_factor_for_z(const float &rz) { - static float z_fade_factor = 1.0; + static float z_fade_factor = 1; if (z_fade_height) { - if (rz >= z_fade_height) return 0.0; + if (rz >= z_fade_height) return 0; if (last_fade_z != rz) { last_fade_z = rz; - z_fade_factor = 1.0 - rz * inverse_z_fade_height; + z_fade_factor = 1 - rz * inverse_z_fade_height; } return z_fade_factor; } - return 1.0; + return 1; } - FORCE_INLINE static void force_fade_recalc() { last_fade_z = -999.999; } + FORCE_INLINE static void force_fade_recalc() { last_fade_z = -999.999f; } FORCE_INLINE static void set_z_fade_height(const float &zfh) { z_fade_height = zfh > 0 ? zfh : 0; @@ -377,7 +385,7 @@ class Planner { FORCE_INLINE static float fade_scaling_factor_for_z(const float &rz) { UNUSED(rz); - return 1.0; + return 1; } FORCE_INLINE static bool leveling_active_at_z(const float &rz) { UNUSED(rz); return true; } @@ -428,11 +436,14 @@ class Planner { #define ARG_Z const float &rz #endif - // Number of moves currently in the planner + // Number of moves currently in the planner including the busy block, if any FORCE_INLINE static uint8_t movesplanned() { return BLOCK_MOD(block_buffer_head - block_buffer_tail); } + // Number of nonbusy moves currently in the planner + FORCE_INLINE static uint8_t nonbusy_movesplanned() { return BLOCK_MOD(block_buffer_head - block_buffer_nonbusy); } + // Remove all blocks from the buffer - FORCE_INLINE static void clear_block_buffer() { block_buffer_head = block_buffer_tail = 0; } + FORCE_INLINE static void clear_block_buffer() { block_buffer_nonbusy = block_buffer_planned = block_buffer_head = block_buffer_tail = 0; } // Check if movement queue is full FORCE_INLINE static bool is_full() { return block_buffer_tail == next_block_index(block_buffer_head); } @@ -634,7 +645,7 @@ class Planner { static block_t* get_current_block() { // Get the number of moves in the planner queue so far - uint8_t nr_moves = movesplanned(); + const uint8_t nr_moves = movesplanned(); // If there are any moves queued ... if (nr_moves) { @@ -658,8 +669,14 @@ class Planner { block_buffer_runtime_us -= block->segment_time_us; // We can't be sure how long an active block will take, so don't count it. #endif - // Mark the block as busy, so the planner does not attempt to replan it - SBI(block->flag, BLOCK_BIT_BUSY); + // As this block is busy, advance the nonbusy block pointer + block_buffer_nonbusy = next_block_index(block_buffer_tail); + + // Push block_buffer_planned pointer, if encountered. + if (block_buffer_tail == block_buffer_planned) + block_buffer_planned = block_buffer_nonbusy; + + // Return the block return block; } @@ -677,27 +694,18 @@ class Planner { * NB: There MUST be a current block to call this function!! */ FORCE_INLINE static void discard_current_block() { - if (has_blocks_queued()) { // Discard non-empty buffer. - uint8_t block_index = next_block_index( block_buffer_tail ); - - // Push block_buffer_planned pointer, if encountered. - if (!has_blocks_queued()) block_buffer_planned = block_index; - - block_buffer_tail = block_index; - } + if (has_blocks_queued()) + block_buffer_tail = next_block_index(block_buffer_tail); } #if ENABLED(ULTRA_LCD) static uint16_t block_buffer_runtime() { - // Protect the access to the variable. Only required for AVR, as - // any 32bit CPU offers atomic access to 32bit variables bool was_enabled = STEPPER_ISR_ENABLED(); if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT(); millis_t bbru = block_buffer_runtime_us; - // Reenable Stepper ISR if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT(); // To translate µs to ms a division by 1000 would be required. @@ -710,14 +718,11 @@ class Planner { } static void clear_block_buffer_runtime() { - // Protect the access to the variable. Only required for AVR, as - // any 32bit CPU offers atomic access to 32bit variables bool was_enabled = STEPPER_ISR_ENABLED(); if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT(); block_buffer_runtime_us = 0; - // Reenable Stepper ISR if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT(); } @@ -730,6 +735,20 @@ class Planner { static void autotemp_M104_M109(); #endif + #if ENABLED(JUNCTION_DEVIATION) + FORCE_INLINE static void recalculate_max_e_jerk() { + #define GET_MAX_E_JERK(N) SQRT(SQRT(0.5) * junction_deviation_mm * (N) * RECIPROCAL(1.0 - SQRT(0.5))) + #if ENABLED(LIN_ADVANCE) + #if ENABLED(DISTINCT_E_FACTORS) + for (uint8_t i = 0; i < EXTRUDERS; i++) + max_e_jerk[i] = GET_MAX_E_JERK(max_acceleration_mm_per_s2[E_AXIS + i]); + #else + max_e_jerk = GET_MAX_E_JERK(max_acceleration_mm_per_s2[E_AXIS]); + #endif + #endif + } + #endif + private: /** @@ -769,7 +788,7 @@ class Planner { return target_velocity_sqr - 2 * accel * distance; } - #if ENABLED(BEZIER_JERK_CONTROL) + #if ENABLED(S_CURVE_ACCELERATION) /** * Calculate the speed reached given initial speed, acceleration and distance */ @@ -790,6 +809,23 @@ class Planner { static void recalculate(); + #if ENABLED(JUNCTION_DEVIATION) + + FORCE_INLINE static void normalize_junction_vector(float (&vector)[XYZE]) { + float magnitude_sq = 0; + LOOP_XYZE(idx) if (vector[idx]) magnitude_sq += sq(vector[idx]); + const float inv_magnitude = RSQRT(magnitude_sq); + LOOP_XYZE(idx) vector[idx] *= inv_magnitude; + } + + FORCE_INLINE static float limit_value_by_axis_maximum(const float &max_value, float (&unit_vec)[XYZE]) { + float limit_value = max_value; + LOOP_XYZE(idx) if (unit_vec[idx]) // Avoid divide by zero + NOMORE(limit_value, ABS(max_acceleration_mm_per_s2[idx] / unit_vec[idx])); + return limit_value; + } + + #endif // JUNCTION_DEVIATION }; #define PLANNER_XY_FEEDRATE() (MIN(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS])) diff --git a/Marlin/planner_bezier.cpp b/Marlin/planner_bezier.cpp index 5ed7c043ce..fdb4bab86b 100644 --- a/Marlin/planner_bezier.cpp +++ b/Marlin/planner_bezier.cpp @@ -37,12 +37,12 @@ #include "Marlin.h" // See the meaning in the documentation of cubic_b_spline(). -#define MIN_STEP 0.002 -#define MAX_STEP 0.1 -#define SIGMA 0.1 +#define MIN_STEP 0.002f +#define MAX_STEP 0.1f +#define SIGMA 0.1f // Compute the linear interpolation between two real numbers. -inline static float interp(float a, float b, float t) { return (1.0 - t) * a + t * b; } +inline static float interp(float a, float b, float t) { return (1.0f - t) * a + t * b; } /** * Compute a Bézier curve using the De Casteljau's algorithm (see @@ -111,7 +111,7 @@ void cubic_b_spline(const float position[NUM_AXIS], const float target[NUM_AXIS] first1 = position[Y_AXIS] + offset[1], second0 = target[X_AXIS] + offset[2], second1 = target[Y_AXIS] + offset[3]; - float t = 0.0; + float t = 0; float bez_target[4]; bez_target[X_AXIS] = position[X_AXIS]; @@ -120,7 +120,7 @@ void cubic_b_spline(const float position[NUM_AXIS], const float target[NUM_AXIS] millis_t next_idle_ms = millis() + 200UL; - while (t < 1.0) { + while (t < 1) { thermalManager.manage_heater(); millis_t now = millis(); @@ -133,16 +133,16 @@ void cubic_b_spline(const float position[NUM_AXIS], const float target[NUM_AXIS] // close to a linear interpolation. bool did_reduce = false; float new_t = t + step; - NOMORE(new_t, 1.0); + NOMORE(new_t, 1); float new_pos0 = eval_bezier(position[X_AXIS], first0, second0, target[X_AXIS], new_t), new_pos1 = eval_bezier(position[Y_AXIS], first1, second1, target[Y_AXIS], new_t); for (;;) { if (new_t - t < (MIN_STEP)) break; - const float candidate_t = 0.5 * (t + new_t), + const float candidate_t = 0.5f * (t + new_t), candidate_pos0 = eval_bezier(position[X_AXIS], first0, second0, target[X_AXIS], candidate_t), candidate_pos1 = eval_bezier(position[Y_AXIS], first1, second1, target[Y_AXIS], candidate_t), - interp_pos0 = 0.5 * (bez_target[X_AXIS] + new_pos0), - interp_pos1 = 0.5 * (bez_target[Y_AXIS] + new_pos1); + interp_pos0 = 0.5f * (bez_target[X_AXIS] + new_pos0), + interp_pos1 = 0.5f * (bez_target[Y_AXIS] + new_pos1); if (dist1(candidate_pos0, candidate_pos1, interp_pos0, interp_pos1) <= (SIGMA)) break; new_t = candidate_t; new_pos0 = candidate_pos0; @@ -153,12 +153,12 @@ void cubic_b_spline(const float position[NUM_AXIS], const float target[NUM_AXIS] // If we did not reduce the step, maybe we should enlarge it. if (!did_reduce) for (;;) { if (new_t - t > MAX_STEP) break; - const float candidate_t = t + 2.0 * (new_t - t); - if (candidate_t >= 1.0) break; + const float candidate_t = t + 2 * (new_t - t); + if (candidate_t >= 1) break; const float candidate_pos0 = eval_bezier(position[X_AXIS], first0, second0, target[X_AXIS], candidate_t), candidate_pos1 = eval_bezier(position[Y_AXIS], first1, second1, target[Y_AXIS], candidate_t), - interp_pos0 = 0.5 * (bez_target[X_AXIS] + candidate_pos0), - interp_pos1 = 0.5 * (bez_target[Y_AXIS] + candidate_pos1); + interp_pos0 = 0.5f * (bez_target[X_AXIS] + candidate_pos0), + interp_pos1 = 0.5f * (bez_target[Y_AXIS] + candidate_pos1); if (dist1(new_pos0, new_pos1, interp_pos0, interp_pos1) > (SIGMA)) break; new_t = candidate_t; new_pos0 = candidate_pos0; diff --git a/Marlin/power_loss_recovery.cpp b/Marlin/power_loss_recovery.cpp index 023c1c25cb..8534fd8fe0 100644 --- a/Marlin/power_loss_recovery.cpp +++ b/Marlin/power_loss_recovery.cpp @@ -42,58 +42,76 @@ job_recovery_info_t job_recovery_info; JobRecoveryPhase job_recovery_phase = JOB_RECOVERY_IDLE; uint8_t job_recovery_commands_count; //=0 char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE]; - // Extern -extern uint8_t commands_in_queue, cmd_queue_index_r; - -// Private -static char sd_filename[MAXPATHNAMELENGTH]; +extern uint8_t active_extruder, commands_in_queue, cmd_queue_index_r; #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) void debug_print_job_recovery(const bool recovery) { - SERIAL_PROTOCOLPAIR("valid_head:", (int)job_recovery_info.valid_head); - SERIAL_PROTOCOLLNPAIR(" valid_foot:", (int)job_recovery_info.valid_foot); + SERIAL_PROTOCOLLNPGM("---- Job Recovery Info ----"); + SERIAL_PROTOCOLPAIR("valid_head:", int(job_recovery_info.valid_head)); + SERIAL_PROTOCOLLNPAIR(" valid_foot:", int(job_recovery_info.valid_foot)); if (job_recovery_info.valid_head) { if (job_recovery_info.valid_head == job_recovery_info.valid_foot) { - SERIAL_PROTOCOLPGM("current_position"); - LOOP_XYZE(i) SERIAL_PROTOCOLPAIR(": ", job_recovery_info.current_position[i]); + SERIAL_PROTOCOLPGM("current_position: "); + LOOP_XYZE(i) { + SERIAL_PROTOCOL(job_recovery_info.current_position[i]); + if (i < E_AXIS) SERIAL_CHAR(','); + } SERIAL_EOL(); SERIAL_PROTOCOLLNPAIR("feedrate: ", job_recovery_info.feedrate); - SERIAL_PROTOCOLPGM("target_temperature"); - HOTEND_LOOP() SERIAL_PROTOCOLPAIR(": ", job_recovery_info.target_temperature[e]); - SERIAL_EOL(); - SERIAL_PROTOCOLPGM("fanSpeeds"); - for(uint8_t i = 0; i < FAN_COUNT; i++) SERIAL_PROTOCOLPAIR(": ", job_recovery_info.fanSpeeds[i]); + + #if HOTENDS > 1 + SERIAL_PROTOCOLLNPAIR("active_hotend: ", int(job_recovery_info.active_hotend)); + #endif + + SERIAL_PROTOCOLPGM("target_temperature: "); + HOTEND_LOOP() { + SERIAL_PROTOCOL(job_recovery_info.target_temperature[e]); + if (e < HOTENDS - 1) SERIAL_CHAR(','); + } SERIAL_EOL(); + + #if HAS_HEATED_BED + SERIAL_PROTOCOLLNPAIR("target_temperature_bed: ", job_recovery_info.target_temperature_bed); + #endif + + #if FAN_COUNT + SERIAL_PROTOCOLPGM("fanSpeeds: "); + for (int8_t i = 0; i < FAN_COUNT; i++) { + SERIAL_PROTOCOL(job_recovery_info.fanSpeeds[i]); + if (i < FAN_COUNT - 1) SERIAL_CHAR(','); + } + SERIAL_EOL(); + #endif + #if HAS_LEVELING SERIAL_PROTOCOLPAIR("leveling: ", int(job_recovery_info.leveling)); SERIAL_PROTOCOLLNPAIR(" fade: ", int(job_recovery_info.fade)); #endif - #if HAS_HEATED_BED - SERIAL_PROTOCOLLNPAIR("target_temperature_bed: ", job_recovery_info.target_temperature_bed); - #endif - SERIAL_PROTOCOLLNPAIR("cmd_queue_index_r: ", job_recovery_info.cmd_queue_index_r); - SERIAL_PROTOCOLLNPAIR("commands_in_queue: ", job_recovery_info.commands_in_queue); + SERIAL_PROTOCOLLNPAIR("cmd_queue_index_r: ", int(job_recovery_info.cmd_queue_index_r)); + SERIAL_PROTOCOLLNPAIR("commands_in_queue: ", int(job_recovery_info.commands_in_queue)); if (recovery) for (uint8_t i = 0; i < job_recovery_commands_count; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_commands[i]); else for (uint8_t i = 0; i < job_recovery_info.commands_in_queue; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_info.command_queue[i]); - SERIAL_PROTOCOLLNPAIR("sd_filename: ", sd_filename); + SERIAL_PROTOCOLLNPAIR("sd_filename: ", job_recovery_info.sd_filename); SERIAL_PROTOCOLLNPAIR("sdpos: ", job_recovery_info.sdpos); SERIAL_PROTOCOLLNPAIR("print_job_elapsed: ", job_recovery_info.print_job_elapsed); } else SERIAL_PROTOCOLLNPGM("INVALID DATA"); } + SERIAL_PROTOCOLLNPGM("---------------------------"); } #endif // DEBUG_POWER_LOSS_RECOVERY /** - * Check for Print Job Recovery - * If the file has a saved state, populate the job_recovery_commands queue + * Check for Print Job Recovery during setup() + * + * If a saved state exists, populate job_recovery_commands with + * commands to restore the machine state and continue the file. */ -void do_print_job_recovery() { - //if (job_recovery_commands_count > 0) return; +void check_print_job_recovery() { memset(&job_recovery_info, 0, sizeof(job_recovery_info)); ZERO(job_recovery_commands); @@ -102,7 +120,7 @@ void do_print_job_recovery() { if (card.cardOK) { #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) - SERIAL_PROTOCOLLNPAIR("Init job recovery info. Size: ", (int)sizeof(job_recovery_info)); + SERIAL_PROTOCOLLNPAIR("Init job recovery info. Size: ", int(sizeof(job_recovery_info))); #endif if (card.jobRecoverFileExists()) { @@ -122,7 +140,9 @@ void do_print_job_recovery() { strcpy_P(job_recovery_commands[ind++], PSTR("G92.0 Z0")); // Ensure Z is equal to 0 strcpy_P(job_recovery_commands[ind++], PSTR("G1 Z2")); // Raise Z by 2mm (we hope!) strcpy_P(job_recovery_commands[ind++], PSTR("G28 R0" - #if !IS_KINEMATIC + #if ENABLED(MARLIN_DEV_MODE) + " S" + #elif !IS_KINEMATIC " X Y" // Home X and Y for Cartesian #endif )); @@ -130,10 +150,12 @@ void do_print_job_recovery() { char str_1[16], str_2[16]; #if HAS_LEVELING - // Restore leveling state before G92 sets Z - // This ensures the steppers correspond to the native Z - dtostrf(job_recovery_info.fade, 1, 1, str_1); - sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), str_1); + if (job_recovery_info.fade || job_recovery_info.leveling) { + // Restore leveling state before G92 sets Z + // This ensures the steppers correspond to the native Z + dtostrf(job_recovery_info.fade, 1, 1, str_1); + sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), str_1); + } #endif dtostrf(job_recovery_info.current_position[Z_AXIS] + 2, 1, 3, str_1); @@ -145,23 +167,21 @@ void do_print_job_recovery() { ); sprintf_P(job_recovery_commands[ind++], PSTR("G92.0 Z%s E%s"), str_1, str_2); // Current Z + 2 and E - strcpy_P(job_recovery_commands[ind++], PSTR("M117 Continuing...")); - - uint8_t r = job_recovery_info.cmd_queue_index_r; - while (job_recovery_info.commands_in_queue) { + uint8_t r = job_recovery_info.cmd_queue_index_r, c = job_recovery_info.commands_in_queue; + while (c--) { strcpy(job_recovery_commands[ind++], job_recovery_info.command_queue[r]); - job_recovery_info.commands_in_queue--; r = (r + 1) % BUFSIZE; } + if (job_recovery_info.sd_filename[0] == '/') job_recovery_info.sd_filename[0] = ' '; + sprintf_P(job_recovery_commands[ind++], PSTR("M23 %s"), job_recovery_info.sd_filename); + sprintf_P(job_recovery_commands[ind++], PSTR("M24 S%ld T%ld"), job_recovery_info.sdpos, job_recovery_info.print_job_elapsed); + job_recovery_commands_count = ind; #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) debug_print_job_recovery(true); #endif - - card.openFile(sd_filename, true); - card.setIndex(job_recovery_info.sdpos); } else { if (job_recovery_info.valid_head != job_recovery_info.valid_foot) @@ -173,7 +193,7 @@ void do_print_job_recovery() { } /** - * Save the current machine state to the "bin" file + * Save the current machine state to the power-loss recovery file */ void save_job_recovery_info() { #if SAVE_INFO_INTERVAL_MS > 0 @@ -201,11 +221,20 @@ void save_job_recovery_info() { // Machine state COPY(job_recovery_info.current_position, current_position); job_recovery_info.feedrate = feedrate_mm_s; + + #if HOTENDS > 1 + job_recovery_info.active_hotend = active_extruder; + #endif + COPY(job_recovery_info.target_temperature, thermalManager.target_temperature); + #if HAS_HEATED_BED job_recovery_info.target_temperature_bed = thermalManager.target_temperature_bed; #endif - COPY(job_recovery_info.fanSpeeds, fanSpeeds); + + #if FAN_COUNT + COPY(job_recovery_info.fanSpeeds, fanSpeeds); + #endif #if HAS_LEVELING job_recovery_info.leveling = planner.leveling_active; @@ -224,14 +253,14 @@ void save_job_recovery_info() { COPY(job_recovery_info.command_queue, command_queue); // Elapsed print job time - job_recovery_info.print_job_elapsed = print_job_timer.duration() * 1000UL; + job_recovery_info.print_job_elapsed = print_job_timer.duration(); // SD file position - card.getAbsFilename(sd_filename); + card.getAbsFilename(job_recovery_info.sd_filename); job_recovery_info.sdpos = card.getIndex(); #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) - SERIAL_PROTOCOLLNPGM("Saving job_recovery_info"); + SERIAL_PROTOCOLLNPGM("Saving..."); debug_print_job_recovery(false); #endif diff --git a/Marlin/power_loss_recovery.h b/Marlin/power_loss_recovery.h index f693b5f9ba..5f25d2c5b3 100644 --- a/Marlin/power_loss_recovery.h +++ b/Marlin/power_loss_recovery.h @@ -40,13 +40,21 @@ typedef struct { // Machine state float current_position[NUM_AXIS], feedrate; - int16_t target_temperature[HOTENDS], - fanSpeeds[FAN_COUNT]; + + #if HOTENDS > 1 + uint8_t active_hotend; + #endif + + int16_t target_temperature[HOTENDS]; #if HAS_HEATED_BED int16_t target_temperature_bed; #endif + #if FAN_COUNT + int16_t fanSpeeds[FAN_COUNT]; + #endif + #if HAS_LEVELING bool leveling; float fade; @@ -56,7 +64,8 @@ typedef struct { uint8_t cmd_queue_index_r, commands_in_queue; char command_queue[BUFSIZE][MAX_CMD_SIZE]; - // SD File position + // SD Filename and position + char sd_filename[MAXPATHNAMELENGTH]; uint32_t sdpos; // Job elapsed time @@ -70,20 +79,21 @@ extern job_recovery_info_t job_recovery_info; enum JobRecoveryPhase : unsigned char { JOB_RECOVERY_IDLE, JOB_RECOVERY_MAYBE, - JOB_RECOVERY_YES + JOB_RECOVERY_YES, + JOB_RECOVERY_DONE }; extern JobRecoveryPhase job_recovery_phase; #if HAS_LEVELING - #define APPEND_CMD_COUNT 7 + #define APPEND_CMD_COUNT 9 #else - #define APPEND_CMD_COUNT 5 + #define APPEND_CMD_COUNT 7 #endif extern char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE]; extern uint8_t job_recovery_commands_count; -void do_print_job_recovery(); +void check_print_job_recovery(); void save_job_recovery_info(); #endif // _POWER_LOSS_RECOVERY_H_ diff --git a/Marlin/printcounter.cpp b/Marlin/printcounter.cpp index 25212cde4e..d1de7d1754 100644 --- a/Marlin/printcounter.cpp +++ b/Marlin/printcounter.cpp @@ -60,7 +60,7 @@ millis_t PrintCounter::deltaDuration() { return lastDuration - tmp; } -void PrintCounter::incFilamentUsed(double const &amount) { +void PrintCounter::incFilamentUsed(float const &amount) { #if ENABLED(DEBUG_PRINTCOUNTER) debug(PSTR("incFilamentUsed")); #endif diff --git a/Marlin/printcounter.h b/Marlin/printcounter.h index 299405a6b4..848d9715b8 100644 --- a/Marlin/printcounter.h +++ b/Marlin/printcounter.h @@ -31,13 +31,13 @@ #include "stopwatch.h" #include -struct printStatistics { // 16 bytes (20 with real doubles) +struct printStatistics { // 16 bytes //const uint8_t magic; // Magic header, it will always be 0x16 uint16_t totalPrints; // Number of prints uint16_t finishedPrints; // Number of complete prints uint32_t printTime; // Accumulated printing time uint32_t longestPrint; // Longest successful print job - double filamentUsed; // Accumulated filament consumed in mm + float filamentUsed; // Accumulated filament consumed in mm }; class PrintCounter: public Stopwatch { @@ -122,7 +122,7 @@ class PrintCounter: public Stopwatch { * * @param amount The amount of filament used in mm */ - static void incFilamentUsed(double const &amount); + static void incFilamentUsed(float const &amount); /** * @brief Reset the Print Statistics diff --git a/Marlin/status_screen_DOGM.h b/Marlin/status_screen_DOGM.h index 5faeb6e406..038351cc81 100644 --- a/Marlin/status_screen_DOGM.h +++ b/Marlin/status_screen_DOGM.h @@ -62,7 +62,7 @@ FORCE_INLINE void _draw_heater_status(const uint8_t x, const int8_t heater, cons if (blink || !is_idle) #endif - _draw_centered_temp(0.5 + ( + _draw_centered_temp(0.5f + ( #if HAS_HEATED_BED isBed ? thermalManager.degTargetBed() : #endif @@ -72,7 +72,7 @@ FORCE_INLINE void _draw_heater_status(const uint8_t x, const int8_t heater, cons } if (PAGE_CONTAINS(21, 28)) { - _draw_centered_temp(0.5 + ( + _draw_centered_temp(0.5f + ( #if HAS_HEATED_BED isBed ? thermalManager.degBed() : #endif @@ -108,11 +108,11 @@ FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const if (blink) lcd_print(value); else { - if (!axis_homed[axis]) + if (!TEST(axis_homed, axis)) while (const char c = *value++) lcd_print(c <= '.' ? c : '?'); else { #if DISABLED(HOME_AFTER_DEACTIVATE) && DISABLED(DISABLE_REDUCED_ACCURACY_WARNING) - if (!axis_known_position[axis]) + if (!TEST(axis_known_position, axis)) lcd_printPGM(axis == Z_AXIS ? PSTR(" ") : PSTR(" ")); else #endif @@ -124,7 +124,7 @@ FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const inline void lcd_implementation_status_message(const bool blink) { #if ENABLED(STATUS_MESSAGE_SCROLLING) static bool last_blink = false; - + // Get the UTF8 character count of the string uint8_t slen = lcd_strlen(lcd_status_message); @@ -162,10 +162,8 @@ inline void lcd_implementation_status_message(const bool blink) { u8g.print('.'); // Always at 1+ spaces left, draw a dot if (--chars) { // Draw a second dot if there's space u8g.print('.'); - if (--chars) { - // Print a second copy of the message - lcd_print_utf(lcd_status_message, LCD_WIDTH - (rlen+2)); - } + if (--chars) // Print a second copy of the message + lcd_print_utf(lcd_status_message, LCD_WIDTH - (rlen + 2)); } } if (last_blink != blink) { diff --git a/Marlin/status_screen_lite_ST7920.h b/Marlin/status_screen_lite_ST7920.h index 37eabe14bf..2acb6a9736 100644 --- a/Marlin/status_screen_lite_ST7920.h +++ b/Marlin/status_screen_lite_ST7920.h @@ -617,7 +617,7 @@ void ST7920_Lite_Status_Screen::draw_status_message(const char *str) { begin_data(); const uint8_t lcd_len = 16; #if ENABLED(STATUS_MESSAGE_SCROLLING) - + uint8_t slen = lcd_strlen(str); // If the string fits into the LCD, just print it and do not scroll it @@ -868,9 +868,7 @@ void ST7920_Lite_Status_Screen::update_status_or_position(bool forceUpdate) { #if ENABLED(DISABLE_REDUCED_ACCURACY_WARNING) true #else - axis_known_position[X_AXIS] && - axis_known_position[Y_AXIS] && - axis_known_position[Z_AXIS] + all_axes_known() #endif ); } diff --git a/Marlin/status_screen_lite_ST7920_class.h b/Marlin/status_screen_lite_ST7920_class.h index 75cede08ca..289723479a 100644 --- a/Marlin/status_screen_lite_ST7920_class.h +++ b/Marlin/status_screen_lite_ST7920_class.h @@ -18,7 +18,7 @@ #define STATUS_SCREEN_LITE_ST7920_CLASS_H #include "macros.h" -#include "duration.h" +#include "duration_t.h" typedef const __FlashStringHelper *progmem_str; diff --git a/Marlin/stepper.cpp b/Marlin/stepper.cpp index bd3f1b77da..8c965c45da 100644 --- a/Marlin/stepper.cpp +++ b/Marlin/stepper.cpp @@ -46,6 +46,29 @@ * and Philipp Tiefenbacher. */ +/** + * __________________________ + * /| |\ _________________ ^ + * / | | \ /| |\ | + * / | | \ / | | \ s + * / | | | | | \ p + * / | | | | | \ e + * +-----+------------------------+---+--+---------------+----+ e + * | BLOCK 1 | BLOCK 2 | d + * + * time -----> + * + * The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates + * first block->accelerate_until step_events_completed, then keeps going at constant speed until + * step_events_completed reaches block->decelerate_after after which it decelerates until the trapezoid generator is reset. + * The slope of acceleration is calculated using v = u + at where t is the accumulated timer values of the steps so far. + */ + +/** + * Marlin uses the Bresenham algorithm. For a detailed explanation of theory and + * method see https://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html + */ + /** * Jerk controlled movements planner added Apr 2018 by Eduardo José Tagle. * Equations based on Synthethos TinyG2 sources, but the fixed-point @@ -73,10 +96,8 @@ Stepper stepper; // Singleton // public: -block_t* Stepper::current_block = NULL; // A pointer to the block currently being traced - #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS) - bool Stepper::performing_homing = false; + bool Stepper::homing_dual_axis = false; #endif #if HAS_MOTOR_CURRENT_PWM @@ -85,29 +106,53 @@ block_t* Stepper::current_block = NULL; // A pointer to the block currently bei // private: +block_t* Stepper::current_block = NULL; // A pointer to the block currently being traced + uint8_t Stepper::last_direction_bits = 0, - Stepper::last_movement_extruder = 0xFF, Stepper::axis_did_move; + bool Stepper::abort_current_block; +#if DISABLED(MIXING_EXTRUDER) + uint8_t Stepper::last_moved_extruder = 0xFF; +#endif + #if ENABLED(X_DUAL_ENDSTOPS) - bool Stepper::locked_x_motor = false, Stepper::locked_x2_motor = false; + bool Stepper::locked_X_motor = false, Stepper::locked_X2_motor = false; #endif #if ENABLED(Y_DUAL_ENDSTOPS) - bool Stepper::locked_y_motor = false, Stepper::locked_y2_motor = false; + bool Stepper::locked_Y_motor = false, Stepper::locked_Y2_motor = false; #endif #if ENABLED(Z_DUAL_ENDSTOPS) - bool Stepper::locked_z_motor = false, Stepper::locked_z2_motor = false; + bool Stepper::locked_Z_motor = false, Stepper::locked_Z2_motor = false; #endif -int32_t Stepper::counter_X = 0, - Stepper::counter_Y = 0, - Stepper::counter_Z = 0, - Stepper::counter_E = 0; +uint32_t Stepper::acceleration_time, Stepper::deceleration_time; +uint8_t Stepper::steps_per_isr; -uint32_t Stepper::step_events_completed = 0; // The number of step events executed in the current block +#if DISABLED(ADAPTIVE_STEP_SMOOTHING) + constexpr +#endif + uint8_t Stepper::oversampling_factor; -#if ENABLED(BEZIER_JERK_CONTROL) +int32_t Stepper::delta_error[XYZE] = { 0 }; + +uint32_t Stepper::advance_dividend[XYZE] = { 0 }, + Stepper::advance_divisor = 0, + Stepper::step_events_completed = 0, // The number of step events executed in the current block + Stepper::accelerate_until, // The point from where we need to stop acceleration + 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; +#else + int8_t Stepper::active_extruder; // Active extruder +#endif + +#if ENABLED(S_CURVE_ACCELERATION) int32_t __attribute__((used)) Stepper::bezier_A __asm__("bezier_A"); // A coefficient in Bézier speed curve with alias for assembler int32_t __attribute__((used)) Stepper::bezier_B __asm__("bezier_B"); // B coefficient in Bézier speed curve with alias for assembler int32_t __attribute__((used)) Stepper::bezier_C __asm__("bezier_C"); // C coefficient in Bézier speed curve with alias for assembler @@ -118,70 +163,47 @@ uint32_t Stepper::step_events_completed = 0; // The number of step events execut #endif uint32_t Stepper::nextMainISR = 0; -bool Stepper::all_steps_done = false; #if ENABLED(LIN_ADVANCE) - uint32_t Stepper::LA_decelerate_after; + constexpr uint32_t LA_ADV_NEVER = 0xFFFFFFFF; + uint32_t Stepper::nextAdvanceISR = LA_ADV_NEVER, + Stepper::LA_isr_rate = LA_ADV_NEVER; + uint16_t Stepper::LA_current_adv_steps = 0, + Stepper::LA_final_adv_steps, + Stepper::LA_max_adv_steps; - constexpr uint32_t ADV_NEVER = 0xFFFFFFFF; - uint32_t Stepper::nextAdvanceISR = ADV_NEVER, - Stepper::eISR_Rate = ADV_NEVER; - uint16_t Stepper::current_adv_steps = 0, - Stepper::final_adv_steps, - Stepper::max_adv_steps; + int8_t Stepper::LA_steps = 0; - int8_t Stepper::e_steps = 0; - - #if E_STEPPERS > 1 - int8_t Stepper::LA_active_extruder; // Copy from current executed block. Needed because current_block is set to NULL "too early". - #else - constexpr int8_t Stepper::LA_active_extruder; - #endif - - bool Stepper::use_advance_lead; + bool Stepper::LA_use_advance_lead; #endif // LIN_ADVANCE -uint32_t Stepper::acceleration_time, Stepper::deceleration_time; - -volatile int32_t Stepper::count_position[NUM_AXIS] = { 0 }; -volatile signed char Stepper::count_direction[NUM_AXIS] = { 1, 1, 1, 1 }; - -#if ENABLED(MIXING_EXTRUDER) - int32_t Stepper::counter_m[MIXING_STEPPERS]; -#endif - -uint32_t Stepper::ticks_nominal; -uint8_t Stepper::step_loops, Stepper::step_loops_nominal; - -#if DISABLED(BEZIER_JERK_CONTROL) +int32_t Stepper::ticks_nominal = -1; +#if DISABLED(S_CURVE_ACCELERATION) uint32_t Stepper::acc_step_rate; // needed for deceleration start point #endif volatile int32_t Stepper::endstops_trigsteps[XYZ]; +volatile int32_t Stepper::count_position[NUM_AXIS] = { 0 }; +int8_t Stepper::count_direction[NUM_AXIS] = { 0, 0, 0, 0 }; + #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS) - #define LOCKED_X_MOTOR locked_x_motor - #define LOCKED_Y_MOTOR locked_y_motor - #define LOCKED_Z_MOTOR locked_z_motor - #define LOCKED_X2_MOTOR locked_x2_motor - #define LOCKED_Y2_MOTOR locked_y2_motor - #define LOCKED_Z2_MOTOR locked_z2_motor - #define DUAL_ENDSTOP_APPLY_STEP(A,V) \ - if (performing_homing) { \ - if (A##_HOME_DIR < 0) { \ - if (!(TEST(endstops.state(), A##_MIN) && count_direction[_AXIS(A)] < 0) && !LOCKED_##A##_MOTOR) A##_STEP_WRITE(V); \ - if (!(TEST(endstops.state(), A##2_MIN) && count_direction[_AXIS(A)] < 0) && !LOCKED_##A##2_MOTOR) A##2_STEP_WRITE(V); \ - } \ - else { \ - if (!(TEST(endstops.state(), A##_MAX) && count_direction[_AXIS(A)] > 0) && !LOCKED_##A##_MOTOR) A##_STEP_WRITE(V); \ - if (!(TEST(endstops.state(), A##2_MAX) && count_direction[_AXIS(A)] > 0) && !LOCKED_##A##2_MOTOR) A##2_STEP_WRITE(V); \ - } \ - } \ - else { \ - A##_STEP_WRITE(V); \ - A##2_STEP_WRITE(V); \ + #define DUAL_ENDSTOP_APPLY_STEP(A,V) \ + if (homing_dual_axis) { \ + if (A##_HOME_DIR < 0) { \ + if (!(TEST(endstops.state(), A##_MIN) && count_direction[_AXIS(A)] < 0) && !locked_##A##_motor) A##_STEP_WRITE(V); \ + if (!(TEST(endstops.state(), A##2_MIN) && count_direction[_AXIS(A)] < 0) && !locked_##A##2_motor) A##2_STEP_WRITE(V); \ + } \ + else { \ + if (!(TEST(endstops.state(), A##_MAX) && count_direction[_AXIS(A)] > 0) && !locked_##A##_motor) A##_STEP_WRITE(V); \ + if (!(TEST(endstops.state(), A##2_MAX) && count_direction[_AXIS(A)] > 0) && !locked_##A##2_motor) A##2_STEP_WRITE(V); \ + } \ + } \ + else { \ + A##_STEP_WRITE(V); \ + A##2_STEP_WRITE(V); \ } #endif @@ -199,7 +221,7 @@ volatile int32_t Stepper::endstops_trigsteps[XYZ]; X2_DIR_WRITE(v); \ } \ else { \ - if (current_block->active_extruder) X2_DIR_WRITE(v); else X_DIR_WRITE(v); \ + if (movement_extruder()) X2_DIR_WRITE(v); else X_DIR_WRITE(v); \ } #define X_APPLY_STEP(v,ALWAYS) \ if (extruder_duplication_enabled || ALWAYS) { \ @@ -207,7 +229,7 @@ volatile int32_t Stepper::endstops_trigsteps[XYZ]; X2_STEP_WRITE(v); \ } \ else { \ - if (current_block->active_extruder) X2_STEP_WRITE(v); else X_STEP_WRITE(v); \ + if (movement_extruder()) X2_STEP_WRITE(v); else X_STEP_WRITE(v); \ } #else #define X_APPLY_DIR(v,Q) X_DIR_WRITE(v) @@ -239,7 +261,7 @@ volatile int32_t Stepper::endstops_trigsteps[XYZ]; #endif #if DISABLED(MIXING_EXTRUDER) - #define E_APPLY_STEP(v,Q) E_STEP_WRITE(v) + #define E_APPLY_STEP(v,Q) E_STEP_WRITE(active_extruder, v) #endif // intRes = longIn1 * longIn2 >> 24 @@ -302,25 +324,6 @@ static FORCE_INLINE uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2 return intRes; } -// Some useful constants - -/** - * __________________________ - * /| |\ _________________ ^ - * / | | \ /| |\ | - * / | | \ / | | \ s - * / | | | | | \ p - * / | | | | | \ e - * +-----+------------------------+---+--+---------------+----+ e - * | BLOCK 1 | BLOCK 2 | d - * - * time -----> - * - * The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates - * first block->accelerate_until step_events_completed, then keeps going at constant speed until - * step_events_completed reaches block->decelerate_after after which it decelerates until the trapezoid generator is reset. - * The slope of acceleration is calculated using v = u + at where t is the accumulated timer values of the steps so far. - */ void Stepper::wake_up() { // TCNT1 = 0; ENABLE_STEPPER_DRIVER_INTERRUPT(); @@ -356,28 +359,44 @@ void Stepper::set_directions() { #endif #if DISABLED(LIN_ADVANCE) - if (motor_direction(E_AXIS)) { - REV_E_DIR(); - count_direction[E_AXIS] = -1; - } - else { - NORM_E_DIR(); - count_direction[E_AXIS] = 1; - } + #if ENABLED(MIXING_EXTRUDER) + if (motor_direction(E_AXIS)) { + MIXING_STEPPERS_LOOP(j) REV_E_DIR(j); + count_direction[E_AXIS] = -1; + } + else { + MIXING_STEPPERS_LOOP(j) NORM_E_DIR(j); + count_direction[E_AXIS] = 1; + } + #else + if (motor_direction(E_AXIS)) { + REV_E_DIR(active_extruder); + count_direction[E_AXIS] = -1; + } + else { + NORM_E_DIR(active_extruder); + count_direction[E_AXIS] = 1; + } + #endif #endif // !LIN_ADVANCE + + // A small delay may be needed after changing direction + #if MINIMUM_STEPPER_DIR_DELAY > 0 + DELAY_NS(MINIMUM_STEPPER_DIR_DELAY); + #endif } -#if ENABLED(BEZIER_JERK_CONTROL) +#if ENABLED(S_CURVE_ACCELERATION) /** - * We are using a quintic (fifth-degree) Bézier polynomial for the velocity curve. - * This gives us a "linear pop" velocity curve; with pop being the sixth derivative of position: + * This uses a quintic (fifth-degree) Bézier polynomial for the velocity curve, giving + * a "linear pop" velocity curve; with pop being the sixth derivative of position: * velocity - 1st, acceleration - 2nd, jerk - 3rd, snap - 4th, crackle - 5th, pop - 6th * * The Bézier curve takes the form: * * V(t) = P_0 * B_0(t) + P_1 * B_1(t) + P_2 * B_2(t) + P_3 * B_3(t) + P_4 * B_4(t) + P_5 * B_5(t) * - * Where 0 <= t <= 1, and V(t) is the velocity. P_0 through P_5 are the control points, and B_0(t) + * Where 0 <= t <= 1, and V(t) is the velocity. P_0 through P_5 are the control points, and B_0(t) * through B_5(t) are the Bernstein basis as follows: * * B_0(t) = (1-t)^5 = -t^5 + 5t^4 - 10t^3 + 10t^2 - 5t + 1 @@ -390,12 +409,12 @@ void Stepper::set_directions() { * | | | | | | * A B C D E F * - * Unfortunately, we cannot use forward-differencing to calculate each position through + * Unfortunately, we cannot use forward-differencing to calculate each position through * the curve, as Marlin uses variable timer periods. So, we require a formula of the form: * * V_f(t) = A*t^5 + B*t^4 + C*t^3 + D*t^2 + E*t + F * - * Looking at the above B_0(t) through B_5(t) expanded forms, if we take the coefficients of t^5 + * Looking at the above B_0(t) through B_5(t) expanded forms, if we take the coefficients of t^5 * through t of the Bézier form of V(t), we can determine that: * * A = -P_0 + 5*P_1 - 10*P_2 + 10*P_3 - 5*P_4 + P_5 @@ -405,7 +424,7 @@ void Stepper::set_directions() { * E = - 5*P_0 + 5*P_1 * F = P_0 * - * Now, since we will (currently) *always* want the initial acceleration and jerk values to be 0, + * Now, since we will (currently) *always* want the initial acceleration and jerk values to be 0, * We set P_i = P_0 = P_1 = P_2 (initial velocity), and P_t = P_3 = P_4 = P_5 (target velocity), * which, after simplification, resolves to: * @@ -416,12 +435,12 @@ void Stepper::set_directions() { * E = 0 * F = P_i * - * As the t is evaluated in non uniform steps here, there is no other way rather than evaluating + * As the t is evaluated in non uniform steps here, there is no other way rather than evaluating * the Bézier curve at each point: * * V_f(t) = A*t^5 + B*t^4 + C*t^3 + F [0 <= t <= 1] * - * Floating point arithmetic execution time cost is prohibitive, so we will transform the math to + * Floating point arithmetic execution time cost is prohibitive, so we will transform the math to * use fixed point values to be able to evaluate it in realtime. Assuming a maximum of 250000 steps * per second (driver pulses should at least be 2µS hi/2µS lo), and allocating 2 bits to avoid * overflows on the evaluation of the Bézier curve, means we can use @@ -432,7 +451,7 @@ void Stepper::set_directions() { * C: signed Q24.7 , |range = +/- 250000 *10 * 128 = +/- 320000000 = 0x1312D000 | 29 bits + sign * F: signed Q24.7 , |range = +/- 250000 * 128 = 32000000 = 0x01E84800 | 25 bits + sign * - * The trapezoid generator state contains the following information, that we will use to create and evaluate + * The trapezoid generator state contains the following information, that we will use to create and evaluate * the Bézier curve: * * blk->step_event_count [TS] = The total count of steps for this movement. (=distance) @@ -444,7 +463,7 @@ void Stepper::set_directions() { * * For Any 32bit CPU: * - * At the start of each trapezoid, we calculate the coefficients A,B,C,F and Advance [AV], as follows: + * At the start of each trapezoid, calculate the coefficients A,B,C,F and Advance [AV], as follows: * * A = 6*128*(VF - VI) = 768*(VF - VI) * B = 15*128*(VI - VF) = 1920*(VI - VF) @@ -452,7 +471,7 @@ void Stepper::set_directions() { * F = 128*VI = 128*VI * AV = (1<<32)/TS ~= 0xFFFFFFFF / TS (To use ARM UDIV, that is 32 bits) (this is computed at the planner, to offload expensive calculations from the ISR) * - * And for each point, we will evaluate the curve with the following sequence: + * And for each point, evaluate the curve with the following sequence: * * void lsrs(uint32_t& d, uint32_t s, int cnt) { * d = s >> cnt; @@ -505,10 +524,10 @@ void Stepper::set_directions() { * return alo; * } * - * This will be rewritten in ARM assembly to get peak performance and will take 43 cycles to execute + * This is rewritten in ARM assembly for optimal performance (43 cycles to execute). * - * For AVR, we scale precision of coefficients to make it possible to evaluate the Bézier curve in - * realtime: Let's reduce precision as much as possible. After some experimentation we found that: + * For AVR, the precision of coefficients is scaled so the Bézier curve can be evaluated in real-time: + * Let's reduce precision as much as possible. After some experimentation we found that: * * Assume t and AV with 24 bits is enough * A = 6*(VF - VI) @@ -517,9 +536,9 @@ void Stepper::set_directions() { * F = VI * AV = (1<<24)/TS (this is computed at the planner, to offload expensive calculations from the ISR) * - * Instead of storing sign for each coefficient, we will store its absolute value, + * Instead of storing sign for each coefficient, we will store its absolute value, * and flag the sign of the A coefficient, so we can save to store the sign bit. - * It always holds that sign(A) = - sign(B) = sign(C) + * It always holds that sign(A) = - sign(B) = sign(C) * * So, the resulting range of the coefficients are: * @@ -529,7 +548,7 @@ void Stepper::set_directions() { * C: signed Q24 , range = 250000 *10 = 2500000 = 0x1312D0 | 21 bits * F: signed Q24 , range = 250000 = 250000 = 0x0ED090 | 20 bits * - * And for each curve, we estimate its coefficients with: + * And for each curve, estimate its coefficients with: * * void _calc_bezier_curve_coeffs(int32_t v0, int32_t v1, uint32_t av) { * // Calculate the Bézier coefficients @@ -548,7 +567,7 @@ void Stepper::set_directions() { * bezier_F = v0; * } * - * And for each point, we will evaluate the curve with the following sequence: + * And for each point, evaluate the curve with the following sequence: * * // unsigned multiplication of 24 bits x 24bits, return upper 16 bits * void umul24x24to16hi(uint16_t& r, uint24_t op1, uint24_t op2) { @@ -598,9 +617,8 @@ void Stepper::set_directions() { * } * return acc; * } - * Those functions will be translated into assembler to get peak performance. coefficient calculations takes 70 cycles, - * Bezier point evaluation takes 150 cycles - * + * These functions are translated to assembler for optimal performance. + * Coefficient calculation takes 70 cycles. Bezier point evaluation takes 150 cycles. */ // For AVR we use assembly to maximize speed @@ -1091,57 +1109,44 @@ void Stepper::set_directions() { return (r2 | (uint16_t(r3) << 8)) | (uint32_t(r4) << 16); } -#endif // BEZIER_JERK_CONTROL +#endif // S_CURVE_ACCELERATION /** * 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 */ HAL_STEP_TIMER_ISR { HAL_timer_isr_prologue(STEP_TIMER_NUM); - // Program timer compare for the maximum period, so it does NOT - // flag an interrupt while this ISR is running - So changes from small - // periods to big periods are respected and the timer does not reset to 0 - HAL_timer_set_compare(STEP_TIMER_NUM, HAL_TIMER_TYPE_MAX); - - // Call the ISR scheduler - hal_timer_t ticks = Stepper::isr_scheduler(); - - // Now 'ticks' contains the period to the next Stepper ISR - And we are - // sure that the time has not arrived yet - Warrantied by the scheduler - - // Set the next ISR to fire at the proper time - HAL_timer_set_compare(STEP_TIMER_NUM, ticks); + Stepper::isr(); HAL_timer_isr_epilogue(STEP_TIMER_NUM); } #define STEP_MULTIPLY(A,B) MultiU24X32toH16(A, B) -hal_timer_t Stepper::isr_scheduler() { - uint32_t interval; +void Stepper::isr() { + DISABLE_ISRS(); + + // Program timer compare for the maximum period, so it does NOT + // flag an interrupt while this ISR is running - So changes from small + // periods to big periods are respected and the timer does not reset to 0 + HAL_timer_set_compare(STEP_TIMER_NUM, HAL_TIMER_TYPE_MAX); // Count of ticks for the next ISR hal_timer_t next_isr_ticks = 0; // Limit the amount of iterations uint8_t max_loops = 10; - + // We need this variable here to be able to use it in the following loop hal_timer_t min_ticks; do { + // Enable ISRs to reduce USART processing latency + ENABLE_ISRS(); + // Run main stepping pulse phase ISR if we have to if (!nextMainISR) Stepper::stepper_pulse_phase_isr(); @@ -1155,13 +1160,13 @@ hal_timer_t Stepper::isr_scheduler() { // Run main stepping block processing ISR if we have to if (!nextMainISR) nextMainISR = Stepper::stepper_block_phase_isr(); - #if ENABLED(LIN_ADVANCE) - // Select the closest interval in time - interval = (nextAdvanceISR <= nextMainISR) ? nextAdvanceISR : nextMainISR; - #else - // The interval is just the remaining time to the stepper ISR - interval = nextMainISR; - #endif + uint32_t interval = + #if ENABLED(LIN_ADVANCE) + MIN(nextAdvanceISR, nextMainISR) // Nearest time interval + #else + nextMainISR // Remaining stepper ISR time + #endif + ; // Limit the value to the maximum possible value of the timer NOMORE(interval, HAL_TIMER_TYPE_MAX); @@ -1171,7 +1176,7 @@ hal_timer_t Stepper::isr_scheduler() { #if ENABLED(LIN_ADVANCE) // Compute the time remaining for the advance isr - if (nextAdvanceISR != ADV_NEVER) nextAdvanceISR -= interval; + if (nextAdvanceISR != LA_ADV_NEVER) nextAdvanceISR -= interval; #endif /** @@ -1200,15 +1205,23 @@ hal_timer_t Stepper::isr_scheduler() { // Compute the tick count for the next ISR next_isr_ticks += interval; + /** + * The following section must be done with global interrupts disabled. + * We want nothing to interrupt it, as that could mess the calculations + * we do for the next value to program in the period register of the + * stepper timer and lead to skipped ISRs (if the value we happen to program + * is less than the current count due to something preempting between the + * read and the write of the new period value). + */ + DISABLE_ISRS(); + /** * Get the current tick value + margin * Assuming at least 6µs between calls to this ISR... - * On AVR the ISR epilogue is estimated at 40 instructions - close to 2.5µS. - * On ARM the ISR epilogue is estimated at 10 instructions - close to 200nS. - * In either case leave at least 8µS for other tasks to execute - That allows - * up to 100khz stepping rates + * On AVR the ISR epilogue+prologue is estimated at 100 instructions - Give 8µs as margin + * On ARM the ISR epilogue+prologue is estimated at 20 instructions - Give 1µs as margin */ - min_ticks = HAL_timer_get_count(STEP_TIMER_NUM) + hal_timer_t((HAL_TICKS_PER_US) * 8); // ISR never takes more than 1ms, so this shouldn't cause trouble + min_ticks = HAL_timer_get_count(STEP_TIMER_NUM) + hal_timer_t((STEPPER_TIMER_TICKS_PER_US) * 8); /** * NB: If for some reason the stepper monopolizes the MPU, eventually the @@ -1221,8 +1234,14 @@ hal_timer_t Stepper::isr_scheduler() { // Advance pulses if not enough time to wait for the next ISR } while (next_isr_ticks < min_ticks); - // Return the count of ticks for the next ISR - return (hal_timer_t)next_isr_ticks; + // Now 'next_isr_ticks' contains the period to the next Stepper ISR - And we are + // sure that the time has not arrived yet - Warrantied by the scheduler + + // Set the next ISR to fire at the proper time + HAL_timer_set_compare(STEP_TIMER_NUM, hal_timer_t(next_isr_ticks)); + + // Don't forget to finally reenable interrupts + ENABLE_ISRS(); } /** @@ -1247,98 +1266,42 @@ void Stepper::stepper_pulse_phase_isr() { // If there is no current block, do nothing if (!current_block) return; - // Take multiple steps per interrupt (For high speed moves) - all_steps_done = false; - for (uint8_t i = step_loops; i--;) { + // Count of pending loops and events for this iteration + const uint32_t pending_events = step_event_count - step_events_completed; + uint8_t events_to_do = MIN(pending_events, steps_per_isr); + + // Just update the value we will get at the end of the loop + step_events_completed += events_to_do; + + // Get the timer count and estimate the end of the pulse + hal_timer_t pulse_end = HAL_timer_get_count(PULSE_TIMER_NUM) + hal_timer_t(MIN_PULSE_TICKS); + + const hal_timer_t added_step_ticks = hal_timer_t(ADDED_STEP_TICKS); + + // Take multiple steps per interrupt (For high speed moves) + do { - #define _COUNTER(AXIS) counter_## AXIS #define _APPLY_STEP(AXIS) AXIS ##_APPLY_STEP #define _INVERT_STEP_PIN(AXIS) INVERT_## AXIS ##_STEP_PIN - // Advance the Bresenham counter; start a pulse if the axis needs a step + // Start an active pulse, if Bresenham says so, and update position #define PULSE_START(AXIS) do{ \ - _COUNTER(AXIS) += current_block->steps[_AXIS(AXIS)]; \ - if (_COUNTER(AXIS) > 0) { _APPLY_STEP(AXIS)(!_INVERT_STEP_PIN(AXIS), 0); } \ - }while(0) - - // Advance the Bresenham counter; start a pulse if the axis needs a step - #define STEP_TICK(AXIS) do { \ - if (_COUNTER(AXIS) > 0) { \ - _COUNTER(AXIS) -= current_block->step_event_count; \ + delta_error[_AXIS(AXIS)] += advance_dividend[_AXIS(AXIS)]; \ + if (delta_error[_AXIS(AXIS)] >= 0) { \ + _APPLY_STEP(AXIS)(!_INVERT_STEP_PIN(AXIS), 0); \ count_position[_AXIS(AXIS)] += count_direction[_AXIS(AXIS)]; \ } \ }while(0) - // Stop an active pulse, if any - #define PULSE_STOP(AXIS) _APPLY_STEP(AXIS)(_INVERT_STEP_PIN(AXIS), 0) - - /** - * Estimate the number of cycles that the stepper logic already takes - * up between the start and stop of the X stepper pulse. - * - * Currently this uses very modest estimates of around 5 cycles. - * True values may be derived by careful testing. - * - * Once any delay is added, the cost of the delay code itself - * may be subtracted from this value to get a more accurate delay. - * Delays under 20 cycles (1.25µs) will be very accurate, using NOPs. - * Longer delays use a loop. The resolution is 8 cycles. - */ - #if HAS_X_STEP - #define _CYCLE_APPROX_1 5 - #else - #define _CYCLE_APPROX_1 0 - #endif - #if ENABLED(X_DUAL_STEPPER_DRIVERS) - #define _CYCLE_APPROX_2 _CYCLE_APPROX_1 + 4 - #else - #define _CYCLE_APPROX_2 _CYCLE_APPROX_1 - #endif - #if HAS_Y_STEP - #define _CYCLE_APPROX_3 _CYCLE_APPROX_2 + 5 - #else - #define _CYCLE_APPROX_3 _CYCLE_APPROX_2 - #endif - #if ENABLED(Y_DUAL_STEPPER_DRIVERS) - #define _CYCLE_APPROX_4 _CYCLE_APPROX_3 + 4 - #else - #define _CYCLE_APPROX_4 _CYCLE_APPROX_3 - #endif - #if HAS_Z_STEP - #define _CYCLE_APPROX_5 _CYCLE_APPROX_4 + 5 - #else - #define _CYCLE_APPROX_5 _CYCLE_APPROX_4 - #endif - #if ENABLED(Z_DUAL_STEPPER_DRIVERS) - #define _CYCLE_APPROX_6 _CYCLE_APPROX_5 + 4 - #else - #define _CYCLE_APPROX_6 _CYCLE_APPROX_5 - #endif - #if DISABLED(LIN_ADVANCE) - #if ENABLED(MIXING_EXTRUDER) - #define _CYCLE_APPROX_7 _CYCLE_APPROX_6 + (MIXING_STEPPERS) * 6 - #else - #define _CYCLE_APPROX_7 _CYCLE_APPROX_6 + 5 - #endif - #else - #define _CYCLE_APPROX_7 _CYCLE_APPROX_6 - #endif - - #define CYCLES_EATEN_XYZE _CYCLE_APPROX_7 - #define EXTRA_CYCLES_XYZE (STEP_PULSE_CYCLES - (CYCLES_EATEN_XYZE)) - - /** - * If a minimum pulse time was specified get the timer 0 value. - * - * On AVR the TCNT0 timer has an 8x prescaler, so it increments every 8 cycles. - * That's every 0.5µs on 16MHz and every 0.4µs on 20MHz. - * 20 counts of TCNT0 -by itself- is a good pulse delay. - * 10µs = 160 or 200 cycles. - */ - #if EXTRA_CYCLES_XYZE > 20 - hal_timer_t pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM); - #endif + // Stop an active pulse, if any, and adjust error term + #define PULSE_STOP(AXIS) do { \ + if (delta_error[_AXIS(AXIS)] >= 0) { \ + delta_error[_AXIS(AXIS)] -= advance_divisor; \ + _APPLY_STEP(AXIS)(_INVERT_STEP_PIN(AXIS), 0); \ + } \ + }while(0) + // Pulse start #if HAS_X_STEP PULSE_START(X); #endif @@ -1349,64 +1312,49 @@ void Stepper::stepper_pulse_phase_isr() { PULSE_START(Z); #endif + // Pulse E/Mixing extruders #if ENABLED(LIN_ADVANCE) - counter_E += current_block->steps[E_AXIS]; - if (counter_E > 0) { - #if DISABLED(MIXING_EXTRUDER) - // Don't step E here for mixing extruder - motor_direction(E_AXIS) ? --e_steps : ++e_steps; - #endif + // Tick the E axis, correct error term and update position + 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; } - - #if ENABLED(MIXING_EXTRUDER) - // Step mixing steppers proportionally - const bool dir = motor_direction(E_AXIS); - MIXING_STEPPERS_LOOP(j) { - counter_m[j] += current_block->steps[E_AXIS]; - if (counter_m[j] > 0) { - counter_m[j] -= current_block->mix_event_count[j]; - dir ? --e_steps[j] : ++e_steps[j]; - } - } - #endif - #else // !LIN_ADVANCE - use linear interpolation for E also - #if ENABLED(MIXING_EXTRUDER) - // Keep updating the single E axis - counter_E += current_block->steps[E_AXIS]; - // Tick the counters used for this mix + + // 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; + } + + // Tick the counters used for this mix in proper proportion MIXING_STEPPERS_LOOP(j) { // Step mixing steppers (proportionally) - counter_m[j] += current_block->steps[E_AXIS]; + delta_error_m[j] += advance_dividend_m[j]; // Step when the counter goes over zero - if (counter_m[j] > 0) En_STEP_WRITE(j, !INVERT_E_STEP_PIN); + if (delta_error_m[j] >= 0) E_STEP_WRITE(j, !INVERT_E_STEP_PIN); } + #else // !MIXING_EXTRUDER PULSE_START(E); #endif #endif // !LIN_ADVANCE - #if HAS_X_STEP - STEP_TICK(X); - #endif - #if HAS_Y_STEP - STEP_TICK(Y); - #endif - #if HAS_Z_STEP - STEP_TICK(Z); + #if MINIMUM_STEPPER_PULSE + // Just wait for the requested pulse duration + while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { /* nada */ } #endif - STEP_TICK(E); // Always tick the single E axis - - // For minimum pulse time wait before stopping pulses - #if EXTRA_CYCLES_XYZE > 20 - while (EXTRA_CYCLES_XYZE > (uint32_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ } - pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM); - #elif EXTRA_CYCLES_XYZE > 0 - DELAY_NS(EXTRA_CYCLES_XYZE * NANOSECONDS_PER_CYCLE); - #endif + // Add the delay needed to ensure the maximum driver rate is enforced + if (signed(added_step_ticks) > 0) pulse_end += hal_timer_t(added_step_ticks); + // Pulse stop #if HAS_X_STEP PULSE_STOP(X); #endif @@ -1420,9 +1368,9 @@ void Stepper::stepper_pulse_phase_isr() { #if DISABLED(LIN_ADVANCE) #if ENABLED(MIXING_EXTRUDER) MIXING_STEPPERS_LOOP(j) { - if (counter_m[j] > 0) { - counter_m[j] -= current_block->mix_event_count[j]; - En_STEP_WRITE(j, 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 // !MIXING_EXTRUDER @@ -1430,19 +1378,20 @@ void Stepper::stepper_pulse_phase_isr() { #endif #endif // !LIN_ADVANCE - if (++step_events_completed >= current_block->step_event_count) { - all_steps_done = true; - break; - } + // Decrement the count of pending pulses to do + --events_to_do; // For minimum pulse time wait after stopping pulses also - #if EXTRA_CYCLES_XYZE > 20 - if (i) while (EXTRA_CYCLES_XYZE > (uint32_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ } - #elif EXTRA_CYCLES_XYZE > 0 - if (i) DELAY_NS(EXTRA_CYCLES_XYZE * NANOSECONDS_PER_CYCLE); - #endif + if (events_to_do) { + // Just wait for the requested pulse duration + while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { /* nada */ } + #if MINIMUM_STEPPER_PULSE + // Add to the value, the time that the pulse must be active (to be used on the next loop) + pulse_end += hal_timer_t(MIN_PULSE_TICKS); + #endif + } - } // steps_loop + } while (events_to_do); } // This is the last half of the stepper interrupt: This one processes and @@ -1452,106 +1401,124 @@ void Stepper::stepper_pulse_phase_isr() { uint32_t Stepper::stepper_block_phase_isr() { // If no queued movements, just wait 1ms for the next move - uint32_t interval = (HAL_STEPPER_TIMER_RATE / 1000); + uint32_t interval = (STEPPER_TIMER_RATE / 1000); // If there is a current block if (current_block) { - // Calculate new timer value - if (step_events_completed <= current_block->accelerate_until) { - - #if ENABLED(BEZIER_JERK_CONTROL) - // Get the next speed to use (Jerk limited!) - uint32_t acc_step_rate = - acceleration_time < current_block->acceleration_time - ? _eval_bezier_curve(acceleration_time) - : current_block->cruise_rate; - #else - acc_step_rate = STEP_MULTIPLY(acceleration_time, current_block->acceleration_rate) + current_block->initial_rate; - NOMORE(acc_step_rate, current_block->nominal_rate); - #endif - - // step_rate to timer interval - interval = calc_timer_interval(acc_step_rate); - acceleration_time += interval; - - #if ENABLED(LIN_ADVANCE) - if (current_block->use_advance_lead) { - if (step_events_completed == step_loops || (e_steps && eISR_Rate != current_block->advance_speed)) { - nextAdvanceISR = 0; // Wake up eISR on first acceleration loop and fire ISR if final adv_rate is reached - eISR_Rate = current_block->advance_speed; - } - } - else { - eISR_Rate = ADV_NEVER; - if (e_steps) nextAdvanceISR = 0; - } - #endif // LIN_ADVANCE - } - else if (step_events_completed > current_block->decelerate_after) { - uint32_t step_rate; - - #if ENABLED(BEZIER_JERK_CONTROL) - // If this is the 1st time we process the 2nd half of the trapezoid... - if (!bezier_2nd_half) { - // Initialize the Bézier speed curve - _calc_bezier_curve_coeffs(current_block->cruise_rate, current_block->final_rate, current_block->deceleration_time_inverse); - bezier_2nd_half = true; - } - - // Calculate the next speed to use - step_rate = deceleration_time < current_block->deceleration_time - ? _eval_bezier_curve(deceleration_time) - : current_block->final_rate; - #else - - // Using the old trapezoidal control - step_rate = STEP_MULTIPLY(deceleration_time, current_block->acceleration_rate); - if (step_rate < acc_step_rate) { // Still decelerating? - step_rate = acc_step_rate - step_rate; - NOLESS(step_rate, current_block->final_rate); - } - else - step_rate = current_block->final_rate; - #endif - - // step_rate to timer interval - interval = calc_timer_interval(step_rate); - deceleration_time += interval; - - #if ENABLED(LIN_ADVANCE) - if (current_block->use_advance_lead) { - if (step_events_completed <= current_block->decelerate_after + step_loops || (e_steps && eISR_Rate != current_block->advance_speed)) { - nextAdvanceISR = 0; // Wake up eISR on first deceleration loop - eISR_Rate = current_block->advance_speed; - } - } - else { - eISR_Rate = ADV_NEVER; - if (e_steps) nextAdvanceISR = 0; - } - #endif // LIN_ADVANCE - } - else { - - #if ENABLED(LIN_ADVANCE) - // If there are any esteps, fire the next advance_isr "now" - if (e_steps && eISR_Rate != current_block->advance_speed) nextAdvanceISR = 0; - #endif - - // The timer interval is just the nominal value for the nominal speed - interval = ticks_nominal; - - // Ensure this runs at the correct step rate, even if it just came off an acceleration - step_loops = step_loops_nominal; - } - // If current block is finished, reset pointer - if (all_steps_done) { + if (step_events_completed >= step_event_count) { axis_did_move = 0; current_block = NULL; planner.discard_current_block(); } + else { + // Step events not completed yet... + + // Are we in acceleration phase ? + if (step_events_completed <= accelerate_until) { // Calculate new timer value + + #if ENABLED(S_CURVE_ACCELERATION) + // Get the next speed to use (Jerk limited!) + uint32_t acc_step_rate = + acceleration_time < current_block->acceleration_time + ? _eval_bezier_curve(acceleration_time) + : current_block->cruise_rate; + #else + acc_step_rate = STEP_MULTIPLY(acceleration_time, current_block->acceleration_rate) + current_block->initial_rate; + NOMORE(acc_step_rate, current_block->nominal_rate); + #endif + + // acc_step_rate is in steps/second + + // step_rate to timer interval and steps per stepper isr + interval = calc_timer_interval(acc_step_rate, oversampling_factor, &steps_per_isr); + acceleration_time += interval; + + #if ENABLED(LIN_ADVANCE) + if (LA_use_advance_lead) { + // Wake up eISR on first acceleration loop and fire ISR if final adv_rate is reached + if (step_events_completed == steps_per_isr || (LA_steps && LA_isr_rate != current_block->advance_speed)) { + nextAdvanceISR = 0; + LA_isr_rate = current_block->advance_speed; + } + } + else { + LA_isr_rate = LA_ADV_NEVER; + if (LA_steps) nextAdvanceISR = 0; + } + #endif // LIN_ADVANCE + } + // Are we in Deceleration phase ? + else if (step_events_completed > decelerate_after) { + uint32_t step_rate; + + #if ENABLED(S_CURVE_ACCELERATION) + // If this is the 1st time we process the 2nd half of the trapezoid... + if (!bezier_2nd_half) { + // Initialize the Bézier speed curve + _calc_bezier_curve_coeffs(current_block->cruise_rate, current_block->final_rate, current_block->deceleration_time_inverse); + bezier_2nd_half = true; + // The first point starts at cruise rate. Just save evaluation of the Bézier curve + step_rate = current_block->cruise_rate; + } + else { + // Calculate the next speed to use + step_rate = deceleration_time < current_block->deceleration_time + ? _eval_bezier_curve(deceleration_time) + : current_block->final_rate; + } + #else + + // Using the old trapezoidal control + step_rate = STEP_MULTIPLY(deceleration_time, current_block->acceleration_rate); + if (step_rate < acc_step_rate) { // Still decelerating? + step_rate = acc_step_rate - step_rate; + NOLESS(step_rate, current_block->final_rate); + } + else + step_rate = current_block->final_rate; + #endif + + // step_rate is in steps/second + + // step_rate to timer interval and steps per stepper isr + interval = calc_timer_interval(step_rate, oversampling_factor, &steps_per_isr); + deceleration_time += interval; + + #if ENABLED(LIN_ADVANCE) + if (LA_use_advance_lead) { + if (step_events_completed <= decelerate_after + steps_per_isr || + (LA_steps && LA_isr_rate != current_block->advance_speed) + ) { + nextAdvanceISR = 0; // Wake up eISR on first deceleration loop + LA_isr_rate = current_block->advance_speed; + } + } + else { + LA_isr_rate = LA_ADV_NEVER; + if (LA_steps) nextAdvanceISR = 0; + } + #endif // LIN_ADVANCE + } + // We must be in cruise phase otherwise + else { + + #if ENABLED(LIN_ADVANCE) + // If there are any esteps, fire the next advance_isr "now" + if (LA_steps && LA_isr_rate != current_block->advance_speed) nextAdvanceISR = 0; + #endif + + // Calculate the ticks_nominal for this nominal speed, if not done yet + if (ticks_nominal < 0) { + // step_rate to timer interval and loops for the nominal speed + ticks_nominal = calc_timer_interval(current_block->nominal_rate, oversampling_factor, &steps_per_isr); + } + + // The timer interval is just the nominal value for the nominal speed + interval = ticks_nominal; + } + } } // If there is no current block at this point, attempt to pop one from the buffer @@ -1646,25 +1613,83 @@ uint32_t Stepper::stepper_block_phase_isr() { //if (!!current_block->steps[C_AXIS]) SBI(axis_bits, Z_HEAD); axis_did_move = axis_bits; + // No acceleration / deceleration time elapsed so far + acceleration_time = deceleration_time = 0; + + uint8_t oversampling = 0; // Assume we won't use it + + #if ENABLED(ADAPTIVE_STEP_SMOOTHING) + // At this point, we must decide if we can use Stepper movement axis smoothing. + uint32_t max_rate = current_block->nominal_rate; // Get the maximum rate (maximum event speed) + while (max_rate < MIN_STEP_ISR_FREQUENCY) { + max_rate <<= 1; + if (max_rate >= MAX_STEP_ISR_FREQUENCY_1X) break; + ++oversampling; + } + oversampling_factor = oversampling; + #endif + + // Based on the oversampling factor, do the calculations + step_event_count = current_block->step_event_count << oversampling; + + // Initialize Bresenham delta errors to 1/2 + delta_error[X_AXIS] = delta_error[Y_AXIS] = delta_error[Z_AXIS] = delta_error[E_AXIS] = -int32_t(step_event_count); + + // Calculate Bresenham dividends + advance_dividend[X_AXIS] = current_block->steps[X_AXIS] << 1; + advance_dividend[Y_AXIS] = current_block->steps[Y_AXIS] << 1; + advance_dividend[Z_AXIS] = current_block->steps[Z_AXIS] << 1; + advance_dividend[E_AXIS] = current_block->steps[E_AXIS] << 1; + + // Calculate Bresenham divisor + advance_divisor = step_event_count << 1; + + // No step events completed so far + step_events_completed = 0; + + // Compute the acceleration and deceleration points + accelerate_until = current_block->accelerate_until << oversampling; + 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; + #else + active_extruder = current_block->active_extruder; + #endif + // Initialize the trapezoid generator from the current block. #if ENABLED(LIN_ADVANCE) - #if E_STEPPERS > 1 - if (current_block->active_extruder != last_movement_extruder) { - current_adv_steps = 0; // If the now active extruder wasn't in use during the last move, its pressure is most likely gone. - LA_active_extruder = current_block->active_extruder; - } + #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; #endif - if ((use_advance_lead = current_block->use_advance_lead)) { - LA_decelerate_after = current_block->decelerate_after; - final_adv_steps = current_block->final_adv_steps; - max_adv_steps = current_block->max_adv_steps; + if ((LA_use_advance_lead = current_block->use_advance_lead)) { + LA_final_adv_steps = current_block->final_adv_steps; + LA_max_adv_steps = current_block->max_adv_steps; } #endif - if (current_block->direction_bits != last_direction_bits || current_block->active_extruder != last_movement_extruder) { + if (current_block->direction_bits != last_direction_bits + #if DISABLED(MIXING_EXTRUDER) + || active_extruder != last_moved_extruder + #endif + ) { last_direction_bits = current_block->direction_bits; - last_movement_extruder = current_block->active_extruder; + #if DISABLED(MIXING_EXTRUDER) + last_moved_extruder = active_extruder; + #endif set_directions(); } @@ -1675,46 +1700,32 @@ uint32_t Stepper::stepper_block_phase_isr() { // done against the endstop. So, check the limits here: If the movement // is against the limits, the block will be marked as to be killed, and // on the next call to this ISR, will be discarded. - endstops.check_possible_change(); + endstops.update(); - // No acceleration / deceleration time elapsed so far - acceleration_time = deceleration_time = 0; + #if ENABLED(Z_LATE_ENABLE) + // If delayed Z enable, enable it now. This option will severely interfere with + // timing between pulses when chaining motion between blocks, and it could lead + // to lost steps in both X and Y axis, so avoid using it unless strictly necessary!! + if (current_block->steps[Z_AXIS]) enable_Z(); + #endif - // No step events completed so far - step_events_completed = 0; + // Mark the time_nominal as not calculated yet + ticks_nominal = -1; - // step_rate to timer interval for the nominal speed - ticks_nominal = calc_timer_interval(current_block->nominal_rate); - - // make a note of the number of step loops required at nominal speed - step_loops_nominal = step_loops; - - #if DISABLED(BEZIER_JERK_CONTROL) + #if DISABLED(S_CURVE_ACCELERATION) // Set as deceleration point the initial rate of the block acc_step_rate = current_block->initial_rate; #endif - #if ENABLED(BEZIER_JERK_CONTROL) + #if ENABLED(S_CURVE_ACCELERATION) // Initialize the Bézier speed curve _calc_bezier_curve_coeffs(current_block->initial_rate, current_block->cruise_rate, current_block->acceleration_time_inverse); - - // We have not started the 2nd half of the trapezoid + // We haven't started the 2nd half of the trapezoid bezier_2nd_half = false; #endif - // Initialize Bresenham counters to 1/2 the ceiling - counter_X = counter_Y = counter_Z = counter_E = -((int32_t)(current_block->step_event_count >> 1)); - #if ENABLED(MIXING_EXTRUDER) - MIXING_STEPPERS_LOOP(i) - counter_m[i] = -(current_block->mix_event_count[i] >> 1); - #endif - - #if ENABLED(Z_LATE_ENABLE) - // If delayed Z enable, enable it now. This option will severely interfere with - // timing between pulses when chaining motion between blocks, and it could lead - // to lost steps in both X and Y axis, so avoid using it unless strictly necessary!! - if (current_block->steps[Z_AXIS]) enable_Z(); - #endif + // Calculate the initial timer interval + interval = calc_timer_interval(current_block->initial_rate, oversampling_factor, &steps_per_isr); } } @@ -1724,164 +1735,121 @@ uint32_t Stepper::stepper_block_phase_isr() { #if ENABLED(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; + // Timer interrupt for E. LA_steps is set in the main routine uint32_t Stepper::advance_isr() { uint32_t interval; - #if ENABLED(MK2_MULTIPLEXER) // For SNMM even-numbered steppers are reversed - #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) E0_DIR_WRITE(e_steps < 0 ? !INVERT_E## INDEX ##_DIR ^ TEST(INDEX, 0) : INVERT_E## INDEX ##_DIR ^ TEST(INDEX, 0)); }while(0) - #elif ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE) - #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) { if (e_steps < 0) REV_E_DIR(); else NORM_E_DIR(); } }while(0) - #elif ENABLED(SWITCHING_EXTRUDER) - #if EXTRUDERS > 4 - #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) { switch (INDEX) { \ - case 0: case 1: E0_DIR_WRITE(!INVERT_E0_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); break; \ - case 2: case 3: E1_DIR_WRITE(!INVERT_E1_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); break; \ - case 4: E2_DIR_WRITE(!INVERT_E2_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); \ - } } }while(0) - #elif EXTRUDERS > 2 - #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) { switch (INDEX) { \ - case 0: case 1: E0_DIR_WRITE(!INVERT_E0_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); break; \ - case 2: case 3: E1_DIR_WRITE(!INVERT_E1_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); break; \ - } } }while(0) - #else - #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) E0_DIR_WRITE(!INVERT_E0_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); }while(0) - #endif - #else - #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) E## INDEX ##_DIR_WRITE(!INVERT_E## INDEX ##_DIR ^ (e_steps < 0)); }while(0) - #endif - - #if ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE) - #define START_E_PULSE(INDEX) do{ if (e_steps) E_STEP_WRITE(!INVERT_E_STEP_PIN); }while(0) - #define STOP_E_PULSE(INDEX) do{ if (e_steps) { e_steps < 0 ? ++e_steps : --e_steps; E_STEP_WRITE(INVERT_E_STEP_PIN); } }while(0) - #elif ENABLED(SWITCHING_EXTRUDER) - #if EXTRUDERS > 4 - #define START_E_PULSE(INDEX) do{ if (e_steps) { switch (INDEX) { \ - case 0: case 1: E0_DIR_WRITE(!INVERT_E_STEP_PIN); break; \ - case 2: case 3: E1_DIR_WRITE(!INVERT_E_STEP_PIN); break; \ - case 4: E2_DIR_WRITE(!INVERT_E_STEP_PIN); } \ - } }while(0) - #define STOP_E_PULSE(INDEX) do{ if (e_steps) { \ - e_steps < 0 ? ++e_steps : --e_steps; \ - switch (INDEX) { \ - case 0: case 1: E0_DIR_WRITE(INVERT_E_STEP_PIN); break; \ - case 2: case 3: E1_DIR_WRITE(INVERT_E_STEP_PIN); break; \ - case 4: E2_DIR_WRITE(INVERT_E_STEP_PIN); } \ - } }while(0) - #elif EXTRUDERS > 2 - #define START_E_PULSE(INDEX) do{ if (e_steps) { if (INDEX < 2) E0_DIR_WRITE(!INVERT_E_STEP_PIN); else E1_DIR_WRITE(!INVERT_E_STEP_PIN); } }while(0) - #define STOP_E_PULSE(INDEX) do{ if (e_steps) { \ - e_steps < 0 ? ++e_steps : --e_steps; \ - if (INDEX < 2) E0_DIR_WRITE(INVERT_E_STEP_PIN); else E1_DIR_WRITE(INVERT_E_STEP_PIN); \ - } }while(0) - #else - #define START_E_PULSE(INDEX) do{ if (e_steps) E0_DIR_WRITE(!INVERT_E_STEP_PIN); }while(0) - #define STOP_E_PULSE(INDEX) do{ if (e_steps) { e_steps < 0 ? ++e_steps : --e_steps; E0_DIR_WRITE(INVERT_E_STEP_PIN); }while(0) - #endif - #else - #define START_E_PULSE(INDEX) do{ if (e_steps) E## INDEX ##_STEP_WRITE(!INVERT_E_STEP_PIN); }while(0) - #define STOP_E_PULSE(INDEX) do { if (e_steps) { e_steps < 0 ? ++e_steps : --e_steps; E## INDEX ##_STEP_WRITE(INVERT_E_STEP_PIN); } }while(0) - #endif - - if (use_advance_lead) { - if (step_events_completed > LA_decelerate_after && current_adv_steps > final_adv_steps) { - e_steps--; - current_adv_steps--; - interval = eISR_Rate; + if (LA_use_advance_lead) { + if (step_events_completed > decelerate_after && LA_current_adv_steps > LA_final_adv_steps) { + LA_steps--; + LA_current_adv_steps--; + interval = LA_isr_rate; } - else if (step_events_completed < LA_decelerate_after && current_adv_steps < max_adv_steps) { - //step_events_completed <= (uint32_t)current_block->accelerate_until) { - e_steps++; - current_adv_steps++; - interval = eISR_Rate; - } - else { - interval = ADV_NEVER; - eISR_Rate = ADV_NEVER; + else if (step_events_completed < decelerate_after && LA_current_adv_steps < LA_max_adv_steps) { + //step_events_completed <= (uint32_t)accelerate_until) { + LA_steps++; + LA_current_adv_steps++; + interval = LA_isr_rate; } + else + interval = LA_isr_rate = LA_ADV_NEVER; } else - interval = ADV_NEVER; + interval = LA_ADV_NEVER; - switch (LA_active_extruder) { - case 0: SET_E_STEP_DIR(0); break; - #if EXTRUDERS > 1 - case 1: SET_E_STEP_DIR(1); break; - #if EXTRUDERS > 2 - case 2: SET_E_STEP_DIR(2); break; - #if EXTRUDERS > 3 - case 3: SET_E_STEP_DIR(3); break; - #if EXTRUDERS > 4 - case 4: SET_E_STEP_DIR(4); break; - #endif // EXTRUDERS > 4 - #endif // EXTRUDERS > 3 - #endif // EXTRUDERS > 2 - #endif // EXTRUDERS > 1 - } + #if ENABLED(MIXING_EXTRUDER) + if (LA_steps >= 0) + MIXING_STEPPERS_LOOP(j) NORM_E_DIR(j); + else + MIXING_STEPPERS_LOOP(j) REV_E_DIR(j); + #else + if (LA_steps >= 0) + NORM_E_DIR(active_extruder); + else + REV_E_DIR(active_extruder); + #endif + + // Get the timer count and estimate the end of the pulse + hal_timer_t pulse_end = HAL_timer_get_count(PULSE_TIMER_NUM) + hal_timer_t(MIN_PULSE_TICKS); + + const hal_timer_t added_step_ticks = hal_timer_t(ADDED_STEP_TICKS); // Step E stepper if we have steps - while (e_steps) { + while (LA_steps) { - #if EXTRA_CYCLES_E > 20 - hal_timer_t pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM); + // 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); + } + #else + E_STEP_WRITE(active_extruder, !INVERT_E_STEP_PIN); #endif - switch (LA_active_extruder) { - case 0: START_E_PULSE(0); break; - #if EXTRUDERS > 1 - case 1: START_E_PULSE(1); break; - #if EXTRUDERS > 2 - case 2: START_E_PULSE(2); break; - #if EXTRUDERS > 3 - case 3: START_E_PULSE(3); break; - #if EXTRUDERS > 4 - case 4: START_E_PULSE(4); break; - #endif // EXTRUDERS > 4 - #endif // EXTRUDERS > 3 - #endif // EXTRUDERS > 2 - #endif // EXTRUDERS > 1 - } - - // For minimum pulse time wait before stopping pulses - #if EXTRA_CYCLES_E > 20 - while (EXTRA_CYCLES_E > (hal_timer_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ } - pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM); - #elif EXTRA_CYCLES_E > 0 - DELAY_NS(EXTRA_CYCLES_E * NANOSECONDS_PER_CYCLE); + // Enforce a minimum duration for STEP pulse ON + #if MINIMUM_STEPPER_PULSE + // Just wait for the requested pulse duration + while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { /* nada */ } #endif - switch (LA_active_extruder) { - case 0: STOP_E_PULSE(0); break; - #if EXTRUDERS > 1 - case 1: STOP_E_PULSE(1); break; - #if EXTRUDERS > 2 - case 2: STOP_E_PULSE(2); break; - #if EXTRUDERS > 3 - case 3: STOP_E_PULSE(3); break; - #if EXTRUDERS > 4 - case 4: STOP_E_PULSE(4); break; - #endif // EXTRUDERS > 4 - #endif // EXTRUDERS > 3 - #endif // EXTRUDERS > 2 - #endif // EXTRUDERS > 1 - } + // Add the delay needed to ensure the maximum driver rate is enforced + if (signed(added_step_ticks) > 0) pulse_end += hal_timer_t(added_step_ticks); + + LA_steps < 0 ? ++LA_steps : --LA_steps; + + // 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); + } + } + #else + E_STEP_WRITE(active_extruder, INVERT_E_STEP_PIN); + #endif // For minimum pulse time wait before looping - #if EXTRA_CYCLES_E > 20 - if (e_steps) while (EXTRA_CYCLES_E > (hal_timer_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ } - #elif EXTRA_CYCLES_E > 0 - if (e_steps) DELAY_NS(EXTRA_CYCLES_E * NANOSECONDS_PER_CYCLE); - #endif - - } // e_steps + // Just wait for the requested pulse duration + if (LA_steps) { + while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { /* nada */ } + #if MINIMUM_STEPPER_PULSE + // Add to the value, the time that the pulse must be active (to be used on the next loop) + pulse_end += hal_timer_t(MIN_PULSE_TICKS); + #endif + } + } // LA_steps return interval; } #endif // LIN_ADVANCE +// Check if the given block is busy or not - Must not be called from ISR contexts +// The current_block could change in the middle of the read by an Stepper ISR, so +// we must explicitly prevent that! +bool Stepper::is_block_busy(const block_t* const block) { + #define sw_barrier() asm volatile("": : :"memory"); + + // Keep reading until 2 consecutive reads return the same value, + // meaning there was no update in-between caused by an interrupt. + // This works because stepper ISRs happen at a slower rate than + // successive reads of a variable, so 2 consecutive reads with + // the same value means no interrupt updated it. + block_t* vold, *vnew = current_block; + sw_barrier(); + do { + vold = vnew; + vnew = current_block; + sw_barrier(); + } while (vold != vnew); + + // Return if the block is busy or not + return block == vnew; +} + void Stepper::init() { // Init Digipot Motor Current @@ -2011,38 +1979,24 @@ void Stepper::init() { AXIS_INIT(Z, Z); #endif - #if HAS_E0_STEP + #if E_STEPPERS > 0 && HAS_E0_STEP E_AXIS_INIT(0); #endif - #if HAS_E1_STEP + #if E_STEPPERS > 1 && HAS_E1_STEP E_AXIS_INIT(1); #endif - #if HAS_E2_STEP + #if E_STEPPERS > 2 && HAS_E2_STEP E_AXIS_INIT(2); #endif - #if HAS_E3_STEP + #if E_STEPPERS > 3 && HAS_E3_STEP E_AXIS_INIT(3); #endif - #if HAS_E4_STEP + #if E_STEPPERS > 4 && HAS_E4_STEP E_AXIS_INIT(4); #endif - // waveform generation = 0100 = CTC - SET_WGM(1, CTC_OCRnA); - - // output mode = 00 (disconnected) - SET_COMA(1, NORMAL); - - // Set the timer pre-scaler - // Generally we use a divider of 8, resulting in a 2MHz timer - // frequency on a 16MHz MCU. If you are going to change this, be - // sure to regenerate speed_lookuptable.h with - // create_speed_lookuptable.py - SET_CS(1, PRESCALER_8); // CS 2 = 1/8 prescaler - // Init Stepper ISR to 122 Hz for quick starting - OCR1A = 0x4000; - TCNT1 = 0; + HAL_timer_start(STEP_TIMER_NUM, 122); // OCR1A = 0x4000 ENABLE_STEPPER_DRIVER_INTERRUPT(); @@ -2091,14 +2045,11 @@ void Stepper::_set_position(const int32_t &a, const int32_t &b, const int32_t &c * Get a stepper's position in steps. */ int32_t Stepper::position(const AxisEnum axis) { - // Protect the access to the position. Only required for AVR, as - // any 32bit CPU offers atomic access to 32bit variables const bool was_enabled = STEPPER_ISR_ENABLED(); if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT(); const int32_t v = count_position[axis]; - // Reenable Stepper ISR if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT(); return v; } @@ -2134,14 +2085,11 @@ void Stepper::endstop_triggered(const AxisEnum axis) { } int32_t Stepper::triggered_position(const AxisEnum axis) { - // Protect the access to the position. Only required for AVR, as - // any 32bit CPU offers atomic access to 32bit variables const bool was_enabled = STEPPER_ISR_ENABLED(); if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT(); const int32_t v = endstops_trigsteps[axis]; - // Reenable Stepper ISR if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT(); return v; @@ -2185,6 +2133,12 @@ void Stepper::report_positions() { #if ENABLED(BABYSTEPPING) + #if MINIMUM_STEPPER_PULSE + #define STEP_PULSE_CYCLES ((MINIMUM_STEPPER_PULSE) * CYCLES_PER_MICROSECOND) + #else + #define STEP_PULSE_CYCLES 0 + #endif + #if ENABLED(DELTA) #define CYCLES_EATEN_BABYSTEP (2 * 15) #else @@ -2198,8 +2152,8 @@ void Stepper::report_positions() { #define _APPLY_DIR(AXIS, INVERT) AXIS ##_APPLY_DIR(INVERT, true) #if EXTRA_CYCLES_BABYSTEP > 20 - #define _SAVE_START const hal_timer_t pulse_start = HAL_timer_get_count(STEP_TIMER_NUM) - #define _PULSE_WAIT while (EXTRA_CYCLES_BABYSTEP > (uint32_t)(HAL_timer_get_count(STEP_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ } + #define _SAVE_START const hal_timer_t pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM) + #define _PULSE_WAIT while (EXTRA_CYCLES_BABYSTEP > (uint32_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ } #else #define _SAVE_START NOOP #if EXTRA_CYCLES_BABYSTEP > 0 diff --git a/Marlin/stepper.h b/Marlin/stepper.h index 5cded30ed0..2ac9c7756a 100644 --- a/Marlin/stepper.h +++ b/Marlin/stepper.h @@ -43,15 +43,158 @@ #ifndef STEPPER_H #define STEPPER_H +#include "MarlinConfig.h" + +// Disable multiple steps per ISR +//#define DISABLE_MULTI_STEPPING + +// +// Estimate the amount of time the Stepper ISR will take to execute +// + +#ifndef MINIMUM_STEPPER_PULSE + #define MINIMUM_STEPPER_PULSE 0UL +#endif + +#ifndef MAXIMUM_STEPPER_RATE + #if MINIMUM_STEPPER_PULSE + #define MAXIMUM_STEPPER_RATE (1000000UL / (2UL * (unsigned long)(MINIMUM_STEPPER_PULSE))) + #else + #define MAXIMUM_STEPPER_RATE 500000UL + #endif +#endif + +// The base ISR takes 752 cycles +#define ISR_BASE_CYCLES 752UL + +// Linear advance base time is 32 cycles +#if ENABLED(LIN_ADVANCE) + #define ISR_LA_BASE_CYCLES 32UL +#else + #define ISR_LA_BASE_CYCLES 0UL +#endif + +// S curve interpolation adds 160 cycles +#if ENABLED(S_CURVE_ACCELERATION) + #define ISR_S_CURVE_CYCLES 160UL +#else + #define ISR_S_CURVE_CYCLES 0UL +#endif + +// Stepper Loop base cycles +#define ISR_LOOP_BASE_CYCLES 32UL + +// To start the step pulse, in the worst case takes +#define ISR_START_STEPPER_CYCLES 57UL + +// And each stepper (start + stop pulse) takes in worst case +#define ISR_STEPPER_CYCLES 88UL + +// Add time for each stepper +#ifdef HAS_X_STEP + #define ISR_START_X_STEPPER_CYCLES ISR_START_STEPPER_CYCLES + #define ISR_X_STEPPER_CYCLES ISR_STEPPER_CYCLES +#else + #define ISR_START_X_STEPPER_CYCLES 0UL + #define ISR_X_STEPPER_CYCLES 0UL +#endif +#ifdef HAS_Y_STEP + #define ISR_START_Y_STEPPER_CYCLES ISR_START_STEPPER_CYCLES + #define ISR_Y_STEPPER_CYCLES ISR_STEPPER_CYCLES +#else + #define ISR_START_Y_STEPPER_CYCLES 0UL + #define ISR_Y_STEPPER_CYCLES 0UL +#endif +#ifdef HAS_Z_STEP + #define ISR_START_Z_STEPPER_CYCLES ISR_START_STEPPER_CYCLES + #define ISR_Z_STEPPER_CYCLES ISR_STEPPER_CYCLES +#else + #define ISR_START_Z_STEPPER_CYCLES 0UL + #define ISR_Z_STEPPER_CYCLES 0UL +#endif + +// E is always interpolated, even for mixing extruders +#define ISR_START_E_STEPPER_CYCLES ISR_START_STEPPER_CYCLES +#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) + #define ISR_START_MIXING_STEPPER_CYCLES ((MIXING_STEPPERS) * (ISR_START_STEPPER_CYCLES)) + #define ISR_MIXING_STEPPER_CYCLES ((MIXING_STEPPERS) * (ISR_STEPPER_CYCLES)) +#else + #define ISR_START_MIXING_STEPPER_CYCLES 0UL + #define ISR_MIXING_STEPPER_CYCLES 0UL +#endif + +// Calculate the minimum time to start all stepper pulses in the ISR loop +#define MIN_ISR_START_LOOP_CYCLES (ISR_START_X_STEPPER_CYCLES + ISR_START_Y_STEPPER_CYCLES + ISR_START_Z_STEPPER_CYCLES + ISR_START_E_STEPPER_CYCLES + ISR_START_MIXING_STEPPER_CYCLES) + +// And the total minimum loop time, not including the base +#define MIN_ISR_LOOP_CYCLES (ISR_X_STEPPER_CYCLES + ISR_Y_STEPPER_CYCLES + ISR_Z_STEPPER_CYCLES + ISR_E_STEPPER_CYCLES + ISR_MIXING_STEPPER_CYCLES) + +// Calculate the minimum MPU cycles needed per pulse to enforce, limited to the max stepper rate +#define _MIN_STEPPER_PULSE_CYCLES(N) MAX((unsigned long)((F_CPU) / (MAXIMUM_STEPPER_RATE)), ((F_CPU) / 500000UL) * (N)) +#if MINIMUM_STEPPER_PULSE + #define MIN_STEPPER_PULSE_CYCLES _MIN_STEPPER_PULSE_CYCLES((unsigned long)(MINIMUM_STEPPER_PULSE)) +#else + #define MIN_STEPPER_PULSE_CYCLES _MIN_STEPPER_PULSE_CYCLES(1UL) +#endif + +// Calculate the minimum ticks of the PULSE timer that must elapse with the step pulse enabled +// adding the "start stepper pulse" code section execution cycles to account for that not all +// pulses start at the beginning of the loop, so an extra time must be added to compensate so +// the last generated pulse (usually the extruder stepper) has the right length +#define MIN_PULSE_TICKS (((PULSE_TIMER_TICKS_PER_US) * (unsigned long)(MINIMUM_STEPPER_PULSE)) + ((MIN_ISR_START_LOOP_CYCLES) / (unsigned long)(PULSE_TIMER_PRESCALE))) + +// Calculate the extra ticks of the PULSE timer between step pulses +#define ADDED_STEP_TICKS (((MIN_STEPPER_PULSE_CYCLES) / (PULSE_TIMER_PRESCALE)) - (MIN_PULSE_TICKS)) + +// But the user could be enforcing a minimum time, so the loop time is +#define ISR_LOOP_CYCLES (ISR_LOOP_BASE_CYCLES + MAX(MIN_STEPPER_PULSE_CYCLES, MIN_ISR_LOOP_CYCLES)) + +// If linear advance is enabled, then it is handled separately +#if ENABLED(LIN_ADVANCE) + + // Estimate the minimum LA loop time + #if ENABLED(MIXING_EXTRUDER) + #define MIN_ISR_LA_LOOP_CYCLES ((MIXING_STEPPERS) * (ISR_STEPPER_CYCLES)) + #else + #define MIN_ISR_LA_LOOP_CYCLES ISR_STEPPER_CYCLES + #endif + + // And the real loop time + #define ISR_LA_LOOP_CYCLES MAX(MIN_STEPPER_PULSE_CYCLES, MIN_ISR_LA_LOOP_CYCLES) + +#else + #define ISR_LA_LOOP_CYCLES 0UL +#endif + +// Now estimate the total ISR execution time in cycles given a step per ISR multiplier +#define ISR_EXECUTION_CYCLES(R) (((ISR_BASE_CYCLES + ISR_S_CURVE_CYCLES + (ISR_LOOP_CYCLES) * (R) + ISR_LA_BASE_CYCLES + ISR_LA_LOOP_CYCLES)) / (R)) + +// The maximum allowable stepping frequency when doing x128-x1 stepping (in Hz) +#define MAX_STEP_ISR_FREQUENCY_128X ((F_CPU) / ISR_EXECUTION_CYCLES(128)) +#define MAX_STEP_ISR_FREQUENCY_64X ((F_CPU) / ISR_EXECUTION_CYCLES(64)) +#define MAX_STEP_ISR_FREQUENCY_32X ((F_CPU) / ISR_EXECUTION_CYCLES(32)) +#define MAX_STEP_ISR_FREQUENCY_16X ((F_CPU) / ISR_EXECUTION_CYCLES(16)) +#define MAX_STEP_ISR_FREQUENCY_8X ((F_CPU) / ISR_EXECUTION_CYCLES(8)) +#define MAX_STEP_ISR_FREQUENCY_4X ((F_CPU) / ISR_EXECUTION_CYCLES(4)) +#define MAX_STEP_ISR_FREQUENCY_2X ((F_CPU) / ISR_EXECUTION_CYCLES(2)) +#define MAX_STEP_ISR_FREQUENCY_1X ((F_CPU) / ISR_EXECUTION_CYCLES(1)) + +// The minimum allowable frequency for step smoothing will be 1/10 of the maximum nominal frequency (in Hz) +#define MIN_STEP_ISR_FREQUENCY MAX_STEP_ISR_FREQUENCY_1X + +// +// Stepper class definition +// + #include "planner.h" #include "speed_lookuptable.h" #include "stepper_indirection.h" #include "language.h" #include "types.h" -class Stepper; -extern Stepper stepper; - // intRes = intIn1 * intIn2 >> 16 // uses: // r26 to store 0 @@ -83,10 +226,8 @@ class Stepper { public: - static block_t* current_block; // A pointer to the block currently being traced - #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS) - static bool performing_homing; + static bool homing_dual_axis; #endif #if HAS_MOTOR_CURRENT_PWM @@ -98,26 +239,57 @@ class Stepper { private: + static block_t* current_block; // A pointer to the block currently being traced + static uint8_t last_direction_bits, // The next stepping-bits to be output - last_movement_extruder, // Last movement extruder, as computed when the last movement was fetched from planner axis_did_move; // Last Movement in the given direction is not null, as computed when the last movement was fetched from planner + static bool abort_current_block; // Signals to the stepper that current block should be aborted + #if DISABLED(MIXING_EXTRUDER) + static uint8_t last_moved_extruder; // Last-moved extruder, as set when the last movement was fetched from planner + #endif + #if ENABLED(X_DUAL_ENDSTOPS) - static bool locked_x_motor, locked_x2_motor; + static bool locked_X_motor, locked_X2_motor; #endif #if ENABLED(Y_DUAL_ENDSTOPS) - static bool locked_y_motor, locked_y2_motor; + static bool locked_Y_motor, locked_Y2_motor; #endif #if ENABLED(Z_DUAL_ENDSTOPS) - static bool locked_z_motor, locked_z2_motor; + static bool locked_Z_motor, locked_Z2_motor; #endif - // Counter variables for the Bresenham line tracer - static int32_t counter_X, counter_Y, counter_Z, counter_E; - static uint32_t step_events_completed; // The number of step events executed in the current block + static uint32_t acceleration_time, deceleration_time; // time measured in Stepper Timer ticks + static uint8_t steps_per_isr; // Count of steps to perform per Stepper ISR call - #if ENABLED(BEZIER_JERK_CONTROL) + #if ENABLED(ADAPTIVE_STEP_SMOOTHING) + static uint8_t oversampling_factor; // Oversampling factor (log2(multiplier)) to increase temporal resolution of axis + #else + static constexpr uint8_t oversampling_factor = 0; + #endif + + // Delta error variables for the Bresenham line tracer + static int32_t delta_error[XYZE]; + static uint32_t advance_dividend[XYZE], + advance_divisor, + step_events_completed, // The number of step events executed in the current block + accelerate_until, // The point from where we need to stop acceleration + 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++) + #else + static int8_t active_extruder; // Active extruder + #endif + + #if ENABLED(S_CURVE_ACCELERATION) static int32_t bezier_A, // A coefficient in Bézier speed curve bezier_B, // B coefficient in Bézier speed curve bezier_C; // C coefficient in Bézier speed curve @@ -128,33 +300,19 @@ class Stepper { #endif static uint32_t nextMainISR; // time remaining for the next Step ISR - static bool all_steps_done; // all steps done - #if ENABLED(LIN_ADVANCE) - - static uint32_t LA_decelerate_after; // Copy from current executed block. Needed because current_block is set to NULL "too early". - static uint32_t nextAdvanceISR, eISR_Rate; - static uint16_t current_adv_steps, final_adv_steps, max_adv_steps; // Copy from current executed block. Needed because current_block is set to NULL "too early". - static int8_t e_steps; - static bool use_advance_lead; - #if E_STEPPERS > 1 - static int8_t LA_active_extruder; // Copy from current executed block. Needed because current_block is set to NULL "too early". - #else - static constexpr int8_t LA_active_extruder = 0; - #endif - + static uint32_t nextAdvanceISR, LA_isr_rate; + static uint16_t LA_current_adv_steps, LA_final_adv_steps, LA_max_adv_steps; // Copy from current executed block. Needed because current_block is set to NULL "too early". + static int8_t LA_steps; + static bool LA_use_advance_lead; #endif // LIN_ADVANCE - static uint32_t acceleration_time, deceleration_time; - static uint8_t step_loops, step_loops_nominal; - - static uint32_t ticks_nominal; - #if DISABLED(BEZIER_JERK_CONTROL) + static int32_t ticks_nominal; + #if DISABLED(S_CURVE_ACCELERATION) static uint32_t acc_step_rate; // needed for deceleration start point #endif static volatile int32_t endstops_trigsteps[XYZ]; - static volatile int32_t endstops_stepsTotal, endstops_stepsDone; // // Positions of stepper motors, in step units @@ -164,17 +322,7 @@ class Stepper { // // Current direction of stepper motors (+1 or -1) // - static volatile signed char count_direction[NUM_AXIS]; - - // - // Mixing extruder mix counters - // - #if ENABLED(MIXING_EXTRUDER) - static int32_t counter_m[MIXING_STEPPERS]; - #define MIXING_STEPPERS_LOOP(VAR) \ - for (uint8_t VAR = 0; VAR < MIXING_STEPPERS; VAR++) \ - if (current_block->mix_event_count[VAR]) - #endif + static int8_t count_direction[NUM_AXIS]; public: @@ -189,7 +337,7 @@ class Stepper { // Interrupt Service Routines // The ISR scheduler - static hal_timer_t isr_scheduler(); + static void isr(); // The stepper pulse phase ISR static void stepper_pulse_phase_isr(); @@ -202,6 +350,9 @@ class Stepper { static uint32_t advance_isr(); #endif + // Check if the given block is busy or not - Must not be called from ISR contexts + static bool is_block_busy(const block_t* const block); + // Get the position of a stepper, in steps static int32_t position(const AxisEnum axis); @@ -222,7 +373,15 @@ class Stepper { FORCE_INLINE static bool axis_is_moving(const AxisEnum axis) { return TEST(axis_did_move, axis); } // The extruder associated to the last movement - FORCE_INLINE static uint8_t movement_extruder() { return last_movement_extruder; } + FORCE_INLINE static uint8_t movement_extruder() { + return + #if ENABLED(MIXING_EXTRUDER) + 0 + #else + last_moved_extruder + #endif + ; + } // Handle a triggered endstop static void endstop_triggered(const AxisEnum axis); @@ -241,20 +400,20 @@ class Stepper { static void microstep_readings(); #endif + #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS) + FORCE_INLINE static void set_homing_dual_axis(const bool state) { homing_dual_axis = state; } + #endif #if ENABLED(X_DUAL_ENDSTOPS) - FORCE_INLINE static void set_homing_flag_x(const bool state) { performing_homing = state; } - FORCE_INLINE static void set_x_lock(const bool state) { locked_x_motor = state; } - FORCE_INLINE static void set_x2_lock(const bool state) { locked_x2_motor = state; } + FORCE_INLINE static void set_x_lock(const bool state) { locked_X_motor = state; } + FORCE_INLINE static void set_x2_lock(const bool state) { locked_X2_motor = state; } #endif #if ENABLED(Y_DUAL_ENDSTOPS) - FORCE_INLINE static void set_homing_flag_y(const bool state) { performing_homing = state; } - FORCE_INLINE static void set_y_lock(const bool state) { locked_y_motor = state; } - FORCE_INLINE static void set_y2_lock(const bool state) { locked_y2_motor = state; } + FORCE_INLINE static void set_y_lock(const bool state) { locked_Y_motor = state; } + FORCE_INLINE static void set_y2_lock(const bool state) { locked_Y2_motor = state; } #endif #if ENABLED(Z_DUAL_ENDSTOPS) - FORCE_INLINE static void set_homing_flag_z(const bool state) { performing_homing = state; } - FORCE_INLINE static void set_z_lock(const bool state) { locked_z_motor = state; } - FORCE_INLINE static void set_z2_lock(const bool state) { locked_z2_motor = state; } + FORCE_INLINE static void set_z_lock(const bool state) { locked_Z_motor = state; } + FORCE_INLINE static void set_z2_lock(const bool state) { locked_Z2_motor = state; } #endif #if ENABLED(BABYSTEPPING) @@ -268,16 +427,21 @@ class Stepper { // Set the current position in steps inline static void set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e) { planner.synchronize(); - CRITICAL_SECTION_START; + const bool was_enabled = STEPPER_ISR_ENABLED(); + if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT(); _set_position(a, b, c, e); - CRITICAL_SECTION_END; + if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT(); } inline static void set_position(const AxisEnum a, const int32_t &v) { planner.synchronize(); - CRITICAL_SECTION_START; + + const bool was_enabled = STEPPER_ISR_ENABLED(); + if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT(); + count_position[a] = v; - CRITICAL_SECTION_END; + + if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT(); } private: @@ -288,25 +452,42 @@ class Stepper { // Set direction bits for all steppers static void set_directions(); - FORCE_INLINE static uint32_t calc_timer_interval(uint32_t step_rate) { + FORCE_INLINE static uint32_t calc_timer_interval(uint32_t step_rate, uint8_t scale, uint8_t* loops) { uint32_t timer; - NOMORE(step_rate, uint32_t(MAX_STEP_FREQUENCY)); + // Scale the frequency, as requested by the caller + step_rate <<= scale; - if (step_rate > 20000) { // If steprate > 20kHz >> step 4 times - step_rate >>= 2; - step_loops = 4; - } - else if (step_rate > 10000) { // If steprate > 10kHz >> step 2 times - step_rate >>= 1; - step_loops = 2; - } - else { - step_loops = 1; - } + uint8_t multistep = 1; + #if DISABLED(DISABLE_MULTI_STEPPING) - NOLESS(step_rate, uint32_t(F_CPU / 500000U)); - step_rate -= F_CPU / 500000; // Correct for minimal speed + // The stepping frequency limits for each multistepping rate + static const uint32_t limit[] PROGMEM = { + ( MAX_STEP_ISR_FREQUENCY_1X ), + ( MAX_STEP_ISR_FREQUENCY_2X >> 1), + ( MAX_STEP_ISR_FREQUENCY_4X >> 2), + ( MAX_STEP_ISR_FREQUENCY_8X >> 3), + ( MAX_STEP_ISR_FREQUENCY_16X >> 4), + ( MAX_STEP_ISR_FREQUENCY_32X >> 5), + ( MAX_STEP_ISR_FREQUENCY_64X >> 6), + (MAX_STEP_ISR_FREQUENCY_128X >> 7) + }; + + // Select the proper multistepping + uint8_t idx = 0; + while (idx < 7 && step_rate > (uint32_t)pgm_read_dword(&limit[idx])) { + step_rate >>= 1; + multistep <<= 1; + ++idx; + }; + #else + NOMORE(step_rate, uint32_t(MAX_STEP_ISR_FREQUENCY_1X)); + #endif + *loops = multistep; + + constexpr uint32_t min_step_rate = F_CPU / 500000U; + NOLESS(step_rate, min_step_rate); + step_rate -= min_step_rate; // Correct for minimal speed if (step_rate >= (8 * 256)) { // higher step rate const uint8_t tmp_step_rate = (step_rate & 0x00FF); const uint16_t table_address = (uint16_t)&speed_lookuptable_fast[(uint8_t)(step_rate >> 8)][0], @@ -320,15 +501,13 @@ class Stepper { timer = (uint16_t)pgm_read_word_near(table_address) - (((uint16_t)pgm_read_word_near(table_address + 2) * (uint8_t)(step_rate & 0x0007)) >> 3); } - if (timer < 100) { // (20kHz - this should never happen) - timer = 100; - SERIAL_ECHOLNPAIR(MSG_STEPPER_TOO_HIGH, step_rate); - } + // (there is no need to limit the timer value here. All limits have been + // applied above, and AVR is able to keep up at 30khz Stepping ISR rate) return timer; } - #if ENABLED(BEZIER_JERK_CONTROL) + #if ENABLED(S_CURVE_ACCELERATION) static void _calc_bezier_curve_coeffs(const int32_t v0, const int32_t v1, const uint32_t av); static int32_t _eval_bezier_curve(const uint32_t curr_step); #endif @@ -343,4 +522,6 @@ class Stepper { }; +extern Stepper stepper; + #endif // STEPPER_H diff --git a/Marlin/stepper_dac.cpp b/Marlin/stepper_dac.cpp index 2013d20302..d5f713fedb 100644 --- a/Marlin/stepper_dac.cpp +++ b/Marlin/stepper_dac.cpp @@ -91,8 +91,8 @@ mcp4728_simpleCommand(UPDATE); } - static float dac_perc(int8_t n) { return 100.0 * mcp4728_getValue(dac_order[n]) * (1.0 / (DAC_STEPPER_MAX)); } - static float dac_amps(int8_t n) { return mcp4728_getDrvPct(dac_order[n]) * (DAC_STEPPER_MAX) * 0.125 * (1.0 / (DAC_STEPPER_SENSE)); } + static float dac_perc(int8_t n) { return 100.0f * mcp4728_getValue(dac_order[n]) * (1.0f / (DAC_STEPPER_MAX)); } + static float dac_amps(int8_t n) { return mcp4728_getDrvPct(dac_order[n]) * (DAC_STEPPER_MAX) * 0.125 * (1.0f / (DAC_STEPPER_SENSE)); } uint8_t dac_current_get_percent(const AxisEnum axis) { return mcp4728_getDrvPct(dac_order[axis]); } void dac_current_set_percents(const uint8_t pct[XYZE]) { diff --git a/Marlin/stepper_indirection.cpp b/Marlin/stepper_indirection.cpp index 9010470218..32ef99a710 100644 --- a/Marlin/stepper_indirection.cpp +++ b/Marlin/stepper_indirection.cpp @@ -201,8 +201,6 @@ #if ENABLED(HYBRID_THRESHOLD) st.stealth_max_speed(12650000UL*microsteps/(256*thrs*spmm)); #endif - #elif ENABLED(SENSORLESS_HOMING) - st.coolstep_min_speed(1024UL * 1024UL - 1UL); #endif st.GSTAT(); // Clear GSTAT } @@ -246,7 +244,7 @@ #if ENABLED(SENSORLESS_HOMING) #define TMC_INIT_SGT(P,Q) stepper##Q.sgt(P##_HOMING_SENSITIVITY); - #ifdef X_HOMING_SENSITIVITY + #if X_SENSORLESS #if ENABLED(X_IS_TMC2130) || ENABLED(IS_TRAMS) stepperX.sgt(X_HOMING_SENSITIVITY); #endif @@ -254,7 +252,7 @@ stepperX2.sgt(X_HOMING_SENSITIVITY); #endif #endif - #ifdef Y_HOMING_SENSITIVITY + #if Y_SENSORLESS #if ENABLED(Y_IS_TMC2130) || ENABLED(IS_TRAMS) stepperY.sgt(Y_HOMING_SENSITIVITY); #endif @@ -262,7 +260,7 @@ stepperY2.sgt(Y_HOMING_SENSITIVITY); #endif #endif - #ifdef Z_HOMING_SENSITIVITY + #if Z_SENSORLESS #if ENABLED(Z_IS_TMC2130) || ENABLED(IS_TRAMS) stepperZ.sgt(Z_HOMING_SENSITIVITY); #endif diff --git a/Marlin/stepper_indirection.h b/Marlin/stepper_indirection.h index 34a2582d00..3e67118d86 100644 --- a/Marlin/stepper_indirection.h +++ b/Marlin/stepper_indirection.h @@ -446,73 +446,54 @@ void reset_stepper_drivers(); // Called by settings.load / settings.reset /** * Extruder indirection for the single E axis */ -#if ENABLED(SWITCHING_EXTRUDER) +#if ENABLED(SWITCHING_EXTRUDER) // One stepper driver per two extruders, reversed on odd index #if EXTRUDERS > 4 - #define E_STEP_WRITE(v) do{ if (current_block->active_extruder < 2) { E0_STEP_WRITE(v); } else if (current_block->active_extruder < 4) { E1_STEP_WRITE(v); } else { E2_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 3: E1_DIR_WRITE( INVERT_E1_DIR); break; case 4: E2_DIR_WRITE(!INVERT_E2_DIR); } }while(0) - #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 4: E2_DIR_WRITE( INVERT_E2_DIR); } }while(0) + #define E_STEP_WRITE(E,V) do{ if (E < 2) { E0_STEP_WRITE(V); } else if (E < 4) { E1_STEP_WRITE(V); } else { E2_STEP_WRITE(V); } }while(0) + #define NORM_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 3: E1_DIR_WRITE( INVERT_E1_DIR); break; case 4: E2_DIR_WRITE(!INVERT_E2_DIR); } }while(0) + #define REV_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 4: E2_DIR_WRITE( INVERT_E2_DIR); } }while(0) #elif EXTRUDERS > 3 - #define E_STEP_WRITE(v) do{ if (current_block->active_extruder < 2) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 3: E1_DIR_WRITE( INVERT_E1_DIR); } }while(0) - #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0) + #define E_STEP_WRITE(E,V) do{ if (E < 2) { E0_STEP_WRITE(V); } else { E1_STEP_WRITE(V); } }while(0) + #define NORM_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 3: E1_DIR_WRITE( INVERT_E1_DIR); } }while(0) + #define REV_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0) #elif EXTRUDERS > 2 - #define E_STEP_WRITE(v) do{ if (current_block->active_extruder < 2) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0) - #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); } }while(0) + #define E_STEP_WRITE(E,V) do{ if (E < 2) { E0_STEP_WRITE(V); } else { E1_STEP_WRITE(V); } }while(0) + #define NORM_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0) + #define REV_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); } }while(0) #else - #define E_STEP_WRITE(v) E0_STEP_WRITE(v) - #define NORM_E_DIR() do{ E0_DIR_WRITE(current_block->active_extruder ? INVERT_E0_DIR : !INVERT_E0_DIR); }while(0) - #define REV_E_DIR() do{ E0_DIR_WRITE(current_block->active_extruder ? !INVERT_E0_DIR : INVERT_E0_DIR); }while(0) + #define E_STEP_WRITE(E,V) E0_STEP_WRITE(V) + #define NORM_E_DIR(E) do{ E0_DIR_WRITE(E ? INVERT_E0_DIR : !INVERT_E0_DIR); }while(0) + #define REV_E_DIR(E) do{ E0_DIR_WRITE(E ? !INVERT_E0_DIR : INVERT_E0_DIR); }while(0) #endif -#elif ENABLED(MK2_MULTIPLEXER) // Even-numbered steppers are reversed - #define E_STEP_WRITE(v) E0_STEP_WRITE(v) - #define NORM_E_DIR() do{ E0_DIR_WRITE(TEST(current_block->active_extruder, 0) ? !INVERT_E0_DIR: INVERT_E0_DIR); }while(0) - #define REV_E_DIR() do{ E0_DIR_WRITE(TEST(current_block->active_extruder, 0) ? INVERT_E0_DIR: !INVERT_E0_DIR); }while(0) -#elif EXTRUDERS > 4 - #define E_STEP_WRITE(v) do{ switch (current_block->active_extruder) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); break; case 3: E3_STEP_WRITE(v); break; case 4: E4_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(!INVERT_E3_DIR); break; case 4: E4_DIR_WRITE(!INVERT_E4_DIR); } }while(0) - #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); break; case 3: E3_DIR_WRITE( INVERT_E3_DIR); break; case 4: E4_DIR_WRITE( INVERT_E4_DIR); } }while(0) -#elif EXTRUDERS > 3 - #define E_STEP_WRITE(v) do{ switch (current_block->active_extruder) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); break; case 3: E3_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(!INVERT_E3_DIR); } }while(0) - #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); break; case 3: E3_DIR_WRITE( INVERT_E3_DIR); } }while(0) -#elif EXTRUDERS > 2 - #define E_STEP_WRITE(v) do{ switch (current_block->active_extruder) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); } }while(0) - #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); } }while(0) -#elif EXTRUDERS > 1 +#elif ENABLED(MK2_MULTIPLEXER) // One multiplexed stepper driver, reversed on odd index + #define E_STEP_WRITE(E,V) E0_STEP_WRITE(V) + #define NORM_E_DIR(E) do{ E0_DIR_WRITE(TEST(E, 0) ? !INVERT_E0_DIR: INVERT_E0_DIR); }while(0) + #define REV_E_DIR(E) do{ E0_DIR_WRITE(TEST(E, 0) ? INVERT_E0_DIR: !INVERT_E0_DIR); }while(0) +#elif E_STEPPERS > 4 + #define E_STEP_WRITE(E,V) do{ switch (E) { case 0: E0_STEP_WRITE(V); break; case 1: E1_STEP_WRITE(V); break; case 2: E2_STEP_WRITE(V); break; case 3: E3_STEP_WRITE(V); break; case 4: E4_STEP_WRITE(V); } }while(0) + #define NORM_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(!INVERT_E3_DIR); break; case 4: E4_DIR_WRITE(!INVERT_E4_DIR); } }while(0) + #define REV_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); break; case 3: E3_DIR_WRITE( INVERT_E3_DIR); break; case 4: E4_DIR_WRITE( INVERT_E4_DIR); } }while(0) +#elif E_STEPPERS > 3 + #define E_STEP_WRITE(E,V) do{ switch (E) { case 0: E0_STEP_WRITE(V); break; case 1: E1_STEP_WRITE(V); break; case 2: E2_STEP_WRITE(V); break; case 3: E3_STEP_WRITE(V); } }while(0) + #define NORM_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(!INVERT_E3_DIR); } }while(0) + #define REV_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); break; case 3: E3_DIR_WRITE( INVERT_E3_DIR); } }while(0) +#elif E_STEPPERS > 2 + #define E_STEP_WRITE(E,V) do{ switch (E) { case 0: E0_STEP_WRITE(V); break; case 1: E1_STEP_WRITE(V); break; case 2: E2_STEP_WRITE(V); } }while(0) + #define NORM_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); } }while(0) + #define REV_E_DIR(E) do{ switch (E) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); } }while(0) +#elif E_STEPPERS > 1 #if ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE) - #define E_STEP_WRITE(v) do{ if (extruder_duplication_enabled) { E0_STEP_WRITE(v); E1_STEP_WRITE(v); } else if (current_block->active_extruder == 0) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ if (extruder_duplication_enabled) { E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); } else if (current_block->active_extruder == 0) { E0_DIR_WRITE(!INVERT_E0_DIR); } else { E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0) - #define REV_E_DIR() do{ if (extruder_duplication_enabled) { E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); } else if (current_block->active_extruder == 0) { E0_DIR_WRITE( INVERT_E0_DIR); } else { E1_DIR_WRITE( INVERT_E1_DIR); } }while(0) + #define E_STEP_WRITE(E,V) do{ if (extruder_duplication_enabled) { E0_STEP_WRITE(V); E1_STEP_WRITE(V); } else if (E == 0) { E0_STEP_WRITE(V); } else { E1_STEP_WRITE(V); } }while(0) + #define NORM_E_DIR(E) do{ if (extruder_duplication_enabled) { E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); } else if (E == 0) { E0_DIR_WRITE(!INVERT_E0_DIR); } else { E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0) + #define REV_E_DIR(E) do{ if (extruder_duplication_enabled) { E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); } else if (E == 0) { E0_DIR_WRITE( INVERT_E0_DIR); } else { E1_DIR_WRITE( INVERT_E1_DIR); } }while(0) #else - #define E_STEP_WRITE(v) do{ if (current_block->active_extruder == 0) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ if (current_block->active_extruder == 0) { E0_DIR_WRITE(!INVERT_E0_DIR); } else { E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0) - #define REV_E_DIR() do{ if (current_block->active_extruder == 0) { E0_DIR_WRITE( INVERT_E0_DIR); } else { E1_DIR_WRITE( INVERT_E1_DIR); } }while(0) - #endif -#elif ENABLED(MIXING_EXTRUDER) - #define E_STEP_WRITE(v) NOOP /* not used for mixing extruders! */ - #if MIXING_STEPPERS > 4 - #define En_STEP_WRITE(n,v) do{ switch (n) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); break; case 3: E3_STEP_WRITE(v); break; case 4: E4_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); E2_DIR_WRITE(!INVERT_E2_DIR); E3_DIR_WRITE(!INVERT_E3_DIR); E4_DIR_WRITE(!INVERT_E4_DIR); }while(0) - #define REV_E_DIR() do{ E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); E2_DIR_WRITE( INVERT_E2_DIR); E3_DIR_WRITE( INVERT_E3_DIR); E4_DIR_WRITE( INVERT_E4_DIR); }while(0) - #elif MIXING_STEPPERS > 3 - #define En_STEP_WRITE(n,v) do{ switch (n) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); break; case 3: E3_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); E2_DIR_WRITE(!INVERT_E2_DIR); E3_DIR_WRITE(!INVERT_E3_DIR); }while(0) - #define REV_E_DIR() do{ E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); E2_DIR_WRITE( INVERT_E2_DIR); E3_DIR_WRITE( INVERT_E3_DIR); }while(0) - #elif MIXING_STEPPERS > 2 - #define En_STEP_WRITE(n,v) do{ switch (n) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); E2_DIR_WRITE(!INVERT_E2_DIR); }while(0) - #define REV_E_DIR() do{ E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); E2_DIR_WRITE( INVERT_E2_DIR); }while(0) - #else - #define En_STEP_WRITE(n,v) do{ switch (n) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); } }while(0) - #define NORM_E_DIR() do{ E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); }while(0) - #define REV_E_DIR() do{ E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); }while(0) + #define E_STEP_WRITE(E,V) do{ if (E == 0) { E0_STEP_WRITE(V); } else { E1_STEP_WRITE(V); } }while(0) + #define NORM_E_DIR(E) do{ if (E == 0) { E0_DIR_WRITE(!INVERT_E0_DIR); } else { E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0) + #define REV_E_DIR(E) do{ if (E == 0) { E0_DIR_WRITE( INVERT_E0_DIR); } else { E1_DIR_WRITE( INVERT_E1_DIR); } }while(0) #endif #else - #define E_STEP_WRITE(v) E0_STEP_WRITE(v) - #define NORM_E_DIR() E0_DIR_WRITE(!INVERT_E0_DIR) - #define REV_E_DIR() E0_DIR_WRITE( INVERT_E0_DIR) + #define E_STEP_WRITE(E,V) E0_STEP_WRITE(V) + #define NORM_E_DIR(E) E0_DIR_WRITE(!INVERT_E0_DIR) + #define REV_E_DIR(E) E0_DIR_WRITE( INVERT_E0_DIR) #endif #endif // STEPPER_INDIRECTION_H diff --git a/Marlin/temperature.cpp b/Marlin/temperature.cpp index 814cdeea11..e287a47794 100644 --- a/Marlin/temperature.cpp +++ b/Marlin/temperature.cpp @@ -53,10 +53,10 @@ #if HOTEND_USES_THERMISTOR #if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) static void* heater_ttbl_map[2] = { (void*)HEATER_0_TEMPTABLE, (void*)HEATER_1_TEMPTABLE }; - static uint8_t heater_ttbllen_map[2] = { HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN }; + static constexpr uint8_t heater_ttbllen_map[2] = { HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN }; #else static void* heater_ttbl_map[HOTENDS] = ARRAY_BY_HOTENDS((void*)HEATER_0_TEMPTABLE, (void*)HEATER_1_TEMPTABLE, (void*)HEATER_2_TEMPTABLE, (void*)HEATER_3_TEMPTABLE, (void*)HEATER_4_TEMPTABLE); - static uint8_t heater_ttbllen_map[HOTENDS] = ARRAY_BY_HOTENDS(HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN, HEATER_2_TEMPTABLE_LEN, HEATER_3_TEMPTABLE_LEN, HEATER_4_TEMPTABLE_LEN); + static constexpr uint8_t heater_ttbllen_map[HOTENDS] = ARRAY_BY_HOTENDS(HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN, HEATER_2_TEMPTABLE_LEN, HEATER_3_TEMPTABLE_LEN, HEATER_4_TEMPTABLE_LEN); #endif #endif @@ -386,13 +386,13 @@ uint8_t Temperature::soft_pwm_amount[HOTENDS]; SERIAL_PROTOCOLPAIR(MSG_T_MIN, min); SERIAL_PROTOCOLPAIR(MSG_T_MAX, max); if (cycles > 2) { - Ku = (4.0 * d) / (M_PI * (max - min) * 0.5); - Tu = ((float)(t_low + t_high) * 0.001); + Ku = (4.0f * d) / (M_PI * (max - min) * 0.5f); + Tu = ((float)(t_low + t_high) * 0.001f); SERIAL_PROTOCOLPAIR(MSG_KU, Ku); SERIAL_PROTOCOLPAIR(MSG_TU, Tu); - workKp = 0.6 * Ku; + workKp = 0.6f * Ku; workKi = 2 * workKp / Tu; - workKd = workKp * Tu * 0.125; + workKd = workKp * Tu * 0.125f; SERIAL_PROTOCOLLNPGM("\n" MSG_CLASSIC_PID); SERIAL_PROTOCOLPAIR(MSG_KP, workKp); SERIAL_PROTOCOLPAIR(MSG_KI, workKi); @@ -633,7 +633,7 @@ float Temperature::get_pid_output(const int8_t e) { #if ENABLED(PIDTEMP) #if DISABLED(PID_OPENLOOP) pid_error[HOTEND_INDEX] = target_temperature[HOTEND_INDEX] - current_temperature[HOTEND_INDEX]; - dTerm[HOTEND_INDEX] = PID_K2 * PID_PARAM(Kd, HOTEND_INDEX) * (current_temperature[HOTEND_INDEX] - temp_dState[HOTEND_INDEX]) + PID_K1 * dTerm[HOTEND_INDEX]; + dTerm[HOTEND_INDEX] = PID_K2 * PID_PARAM(Kd, HOTEND_INDEX) * (current_temperature[HOTEND_INDEX] - temp_dState[HOTEND_INDEX]) + float(PID_K1) * dTerm[HOTEND_INDEX]; temp_dState[HOTEND_INDEX] = current_temperature[HOTEND_INDEX]; #if HEATER_IDLE_HANDLER if (heater_idle_timeout_exceeded[HOTEND_INDEX]) { @@ -918,17 +918,25 @@ void Temperature::manage_heater() { #define TEMP_AD595(RAW) ((RAW) * 5.0 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD595_GAIN) + TEMP_SENSOR_AD595_OFFSET) #define TEMP_AD8495(RAW) ((RAW) * 6.6 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD8495_GAIN) + TEMP_SENSOR_AD8495_OFFSET) -#define SCAN_THERMISTOR_TABLE(TBL,LEN) do{ \ - for (uint8_t i = 1; i < LEN; i++) { \ - const short entry10 = (short)pgm_read_word(&TBL[i][0]); \ - if (entry10 > raw) { \ - const short entry00 = (short)pgm_read_word(&TBL[i-1][0]), \ - entry01 = (short)pgm_read_word(&TBL[i-1][1]), \ - entry11 = (short)pgm_read_word(&TBL[i][1]); \ - return entry01 + (raw - entry00) * float(entry11 - entry01) / float(entry10 - entry00); \ - } \ - } \ - return (short)pgm_read_word(&TBL[LEN-1][1]); \ +/** + * Bisect search for the range of the 'raw' value, then interpolate + * proportionally between the under and over values. + */ +#define SCAN_THERMISTOR_TABLE(TBL,LEN) do{ \ + uint8_t l = 0, r = LEN, m; \ + for (;;) { \ + m = (l + r) >> 1; \ + if (m == l || m == r) return (short)pgm_read_word(&TBL[LEN-1][1]); \ + short v00 = pgm_read_word(&TBL[m-1][0]), \ + v10 = pgm_read_word(&TBL[m-0][0]); \ + if (raw < v00) r = m; \ + else if (raw > v10) l = m; \ + else { \ + const short v01 = (short)pgm_read_word(&TBL[m-1][1]), \ + v11 = (short)pgm_read_word(&TBL[m-0][1]); \ + return v01 + (raw - v00) * float(v11 - v01) / float(v10 - v00); \ + } \ + } \ }while(0) // Derived from RepRap FiveD extruder::getTemperature() @@ -1071,7 +1079,7 @@ void Temperature::updateTemperaturesFromRawValues() { // Convert raw Filament Width to millimeters float Temperature::analog2widthFil() { - return current_raw_filwidth * 5.0 * (1.0 / 16383.0); + return current_raw_filwidth * 5.0f * (1.0f / 16383.0); } /** @@ -1084,7 +1092,7 @@ void Temperature::updateTemperaturesFromRawValues() { */ int8_t Temperature::widthFil_to_size_ratio() { if (ABS(filament_width_nominal - filament_width_meas) <= FILWIDTH_ERROR_MARGIN) - return int(100.0 * filament_width_nominal / filament_width_meas) - 100; + return int(100.0f * filament_width_nominal / filament_width_meas) - 100; return 0; } @@ -1201,9 +1209,7 @@ void Temperature::init() { HAL_ANALOG_SELECT(FILWIDTH_PIN); #endif - // Use timer0 for temperature measurement - // Interleave temperature interrupt with millies interrupt - OCR0B = 128; + HAL_timer_start(TEMP_TIMER_NUM, TEMP_TIMER_FREQUENCY); ENABLE_TEMPERATURE_INTERRUPT(); #if HAS_AUTO_FAN_0 @@ -2122,7 +2128,7 @@ void Temperature::isr() { HAL_START_ADC(TEMP_CHAMBER_PIN); break; case MeasureTemp_CHAMBER: - raw_temp_chamber_value += ADC; + raw_temp_chamber_value += HAL_READ_ADC; break; #endif @@ -2180,7 +2186,7 @@ void Temperature::isr() { break; case Measure_ADC_KEY: if (ADCKey_count < 16) { - raw_ADCKey_value = ADC; + raw_ADCKey_value = HAL_READ_ADC; if (raw_ADCKey_value > 900) { //ADC Key release ADCKey_count = 0; diff --git a/Marlin/temperature.h b/Marlin/temperature.h index c30bb396c9..1112e1799e 100644 --- a/Marlin/temperature.h +++ b/Marlin/temperature.h @@ -51,10 +51,8 @@ #if HOTENDS == 1 #define HOTEND_INDEX 0 - #define EXTRUDER_IDX 0 #else #define HOTEND_INDEX e - #define EXTRUDER_IDX active_extruder #endif /** @@ -109,14 +107,14 @@ enum ADCSensorState : char { #define ACTUAL_ADC_SAMPLES MAX(int(MIN_ADC_ISR_LOOPS), int(SensorsReady)) #if HAS_PID_HEATING - #define PID_K2 (1.0-PID_K1) - #define PID_dT ((OVERSAMPLENR * float(ACTUAL_ADC_SAMPLES)) / (F_CPU / 64.0 / 256.0)) + #define PID_K2 (1.0f-PID_K1) + #define PID_dT ((OVERSAMPLENR * float(ACTUAL_ADC_SAMPLES)) / (F_CPU / 64.0f / 256.0f)) // Apply the scale factors to the PID values - #define scalePID_i(i) ( (i) * PID_dT ) - #define unscalePID_i(i) ( (i) / PID_dT ) - #define scalePID_d(d) ( (d) / PID_dT ) - #define unscalePID_d(d) ( (d) * PID_dT ) + #define scalePID_i(i) ( (i) * float(PID_dT) ) + #define unscalePID_i(i) ( (i) / float(PID_dT) ) + #define scalePID_d(d) ( (d) / float(PID_dT) ) + #define unscalePID_d(d) ( (d) * float(PID_dT) ) #endif class Temperature { @@ -507,7 +505,7 @@ class Temperature { #if ENABLED(BABYSTEPPING) static void babystep_axis(const AxisEnum axis, const int16_t distance) { - if (axis_known_position[axis]) { + if (TEST(axis_known_position, axis)) { #if IS_CORE #if ENABLED(BABYSTEP_XY) switch (axis) { diff --git a/Marlin/thermistortable_501.h b/Marlin/thermistortable_501.h new file mode 100644 index 0000000000..512ac0d8d8 --- /dev/null +++ b/Marlin/thermistortable_501.h @@ -0,0 +1,57 @@ +/** + * 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 . + * + */ + +// 100k Zonestar thermistor. Adjusted By Hally +const short temptable_501[][2] PROGMEM = { + {OV( 1), 713}, + {OV( 14), 300}, // Top rating 300C + {OV( 16), 290}, + {OV( 19), 280}, + {OV( 23), 270}, + {OV( 27), 260}, + {OV( 32), 250}, + {OV( 30), 240}, + {OV( 47), 230}, + {OV( 57), 220}, + {OV( 68), 210}, + {OV( 84), 200}, + {OV( 100), 190}, + {OV( 128), 180}, + {OV( 155), 170}, + {OV( 189), 160}, + {OV( 230), 150}, + {OV( 278), 140}, + {OV( 336), 130}, + {OV( 402), 120}, + {OV( 476), 110}, + {OV( 554), 100}, + {OV( 635), 90}, + {OV( 713), 80}, + {OV( 784), 70}, + {OV( 846), 60}, + {OV( 897), 50}, + {OV( 937), 40}, + {OV( 966), 30}, + {OV( 986), 20}, + {OV(1000), 10}, + {OV(1010), 0} +}; diff --git a/Marlin/thermistortable_70.h b/Marlin/thermistortable_70.h index fd7838b809..2f9434e300 100644 --- a/Marlin/thermistortable_70.h +++ b/Marlin/thermistortable_70.h @@ -20,64 +20,23 @@ * */ +// Stock BQ Hephestos 2 100k thermistor. +// Created on 29/12/2017 with an ambient temperature of 20C. +// ANENG AN8009 DMM with a K-type probe used for measurements. + // R25 = 100 kOhm, beta25 = 4100 K, 4.7 kOhm pull-up, bqh2 stock thermistor const short temptable_70[][2] PROGMEM = { - { OV( 22), 300 }, - { OV( 24), 295 }, - { OV( 25), 290 }, - { OV( 27), 285 }, - { OV( 29), 280 }, - { OV( 32), 275 }, - { OV( 34), 270 }, - { OV( 37), 265 }, - { OV( 40), 260 }, - { OV( 43), 255 }, - { OV( 46), 250 }, - { OV( 50), 245 }, - { OV( 54), 240 }, - { OV( 59), 235 }, - { OV( 64), 230 }, - { OV( 70), 225 }, - { OV( 76), 220 }, - { OV( 83), 215 }, - { OV( 90), 210 }, - { OV( 99), 205 }, - { OV( 108), 200 }, - { OV( 118), 195 }, - { OV( 129), 190 }, - { OV( 141), 185 }, - { OV( 154), 180 }, - { OV( 169), 175 }, - { OV( 185), 170 }, - { OV( 203), 165 }, - { OV( 222), 160 }, - { OV( 243), 155 }, - { OV( 266), 150 }, - { OV( 290), 145 }, - { OV( 317), 140 }, - { OV( 346), 135 }, - { OV( 376), 130 }, - { OV( 408), 125 }, - { OV( 442), 120 }, - { OV( 477), 115 }, - { OV( 513), 110 }, - { OV( 551), 105 }, - { OV( 588), 100 }, - { OV( 626), 95 }, - { OV( 663), 90 }, - { OV( 699), 85 }, - { OV( 735), 80 }, - { OV( 768), 75 }, - { OV( 800), 70 }, - { OV( 829), 65 }, - { OV( 856), 60 }, - { OV( 881), 55 }, - { OV( 903), 50 }, - { OV( 922), 45 }, - { OV( 939), 40 }, - { OV( 954), 35 }, - { OV( 966), 30 }, - { OV( 977), 25 }, + { OV( 18), 270 }, + { OV( 27), 248 }, + { OV( 34), 234 }, + { OV( 45), 220 }, + { OV( 61), 205 }, + { OV( 86), 188 }, + { OV( 123), 172 }, + { OV( 420), 110 }, + { OV( 590), 90 }, + { OV( 845), 56 }, + { OV( 970), 25 }, { OV( 986), 20 }, { OV( 994), 15 }, { OV(1000), 10 }, diff --git a/Marlin/thermistortables.h b/Marlin/thermistortables.h index 1835b306da..462337778d 100644 --- a/Marlin/thermistortables.h +++ b/Marlin/thermistortables.h @@ -56,6 +56,9 @@ #if ANY_THERMISTOR_IS(5) // 100k ParCan thermistor (104GT-2) #include "thermistortable_5.h" #endif +#if ANY_THERMISTOR_IS(501) // 100k Zonestar thermistor + #include "thermistortable_501.h" +#endif #if ANY_THERMISTOR_IS(6) // 100k Epcos thermistor #include "thermistortable_6.h" #endif @@ -185,21 +188,26 @@ #ifdef THERMISTORBED #define BEDTEMPTABLE TT_NAME(THERMISTORBED) #define BEDTEMPTABLE_LEN COUNT(BEDTEMPTABLE) +#elif defined(HEATER_BED_USES_THERMISTOR) + #error "No bed thermistor table specified" #else - #ifdef HEATER_BED_USES_THERMISTOR - #error "No bed thermistor table specified" - #endif + #define BEDTEMPTABLE_LEN 0 #endif #ifdef THERMISTORCHAMBER #define CHAMBERTEMPTABLE TT_NAME(THERMISTORCHAMBER) #define CHAMBERTEMPTABLE_LEN COUNT(CHAMBERTEMPTABLE) +#elif defined(HEATER_CHAMBER_USES_THERMISTOR) + #error "No chamber thermistor table specified" #else - #ifdef HEATER_CHAMBER_USES_THERMISTOR - #error "No chamber thermistor table specified" - #endif + #define CHAMBERTEMPTABLE_LEN 0 #endif +// The SCAN_THERMISTOR_TABLE macro needs alteration? +static_assert(HEATER_0_TEMPTABLE_LEN < 128 && HEATER_1_TEMPTABLE_LEN < 128 && HEATER_2_TEMPTABLE_LEN < 128 && HEATER_3_TEMPTABLE_LEN < 128 && HEATER_4_TEMPTABLE_LEN < 128 && BEDTEMPTABLE_LEN < 128 && CHAMBERTEMPTABLE_LEN < 128, + "Temperature conversion tables over 127 entries need special consideration." +); + // Set the high and low raw values for the heaters // For thermistors the highest temperature results in the lowest ADC value // For thermocouples the highest temperature results in the highest ADC value diff --git a/Marlin/tmc_util.cpp b/Marlin/tmc_util.cpp index 3f7272fec1..662fbc176c 100644 --- a/Marlin/tmc_util.cpp +++ b/Marlin/tmc_util.cpp @@ -534,7 +534,7 @@ void _tmc_say_sgt(const TMC_AxisEnum axis, const int8_t sgt) { TMC_REPORT("Run current", TMC_IRUN); TMC_REPORT("Hold current", TMC_IHOLD); TMC_REPORT("CS actual\t", TMC_CS_ACTUAL); - TMC_REPORT("PWM scale", TMC_PWM_SCALE); + TMC_REPORT("PWM scale\t", TMC_PWM_SCALE); TMC_REPORT("vsense\t", TMC_VSENSE); TMC_REPORT("stealthChop", TMC_STEALTHCHOP); TMC_REPORT("msteps\t", TMC_MICROSTEPS); @@ -580,8 +580,8 @@ void _tmc_say_sgt(const TMC_AxisEnum axis, const int8_t sgt) { #if ENABLED(SENSORLESS_HOMING) void tmc_sensorless_homing(TMC2130Stepper &st, const bool enable/*=true*/) { + st.coolstep_min_speed(enable ? 1024UL * 1024UL - 1UL : 0); #if ENABLED(STEALTHCHOP) - st.coolstep_min_speed(enable ? 1024UL * 1024UL - 1UL : 0); st.stealthChop(!enable); #endif st.diag1_stall(enable ? 1 : 0); diff --git a/Marlin/ubl.h b/Marlin/ubl.h index 7b9c08fe66..873e5e6ec4 100644 --- a/Marlin/ubl.h +++ b/Marlin/ubl.h @@ -161,14 +161,14 @@ class unified_bed_leveling { FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const float &z) { z_values[px][py] = z; } static int8_t get_cell_index_x(const float &x) { - const int8_t cx = (x - (MESH_MIN_X)) * (1.0 / (MESH_X_DIST)); + const int8_t cx = (x - (MESH_MIN_X)) * (1.0f / (MESH_X_DIST)); return constrain(cx, 0, (GRID_MAX_POINTS_X) - 1); // -1 is appropriate if we want all movement to the X_MAX } // position. But with this defined this way, it is possible // to extrapolate off of this point even further out. Probably // that is OK because something else should be keeping that from // happening and should not be worried about at this level. static int8_t get_cell_index_y(const float &y) { - const int8_t cy = (y - (MESH_MIN_Y)) * (1.0 / (MESH_Y_DIST)); + const int8_t cy = (y - (MESH_MIN_Y)) * (1.0f / (MESH_Y_DIST)); return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 1); // -1 is appropriate if we want all movement to the Y_MAX } // position. But with this defined this way, it is possible // to extrapolate off of this point even further out. Probably @@ -176,12 +176,12 @@ class unified_bed_leveling { // happening and should not be worried about at this level. static int8_t find_closest_x_index(const float &x) { - const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * (1.0 / (MESH_X_DIST)); + const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5f) * (1.0f / (MESH_X_DIST)); return WITHIN(px, 0, GRID_MAX_POINTS_X - 1) ? px : -1; } static int8_t find_closest_y_index(const float &y) { - const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * (1.0 / (MESH_Y_DIST)); + const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5f) * (1.0f / (MESH_Y_DIST)); return WITHIN(py, 0, GRID_MAX_POINTS_Y - 1) ? py : -1; } diff --git a/Marlin/ubl_G29.cpp b/Marlin/ubl_G29.cpp index a4c7b33287..0286a76858 100644 --- a/Marlin/ubl_G29.cpp +++ b/Marlin/ubl_G29.cpp @@ -62,8 +62,8 @@ unified_bed_leveling::g29_y_flag; float unified_bed_leveling::g29_x_pos, unified_bed_leveling::g29_y_pos, - unified_bed_leveling::g29_card_thickness = 0.0, - unified_bed_leveling::g29_constant = 0.0; + unified_bed_leveling::g29_card_thickness = 0, + unified_bed_leveling::g29_constant = 0; #if HAS_BED_PROBE int unified_bed_leveling::g29_grid_size; @@ -289,13 +289,17 @@ void unified_bed_leveling::G29() { - if (g29_parameter_parsing()) return; // abort if parsing the simple parameters causes a problem, + if (g29_parameter_parsing()) return; // Abort on parameter error + + const int8_t p_val = parser.intval('P', -1); + const bool may_move = p_val == 1 || p_val == 2 || p_val == 4 || parser.seen('J'); // Check for commands that require the printer to be homed - if (axis_unhomed_error()) { - const int8_t p_val = parser.intval('P', -1); - if (p_val == 1 || p_val == 2 || p_val == 4 || parser.seen('J')) - home_all_axes(); + if (may_move) { + if (axis_unhomed_error()) home_all_axes(); + #if ENABLED(DUAL_X_CARRIAGE) + if (active_extruder != 0) tool_change(0); + #endif } // Invalidate Mesh Points. This command is a little bit asymmetrical because @@ -339,23 +343,23 @@ case 0: for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) { // Create a bowl shape - similar to for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) { // a poorly calibrated Delta. - const float p1 = 0.5 * (GRID_MAX_POINTS_X) - x, - p2 = 0.5 * (GRID_MAX_POINTS_Y) - y; - z_values[x][y] += 2.0 * HYPOT(p1, p2); + const float p1 = 0.5f * (GRID_MAX_POINTS_X) - x, + p2 = 0.5f * (GRID_MAX_POINTS_Y) - y; + z_values[x][y] += 2.0f * HYPOT(p1, p2); } } break; case 1: for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) { // Create a diagonal line several Mesh cells thick that is raised - z_values[x][x] += 9.999; - z_values[x][x + (x < GRID_MAX_POINTS_Y - 1) ? 1 : -1] += 9.999; // We want the altered line several mesh points thick + z_values[x][x] += 9.999f; + z_values[x][x + (x < GRID_MAX_POINTS_Y - 1) ? 1 : -1] += 9.999f; // We want the altered line several mesh points thick } break; case 2: // Allow the user to specify the height because 10mm is a little extreme in some cases. for (uint8_t x = (GRID_MAX_POINTS_X) / 3; x < 2 * (GRID_MAX_POINTS_X) / 3; x++) // Create a rectangular raised area in for (uint8_t y = (GRID_MAX_POINTS_Y) / 3; y < 2 * (GRID_MAX_POINTS_Y) / 3; y++) // the center of the bed - z_values[x][y] += parser.seen('C') ? g29_constant : 9.99; + z_values[x][y] += parser.seen('C') ? g29_constant : 9.99f; break; } } @@ -374,7 +378,7 @@ tilt_mesh_based_on_probed_grid(true /* true says to do 3-Point leveling */ ); restore_ubl_active_state_and_leave(); } - do_blocking_move_to_xy(0.5 * (MESH_MAX_X - (MESH_MIN_X)), 0.5 * (MESH_MAX_Y - (MESH_MIN_Y))); + do_blocking_move_to_xy(0.5f * (MESH_MAX_X - (MESH_MIN_X)), 0.5f * (MESH_MAX_Y - (MESH_MIN_Y))); report_current_position(); } @@ -446,7 +450,7 @@ if (parser.seen('B')) { g29_card_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness((float) Z_CLEARANCE_BETWEEN_PROBES); - if (ABS(g29_card_thickness) > 1.5) { + if (ABS(g29_card_thickness) > 1.5f) { SERIAL_PROTOCOLLNPGM("?Error in Business Card measurement."); return; } @@ -502,7 +506,7 @@ } else { const float cvf = parser.value_float(); - switch ((int)truncf(cvf * 10.0) - 30) { // 3.1 -> 1 + switch ((int)truncf(cvf * 10.0f) - 30) { // 3.1 -> 1 #if ENABLED(UBL_G29_P31) case 1: { @@ -512,8 +516,8 @@ // P3.12 100X distance weighting // P3.13 1000X distance weighting, approaches simple average of nearest points - const float weight_power = (cvf - 3.10) * 100.0, // 3.12345 -> 2.345 - weight_factor = weight_power ? POW(10.0, weight_power) : 0; + const float weight_power = (cvf - 3.10f) * 100.0f, // 3.12345 -> 2.345 + weight_factor = weight_power ? POW(10.0f, weight_power) : 0; smart_fill_wlsf(weight_factor); } break; @@ -627,7 +631,7 @@ } void unified_bed_leveling::adjust_mesh_to_mean(const bool cflag, const float value) { - float sum = 0.0; + float sum = 0; int n = 0; for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) @@ -641,7 +645,7 @@ // // Sum the squares of difference from mean // - float sum_of_diff_squared = 0.0; + float sum_of_diff_squared = 0; for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) if (!isnan(z_values[x][y])) @@ -779,7 +783,7 @@ float unified_bed_leveling::measure_point_with_encoder() { KEEPALIVE_STATE(PAUSED_FOR_USER); - move_z_with_encoder(0.01); + move_z_with_encoder(0.01f); KEEPALIVE_STATE(IN_HANDLER); return current_position[Z_AXIS]; } @@ -790,8 +794,8 @@ lcd_external_control = true; save_ubl_active_state_and_disable(); // Disable bed level correction for probing - do_blocking_move_to(0.5 * (MESH_MAX_X - (MESH_MIN_X)), 0.5 * (MESH_MAX_Y - (MESH_MIN_Y)), in_height); - //, MIN(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]) / 2.0); + do_blocking_move_to(0.5f * (MESH_MAX_X - (MESH_MIN_X)), 0.5f * (MESH_MAX_Y - (MESH_MIN_Y)), in_height); + //, MIN(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]) * 0.5f); planner.synchronize(); SERIAL_PROTOCOLPGM("Place shim under nozzle"); @@ -867,8 +871,8 @@ serialprintPGM(parser.seen('B') ? PSTR(MSG_UBL_BC_INSERT) : PSTR(MSG_UBL_BC_INSERT2)); - const float z_step = 0.01; // existing behavior: 0.01mm per click, occasionally step - //const float z_step = 1.0 / planner.axis_steps_per_mm[Z_AXIS]; // approx one step each click + const float z_step = 0.01f; // existing behavior: 0.01mm per click, occasionally step + //const float z_step = planner.steps_to_mm[Z_AXIS]; // approx one step each click move_z_with_encoder(z_step); @@ -906,7 +910,7 @@ lcd_quick_feedback(true); #endif - g29_constant = 0.0; + g29_constant = 0; g29_repetition_cnt = 0; g29_x_flag = parser.seenval('X'); @@ -997,7 +1001,7 @@ #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) if (parser.seenval('F')) { const float fh = parser.value_float(); - if (!WITHIN(fh, 0.0, 100.0)) { + if (!WITHIN(fh, 0, 100)) { SERIAL_PROTOCOLLNPGM("?(F)ade height for Bed Level Correction not plausible.\n"); return UBL_ERR; } @@ -1219,7 +1223,7 @@ mesh_index_pair out_mesh; out_mesh.x_index = out_mesh.y_index = -1; - out_mesh.distance = -99999.99; + out_mesh.distance = -99999.99f; for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) { for (int8_t j = 0; j < GRID_MAX_POINTS_Y; j++) { @@ -1235,7 +1239,7 @@ found_a_NAN = true; int8_t closest_x = -1, closest_y = -1; - float d1, d2 = 99999.9; + float d1, d2 = 99999.9f; for (int8_t k = 0; k < GRID_MAX_POINTS_X; k++) { for (int8_t l = 0; l < GRID_MAX_POINTS_Y; l++) { if (!isnan(z_values[k][l])) { @@ -1245,7 +1249,7 @@ // last half of the mesh (when every unprobed mesh point is one index // from a probed location). - d1 = HYPOT(i - k, j - l) + (1.0 / ((millis() % 47) + 13)); + d1 = HYPOT(i - k, j - l) + (1.0f / ((millis() % 47) + 13)); if (d1 < d2) { // found a closer distance from invalid mesh point at (i,j) to defined mesh point at (k,l) d2 = d1; // found a closer location with @@ -1272,7 +1276,7 @@ if (!found_a_real && found_a_NAN) { // if the mesh is totally unpopulated, start the probing out_mesh.x_index = GRID_MAX_POINTS_X / 2; out_mesh.y_index = GRID_MAX_POINTS_Y / 2; - out_mesh.distance = 1.0; + out_mesh.distance = 1; } return out_mesh; } @@ -1280,13 +1284,13 @@ mesh_index_pair unified_bed_leveling::find_closest_mesh_point_of_type(const MeshPointType type, const float &rx, const float &ry, const bool probe_as_reference, uint16_t bits[16]) { mesh_index_pair out_mesh; out_mesh.x_index = out_mesh.y_index = -1; - out_mesh.distance = -99999.9; + out_mesh.distance = -99999.9f; // Get our reference position. Either the nozzle or probe location. const float px = rx - (probe_as_reference == USE_PROBE_AS_REFERENCE ? X_PROBE_OFFSET_FROM_EXTRUDER : 0), py = ry - (probe_as_reference == USE_PROBE_AS_REFERENCE ? Y_PROBE_OFFSET_FROM_EXTRUDER : 0); - float best_so_far = 99999.99; + float best_so_far = 99999.99f; for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) { for (int8_t j = 0; j < GRID_MAX_POINTS_Y; j++) { @@ -1313,7 +1317,7 @@ // factor in the distance from the current location for the normal case // so the nozzle isn't running all over the bed. - distance += HYPOT(current_position[X_AXIS] - mx, current_position[Y_AXIS] - my) * 0.1; + distance += HYPOT(current_position[X_AXIS] - mx, current_position[Y_AXIS] - my) * 0.1f; if (distance < best_so_far) { best_so_far = distance; // We found a closer location with out_mesh.x_index = i; // the specified type of mesh value. @@ -1379,7 +1383,7 @@ const float rawx = mesh_index_to_xpos(location.x_index), rawy = mesh_index_to_ypos(location.y_index); - if (!position_is_reachable(rawx, rawy)) break; // SHOULD NOT OCCUR because find_closest_mesh_point_of_type will only return reachable + if (!position_is_reachable(rawx, rawy)) break; // SHOULD NOT OCCUR because find_closest_mesh_point_of_type will only return reachable do_blocking_move_to(rawx, rawy, Z_CLEARANCE_BETWEEN_PROBES); // Move the nozzle to the edit point with probe clearance @@ -1394,8 +1398,8 @@ lcd_refresh(); float new_z = z_values[location.x_index][location.y_index]; - if (isnan(new_z)) new_z = 0.0; // Invalid points begin at 0 - new_z = FLOOR(new_z * 1000.0) * 0.001; // Chop off digits after the 1000ths place + if (isnan(new_z)) new_z = 0; // Invalid points begin at 0 + new_z = FLOOR(new_z * 1000) * 0.001f; // Chop off digits after the 1000ths place lcd_mesh_edit_setup(new_z); @@ -1454,7 +1458,7 @@ if (z_values[x1][y1] < z_values[x2][y2]) // Angled downward? z_values[x][y] = z_values[x1][y1]; // Use nearest (maybe a little too high.) else - z_values[x][y] = 2.0 * z_values[x1][y1] - z_values[x2][y2]; // Angled upward... + z_values[x][y] = 2.0f * z_values[x1][y1] - z_values[x2][y2]; // Angled upward... return true; } return false; @@ -1503,8 +1507,8 @@ float measured_z; - const float dx = float(x_max - x_min) / (g29_grid_size - 1.0), - dy = float(y_max - y_min) / (g29_grid_size - 1.0); + const float dx = float(x_max - x_min) / (g29_grid_size - 1), + dy = float(y_max - y_min) / (g29_grid_size - 1); struct linear_fit_data lsf_results; @@ -1555,7 +1559,12 @@ incremental_LSF(&lsf_results, PROBE_PT_3_X, PROBE_PT_3_Y, measured_z); } } - + + STOW_PROBE(); + #ifdef Z_AFTER_PROBING + move_z_after_probing(); + #endif + if (abort_flag) { SERIAL_ECHOPGM("?Error probing point. Aborting operation.\n"); return; @@ -1611,15 +1620,18 @@ zig_zag ^= true; } - STOW_PROBE(); } - + STOW_PROBE(); + #ifdef Z_AFTER_PROBING + move_z_after_probing(); + #endif + if (abort_flag || finish_incremental_LSF(&lsf_results)) { SERIAL_ECHOPGM("Could not complete LSF!"); return; } - vector_3 normal = vector_3(lsf_results.A, lsf_results.B, 1.0000).get_normal(); + vector_3 normal = vector_3(lsf_results.A, lsf_results.B, 1).get_normal(); if (g29_verbose_level > 2) { SERIAL_ECHOPGM("bed plane normal = ["); @@ -1698,7 +1710,7 @@ * The only difference is just 3 points are used in the calculations. That fact guarantees * each probed point should have an exact match when a get_z_correction() for that location * is calculated. The Z error between the probed point locations and the get_z_correction() - * numbers for those locations should be 0.000 + * numbers for those locations should be 0. */ #if 0 float t, t1, d; @@ -1728,13 +1740,13 @@ SERIAL_EOL(); t = normal.x * (Z_SAFE_HOMING_X_POINT) + normal.y * (Z_SAFE_HOMING_Y_POINT); - d = t + normal.z * 0.000; + d = t + normal.z * 0; SERIAL_ECHOPGM("D from home location with Z=0 : "); SERIAL_ECHO_F(d, 6); SERIAL_EOL(); t = normal.x * (Z_SAFE_HOMING_X_POINT) + normal.y * (Z_SAFE_HOMING_Y_POINT); - d = t + get_z_correction(Z_SAFE_HOMING_X_POINT, Z_SAFE_HOMING_Y_POINT); // normal.z * 0.000; + d = t + get_z_correction(Z_SAFE_HOMING_X_POINT, Z_SAFE_HOMING_Y_POINT); // normal.z * 0; SERIAL_ECHOPGM("D from home location using mesh value for Z: "); SERIAL_ECHO_F(d, 6); @@ -1785,7 +1797,7 @@ if (TEST(bitmap[jx], jy)) { const float ry = mesh_index_to_ypos(jy), rz = z_values[jx][jy], - w = 1.0 + weight_scaled / HYPOT((rx - px), (ry - py)); + w = 1 + weight_scaled / HYPOT((rx - px), (ry - py)); incremental_WLSF(&lsf_results, rx, ry, rz, w); } } diff --git a/Marlin/ubl_motion.cpp b/Marlin/ubl_motion.cpp index 0e8e7b9092..5272e20795 100644 --- a/Marlin/ubl_motion.cpp +++ b/Marlin/ubl_motion.cpp @@ -97,7 +97,7 @@ FINAL_MOVE: // The distance is always MESH_X_DIST so multiply by the constant reciprocal. - const float xratio = (end[X_AXIS] - mesh_index_to_xpos(cell_dest_xi)) * (1.0 / (MESH_X_DIST)); + const float xratio = (end[X_AXIS] - mesh_index_to_xpos(cell_dest_xi)) * (1.0f / (MESH_X_DIST)); float z1 = z_values[cell_dest_xi ][cell_dest_yi ] + xratio * (z_values[cell_dest_xi + 1][cell_dest_yi ] - z_values[cell_dest_xi][cell_dest_yi ]), @@ -107,7 +107,7 @@ if (cell_dest_xi >= GRID_MAX_POINTS_X - 1) z1 = z2 = 0.0; // X cell-fraction done. Interpolate the two Z offsets with the Y fraction for the final Z offset. - const float yratio = (end[Y_AXIS] - mesh_index_to_ypos(cell_dest_yi)) * (1.0 / (MESH_Y_DIST)), + const float yratio = (end[Y_AXIS] - mesh_index_to_ypos(cell_dest_yi)) * (1.0f / (MESH_Y_DIST)), z0 = cell_dest_yi < GRID_MAX_POINTS_Y - 1 ? (z1 + (z2 - z1) * yratio) * planner.fade_scaling_factor_for_z(end[Z_AXIS]) : 0.0; // Undefined parts of the Mesh in z_values[][] are NAN. @@ -435,14 +435,14 @@ #if IS_KINEMATIC const float seconds = cartesian_xy_mm / feedrate; // seconds to move xy distance at requested rate uint16_t segments = lroundf(delta_segments_per_second * seconds), // preferred number of segments for distance @ feedrate - seglimit = lroundf(cartesian_xy_mm * (1.0 / (DELTA_SEGMENT_MIN_LENGTH))); // number of segments at minimum segment length + seglimit = lroundf(cartesian_xy_mm * (1.0f / (DELTA_SEGMENT_MIN_LENGTH))); // number of segments at minimum segment length NOMORE(segments, seglimit); // limit to minimum segment length (fewer segments) #else - uint16_t segments = lroundf(cartesian_xy_mm * (1.0 / (DELTA_SEGMENT_MIN_LENGTH))); // cartesian fixed segment length + uint16_t segments = lroundf(cartesian_xy_mm * (1.0f / (DELTA_SEGMENT_MIN_LENGTH))); // cartesian fixed segment length #endif NOLESS(segments, 1U); // must have at least one segment - const float inv_segments = 1.0 / segments; // divide once, multiply thereafter + const float inv_segments = 1.0f / segments; // divide once, multiply thereafter #if IS_SCARA // scale the feed rate from mm/s to degrees/s scara_feed_factor = cartesian_xy_mm * inv_segments * feedrate; @@ -495,8 +495,8 @@ // in top of loop and again re-find same adjacent cell and use it, just less efficient // for mesh inset area. - int8_t cell_xi = (raw[X_AXIS] - (MESH_MIN_X)) * (1.0 / (MESH_X_DIST)), - cell_yi = (raw[Y_AXIS] - (MESH_MIN_Y)) * (1.0 / (MESH_Y_DIST)); + int8_t cell_xi = (raw[X_AXIS] - (MESH_MIN_X)) * (1.0f / (MESH_X_DIST)), + cell_yi = (raw[Y_AXIS] - (MESH_MIN_Y)) * (1.0f / (MESH_Y_DIST)); cell_xi = constrain(cell_xi, 0, (GRID_MAX_POINTS_X) - 1); cell_yi = constrain(cell_yi, 0, (GRID_MAX_POINTS_Y) - 1); @@ -517,15 +517,15 @@ float cx = raw[X_AXIS] - x0, // cell-relative x and y cy = raw[Y_AXIS] - y0; - const float z_xmy0 = (z_x1y0 - z_x0y0) * (1.0 / (MESH_X_DIST)), // z slope per x along y0 (lower left to lower right) - z_xmy1 = (z_x1y1 - z_x0y1) * (1.0 / (MESH_X_DIST)); // z slope per x along y1 (upper left to upper right) + const float z_xmy0 = (z_x1y0 - z_x0y0) * (1.0f / (MESH_X_DIST)), // z slope per x along y0 (lower left to lower right) + z_xmy1 = (z_x1y1 - z_x0y1) * (1.0f / (MESH_X_DIST)); // z slope per x along y1 (upper left to upper right) float z_cxy0 = z_x0y0 + z_xmy0 * cx; // z height along y0 at cx (changes for each cx in cell) const float z_cxy1 = z_x0y1 + z_xmy1 * cx, // z height along y1 at cx z_cxyd = z_cxy1 - z_cxy0; // z height difference along cx from y0 to y1 - float z_cxym = z_cxyd * (1.0 / (MESH_Y_DIST)); // z slope per y along cx from y0 to y1 (changes for each cx in cell) + float z_cxym = z_cxyd * (1.0f / (MESH_Y_DIST)); // z slope per y along cx from y0 to y1 (changes for each cx in cell) // float z_cxcy = z_cxy0 + z_cxym * cy; // interpolated mesh z height along cx at cy (do inside the segment loop) @@ -534,7 +534,7 @@ // each change by a constant for fixed segment lengths. const float z_sxy0 = z_xmy0 * diff[X_AXIS], // per-segment adjustment to z_cxy0 - z_sxym = (z_xmy1 - z_xmy0) * (1.0 / (MESH_Y_DIST)) * diff[X_AXIS]; // per-segment adjustment to z_cxym + z_sxym = (z_xmy1 - z_xmy0) * (1.0f / (MESH_Y_DIST)) * diff[X_AXIS]; // per-segment adjustment to z_cxym for (;;) { // for all segments within this mesh cell diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index 325ece19f6..80aeadd29e 100644 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -246,7 +246,7 @@ uint16_t max_display_update_time = 0; void menu_edit_callback_ ## _name(); \ void _menu_action_setting_edit_ ## _name(const char * const pstr, _type* const ptr, const _type minValue, const _type maxValue); \ void menu_action_setting_edit_ ## _name(const char * const pstr, _type * const ptr, const _type minValue, const _type maxValue); \ - void menu_action_setting_edit_callback_ ## _name(const char * const pstr, _type * const ptr, const _type minValue, const _type maxValue, const screenFunc_t callback, const bool live=false); \ + void menu_action_setting_edit_callback_ ## _name(const char * const pstr, _type * const ptr, const _type minValue, const _type maxValue, const screenFunc_t callback=NULL, const bool live=false); \ typedef void _name##_void DECLARE_MENU_EDIT_TYPE(int16_t, int3); @@ -467,7 +467,7 @@ uint16_t max_display_update_time = 0; #if IS_KINEMATIC bool processing_manual_move = false; - float manual_move_offset = 0.0; + float manual_move_offset = 0; #else constexpr bool processing_manual_move = false; #endif @@ -861,21 +861,13 @@ void lcd_quick_feedback(const bool clear_buttons) { abort_sd_printing = true; lcd_setstatusPGM(PSTR(MSG_PRINT_ABORTED), -1); lcd_return_to_status(); - - #if ENABLED(POWER_LOSS_RECOVERY) - card.openJobRecoveryFile(false); - job_recovery_info.valid_head = job_recovery_info.valid_foot = 0; - (void)card.saveJobRecoveryInfo(); - card.closeJobRecoveryFile(); - job_recovery_commands_count = 0; - #endif } #endif // SDSUPPORT #if ENABLED(POWER_LOSS_RECOVERY) - static void lcd_sdcard_recover_job() { + static void lcd_power_loss_recovery_resume() { char cmd[20]; // Return to status now @@ -883,45 +875,65 @@ void lcd_quick_feedback(const bool clear_buttons) { // Turn leveling off and home enqueue_and_echo_commands_P(PSTR("M420 S0\nG28" - #if !IS_KINEMATIC + #if ENABLED(MARLIN_DEV_MODE) + " S" + #elif !IS_KINEMATIC " X Y" #endif )); #if HAS_HEATED_BED - // Restore the bed temperature - sprintf_P(cmd, PSTR("M190 S%i"), job_recovery_info.target_temperature_bed); - enqueue_and_echo_command(cmd); + const int16_t bt = job_recovery_info.target_temperature_bed; + if (bt) { + // Restore the bed temperature + sprintf_P(cmd, PSTR("M190 S%i"), bt); + enqueue_and_echo_command(cmd); + } #endif // Restore all hotend temperatures HOTEND_LOOP() { - sprintf_P(cmd, PSTR("M109 S%i"), job_recovery_info.target_temperature[e]); - enqueue_and_echo_command(cmd); + const int16_t et = job_recovery_info.target_temperature[e]; + if (et) { + #if HOTENDS > 1 + sprintf_P(cmd, PSTR("T%i"), e); + enqueue_and_echo_command(cmd); + #endif + sprintf_P(cmd, PSTR("M109 S%i"), et); + enqueue_and_echo_command(cmd); + } } + #if HOTENDS > 1 + sprintf_P(cmd, PSTR("T%i"), job_recovery_info.active_hotend); + enqueue_and_echo_command(cmd); + #endif + // Restore print cooling fan speeds for (uint8_t i = 0; i < FAN_COUNT; i++) { - sprintf_P(cmd, PSTR("M106 P%i S%i"), i, job_recovery_info.fanSpeeds[i]); - enqueue_and_echo_command(cmd); + int16_t f = job_recovery_info.fanSpeeds[i]; + if (f) { + sprintf_P(cmd, PSTR("M106 P%i S%i"), i, f); + enqueue_and_echo_command(cmd); + } } // Start draining the job recovery command queue job_recovery_phase = JOB_RECOVERY_YES; + } - // Resume the print job timer - if (job_recovery_info.print_job_elapsed) - print_job_timer.resume(job_recovery_info.print_job_elapsed); - - // Start getting commands from SD - card.startFileprint(); + static void lcd_power_loss_recovery_cancel() { + card.removeJobRecoveryFile(); + card.autostart_index = 0; + lcd_return_to_status(); } static void lcd_job_recovery_menu() { defer_return_to_status = true; START_MENU(); - MENU_ITEM(function, MSG_RESUME_PRINT, lcd_sdcard_recover_job); - MENU_ITEM(function, MSG_STOP_PRINT, lcd_sdcard_stop); + STATIC_ITEM(MSG_POWER_LOSS_RECOVERY); + MENU_ITEM(function, MSG_RESUME_PRINT, lcd_power_loss_recovery_resume); + MENU_ITEM(function, MSG_STOP_PRINT, lcd_power_loss_recovery_cancel); END_MENU(); } @@ -939,7 +951,7 @@ void lcd_quick_feedback(const bool clear_buttons) { // ^ Main // MENU_BACK(MSG_MAIN); - //MENU_ITEM_EDIT_CALLBACK(int8, MSG_CASE_LIGHT_BRIGHTNESS, &case_light_brightness, 0, 255, update_case_light, true); + MENU_ITEM_EDIT_CALLBACK(int8, MSG_CASE_LIGHT_BRIGHTNESS, &case_light_brightness, 0, 255, update_case_light, true); MENU_ITEM_EDIT_CALLBACK(bool, MSG_CASE_LIGHT, (bool*)&case_light_on, update_case_light); END_MENU(); } @@ -1263,13 +1275,13 @@ void lcd_quick_feedback(const bool clear_buttons) { ubl_encoderPosition = (ubl.encoder_diff > 0) ? 1 : -1; ubl.encoder_diff = 0; - mesh_edit_accumulator += float(ubl_encoderPosition) * 0.005 / 2.0; + mesh_edit_accumulator += float(ubl_encoderPosition) * 0.005f / 2.0f; mesh_edit_value = mesh_edit_accumulator; encoderPosition = 0; lcdDrawUpdate = LCDVIEW_CALL_REDRAW_NEXT; - const int32_t rounded = (int32_t)(mesh_edit_value * 1000.0); - mesh_edit_value = float(rounded - (rounded % 5L)) / 1000.0; + const int32_t rounded = (int32_t)(mesh_edit_value * 1000); + mesh_edit_value = float(rounded - (rounded % 5L)) / 1000; } if (lcdDrawUpdate) { @@ -1397,7 +1409,7 @@ void lcd_quick_feedback(const bool clear_buttons) { // Leveling Fade Height // #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) && DISABLED(SLIM_LCD_MENUS) - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float62, MSG_Z_FADE_HEIGHT, &new_z_fade_height, 0.0, 100.0, _lcd_set_z_fade_height); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float3, MSG_Z_FADE_HEIGHT, &new_z_fade_height, 0, 100, _lcd_set_z_fade_height); #endif // @@ -1956,7 +1968,7 @@ void lcd_quick_feedback(const bool clear_buttons) { // if (encoderPosition) { const float z = current_position[Z_AXIS] + float((int32_t)encoderPosition) * (MBL_Z_STEP); - line_to_z(constrain(z, -(LCD_PROBE_Z_RANGE) * 0.5, (LCD_PROBE_Z_RANGE) * 0.5)); + line_to_z(constrain(z, -(LCD_PROBE_Z_RANGE) * 0.5f, (LCD_PROBE_Z_RANGE) * 0.5f)); lcdDrawUpdate = LCDVIEW_CALL_REDRAW_NEXT; encoderPosition = 0; } @@ -1966,7 +1978,7 @@ void lcd_quick_feedback(const bool clear_buttons) { // if (lcdDrawUpdate) { const float v = current_position[Z_AXIS]; - lcd_implementation_drawedit(PSTR(MSG_MOVE_Z), ftostr43sign(v + (v < 0 ? -0.0001 : 0.0001), '+')); + lcd_implementation_drawedit(PSTR(MSG_MOVE_Z), ftostr43sign(v + (v < 0 ? -0.0001f : 0.0001f), '+')); } } @@ -2016,8 +2028,7 @@ void lcd_quick_feedback(const bool clear_buttons) { void _lcd_level_bed_homing() { if (lcdDrawUpdate) lcd_implementation_drawedit(PSTR(MSG_LEVEL_BED_HOMING), NULL); lcdDrawUpdate = LCDVIEW_CALL_NO_REDRAW; - if (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) - lcd_goto_screen(_lcd_level_bed_homing_done); + if (all_axes_homed()) lcd_goto_screen(_lcd_level_bed_homing_done); } #if ENABLED(PROBE_MANUALLY) @@ -2029,7 +2040,7 @@ void lcd_quick_feedback(const bool clear_buttons) { */ void _lcd_level_bed_continue() { defer_return_to_status = true; - axis_homed[X_AXIS] = axis_homed[Y_AXIS] = axis_homed[Z_AXIS] = false; + axis_homed = 0; lcd_goto_screen(_lcd_level_bed_homing); enqueue_and_echo_commands_P(PSTR("G28")); } @@ -2359,7 +2370,7 @@ void lcd_quick_feedback(const bool clear_buttons) { defer_return_to_status = true; if (lcdDrawUpdate) lcd_implementation_drawmenu_static(LCD_HEIGHT < 3 ? 0 : (LCD_HEIGHT > 4 ? 2 : 1), PSTR(MSG_LEVEL_BED_HOMING)); lcdDrawUpdate = LCDVIEW_CALL_NO_REDRAW; - if (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) { + if (all_axes_homed()) { ubl.lcd_map_control = true; // Return to the map screen lcd_goto_screen(_lcd_ubl_output_map_lcd); } @@ -2403,7 +2414,7 @@ void lcd_quick_feedback(const bool clear_buttons) { void _lcd_ubl_output_map_lcd() { static int16_t step_scaler = 0; - if (!(axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS])) + if (!all_axes_known()) return lcd_goto_screen(_lcd_ubl_map_homing); if (use_click()) return _lcd_ubl_map_lcd_edit_cmd(); @@ -2452,8 +2463,8 @@ void lcd_quick_feedback(const bool clear_buttons) { * UBL Homing before LCD map */ void _lcd_ubl_output_map_lcd_cmd() { - if (!(axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS])) { - axis_homed[X_AXIS] = axis_homed[Y_AXIS] = axis_homed[Z_AXIS] = false; + if (!all_axes_known()) { + axis_homed = 0; enqueue_and_echo_commands_P(PSTR("G28")); } lcd_goto_screen(_lcd_ubl_map_homing); @@ -2549,7 +2560,7 @@ void lcd_quick_feedback(const bool clear_buttons) { MENU_ITEM(submenu, MSG_UBL_TOOLS, _lcd_ubl_tools_menu); MENU_ITEM(gcode, MSG_UBL_INFO_UBL, PSTR("G29 W")); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float62, MSG_Z_FADE_HEIGHT, &new_z_fade_height, 0.0, 100.0, _lcd_set_z_fade_height); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float3, MSG_Z_FADE_HEIGHT, &new_z_fade_height, 0, 100, _lcd_set_z_fade_height); #endif END_MENU(); } @@ -2557,7 +2568,7 @@ void lcd_quick_feedback(const bool clear_buttons) { #endif // AUTO_BED_LEVELING_UBL - #if ENABLED(LCD_BED_LEVELING) || (PLANNER_LEVELING && DISABLED(SLIM_LCD_MENUS)) + #if ENABLED(LCD_BED_LEVELING) || (HAS_LEVELING && DISABLED(SLIM_LCD_MENUS)) void _lcd_toggle_bed_leveling() { set_bed_leveling_enabled(!planner.leveling_active); } #endif @@ -2581,7 +2592,7 @@ void lcd_quick_feedback(const bool clear_buttons) { START_MENU(); MENU_BACK(MSG_PREPARE); - const bool is_homed = axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS]; + const bool is_homed = all_axes_known(); // Auto Home if not using manual probing #if DISABLED(PROBE_MANUALLY) && DISABLED(MESH_BED_LEVELING) @@ -2605,7 +2616,7 @@ void lcd_quick_feedback(const bool clear_buttons) { // Z Fade Height #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float62, MSG_Z_FADE_HEIGHT, &new_z_fade_height, 0.0, 100.0, _lcd_set_z_fade_height); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float3, MSG_Z_FADE_HEIGHT, &new_z_fade_height, 0, 100, _lcd_set_z_fade_height); #endif // @@ -2623,8 +2634,7 @@ void lcd_quick_feedback(const bool clear_buttons) { #if ENABLED(LEVEL_BED_CORNERS) // Move to the next corner for leveling - if (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) - MENU_ITEM(submenu, MSG_LEVEL_CORNERS, _lcd_level_bed_corners); + if (all_axes_homed()) MENU_ITEM(submenu, MSG_LEVEL_CORNERS, _lcd_level_bed_corners); #endif #if ENABLED(EEPROM_SETTINGS) @@ -2654,7 +2664,7 @@ void lcd_quick_feedback(const bool clear_buttons) { // Move Axis // #if ENABLED(DELTA) - if (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) + if (all_axes_homed()) #endif MENU_ITEM(submenu, MSG_MOVE_AXIS, lcd_move_menu); @@ -2682,7 +2692,7 @@ void lcd_quick_feedback(const bool clear_buttons) { #endif MENU_ITEM(submenu, MSG_BED_LEVELING, lcd_bed_leveling); - #elif PLANNER_LEVELING && DISABLED(SLIM_LCD_MENUS) + #elif HAS_LEVELING && DISABLED(SLIM_LCD_MENUS) #if DISABLED(PROBE_MANUALLY) MENU_ITEM(gcode, MSG_LEVEL_BED, PSTR("G28\nG29")); @@ -2692,13 +2702,13 @@ void lcd_quick_feedback(const bool clear_buttons) { MENU_ITEM_EDIT_CALLBACK(bool, MSG_BED_LEVELING, &new_level_state, _lcd_toggle_bed_leveling); } #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float62, MSG_Z_FADE_HEIGHT, &new_z_fade_height, 0.0, 100.0, _lcd_set_z_fade_height); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float3, MSG_Z_FADE_HEIGHT, &new_z_fade_height, 0, 100, _lcd_set_z_fade_height); #endif #endif #if ENABLED(LEVEL_BED_CORNERS) && DISABLED(LCD_BED_LEVELING) - if (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) + if (all_axes_homed()) MENU_ITEM(function, MSG_LEVEL_CORNERS, _lcd_level_bed_corners); #endif @@ -2828,7 +2838,7 @@ void lcd_quick_feedback(const bool clear_buttons) { void _lcd_calibrate_homing() { if (lcdDrawUpdate) lcd_implementation_drawmenu_static(LCD_HEIGHT >= 4 ? 1 : 0, PSTR(MSG_LEVEL_BED_HOMING)); lcdDrawUpdate = LCDVIEW_CALL_REDRAW_NEXT; - if (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) + if (all_axes_homed()) lcd_goto_previous_menu(); } @@ -2856,15 +2866,15 @@ void lcd_quick_feedback(const bool clear_buttons) { void lcd_delta_settings() { START_MENU(); MENU_BACK(MSG_DELTA_CALIBRATE); - MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_HEIGHT, &delta_height, delta_height - 10.0, delta_height + 10.0, _recalc_delta_settings); - MENU_ITEM_EDIT_CALLBACK(float43, "Ex", &delta_endstop_adj[A_AXIS], -5.0, 5.0, _recalc_delta_settings); - MENU_ITEM_EDIT_CALLBACK(float43, "Ey", &delta_endstop_adj[B_AXIS], -5.0, 5.0, _recalc_delta_settings); - MENU_ITEM_EDIT_CALLBACK(float43, "Ez", &delta_endstop_adj[C_AXIS], -5.0, 5.0, _recalc_delta_settings); - MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_RADIUS, &delta_radius, delta_radius - 5.0, delta_radius + 5.0, _recalc_delta_settings); - MENU_ITEM_EDIT_CALLBACK(float43, "Tx", &delta_tower_angle_trim[A_AXIS], -5.0, 5.0, _recalc_delta_settings); - MENU_ITEM_EDIT_CALLBACK(float43, "Ty", &delta_tower_angle_trim[B_AXIS], -5.0, 5.0, _recalc_delta_settings); - MENU_ITEM_EDIT_CALLBACK(float43, "Tz", &delta_tower_angle_trim[C_AXIS], -5.0, 5.0, _recalc_delta_settings); - MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_DIAG_ROD, &delta_diagonal_rod, delta_diagonal_rod - 5.0, delta_diagonal_rod + 5.0, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_HEIGHT, &delta_height, delta_height - 10, delta_height + 10, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float43, "Ex", &delta_endstop_adj[A_AXIS], -5, 5, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float43, "Ey", &delta_endstop_adj[B_AXIS], -5, 5, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float43, "Ez", &delta_endstop_adj[C_AXIS], -5, 5, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_RADIUS, &delta_radius, delta_radius - 5, delta_radius + 5, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float43, "Tx", &delta_tower_angle_trim[A_AXIS], -5, 5, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float43, "Ty", &delta_tower_angle_trim[B_AXIS], -5, 5, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float43, "Tz", &delta_tower_angle_trim[C_AXIS], -5, 5, _recalc_delta_settings); + MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_DELTA_DIAG_ROD, &delta_diagonal_rod, delta_diagonal_rod - 5, delta_diagonal_rod + 5, _recalc_delta_settings); END_MENU(); } @@ -2883,7 +2893,7 @@ void lcd_quick_feedback(const bool clear_buttons) { MENU_ITEM(submenu, MSG_DELTA_SETTINGS, lcd_delta_settings); #if ENABLED(DELTA_CALIBRATION_MENU) MENU_ITEM(submenu, MSG_AUTO_HOME, _lcd_delta_calibrate_home); - if (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) { + if (all_axes_homed()) { MENU_ITEM(submenu, MSG_DELTA_CALIBRATE_X, _goto_tower_x); MENU_ITEM(submenu, MSG_DELTA_CALIBRATE_Y, _goto_tower_y); MENU_ITEM(submenu, MSG_DELTA_CALIBRATE_Z, _goto_tower_z); @@ -2920,7 +2930,7 @@ void lcd_quick_feedback(const bool clear_buttons) { destination[manual_move_axis] += manual_move_offset; // Reset for the next move - manual_move_offset = 0.0; + manual_move_offset = 0; manual_move_axis = (int8_t)NO_AXIS; // DELTA and SCARA machines use segmented moves, which could fill the planner during the call to @@ -2960,7 +2970,7 @@ void lcd_quick_feedback(const bool clear_buttons) { #endif manual_move_e_index = eindex >= 0 ? eindex : active_extruder; #endif - manual_move_start_time = millis() + (move_menu_scale < 0.99 ? 0UL : 250UL); // delay for bigger moves + manual_move_start_time = millis() + (move_menu_scale < 0.99f ? 0UL : 250UL); // delay for bigger moves manual_move_axis = (int8_t)axis; } @@ -3044,7 +3054,7 @@ void lcd_quick_feedback(const bool clear_buttons) { + manual_move_offset #endif , axis); - lcd_implementation_drawedit(name, move_menu_scale >= 0.1 ? ftostr41sign(pos) : ftostr43sign(pos)); + lcd_implementation_drawedit(name, move_menu_scale >= 0.1f ? ftostr41sign(pos) : ftostr43sign(pos)); } } void lcd_move_x() { _lcd_move_xyz(PSTR(MSG_MOVE_X), X_AXIS); } @@ -3129,9 +3139,9 @@ void lcd_quick_feedback(const bool clear_buttons) { move_menu_scale = scale; lcd_goto_screen(_manual_move_func_ptr); } - void lcd_move_menu_10mm() { _goto_manual_move(10.0); } - void lcd_move_menu_1mm() { _goto_manual_move( 1.0); } - void lcd_move_menu_01mm() { _goto_manual_move( 0.1); } + void lcd_move_menu_10mm() { _goto_manual_move(10); } + void lcd_move_menu_1mm() { _goto_manual_move( 1); } + void lcd_move_menu_01mm() { _goto_manual_move( 0.1f); } void _lcd_move_distance_menu(const AxisEnum axis, const screenFunc_t func) { _manual_move_func_ptr = func; @@ -3179,7 +3189,7 @@ void lcd_quick_feedback(const bool clear_buttons) { */ #if IS_KINEMATIC || ENABLED(NO_MOTION_BEFORE_HOMING) - #define _MOVE_XYZ_ALLOWED (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) + #define _MOVE_XYZ_ALLOWED (all_axes_homed()) #else #define _MOVE_XYZ_ALLOWED true #endif @@ -3249,20 +3259,36 @@ void lcd_quick_feedback(const bool clear_buttons) { #endif - MENU_ITEM(submenu, MSG_MOVE_E, lcd_move_get_e_amount); - #if E_MANUAL > 1 - MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E1, lcd_move_get_e0_amount); - MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E2, lcd_move_get_e1_amount); - #if E_MANUAL > 2 + #if ENABLED(SWITCHING_EXTRUDER) || ENABLED(SWITCHING_NOZZLE) + + // Only the current... + MENU_ITEM(submenu, MSG_MOVE_E, lcd_move_get_e_amount); + // ...and the non-switching + #if E_MANUAL == 5 + MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E5, lcd_move_get_e4_amount); + #elif E_MANUAL == 3 MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E3, lcd_move_get_e2_amount); - #if E_MANUAL > 3 - MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E4, lcd_move_get_e3_amount); - #if E_MANUAL > 4 - MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E5, lcd_move_get_e4_amount); - #endif // E_MANUAL > 4 - #endif // E_MANUAL > 3 - #endif // E_MANUAL > 2 - #endif // E_MANUAL > 1 + #endif + + #else + + // Independent extruders with one E-stepper per hotend + MENU_ITEM(submenu, MSG_MOVE_E, lcd_move_get_e_amount); + #if E_MANUAL > 1 + MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E1, lcd_move_get_e0_amount); + MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E2, lcd_move_get_e1_amount); + #if E_MANUAL > 2 + MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E3, lcd_move_get_e2_amount); + #if E_MANUAL > 3 + MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E4, lcd_move_get_e3_amount); + #if E_MANUAL > 4 + MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E5, lcd_move_get_e4_amount); + #endif // E_MANUAL > 4 + #endif // E_MANUAL > 3 + #endif // E_MANUAL > 2 + #endif // E_MANUAL > 1 + + #endif END_MENU(); } @@ -3490,8 +3516,8 @@ void lcd_quick_feedback(const bool clear_buttons) { // #if ENABLED(AUTOTEMP) && HAS_TEMP_HOTEND MENU_ITEM_EDIT(bool, MSG_AUTOTEMP, &planner.autotemp_enabled); - MENU_ITEM_EDIT(float3, MSG_MIN, &planner.autotemp_min, 0, HEATER_0_MAXTEMP - 15); - MENU_ITEM_EDIT(float3, MSG_MAX, &planner.autotemp_max, 0, HEATER_0_MAXTEMP - 15); + MENU_ITEM_EDIT(float3, MSG_MIN, &planner.autotemp_min, 0, float(HEATER_0_MAXTEMP) - 15); + MENU_ITEM_EDIT(float3, MSG_MAX, &planner.autotemp_max, 0, float(HEATER_0_MAXTEMP) - 15); MENU_ITEM_EDIT(float52, MSG_FACTOR, &planner.autotemp_factor, 0.0, 1.0); #endif @@ -3509,7 +3535,7 @@ void lcd_quick_feedback(const bool clear_buttons) { raw_Ki = unscalePID_i(PID_PARAM(Ki, eindex)); \ raw_Kd = unscalePID_d(PID_PARAM(Kd, eindex)); \ MENU_ITEM_EDIT(float52sign, MSG_PID_P ELABEL, &PID_PARAM(Kp, eindex), 1, 9990); \ - MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_PID_I ELABEL, &raw_Ki, 0.01, 9990, copy_and_scalePID_i_E ## eindex); \ + MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_PID_I ELABEL, &raw_Ki, 0.01f, 9990, copy_and_scalePID_i_E ## eindex); \ MENU_ITEM_EDIT_CALLBACK(float52sign, MSG_PID_D ELABEL, &raw_Kd, 1, 9990, copy_and_scalePID_d_E ## eindex) #if ENABLED(PID_EXTRUSION_SCALING) @@ -3631,7 +3657,7 @@ void lcd_quick_feedback(const bool clear_buttons) { if (e == active_extruder) _planner_refresh_positioning(); else - planner.steps_to_mm[E_AXIS + e] = 1.0 / planner.axis_steps_per_mm[E_AXIS + e]; + planner.steps_to_mm[E_AXIS + e] = 1.0f / planner.axis_steps_per_mm[E_AXIS + e]; } void _planner_refresh_e0_positioning() { _planner_refresh_e_positioning(0); } void _planner_refresh_e1_positioning() { _planner_refresh_e_positioning(1); } @@ -3652,32 +3678,32 @@ void lcd_quick_feedback(const bool clear_buttons) { MENU_BACK(MSG_MOTION); // M203 Max Feedrate - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_A, &planner.max_feedrate_mm_s[A_AXIS], 1, 999); - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_B, &planner.max_feedrate_mm_s[B_AXIS], 1, 999); - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_C, &planner.max_feedrate_mm_s[C_AXIS], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_A, &planner.max_feedrate_mm_s[A_AXIS], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_B, &planner.max_feedrate_mm_s[B_AXIS], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_C, &planner.max_feedrate_mm_s[C_AXIS], 1, 999); #if ENABLED(DISTINCT_E_FACTORS) - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_E, &planner.max_feedrate_mm_s[E_AXIS + active_extruder], 1, 999); - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_E1, &planner.max_feedrate_mm_s[E_AXIS], 1, 999); - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_E2, &planner.max_feedrate_mm_s[E_AXIS + 1], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_E, &planner.max_feedrate_mm_s[E_AXIS + active_extruder], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_E1, &planner.max_feedrate_mm_s[E_AXIS], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_E2, &planner.max_feedrate_mm_s[E_AXIS + 1], 1, 999); #if E_STEPPERS > 2 - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_E3, &planner.max_feedrate_mm_s[E_AXIS + 2], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_E3, &planner.max_feedrate_mm_s[E_AXIS + 2], 1, 999); #if E_STEPPERS > 3 - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_E4, &planner.max_feedrate_mm_s[E_AXIS + 3], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_E4, &planner.max_feedrate_mm_s[E_AXIS + 3], 1, 999); #if E_STEPPERS > 4 - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_E5, &planner.max_feedrate_mm_s[E_AXIS + 4], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_E5, &planner.max_feedrate_mm_s[E_AXIS + 4], 1, 999); #endif // E_STEPPERS > 4 #endif // E_STEPPERS > 3 #endif // E_STEPPERS > 2 #else - MENU_ITEM_EDIT(float3, MSG_VMAX MSG_E, &planner.max_feedrate_mm_s[E_AXIS], 1, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMAX MSG_E, &planner.max_feedrate_mm_s[E_AXIS], 1, 999); #endif // M205 S Min Feedrate - MENU_ITEM_EDIT(float3, MSG_VMIN, &planner.min_feedrate_mm_s, 0, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VMIN, &planner.min_feedrate_mm_s, 0, 999); // M205 T Min Travel Feedrate - MENU_ITEM_EDIT(float3, MSG_VTRAV_MIN, &planner.min_travel_feedrate_mm_s, 0, 999); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VTRAV_MIN, &planner.min_travel_feedrate_mm_s, 0, 999); END_MENU(); } @@ -3688,34 +3714,34 @@ void lcd_quick_feedback(const bool clear_buttons) { MENU_BACK(MSG_MOTION); // M204 P Acceleration - MENU_ITEM_EDIT(float5, MSG_ACC, &planner.acceleration, 10, 99000); + MENU_MULTIPLIER_ITEM_EDIT(float5, MSG_ACC, &planner.acceleration, 10, 99000); // M204 R Retract Acceleration - MENU_ITEM_EDIT(float5, MSG_A_RETRACT, &planner.retract_acceleration, 100, 99000); + MENU_MULTIPLIER_ITEM_EDIT(float5, MSG_A_RETRACT, &planner.retract_acceleration, 100, 99000); // M204 T Travel Acceleration - MENU_ITEM_EDIT(float5, MSG_A_TRAVEL, &planner.travel_acceleration, 100, 99000); + MENU_MULTIPLIER_ITEM_EDIT(float5, MSG_A_TRAVEL, &planner.travel_acceleration, 100, 99000); // M201 settings - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_A, &planner.max_acceleration_mm_per_s2[A_AXIS], 100, 99000, _reset_acceleration_rates); - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_B, &planner.max_acceleration_mm_per_s2[B_AXIS], 100, 99000, _reset_acceleration_rates); - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_C, &planner.max_acceleration_mm_per_s2[C_AXIS], 10, 99000, _reset_acceleration_rates); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_A, &planner.max_acceleration_mm_per_s2[A_AXIS], 100, 99000, _reset_acceleration_rates); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_B, &planner.max_acceleration_mm_per_s2[B_AXIS], 100, 99000, _reset_acceleration_rates); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_C, &planner.max_acceleration_mm_per_s2[C_AXIS], 10, 99000, _reset_acceleration_rates); #if ENABLED(DISTINCT_E_FACTORS) - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E, &planner.max_acceleration_mm_per_s2[E_AXIS + active_extruder], 100, 99000, _reset_acceleration_rates); - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E1, &planner.max_acceleration_mm_per_s2[E_AXIS], 100, 99000, _reset_e0_acceleration_rate); - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E2, &planner.max_acceleration_mm_per_s2[E_AXIS + 1], 100, 99000, _reset_e1_acceleration_rate); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E, &planner.max_acceleration_mm_per_s2[E_AXIS + active_extruder], 100, 99000, _reset_acceleration_rates); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E1, &planner.max_acceleration_mm_per_s2[E_AXIS], 100, 99000, _reset_e0_acceleration_rate); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E2, &planner.max_acceleration_mm_per_s2[E_AXIS + 1], 100, 99000, _reset_e1_acceleration_rate); #if E_STEPPERS > 2 - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E3, &planner.max_acceleration_mm_per_s2[E_AXIS + 2], 100, 99000, _reset_e2_acceleration_rate); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E3, &planner.max_acceleration_mm_per_s2[E_AXIS + 2], 100, 99000, _reset_e2_acceleration_rate); #if E_STEPPERS > 3 - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E4, &planner.max_acceleration_mm_per_s2[E_AXIS + 3], 100, 99000, _reset_e3_acceleration_rate); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E4, &planner.max_acceleration_mm_per_s2[E_AXIS + 3], 100, 99000, _reset_e3_acceleration_rate); #if E_STEPPERS > 4 - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E5, &planner.max_acceleration_mm_per_s2[E_AXIS + 4], 100, 99000, _reset_e4_acceleration_rate); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E5, &planner.max_acceleration_mm_per_s2[E_AXIS + 4], 100, 99000, _reset_e4_acceleration_rate); #endif // E_STEPPERS > 4 #endif // E_STEPPERS > 3 #endif // E_STEPPERS > 2 #else - MENU_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E, &planner.max_acceleration_mm_per_s2[E_AXIS], 100, 99000, _reset_acceleration_rates); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(long5, MSG_AMAX MSG_E, &planner.max_acceleration_mm_per_s2[E_AXIS], 100, 99000, _reset_acceleration_rates); #endif END_MENU(); @@ -3726,14 +3752,18 @@ void lcd_quick_feedback(const bool clear_buttons) { START_MENU(); MENU_BACK(MSG_MOTION); - MENU_ITEM_EDIT(float3, MSG_VA_JERK, &planner.max_jerk[A_AXIS], 1, 990); - MENU_ITEM_EDIT(float3, MSG_VB_JERK, &planner.max_jerk[B_AXIS], 1, 990); - #if ENABLED(DELTA) - MENU_ITEM_EDIT(float3, MSG_VC_JERK, &planner.max_jerk[C_AXIS], 1, 990); + #if ENABLED(JUNCTION_DEVIATION) + MENU_ITEM_EDIT_CALLBACK(float43, MSG_JUNCTION_DEVIATION, &planner.junction_deviation_mm, 0.01f, 0.3f, planner.recalculate_max_e_jerk); #else - MENU_ITEM_EDIT(float52sign, MSG_VC_JERK, &planner.max_jerk[C_AXIS], 0.1, 990); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VA_JERK, &planner.max_jerk[A_AXIS], 1, 990); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VB_JERK, &planner.max_jerk[B_AXIS], 1, 990); + #if ENABLED(DELTA) + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VC_JERK, &planner.max_jerk[C_AXIS], 1, 990); + #else + MENU_MULTIPLIER_ITEM_EDIT(float52sign, MSG_VC_JERK, &planner.max_jerk[C_AXIS], 0.1f, 990); + #endif + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_VE_JERK, &planner.max_jerk[E_AXIS], 1, 990); #endif - MENU_ITEM_EDIT(float3, MSG_VE_JERK, &planner.max_jerk[E_AXIS], 1, 990); END_MENU(); } @@ -3828,17 +3858,17 @@ void lcd_quick_feedback(const bool clear_buttons) { if (parser.volumetric_enabled) { #if EXTRUDERS == 1 - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM, &planner.filament_size[0], 1.5, 3.25, planner.calculate_volumetric_multipliers); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM, &planner.filament_size[0], 1.5f, 3.25f, planner.calculate_volumetric_multipliers); #else // EXTRUDERS > 1 - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM, &planner.filament_size[active_extruder], 1.5, 3.25, planner.calculate_volumetric_multipliers); - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E1, &planner.filament_size[0], 1.5, 3.25, planner.calculate_volumetric_multipliers); - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E2, &planner.filament_size[1], 1.5, 3.25, planner.calculate_volumetric_multipliers); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM, &planner.filament_size[active_extruder], 1.5f, 3.25f, planner.calculate_volumetric_multipliers); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E1, &planner.filament_size[0], 1.5f, 3.25f, planner.calculate_volumetric_multipliers); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E2, &planner.filament_size[1], 1.5f, 3.25f, planner.calculate_volumetric_multipliers); #if EXTRUDERS > 2 - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E3, &planner.filament_size[2], 1.5, 3.25, planner.calculate_volumetric_multipliers); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E3, &planner.filament_size[2], 1.5f, 3.25f, planner.calculate_volumetric_multipliers); #if EXTRUDERS > 3 - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E4, &planner.filament_size[3], 1.5, 3.25, planner.calculate_volumetric_multipliers); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E4, &planner.filament_size[3], 1.5f, 3.25f, planner.calculate_volumetric_multipliers); #if EXTRUDERS > 4 - MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E5, &planner.filament_size[4], 1.5, 3.25, planner.calculate_volumetric_multipliers); + MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float43, MSG_FILAMENT_DIAM MSG_DIAM_E5, &planner.filament_size[4], 1.5f, 3.25f, planner.calculate_volumetric_multipliers); #endif // EXTRUDERS > 4 #endif // EXTRUDERS > 3 #endif // EXTRUDERS > 2 @@ -3851,39 +3881,39 @@ void lcd_quick_feedback(const bool clear_buttons) { #if ENABLED(PREVENT_LENGTHY_EXTRUDE) EXTRUDE_MAXLENGTH #else - 999.0f + 999 #endif ; #if EXTRUDERS == 1 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD, &filament_change_unload_length[0], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD, &filament_change_unload_length[0], 0, extrude_maxlength); #else // EXTRUDERS > 1 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD, &filament_change_unload_length[active_extruder], 0.0, extrude_maxlength); - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E1, &filament_change_unload_length[0], 0.0, extrude_maxlength); - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E2, &filament_change_unload_length[1], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD, &filament_change_unload_length[active_extruder], 0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E1, &filament_change_unload_length[0], 0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E2, &filament_change_unload_length[1], 0, extrude_maxlength); #if EXTRUDERS > 2 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E3, &filament_change_unload_length[2], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E3, &filament_change_unload_length[2], 0, extrude_maxlength); #if EXTRUDERS > 3 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E4, &filament_change_unload_length[3], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E4, &filament_change_unload_length[3], 0, extrude_maxlength); #if EXTRUDERS > 4 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E5, &filament_change_unload_length[4], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_UNLOAD MSG_DIAM_E5, &filament_change_unload_length[4], 0, extrude_maxlength); #endif // EXTRUDERS > 4 #endif // EXTRUDERS > 3 #endif // EXTRUDERS > 2 #endif // EXTRUDERS > 1 #if EXTRUDERS == 1 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD, &filament_change_load_length[0], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD, &filament_change_load_length[0], 0, extrude_maxlength); #else // EXTRUDERS > 1 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD, &filament_change_load_length[active_extruder], 0.0, extrude_maxlength); - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E1, &filament_change_load_length[0], 0.0, extrude_maxlength); - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E2, &filament_change_load_length[1], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD, &filament_change_load_length[active_extruder], 0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E1, &filament_change_load_length[0], 0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E2, &filament_change_load_length[1], 0, extrude_maxlength); #if EXTRUDERS > 2 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E3, &filament_change_load_length[2], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E3, &filament_change_load_length[2], 0, extrude_maxlength); #if EXTRUDERS > 3 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E4, &filament_change_load_length[3], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E4, &filament_change_load_length[3], 0, extrude_maxlength); #if EXTRUDERS > 4 - MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E5, &filament_change_load_length[4], 0.0, extrude_maxlength); + MENU_MULTIPLIER_ITEM_EDIT(float3, MSG_FILAMENT_LOAD MSG_DIAM_E5, &filament_change_load_length[4], 0, extrude_maxlength); #endif // EXTRUDERS > 4 #endif // EXTRUDERS > 3 #endif // EXTRUDERS > 2 @@ -4228,10 +4258,8 @@ void lcd_quick_feedback(const bool clear_buttons) { void lcd_led_menu() { START_MENU(); MENU_BACK(MSG_MAIN); - if (leds.lights_on) - MENU_ITEM(function, MSG_LEDS_OFF, leds.toggle); - else - MENU_ITEM(function, MSG_LEDS_ON, leds.toggle); + bool led_on = leds.lights_on; + MENU_ITEM_EDIT_CALLBACK(bool, MSG_LEDS, &led_on, leds.toggle); MENU_ITEM(function, MSG_SET_LEDS_DEFAULT, leds.set_default); #if ENABLED(LED_COLOR_PRESETS) MENU_ITEM(submenu, MSG_LED_PRESETS, lcd_led_presets_menu); @@ -4785,17 +4813,16 @@ void lcd_quick_feedback(const bool clear_buttons) { if ((int32_t)encoderPosition < 0) encoderPosition = 0; \ if ((int32_t)encoderPosition > maxEditValue) encoderPosition = maxEditValue; \ if (lcdDrawUpdate) \ - lcd_implementation_drawedit(editLabel, _strFunc(((_type)((int32_t)encoderPosition + minEditValue)) * (1.0 / _scale))); \ + lcd_implementation_drawedit(editLabel, _strFunc(((_type)((int32_t)encoderPosition + minEditValue)) * (1.0f / _scale))); \ if (lcd_clicked || (liveEdit && lcdDrawUpdate)) { \ - _type value = ((_type)((int32_t)encoderPosition + minEditValue)) * (1.0 / _scale); \ + _type value = ((_type)((int32_t)encoderPosition + minEditValue)) * (1.0f / _scale); \ if (editValue != NULL) *((_type*)editValue) = value; \ - if (liveEdit) (*callbackFunc)(); \ + if (callbackFunc && (liveEdit || lcd_clicked)) (*callbackFunc)(); \ if (lcd_clicked) lcd_goto_previous_menu(); \ } \ return use_click(); \ } \ void menu_edit_ ## _name() { _menu_edit_ ## _name(); } \ - void menu_edit_callback_ ## _name() { if (_menu_edit_ ## _name()) (*callbackFunc)(); } \ void _menu_action_setting_edit_ ## _name(const char * const pstr, _type* const ptr, const _type minValue, const _type maxValue) { \ lcd_save_previous_screen(); \ lcd_refresh(); \ @@ -4806,28 +4833,27 @@ void lcd_quick_feedback(const bool clear_buttons) { maxEditValue = maxValue * _scale - minEditValue; \ encoderPosition = (*ptr) * _scale - minEditValue; \ } \ - void menu_action_setting_edit_ ## _name(const char * const pstr, _type * const ptr, const _type minValue, const _type maxValue) { \ - _menu_action_setting_edit_ ## _name(pstr, ptr, minValue, maxValue); \ - currentScreen = menu_edit_ ## _name; \ - } \ void menu_action_setting_edit_callback_ ## _name(const char * const pstr, _type * const ptr, const _type minValue, const _type maxValue, const screenFunc_t callback, const bool live) { \ _menu_action_setting_edit_ ## _name(pstr, ptr, minValue, maxValue); \ - currentScreen = menu_edit_callback_ ## _name; \ + currentScreen = menu_edit_ ## _name; \ callbackFunc = callback; \ liveEdit = live; \ } \ + FORCE_INLINE void menu_action_setting_edit_ ## _name(const char * const pstr, _type * const ptr, const _type minValue, const _type maxValue) { \ + menu_action_setting_edit_callback_ ## _name(pstr, ptr, minValue, maxValue); \ + } \ typedef void _name##_void DEFINE_MENU_EDIT_TYPE(int16_t, int3, itostr3, 1); DEFINE_MENU_EDIT_TYPE(uint8_t, int8, i8tostr3, 1); - DEFINE_MENU_EDIT_TYPE(float, float3, ftostr3, 1.0); - DEFINE_MENU_EDIT_TYPE(float, float52, ftostr52, 100.0); - DEFINE_MENU_EDIT_TYPE(float, float43, ftostr43sign, 1000.0); - DEFINE_MENU_EDIT_TYPE(float, float5, ftostr5rj, 0.01); - DEFINE_MENU_EDIT_TYPE(float, float51, ftostr51sign, 10.0); - DEFINE_MENU_EDIT_TYPE(float, float52sign, ftostr52sign, 100.0); - DEFINE_MENU_EDIT_TYPE(float, float62, ftostr62rj, 100.0); - DEFINE_MENU_EDIT_TYPE(uint32_t, long5, ftostr5rj, 0.01); + DEFINE_MENU_EDIT_TYPE(float, float3, ftostr3, 1.0f); + DEFINE_MENU_EDIT_TYPE(float, float52, ftostr52, 100.0f); + DEFINE_MENU_EDIT_TYPE(float, float43, ftostr43sign, 1000.0f); + DEFINE_MENU_EDIT_TYPE(float, float5, ftostr5rj, 0.01f); + DEFINE_MENU_EDIT_TYPE(float, float51, ftostr51sign, 10.0f); + DEFINE_MENU_EDIT_TYPE(float, float52sign, ftostr52sign, 100.0f); + DEFINE_MENU_EDIT_TYPE(float, float62, ftostr62rj, 100.0f); + DEFINE_MENU_EDIT_TYPE(uint32_t, long5, ftostr5rj, 0.01f); /** * @@ -4901,7 +4927,7 @@ void lcd_quick_feedback(const bool clear_buttons) { if (REPRAPWORLD_KEYPAD_MOVE_Z_UP) reprapworld_keypad_move_z_up(); #endif - if (axis_homed[X_AXIS] && axis_homed[Y_AXIS] && axis_homed[Z_AXIS]) { + if (all_axes_homed()) { #if ENABLED(DELTA) || Z_HOME_DIR != -1 if (REPRAPWORLD_KEYPAD_MOVE_Z_UP) reprapworld_keypad_move_z_up(); #endif @@ -5209,7 +5235,7 @@ void lcd_update() { if (lastEncoderMovementMillis) { // Note that the rate is always calculated between two passes through the // loop and that the abs of the encoderDiff value is tracked. - float encoderStepRate = float(encoderMovementSteps) / float(ms - lastEncoderMovementMillis) * 1000.0; + float encoderStepRate = float(encoderMovementSteps) / float(ms - lastEncoderMovementMillis) * 1000; if (encoderStepRate >= ENCODER_100X_STEPS_PER_SEC) encoderMultiplier = 100; else if (encoderStepRate >= ENCODER_10X_STEPS_PER_SEC) encoderMultiplier = 10; diff --git a/Marlin/ultralcd_impl_HD44780.h b/Marlin/ultralcd_impl_HD44780.h index 0926c06e49..e2be71332b 100644 --- a/Marlin/ultralcd_impl_HD44780.h +++ b/Marlin/ultralcd_impl_HD44780.h @@ -636,11 +636,11 @@ FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const if (blink) lcd.print(value); else { - if (!axis_homed[axis]) + if (!TEST(axis_homed, axis)) while (const char c = *value++) lcd_print(c <= '.' ? c : '?'); else { #if DISABLED(HOME_AFTER_DEACTIVATE) && DISABLED(DISABLE_REDUCED_ACCURACY_WARNING) - if (!axis_known_position[axis]) + if (!TEST(axis_known_position, axis)) lcd_printPGM(axis == Z_AXIS ? PSTR(" ") : PSTR(" ")); else #endif diff --git a/Marlin/ultralcd_st7565_u8glib_VIKI.h b/Marlin/ultralcd_st7565_u8glib_VIKI.h index 0e97edfaa0..9ab142b2e8 100644 --- a/Marlin/ultralcd_st7565_u8glib_VIKI.h +++ b/Marlin/ultralcd_st7565_u8glib_VIKI.h @@ -122,10 +122,26 @@ #define U8G_DELAY() u8g_10MicroDelay() #endif -#define ST7565_CS() { WRITE(ST7565_CS_PIN,1); U8G_DELAY(); } -#define ST7565_NCS() { WRITE(ST7565_CS_PIN,0); } -#define ST7565_A0() { WRITE(ST7565_A0_PIN,1); U8G_DELAY(); } -#define ST7565_NA0() { WRITE(ST7565_A0_PIN,0); } +#define ST7565_CS() do{ WRITE(ST7565_CS_PIN, HIGH); U8G_DELAY(); }while(0) +#define ST7565_NCS() WRITE(ST7565_CS_PIN, LOW) +#define ST7565_A0() do{ WRITE(ST7565_A0_PIN, HIGH); U8G_DELAY(); }while(0) +#define ST7565_NA0() WRITE(ST7565_A0_PIN, LOW) + +#define ST7565_ADC_REVERSE(N) ST7565_WRITE_BYTE(0xA0 | ((N) & 0x1)) +#define ST7565_BIAS_MODE(N) ST7565_WRITE_BYTE(0xA2 | ((N) & 0x1)) +#define ST7565_ALL_PIX(N) ST7565_WRITE_BYTE(0xA4 | ((N) & 0x1)) +#define ST7565_INVERTED(N) ST7565_WRITE_BYTE(0xA6 | ((N) & 0x1)) +#define ST7565_ON(N) ST7565_WRITE_BYTE(0xAE | ((N) & 0x1)) +#define ST7565_OUT_MODE(N) ST7565_WRITE_BYTE(0xC0 | ((N) & 0x1) << 3) +#define ST7565_POWER_CONTROL(N) ST7565_WRITE_BYTE(0x28 | (N)) +#define ST7565_V0_RATIO(N) ST7565_WRITE_BYTE(0x10 | ((N) & 0x7)) // Specific to Displaytech 64128N? (ST7565 is 0x20 | N) +#define ST7565_CONTRAST(N) do{ ST7565_WRITE_BYTE(0x81); ST7565_WRITE_BYTE(N); }while(0) + +#define ST7565_COLUMN_ADR(N) do{ ST7565_WRITE_BYTE(0x10 | (((N) >> 4) & 0xF)); ST7565_WRITE_BYTE((N) & 0xF); }while(0) +#define ST7565_PAGE_ADR(N) ST7565_WRITE_BYTE(0xB0 | (N)) +#define ST7565_START_LINE(N) ST7565_WRITE_BYTE(0x40 | (N)) +#define ST7565_SLEEP_MODE() ST7565_WRITE_BYTE(0xAC) +#define ST7565_NOOP() ST7565_WRITE_BYTE(0xE3) uint8_t u8g_dev_st7565_64128n_2x_VIKI_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) { switch (msg) { @@ -137,109 +153,103 @@ uint8_t u8g_dev_st7565_64128n_2x_VIKI_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg OUT_WRITE(ST7565_CLK_PIN, LOW); #if HARDWARE_SPI - OUT_WRITE(SDSS, 1); // must be set to an output first or else will never go into master mode - SPCR = 0x50; // enable SPI in master mode at fast speed - SPSR = 1; // kick it up to 2x speed mode + OUT_WRITE(SDSS, 1); // must be set to an output first or else will never go into master mode + SPCR = 0x50; // enable SPI in master mode at fast speed + SPSR = 1; // kick it up to 2x speed mode #endif OUT_WRITE(ST7565_A0_PIN, LOW); - ST7565_CS(); /* disable chip */ - ST7565_NA0(); /* instruction mode */ - ST7565_NCS(); /* enable chip */ + ST7565_CS(); // chip select off + ST7565_NA0(); // instruction mode + ST7565_NCS(); // chip select - ST7565_WRITE_BYTE(0x0A2); /* 0x0A2: LCD bias 1/9 (according to Displaytech 64128N datasheet) */ - ST7565_WRITE_BYTE(0x0A0); /* Normal ADC Select (according to Displaytech 64128N datasheet) */ + ST7565_BIAS_MODE(0); // 0xA2: LCD bias 1/9 (according to Displaytech 64128N datasheet) + ST7565_ADC_REVERSE(0); // Normal (not flipped) ADC Select (according to Displaytech 64128N datasheet) - ST7565_WRITE_BYTE(0x0C8); /* common output mode: set scan direction normal operation/SHL Select; 0x0C0 --> SHL = 0; normal; 0x0C8 --> SHL = 1 */ - ST7565_WRITE_BYTE(0x040); /* Display start line for Displaytech 64128N */ + ST7565_OUT_MODE(1); // common output mode: set scan direction normal operation/SHL Select; 0x0C0 --> SHL = 0; normal; 0x0C8 --> SHL = 1 + ST7565_START_LINE(0); // Display start line for Displaytech 64128N - ST7565_WRITE_BYTE(0x028 | 0x04); /* power control: turn on voltage converter */ - //U8G_ESC_DLY(50); /* delay 50 ms - hangs after a reset if used */ + ST7565_POWER_CONTROL(0x4); // power control: turn on Booster + U8G_ESC_DLY(50); // delay 50 ms - hangs after a reset if used - ST7565_WRITE_BYTE(0x028 | 0x06); /* power control: turn on voltage regulator */ - //U8G_ESC_DLY(50); /* delay 50 ms - hangs after a reset if used */ + ST7565_POWER_CONTROL(0x6); // power control: turn on Booster, Voltage Regulator + U8G_ESC_DLY(50); // delay 50 ms - hangs after a reset if used - ST7565_WRITE_BYTE(0x028 | 0x07); /* power control: turn on voltage follower */ - //U8G_ESC_DLY(50); /* delay 50 ms - hangs after a reset if used */ + ST7565_POWER_CONTROL(0x7); // power control: turn on Booster, Voltage Regulator, Voltage Follower + U8G_ESC_DLY(50); // delay 50 ms - hangs after a reset if used - ST7565_WRITE_BYTE(0x010); /* Set V0 voltage resistor ratio. Setting for controlling brightness of Displaytech 64128N */ + ST7565_V0_RATIO(0); // Set V0 Voltage Resistor ratio. Setting for controlling brightness of Displaytech 64128N - ST7565_WRITE_BYTE(0x0A6); /* display normal, bit val 0: LCD pixel off. */ + ST7565_INVERTED(0); // display normal, bit val 0: LCD pixel off. - ST7565_WRITE_BYTE(0x081); /* set contrast */ - ST7565_WRITE_BYTE(0x01E); /* Contrast value. Setting for controlling brightness of Displaytech 64128N */ + ST7565_CONTRAST(0x1E); // Contrast value. Setting for controlling contrast of Displaytech 64128N - ST7565_WRITE_BYTE(0x0AF); /* display on */ + ST7565_ON(1); // display on - U8G_ESC_DLY(100); /* delay 100 ms */ - ST7565_WRITE_BYTE(0x0A5); /* display all points; ST7565 */ - U8G_ESC_DLY(100); /* delay 100 ms */ - U8G_ESC_DLY(100); /* delay 100 ms */ - ST7565_WRITE_BYTE(0x0A4); /* normal display */ - ST7565_CS(); /* disable chip */ - } /* end of sequence */ + U8G_ESC_DLY(100); // delay 100 ms + ST7565_ALL_PIX(1); // display all points; ST7565 + U8G_ESC_DLY(100); // delay 100 ms + U8G_ESC_DLY(100); // delay 100 ms + ST7565_ALL_PIX(0); // normal display + ST7565_CS(); // chip select off + } // end of sequence break; case U8G_DEV_MSG_STOP: break; case U8G_DEV_MSG_PAGE_NEXT: { - u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem); - ST7565_CS(); /* disable chip */ - ST7565_NA0(); /* instruction mode */ - ST7565_NCS(); /* enable chip */ - ST7565_WRITE_BYTE(0x010); /* set upper 4 bit of the col adr to 0x10 */ - ST7565_WRITE_BYTE(0x000); /* set lower 4 bit of the col adr to 0x00. Changed for DisplayTech 64128N */ - /* end of sequence */ - ST7565_WRITE_BYTE(0x0B0 | (2*pb->p.page));; /* select current page (ST7565R) */ - ST7565_A0(); /* data mode */ - ST7560_WriteSequence( (uint8_t) pb->width, (uint8_t *)pb->buf); - ST7565_CS(); /* disable chip */ - ST7565_NA0(); /* instruction mode */ - ST7565_NCS(); /* enable chip */ - ST7565_WRITE_BYTE(0x010); /* set upper 4 bit of the col adr to 0x10 */ - ST7565_WRITE_BYTE(0x000); /* set lower 4 bit of the col adr to 0x00. Changed for DisplayTech 64128N */ - /* end of sequence */ - ST7565_WRITE_BYTE(0x0B0 | (2*pb->p.page+1)); /* select current page (ST7565R) */ - ST7565_A0(); /* data mode */ - ST7560_WriteSequence( (uint8_t) pb->width, (uint8_t *)(pb->buf)+pb->width); - ST7565_CS(); /* disable chip */ + u8g_pb_t *pb = (u8g_pb_t*)(dev->dev_mem); + ST7565_CS(); // chip select off + ST7565_NA0(); // instruction mode + ST7565_NCS(); // chip select + ST7565_COLUMN_ADR(0x00); // high 4 bits to 0, low 4 bits to 0. Changed for DisplayTech 64128N + // end of sequence + ST7565_PAGE_ADR(2 * pb->p.page); // select current page (ST7565R) + ST7565_A0(); // data mode + ST7560_WriteSequence((uint8_t)pb->width, (uint8_t*)pb->buf); + ST7565_CS(); // chip select off + ST7565_NA0(); // instruction mode + ST7565_NCS(); // chip select + ST7565_COLUMN_ADR(0x00); // high 4 bits to 0, low 4 bits to 0 + // end of sequence + ST7565_PAGE_ADR(2 * pb->p.page + 1); // select current page (ST7565R) + ST7565_A0(); // data mode + ST7560_WriteSequence((uint8_t)pb->width, (uint8_t*)(pb->buf) + pb->width); + ST7565_CS(); // chip select off } break; case U8G_DEV_MSG_CONTRAST: ST7565_NCS(); - ST7565_NA0(); /* instruction mode */ - ST7565_WRITE_BYTE(0x081); - ST7565_WRITE_BYTE((*(uint8_t *)arg) >> 2); - ST7565_CS(); /* disable chip */ + ST7565_NA0(); // instruction mode + ST7565_CONTRAST((*(uint8_t*)arg) >> 2); + ST7565_CS(); // chip select off return 1; case U8G_DEV_MSG_SLEEP_ON: - ST7565_NA0(); /* instruction mode */ - ST7565_NCS(); /* enable chip */ - ST7565_WRITE_BYTE(0x0AC); /* static indicator off */ - ST7565_WRITE_BYTE(0x000); /* indicator register set (not sure if this is required) */ - ST7565_WRITE_BYTE(0x0AE); /* display off */ - ST7565_WRITE_BYTE(0x0A5); /* all points on */ - ST7565_CS(); /* disable chip , bugfix 12 nov 2014 */ - /* end of sequence */ + ST7565_NA0(); // instruction mode + ST7565_NCS(); // chip select + ST7565_SLEEP_MODE(); // static indicator off + //ST7565_WRITE_BYTE(0x00); // indicator register set (not sure if this is required) + ST7565_ON(0); // display off + ST7565_ALL_PIX(1); // all points on + ST7565_CS(); // chip select off return 1; case U8G_DEV_MSG_SLEEP_OFF: - ST7565_NA0(); /* instruction mode */ - ST7565_NCS(); /* enable chip */ - ST7565_WRITE_BYTE(0x0A4); /* all points off */ - ST7565_WRITE_BYTE(0x0AF); /* display on */ - U8G_ESC_DLY(50); /* delay 50 ms */ - ST7565_CS(); /* disable chip , bugfix 12 nov 2014 */ - /* end of sequence */ + ST7565_NA0(); // instruction mode + ST7565_NCS(); // chip select + ST7565_ALL_PIX(0); // all points off + ST7565_ON(1); // display on + U8G_ESC_DLY(50); // delay 50 ms + ST7565_CS(); // chip select off return 1; } return u8g_dev_pb16v1_base_fn(u8g, dev, msg, arg); } -uint8_t u8g_dev_st7565_64128n_2x_VIKI_buf[LCD_PIXEL_WIDTH*2] U8G_NOCOMMON; +uint8_t u8g_dev_st7565_64128n_2x_VIKI_buf[LCD_PIXEL_WIDTH * 2] U8G_NOCOMMON; u8g_pb_t u8g_dev_st7565_64128n_2x_VIKI_pb = {{16, LCD_PIXEL_HEIGHT, 0, 0, 0}, LCD_PIXEL_WIDTH, u8g_dev_st7565_64128n_2x_VIKI_buf}; u8g_dev_t u8g_dev_st7565_64128n_2x_VIKI_sw_spi = {u8g_dev_st7565_64128n_2x_VIKI_fn, &u8g_dev_st7565_64128n_2x_VIKI_pb, &u8g_com_null_fn}; diff --git a/Marlin/ultralcd_st7920_u8glib_rrd.h b/Marlin/ultralcd_st7920_u8glib_rrd.h index 96a1f05f09..db6224e478 100644 --- a/Marlin/ultralcd_st7920_u8glib_rrd.h +++ b/Marlin/ultralcd_st7920_u8glib_rrd.h @@ -61,6 +61,10 @@ #define CPU_ST7920_DELAY_1 DELAY_NS(0) #define CPU_ST7920_DELAY_2 DELAY_NS(0) #define CPU_ST7920_DELAY_3 DELAY_NS(0) +#elif MB(BQ_ZUM_MEGA_3D) + #define CPU_ST7920_DELAY_1 DELAY_NS(0) + #define CPU_ST7920_DELAY_2 DELAY_NS(0) + #define CPU_ST7920_DELAY_3 DELAY_NS(189) #elif F_CPU == 16000000 #define CPU_ST7920_DELAY_1 DELAY_NS(0) #define CPU_ST7920_DELAY_2 DELAY_NS(0) diff --git a/Marlin/utility.h b/Marlin/utility.h index dff2cec105..991d66289a 100644 --- a/Marlin/utility.h +++ b/Marlin/utility.h @@ -73,14 +73,14 @@ void safe_delay(millis_t ms); char* ftostr62rj(const float &x); // Convert float to rj string with 123 or -12 format - FORCE_INLINE char* ftostr3(const float &x) { return itostr3(int(x + (x < 0 ? -0.5 : 0.5))); } + FORCE_INLINE char* ftostr3(const float &x) { return itostr3(int(x + (x < 0 ? -0.5f : 0.5f))); } #if ENABLED(LCD_DECIMAL_SMALL_XY) // Convert float to rj string with 1234, _123, 12.3, _1.2, -123, _-12, or -1.2 format char* ftostr4sign(const float &fx); #else // Convert float to rj string with 1234, _123, -123, __12, _-12, ___1, or __-1 format - FORCE_INLINE char* ftostr4sign(const float &x) { return itostr4sign(int(x + (x < 0 ? -0.5 : 0.5))); } + FORCE_INLINE char* ftostr4sign(const float &x) { return itostr4sign(int(x + (x < 0 ? -0.5f : 0.5f))); } #endif #endif // ULTRA_LCD || (DEBUG_LEVELING_FEATURE && (MESH_BED_LEVELING || (HAS_ABL && !ABL_PLANAR))) diff --git a/Marlin/vector_3.cpp b/Marlin/vector_3.cpp index 4bfcdeb933..e5afd9e831 100644 --- a/Marlin/vector_3.cpp +++ b/Marlin/vector_3.cpp @@ -69,7 +69,7 @@ vector_3 vector_3::get_normal() { float vector_3::get_length() { return SQRT(sq(x) + sq(y) + sq(z)); } void vector_3::normalize() { - const float inv_length = 1.0 / get_length(); + const float inv_length = RSQRT(sq(x) + sq(y) + sq(z)); x *= inv_length; y *= inv_length; z *= inv_length;