diff --git a/Marlin/src/Marlin.cpp b/Marlin/src/Marlin.cpp index e612b0de93..5cf65cd305 100644 --- a/Marlin/src/Marlin.cpp +++ b/Marlin/src/Marlin.cpp @@ -939,6 +939,15 @@ void setup() { queue_setup(); + // UI must be initialized before EEPROM + // (because EEPROM code calls the UI). + ui.init(); + ui.reset_status(); + + #if HAS_SPI_LCD && ENABLED(SHOW_BOOTSCREEN) + ui.show_bootscreen(); + #endif + #if ENABLED(SDIO_SUPPORT) && SD_DETECT_PIN == -1 // Auto-mount the SD for EEPROM.dat emulation if (!card.isDetected()) card.initsd(); @@ -1044,13 +1053,6 @@ void setup() { fanmux_init(); #endif - ui.init(); - ui.reset_status(); - - #if HAS_SPI_LCD && ENABLED(SHOW_BOOTSCREEN) - ui.show_bootscreen(); - #endif - #if ENABLED(MIXING_EXTRUDER) mixer.init(); #endif diff --git a/Marlin/src/feature/backlash.cpp b/Marlin/src/feature/backlash.cpp new file mode 100644 index 0000000000..3254fb3f3c --- /dev/null +++ b/Marlin/src/feature/backlash.cpp @@ -0,0 +1,139 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2019 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 . + * + */ + +#include "../Marlin.h" + +#if ENABLED(BACKLASH_COMPENSATION) + +#include "backlash.h" +#include "../module/planner.h" + +#if ENABLED(BACKLASH_GCODE) + uint8_t Backlash::correction = (BACKLASH_CORRECTION) * 0xFF; + #ifdef BACKLASH_DISTANCE_MM + float Backlash::distance_mm[XYZ] = BACKLASH_DISTANCE_MM; + #endif + #ifdef BACKLASH_SMOOTHING_MM + float Backlash::smoothing_mm = BACKLASH_SMOOTHING_MM; + #endif +#endif + +#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) + float Backlash::measured_mm[XYZ] = { 0 }; + uint8_t Backlash::measured_count[XYZ] = { 0 }; +#endif + +Backlash backlash; + +/** + * To minimize seams in the printed part, backlash correction only adds + * steps to the current segment (instead of creating a new segment, which + * causes discontinuities and print artifacts). + * + * With a non-zero BACKLASH_SMOOTHING_MM value the backlash correction is + * spread over multiple segments, smoothing out artifacts even more. + */ + +void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const uint8_t dm, block_t * const block) { + static uint8_t last_direction_bits; + uint8_t changed_dir = last_direction_bits ^ dm; + // Ignore direction change if no steps are taken in that direction + if (da == 0) CBI(changed_dir, X_AXIS); + if (db == 0) CBI(changed_dir, Y_AXIS); + if (dc == 0) CBI(changed_dir, Z_AXIS); + last_direction_bits ^= changed_dir; + + if (correction == 0) return; + + #ifdef BACKLASH_SMOOTHING_MM + // The segment proportion is a value greater than 0.0 indicating how much residual_error + // is corrected for in this segment. The contribution is based on segment length and the + // smoothing distance. Since the computation of this proportion involves a floating point + // division, defer computation until needed. + float segment_proportion = 0; + + // Residual error carried forward across multiple segments, so correction can be applied + // to segments where there is no direction change. + static int32_t residual_error[XYZ] = { 0 }; + #else + // No leftover residual error from segment to segment + int32_t residual_error[XYZ] = { 0 }; + // No direction change, no correction. + if (!changed_dir) return; + #endif + + const float f_corr = float(correction) / 255.0f; + + LOOP_XYZ(axis) { + if (distance_mm[axis]) { + const bool reversing = TEST(dm,axis); + + // When an axis changes direction, add axis backlash to the residual error + if (TEST(changed_dir, axis)) + residual_error[axis] += (reversing ? -f_corr : f_corr) * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis]; + + // Decide how much of the residual error to correct in this segment + int32_t error_correction = residual_error[axis]; + #ifdef BACKLASH_SMOOTHING_MM + if (error_correction && smoothing_mm != 0) { + // Take up a portion of the residual_error in this segment, but only when + // the current segment travels in the same direction as the correction + if (reversing == (error_correction < 0)) { + if (segment_proportion == 0) + segment_proportion = MIN(1.0f, block->millimeters / smoothing_mm); + error_correction = ceil(segment_proportion * error_correction); + } + else + error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps + } + #endif + // Making a correction reduces the residual error and modifies delta_mm + if (error_correction) { + block->steps[axis] += ABS(error_correction); + residual_error[axis] -= error_correction; + } + } + } +} + +#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) + #if USES_Z_MIN_PROBE_ENDSTOP + #define TEST_PROBE_PIN (READ(Z_MIN_PROBE_PIN) != Z_MIN_PROBE_ENDSTOP_INVERTING) + #else + #define TEST_PROBE_PIN (READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING) + #endif + + // Measure Z backlash by raising nozzle in increments until probe deactivates + void Backlash::measure_with_probe() { + if (measured_count[Z_AXIS] == 255) return; + + float start_height = current_position[Z_AXIS]; + while (current_position[Z_AXIS] < (start_height + BACKLASH_MEASUREMENT_LIMIT) && TEST_PROBE_PIN) + do_blocking_move_to_z(current_position[Z_AXIS] + BACKLASH_MEASUREMENT_RESOLUTION, MMM_TO_MMS(BACKLASH_MEASUREMENT_FEEDRATE)); + + // The backlash from all probe points is averaged, so count the number of measurements + measured_mm[Z_AXIS] += current_position[Z_AXIS] - start_height; + measured_count[Z_AXIS]++; + } +#endif + +#endif // BACKLASH_COMPENSATION diff --git a/Marlin/src/feature/backlash.h b/Marlin/src/feature/backlash.h new file mode 100644 index 0000000000..c35675f1b1 --- /dev/null +++ b/Marlin/src/feature/backlash.h @@ -0,0 +1,88 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2019 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 . + * + */ +#pragma once + +#include "../inc/MarlinConfigPre.h" +#include "../module/planner.h" + +class Backlash { +public: + #if ENABLED(BACKLASH_GCODE) + static uint8_t correction; + #ifdef BACKLASH_DISTANCE_MM + static float distance_mm[XYZ]; + #endif + #ifdef BACKLASH_SMOOTHING_MM + static float smoothing_mm; + #endif + static inline void set_correction(const float &v) { correction = MAX(0, MIN(1.0, v)) * all_on; } + static inline float get_correction() { return float(ui8_to_percent(correction)) / 100.0f; } + #elif ENABLED(BACKLASH_COMPENSATION) + static constexpr uint8_t correction = (BACKLASH_CORRECTION) * 0xFF; + #ifdef BACKLASH_DISTANCE_MM + static constexpr float distance_mm[XYZ] = BACKLASH_DISTANCE_MM; + #endif + #ifdef BACKLASH_SMOOTHING_MM + static constexpr float smoothing_mm = BACKLASH_SMOOTHING_MM; + #endif + static inline void set_correction(float) { } + static inline float get_correction() { return float(ui8_to_percent(correction)) / 100.0f; } + #else + static constexpr uint8_t correction = 0; + static inline void set_correction(float) { } + static inline float get_correction() { return 0; } + #endif + + #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) + private: + static float measured_mm[XYZ]; + static uint8_t measured_count[XYZ]; + public: + static void measure_with_probe(); + #endif + + static inline float get_measurement(const uint8_t e) { + // Return the measurement averaged over all readings + return ( + #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) + measured_count[e] > 0 ? measured_mm[e] / measured_count[e] : + #endif + 0 + ); + } + + static inline bool has_measurement(const uint8_t e) { + return (false + #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) + || (measured_count[e] > 0) + #endif + ); + } + + static inline bool has_any_measurement() { + return has_measurement(X_AXIS) || has_measurement(Y_AXIS) || has_measurement(Z_AXIS); + } + + void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const uint8_t dm, block_t * const block); +}; + +extern Backlash backlash; diff --git a/Marlin/src/feature/runout.cpp b/Marlin/src/feature/runout.cpp index 9395dec5b4..e319a7cc2f 100644 --- a/Marlin/src/feature/runout.cpp +++ b/Marlin/src/feature/runout.cpp @@ -51,7 +51,7 @@ void FilamentSensorBase::filament_present(const uint8_t extruder) { uint8_t FilamentSensorEncoder::motion_detected; #endif -#if FILAMENT_RUNOUT_DISTANCE_MM > 0 +#ifdef FILAMENT_RUNOUT_DISTANCE_MM float RunoutResponseDelayed::runout_distance_mm = FILAMENT_RUNOUT_DISTANCE_MM; volatile float RunoutResponseDelayed::runout_mm_countdown[EXTRUDERS]; #else diff --git a/Marlin/src/feature/runout.h b/Marlin/src/feature/runout.h index 3f1e5fa018..eccc7ab931 100644 --- a/Marlin/src/feature/runout.h +++ b/Marlin/src/feature/runout.h @@ -78,6 +78,11 @@ class TFilamentMonitor : public FilamentMonitorBase { response.filament_present(extruder); } + #ifdef FILAMENT_RUNOUT_DISTANCE_MM + static inline float& runout_distance() { return response.runout_distance_mm; } + static inline void set_runout_distance(const float &mm) { response.runout_distance_mm = mm; } + #endif + // Handle a block completion. RunoutResponseDelayed uses this to // add up the length of filament moved while the filament is out. static inline void block_completed(const block_t* const b) { @@ -90,13 +95,13 @@ class TFilamentMonitor : public FilamentMonitorBase { // Give the response a chance to update its counter. static inline void run() { if (enabled && !filament_ran_out && (IS_SD_PRINTING() || print_job_timer.isRunning() || did_pause_print)) { - #if FILAMENT_RUNOUT_DISTANCE_MM > 0 + #ifdef FILAMENT_RUNOUT_DISTANCE_MM cli(); // Prevent RunoutResponseDelayed::block_completed from accumulating here #endif response.run(); sensor.run(); const bool ran_out = response.has_run_out(); - #if FILAMENT_RUNOUT_DISTANCE_MM > 0 + #ifdef FILAMENT_RUNOUT_DISTANCE_MM sei(); #endif if (ran_out) { @@ -272,7 +277,7 @@ class FilamentSensorBase { /********************************* RESPONSE TYPE *********************************/ -#if FILAMENT_RUNOUT_DISTANCE_MM > 0 +#ifdef FILAMENT_RUNOUT_DISTANCE_MM // RunoutResponseDelayed triggers a runout event only if the length // of filament specified by FILAMENT_RUNOUT_DISTANCE_MM has been fed @@ -347,11 +352,12 @@ class FilamentSensorBase { /********************************* TEMPLATE SPECIALIZATION *********************************/ typedef TFilamentMonitor< - #if FILAMENT_RUNOUT_DISTANCE_MM > 0 + #ifdef FILAMENT_RUNOUT_DISTANCE_MM + RunoutResponseDelayed, #if ENABLED(FILAMENT_MOTION_SENSOR) - RunoutResponseDelayed, FilamentSensorEncoder + FilamentSensorEncoder #else - RunoutResponseDelayed, FilamentSensorSwitch + FilamentSensorSwitch #endif #else RunoutResponseDebounced, FilamentSensorSwitch diff --git a/Marlin/src/gcode/calibrate/G425.cpp b/Marlin/src/gcode/calibrate/G425.cpp index 1174fc38f8..03e4c4eb43 100644 --- a/Marlin/src/gcode/calibrate/G425.cpp +++ b/Marlin/src/gcode/calibrate/G425.cpp @@ -31,6 +31,7 @@ #include "../../module/tool_change.h" #include "../../module/endstops.h" #include "../../feature/bedlevel/bedlevel.h" +#include "../../feature/backlash.h" /** @@ -55,11 +56,6 @@ #define HAS_X_CENTER BOTH(CALIBRATION_MEASURE_LEFT, CALIBRATION_MEASURE_RIGHT) #define HAS_Y_CENTER BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK) -#if ENABLED(BACKLASH_GCODE) - extern float backlash_distance_mm[], backlash_smoothing_mm; - extern uint8_t backlash_correction; -#endif - enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES }; struct measurements_t { @@ -79,13 +75,13 @@ struct measurements_t { #define TEMPORARY_SOFT_ENDSTOP_STATE(enable) REMEMBER(tes, soft_endstops_enabled, enable); #if ENABLED(BACKLASH_GCODE) - #define TEMPORARY_BACKLASH_CORRECTION(value) REMEMBER(tbst, backlash_correction, value) + #define TEMPORARY_BACKLASH_CORRECTION(value) REMEMBER(tbst, backlash.correction, value) #else #define TEMPORARY_BACKLASH_CORRECTION(value) #endif #if ENABLED(BACKLASH_GCODE) && defined(BACKLASH_SMOOTHING_MM) - #define TEMPORARY_BACKLASH_SMOOTHING(value) REMEMBER(tbsm, backlash_smoothing_mm, value) + #define TEMPORARY_BACKLASH_SMOOTHING(value) REMEMBER(tbsm, backlash.smoothing_mm, value) #else #define TEMPORARY_BACKLASH_SMOOTHING(value) #endif @@ -454,22 +450,22 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) { #if ENABLED(BACKLASH_GCODE) #if HAS_X_CENTER - backlash_distance_mm[X_AXIS] = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2; + backlash.distance_mm[X_AXIS] = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2; #elif ENABLED(CALIBRATION_MEASURE_LEFT) - backlash_distance_mm[X_AXIS] = m.backlash[LEFT]; + backlash.distance_mm[X_AXIS] = m.backlash[LEFT]; #elif ENABLED(CALIBRATION_MEASURE_RIGHT) - backlash_distance_mm[X_AXIS] = m.backlash[RIGHT]; + backlash.distance_mm[X_AXIS] = m.backlash[RIGHT]; #endif #if HAS_Y_CENTER - backlash_distance_mm[Y_AXIS] = (m.backlash[FRONT] + m.backlash[BACK]) / 2; + backlash.distance_mm[Y_AXIS] = (m.backlash[FRONT] + m.backlash[BACK]) / 2; #elif ENABLED(CALIBRATION_MEASURE_FRONT) - backlash_distance_mm[Y_AXIS] = m.backlash[FRONT]; + backlash.distance_mm[Y_AXIS] = m.backlash[FRONT]; #elif ENABLED(CALIBRATION_MEASURE_BACK) - backlash_distance_mm[Y_AXIS] = m.backlash[BACK]; + backlash.distance_mm[Y_AXIS] = m.backlash[BACK]; #endif - backlash_distance_mm[Z_AXIS] = m.backlash[TOP]; + backlash.distance_mm[Z_AXIS] = m.backlash[TOP]; #endif } diff --git a/Marlin/src/gcode/calibrate/M425.cpp b/Marlin/src/gcode/calibrate/M425.cpp index e51c9301cf..74b38f56cc 100644 --- a/Marlin/src/gcode/calibrate/M425.cpp +++ b/Marlin/src/gcode/calibrate/M425.cpp @@ -24,20 +24,9 @@ #if ENABLED(BACKLASH_GCODE) +#include "../../feature/backlash.h" #include "../../module/planner.h" -float backlash_distance_mm[XYZ] = BACKLASH_DISTANCE_MM; -uint8_t backlash_correction = BACKLASH_CORRECTION * all_on; - -#ifdef BACKLASH_SMOOTHING_MM - float backlash_smoothing_mm = BACKLASH_SMOOTHING_MM; -#endif - -#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) - float backlash_measured_mm[XYZ] = { 0 }; - uint8_t backlash_measured_num[XYZ] = { 0 }; -#endif - #include "../gcode.h" /** @@ -60,59 +49,52 @@ void GcodeSuite::M425() { LOOP_XYZ(i) { if (parser.seen(axis_codes[i])) { planner.synchronize(); - const float measured_backlash = ( - #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) - backlash_measured_num[i] > 0 ? backlash_measured_mm[i] / backlash_measured_num[i] : 0 - #else - 0 - #endif - ); - backlash_distance_mm[i] = parser.has_value() ? parser.value_linear_units() : measured_backlash; + backlash.distance_mm[i] = parser.has_value() ? parser.value_linear_units() : backlash.get_measurement(i); noArgs = false; } } if (parser.seen('F')) { planner.synchronize(); - backlash_correction = MAX(0, MIN(1.0, parser.value_float())) * all_on; + backlash.set_correction(parser.value_float()); noArgs = false; } #ifdef BACKLASH_SMOOTHING_MM if (parser.seen('S')) { planner.synchronize(); - backlash_smoothing_mm = parser.value_linear_units(); + backlash.smoothing_mm = parser.value_linear_units(); noArgs = false; } #endif if (noArgs) { - SERIAL_ECHOPGM("Backlash correction is "); - if (!backlash_correction) SERIAL_ECHOPGM("in"); + SERIAL_ECHOPGM("Backlash Correction "); + if (!backlash.correction) SERIAL_ECHOPGM("in"); SERIAL_ECHOLNPGM("active:"); - SERIAL_ECHOLNPAIR(" Correction Amount/Fade-out: F", float(ui8_to_percent(backlash_correction)) / 100, " (F1.0 = full, F0.0 = none)"); + SERIAL_ECHOLNPAIR(" Correction Amount/Fade-out: F", backlash.get_correction(), " (F1.0 = full, F0.0 = none)"); SERIAL_ECHOPGM(" Backlash Distance (mm): "); LOOP_XYZ(a) { SERIAL_CHAR(' '); SERIAL_CHAR(axis_codes[a]); - SERIAL_ECHO(backlash_distance_mm[a]); + SERIAL_ECHO(backlash.distance_mm[a]); SERIAL_EOL(); } #ifdef BACKLASH_SMOOTHING_MM - SERIAL_ECHOLNPAIR(" Smoothing (mm): S", backlash_smoothing_mm); + SERIAL_ECHOLNPAIR(" Smoothing (mm): S", backlash.smoothing_mm); #endif #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) SERIAL_ECHOPGM(" Average measured backlash (mm):"); - LOOP_XYZ(a) { - if (backlash_measured_num[a] > 0) { + if (backlash.has_any_measurement()) { + LOOP_XYZ(a) if (backlash.has_measurement(a)) { SERIAL_CHAR(' '); SERIAL_CHAR(axis_codes[a]); - SERIAL_ECHO(backlash_measured_mm[a] / backlash_measured_num[a]); + SERIAL_ECHO(backlash.get_measurement(a)); } } - if (!backlash_measured_num[X_AXIS] && !backlash_measured_num[Y_AXIS] && !backlash_measured_num[Z_AXIS]) + else SERIAL_ECHOPGM(" (Not yet measured)"); SERIAL_EOL(); #endif diff --git a/Marlin/src/gcode/feature/runout/M412.cpp b/Marlin/src/gcode/feature/runout/M412.cpp index 749ce2a3b5..6cf2238dbb 100644 --- a/Marlin/src/gcode/feature/runout/M412.cpp +++ b/Marlin/src/gcode/feature/runout/M412.cpp @@ -32,6 +32,9 @@ */ void GcodeSuite::M412() { if (parser.seen("HS" + #ifdef FILAMENT_RUNOUT_DISTANCE_MM + "D" + #endif #if ENABLED(HOST_ACTION_COMMANDS) "R" #endif @@ -42,11 +45,17 @@ void GcodeSuite::M412() { const bool seenR = parser.seen('R'), seenS = parser.seen('S'); if (seenR || seenS) runout.reset(); if (seenS) runout.enabled = parser.value_bool(); + #ifdef FILAMENT_RUNOUT_DISTANCE_MM + if (parser.seen('D')) runout.set_runout_distance(parser.value_linear_units()); + #endif } else { SERIAL_ECHO_START(); SERIAL_ECHOPGM("Filament runout "); serialprintln_onoff(runout.enabled); + #ifdef FILAMENT_RUNOUT_DISTANCE_MM + SERIAL_ECHOLNPAIR("Filament runout distance (mm): ", runout.runout_distance()); + #endif } } diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h index 45051e3a82..764ccb5abc 100644 --- a/Marlin/src/inc/Conditionals_LCD.h +++ b/Marlin/src/inc/Conditionals_LCD.h @@ -516,6 +516,9 @@ #define GRID_MAX_POINTS ((GRID_MAX_POINTS_X) * (GRID_MAX_POINTS_Y)) #endif +#if ENABLED(MALYAN_LCD) + #define EXTENSIBLE_UI +#endif #define HAS_SOFTWARE_ENDSTOPS EITHER(MIN_SOFTWARE_ENDSTOPS, MAX_SOFTWARE_ENDSTOPS) #define HAS_RESUME_CONTINUE ANY(EXTENSIBLE_UI, NEWPANEL, EMERGENCY_PARSER) #define HAS_COLOR_LEDS ANY(BLINKM, RGB_LED, RGBW_LED, PCA9632, PCA9533, NEOPIXEL_LED) diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index a268595c25..3f80764ac4 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -613,6 +613,8 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #error "FILAMENT_RUNOUT_SENSOR with NUM_RUNOUT_SENSORS > 5 requires FIL_RUNOUT6_PIN." #elif DISABLED(SDSUPPORT, PRINTJOB_TIMER_AUTOSTART) #error "FILAMENT_RUNOUT_SENSOR requires SDSUPPORT or PRINTJOB_TIMER_AUTOSTART." + #elif FILAMENT_RUNOUT_DISTANCE_MM < 0 + #error "FILAMENT_RUNOUT_DISTANCE_MM must be greater than or equal to zero." #elif DISABLED(ADVANCED_PAUSE_FEATURE) static_assert(NULL == strstr(FILAMENT_RUNOUT_SCRIPT, "M600"), "ADVANCED_PAUSE_FEATURE is required to use M600 with FILAMENT_RUNOUT_SENSOR."); #endif @@ -1784,7 +1786,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS + ENABLED(OLED_PANEL_TINYBOY2) \ + ENABLED(ZONESTAR_LCD) \ + ENABLED(ULTI_CONTROLLER) \ - + ENABLED(EXTENSIBLE_UI) + + (ENABLED(EXTENSIBLE_UI) && DISABLED(MALYAN_LCD)) #error "Please select no more than one LCD controller option." #endif diff --git a/Marlin/src/lcd/extensible_ui/lib/example.cpp b/Marlin/src/lcd/extensible_ui/lib/example.cpp index 2a11d5c3c4..5a52e26787 100644 --- a/Marlin/src/lcd/extensible_ui/lib/example.cpp +++ b/Marlin/src/lcd/extensible_ui/lib/example.cpp @@ -1,6 +1,6 @@ -/************* - * dummy.cpp * - *************/ +/*************** + * example.cpp * + ***************/ /**************************************************************************** * Written By Marcio Teixeira 2018 - Aleph Objects, Inc. * @@ -21,7 +21,7 @@ #include "../../../inc/MarlinConfigPre.h" -#if ENABLED(EXTENSIBLE_UI) +#if BOTH(EXTUI_EXAMPLE, EXTENSIBLE_UI) #include "../ui_api.h" @@ -58,8 +58,36 @@ namespace ExtUI { void onUserConfirmRequired(const char * const msg) {} void onStatusChanged(const char * const msg) {} void onFactoryReset() {} - void onLoadSettings() {} - void onStoreSettings() {} + + void onStoreSettings(char *buff) { + // This is called when saving to EEPROM (i.e. M500). If the ExtUI needs + // permanent data to be stored, it can write up to eeprom_data_size bytes + // into buff. + + // Example: + // static_assert(sizeof(myDataStruct) <= ExtUI::eeprom_data_size); + // memcpy(buff, &myDataStruct, sizeof(myDataStruct)); + } + + void onLoadSettings(const char *buff) { + // This is called while loading settings from EEPROM. If the ExtUI + // needs to retrieve data, it should copy up to eeprom_data_size bytes + // from buff + + // Example: + // static_assert(sizeof(myDataStruct) <= ExtUI::eeprom_data_size); + // memcpy(&myDataStruct, buff, sizeof(myDataStruct)); + } + + void onConfigurationStoreWritten(bool success) { + // This is called after the entire EEPROM has been written, + // whether successful or not. + } + + void onConfigurationStoreRead(bool success) { + // This is called after the entire EEPROM has been read, + // whether successful or not. + } } -#endif // EXTENSIBLE_UI +#endif // EXTUI_EXAMPLE && EXTENSIBLE_UI diff --git a/Marlin/src/lcd/extensible_ui/ui_api.cpp b/Marlin/src/lcd/extensible_ui/ui_api.cpp index 8e531ec2a9..a140896bfa 100644 --- a/Marlin/src/lcd/extensible_ui/ui_api.cpp +++ b/Marlin/src/lcd/extensible_ui/ui_api.cpp @@ -82,11 +82,7 @@ #include "ui_api.h" #if ENABLED(BACKLASH_GCODE) - extern float backlash_distance_mm[XYZ]; - extern uint8_t backlash_correction; - #ifdef BACKLASH_SMOOTHING_MM - extern float backlash_smoothing_mm; - #endif + #include "../../feature/backlash.h" #endif #if HAS_LEVELING @@ -111,7 +107,6 @@ static struct { } flags; namespace ExtUI { - #ifdef __SAM3X8E__ /** * Implement a special millis() to allow time measurement @@ -517,13 +512,13 @@ namespace ExtUI { bool getFilamentRunoutEnabled() { return runout.enabled; } void setFilamentRunoutEnabled(const bool value) { runout.enabled = value; } - #if FILAMENT_RUNOUT_DISTANCE_MM > 0 + #ifdef FILAMENT_RUNOUT_DISTANCE_MM float getFilamentRunoutDistance_mm() { - return RunoutResponseDelayed::runout_distance_mm; + return runout.runout_distance(); } void setFilamentRunoutDistance_mm(const float value) { - RunoutResponseDelayed::runout_distance_mm = clamp(value, 0, 999); + runout.set_runout_distance(clamp(value, 0, 999)); } #endif #endif @@ -687,16 +682,16 @@ namespace ExtUI { #endif // HAS_HOTEND_OFFSET #if ENABLED(BACKLASH_GCODE) - float getAxisBacklash_mm(const axis_t axis) { return backlash_distance_mm[axis]; } + float getAxisBacklash_mm(const axis_t axis) { return backlash.distance_mm[axis]; } void setAxisBacklash_mm(const float value, const axis_t axis) - { backlash_distance_mm[axis] = clamp(value,0,5); } + { backlash.distance_mm[axis] = clamp(value,0,5); } - float getBacklashCorrection_percent() { return ui8_to_percent(backlash_correction); } - void setBacklashCorrection_percent(const float value) { backlash_correction = map(clamp(value, 0, 100), 0, 100, 0, 255); } + float getBacklashCorrection_percent() { return ui8_to_percent(backlash.correction); } + void setBacklashCorrection_percent(const float value) { backlash.correction = map(clamp(value, 0, 100), 0, 100, 0, 255); } #ifdef BACKLASH_SMOOTHING_MM - float getBacklashSmoothing_mm() { return backlash_smoothing_mm; } - void setBacklashSmoothing_mm(const float value) { backlash_smoothing_mm = clamp(value, 0, 999); } + float getBacklashSmoothing_mm() { return backlash.smoothing_mm; } + void setBacklashSmoothing_mm(const float value) { backlash.smoothing_mm = clamp(value, 0, 999); } #endif #endif @@ -750,7 +745,7 @@ namespace ExtUI { } bool commandsInQueue() { return (planner.movesplanned() || commands_in_queue); } - + bool isAxisPositionKnown(const axis_t axis) { return TEST(axis_known_position, axis); } diff --git a/Marlin/src/lcd/extensible_ui/ui_api.h b/Marlin/src/lcd/extensible_ui/ui_api.h index 2f2f99dc11..a643ffd70d 100644 --- a/Marlin/src/lcd/extensible_ui/ui_api.h +++ b/Marlin/src/lcd/extensible_ui/ui_api.h @@ -46,6 +46,11 @@ #include "../../inc/MarlinConfig.h" namespace ExtUI { + // The ExtUI implementation can store up to this many bytes + // in the EEPROM when the methods onStoreSettings and + // onLoadSettings are called. + + static constexpr size_t eeprom_data_size = 48; enum axis_t : uint8_t { X, Y, Z }; enum extruder_t : uint8_t { E0, E1, E2, E3, E4, E5 }; @@ -207,7 +212,7 @@ namespace ExtUI { bool getFilamentRunoutEnabled(); void setFilamentRunoutEnabled(const bool); - #if FILAMENT_RUNOUT_DISTANCE_MM > 0 + #ifdef FILAMENT_RUNOUT_DISTANCE_MM float getFilamentRunoutDistance_mm(); void setFilamentRunoutDistance_mm(const float); #endif @@ -283,8 +288,10 @@ namespace ExtUI { void onUserConfirmRequired(const char * const msg); void onStatusChanged(const char * const msg); void onFactoryReset(); - void onStoreSettings(); - void onLoadSettings(); + void onStoreSettings(char *); + void onLoadSettings(const char *); + void onConfigurationStoreWritten(bool success); + void onConfigurationStoreRead(bool success); }; /** diff --git a/Marlin/src/lcd/malyanlcd.cpp b/Marlin/src/lcd/malyanlcd.cpp index cb0e2a00cc..8a6d611bd9 100644 --- a/Marlin/src/lcd/malyanlcd.cpp +++ b/Marlin/src/lcd/malyanlcd.cpp @@ -41,23 +41,19 @@ * Copyright (c) 2017 Jason Nelson (xC0000005) */ -#include "../inc/MarlinConfig.h" +#include "../inc/MarlinConfigPre.h" #if ENABLED(MALYAN_LCD) +#include "extensible_ui/ui_api.h" + #include "ultralcd.h" #include "../module/temperature.h" -#include "../module/planner.h" #include "../module/stepper.h" #include "../module/motion.h" -#include "../module/probe.h" #include "../libs/duration_t.h" #include "../module/printcounter.h" -#include "../gcode/gcode.h" #include "../gcode/queue.h" -#include "../module/configuration_store.h" - -#include "../Marlin.h" #if ENABLED(SDSUPPORT) #include "../sd/cardreader.h" @@ -412,78 +408,118 @@ void update_usb_status(const bool forceUpdate) { } } -/** - * - from printer on startup: - * {SYS:STARTED}{VER:29}{SYS:STARTED}{R:UD} - * The optimize attribute fixes a register Compile - * error for amtel. - */ -void MarlinUI::update() { - static char inbound_buffer[MAX_CURLY_COMMAND]; +namespace ExtUI { + void onStartup() { + /** + * The Malyan LCD actually runs as a separate MCU on Serial 1. + * This code's job is to siphon the weird curly-brace commands from + * it and translate into gcode, which then gets injected into + * the command queue where possible. + */ + inbound_count = 0; + LCD_SERIAL.begin(500000); - // First report USB status. - update_usb_status(false); + // Signal init + write_to_lcd_P(PSTR("{SYS:STARTED}\r\n")); - // now drain commands... - while (LCD_SERIAL.available()) { - const byte b = (byte)LCD_SERIAL.read() & 0x7F; - inbound_buffer[inbound_count++] = b; - if (b == '}' || inbound_count == sizeof(inbound_buffer) - 1) { - inbound_buffer[inbound_count - 1] = '\0'; - process_lcd_command(inbound_buffer); - inbound_count = 0; - inbound_buffer[0] = 0; - } + // send a version that says "unsupported" + write_to_lcd_P(PSTR("{VER:99}\r\n")); + + // No idea why it does this twice. + write_to_lcd_P(PSTR("{SYS:STARTED}\r\n")); + update_usb_status(true); } - #if ENABLED(SDSUPPORT) - // The way last printing status works is simple: - // The UI needs to see at least one TQ which is not 100% - // and then when the print is complete, one which is. - static uint8_t last_percent_done = 100; + void onIdle() { + /** + * - from printer on startup: + * {SYS:STARTED}{VER:29}{SYS:STARTED}{R:UD} + * The optimize attribute fixes a register Compile + * error for amtel. + */ + static char inbound_buffer[MAX_CURLY_COMMAND]; - // If there was a print in progress, we need to emit the final - // print status as {TQ:100}. Reset last percent done so a new print will - // issue a percent of 0. - const uint8_t percent_done = IS_SD_PRINTING() ? card.percentDone() : last_printing_status ? 100 : 0; - if (percent_done != last_percent_done) { - char message_buffer[10]; - sprintf_P(message_buffer, PSTR("{TQ:%03i}"), percent_done); - write_to_lcd(message_buffer); - last_percent_done = percent_done; - last_printing_status = IS_SD_PRINTING(); + // First report USB status. + update_usb_status(false); + + // now drain commands... + while (LCD_SERIAL.available()) { + const byte b = (byte)LCD_SERIAL.read() & 0x7F; + inbound_buffer[inbound_count++] = b; + if (b == '}' || inbound_count == sizeof(inbound_buffer) - 1) { + inbound_buffer[inbound_count - 1] = '\0'; + process_lcd_command(inbound_buffer); + inbound_count = 0; + inbound_buffer[0] = 0; + } } - #endif -} -/** - * The Malyan LCD actually runs as a separate MCU on Serial 1. - * This code's job is to siphon the weird curly-brace commands from - * it and translate into gcode, which then gets injected into - * the command queue where possible. - */ -void MarlinUI::init() { - inbound_count = 0; - LCD_SERIAL.begin(500000); + #if ENABLED(SDSUPPORT) + // The way last printing status works is simple: + // The UI needs to see at least one TQ which is not 100% + // and then when the print is complete, one which is. + static uint8_t last_percent_done = 100; - // Signal init - write_to_lcd_P(PSTR("{SYS:STARTED}\r\n")); + // If there was a print in progress, we need to emit the final + // print status as {TQ:100}. Reset last percent done so a new print will + // issue a percent of 0. + const uint8_t percent_done = IS_SD_PRINTING() ? card.percentDone() : last_printing_status ? 100 : 0; + if (percent_done != last_percent_done) { + char message_buffer[10]; + sprintf_P(message_buffer, PSTR("{TQ:%03i}"), percent_done); + write_to_lcd(message_buffer); + last_percent_done = percent_done; + last_printing_status = IS_SD_PRINTING(); + } + #endif + } - // send a version that says "unsupported" - write_to_lcd_P(PSTR("{VER:99}\r\n")); + void onPrinterKilled(PGM_P const msg) {} + void onMediaInserted() {}; + void onMediaError() {}; + void onMediaRemoved() {}; + void onPlayTone(const uint16_t frequency, const uint16_t duration) {} + void onPrintTimerStarted() {} + void onPrintTimerPaused() {} + void onPrintTimerStopped() {} + void onFilamentRunout() {} + void onUserConfirmRequired(const char * const msg) {} + void onStatusChanged(const char * const msg) { + write_to_lcd_P(PSTR("{E:")); + write_to_lcd(msg); + write_to_lcd_P("}"); + } + void onFactoryReset() {} - // No idea why it does this twice. - write_to_lcd_P(PSTR("{SYS:STARTED}\r\n")); - update_usb_status(true); -} + void onStoreSettings(char *buff) { + // This is called when saving to EEPROM (i.e. M500). If the ExtUI needs + // permanent data to be stored, it can write up to eeprom_data_size bytes + // into buff. -/** - * Set an alert. - */ -void MarlinUI::set_alert_status_P(PGM_P const message) { - write_to_lcd_P(PSTR("{E:")); - write_to_lcd_P(message); - write_to_lcd_P("}"); + // Example: + // static_assert(sizeof(myDataStruct) <= ExtUI::eeprom_data_size); + // memcpy(buff, &myDataStruct, sizeof(myDataStruct)); + } + + void onLoadSettings(const char *buff) { + // This is called while loading settings from EEPROM. If the ExtUI + // needs to retrieve data, it should copy up to eeprom_data_size bytes + // from buff + + // Example: + // static_assert(sizeof(myDataStruct) <= ExtUI::eeprom_data_size); + // memcpy(&myDataStruct, buff, sizeof(myDataStruct)); + } + + void onConfigurationStoreWritten(bool success) { + // This is called after the entire EEPROM has been written, + // whether successful or not. + } + + void onConfigurationStoreRead(bool success) { + // This is called after the entire EEPROM has been read, + // whether successful or not. + } } #endif // MALYAN_LCD diff --git a/Marlin/src/lcd/menu/menu_backlash.cpp b/Marlin/src/lcd/menu/menu_backlash.cpp index 1b183e1063..9c512303b7 100644 --- a/Marlin/src/lcd/menu/menu_backlash.cpp +++ b/Marlin/src/lcd/menu/menu_backlash.cpp @@ -30,26 +30,21 @@ #include "menu.h" -extern float backlash_distance_mm[XYZ]; -extern uint8_t backlash_correction; - -#ifdef BACKLASH_SMOOTHING_MM - extern float backlash_smoothing_mm; -#endif +#include "../../feature/backlash.h" void menu_backlash() { START_MENU(); MENU_BACK(MSG_MAIN); - MENU_MULTIPLIER_ITEM_EDIT(percent, MSG_BACKLASH_CORRECTION, &backlash_correction, all_off, all_on); + MENU_MULTIPLIER_ITEM_EDIT(percent, MSG_BACKLASH_CORRECTION, &backlash.correction, all_off, all_on); - #define EDIT_BACKLASH_DISTANCE(N) MENU_MULTIPLIER_ITEM_EDIT(float43, MSG_##N, &backlash_distance_mm[_AXIS(N)], 0.0f, 9.9f); + #define EDIT_BACKLASH_DISTANCE(N) MENU_MULTIPLIER_ITEM_EDIT(float43, MSG_##N, &backlash.distance_mm[_AXIS(N)], 0.0f, 9.9f); EDIT_BACKLASH_DISTANCE(A); EDIT_BACKLASH_DISTANCE(B); EDIT_BACKLASH_DISTANCE(C); #ifdef BACKLASH_SMOOTHING_MM - MENU_MULTIPLIER_ITEM_EDIT(float43, MSG_BACKLASH_SMOOTHING, &backlash_smoothing_mm, 0.0f, 9.9f); + MENU_MULTIPLIER_ITEM_EDIT(float43, MSG_BACKLASH_SMOOTHING, &backlash.smoothing_mm, 0.0f, 9.9f); #endif END_MENU(); diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp index 3fd483a79f..2208b6b1bf 100644 --- a/Marlin/src/lcd/ultralcd.cpp +++ b/Marlin/src/lcd/ultralcd.cpp @@ -23,7 +23,7 @@ #include "../inc/MarlinConfigPre.h" // These displays all share the MarlinUI class -#if HAS_SPI_LCD || EITHER(MALYAN_LCD, EXTENSIBLE_UI) +#if HAS_SPI_LCD || ENABLED(EXTENSIBLE_UI) #include "ultralcd.h" #include "fontutils.h" MarlinUI ui; diff --git a/Marlin/src/module/configuration_store.cpp b/Marlin/src/module/configuration_store.cpp index ceab713813..63d95e6b59 100644 --- a/Marlin/src/module/configuration_store.cpp +++ b/Marlin/src/module/configuration_store.cpp @@ -37,7 +37,7 @@ */ // Change EEPROM version if the structure changes -#define EEPROM_VERSION "V65" +#define EEPROM_VERSION "V66" #define EEPROM_OFFSET 100 // Check the integrity of data offsets. @@ -90,10 +90,16 @@ #include "../feature/pause.h" +#if ENABLED(BACKLASH_COMPENSATION) + #include "../feature/backlash.h" +#endif + #if HAS_FILAMENT_SENSOR #include "../feature/runout.h" #endif +#include "../lcd/extensible_ui/ui_api.h" + #if ENABLED(EXTRA_LIN_ADVANCE_K) extern float saved_extruder_advance_K[EXTRUDERS]; #endif @@ -149,6 +155,7 @@ typedef struct SettingsDataStruct { // FILAMENT_RUNOUT_SENSOR // bool runout_sensor_enabled; // M412 S + float runout_distance_mm; // M412 D // // ENABLE_LEVELING_FADE_HEIGHT @@ -298,6 +305,21 @@ typedef struct SettingsDataStruct { toolchange_settings_t toolchange_settings; // M217 S P R #endif + // + // BACKLASH_COMPENSATION + // + float backlash_distance_mm[XYZ]; // M425 X Y Z + uint8_t backlash_correction; // M425 F + float backlash_smoothing_mm; // M425 S + + // + // EXTENSIBLE_UI + // + #if ENABLED(EXTENSIBLE_UI) + // This is a significant hardware change; don't reserve space when not present + uint8_t extui_data[ExtUI::eeprom_data_size]; + #endif + } SettingsData; //static_assert(sizeof(SettingsData) <= E2END + 1, "EEPROM too small to contain SettingsData!"); @@ -372,6 +394,16 @@ void MarlinSettings::postprocess() { report_current_position(); } +#if ENABLED(PRINTCOUNTER) && ENABLED(EEPROM_SETTINGS) + #include "printcounter.h" + + static_assert( + !WITHIN(STATS_EEPROM_ADDRESS, EEPROM_OFFSET, EEPROM_OFFSET + sizeof(SettingsData)) && + !WITHIN(STATS_EEPROM_ADDRESS + sizeof(printStatistics), EEPROM_OFFSET, EEPROM_OFFSET + sizeof(SettingsData)), + "STATS_EEPROM_ADDRESS collides with EEPROM settings storage." + ); +#endif + #if ENABLED(SD_FIRMWARE_UPDATE) #if ENABLED(EEPROM_SETTINGS) @@ -528,11 +560,18 @@ void MarlinSettings::postprocess() { // { #if HAS_FILAMENT_SENSOR - EEPROM_WRITE(runout.enabled); + const bool &runout_sensor_enabled = runout.enabled; #else - const bool runout_sensor_enabled = true; - EEPROM_WRITE(runout_sensor_enabled); + const bool runout_sensor_enabled = false; #endif + #if HAS_FILAMENT_SENSOR && defined(FILAMENT_RUNOUT_DISTANCE_MM) + const float &runout_distance_mm = runout.runout_distance(); + #else + const float runout_distance_mm = 0; + #endif + _FIELD_TEST(runout_sensor_enabled); + EEPROM_WRITE(runout_sensor_enabled); + EEPROM_WRITE(runout_distance_mm); } // @@ -1118,6 +1157,42 @@ void MarlinSettings::postprocess() { EEPROM_WRITE(toolchange_settings); #endif + // + // Backlash Compensation + // + { + #if ENABLED(BACKLASH_COMPENSATION) + const float (&backlash_distance_mm)[XYZ] = backlash.distance_mm; + const uint8_t &backlash_correction = backlash.correction; + #else + const float backlash_distance_mm[XYZ] = { 0 }; + const uint8_t backlash_correction = 0; + #endif + #ifdef BACKLASH_SMOOTHING_MM + const float &backlash_smoothing_mm = backlash.smoothing_mm; + #else + const float backlash_smoothing_mm = 3; + #endif + _FIELD_TEST(backlash_distance_mm); + EEPROM_WRITE(backlash_distance_mm[X_AXIS]); + EEPROM_WRITE(backlash_distance_mm[Y_AXIS]); + EEPROM_WRITE(backlash_distance_mm[Z_AXIS]); + EEPROM_WRITE(backlash_correction); + EEPROM_WRITE(backlash_smoothing_mm); + } + + // + // Extensible UI User Data + // + #if ENABLED(EXTENSIBLE_UI) + { + char extui_data[ExtUI::eeprom_data_size] = { 0 }; + ExtUI::onStoreSettings(extui_data); + _FIELD_TEST(extui_data); + EEPROM_WRITE(extui_data); + } + #endif + // // Validate CRC and Data Size // @@ -1148,7 +1223,7 @@ void MarlinSettings::postprocess() { #endif #if ENABLED(EXTENSIBLE_UI) - if (!eeprom_error) ExtUI::onStoreSettings(); + ExtUI::onConfigurationStoreWritten(!eeprom_error); #endif return !eeprom_error; @@ -1264,12 +1339,18 @@ void MarlinSettings::postprocess() { // Filament Runout Sensor // { - _FIELD_TEST(runout_sensor_enabled); #if HAS_FILAMENT_SENSOR - EEPROM_READ(runout.enabled); + bool &runout_sensor_enabled = runout.enabled; #else bool runout_sensor_enabled; - EEPROM_READ(runout_sensor_enabled); + #endif + _FIELD_TEST(runout_sensor_enabled); + EEPROM_READ(runout_sensor_enabled); + + float runout_distance_mm; + EEPROM_READ(runout_distance_mm); + #if HAS_FILAMENT_SENSOR && defined(FILAMENT_RUNOUT_DISTANCE_MM) + runout.set_runout_distance(runout_distance_mm); #endif } @@ -1851,6 +1932,44 @@ void MarlinSettings::postprocess() { EEPROM_READ(toolchange_settings); #endif + // + // Backlash Compensation + // + { + #if ENABLED(BACKLASH_COMPENSATION) + float (&backlash_distance_mm)[XYZ] = backlash.distance_mm; + uint8_t &backlash_correction = backlash.correction; + #else + float backlash_distance_mm[XYZ]; + uint8_t backlash_correction; + #endif + #ifdef BACKLASH_SMOOTHING_MM + float &backlash_smoothing_mm = backlash.smoothing_mm; + #else + float backlash_smoothing_mm; + #endif + _FIELD_TEST(backlash_distance_mm); + EEPROM_READ(backlash_distance_mm[X_AXIS]); + EEPROM_READ(backlash_distance_mm[Y_AXIS]); + EEPROM_READ(backlash_distance_mm[Z_AXIS]); + EEPROM_READ(backlash_correction); + EEPROM_READ(backlash_smoothing_mm); + } + + // + // Extensible UI User Data + // + #if ENABLED(EXTENSIBLE_UI) + // This is a significant hardware change; don't reserve EEPROM space when not present + { + const char extui_data[ExtUI::eeprom_data_size] = { 0 }; + _FIELD_TEST(extui_data); + EEPROM_READ(extui_data); + if(!validating) + ExtUI::onLoadSettings(extui_data); + } + #endif + eeprom_error = size_error(eeprom_index - (EEPROM_OFFSET)); if (eeprom_error) { DEBUG_ECHO_START(); @@ -1921,7 +2040,7 @@ void MarlinSettings::postprocess() { if (validate()) { const bool success = _load(); #if ENABLED(EXTENSIBLE_UI) - if (success) ExtUI::onLoadSettings(); + ExtUI::onConfigurationStoreRead(success); #endif return success; } @@ -2090,6 +2209,9 @@ void MarlinSettings::reset() { #if HAS_FILAMENT_SENSOR runout.enabled = true; runout.reset(); + #ifdef FILAMENT_RUNOUT_DISTANCE_MM + runout.set_runout_distance(FILAMENT_RUNOUT_DISTANCE_MM); + #endif #endif // @@ -2108,6 +2230,23 @@ void MarlinSettings::reset() { toolchange_settings.z_raise = TOOLCHANGE_ZRAISE; #endif + #if ENABLED(BACKLASH_GCODE) + backlash.correction = (BACKLASH_CORRECTION) * 255; + #ifdef BACKLASH_DISTANCE_MM + constexpr float tmp[XYZ] = BACKLASH_DISTANCE_MM; + backlash.distance_mm[X_AXIS] = tmp[X_AXIS]; + backlash.distance_mm[Y_AXIS] = tmp[Y_AXIS]; + backlash.distance_mm[Z_AXIS] = tmp[Z_AXIS]; + #endif + #ifdef BACKLASH_SMOOTHING_MM + backlash.smoothing_mm = BACKLASH_SMOOTHING_MM; + #endif + #endif + + #if ENABLED(EXTENSIBLE_UI) + ExtUI::onFactoryReset(); + #endif + // // Magnetic Parking Extruder // @@ -3200,6 +3339,31 @@ void MarlinSettings::reset() { CONFIG_ECHO_START(); M217_report(true); #endif + + #if ENABLED(BACKLASH_GCODE) + CONFIG_ECHO_HEADING("Backlash compensation:"); + CONFIG_ECHO_START(); + SERIAL_ECHOLNPAIR( + " M425 F", backlash.get_correction(), + " X", LINEAR_UNIT(backlash.distance_mm[X_AXIS]), + " Y", LINEAR_UNIT(backlash.distance_mm[Y_AXIS]), + " Z", LINEAR_UNIT(backlash.distance_mm[Z_AXIS]) + #ifdef BACKLASH_SMOOTHING_MM + , " S", LINEAR_UNIT(backlash.smoothing_mm) + #endif + ); + #endif + + #if HAS_FILAMENT_SENSOR + CONFIG_ECHO_HEADING("Filament runout sensor:"); + CONFIG_ECHO_START(); + SERIAL_ECHOLNPAIR( + " M412 S", int(runout.enabled) + #ifdef FILAMENT_RUNOUT_DISTANCE_MM + , " D", LINEAR_UNIT(runout.runout_distance()) + #endif + ); + #endif } #endif // !DISABLE_M503 diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 5073d903da..9c8a6f078f 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -92,6 +92,10 @@ #include "../feature/power.h" #endif +#if ENABLED(BACKLASH_COMPENSATION) + #include "../feature/backlash.h" +#endif + // 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 100 @@ -1560,94 +1564,6 @@ void Planner::synchronize() { ) idle(); } -/** - * The following implements axis backlash correction. To minimize seams - * on the printed part, the backlash correction only adds steps to the - * current segment (instead of creating a new segment, which causes - * discontinuities and print artifacts). - * - * When BACKLASH_SMOOTHING_MM is enabled and non-zero, the backlash - * correction is spread over multiple segments, smoothing out print - * artifacts even more. - */ -#if ENABLED(BACKLASH_COMPENSATION) - #if ENABLED(BACKLASH_GCODE) - extern float backlash_distance_mm[]; - extern uint8_t backlash_correction; - #ifdef BACKLASH_SMOOTHING_MM - extern float backlash_smoothing_mm; - #endif - #else - constexpr float backlash_distance_mm[XYZ] = BACKLASH_DISTANCE_MM, - constexpr uint8_t backlash_correction = BACKLASH_CORRECTION * 255; - #ifdef BACKLASH_SMOOTHING_MM - constexpr float backlash_smoothing_mm = BACKLASH_SMOOTHING_MM; - #endif - #endif - - void Planner::add_backlash_correction_steps(const int32_t da, const int32_t db, const int32_t dc, const uint8_t dm, block_t * const block) { - static uint8_t last_direction_bits; - uint8_t changed_dir = last_direction_bits ^ dm; - // Ignore direction change if no steps are taken in that direction - if (da == 0) CBI(changed_dir, X_AXIS); - if (db == 0) CBI(changed_dir, Y_AXIS); - if (dc == 0) CBI(changed_dir, Z_AXIS); - last_direction_bits ^= changed_dir; - - if (backlash_correction == 0) return; - - #ifdef BACKLASH_SMOOTHING_MM - // The segment proportion is a value greater than 0.0 indicating how much residual_error - // is corrected for in this segment. The contribution is based on segment length and the - // smoothing distance. Since the computation of this proportion involves a floating point - // division, defer computation until needed. - float segment_proportion = 0; - - // Residual error carried forward across multiple segments, so correction can be applied - // to segments where there is no direction change. - static int32_t residual_error[XYZ] = { 0 }; - #else - // No leftover residual error from segment to segment - int32_t residual_error[XYZ] = { 0 }; - // No direction change, no correction. - if (!changed_dir) return; - #endif - - const float f_corr = float(backlash_correction) / 255.0f; - - LOOP_XYZ(axis) { - if (backlash_distance_mm[axis]) { - const bool reversing = TEST(dm,axis); - - // When an axis changes direction, add axis backlash to the residual error - if (TEST(changed_dir, axis)) - residual_error[axis] += (reversing ? -f_corr : f_corr) * backlash_distance_mm[axis] * planner.settings.axis_steps_per_mm[axis]; - - // Decide how much of the residual error to correct in this segment - int32_t error_correction = residual_error[axis]; - #ifdef BACKLASH_SMOOTHING_MM - if (error_correction && backlash_smoothing_mm != 0) { - // Take up a portion of the residual_error in this segment, but only when - // the current segment travels in the same direction as the correction - if (reversing == (error_correction < 0)) { - if (segment_proportion == 0) - segment_proportion = MIN(1.0f, block->millimeters / backlash_smoothing_mm); - error_correction = ceil(segment_proportion * error_correction); - } - else - error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps - } - #endif - // Making a correction reduces the residual error and modifies delta_mm - if (error_correction) { - block->steps[axis] += ABS(error_correction); - residual_error[axis] -= error_correction; - } - } - } - } -#endif // BACKLASH_COMPENSATION - /** * Planner::_buffer_steps * @@ -1919,7 +1835,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, * should *never* remove steps! */ #if ENABLED(BACKLASH_COMPENSATION) - add_backlash_correction_steps(da, db, dc, dm, block); + backlash.add_correction_steps(da, db, dc, dm, block); #endif } diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index a95c3f2605..5e24ce6139 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -338,10 +338,6 @@ class Planner { volatile static uint32_t block_buffer_runtime_us; //Theoretical block buffer runtime in µs #endif - #if ENABLED(BACKLASH_COMPENSATION) - static void add_backlash_correction_steps(const int32_t da, const int32_t db, const int32_t dc, const uint8_t dm, block_t * const block); - #endif - public: /** diff --git a/Marlin/src/module/printcounter.cpp b/Marlin/src/module/printcounter.cpp index 4fa3e501f3..51af80afcf 100644 --- a/Marlin/src/module/printcounter.cpp +++ b/Marlin/src/module/printcounter.cpp @@ -29,6 +29,10 @@ Stopwatch print_job_timer; // Global Print Job Timer instance #else // PRINTCOUNTER +#if ENABLED(EXTENSIBLE_UI) + #include "../lcd/extensible_ui/ui_api.h" +#endif + #include "printcounter.h" #include "../Marlin.h" #include "../HAL/shared/persistent_store_api.h" @@ -169,6 +173,10 @@ void PrintCounter::saveStats() { persistentStore.access_start(); persistentStore.write_data(address + sizeof(uint8_t), (uint8_t*)&data, sizeof(printStatistics)); persistentStore.access_finish(); + + #if ENABLED(EXTENSIBLE_UI) + ExtUI::onConfigurationStoreWritten(true); + #endif } #if HAS_SERVICE_INTERVALS diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index 8988b15bea..39b6c8e8cf 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -54,6 +54,10 @@ #include "planner.h" #endif +#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) + #include "../feature/backlash.h" +#endif + float zprobe_zoffset; // Initialized by settings.load() #if ENABLED(BLTOUCH) @@ -463,30 +467,6 @@ bool set_probe_deployed(const bool deploy) { } #endif -#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) - #if USES_Z_MIN_PROBE_ENDSTOP - #define TEST_PROBE_PIN (READ(Z_MIN_PROBE_PIN) != Z_MIN_PROBE_ENDSTOP_INVERTING) - #else - #define TEST_PROBE_PIN (READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING) - #endif - - extern float backlash_measured_mm[]; - extern uint8_t backlash_measured_num[]; - - /* Measure Z backlash by raising nozzle in increments until probe deactivates */ - static void measure_backlash_with_probe() { - if (backlash_measured_num[Z_AXIS] == 255) return; - - float start_height = current_position[Z_AXIS]; - while (current_position[Z_AXIS] < (start_height + BACKLASH_MEASUREMENT_LIMIT) && TEST_PROBE_PIN) - do_blocking_move_to_z(current_position[Z_AXIS] + BACKLASH_MEASUREMENT_RESOLUTION, MMM_TO_MMS(BACKLASH_MEASUREMENT_FEEDRATE)); - - // The backlash from all probe points is averaged, so count the number of measurements - backlash_measured_mm[Z_AXIS] += current_position[Z_AXIS] - start_height; - backlash_measured_num[Z_AXIS]++; - } -#endif - /** * @brief Used by run_z_probe to do a single Z probe move. * @@ -643,7 +623,7 @@ static float run_z_probe() { } #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) - measure_backlash_with_probe(); + backlash.measure_with_probe(); #endif #if MULTIPLE_PROBING > 2 diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index ce3141c12f..eb4957ec2e 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -113,7 +113,7 @@ Stepper stepper; // Singleton #include "../feature/mixing.h" #endif -#if FILAMENT_RUNOUT_DISTANCE_MM > 0 +#ifdef FILAMENT_RUNOUT_DISTANCE_MM #include "../feature/runout.h" #endif @@ -1537,7 +1537,7 @@ uint32_t Stepper::stepper_block_phase_isr() { // If current block is finished, reset pointer if (step_events_completed >= step_event_count) { - #if FILAMENT_RUNOUT_DISTANCE_MM > 0 + #ifdef FILAMENT_RUNOUT_DISTANCE_MM runout.block_completed(current_block); #endif axis_did_move = 0;