From 52cc705e60bb88ed72f6e573924bbb885f06b909 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Fri, 26 Sep 2025 02:40:28 -0500 Subject: [PATCH 001/149] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Use?= =?UTF-8?q?=20basic=20PGM=20string=20with=20"S=5FFMT"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp | 6 +++--- Marlin/src/feature/stepper_driver_safety.cpp | 2 +- Marlin/src/gcode/bedlevel/abl/G29.cpp | 4 ++-- Marlin/src/gcode/bedlevel/mbl/G29.cpp | 2 +- Marlin/src/gcode/calibrate/M48.cpp | 2 +- Marlin/src/module/temperature.cpp | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp index d97f1aa0af..caf10d01fd 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp @@ -782,7 +782,7 @@ void unified_bed_leveling::shift_mesh_height(const float zoffs) { const grid_count_t point_num = (GRID_MAX_POINTS - count) + 1; SERIAL_ECHOLNPGM("Probing mesh point ", point_num, "/", GRID_MAX_POINTS, "."); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT_F(MSG_PROBING_POINT), point_num, int(GRID_MAX_POINTS))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), point_num, int(GRID_MAX_POINTS))); TERN_(HAS_BACKLIGHT_TIMEOUT, ui.refresh_backlight_timeout()); #if HAS_MARLINUI_MENU @@ -1511,7 +1511,7 @@ void unified_bed_leveling::smart_fill_mesh() { for (uint8_t i = 0; i < 3; ++i) { SERIAL_ECHOLNPGM("Tilting mesh (", i + 1, "/3)"); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/3"), GET_TEXT_F(MSG_LCD_TILTING_MESH), i + 1)); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/3"), GET_TEXT(MSG_LCD_TILTING_MESH), i + 1)); measured_z = probe.probe_at_point(points[i], i < 2 ? PROBE_PT_RAISE : PROBE_PT_LAST_STOW, param.V_verbosity); if ((abort_flag = isnan(measured_z))) break; @@ -1567,7 +1567,7 @@ void unified_bed_leveling::smart_fill_mesh() { #endif SERIAL_ECHOLNPGM("Tilting mesh point ", point_num, "/", total_points, "\n"); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT_F(MSG_LCD_TILTING_MESH), point_num, total_points)); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_LCD_TILTING_MESH), point_num, total_points)); measured_z = probe.probe_at_point(rpos, parser.seen_test('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, param.V_verbosity); // TODO: Needs error handling diff --git a/Marlin/src/feature/stepper_driver_safety.cpp b/Marlin/src/feature/stepper_driver_safety.cpp index 3ddc05ea1e..acdd695909 100644 --- a/Marlin/src/feature/stepper_driver_safety.cpp +++ b/Marlin/src/feature/stepper_driver_safety.cpp @@ -31,7 +31,7 @@ static uint32_t axis_plug_backward = 0; void stepper_driver_backward_error(FSTR_P const fstr) { SERIAL_ERROR_START(); SERIAL_ECHOLN(fstr, F(" driver is backward!")); - ui.status_printf(2, F(S_FMT S_FMT), FTOP(fstr), GET_TEXT_F(MSG_DRIVER_BACKWARD)); + ui.status_printf(2, F(S_FMT S_FMT), FTOP(fstr), GET_TEXT(MSG_DRIVER_BACKWARD)); } void stepper_driver_backward_check() { diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp index e0cf28156b..86c22f373a 100644 --- a/Marlin/src/gcode/bedlevel/abl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp @@ -708,7 +708,7 @@ G29_TYPE GcodeSuite::G29() { if (TERN0(IS_KINEMATIC, !probe.can_reach(abl.probePos))) continue; if (abl.verbose_level) SERIAL_ECHOLNPGM("Probing mesh point ", pt_index, "/", abl.abl_points, "."); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT_F(MSG_PROBING_POINT), int(pt_index), int(abl.abl_points))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), int(pt_index), int(abl.abl_points))); #if ENABLED(BD_SENSOR_PROBE_NO_STOP) if (PR_INNER_VAR == inStart) { @@ -813,7 +813,7 @@ G29_TYPE GcodeSuite::G29() { for (uint8_t i = 0; i < 3; ++i) { if (abl.verbose_level) SERIAL_ECHOLNPGM("Probing point ", i + 1, "/3."); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/3"), GET_TEXT_F(MSG_PROBING_POINT), int(i + 1))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/3"), GET_TEXT(MSG_PROBING_POINT), int(i + 1))); // Retain the last probe position abl.probePos = xy_pos_t(points[i]); diff --git a/Marlin/src/gcode/bedlevel/mbl/G29.cpp b/Marlin/src/gcode/bedlevel/mbl/G29.cpp index 4bd444c5a3..8c911a1613 100644 --- a/Marlin/src/gcode/bedlevel/mbl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/mbl/G29.cpp @@ -261,7 +261,7 @@ void GcodeSuite::G29() { if (state == MeshNext) { SERIAL_ECHOLNPGM("MBL G29 point ", _MIN(mbl_probe_index, GRID_MAX_POINTS), " of ", GRID_MAX_POINTS); - if (mbl_probe_index > 0) TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT_F(MSG_PROBING_POINT), _MIN(mbl_probe_index, GRID_MAX_POINTS), int(GRID_MAX_POINTS))); + if (mbl_probe_index > 0) TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), _MIN(mbl_probe_index, GRID_MAX_POINTS), int(GRID_MAX_POINTS))); } report_current_position(); diff --git a/Marlin/src/gcode/calibrate/M48.cpp b/Marlin/src/gcode/calibrate/M48.cpp index f57bfeb584..0c4355f5b1 100644 --- a/Marlin/src/gcode/calibrate/M48.cpp +++ b/Marlin/src/gcode/calibrate/M48.cpp @@ -149,7 +149,7 @@ void GcodeSuite::M48() { for (uint8_t n = 0; n < n_samples; ++n) { #if HAS_STATUS_MESSAGE // Display M48 progress in the status bar - ui.status_printf(0, F(S_FMT ": %d/%d"), GET_TEXT_F(MSG_M48_POINT), int(n + 1), int(n_samples)); + ui.status_printf(0, F(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples)); #endif // When there are "legs" of movement move around the point before probing diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 73f64aedb6..bd92d7f073 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -948,7 +948,7 @@ void Temperature::factory_reset() { } } SHV((bias + d) >> 1); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT_F(MSG_PID_CYCLE), cycles, ncycles)); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_PID_CYCLE), cycles, ncycles)); cycles++; minT = target; } @@ -4753,7 +4753,7 @@ void Temperature::isr() { #else F("E1 " S_FMT) #endif - , heating ? GET_TEXT_F(MSG_HEATING) : GET_TEXT_F(MSG_COOLING) + , heating ? GET_TEXT(MSG_HEATING) : GET_TEXT(MSG_COOLING) ); if (isM104) { From 0628573fc305fd1df48a3d20324f830cb4448bc8 Mon Sep 17 00:00:00 2001 From: 12oclocker <73079144+12oclocker@users.noreply.github.com> Date: Fri, 26 Sep 2025 04:21:43 -0400 Subject: [PATCH 002/149] =?UTF-8?q?=F0=9F=A9=B9=20Prevent=20RAMPS=20defaul?= =?UTF-8?q?t=20`X=5FMAX=5FPIN`=20messing=20up=20AnyCubic=20i3=20Mega=20(#2?= =?UTF-8?q?8038)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/pins/ramps/pins_TRIGORILLA_14.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Marlin/src/pins/ramps/pins_TRIGORILLA_14.h b/Marlin/src/pins/ramps/pins_TRIGORILLA_14.h index 7c01c8aba8..9a1866c215 100644 --- a/Marlin/src/pins/ramps/pins_TRIGORILLA_14.h +++ b/Marlin/src/pins/ramps/pins_TRIGORILLA_14.h @@ -120,6 +120,7 @@ #define Y_STOP_PIN 42 // AUX (1) #define Z_STOP_PIN 43 // AUX (2) #ifndef Z2_STOP_PIN + #define X_MAX_PIN 18 // If X_MAX_PIN != Z2_STOP_PIN homing sensor trigger state is "Inverted". #define Z2_STOP_PIN 18 // Z- #endif From 4dd5b713eb557cefbb693c67c0367a42dee43b8c Mon Sep 17 00:00:00 2001 From: ellensp <530024+ellensp@users.noreply.github.com> Date: Fri, 26 Sep 2025 21:03:53 +1200 Subject: [PATCH 003/149] =?UTF-8?q?=F0=9F=9A=B8=20M402=20Report=20(BLTouch?= =?UTF-8?q?=20HS=20Mode)=20(#28072)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/gcode/gcode.h | 1 + Marlin/src/gcode/probe/M401_M402.cpp | 11 +++++++++++ Marlin/src/module/settings.cpp | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index 0e5d8f5681..8fbb498761 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -1050,6 +1050,7 @@ private: #if HAS_BED_PROBE static void M401(); + static void M401_report(const bool forReplay=true); static void M402(); #endif diff --git a/Marlin/src/gcode/probe/M401_M402.cpp b/Marlin/src/gcode/probe/M401_M402.cpp index 237f841212..a2a1caa01d 100644 --- a/Marlin/src/gcode/probe/M401_M402.cpp +++ b/Marlin/src/gcode/probe/M401_M402.cpp @@ -57,6 +57,17 @@ void GcodeSuite::M401() { report_current_position(); } +void GcodeSuite::M401_report(const bool forReplay/*=true*/) { + TERN_(MARLIN_SMALL_BUILD, return); + + #if HAS_BLTOUCH_HS_MODE + if (!forReplay) { + report_heading_etc(forReplay, F("BLTouch HS mode")); + SERIAL_ECHOLNPGM(" M401 S", bltouch.high_speed_mode, " ; ", ON_OFF(bltouch.high_speed_mode)); + } + #endif +} + /** * M402: Deactivate and stow the Z probe * R Remain in place after stowing (and before deactivating) the probe diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp index dd724b8441..1eb46e606b 100644 --- a/Marlin/src/module/settings.cpp +++ b/Marlin/src/module/settings.cpp @@ -3960,6 +3960,11 @@ void MarlinSettings::reset() { // TERN_(EDITABLE_SERVO_ANGLES, gcode.M281_report(forReplay)); + // + // BLTouch High Speed Mode + // + TERN_(BLTOUCH_HS_MODE, gcode.M401_report(forReplay)); + // // Kinematic Settings // From 0ca860b2928321b7a9c0f159642bc835e3f68002 Mon Sep 17 00:00:00 2001 From: ThomasToka <117008525+ThomasToka@users.noreply.github.com> Date: Fri, 26 Sep 2025 23:16:39 +0200 Subject: [PATCH 004/149] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Res?= =?UTF-8?q?olve=20unused=20ftm=5Factive=20warning=20(#28076)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/module/planner.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index f1317b0780..47e4ed4f28 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -2417,7 +2417,9 @@ bool Planner::_populate_block( */ if (esteps && dm.e) { const bool ftm_active = TERN0(FTM_HAS_LIN_ADVANCE, ftMotion.cfg.active); - const float advK = TERN_(FTM_HAS_LIN_ADVANCE, ftm_active ? ftMotion.cfg.linearAdvK :) TERN0(LIN_ADVANCE, extruder_advance_K[E_INDEX_N(extruder)]); + const float advK = ftm_active + ? TERN0(FTM_HAS_LIN_ADVANCE, ftMotion.cfg.linearAdvK) + : TERN0(HAS_ROUGH_LIN_ADVANCE, extruder_advance_K[E_INDEX_N(extruder)]); if (advK) { float e_D_ratio = (target_float.e - position_float.e) / TERN(IS_KINEMATIC, block->millimeters, @@ -2431,14 +2433,12 @@ bool Planner::_populate_block( // 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. use_adv_lead = e_D_ratio <= 3.0f; - if (use_adv_lead) { - if (TERN0(HAS_ROUGH_LIN_ADVANCE, !ftm_active)) { - // Scale E acceleration so that it will be possible to jump to the advance speed. - const uint32_t max_accel_steps_per_s2 = (MAX_E_JERK(extruder) / (advK * e_D_ratio)) * steps_per_mm; - if (accel > max_accel_steps_per_s2) { - accel = max_accel_steps_per_s2; - if (TERN0(LA_DEBUG, DEBUGGING(INFO))) SERIAL_ECHOLNPGM("Acceleration limited."); - } + if (use_adv_lead && TERN0(HAS_ROUGH_LIN_ADVANCE, !ftm_active)) { + // For Standard Motion LA: Scale E acceleration so it'll be possible to jump to the advance speed + const uint32_t max_accel_steps_per_s2 = (MAX_E_JERK(extruder) / (advK * e_D_ratio)) * steps_per_mm; + if (accel > max_accel_steps_per_s2) { + accel = max_accel_steps_per_s2; + if (TERN0(LA_DEBUG, DEBUGGING(INFO))) SERIAL_ECHOLNPGM("Acceleration limited."); } } } From 5af84257765b33ae510a40938585a872e017e069 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Sat, 27 Sep 2025 00:29:05 +0000 Subject: [PATCH 005/149] [cron] Bump distribution date (2025-09-27) --- Marlin/Version.h | 2 +- Marlin/src/inc/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Marlin/Version.h b/Marlin/Version.h index 3ffdf4d71c..18d0e5057e 100644 --- a/Marlin/Version.h +++ b/Marlin/Version.h @@ -41,7 +41,7 @@ * here we define this default string as the date where the latest release * version was tagged. */ -//#define STRING_DISTRIBUTION_DATE "2025-09-26" +//#define STRING_DISTRIBUTION_DATE "2025-09-27" /** * The protocol for communication to the host. Protocol indicates communication diff --git a/Marlin/src/inc/Version.h b/Marlin/src/inc/Version.h index 76e2272cef..f466eec073 100644 --- a/Marlin/src/inc/Version.h +++ b/Marlin/src/inc/Version.h @@ -42,7 +42,7 @@ * version was tagged. */ #ifndef STRING_DISTRIBUTION_DATE - #define STRING_DISTRIBUTION_DATE "2025-09-26" + #define STRING_DISTRIBUTION_DATE "2025-09-27" #endif /** From 6e74409c14bb4afb7ffb8bbba3deabec14883d89 Mon Sep 17 00:00:00 2001 From: Bob Kuhn Date: Sat, 27 Sep 2025 02:25:03 -0500 Subject: [PATCH 006/149] =?UTF-8?q?=F0=9F=90=9B=20Fix=20STM32=20I2C=202-wi?= =?UTF-8?q?re=20LCD,=20Soft=20I2C=20impl.=20(#26433)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/HAL/STM32/u8g/LCD_defines.h | 3 + .../STM32/u8g/u8g_com_stm32duino_ssd_i2c.cpp | 194 ++++++++++++++++++ Marlin/src/inc/Conditionals-2-LCD.h | 15 ++ Marlin/src/lcd/dogm/marlinui_DOGM.cpp | 17 +- Marlin/src/lcd/dogm/marlinui_DOGM.h | 26 ++- .../src/lcd/dogm/u8g/HAL_LCD_class_defines.h | 4 +- .../u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp | 20 +- buildroot/tests/STM32F103RE_btt | 2 +- ini/features.ini | 2 +- 9 files changed, 264 insertions(+), 19 deletions(-) create mode 100644 Marlin/src/HAL/STM32/u8g/u8g_com_stm32duino_ssd_i2c.cpp diff --git a/Marlin/src/HAL/STM32/u8g/LCD_defines.h b/Marlin/src/HAL/STM32/u8g/LCD_defines.h index 59b2b8839a..96f73002a5 100644 --- a/Marlin/src/HAL/STM32/u8g/LCD_defines.h +++ b/Marlin/src/HAL/STM32/u8g/LCD_defines.h @@ -30,3 +30,6 @@ uint8_t u8g_com_HAL_STM32_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, vo uint8_t u8g_com_stm32duino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // See U8glib-HAL #define U8G_COM_HAL_HW_SPI_FN u8g_com_stm32duino_hw_spi_fn + +uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // u8g_com_stm32duino_ssd_i2c.cpp +#define U8G_COM_SSD_I2C_HAL u8g_com_stm32duino_ssd_i2c_fn diff --git a/Marlin/src/HAL/STM32/u8g/u8g_com_stm32duino_ssd_i2c.cpp b/Marlin/src/HAL/STM32/u8g/u8g_com_stm32duino_ssd_i2c.cpp new file mode 100644 index 0000000000..72abe1a656 --- /dev/null +++ b/Marlin/src/HAL/STM32/u8g/u8g_com_stm32duino_ssd_i2c.cpp @@ -0,0 +1,194 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 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 . + * + */ + +/** + * 2-Wire I2C COM Driver + * + * Handles both Hardware and Software I2C so any pins can be used as SDA and SLC. + * Wire library is used for Hardware I2C. + * SlowSoftWire is used for Software I2C. + * + * Wire / SoftWire library selection can be done automatically at runtime. + * + * SDA and SLC pins must be named DOGLCD_SDA_PIN, DOGLCD_SCL_PIN to distinguish + * from other I2C devices (e.g., EEPROM) that use I2C_SDA_PIN, I2C_SLC_PIN. + */ +#ifdef ARDUINO_ARCH_STM32 + +#include "../../../inc/MarlinConfig.h" + +#if HAS_U8GLIB_I2C_OLED + +#include + +#if ENABLED(U8G_USES_HW_I2C) + #include + #ifndef MASTER_ADDRESS + #define MASTER_ADDRESS 0x01 + #endif +#endif + +#if ENABLED(U8G_USES_SW_I2C) + #include + #include +#endif + +/** + * BUFFER_LENGTH is defined in libraries\Wire\utility\WireBase.h + * Default value is 32 + * Increase this value to 144 to send U8G_COM_MSG_WRITE_SEQ in single block + */ +#ifndef BUFFER_LENGTH + #define BUFFER_LENGTH 32 +#endif +#if BUFFER_LENGTH > 144 + #error "BUFFER_LENGTH should not be greater than 144." +#endif +#define I2C_MAX_LENGTH (BUFFER_LENGTH - 1) + +uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) { + // Hardware I2C flag + #ifdef COMPILE_TIME_I2C_IS_HARDWARE + constexpr bool isHardI2C = ENABLED(COMPILE_TIME_I2C_IS_HARDWARE); + #else + static bool isHardI2C = false; + static bool i2c_initialized = false; // Flag to only run init/linking code once + if (!i2c_initialized) { // Init runtime linkages + i2c_initialized = true; // Only do this once + I2C_TypeDef *i2cInstance1 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SDA_PIN), PinMap_I2C_SDA); + I2C_TypeDef *i2cInstance2 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SCL_PIN), PinMap_I2C_SCL); + isHardI2C = (i2cInstance1 && (i2cInstance1 == i2cInstance2)); // Found hardware I2C controller + } + #endif + + static uint8_t msgInitCount = 0; // Ignore all messages until 2nd U8G_COM_MSG_INIT + if (msgInitCount) { + if (msg == U8G_COM_MSG_INIT) msgInitCount--; + if (msgInitCount) return -1; + } + + static uint8_t control; + if (isHardI2C) { // Found hardware I2C controller + #if ENABLED(U8G_USES_HW_I2C) + static TwoWire wire2; // A TwoWire object for use below + switch (msg) { + case U8G_COM_MSG_INIT: + wire2.setClock(400000); + wire2.setSCL(DOGLCD_SCL_PIN); + wire2.setSDA(DOGLCD_SDA_PIN); + wire2.begin(MASTER_ADDRESS, 0); // Start as master + break; + + case U8G_COM_MSG_ADDRESS: // Define cmd (arg_val = 0) or data mode (arg_val = 1) + control = arg_val ? 0x40 : 0x00; + break; + + case U8G_COM_MSG_WRITE_BYTE: + wire2.beginTransmission(0x3C); + wire2.write(control); + wire2.write(arg_val); + wire2.endTransmission(); + break; + + case U8G_COM_MSG_WRITE_SEQ: { + uint8_t* dataptr = (uint8_t*)arg_ptr; + #ifdef I2C_MAX_LENGTH + while (arg_val > 0) { + wire2.beginTransmission(0x3C); + wire2.write(control); + if (arg_val <= I2C_MAX_LENGTH) { + wire2.write(dataptr, arg_val); + arg_val = 0; + } + else { + wire2.write(dataptr, I2C_MAX_LENGTH); + arg_val -= I2C_MAX_LENGTH; + dataptr += I2C_MAX_LENGTH; + } + wire2.endTransmission(); + } + #else + wire2.beginTransmission(0x3C); + wire2.write(control); + wire2.write(dataptr, arg_val); + wire2.endTransmission(); + #endif // I2C_MAX_LENGTH + break; + } + } + #endif // U8G_USES_HW_I2C + } + else { // Software I2C + #if ENABLED(U8G_USES_SW_I2C) + static SlowSoftWire sWire = SlowSoftWire(DOGLCD_SDA_PIN, DOGLCD_SCL_PIN); + + switch (msg) { + case U8G_COM_MSG_INIT: + sWire.setClock(400000); + sWire.begin(); // Start as master + break; + + case U8G_COM_MSG_ADDRESS: // Define cmd (arg_val = 0) or data mode (arg_val = 1) + control = arg_val ? 0x40 : 0x00; + break; + + case U8G_COM_MSG_WRITE_BYTE: + sWire.beginTransmission((uint8_t)0x3C); + sWire.write((uint8_t)control); + sWire.write((uint8_t)arg_val); + sWire.endTransmission(); + break; + + case U8G_COM_MSG_WRITE_SEQ: { + uint8_t* dataptr = (uint8_t*)arg_ptr; + #ifdef I2C_MAX_LENGTH + while (arg_val > 0) { + sWire.beginTransmission((uint8_t)0x3C); + sWire.write((uint8_t)control); + if (arg_val <= I2C_MAX_LENGTH) { + sWire.write((const uint8_t *)dataptr, (size_t)arg_val); + arg_val = 0; + } + else { + sWire.write((const uint8_t *)dataptr, I2C_MAX_LENGTH); + arg_val -= I2C_MAX_LENGTH; + dataptr += I2C_MAX_LENGTH; + } + sWire.endTransmission(); + } + #else + sWire.beginTransmission((uint8_t)0x3C); + sWire.write((uint8_t)control); + sWire.write((const uint8_t *)dataptr, (size_t)arg_val); + sWire.endTransmission(); + #endif // I2C_MAX_LENGTH + break; + } + } + #endif // U8G_USES_SW_I2C + } + + return 1; +} + +#endif // HAS_U8GLIB_I2C_OLED +#endif // ARDUINO_ARCH_STM32 diff --git a/Marlin/src/inc/Conditionals-2-LCD.h b/Marlin/src/inc/Conditionals-2-LCD.h index a53b7f8423..41d3805954 100644 --- a/Marlin/src/inc/Conditionals-2-LCD.h +++ b/Marlin/src/inc/Conditionals-2-LCD.h @@ -354,7 +354,22 @@ // ...and 128x64 SPI OLED LCDs (SSD1306 / SH1106) #if ANY(U8GLIB_SSD1306, U8GLIB_SSD1309, U8GLIB_SH1106) #define HAS_U8GLIB_I2C_OLED 1 + + // Define this to reduce build size and optimize performance + //#define COMPILE_TIME_I2C_IS_HARDWARE true // true: Hardware false: Software undefined: Solve at runtime + + #ifdef COMPILE_TIME_I2C_IS_HARDWARE + #if COMPILE_TIME_I2C_IS_HARDWARE + #define U8G_USES_HW_I2C + #else + #define U8G_USES_SW_I2C + #endif + #else + #define U8G_USES_HW_I2C + #define U8G_USES_SW_I2C + #endif #endif + #if ANY(HAS_U8GLIB_I2C_OLED, U8GLIB_SSD1306_SPI, U8GLIB_SH1106_SPI) #define HAS_WIRED_LCD 1 #define DOGLCD diff --git a/Marlin/src/lcd/dogm/marlinui_DOGM.cpp b/Marlin/src/lcd/dogm/marlinui_DOGM.cpp index 721389cb2c..94e911dba1 100644 --- a/Marlin/src/lcd/dogm/marlinui_DOGM.cpp +++ b/Marlin/src/lcd/dogm/marlinui_DOGM.cpp @@ -310,7 +310,22 @@ void MarlinUI::init_lcd() { #endif #if ANY(MKS_12864OLED, MKS_12864OLED_SSD1306, FYSETC_242_OLED_12864, ZONESTAR_12864OLED, K3D_242_OLED_CONTROLLER) - SET_OUTPUT(LCD_PINS_DC); + + #if defined(LCD_PINS_DC) && LCD_PINS_DC != -1 + #if IS_I2C_LCD + I2C_TypeDef *i2cInstance1 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SDA_PIN), PinMap_I2C_SDA); + I2C_TypeDef *i2cInstance2 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SCL_PIN), PinMap_I2C_SCL); + const bool isSoftI2C = !(i2cInstance1 && (i2cInstance1 == i2cInstance2)); // Using software I2C driver for LCD + #else + constexpr bool isSoftI2C = false; + #endif + if (!isSoftI2C) SET_OUTPUT(LCD_PINS_DC); // For these LCDs, set as output if not using software I2C driver + #endif + + #ifndef LCD_RESET_PIN + #define LCD_RESET_PIN LCD_PINS_RS + #endif + #endif #if PIN_EXISTS(LCD_RESET) diff --git a/Marlin/src/lcd/dogm/marlinui_DOGM.h b/Marlin/src/lcd/dogm/marlinui_DOGM.h index c0cc7c4438..9d3511084b 100644 --- a/Marlin/src/lcd/dogm/marlinui_DOGM.h +++ b/Marlin/src/lcd/dogm/marlinui_DOGM.h @@ -32,6 +32,11 @@ //#define ALTERNATIVE_LCD +// Defined DOGLCD_SDA_PIN and DOGLCD_SCL_PIN pins indicate I2C LCD +#if PINS_EXIST(DOGLCD_SDA, DOGLCD_SCL) + #define IS_I2C_LCD 1 +#endif + #if ENABLED(REPRAPWORLD_GRAPHICAL_LCD) // RepRapWorld Graphical LCD @@ -126,12 +131,15 @@ // MKS 128x64 (SSD1306) OLED I2C LCD - #define FORCE_SOFT_SPI // SW-SPI - - #if ENABLED(ALTERNATIVE_LCD) - #define U8G_CLASS U8GLIB_SSD1306_128X64_2X // 4 stripes + #if IS_I2C_LCD + #define U8G_CLASS U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE // I2C #else - #define U8G_CLASS U8GLIB_SSD1306_128X64 // 8 stripes + #define FORCE_SOFT_SPI // SW-SPI + #if ENABLED(ALTERNATIVE_LCD) + #define U8G_CLASS U8GLIB_SSD1306_128X64_2X // 4 stripes + #else + #define U8G_CLASS U8GLIB_SSD1306_128X64 // 8 stripes + #endif #endif #elif ANY(FYSETC_242_OLED_12864, K3D_242_OLED_CONTROLLER) @@ -168,7 +176,9 @@ // - or - // Zonestar SH1106 OLED SPI LCD - #define FORCE_SOFT_SPI // SW-SPI + #if !IS_I2C_LCD + #define FORCE_SOFT_SPI // SW-SPI + #endif #if ENABLED(ALTERNATIVE_LCD) #define U8G_CLASS U8GLIB_SH1106_128X64_2X // 4 stripes #else @@ -238,7 +248,9 @@ // Use HW-SPI if no other option is specified #ifndef U8G_PARAM - #if ENABLED(FORCE_SOFT_SPI) + #if IS_I2C_LCD + #define U8G_PARAM U8G_I2C_OPT_NONE // I2C LCD + #elif ENABLED(FORCE_SOFT_SPI) #define U8G_PARAM DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0 // SW-SPI #else #define U8G_PARAM DOGLCD_CS, DOGLCD_A0 // HW-SPI diff --git a/Marlin/src/lcd/dogm/u8g/HAL_LCD_class_defines.h b/Marlin/src/lcd/dogm/u8g/HAL_LCD_class_defines.h index 33a9c21c41..576e58eabd 100644 --- a/Marlin/src/lcd/dogm/u8g/HAL_LCD_class_defines.h +++ b/Marlin/src/lcd/dogm/u8g/HAL_LCD_class_defines.h @@ -78,7 +78,7 @@ class U8GLIB_SH1106_128X64_2X_I2C_2_WIRE : public U8GLIB { public: U8GLIB_SH1106_128X64_2X_I2C_2_WIRE() : U8GLIB() { } U8GLIB_SH1106_128X64_2X_I2C_2_WIRE(uint8_t options) { init(options); } - void init(uint8_t options = U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_sh1106_128x64_2x_i2c_2_wire, options); } + void init(uint8_t options=U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_sh1106_128x64_2x_i2c_2_wire, options); } }; extern u8g_dev_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire; @@ -87,7 +87,7 @@ class U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE : public U8GLIB { public: U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE() : U8GLIB() { } U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE(uint8_t options) { init(options); } - void init(uint8_t options = U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_ssd1306_128x64_2x_i2c_2_wire, options); } + void init(uint8_t options=U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_ssd1306_128x64_2x_i2c_2_wire, options); } }; #if ENABLED(U8GLIB_SH1106_SPI) diff --git a/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp b/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp index f44b4f8ac5..541140c0ad 100644 --- a/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp +++ b/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp @@ -67,7 +67,7 @@ #include "../../../inc/MarlinConfigPre.h" -#if HAS_MARLINUI_U8GLIB +#if HAS_U8GLIB_I2C_OLED #include "HAL_LCD_com_defines.h" @@ -97,6 +97,7 @@ #define CMD_NOOP() (0xE3) uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_seq); +uint8_t u8g_Write_Init_Sequence_2_wire(u8g_t *u8g, u8g_dev_t *dev, uint32_t length, const uint8_t *init_seq); // SH1106 is compatible with SSD1306, but is 132x64. Display 128x64 centered within the 132x64. @@ -133,7 +134,7 @@ uint8_t u8g_dev_sh1106_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t m switch (msg) { case U8G_DEV_MSG_INIT: u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS); - u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_sh1106_128x64_init_seq_2_wire); + u8g_Write_Init_Sequence_2_wire(u8g, dev, COUNT(u8g_dev_sh1106_128x64_init_seq_2_wire), u8g_dev_sh1106_128x64_init_seq_2_wire); break; case U8G_DEV_MSG_STOP: break; case U8G_DEV_MSG_PAGE_NEXT: { @@ -151,7 +152,7 @@ uint8_t u8g_dev_sh1106_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t m u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *)(pb->buf)+pb->width); u8g_SetChipSelect(u8g, dev, 0); } break; - case U8G_DEV_MSG_SLEEP_ON: return 1; + case U8G_DEV_MSG_SLEEP_ON: case U8G_DEV_MSG_SLEEP_OFF: return 1; } return u8g_dev_pb16v1_base_fn(u8g, dev, msg, arg); @@ -169,7 +170,6 @@ static const uint8_t u8g_dev_ssd1306_128x64_data_start_2_wire[] PROGMEM = { }; static const uint8_t u8g_dev_ssd1306_128x64_init_seq_2_wire[] PROGMEM = { - U8G_ESC_CS(0), // Disable chip CMD_ON(0), // Display OFF, sleep mode CMD_MUX_RATIO(0x3F), // Mux ratio CMD_DISP_OFFS(0), // Display offset @@ -196,7 +196,7 @@ uint8_t u8g_dev_ssd1306_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t switch (msg) { case U8G_DEV_MSG_INIT: u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS); - u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_ssd1306_128x64_init_seq_2_wire); + u8g_Write_Init_Sequence_2_wire(u8g, dev, COUNT(u8g_dev_ssd1306_128x64_init_seq_2_wire), u8g_dev_ssd1306_128x64_init_seq_2_wire); break; case U8G_DEV_MSG_STOP: break; case U8G_DEV_MSG_PAGE_NEXT: { @@ -214,7 +214,7 @@ uint8_t u8g_dev_ssd1306_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *)(pb->buf)+pb->width); u8g_SetChipSelect(u8g, dev, 0); } break; - case U8G_DEV_MSG_SLEEP_ON: return 1; + case U8G_DEV_MSG_SLEEP_ON: case U8G_DEV_MSG_SLEEP_OFF: return 1; } return u8g_dev_pb16v1_base_fn(u8g, dev, msg, arg); @@ -283,4 +283,10 @@ uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_s return 1; } -#endif // HAS_MARLINUI_U8GLIB +uint8_t u8g_Write_Init_Sequence_2_wire(u8g_t *u8g, u8g_dev_t *dev, uint32_t length, const uint8_t *init_seq) { + u8g_SetAddress(u8g, dev, 0); // Instruction mode + u8g_WriteSequence(u8g, dev, length, (uint8_t*)init_seq); + return 1; +} + +#endif // HAS_U8GLIB_I2C_OLED diff --git a/buildroot/tests/STM32F103RE_btt b/buildroot/tests/STM32F103RE_btt index fdf1580e98..2c46272625 100755 --- a/buildroot/tests/STM32F103RE_btt +++ b/buildroot/tests/STM32F103RE_btt @@ -13,5 +13,5 @@ restore_configs opt_set MOTHERBOARD BOARD_BTT_SKR_E3_DIP \ SERIAL_PORT 1 SERIAL_PORT_2 -1 \ X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2130 -opt_enable SERIAL_DMA +opt_enable SERIAL_DMA ULTI_CONTROLLER exec_test $1 $2 "BTT SKR E3 DIP 1.0 | Mixed TMC Drivers" "$3" diff --git a/ini/features.ini b/ini/features.ini index 1f09858f07..d8e0357b02 100644 --- a/ini/features.ini +++ b/ini/features.ini @@ -43,7 +43,7 @@ HAS_MARLINUI_U8GLIB = marlinfirmware/U8glib-HAL@0.5.5 build_src_filter=+ HAS_(FSMC|SPI|LTDC)_TFT = build_src_filter=+ I2C_EEPROM = build_src_filter=+ -SOFT_I2C_EEPROM = SlowSoftI2CMaster, SlowSoftWire=https://github.com/felias-fogg/SlowSoftWire/archive/f34d777f39.zip +SOFT_I2C_EEPROM|U8G_USES_SW_I2C = SlowSoftI2CMaster, SlowSoftWire=https://github.com/felias-fogg/SlowSoftWire/archive/f34d777f39.zip SPI_EEPROM = build_src_filter=+ HAS_DWIN_E3V2|IS_DWIN_MARLINUI = build_src_filter=+ DWIN_CREALITY_LCD = build_src_filter=+ From 657929740f079810bf8f5b5bbed792ca341e8db0 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Sun, 28 Sep 2025 00:34:33 +0000 Subject: [PATCH 007/149] [cron] Bump distribution date (2025-09-28) --- Marlin/Version.h | 2 +- Marlin/src/inc/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Marlin/Version.h b/Marlin/Version.h index 18d0e5057e..9569ccdae9 100644 --- a/Marlin/Version.h +++ b/Marlin/Version.h @@ -41,7 +41,7 @@ * here we define this default string as the date where the latest release * version was tagged. */ -//#define STRING_DISTRIBUTION_DATE "2025-09-27" +//#define STRING_DISTRIBUTION_DATE "2025-09-28" /** * The protocol for communication to the host. Protocol indicates communication diff --git a/Marlin/src/inc/Version.h b/Marlin/src/inc/Version.h index f466eec073..a41839a612 100644 --- a/Marlin/src/inc/Version.h +++ b/Marlin/src/inc/Version.h @@ -42,7 +42,7 @@ * version was tagged. */ #ifndef STRING_DISTRIBUTION_DATE - #define STRING_DISTRIBUTION_DATE "2025-09-27" + #define STRING_DISTRIBUTION_DATE "2025-09-28" #endif /** From dc9e57464fbd89c851524f2282bfd9e5711cdbf4 Mon Sep 17 00:00:00 2001 From: David Buezas Date: Sun, 28 Sep 2025 02:42:33 +0200 Subject: [PATCH 008/149] =?UTF-8?q?=E2=9C=A8=20FTMotion=20Z=20shaping,=20a?= =?UTF-8?q?xis=20sync,=20axis=20smoothing=20(#28055)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/Configuration_adv.h | 74 ++-- .../src/HAL/LPC1768/inc/Conditionals_post.h | 2 +- Marlin/src/HAL/STM32/inc/Conditionals_post.h | 2 +- Marlin/src/HAL/STM32F1/MinSerial.cpp | 2 +- Marlin/src/core/language.h | 15 + Marlin/src/gcode/feature/ft_motion/M493.cpp | 343 +++++++++++++----- Marlin/src/gcode/feature/ft_motion/M494.cpp | 133 +++++++ Marlin/src/gcode/gcode.cpp | 3 + Marlin/src/gcode/gcode.h | 4 + Marlin/src/inc/Conditionals-1-axes.h | 1 + Marlin/src/inc/Conditionals-4-adv.h | 10 + Marlin/src/inc/Conditionals-5-post.h | 16 + Marlin/src/inc/SanityCheck.h | 6 + .../lcd/dogm/fontdata/fontdata_ISO10646_1.h | 2 +- Marlin/src/lcd/e3v2/proui/dwin.cpp | 4 +- Marlin/src/lcd/language/language_en.h | 2 + Marlin/src/lcd/menu/menu_motion.cpp | 130 ++++--- Marlin/src/module/ft_motion.cpp | 188 ++++++++-- Marlin/src/module/ft_motion.h | 106 ++++-- Marlin/src/module/ft_types.h | 59 ++- buildroot/tests/rambo | 2 +- 21 files changed, 845 insertions(+), 259 deletions(-) create mode 100644 Marlin/src/gcode/feature/ft_motion/M494.cpp diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 215f1f8026..722daf49c9 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -1143,49 +1143,73 @@ /** * Fixed-time-based Motion Control -- BETA FEATURE - * Enable/disable and set parameters with G-code M493. + * Enable/disable and set parameters with G-code M493 and M494. * See ft_types.h for named values used by FTM options. */ //#define FT_MOTION #if ENABLED(FT_MOTION) - //#define FTM_IS_DEFAULT_MOTION // Use FT Motion as the factory default? + //#define FTM_IS_DEFAULT_MOTION // Use FT Motion as the factory default? + //#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 and M494 parameters + #define FTM_DEFAULT_DYNFREQ_MODE dynFreqMode_DISABLED // Default mode of dynamic frequency calculation. (DISABLED, Z_BASED, MASS_BASED) - #define FTM_LINEAR_ADV_DEFAULT_ENA false // Default linear advance enable (true) or disable (false) - #define FTM_LINEAR_ADV_DEFAULT_K 0.0f // Default linear advance gain. (Acceleration-based scaling factor.) + #define FTM_LINEAR_ADV_DEFAULT_ENA false // Default linear advance enable (true) or disable (false) + #define FTM_LINEAR_ADV_DEFAULT_K 0.0f // Default linear advance gain. (Acceleration-based scaling factor.) #define FTM_DEFAULT_SHAPER_X ftMotionShaper_NONE // Default shaper mode on X axis (NONE, ZV, ZVD, ZVDD, ZVDDD, EI, 2HEI, 3HEI, MZV) - #define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers - #define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis - #define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis + #define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers + #define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis + #define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis #define FTM_DEFAULT_SHAPER_Y ftMotionShaper_NONE // Default shaper mode on Y axis - #define FTM_SHAPING_DEFAULT_FREQ_Y 37.0f // (Hz) Default peak frequency used by input shapers - #define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis - #define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis + #define FTM_SHAPING_DEFAULT_FREQ_Y 37.0f // (Hz) Default peak frequency used by input shapers + #define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis + #define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis - //#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 parameters + //#define FTM_SHAPER_Z // Include Z shaping support + #define FTM_DEFAULT_SHAPER_Z ftMotionShaper_NONE // Default shaper mode on Z axis + #define FTM_SHAPING_DEFAULT_FREQ_Z 21.0f // (Hz) Default peak frequency used by input shapers + #define FTM_SHAPING_ZETA_Z 0.03f // Zeta used by input shapers for Z axis + #define FTM_SHAPING_V_TOL_Z 0.05f // Vibration tolerance used by EI input shapers for Z axis + + //#define FTM_SHAPER_E // Include E shaping support + // Required to synchronise extruder with XYZ (better quality) + #define FTM_DEFAULT_SHAPER_E ftMotionShaper_NONE // Default shaper mode on Extruder axis + #define FTM_SHAPING_DEFAULT_FREQ_E 21.0f // (Hz) Default peak frequency used by input shapers + #define FTM_SHAPING_ZETA_E 0.03f // Zeta used by input shapers for E axis + #define FTM_SHAPING_V_TOL_E 0.05f // Vibration tolerance used by EI input shapers for E axis + + //#define FTM_SMOOTHING // Smoothing can reduce artifacts and make steppers quieter + // on sharp corners, but too much will round corners. + #if ENABLED(FTM_SMOOTHING) + #define FTM_MAX_SMOOTHING_TIME 0.10f // Maximum smoothing time (seconds), higher consumes more RAM. + // Increase smoothing time to reduce jerky motion, ghosting and noises. + #define FTM_SMOOTHING_TIME_X 0.00f // (s) Smoothing time for X axis. Zero means disabled. + #define FTM_SMOOTHING_TIME_Y 0.00f // (s) Smoothing time for Y axis + #define FTM_SMOOTHING_TIME_Z 0.00f // (s) Smoothing time for Z axis + #define FTM_SMOOTHING_TIME_E 0.02f // (s) Smoothing time for E axis. Prevents noise/skipping from LA by + // smoothing acceleration peaks, which may also smooth curved surfaces. + #endif /** * Advanced configuration */ - #define FTM_UNIFIED_BWS // DON'T DISABLE unless you use Ulendo FBS (not implemented) + #define FTM_UNIFIED_BWS // DON'T DISABLE unless you use Ulendo FBS (not implemented) #if ENABLED(FTM_UNIFIED_BWS) - #define FTM_BW_SIZE 100 // Unified Window and Batch size with a ratio of 2 + #define FTM_BW_SIZE 100 // Unified Window and Batch size with a ratio of 2 #else - #define FTM_WINDOW_SIZE 200 // Custom Window size for trajectory generation needed by Ulendo FBS - #define FTM_BATCH_SIZE 100 // Custom Batch size for trajectory generation needed by Ulendo FBS + #define FTM_WINDOW_SIZE 200 // Custom Window size for trajectory generation needed by Ulendo FBS + #define FTM_BATCH_SIZE 100 // Custom Batch size for trajectory generation needed by Ulendo FBS #endif - #define FTM_FS 1000 // (Hz) Frequency for trajectory generation. (Reciprocal of FTM_TS) - #define FTM_TS 0.001f // (s) Time step for trajectory generation. (Reciprocal of FTM_FS) + #define FTM_FS 1000 // (Hz) Frequency for trajectory generation. (Reciprocal of FTM_TS) #if DISABLED(COREXY) - #define FTM_STEPPER_FS 20000 // (Hz) Frequency for stepper I/O update + #define FTM_STEPPER_FS 20000 // (Hz) Frequency for stepper I/O update // Use this to adjust the time required to consume the command buffer. // Try increasing this value if stepper motion is choppy. - #define FTM_STEPPERCMD_BUFF_SIZE 3000 // Size of the stepper command buffers + #define FTM_STEPPERCMD_BUFF_SIZE 3000 // Size of the stepper command buffers #else // CoreXY motion needs a larger buffer size. These values are based on our testing. @@ -1193,17 +1217,7 @@ #define FTM_STEPPERCMD_BUFF_SIZE 6000 #endif - #define FTM_STEPS_PER_UNIT_TIME (FTM_STEPPER_FS / FTM_FS) // Interpolated stepper commands per unit time - #define FTM_MIN_TICKS ((STEPPER_TIMER_RATE) / (FTM_STEPPER_FS)) // Minimum stepper ticks between steps - - #define FTM_MIN_SHAPE_FREQ 10 // Minimum shaping frequency - #define FTM_RATIO (FTM_FS / FTM_MIN_SHAPE_FREQ) // Factor for use in FTM_ZMAX. DON'T CHANGE. - #define FTM_ZMAX (FTM_RATIO * 2) // Maximum delays for shaping functions (even numbers only!) - // Calculate as: - // ZV : FTM_RATIO / 2 - // ZVD, MZV : FTM_RATIO - // 2HEI : FTM_RATIO * 3 / 2 - // 3HEI : FTM_RATIO * 2 + #define FTM_MIN_SHAPE_FREQ 10 // (Hz) Minimum shaping frequency, lower consumes more RAM #endif // FT_MOTION /** diff --git a/Marlin/src/HAL/LPC1768/inc/Conditionals_post.h b/Marlin/src/HAL/LPC1768/inc/Conditionals_post.h index 0b03cb2aea..a1b4dd5099 100644 --- a/Marlin/src/HAL/LPC1768/inc/Conditionals_post.h +++ b/Marlin/src/HAL/LPC1768/inc/Conditionals_post.h @@ -29,6 +29,6 @@ // LPC1768 boards seem to lose steps when saving to EEPROM during print (issue #20785) // TODO: Which other boards are incompatible? -#if defined(MCU_LPC1768) && ENABLED(FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0 +#if ALL(MCU_LPC1768, FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0 #define PRINTCOUNTER_SYNC #endif diff --git a/Marlin/src/HAL/STM32/inc/Conditionals_post.h b/Marlin/src/HAL/STM32/inc/Conditionals_post.h index 6c97a635b3..8d72e720c1 100644 --- a/Marlin/src/HAL/STM32/inc/Conditionals_post.h +++ b/Marlin/src/HAL/STM32/inc/Conditionals_post.h @@ -29,6 +29,6 @@ #endif // Some STM32F4 boards may lose steps when saving to EEPROM during print (PR #17946) -#if defined(STM32F4xx) && ENABLED(FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0 +#if ALL(STM32F4xx, FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0 #define PRINTCOUNTER_SYNC #endif diff --git a/Marlin/src/HAL/STM32F1/MinSerial.cpp b/Marlin/src/HAL/STM32F1/MinSerial.cpp index 8fb9133254..0d9a611d7e 100644 --- a/Marlin/src/HAL/STM32F1/MinSerial.cpp +++ b/Marlin/src/HAL/STM32F1/MinSerial.cpp @@ -92,7 +92,7 @@ void install_min_serial() { HAL_min_serial_out = &TX; } -#if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them +#if NONE(DYNAMIC_VECTORTABLE, STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them extern "C" { __attribute__((naked)) void JumpHandler_ASM() { __asm__ __volatile__ ( diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h index 3a50fcdc6b..f2860ae7d4 100644 --- a/Marlin/src/core/language.h +++ b/Marlin/src/core/language.h @@ -358,6 +358,21 @@ #define STR_Z2 STR_C "2" #define STR_Z3 STR_C "3" #define STR_Z4 STR_C "4" +#if CORE_IS_XY || CORE_IS_XZ + #define STEPPER_A_NAME 'A' +#else + #define STEPPER_A_NAME 'X' +#endif +#if CORE_IS_XY || CORE_IS_YZ + #define STEPPER_B_NAME 'B' +#else + #define STEPPER_B_NAME 'Y' +#endif +#if CORE_IS_XZ || CORE_IS_YZ + #define STEPPER_C_NAME 'C' +#else + #define STEPPER_C_NAME 'Z' +#endif // // Endstop Names used by Endstops::report_states diff --git a/Marlin/src/gcode/feature/ft_motion/M493.cpp b/Marlin/src/gcode/feature/ft_motion/M493.cpp index 606d23179d..158ca27706 100644 --- a/Marlin/src/gcode/feature/ft_motion/M493.cpp +++ b/Marlin/src/gcode/feature/ft_motion/M493.cpp @@ -28,8 +28,9 @@ #include "../../../module/ft_motion.h" #include "../../../module/stepper.h" -void say_shaper_type(const AxisEnum a) { - SERIAL_ECHOPGM(" axis "); +void say_shaper_type(const AxisEnum a, bool &sep, const char axis_name) { + if (sep) SERIAL_ECHOPGM(" ; "); + SERIAL_CHAR(axis_name, '='); switch (ftMotion.cfg.shaper[a]) { default: break; case ftMotionShaper_ZV: SERIAL_ECHOPGM("ZV"); break; @@ -41,46 +42,36 @@ void say_shaper_type(const AxisEnum a) { case ftMotionShaper_3HEI: SERIAL_ECHOPGM("3 Hump EI"); break; case ftMotionShaper_MZV: SERIAL_ECHOPGM("MZV"); break; } - SERIAL_ECHOPGM(" shaping"); + sep = true; } -#if CORE_IS_XY || CORE_IS_XZ - #define AXIS_0_NAME "A" -#else - #define AXIS_0_NAME "X" -#endif -#if CORE_IS_XY || CORE_IS_YZ - #define AXIS_1_NAME "B" -#else - #define AXIS_1_NAME "Y" -#endif - void say_shaping() { // FT Enabled SERIAL_ECHO_TERNARY(ftMotion.cfg.active, "Fixed-Time Motion ", "en", "dis", "abled"); // FT Shaping + bool sep = false; + SERIAL_ECHOPGM(" ("); #if HAS_X_AXIS - if (AXIS_IS_SHAPING(X)) { - SERIAL_ECHOPGM(" with " AXIS_0_NAME); - say_shaper_type(X_AXIS); - } + if (AXIS_IS_SHAPING(X)) say_shaper_type(X_AXIS, sep, STEPPER_A_NAME); #endif #if HAS_Y_AXIS - if (AXIS_IS_SHAPING(Y)) { - SERIAL_ECHOPGM(" and with " AXIS_1_NAME); - say_shaper_type(Y_AXIS); - } + if (AXIS_IS_SHAPING(Y)) say_shaper_type(Y_AXIS, sep, STEPPER_B_NAME); #endif - - SERIAL_ECHOLNPGM("."); + #if ENABLED(FTM_SHAPER_Z) + if (AXIS_IS_SHAPING(Z)) say_shaper_type(Z_AXIS, sep, STEPPER_C_NAME); + #endif + #if ENABLED(FTM_SHAPER_E) + if (AXIS_IS_SHAPING(E)) say_shaper_type(E_AXIS, sep, 'E'); + #endif + SERIAL_ECHOLNPGM(")"); const bool z_based = TERN0(HAS_DYNAMIC_FREQ_MM, ftMotion.cfg.dynFreqMode == dynFreqMode_Z_BASED), g_based = TERN0(HAS_DYNAMIC_FREQ_G, ftMotion.cfg.dynFreqMode == dynFreqMode_MASS_BASED), dynamic = z_based || g_based; // FT Dynamic Frequency Mode - if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y)) { + if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y) || AXIS_IS_SHAPING(Z) || AXIS_IS_SHAPING(E)) { #if HAS_DYNAMIC_FREQ SERIAL_ECHOPGM("Dynamic Frequency Mode "); switch (ftMotion.cfg.dynFreqMode) { @@ -97,7 +88,8 @@ void say_shaping() { #endif #if HAS_X_AXIS - SERIAL_ECHO_TERNARY(dynamic, AXIS_0_NAME " ", "base dynamic", "static", " shaper frequency: "); + SERIAL_CHAR(STEPPER_A_NAME); + SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: "); SERIAL_ECHO(p_float_t(ftMotion.cfg.baseFreq.x, 2), F("Hz")); #if HAS_DYNAMIC_FREQ if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(ftMotion.cfg.dynFreqK.x, 2), F("Hz/"), z_based ? F("mm") : F("g")); @@ -106,13 +98,24 @@ void say_shaping() { #endif #if HAS_Y_AXIS - SERIAL_ECHO_TERNARY(dynamic, AXIS_1_NAME " ", "base dynamic", "static", " shaper frequency: "); + SERIAL_CHAR(STEPPER_B_NAME); + SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: "); SERIAL_ECHO(p_float_t(ftMotion.cfg.baseFreq.y, 2), F(" Hz")); #if HAS_DYNAMIC_FREQ if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(ftMotion.cfg.dynFreqK.y, 2), F("Hz/"), z_based ? F("mm") : F("g")); #endif SERIAL_EOL(); #endif + + #if ENABLED(FTM_SHAPER_Z) + SERIAL_CHAR(STEPPER_C_NAME); + SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: "); + SERIAL_ECHO(p_float_t(ftMotion.cfg.baseFreq.z, 2), F(" Hz")); + #if HAS_DYNAMIC_FREQ + if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(ftMotion.cfg.dynFreqK.z, 2), F("Hz/"), z_based ? F("mm") : F("g")); + #endif + SERIAL_EOL(); + #endif } #if HAS_EXTRUDERS @@ -129,26 +132,44 @@ void GcodeSuite::M493_report(const bool forReplay/*=true*/) { report_heading_etc(forReplay, F(STR_FT_MOTION)); const ft_config_t &c = ftMotion.cfg; - SERIAL_ECHOPGM(" M493 S", c.active); - #if HAS_X_AXIS - SERIAL_ECHOPGM(" A", c.baseFreq.x); - #if HAS_Y_AXIS - SERIAL_ECHOPGM(" B", c.baseFreq.y); - #endif - #endif - #if HAS_DYNAMIC_FREQ - SERIAL_ECHOPGM(" D", c.dynFreqMode); + SERIAL_ECHOLNPGM( + " M493 S", c.active #if HAS_X_AXIS - SERIAL_ECHOPGM(" F", c.dynFreqK.x); + , " A", c.baseFreq.x + #endif + #if HAS_Y_AXIS + , " B", c.baseFreq.y + #endif + #if ENABLED(FTM_SHAPER_Z) + , " C", c.baseFreq.z + #endif + #if ENABLED(FTM_SHAPER_E) + , " E", c.baseFreq.e + #endif + + #if HAS_DYNAMIC_FREQ + , " D", c.dynFreqMode + #if HAS_X_AXIS + , " F", c.dynFreqK.x + #endif #if HAS_Y_AXIS - SERIAL_ECHOPGM(" H", c.dynFreqK.y); + , " H", c.dynFreqK.y + #endif + #if ENABLED(FTM_SHAPER_Z) + , " L", c.dynFreqK.z + #endif + #if ENABLED(FTM_SHAPER_E) + , " O", c.dynFreqK.e #endif #endif - #endif - #if HAS_EXTRUDERS - SERIAL_ECHOPGM(" P", c.linearAdvEna, " K", c.linearAdvK); - #endif - SERIAL_EOL(); + + , " G", c.axis_sync_enabled + + #if HAS_EXTRUDERS + , " P", c.linearAdvEna, " K", c.linearAdvK + #endif + + ); } /** @@ -158,8 +179,8 @@ void GcodeSuite::M493_report(const bool forReplay/*=true*/) { * 0: Fixed-Time Motion OFF (Standard Motion) * 1: Fixed-Time Motion ON * - * X/Y Set the vibration compensator [input shaper] mode for X / Y axis. - * Users / slicers must remember to set the mode for both axes! + * X/Y/Z/E Set the vibration compensator [input shaper] mode for an axis. + * Users / slicers must remember to set the mode for all relevant axes! * 0: NONE : No input shaper * 1: ZV : Zero Vibration * 2: ZVD : Zero Vibration and Derivative @@ -174,20 +195,35 @@ void GcodeSuite::M493_report(const bool forReplay/*=true*/) { * * K Set Linear Advance gain * + * G Enable (1) or Disable (0) axis synchronization. + * * D Set Dynamic Frequency mode * 0: DISABLED * 1: Z-based (Requires a Z axis) * 2: Mass-based (Requires X and E axes) * - * A Set static/base frequency for the X axis - * F Set frequency scaling for the X axis - * I 0.0 Set damping ratio for the X axis - * Q 0.00 Set the vibration tolerance for the X axis + * A Set X static/base frequency + * F Set X frequency scaling + * I Set X damping ratio + * Q Set X vibration tolerance + * + * B Set Y static/base frequency + * H Set Y frequency scaling + * J Set Y damping ratio + * R Set Y vibration tolerance + * + * With FTM_SHAPING_Z: + * C Set Z static/base frequency + * L Set Z frequency scaling + * O Set Z damping ratio + * M Set Z vibration tolerance + * + * With FTM_SHAPING_E: + * W Set E static/base frequency + * O Set E frequency scaling + * U Set E damping ratio + * V Set E vibration tolerance * - * B Set static/base frequency for the Y axis - * H Set frequency scaling for the Y axis - * J 0.0 Set damping ratio for the Y axis - * R 0.00 Set the vibration tolerance for the Y axis */ void GcodeSuite::M493() { struct { bool update:1, report:1; } flag = { false }; @@ -205,7 +241,8 @@ void GcodeSuite::M493() { } } - #if HAS_X_AXIS + #if ANY(HAS_X_AXIS, HAS_Y_AXIS, FTM_SHAPER_Z, FTM_SHAPER_E) + auto set_shaper = [&](const AxisEnum axis, const char c) { const ftMotionShaper_t newsh = (ftMotionShaper_t)parser.value_byte(); if (newsh != ftMotion.cfg.shaper[axis]) { @@ -228,13 +265,20 @@ void GcodeSuite::M493() { return false; }; - if (parser.seenval('X') && set_shaper(X_AXIS, 'X')) return; // Parse 'X' mode parameter - + #if HAS_X_AXIS + if (parser.seenval('X') && set_shaper(X_AXIS, 'X')) return; // Parse 'X' mode parameter + #endif #if HAS_Y_AXIS if (parser.seenval('Y') && set_shaper(Y_AXIS, 'Y')) return; // Parse 'Y' mode parameter #endif + #if ENABLED(FTM_SHAPER_Z) + if (parser.seenval('Z') && set_shaper(Z_AXIS, 'Z')) return; // Parse 'Z' mode parameter + #endif + #if ENABLED(FTM_SHAPER_E) + if (parser.seenval('E') && set_shaper(E_AXIS, 'E')) return; // Parse 'E' mode parameter + #endif - #endif // HAS_X_AXIS + #endif // HAS_X_AXIS || HAS_Y_AXIS || FTM_SHAPER_Z || FTM_SHAPER_E #if HAS_EXTRUDERS @@ -259,11 +303,20 @@ void GcodeSuite::M493() { #endif // HAS_EXTRUDERS + // Parse '?' axis synchronization parameter. + if (parser.seen('?')) { + const bool enabled = parser.value_bool(); + if (enabled != ftMotion.cfg.axis_sync_enabled) { + ftMotion.cfg.axis_sync_enabled = enabled; + flag.report = true; + } + } + #if HAS_DYNAMIC_FREQ // Dynamic frequency mode parameter. if (parser.seenval('D')) { - if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y)) { + if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y) || AXIS_IS_SHAPING(Z) || AXIS_IS_SHAPING(E)) { const dynFreqMode_t val = dynFreqMode_t(parser.value_byte()); switch (val) { #if HAS_DYNAMIC_FREQ_MM @@ -295,7 +348,7 @@ void GcodeSuite::M493() { #if HAS_X_AXIS - // Parse frequency parameter (X axis). + // Parse X frequency parameter if (parser.seenval('A')) { if (AXIS_IS_SHAPING(X)) { const float val = parser.value_float(); @@ -305,59 +358,59 @@ void GcodeSuite::M493() { flag.update = flag.report = true; } else // Frequency out of range. - SERIAL_ECHOLNPGM("Invalid [", C('A'), "] frequency value."); + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_A_NAME), " [", C('A'), "] frequency value."); } else // Mode doesn't use frequency. - SERIAL_ECHOLNPGM("Wrong mode for [", C('A'), "] frequency."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " [", C('A'), "] frequency."); } #if HAS_DYNAMIC_FREQ - // Parse frequency scaling parameter (X axis). + // Parse X frequency scaling parameter if (parser.seenval('F')) { if (modeUsesDynFreq) { ftMotion.cfg.dynFreqK.x = parser.value_float(); flag.report = true; } else - SERIAL_ECHOLNPGM("Wrong mode for [", C('F'), "] frequency scaling."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " [", C('F'), "] frequency scaling."); } #endif - // Parse zeta parameter (X axis). + // Parse X zeta parameter if (parser.seenval('I')) { const float val = parser.value_float(); if (AXIS_IS_SHAPING(X)) { if (WITHIN(val, 0.01f, 1.0f)) { - ftMotion.cfg.zeta[0] = val; + ftMotion.cfg.zeta.x = val; flag.update = true; } else - SERIAL_ECHOLNPGM("Invalid X zeta [", C('I'), "] value."); // Zeta out of range. + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_A_NAME), " zeta [", C('I'), "] value."); // Zeta out of range } else - SERIAL_ECHOLNPGM("Wrong mode for zeta parameter."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " zeta parameter."); } - // Parse vtol parameter (X axis). + // Parse X vtol parameter if (parser.seenval('Q')) { const float val = parser.value_float(); if (AXIS_IS_EISHAPING(X)) { if (WITHIN(val, 0.00f, 1.0f)) { - ftMotion.cfg.vtol[0] = val; + ftMotion.cfg.vtol.x = val; flag.update = true; } else - SERIAL_ECHOLNPGM("Invalid X vtol [", C('Q'), "] value."); // VTol out of range. + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_A_NAME), " vtol [", C('Q'), "] value."); // VTol out of range. } else - SERIAL_ECHOLNPGM("Wrong mode for vtol parameter."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " vtol parameter."); } #endif // HAS_X_AXIS #if HAS_Y_AXIS - // Parse frequency parameter (Y axis). + // Parse Y frequency parameter if (parser.seenval('B')) { if (AXIS_IS_SHAPING(Y)) { const float val = parser.value_float(); @@ -366,56 +419,178 @@ void GcodeSuite::M493() { flag.update = flag.report = true; } else // Frequency out of range. - SERIAL_ECHOLNPGM("Invalid frequency [", C('B'), "] value."); + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_B_NAME), " frequency [", C('B'), "] value."); } else // Mode doesn't use frequency. - SERIAL_ECHOLNPGM("Wrong mode for [", C('B'), "] frequency."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " [", C('B'), "] frequency."); } #if HAS_DYNAMIC_FREQ - // Parse frequency scaling parameter (Y axis). + // Parse Y frequency scaling parameter if (parser.seenval('H')) { if (modeUsesDynFreq) { ftMotion.cfg.dynFreqK.y = parser.value_float(); flag.report = true; } else - SERIAL_ECHOLNPGM("Wrong mode for [", C('H'), "] frequency scaling."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " [", C('H'), "] frequency scaling."); } #endif - // Parse zeta parameter (Y axis). + // Parse Y zeta parameter if (parser.seenval('J')) { const float val = parser.value_float(); if (AXIS_IS_SHAPING(Y)) { if (WITHIN(val, 0.01f, 1.0f)) { - ftMotion.cfg.zeta[1] = val; + ftMotion.cfg.zeta.y = val; flag.update = true; } else - SERIAL_ECHOLNPGM("Invalid Y zeta [", C('J'), "] value."); // Zeta Out of range + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_B_NAME), " zeta [", C('J'), "] value."); // Zeta out of range } else - SERIAL_ECHOLNPGM("Wrong mode for zeta parameter."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " zeta parameter."); } - // Parse vtol parameter (Y axis). + // Parse Y vtol parameter if (parser.seenval('R')) { const float val = parser.value_float(); if (AXIS_IS_EISHAPING(Y)) { if (WITHIN(val, 0.00f, 1.0f)) { - ftMotion.cfg.vtol[1] = val; + ftMotion.cfg.vtol.y = val; flag.update = true; } else - SERIAL_ECHOLNPGM("Invalid Y vtol [", C('R'), "] value."); // VTol out of range. + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_B_NAME), " vtol [", C('R'), "] value."); // VTol out of range. } else - SERIAL_ECHOLNPGM("Wrong mode for vtol parameter."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " vtol parameter."); } #endif // HAS_Y_AXIS + #if ENABLED(FTM_SHAPER_Z) + + // Parse Z frequency parameter + if (parser.seenval('C')) { + if (AXIS_IS_SHAPING(Z)) { + const float val = parser.value_float(); + if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) { + ftMotion.cfg.baseFreq.z = val; + flag.update = flag.report = true; + } + else // Frequency out of range. + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_C_NAME), " frequency [", C('C'), "] value."); + } + else // Mode doesn't use frequency. + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " [", C('C'), "] frequency."); + } + + #if HAS_DYNAMIC_FREQ + // Parse Z frequency scaling parameter + if (parser.seenval('L')) { + if (modeUsesDynFreq) { + ftMotion.cfg.dynFreqK.z = parser.value_float(); + flag.report = true; + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " [", C('L'), "] frequency scaling."); + } + #endif + + // Parse Z zeta parameter + if (parser.seenval('O')) { + const float val = parser.value_float(); + if (AXIS_IS_SHAPING(Z)) { + if (WITHIN(val, 0.01f, 1.0f)) { + ftMotion.cfg.zeta.z = val; + flag.update = true; + } + else + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_C_NAME), " zeta [", C('O'), "] value."); // Zeta out of range + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " zeta parameter."); + } + + // Parse Z vtol parameter + if (parser.seenval('M')) { + const float val = parser.value_float(); + if (AXIS_IS_EISHAPING(Z)) { + if (WITHIN(val, 0.00f, 1.0f)) { + ftMotion.cfg.vtol.z = val; + flag.update = true; + } + else + SERIAL_ECHOLNPGM("?Invalid ", C(STEPPER_C_NAME), " vtol [", C('M'), "] value."); // VTol out of range. + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " vtol parameter."); + } + + #endif // FTM_SHAPER_Z + + #if ENABLED(FTM_SHAPER_E) + + // Parse E frequency parameter + if (parser.seenval('W')) { + if (AXIS_IS_SHAPING(E)) { + const float val = parser.value_float(); + if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) { + ftMotion.cfg.baseFreq.e = val; + flag.update = flag.report = true; + } + else // Frequency out of range. + SERIAL_ECHOLNPGM("?Invalid ", C('E'), " frequency [", C('W'), "] value."); + } + else // Mode doesn't use frequency. + SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " [", C('W'), "] frequency."); + } + + #if HAS_DYNAMIC_FREQ + // Parse E frequency scaling parameter + if (parser.seenval('O')) { + if (modeUsesDynFreq) { + ftMotion.cfg.dynFreqK.e = parser.value_float(); + flag.report = true; + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " [", C('O'), "] frequency scaling."); + } + #endif + + // Parse E zeta parameter + if (parser.seenval('U')) { + const float val = parser.value_float(); + if (AXIS_IS_SHAPING(E)) { + if (WITHIN(val, 0.01f, 1.0f)) { + ftMotion.cfg.zeta.e = val; + flag.update = true; + } + else + SERIAL_ECHOLNPGM("?Invalid ", C('E'), " zeta [", C('U'), "] value."); // Zeta out of range + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " zeta parameter."); + } + + // Parse E vtol parameter + if (parser.seenval('V')) { + const float val = parser.value_float(); + if (AXIS_IS_EISHAPING(E)) { + if (WITHIN(val, 0.00f, 1.0f)) { + ftMotion.cfg.vtol.e = val; + flag.update = true; + } + else + SERIAL_ECHOLNPGM("?Invalid ", C('E'), " vtol [", C('V'), "] value."); // VTol out of range. + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " vtol parameter."); + } + + #endif // FTM_SHAPER_E + if (flag.update) ftMotion.update_shaping_params(); if (flag.report) say_shaping(); diff --git a/Marlin/src/gcode/feature/ft_motion/M494.cpp b/Marlin/src/gcode/feature/ft_motion/M494.cpp new file mode 100644 index 0000000000..119a0c8dfc --- /dev/null +++ b/Marlin/src/gcode/feature/ft_motion/M494.cpp @@ -0,0 +1,133 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 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 "../../../inc/MarlinConfigPre.h" + +#if ENABLED(FTM_SMOOTHING) + +#include "../../gcode.h" +#include "../../../module/ft_motion.h" +#include "../../../module/stepper.h" + +void say_smoothing() { + #if HAS_X_AXIS + SERIAL_ECHOLN(F(" "), C('X'), F(" smoothing time: "), p_float_t(ftMotion.cfg.smoothingTime.x, 3), C('s')); + #endif + #if HAS_Y_AXIS + SERIAL_ECHOLN(F(" "), C('Y'), F(" smoothing time: "), p_float_t(ftMotion.cfg.smoothingTime.y, 3), C('s')); + #endif + #if HAS_Z_AXIS + SERIAL_ECHOLN(F(" "), C('Z'), F(" smoothing time: "), p_float_t(ftMotion.cfg.smoothingTime.z, 3), C('s')); + #endif + #if HAS_EXTRUDERS + SERIAL_ECHOLN(F(" "), C('E'), F(" smoothing time: "), p_float_t(ftMotion.cfg.smoothingTime.e, 3), C('s')); + #endif +} + +void GcodeSuite::M494_report(const bool forReplay/*=true*/) { + TERN_(MARLIN_SMALL_BUILD, return); + + report_heading_etc(forReplay, F("FTM Smoothing")); + const ft_config_t &c = ftMotion.cfg; + SERIAL_ECHOLN(F(" M494") + #if HAS_X_AXIS + , F(" X"), c.smoothingTime.x + #endif + #if HAS_Y_AXIS + , F(" Y"), c.smoothingTime.y + #endif + #if HAS_Z_AXIS + , F(" Z"), c.smoothingTime.z + #endif + #if HAS_EXTRUDERS + , F(" E"), c.smoothingTime.e + #endif + ); +} + +/** + * M494: Set Fixed-time Motion Control Smoothing parameters + * + * Parameters: + * X