From d9ed6f0e5f7f72c6b79492ce7d06866916d7dc1f Mon Sep 17 00:00:00 2001 From: David Buezas Date: Tue, 30 Dec 2025 09:40:40 +0100 Subject: [PATCH 01/39] =?UTF-8?q?=F0=9F=A9=B9=20Ensure=20safe=20change=20o?= =?UTF-8?q?f=20XYZ=20smoothing=20(#28247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/module/ft_motion.cpp | 19 +++++++++++-------- Marlin/src/module/ft_motion.h | 16 ++++++++++++++-- Marlin/src/module/ft_motion/shaping.h | 5 +++++ Marlin/src/module/ft_motion/smoothing.cpp | 6 +++--- Marlin/src/module/ft_motion/smoothing.h | 5 +++++ Marlin/src/module/ft_motion/stepping.h | 13 ++++++------- 6 files changed, 44 insertions(+), 20 deletions(-) diff --git a/Marlin/src/module/ft_motion.cpp b/Marlin/src/module/ft_motion.cpp index f6113c398e..f9b7c86998 100644 --- a/Marlin/src/module/ft_motion.cpp +++ b/Marlin/src/module/ft_motion.cpp @@ -70,7 +70,8 @@ AxisBits FTMotion::moving_axis_flags, // These axes are moving in the // Block data variables. xyze_pos_t FTMotion::startPos, // (mm) Start position of block - FTMotion::endPos_prevBlock = { 0.0f }; // (mm) End position of previous block + FTMotion::endPos_prevBlock = { 0.0f }, // (mm) End position of previous block + FTMotion::last_target_traj = { 0.0f }; // (mm) Last target position after shaping and smoothing xyze_float_t FTMotion::ratio; // (ratio) Axis move ratio of block float FTMotion::tau = 0.0f; // (s) Time since start of block bool FTMotion::fastForwardUntilMotion = false; // Fast forward time if there is no motion @@ -524,13 +525,14 @@ xyze_float_t FTMotion::calc_traj_point(const float dist) { // Approximate Gaussian smoothing via chained EMAs auto _smoothen = [&](const AxisEnum axis, axis_smoothing_t &smoo) { - if (smoo.alpha <= 0.0f) return; - float smooth_val = traj_coords[axis]; - for (uint8_t _i = 0; _i < FTM_SMOOTHING_ORDER; ++_i) { - smoo.smoothing_pass[_i] += (smooth_val - smoo.smoothing_pass[_i]) * smoo.alpha; - smooth_val = smoo.smoothing_pass[_i]; + if (smoo.alpha != 1.0f) { + float smooth_val = traj_coords[axis]; + for (uint8_t _i = 0; _i < FTM_SMOOTHING_ORDER; ++_i) { + smoo.smoothing_pass[_i] += (smooth_val - smoo.smoothing_pass[_i]) * smoo.alpha; + smooth_val = smoo.smoothing_pass[_i]; + } + traj_coords[axis] = smooth_val; } - traj_coords[axis] = smooth_val; }; #define _SMOOTHEN(A) _smoothen(_AXIS(A), smoothing.A); @@ -604,7 +606,7 @@ void FTMotion::fill_stepper_plan_buffer() { // Get distance from trajectory generator xyze_float_t traj_coords = calc_traj_point(currentGenerator->getDistanceAtTime(tau)); - if (fastForwardUntilMotion && traj_coords == startPos) { + if (fastForwardUntilMotion && traj_coords == last_target_traj) { // Axis synchronization delays all axes. When coming from a reset, there is a ramp up time filling all buffers. // If the slowest axis doesn't move and it isn't smoothened, this time can be skipped. // It eliminates idle time when changing smoothing time or shapers and speeds up homing and bed leveling. @@ -614,6 +616,7 @@ void FTMotion::fill_stepper_plan_buffer() { // Calculate and store stepper plan in buffer stepping_enqueue(traj_coords); } + last_target_traj = traj_coords; } } diff --git a/Marlin/src/module/ft_motion.h b/Marlin/src/module/ft_motion.h index 42dab9951d..1d2849f289 100644 --- a/Marlin/src/module/ft_motion.h +++ b/Marlin/src/module/ft_motion.h @@ -326,7 +326,8 @@ class FTMotion { private: // Block data variables. static xyze_pos_t startPos, // (mm) Start position of block - endPos_prevBlock; // (mm) End position of previous block + endPos_prevBlock, // (mm) End position of previous block + last_target_traj; // (mm) Last target position after shaping and smoothing static xyze_float_t ratio; // (ratio) Axis move ratio of block static float tau; // (s) Time since start of block static bool fastForwardUntilMotion; // Fast forward time if there is no motion @@ -375,8 +376,19 @@ class FTMotion { // Synchronize and reset motion prior to parameter changes friend void ft_config_t::prep_for_shaper_change(); static void prep_for_shaper_change() { + // planner.synchronize guarantees that motion reached a standstill with no echoes pending execution (including a runout block) planner.synchronize(); - reset(); + // Due to smoothing, the end position may not have been reached exactly. + // This is normally fine, but if smoothing time changes, and we assume it was reached, + // it may cause discontinuities. + // Therefore, set the next starting position to the exact reached position. + endPos_prevBlock = last_target_traj; + // We now know that we are not moving and there are no pending echoes, + // so set all shaping buffers to current position in case the new smoothing/shaping + // parameters force input shaping to look in a past position for echoes. + shaping.fill(endPos_prevBlock); + TERN_(FTM_SMOOTHING, smoothing.fill(endPos_prevBlock)); + fastForwardUntilMotion = true; } // Buffers diff --git a/Marlin/src/module/ft_motion/shaping.h b/Marlin/src/module/ft_motion/shaping.h index 0140a969ba..88b02fbdac 100644 --- a/Marlin/src/module/ft_motion/shaping.h +++ b/Marlin/src/module/ft_motion/shaping.h @@ -151,4 +151,9 @@ typedef struct Shaping { SHAPED_MAP(_RESET_ZI); zi_idx = 0; } + void fill(const xyze_float_t pos) { + #define _FILL_ZI(A) for (uint32_t i = 0; i < ftm_zmax; i++) A.d_zi[i] = pos.A; + SHAPED_MAP(_FILL_ZI); + #undef _FILL_ZI + } } shaping_t; diff --git a/Marlin/src/module/ft_motion/smoothing.cpp b/Marlin/src/module/ft_motion/smoothing.cpp index f40bd828aa..3708629271 100644 --- a/Marlin/src/module/ft_motion/smoothing.cpp +++ b/Marlin/src/module/ft_motion/smoothing.cpp @@ -28,12 +28,12 @@ // Set smoothing time and recalculate alpha and delay. void AxisSmoothing::set_time(const float s_time) { - if (s_time > 0.001f) { - alpha = 1.0f - expf(-(FTM_TS) * (FTM_SMOOTHING_ORDER) / s_time ); + if (s_time >= 0.0001f) { + alpha = 1.0f - expf(-(FTM_TS) * (FTM_SMOOTHING_ORDER) / s_time); delay_samples = s_time * FTM_FS; } else { - alpha = 0.0f; + alpha = 1.0f; delay_samples = 0; } } diff --git a/Marlin/src/module/ft_motion/smoothing.h b/Marlin/src/module/ft_motion/smoothing.h index e3f9962a09..00be0f8892 100644 --- a/Marlin/src/module/ft_motion/smoothing.h +++ b/Marlin/src/module/ft_motion/smoothing.h @@ -55,4 +55,9 @@ typedef struct Smoothing { LOGICAL_AXIS_MAP(_CLEAR); #undef _CLEAR } + void fill(const xyze_float_t pos) { + #define _FILL_SMO(A) for (uint32_t i = 0; i < FTM_SMOOTHING_ORDER; i++) A.smoothing_pass[i] = pos.A; + LOGICAL_AXIS_MAP(_FILL_SMO); + #undef _FILL_SMO + } } smoothing_t; diff --git a/Marlin/src/module/ft_motion/stepping.h b/Marlin/src/module/ft_motion/stepping.h index b7cbe8391b..636945ac06 100644 --- a/Marlin/src/module/ft_motion/stepping.h +++ b/Marlin/src/module/ft_motion/stepping.h @@ -35,18 +35,11 @@ FORCE_INLINE constexpr uint32_t a_times_b_shift_16(const uint32_t a, const uint3 constexpr int CLZ32(const uint32_t v, const int c=0) { return v ? (TEST32(v, 31)) ? c : CLZ32(v << 1, c + 1) : 32; } -#define FTM_NEVER uint32_t(UINT16_MAX) // Reserved number to indicate "no ticks in this frame" (FRAME_TICKS_FP+1 would work too) constexpr uint32_t FRAME_TICKS = STEPPER_TIMER_RATE / FTM_FS; // Timer ticks per frame constexpr uint32_t FTM_Q_INT = 32u - CLZ32(FRAME_TICKS + 1U); // Bits to represent the integer part of the max value (duration of a frame, +1 one for FTM_NEVER). constexpr uint32_t FTM_Q = 16u - FTM_Q_INT; // uint16 interval fractional bits. // Intervals buffer has fixed point numbers with the point on this position -static_assert(FRAME_TICKS < FTM_NEVER, "(STEPPER_TIMER_RATE / FTM_FS) (" STRINGIFY(STEPPER_TIMER_RATE) " / " STRINGIFY(FTM_FS) ") must be < " STRINGIFY(FTM_NEVER) " to fit 16-bit fixed-point numbers."); - -// Sanity check -static_assert(POW(2, 16 - FTM_Q) > FRAME_TICKS, "FRAME_TICKS in Q format should fit in a uint16"); -static_assert(POW(2, 16 - FTM_Q - 1) <= FRAME_TICKS, "A smaller FTM_Q would still alow a FRAME_TICKS in Q format to fit in a uint16"); - // The _FP and _fp suffixes mean the number is in fixed point format with the point at the FTM_Q position. // See: https://en.wikipedia.org/wiki/Fixed-point_arithmetic // e.g., number_fp = number << FTM_Q @@ -54,6 +47,12 @@ static_assert(POW(2, 16 - FTM_Q - 1) <= FRAME_TICKS, "A smaller FTM_Q would stil constexpr uint32_t ONE_FP = 1UL << FTM_Q; // Number 1 in fixed point format constexpr uint32_t FP_FLOOR_MASK = ~(ONE_FP - 1); // Bit mask to do FLOOR in fixed point constexpr uint32_t FRAME_TICKS_FP = FRAME_TICKS << FTM_Q; // Ticks in a frame in fixed point +constexpr uint32_t FTM_NEVER = FRAME_TICKS_FP + 1; // Reserved number to indicate "no ticks in this frame", also max isr wait on empty stepper buffer + +// Sanity check +static_assert(FRAME_TICKS < FTM_NEVER, "(STEPPER_TIMER_RATE / FTM_FS) (" STRINGIFY(STEPPER_TIMER_RATE) " / " STRINGIFY(FTM_FS) ") must be < " STRINGIFY(FTM_NEVER) " to fit 16-bit fixed-point numbers."); +static_assert(POW(2, 16 - FTM_Q) > FRAME_TICKS, "FRAME_TICKS in Q format should fit in a uint16"); +static_assert(POW(2, 16 - FTM_Q - 1) <= FRAME_TICKS, "A smaller FTM_Q would still alow a FRAME_TICKS in Q format to fit in a uint16"); typedef struct stepper_plan { AxisBits dir_bits; From 04416f8cc3033e794f52440e5175badfa6d5a014 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Tue, 30 Dec 2025 12:13:53 +0000 Subject: [PATCH 02/39] [cron] Bump distribution date (2025-12-30) --- 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 3ebdc70bf4..ab0b88b689 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-12-25" +//#define STRING_DISTRIBUTION_DATE "2025-12-30" /** * 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 d73463086b..a5c36dcfc9 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-12-25" + #define STRING_DISTRIBUTION_DATE "2025-12-30" #endif /** From 777bc842de1e81858985200cf024967036db36ae Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Mon, 5 Jan 2026 00:17:01 +0100 Subject: [PATCH 03/39] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Optimize=20FTM=20res?= =?UTF-8?q?onance=20test=20(#28263)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Scott Lahteine --- .../module/ft_motion/resonance_generator.cpp | 43 ++++++++++++++----- .../module/ft_motion/resonance_generator.h | 38 +++++++++------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/Marlin/src/module/ft_motion/resonance_generator.cpp b/Marlin/src/module/ft_motion/resonance_generator.cpp index 2c2297e312..40fba11d67 100644 --- a/Marlin/src/module/ft_motion/resonance_generator.cpp +++ b/Marlin/src/module/ft_motion/resonance_generator.cpp @@ -50,13 +50,33 @@ void ResonanceGenerator::reset() { done = false; } +// Fast sine approximation +float ResonanceGenerator::fast_sin(float x) { + static constexpr float INV_TAU = (1.0f / M_TAU); + + // Reduce the angle to [-π, π] + const float y = x * INV_TAU; // Multiples of 2π + int k = static_cast(y); // Truncates toward zero + + // Negative? The truncation is one too high. + if (y < 0.0f) --k; // Correct for negatives + + const float r = x - k * M_TAU; // -π <= r <= π + + // Cheap polynomial approximation of sin(r) + return r * (1.27323954f - 0.405284735f * ABS(r)); +} + void ResonanceGenerator::fill_stepper_plan_buffer() { - xyze_float_t traj_coords = {}; + xyze_float_t traj_coords = rt_params.start_pos; + + const float amplitude_precalc = (rt_params.amplitude_correction * rt_params.accel_per_hz * 0.25f) / sq(M_PI); + + float rt_factor = rt_time * M_TAU; while (!ftMotion.stepping.is_full()) { // Calculate current frequency - // Logarithmic approach with duration per octave - const float freq = rt_params.min_freq * powf(2.0f, rt_time / rt_params.octave_duration); + const float freq = getFrequencyFromTimeline(); if (freq > rt_params.max_freq) { done = true; return; @@ -65,22 +85,23 @@ void ResonanceGenerator::fill_stepper_plan_buffer() { // Amplitude based on a sinusoidal wave : A = accel / (4 * PI^2 * f^2) //const float accel_magnitude = rt_params.accel_per_hz * freq; //const float amplitude = rt_params.amplitude_correction * accel_magnitude / (4.0f * sq(M_PI) * sq(freq)); - const float amplitude = rt_params.amplitude_correction * rt_params.accel_per_hz * 0.25f / (sq(M_PI) * freq); + const float amplitude = amplitude_precalc / freq; // Phase in radians - const float phase = 2.0f * M_PI * freq * rt_time; + const float phase = freq * rt_factor; // Position Offset : between -A and +A - const float pos_offset = amplitude * sinf(phase); + const float pos_offset = amplitude * fast_sin(phase); - // Set base position and apply offset to the test axis in one step for all axes - #define _SET_TRAJ(A) traj_coords.A = rt_params.start_pos.A + (rt_params.axis == A##_AXIS ? pos_offset : 0.0f); - LOGICAL_AXIS_MAP(_SET_TRAJ); + // Resonate the axis being tested + traj_coords[rt_params.axis] = rt_params.start_pos[rt_params.axis] + pos_offset; + + // Increment for the next point (before calling out) + rt_time += FTM_TS; + rt_factor += FTM_TS * M_TAU; // Store in buffer ftMotion.stepping_enqueue(traj_coords); - // Increment time for the next point - rt_time += FTM_TS; } } diff --git a/Marlin/src/module/ft_motion/resonance_generator.h b/Marlin/src/module/ft_motion/resonance_generator.h index 16156693dd..5acc1dc415 100644 --- a/Marlin/src/module/ft_motion/resonance_generator.h +++ b/Marlin/src/module/ft_motion/resonance_generator.h @@ -25,14 +25,18 @@ #include +#ifndef M_TAU + #define M_TAU (2.0f * M_PI) +#endif + typedef struct FTMResonanceTestParams { - AxisEnum axis = NO_AXIS_ENUM; // Axis to test - float min_freq = 5.0f; // Minimum frequency [Hz] - float max_freq = 100.0f; // Maximum frequency [Hz] - float octave_duration = 40.0f; // Octave duration for logarithmic progression - float accel_per_hz = 60.0f; // Acceleration per Hz [mm/sec/Hz] or [g/Hz] - int16_t amplitude_correction = 5; // Amplitude correction factor - xyze_pos_t start_pos; // Initial stepper position + AxisEnum axis = NO_AXIS_ENUM; // Axis to test + float min_freq = 5.0f; // Minimum frequency [Hz] + float max_freq = 100.0f; // Maximum frequency [Hz] + float octave_duration = 40.0f; // Octave duration for logarithmic progression + float accel_per_hz = 60.0f; // Acceleration per Hz [mm/sec/Hz] or [g/Hz] + int16_t amplitude_correction = 5; // Amplitude correction factor + xyze_pos_t start_pos; // Initial stepper position } ftm_resonance_test_params_t; class ResonanceGenerator { @@ -53,21 +57,25 @@ class ResonanceGenerator { done = false; } + // Return frequency based on timeline float getFrequencyFromTimeline() { - return (rt_params.min_freq * powf(2.0f, timeline / rt_params.octave_duration)); // Return frequency based on timeline + // Logarithmic approach with duration per octave + return rt_params.min_freq * 2.0f * exp2f((rt_time / rt_params.octave_duration) - 1); } void fill_stepper_plan_buffer(); // Fill stepper plan buffer with trajectory points + void setActive(const bool state) { active = state; } bool isActive() const { return active; } - bool isDone() const { return done; } - void setActive(bool state) { active = state; } - void setDone(bool state) { done = state; } - void abort(); // Abort resonance test + void setDone(const bool state) { done = state; } + bool isDone() const { return done; } + + void abort(); // Abort resonance test private: - static float rt_time; // Test timer - static bool active; // Resonance test active - static bool done; // Resonance test done + float fast_sin(float x); // Fast sine approximation + static float rt_time; // Test timer + static bool active; // Resonance test active + static bool done; // Resonance test done }; From 5cb9474a7dca11e9c8b21935105849e8b628dd53 Mon Sep 17 00:00:00 2001 From: ellensp <530024+ellensp@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:42:38 +1300 Subject: [PATCH 04/39] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Old?= =?UTF-8?q?=20macros=20cleanup=20(2)=20(#28262)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Followup to ba8b685 --- Marlin/src/inc/Conditionals-1-axes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/inc/Conditionals-1-axes.h b/Marlin/src/inc/Conditionals-1-axes.h index d81aa2c746..fd917322f0 100644 --- a/Marlin/src/inc/Conditionals-1-axes.h +++ b/Marlin/src/inc/Conditionals-1-axes.h @@ -293,7 +293,7 @@ #if NUM_AXES >= 1 #define HAS_X_AXIS 1 #define HAS_A_AXIS 1 - #if NUM_AXES >= XY + #if NUM_AXES >= 2 #define HAS_Y_AXIS 1 #define HAS_B_AXIS 1 #if NUM_AXES >= 3 From 868843fc890a7f89391d45dd3359f648dd472aa4 Mon Sep 17 00:00:00 2001 From: ellensp <530024+ellensp@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:45:51 +1300 Subject: [PATCH 05/39] =?UTF-8?q?=F0=9F=94=A7=20Creality=204.2.5=20FAN1/2?= =?UTF-8?q?=20and=20LED=20pins=20(#28232)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/pins/stm32f1/pins_CREALITY_V425.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Marlin/src/pins/stm32f1/pins_CREALITY_V425.h b/Marlin/src/pins/stm32f1/pins_CREALITY_V425.h index a225fe11be..42875713f1 100644 --- a/Marlin/src/pins/stm32f1/pins_CREALITY_V425.h +++ b/Marlin/src/pins/stm32f1/pins_CREALITY_V425.h @@ -68,6 +68,13 @@ // #define HEATER_0_PIN PA0 // HEATER1 #define HEATER_BED_PIN PA1 // HOT BED -#define FAN0_PIN PA2 // FAN +#define FAN0_PIN PA2 // FAN0 +#define FAN1_PIN PC1 // FAN1 +#define FAN2_PIN PC0 // FAN2 + +// +// Misc. Functions +// +#define LED_PIN PC14 #include "pins_CREALITY_V4.h" From 4489832bf392697f37007795d3dc02d7404ea5a8 Mon Sep 17 00:00:00 2001 From: ellensp <530024+ellensp@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:52:38 +1300 Subject: [PATCH 06/39] =?UTF-8?q?=E2=9C=A8=20AtomStack=20FB5=20V2=20board?= =?UTF-8?q?=20(#28242)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/core/boards.h | 1 + Marlin/src/pins/pins.h | 2 + .../src/pins/stm32f1/pins_ATOMSTACK_FB5_V2.h | 198 ++++++++++++++++++ ini/stm32f1.ini | 16 ++ 4 files changed, 217 insertions(+) create mode 100644 Marlin/src/pins/stm32f1/pins_ATOMSTACK_FB5_V2.h diff --git a/Marlin/src/core/boards.h b/Marlin/src/core/boards.h index 6c51cb01a4..b99fcad89a 100644 --- a/Marlin/src/core/boards.h +++ b/Marlin/src/core/boards.h @@ -419,6 +419,7 @@ #define BOARD_MD_D301 5068 // Mingda D2 DZ301 V1.0 (STM32F103ZE) #define BOARD_VOXELAB_AQUILA 5069 // Voxelab Aquila V1.0.0/V1.0.1 (GD32F103RC / N32G455RE / STM32F103RE) #define BOARD_SPRINGER_CONTROLLER 5070 // ORCA 3D SPRINGER Modular Controller (STM32F103VC) +#define BOARD_ATOMSTACK_FB5_V2 5071 // Atomstack FB5 V2.0 (STM32F103RCT6) // // ARM Cortex-M4F diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h index a89f1e1d52..3cc1849acc 100644 --- a/Marlin/src/pins/pins.h +++ b/Marlin/src/pins/pins.h @@ -686,6 +686,8 @@ #include "gd32f1/pins_VOXELAB_AQUILA.h" // GD32F1, N32G4, STM32F1 env:GD32F103RC_voxelab_maple env:N32G455RE_voxelab_maple env:STM32F103RE_creality_maple env:STM32F103RE_creality #elif MB(SPRINGER_CONTROLLER) #include "stm32f1/pins_ORCA_3D_SPRINGER.h" // STM32F1 env:STM32F103VC_orca3d +#elif MB(ATOMSTACK_FB5_V2) + #include "stm32f1/pins_ATOMSTACK_FB5_V2.h" // STM32F1 env:STM32F103RC_atomstack // // ARM Cortex-M4F diff --git a/Marlin/src/pins/stm32f1/pins_ATOMSTACK_FB5_V2.h b/Marlin/src/pins/stm32f1/pins_ATOMSTACK_FB5_V2.h new file mode 100644 index 0000000000..220fa116e9 --- /dev/null +++ b/Marlin/src/pins/stm32f1/pins_ATOMSTACK_FB5_V2.h @@ -0,0 +1,198 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2026 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 + +/** + * AtomStack FB5 V2.0 (STM32F103RCT6) common board pin assignments + */ + +#include "env_validate.h" + +#ifndef BOARD_INFO_NAME + #define BOARD_INFO_NAME "AtomStack FB5 V2.0" +#endif +#ifndef DEFAULT_MACHINE_NAME + #define DEFAULT_MACHINE_NAME "ATOMSTACK Cambrian" +#endif + +// +// EEPROM +// +#if ANY(NO_EEPROM_SELECTED, FLASH_EEPROM_EMULATION) + #define FLASH_EEPROM_EMULATION + #define EEPROM_PAGE_SIZE (0x800U) // 2K + #define EEPROM_START_ADDRESS (0x8000000UL + (STM32_FLASH_SIZE) * 1024UL - (EEPROM_PAGE_SIZE) * 2UL) + #define MARLIN_EEPROM_SIZE EEPROM_PAGE_SIZE // 2K +#endif + +// +// Servos +// +#define SERVO0_PIN PA3 + +// +// Limit Switches +// +#define X_STOP_PIN PA4 +#define Y_STOP_PIN PA5 +#define Z_STOP_PIN PC6 + +// +// Z Probe +// +#ifndef Z_MIN_PROBE_PIN + #define Z_MIN_PROBE_PIN PB1 +#endif + +// +// Probe enable +// +#if ENABLED(PROBE_ENABLE_DISABLE) && !defined(PROBE_ENABLE_PIN) + #define PROBE_ENABLE_PIN SERVO0_PIN +#endif + +// +// Steppers +// +#define X_STEP_PIN PC0 +#define X_DIR_PIN PB2 +#define X_ENABLE_PIN PC13 + +#define Y_STEP_PIN PC2 +#define Y_DIR_PIN PB9 +#define Y_ENABLE_PIN PB12 + +#ifndef Z_STEP_PIN + #define Z_STEP_PIN PB7 +#endif +#ifndef Z_DIR_PIN + #define Z_DIR_PIN PB6 +#endif +#define Z_ENABLE_PIN PB8 + +#define E0_STEP_PIN PB4 +#define E0_DIR_PIN PB3 +#define E0_ENABLE_PIN PB5 + +#if HAS_TMC_UART + /** + * TMC2208/TMC2209 stepper drivers + * + * Hardware serial communication ports. + * If undefined software serial is used according to the pins below + */ + //#define X_HARDWARE_SERIAL MSerial1 + //#define Y_HARDWARE_SERIAL MSerial1 + //#define Z_HARDWARE_SERIAL MSerial1 + //#define E0_HARDWARE_SERIAL MSerial1 + + #define X_SERIAL_TX_PIN PC7 + #define Y_SERIAL_TX_PIN PD2 + #define Z_SERIAL_TX_PIN PC12 + #define E0_SERIAL_TX_PIN PC11 + + // Reduce baud rate to improve software serial reliability + #ifndef TMC_BAUD_RATE + #define TMC_BAUD_RATE 19200 + #endif + +#endif // HAS_TMC_UART + +// +// Heaters / Fans +// +#define HEATER_0_PIN PC9 +#define HEATER_BED_PIN PC8 + +#define FAN0_PIN PA8 + +// +// Temperature Sensors +// +#define TEMP_0_PIN PA0 // TH1 +#define TEMP_BED_PIN PA1 // TB + +// +// Filament Runout Sensor +// +//#define FIL_RUNOUT_PIN PB10 // MT_DET (Mechanical Limit Detector) +#define FIL_RUNOUT_PIN PC15 // E0-STOP + +// +// Power-loss Detection +// +#ifndef POWER_LOSS_PIN + //#define POWER_LOSS_PIN PC12 // Power Loss Detection: PWR-DET +#endif + +// +// USB connect control +// +#ifndef USB_CONNECT_PIN + #define USB_CONNECT_PIN PB11 + #define USB_CONNECT_INVERTING false +#endif + +/** + * AtomStack FB5 V2.0 + * ------ + * 5V | 1 2 | 5V + * GND | 3 4 | GND + * NC | 5 6 | PA9 (TX1) + * NC | 7 8 | PA10 (RX1) + * ------ + * AUX1 + */ + +/** + * DWG80480C043_02WTCZ12 4.3 Inch Smart LCD [800x480] pinout + * --- + * 5V | 1 | + * NC | 2 | + * (TX1) PA9 | 3 | + * (RX1)PA10 | 4 | + * NC | 5 | + * GND | 6 | + * --- + * DWIN (plug) + */ + +// LED driving pin +#ifndef BOARD_NEOPIXEL_PIN + #define BOARD_NEOPIXEL_PIN PA2 +#endif + +// +// SD Card +// +#define SDCARD_CONNECTION ONBOARD +#define ONBOARD_SPI_DEVICE 2 // Maple +#define ONBOARD_SD_CS_PIN SD_SS_PIN +#define SD_DETECT_PIN PC10 +#define NO_SD_HOST_DRIVE + +// TODO: This is the only way to set SPI for SD on STM32 (for now) +#define ENABLE_SPI2 +#define SD_SCK_PIN PB13 +#define SD_MISO_PIN PB14 +#define SD_MOSI_PIN PB15 +#define SD_SS_PIN PA15 diff --git a/ini/stm32f1.ini b/ini/stm32f1.ini index 58b33afaaf..3e9cd3b058 100644 --- a/ini/stm32f1.ini +++ b/ini/stm32f1.ini @@ -115,6 +115,22 @@ build_unflags = ${common_STM32F103RC_variant.build_unflags} -DUSBCON -DUSBD_USE_CDC debug_tool = stlink +# +# AtomStack FB5 V2.0 (STM32F103RCT6 ARM Cortex-M3) +# +[env:STM32F103RC_atomstack] +extends = common_STM32F103RC_variant +board_build.encrypt_mks = Robin_e3.bin +board_build.offset = 0x5000 +board_upload.offset_address = 0x08005000 +build_flags = ${common_STM32F103RC_variant.build_flags} + -DTIMER_SERVO=TIM5 + -DUSE_USB_FS + -DUSBD_IRQ_PRIO=5 + -DUSBD_IRQ_SUBPRIO=6 + -DUSBD_USE_CDC_MSC +;upload_protocol = stlink + # # Creality (STM32F103Rx) # From e61469f8c1c588a7debf79cc5868ad0dc50f1e03 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Mon, 5 Jan 2026 00:38:17 +0000 Subject: [PATCH 07/39] [cron] Bump distribution date (2026-01-05) --- 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 ab0b88b689..2e0b47fcfe 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-12-30" +//#define STRING_DISTRIBUTION_DATE "2026-01-05" /** * 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 a5c36dcfc9..798fa2c598 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-12-30" + #define STRING_DISTRIBUTION_DATE "2026-01-05" #endif /** From 65b62a900f5c6c234040927fd3162e09b90fd163 Mon Sep 17 00:00:00 2001 From: ellensp <530024+ellensp@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:20:53 +1300 Subject: [PATCH 08/39] =?UTF-8?q?=F0=9F=94=A8=20More=20specific=20preproce?= =?UTF-8?q?ss=20exception=20(#28256)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Scott Lahteine --- .../share/PlatformIO/scripts/preprocessor.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/buildroot/share/PlatformIO/scripts/preprocessor.py b/buildroot/share/PlatformIO/scripts/preprocessor.py index efc3a6261e..3cd279fa57 100644 --- a/buildroot/share/PlatformIO/scripts/preprocessor.py +++ b/buildroot/share/PlatformIO/scripts/preprocessor.py @@ -37,15 +37,20 @@ def run_preprocessor(env, fn=None): else: cmd += ['-D' + s] - cmd += ['-D__MARLIN_DEPS__ -w -dM -E -x c++'] - depcmd = cmd + [ filename ] - cmd = ' '.join(depcmd) + cmd += ['-D__MARLIN_DEPS__ -w -dM -E -x c++', filename] + + cmd = ' '.join(cmd) blab(cmd) + try: - define_list = subprocess.check_output(cmd, shell=True).splitlines() + define_list_text = subprocess.check_output(cmd, shell=True) except: - define_list = {} + raise RuntimeError(f"Command `{cmd}` failed during build pre-processing.") + + define_list = define_list_text.splitlines() if define_list_text else [] + preprocessor_cache[filename] = define_list + return define_list From bc97723e020eabc0cb387985f410a0617c550e11 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Thu, 8 Jan 2026 06:12:37 +0000 Subject: [PATCH 09/39] [cron] Bump distribution date (2026-01-08) --- 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 2e0b47fcfe..d832301eee 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 "2026-01-05" +//#define STRING_DISTRIBUTION_DATE "2026-01-08" /** * 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 798fa2c598..85ce7e7192 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 "2026-01-05" + #define STRING_DISTRIBUTION_DATE "2026-01-08" #endif /** From f2532ec07a710695dfc550c724dd921adf452894 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 8 Jan 2026 16:27:51 -0600 Subject: [PATCH 10/39] =?UTF-8?q?=F0=9F=93=9D=20README=20(and=20other=20.m?= =?UTF-8?q?d)=20updates=20(#28272)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- Marlin/src/HAL/HC32/README.md | 25 ++--- .../src/feature/mmu/mmu2-serial-protocol.md | 4 +- .../ftdi_eve_touch_ui/ftdi_eve_lib/README.md | 18 +-- README.md | 104 ++++++++++-------- buildroot/share/fonts/README.md | 2 +- buildroot/share/git/README.md | 38 +++---- buildroot/share/scripts/linesformat.py | 3 - docs/BinaryFileTransferProtocol.md | 78 ++++++------- docs/Bresenham.md | 2 +- docs/ConfigEmbedding.md | 2 +- docs/Cutter.md | 2 +- docs/Serial.md | 43 ++++---- 13 files changed, 163 insertions(+), 160 deletions(-) diff --git a/Makefile b/Makefile index f8a9ae3c4e..a63b4dc683 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ format-lines: validate-lines: @echo "Validating text formatting" - @npx prettier --check . --editorconfig --object-wrap preserve + @npx prettier --check . --editorconfig --object-wrap preserve --prose-wrap never validate-urls: @echo "Checking URLs in source files" diff --git a/Marlin/src/HAL/HC32/README.md b/Marlin/src/HAL/HC32/README.md index 1161e881f6..fe265dcf24 100644 --- a/Marlin/src/HAL/HC32/README.md +++ b/Marlin/src/HAL/HC32/README.md @@ -6,7 +6,7 @@ This document provides notes on the HAL for the HC32F460 MCU. The HC32F460 HAL is designed to be generic enough for any HC32F460-based board. Adding support for a new HC32F460-based board will require the following steps: -1. Follow [the usual instructions](https://marlinfw.org/docs/development/boards.html#adding-a-new-board) to add a new board to Marlin. (i.e., Add a pins file, edit `boards.h` and `pins.h`, etc.) +1. Follow [the usual instructions](//marlinfw.org/docs/development/boards.html#adding-a-new-board) to add a new board to Marlin. (i.e., Add a pins file, edit `boards.h` and `pins.h`, etc.) 2. Determine the flash size your board uses: - Examine the board's main processor. (Refer the naming key in `hc32.ini`.) - Extend the `HC32F460C_common` base env for 256K, or `HC32F460E_common` for 512K. @@ -49,11 +49,9 @@ SCB->VTOR = ((uint32_t) APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk); Just searching for `SCB->VTOR` should yield some results. From there, you just need to look at the value that's assigned to it. The example uses `APP_START_ADDRESS`. -> [!NOTE] -> Some vendors publish incomplete source code. But they sometimes leave version control related files in the repo, which can contain previous version of files that were removed. Find these by including folders like `.git` or `.svn` in your search. +> [!NOTE] Some vendors publish incomplete source code. But they sometimes leave version control related files in the repo, which can contain previous version of files that were removed. Find these by including folders like `.git` or `.svn` in your search. -> [!NOTE] -> The example is based on the [Voxelab-64/Aquila_X2](https://github.com/Voxelab-64/Aquila_X2/blob/main/firmware/Sources/.svn/pristine/ec/ec82bcb480b511906bc3e6658450e3a803ab9813.svn-base#L96) which actually includes deleted files in its repo. +> [!NOTE] The example is based on the [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/main/firmware/Sources/.svn/pristine/ec/ec82bcb480b511906bc3e6658450e3a803ab9813.svn-base#L96) which actually includes deleted files in its repo. 2. Using a linker script @@ -71,8 +69,7 @@ MEMORY } ``` -> [!NOTE] -> This example is based on [Voxelab-64/Aquila_X2](https://github.com/Voxelab-64/Aquila_X2/blob/d1f23adf96920996b979bc31023d1dce236d05db/firmware/Sources/main/hdsc32core/hc32f46x_flash.ld#L55) +> [!NOTE] This example is based on [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/d1f23adf96920996b979bc31023d1dce236d05db/firmware/Sources/main/hdsc32core/hc32f46x_flash.ld#L55) ## Documentation on the HC32F460 @@ -96,15 +93,15 @@ Contact me on Discord (@shadow578) if you need it. This HAL depends on the following projects: -- [shadow578/platform-hc32f46x](https://github.com/shadow578/platform-hc32f46x) (PlatformIO platform for HC32F46x) -- [shadow578/framework-arduino-hc32f46x](https://github.com/shadow578/framework-arduino-hc32f46x) (Arduino framework for HC32F46x) -- [shadow578/framework-hc32f46x-ddl](https://github.com/shadow578/framework-hc32f46x-ddl) (HC32F46x DDL framework) +- [shadow578/platform-hc32f46x](//github.com/shadow578/platform-hc32f46x) (PlatformIO platform for HC32F46x) +- [shadow578/framework-arduino-hc32f46x](//github.com/shadow578/framework-arduino-hc32f46x) (Arduino framework for HC32F46x) +- [shadow578/framework-hc32f46x-ddl](//github.com/shadow578/framework-hc32f46x-ddl) (HC32F46x DDL framework) ## Credits This HAL wouldn't be possible without the following projects: -- [Voxelab-64/Aquila_X2](https://github.com/Voxelab-64/Aquila_X2) (original implementation) -- [alexqzd/Marlin-H32](https://github.com/alexqzd/Marlin-H32) (misc. fixes to the original implementation) -- [kgoveas/Arduino-Core-Template](https://github.com/kgoveas/Arduino-Core-Template) (template for Arduino headers) -- [stm32duino/Arduino_Core_STM32](https://github.com/stm32duino/Arduino_Core_STM32) (misc. Arduino functions) +- [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2) (original implementation) +- [alexqzd/Marlin-H32](//github.com/alexqzd/Marlin-H32) (misc. fixes to the original implementation) +- [kgoveas/Arduino-Core-Template](//github.com/kgoveas/Arduino-Core-Template) (template for Arduino headers) +- [stm32duino/Arduino_Core_STM32](//github.com/stm32duino/Arduino_Core_STM32) (misc. Arduino functions) diff --git a/Marlin/src/feature/mmu/mmu2-serial-protocol.md b/Marlin/src/feature/mmu/mmu2-serial-protocol.md index 474fcd488b..80480d15c7 100644 --- a/Marlin/src/feature/mmu/mmu2-serial-protocol.md +++ b/Marlin/src/feature/mmu/mmu2-serial-protocol.md @@ -41,12 +41,12 @@ as soon as the filament is fed down to the extruder. We follow with: - MMU <= 'C0\n' -MMU will feed a few more millimeters of filament for the extruder gears to grab. +MMU will feed a few more millimeters of filament for the extruder gears to grab.\ When done, the MMU sends - MMU => 'ok\n' -We don't wait for a response here but immediately continue with the next G-code which should +We don't wait for a response here but immediately continue with the next G-code which should\ be one or more extruder moves to feed the filament into the hotend. # FINDA status diff --git a/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/README.md b/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/README.md index 61e5f3a388..4a5fd4abc8 100644 --- a/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/README.md +++ b/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/README.md @@ -1,17 +1,17 @@ ## FTDI EVE Library -The FTDI EVE Library is a fully open-source library and UI framework for the FTDI +The FTDI EVE Library is a fully open-source library and UI framework for the FTDI\ FT800 and FT810 graphics processor. -Although the library has been developed within Lulzbot for providing a user interface +Although the library has been developed within Lulzbot for providing a user interface\ for Marlin, the library has been written so that it can be used in any Arduino sketch. -The library is split into two parts. The "basic" API provides a shallow interface to -the underlying FTDI hardware and command FIFO and provides low-level access to the +The library is split into two parts. The "basic" API provides a shallow interface to\ +the underlying FTDI hardware and command FIFO and provides low-level access to the\ hardware as closely as possible to the API described in the FTDI Programmer's Guide. -The "extended" API builds on top of the "basic" API to provide a GUI framework for -handling common challenges in building a usable GUI. The GUI framework provides the +The "extended" API builds on top of the "basic" API to provide a GUI framework for\ +handling common challenges in building a usable GUI. The GUI framework provides the\ following features: - Macros for a resolution-independent placement of widgets based on a grid. @@ -22,6 +22,6 @@ following features: - A sound player class for playing individual notes or complete sound sequences. - Display list caching, for storing static background elements of a screen in RAM_G. -See the "examples" folder for Arduino sketches. Modify the "src/config.h" file in -each to suit your particular setup. The "sample_configs" contain sample configuration -files for running the sketches on our 3D printer boards. +See the "examples" folder for Arduino sketches. Modify the "src/config.h" file in each\ +to suit your particular setup. The "sample_configs" contain sample configuration files\ +for running the sketches on our 3D printer boards. diff --git a/README.md b/README.md index d68fd0297e..93e16d33f0 100644 --- a/README.md +++ b/README.md @@ -110,49 +110,59 @@ Did you know that Marlin includes a Simulator that can run on Windows, macOS, an ### Supported Platforms -| Platform | MCU | Example Boards | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | ---------------------------------------------------------- | -| [Arduino AVR](//www.arduino.cc/) | ATmega | RAMPS, Melzi, RAMBo | -| [Teensy++ 2.0](//www.microchip.com/en-us/product/AT90USB1286) | AT90USB1286 | Printrboard | -| [Arduino Due](//www.arduino.cc/en/Guide/ArduinoDue) | SAM3X8E | RAMPS-FD, RADDS, RAMPS4DUE | -| [ESP32](//github.com/espressif/arduino-esp32) | ESP32 | FYSETC E4, E4d@BOX, MRR | -| [GD32](//www.gigadevice.com/) | GD32 ARM Cortex-M4 | Creality MFL GD32 V4.2.2 | -| [HC32](//www.huazhoucn.com/) | HC32 | Ender-2 Pro, Voxelab Aquila | -| [LPC1768](//www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/512-kb-flash-64-kb-sram-ethernet-usb-lqfp100-package:LPC1768FBD100) | ARM® Cortex-M3 | MKS SBASE, Re-ARM, Selena Compact | -| [LPC1769](//www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/512-kb-flash-64-kb-sram-ethernet-usb-lqfp100-package:LPC1769FBD100) | ARM® Cortex-M3 | Smoothieboard, Azteeg X5 mini, TH3D EZBoard | -| [Pico RP2040](//www.raspberrypi.com/documentation/microcontrollers/pico-series.html) | Dual Cortex M0+ | BigTreeTech SKR Pico | -| [STM32F103](//www.st.com/en/microcontrollers-microprocessors/stm32f103.html) | ARM® Cortex-M3 | Malyan M200, GTM32 Pro, MKS Robin, BTT SKR Mini | -| [STM32F401](//www.st.com/en/microcontrollers-microprocessors/stm32f401.html) | ARM® Cortex-M4 | ARMED, Rumba32, SKR Pro, Lerdge, FYSETC S6, Artillery Ruby | -| [STM32F7x6](//www.st.com/en/microcontrollers-microprocessors/stm32f7x6.html) | ARM® Cortex-M7 | The Borg, RemRam V1 | -| [STM32G0B1RET6](//www.st.com/en/microcontrollers-microprocessors/stm32g0x1.html) | ARM® Cortex-M0+ | BigTreeTech SKR mini E3 V3.0 | -| [STM32H743xIT6](//www.st.com/en/microcontrollers-microprocessors/stm32h743-753.html) | ARM® Cortex-M7 | BigTreeTech SKR V3.0, SKR EZ V3.0, SKR SE BX V2.0/V3.0 | -| [SAMD21P20A](//www.adafruit.com/product/4064) | ARM® Cortex-M0+ | Adafruit Grand Central M4 | -| [SAMD51P20A](//www.adafruit.com/product/4064) | ARM® Cortex-M4 | Adafruit Grand Central M4 | -| [Teensy 3.2/3.1](//www.pjrc.com/teensy/teensy31.html) | MK20DX256VLH7 ARM® Cortex-M4 | -| [Teensy 3.5](//www.pjrc.com/store/teensy35.html) | MK64FX512-VMD12 ARM® Cortex-M4 | -| [Teensy 3.6](//www.pjrc.com/store/teensy36.html) | MK66FX1MB-VMD18 ARM® Cortex-M4 | -| [Teensy 4.0](//www.pjrc.com/store/teensy40.html) | MIMXRT1062-DVL6B ARM® Cortex-M7 | -| [Teensy 4.1](//www.pjrc.com/store/teensy41.html) | MIMXRT1062-DVJ6B ARM® Cortex-M7 | -| Linux Native | x86 / ARM / RISC-V | Raspberry Pi GPIO | -| Simulator | Windows, macOS, Linux | Desktop OS | -| [All supported boards](//marlinfw.org/docs/hardware/boards.html#boards-list) | All platforms | All boards | +| Platform | MCU | Example Boards | +| --- | --- | --- | +| [Arduino AVR](//www.arduino.cc/) | ATmega | RAMPS, Melzi, RAMBo | +| [Teensy++ 2.0](//www.microchip.com/en-us/product/AT90USB1286) | AT90USB1286 | Printrboard | +| [Arduino Due](//www.arduino.cc/en/Guide/ArduinoDue) | SAM3X8E | RAMPS-FD, RADDS, RAMPS4DUE | +| [ESP32](//github.com/espressif/arduino-esp32) | ESP32 | FYSETC E4, E4d@BOX, MRR | +| [GD32](//www.gigadevice.com/) | GD32 ARM Cortex-M4 | Creality MFL GD32 V4.2.2 | +| [HC32](//www.huazhoucn.com/) | HC32 | Ender-2 Pro, Voxelab Aquila | +| [LPC1768](//www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/512-kb-flash-64-kb-sram-ethernet-usb-lqfp100-package:LPC1768FBD100) | ARM® Cortex-M3 | MKS SBASE, Re-ARM, Selena Compact | +| [LPC1769](//www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/512-kb-flash-64-kb-sram-ethernet-usb-lqfp100-package:LPC1769FBD100) | ARM® Cortex-M3 | Smoothieboard, Azteeg X5 mini, TH3D EZBoard | +| [Pico RP2040](//www.raspberrypi.com/documentation/microcontrollers/pico-series.html) | Dual Cortex M0+ | BigTreeTech SKR Pico | +| [STM32F103](//www.st.com/en/microcontrollers-microprocessors/stm32f103.html) | ARM® Cortex-M3 | Malyan M200, GTM32 Pro, MKS Robin, BTT SKR Mini | +| [STM32F401](//www.st.com/en/microcontrollers-microprocessors/stm32f401.html) | ARM® Cortex-M4 | ARMED, Rumba32, SKR Pro, Lerdge, FYSETC S6, Artillery Ruby | +| [STM32F7x6](//www.st.com/en/microcontrollers-microprocessors/stm32f7x6.html) | ARM® Cortex-M7 | The Borg, RemRam V1 | +| [STM32G0B1RET6](//www.st.com/en/microcontrollers-microprocessors/stm32g0x1.html) | ARM® Cortex-M0+ | BigTreeTech SKR mini E3 V3.0 | +| [STM32H743xIT6](//www.st.com/en/microcontrollers-microprocessors/stm32h743-753.html) | ARM® Cortex-M7 | BigTreeTech SKR V3.0, SKR EZ V3.0, SKR SE BX V2.0/V3.0 | +| [SAMD21P20A](//www.adafruit.com/product/4064) | ARM® Cortex-M0+ | Adafruit Grand Central M4 | +| [SAMD51P20A](//www.adafruit.com/product/4064) | ARM® Cortex-M4 | Adafruit Grand Central M4 | +| [Teensy 3.2/3.1](//www.pjrc.com/teensy/teensy31.html) | MK20DX256VLH7 ARM® Cortex-M4 | +| [Teensy 3.5](//www.pjrc.com/store/teensy35.html) | MK64FX512-VMD12 ARM® Cortex-M4 | +| [Teensy 3.6](//www.pjrc.com/store/teensy36.html) | MK66FX1MB-VMD18 ARM® Cortex-M4 | +| [Teensy 4.0](//www.pjrc.com/store/teensy40.html) | MIMXRT1062-DVL6B ARM® Cortex-M7 | +| [Teensy 4.1](//www.pjrc.com/store/teensy41.html) | MIMXRT1062-DVJ6B ARM® Cortex-M7 | +| Linux Native | x86 / ARM / RISC-V | Raspberry Pi GPIO | +| Simulator | Windows, macOS, Linux | Desktop OS | +| [All supported boards](//marlinfw.org/docs/hardware/boards.html#boards-list) | All platforms | All boards | -## Marlin Support +## Marlin Discord + +The [Marlin Firmware Discord](//discord.gg/marlin-firmware-461605380783472640) is a great place to discuss issues with Marlin users and developers, get interactive help with troubleshooting, and build on your best ideas to improve to Marlin in tandem with the most active members of the development team. + +

+ + + +

+ +## Other Marlin Support The Issue Queue is reserved for Bug Reports and Feature Requests. Please use the following resources for help with configuration and troubleshooting: - [Marlin Documentation](//marlinfw.org) - Official Marlin documentation -- [Marlin Discord](//discord.com/servers/marlin-firmware-461605380783472640) - Discuss issues with Marlin users and developers -- Facebook Group ["Marlin Firmware"](//www.facebook.com/groups/1049718498464482/) -- RepRap.org [Marlin Forum](//forums.reprap.org/list.php?415) -- Facebook Group ["Marlin Firmware for 3D Printers"](//www.facebook.com/groups/3Dtechtalk/) -- [Marlin Configuration](//www.youtube.com/results?search_query=marlin+configuration) on YouTube +- ["Marlin Firmware"](//www.facebook.com/groups/1049718498464482/) Facebook Group +- [Marlin Forum](//forums.reprap.org/list.php?415) at RepRap.org +- ["Marlin Firmware for 3D Printers"](//www.facebook.com/groups/3Dtechtalk/) Facebook Group +- [Marlin Configuration](//www.youtube.com/results?search_query=marlin+configuration) playlist on YouTube ## Contributing Patches -You can contribute patches by submitting a Pull Request to the ([bugfix-2.1.x](//github.com/MarlinFirmware/Marlin/tree/bugfix-2.1.x)) branch. +Contribute your patches to Marlin by submitting a Pull Request to the ([bugfix-2.1.x](//github.com/MarlinFirmware/Marlin/tree/bugfix-2.1.x)) branch. -- We use branches named with a "bugfix" or "dev" prefix to fix bugs and integrate new features. +- We use the `bugfix-2.1.x` branch to fix bugs and integrate new features into the latest firmware. Use with caution! +- We maintain `lts-x.x.x` branches, mainly for vendors, so that older versions of Marlin can be patched as-needed to stay functional and fix bugs. - Follow the [Coding Standards](//marlinfw.org/docs/development/coding_standards.html) to gain points with the maintainers. - Please submit Feature Requests and Bug Reports to the [Issue Queue](//github.com/MarlinFirmware/Marlin/issues/new/choose). See above for user support. - Whenever you add new features, be sure to add one or more build tests to `buildroot/tests`. Any tests added to a PR will be run within that PR on GitHub servers as soon as they are pushed. To minimize iteration be sure to run your new tests locally, if possible. @@ -165,11 +175,11 @@ You can contribute patches by submitting a Pull Request to the ([bugfix-2.1.x](/ - To run all unit test suites: - Using PIO: `platformio run -t test-marlin` - Using Make: `make unit-test-all-local` - - Using Docker + make: `maker unit-test-all-local-docker` + - Using Docker + make: `make unit-test-all-local-docker` - To run a single unit test suite: - Using PIO: `platformio run -t marlin_` - Using make: `make unit-test-single-local TEST_TARGET=` - - Using Docker + make: `maker unit-test-single-local-docker TEST_TARGET=` + - Using Docker + make: `make unit-test-single-local-docker TEST_TARGET=` - If your feature can be unit tested, add one or more unit tests. For more information see our documentation on [Unit Tests](test). ## Contributors @@ -180,17 +190,17 @@ Marlin Firmware original logo design by Ahmet Cem TURAN [@ahmetcemturan](//githu ## Project Leadership -| Name | Role | Link | Donate | -| -------------------- | ------------ | -------------------------------------------- | --------------------------------------------------------------------- | -| 🇺🇸 Scott Lahteine | Project Lead | [[@thinkyhead](//github.com/thinkyhead)] | [💸 Donate](//marlinfw.org/docs/development/contributing.html#donate) | -| 🇺🇸 Roxanne Neufeld | Admin | [[@Roxy-3D](//github.com/Roxy-3D)] | -| 🇺🇸 Keith Bennett | Admin | [[@thisiskeithb](//github.com/thisiskeithb)] | [💸 Donate](//github.com/sponsors/thisiskeithb) | -| 🇺🇸 Jason Smith | Admin | [[@sjasonsmith](//github.com/sjasonsmith)] | -| 🇧🇷 Victor Oliveira | Admin | [[@rhapsodyv](//github.com/rhapsodyv)] | -| 🇬🇧 Chris Pepper | Admin | [[@p3p](//github.com/p3p)] | -| 🇳🇿 Peter Ellens | Admin | [[@ellensp](//github.com/ellensp)] | [💸 Donate](//ko-fi.com/ellensp) | -| 🇺🇸 Bob Kuhn | Admin | [[@Bob-the-Kuhn](//github.com/Bob-the-Kuhn)] | -| 🇳🇱 Erik van der Zalm | Founder | [[@ErikZalm](//github.com/ErikZalm)] | +| Name | Role | Link | Donate | +| --- | --- | --- | --- | +| 🇺🇸 Scott Lahteine | Project Lead | [[@thinkyhead](//github.com/thinkyhead)] | [❤️ Donate](//marlinfw.org/docs/development/contributing.html#donate) | +| 🇳🇿 Peter Ellens | Admin | [[@ellensp](//github.com/ellensp)] | [❤️ Donate](//ko-fi.com/ellensp) | +| 🇬🇧 Chris Pepper | Admin | [[@p3p](//github.com/p3p)] | +| 🇺🇸 Keith Bennett | Admin | [[@thisiskeithb](//github.com/thisiskeithb)] | [❤️ Donate](//github.com/sponsors/thisiskeithb) | +| 🇺🇸 Roxanne Neufeld | Admin | [[@Roxy-3D](//github.com/Roxy-3D)] | +| 🇺🇸 Jason Smith | Admin | [[@sjasonsmith](//github.com/sjasonsmith)] | +| 🇧🇷 Victor Oliveira | Admin | [[@rhapsodyv](//github.com/rhapsodyv)] | +| 🇺🇸 Bob Kuhn | Admin | [[@Bob-the-Kuhn](//github.com/Bob-the-Kuhn)] | +| 🇳🇱 Erik van der Zalm | Founder | [[@ErikZalm](//github.com/ErikZalm)] | ## Star History diff --git a/buildroot/share/fonts/README.md b/buildroot/share/fonts/README.md index 57b4d1f57e..22c2d36651 100644 --- a/buildroot/share/fonts/README.md +++ b/buildroot/share/fonts/README.md @@ -4,7 +4,7 @@ The original author of the following font files is [A. Hardtung](https://github.com/AnHardt). -Any copyright is dedicated to the Public Domain. +Any copyright is dedicated to the Public Domain.\ https://creativecommons.org/publicdomain/zero/1.0/ - HD44780_C.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a)) diff --git a/buildroot/share/git/README.md b/buildroot/share/git/README.md index ae321303fc..3a22603ea9 100644 --- a/buildroot/share/git/README.md +++ b/buildroot/share/git/README.md @@ -16,26 +16,26 @@ The following scripts can be used on any system with a GNU environment to speed #### Remotes -| File | Description | -| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| mfadd [user] | Add and Fetch Remote - Add and fetch another user's Marlin fork. Optionally, check out one of their branches. | -| mfinit | Init Working Copy - Create a remote named '`upstream`' (for use by the other scripts) pointing to the '`MarlinFirmware`' fork. This only needs to be used once. Newer versions of GitHub Desktop may create `upstream` on your behalf. | +| File | Description | +| --- | --- | +| mfadd [user] | Add and Fetch Remote - Add and fetch another user's Marlin fork. Optionally, check out one of their branches. | +| mfinit | Init Working Copy - Create a remote named '`upstream`' (for use by the other scripts) pointing to the '`MarlinFirmware`' fork. This only needs to be used once. Newer versions of GitHub Desktop may create `upstream` on your behalf. | #### Branches -| File | Description | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| mfnew [branch] | New Branch - Creates a new branch based on `upstream/[PR-target]`. All new work should start with this command. | -| mffp | Fast Push - Push the HEAD or a commit ID to `upstream` immediately. Requires privileged access to the MarlinFirmware repo. | -| firstpush | Push the current branch to 'origin' -your fork on GitHub- and set it to track '`origin`'. The branch needs to reside on GitHub before you can use it to make a PR. | +| File | Description | +| --- | --- | +| mfnew [branch] | New Branch - Creates a new branch based on `upstream/[PR-target]`. All new work should start with this command. | +| mffp | Fast Push - Push the HEAD or a commit ID to `upstream` immediately. Requires privileged access to the MarlinFirmware repo. | +| firstpush | Push the current branch to 'origin' -your fork on GitHub- and set it to track '`origin`'. The branch needs to reside on GitHub before you can use it to make a PR. | #### Making / Amending PRs -| File | Description | -| ---- | -------------------------------------------------------------------------------------------------------------------------------- | -| mfpr | Pull Request - Open the Compare / Pull Request page on GitHub for the current branch. | +| File | Description | +| --- | --- | +| mfpr | Pull Request - Open the Compare / Pull Request page on GitHub for the current branch. | | mfrb | Do a `git rebase` then `git rebase -i` of the current branch onto `upstream/[PR-target]`. Use this to edit your commits anytime. | -| mfqp | Quick Patch - Commit all current changes as "patch", then do `mfrb`, followed by `git push -f` if no conflicts need resolution. | +| mfqp | Quick Patch - Commit all current changes as "patch", then do `mfrb`, followed by `git push -f` if no conflicts need resolution. | #### Documentation @@ -46,12 +46,12 @@ The following scripts can be used on any system with a GNU environment to speed #### Utilities -| File | Description | -| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| ghtp -[h/s] | Set the protocol to use for all remotes. -h for HTTPS, -s for SSL. | -| ghpc [-f] | Push current branch to 'origin' or to the remote indicated by the error. | -| mfinfo | This utility script is used by the other scripts to get:
- The upstream project ('`MarlinFirmware`')
- the '`origin`' project (i.e., your GitHub username),
- the repository name ('`Marlin`'),
- the PR target branch ('`bugfix-1.1.x`'), and
- the current branch (or the first command-line argument).

By itself, `mfinfo` simply prints these values to the console. | -| mfclean      | Prune your merged and remotely-deleted branches. | +| File | Description | +| --- | --- | +| ghtp -[h/s] | Set the protocol to use for all remotes. -h for HTTPS, -s for SSL. | +| ghpc [-f] | Push current branch to 'origin' or to the remote indicated by the error. | +| mfinfo | This utility script is used by the other scripts to get:
- The upstream project ('`MarlinFirmware`')
- the '`origin`' project (i.e., your GitHub username),
- the repository name ('`Marlin`'),
- the PR target branch ('`bugfix-1.1.x`'), and
- the current branch (or the first command-line argument).

By itself, `mfinfo` simply prints these values to the console. | +| mfclean      | Prune your merged and remotely-deleted branches. | --- diff --git a/buildroot/share/scripts/linesformat.py b/buildroot/share/scripts/linesformat.py index 8978de0ec9..7fd80c1b55 100755 --- a/buildroot/share/scripts/linesformat.py +++ b/buildroot/share/scripts/linesformat.py @@ -69,6 +69,3 @@ def format_text(argv): # Python standard startup for command line with arguments if __name__ == '__main__': format_text(sys.argv[1:]) - -# Usage -apply_editorconfig_rules('/path/to/your/folder') diff --git a/docs/BinaryFileTransferProtocol.md b/docs/BinaryFileTransferProtocol.md index ebb1194aac..cd8eaae46f 100644 --- a/docs/BinaryFileTransferProtocol.md +++ b/docs/BinaryFileTransferProtocol.md @@ -38,14 +38,14 @@ ADB5 00 0 1 0000 0103 +-------------------------------+-------------------------------+ ``` -| Field | Width | Description | -| --------------- | ------- | ------------------------------------------------------------------------------------------------- | -| Start Token | 16 bits | Each packet must start with the 16-bit value `0xB5AD`. | -| Sync Number | 8 bits | Synchronization value, each packet after sync should increment this value by 1. | -| Protocol ID | 4 bits | Protocol ID. `0` for Connection Control, `1` for Transfer. See Below. | -| Packet Type | 4 bits | Packet Type ID. Depends on the Protocol ID, see below. | -| Payload Length | 16 bits | Length of payload data. If this value is greater than 0, a packet payload will follow the header. | -| Header Checksum | 16 bits | 16-bit Fletchers checksum of the header data excluding the Start Token | +| Field | Width | Description | +| --- | --- | --- | +| Start Token | 16 bits | Each packet must start with the 16-bit value `0xB5AD`. | +| Sync Number | 8 bits | Synchronization value, each packet after sync should increment this value by 1. | +| Protocol ID | 4 bits | Protocol ID. `0` for Connection Control, `1` for Transfer. See Below. | +| Packet Type | 4 bits | Packet Type ID. Depends on the Protocol ID, see below. | +| Payload Length | 16 bits | Length of payload data. If this value is greater than 0, a packet payload will follow the header. | +| Header Checksum | 16 bits | 16-bit Fletchers checksum of the header data excluding the Start Token | ## Packet Payload @@ -61,10 +61,10 @@ If the Payload Length field of the header is non-zero, payload data is expected +-------------------------------+-------------------------------+ ``` -| Field | Width | Description | -| --------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ | -| Payload Data | Payload Length bytes | Payload data. This should be no longer than the buffer length reported by the Connection SYNC operation. | -| Packet Checksum | 16 bits | 16-bit Fletchers checksum of the header and payload, including the Header Checksum, but excluding the Start Token. | +| Field | Width | Description | +| --- | --- | --- | +| Payload Data | Payload Length bytes | Payload data. This should be no longer than the buffer length reported by the Connection SYNC operation. | +| Packet Checksum | 16 bits | 16-bit Fletchers checksum of the header and payload, including the Header Checksum, but excluding the Start Token. | ## Fletchers Checksum @@ -125,13 +125,13 @@ Returns a sync response: ss,,.. ``` -| Value | Description | -| ------------- | --------------------------------------------------------------------------------------------------------------------- | -| SYNC | The current Sync Number, this should be used in the next packet sent and incremented by 1 for each packet sent after. | -| BUFFER_SIZE | The client buffer size. Packet Payload Length must not exceed this value. | -| VERSION_MAJOR | The major version number of the client Marlin BFT protocol, e.g., `0`. | -| VERSION_MINOR | The minor version number of the client Marlin BFT protocol, e.g., `1`. | -| VERSION_PATCH | The patch version number of the client Marlin BFT protocol, e.g., `0`. | +| Value | Description | +| --- | --- | +| SYNC | The current Sync Number, this should be used in the next packet sent and incremented by 1 for each packet sent after. | +| BUFFER_SIZE | The client buffer size. Packet Payload Length must not exceed this value. | +| VERSION_MAJOR | The major version number of the client Marlin BFT protocol, e.g., `0`. | +| VERSION_MINOR | The minor version number of the client Marlin BFT protocol, e.g., `1`. | +| VERSION_PATCH | The patch version number of the client Marlin BFT protocol, e.g., `0`. | Example response: @@ -147,13 +147,13 @@ A CLOSE packet should be the last packet sent by a host. On success, the client `Protocol ID` 1 packets control the file transfers performed over the connection: -| Packet Type | Name | Description | -| ----------- | ----- | ------------------------------------------------------------- | -| 0 | QUERY | Query the client protocol details and compression parameters. | -| 1 | OPEN | Open a file for writing and begin accepting data to transfer. | -| 2 | CLOSE | Finish writing and close the current file. | -| 3 | WRITE | Write data to an open file. | -| 4 | ABORT | Abort file transfer. | +| Packet Type | Name | Description | +| --- | --- | --- | +| 0 | QUERY | Query the client protocol details and compression parameters. | +| 1 | OPEN | Open a file for writing and begin accepting data to transfer. | +| 2 | CLOSE | Finish writing and close the current file. | +| 3 | WRITE | Write data to an open file. | +| 4 | ABORT | Abort file transfer. | ### QUERY Packet @@ -165,12 +165,12 @@ Returns a query response: PFT:version:..:compression:(,) ``` -| Value | Description | -| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | -| VERSION_MAJOR | The major version number of the client Marlin BFT protocol, e.g., `0`. | -| VERSION_MINOR | The minor version number of the client Marlin BFT protocol, e.g., `1`. | -| VERSION_PATCH | The patch version number of the client Marlin BFT protocol, e.g., `0`. | -| COMPRESSION_ALGO | Compression algorithm. Currently either `heatshrink` or `none` | +| Value | Description | +| --- | --- | +| VERSION_MAJOR | The major version number of the client Marlin BFT protocol, e.g., `0`. | +| VERSION_MINOR | The minor version number of the client Marlin BFT protocol, e.g., `1`. | +| VERSION_PATCH | The patch version number of the client Marlin BFT protocol, e.g., `0`. | +| COMPRESSION_ALGO | Compression algorithm. Currently either `heatshrink` or `none` | | COMPRESSION_PARAMS | Compression parameters, separated by commas. Currently, if `COMPRESSION_AGLO` is heatshrink, this will be the window size and lookahead size. | Example response: @@ -197,12 +197,12 @@ Payload: +-------------------------------+-------------------------------+ ``` -| Field | Width | Description | -| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Dummy | 8 bits | A boolean value indicating if this file transfer should be actually carried out or not. If `1`, the client will respond as if the file is opened and accept data transfer, but no data will be written. | -| Compression | 8 bits | A boolean value indicating if the data to be transferred will be compressed using the algorithm and parameters returned in the QUERY Packet. | -| Filename | ... | A filename including a null terminator byte. | -| Packet Checksum | 16 bits | 16-bit Fletchers checksum of the header and payload, including the Header Checksum, but excluding the Start Token. | +| Field | Width | Description | +| --- | --- | --- | +| Dummy | 8 bits | A boolean value indicating if this file transfer should be actually carried out or not. If `1`, the client will respond as if the file is opened and accept data transfer, but no data will be written. | +| Compression | 8 bits | A boolean value indicating if the data to be transferred will be compressed using the algorithm and parameters returned in the QUERY Packet. | +| Filename | ... | A filename including a null terminator byte. | +| Packet Checksum | 16 bits | 16-bit Fletchers checksum of the header and payload, including the Header Checksum, but excluding the Start Token. | Responses: @@ -228,7 +228,7 @@ Responses: Writes payload data to the currently open file. If the file was opened with Compression set to 1, the data will be decompressed first. Payload Length must not exceed the buffer size returned by the SYNC Packet. -Responses: +Responses:\ On success, an `ok` response will be sent. On error, an `ok` response will be followed by an error response: | Response | Description | diff --git a/docs/Bresenham.md b/docs/Bresenham.md index 6916e35f48..f447ac0056 100644 --- a/docs/Bresenham.md +++ b/docs/Bresenham.md @@ -1,4 +1,4 @@ -On the Bresenham algorithm as implemented by Marlin: +On the Bresenham algorithm as implemented by Marlin:\ (Taken from (https://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html) The basic Bresenham algorithm: diff --git a/docs/ConfigEmbedding.md b/docs/ConfigEmbedding.md index a9770fb42b..ac032ab1ed 100644 --- a/docs/ConfigEmbedding.md +++ b/docs/ConfigEmbedding.md @@ -8,7 +8,7 @@ At the start of the PlatformIO build process, we create an embedded configuratio ## Extracting configurations from a Marlin binary -To get the configuration out of a binary firmware, you'll need a non-write-protected SD card inserted into the printer while running the firmware. +To get the configuration out of a binary firmware, you'll need a non-write-protected SD card inserted into the printer while running the firmware.\ Send the command `M503 C` to write the file `mc.zip` to the SD card. Copy the file to your computer, ideally in the same folder as the Marlin repository. Run the following commands to extract and apply the configuration: diff --git a/docs/Cutter.md b/docs/Cutter.md index ace957210a..51ed260b08 100644 --- a/docs/Cutter.md +++ b/docs/Cutter.md @@ -91,7 +91,7 @@ Once the entry and exit power values are determined, the values are divided into trap step power incr_decr = ( cruize power - entry_exit ) / accel_decel_steps -The trap steps are incremented or decremented during each accel or decel step until the block is complete. +The trap steps are incremented or decremented during each accel or decel step until the block is complete.\ Step power is either cumulatively added or subtracted during trapezoid ramp progressions. #### Planner Code: diff --git a/docs/Serial.md b/docs/Serial.md index 9ef9a781fa..6806199ae9 100644 --- a/docs/Serial.md +++ b/docs/Serial.md @@ -1,6 +1,6 @@ # Serial port architecture in Marlin -Marlin is targeting a plethora of different CPU architectures and platforms. Each of these platforms has its own serial interface. +Marlin is targeting a plethora of different CPU architectures and platforms. Each of these platforms has its own serial interface.\ While many provide a Arduino-like Serial class, it's not all of them, and the differences in the existing API create a very complex brain teaser for writing code that works more or less on each platform. Moreover, many platform have intrinsic needs about serial port (like forwarding the output on multiple serial port, providing a _serial-like_ telnet server, mixing USB-based serial port with SD card emulation) that are difficult to handle cleanly in the other platform serial logic. @@ -9,18 +9,17 @@ Starting with version 2.0.8, Marlin provides a common interface for its serial n ## Common interface -This interface is declared in `Marlin/src/core/serial_base.h` +This interface is declared in `Marlin/src/core/serial_base.h`.\ Any implementation will need to follow this interface for being used transparently in Marlin's codebase. -The implementation was written to prioritize performance over abstraction, so the base interface is not using virtual inheritance to avoid the cost of virtual dispatching while calling methods. +The implementation was written to prioritize performance over abstraction, so the base interface is not using virtual inheritance to avoid the cost of virtual dispatching while calling methods.\ Instead, the Curiously Recurring Template Pattern (**CRTP**) is used so that, upon compilation, the interface abstraction does not incur a performance cost. Because some platform do not follow the same interface, the missing method in the actual low-level implementation are detected via SFINAE and a wrapper is generated when such method are missing. See the `CALL_IF_EXISTS` macro in `Marlin/src/core/macros.h` for documentation of this technique. ## Composing the desired feature -The different specificities for each architecture are provided by composing the serial type based on desired functionality. -In the `Marlin/src/core/serial_hook.h` file, the different serial feature are declared and defined in each templated type: +The different specificities for each architecture are provided by composing the serial type based on desired functionality. In the `Marlin/src/core/serial_hook.h` file, the different serial feature are declared and defined in each templated type: 1. `BaseSerial` is a simple 1:1 wrapper to the underlying, Arduino compatible, `Serial`'s class. It derives from it. You'll use this if the platform does not do anything specific for the `Serial` object (for example, if an interrupt callback calls directly the serial **instance** in the platform's framework code, this is not the right class to use). This wrapper is completely inlined so that it does not generate any code upon compilation. `BaseSerial` constructor forwards any parameter to the platform's `Serial`'s constructor. 2. `ForwardSerial` is a composing wrapper. It references an actual Arduino compatible `Serial` instance. You'll use this if the instance is declared in the platform's framework and is being referred directly in the framework. This is not as efficient as the `BaseSerial` implementation since static dereferencing is done for each method call (it'll still be faster than virtual dispatching) @@ -30,7 +29,7 @@ In the `Marlin/src/core/serial_hook.h` file, the different serial feature are de ## Plumbing -Since all the types above are using CRTP, it's possible to combine them to get the appropriate functionality. +Since all the types above are using CRTP, it's possible to combine them to get the appropriate functionality.\ This is easily done via type definition of the feature. For example, to create a single serial interface with 2 serial outputs (one enabled at runtime and the other switchable): @@ -51,8 +50,8 @@ The magical numbers here are the step and offset for computing the serial port. MS< A = MS, B=MS, offset=0, step=2> ``` -This means that the underlying multiserial A (with output to `a,b`) is available from offset = 0 to offset + step = 1 (default value). -The multiserial B (with output to `c,d`) is available from offset = 2 (the next step from the root multiserial) to offset + step = 3. +This means that the underlying multiserial A (with output to `a,b`) is available from offset = 0 to offset + step = 1 (default value).\ +The multiserial B (with output to `c,d`) is available from offset = 2 (the next step from the root multiserial) to offset + step = 3.\ In practice, the root multiserial will redirect any index/mask `offset` to `offset + step - 1` to its first leaf, and any index/mask `offset + step` to `offset + 2*step - 1` to its second leaf. ## Emergency parser @@ -63,19 +62,19 @@ By default, the serial base interface provide an emergency parser that's only en The following macros are defined (in `serial.h`) to output data to the serial ports: -| MACRO | Parameters | Usage | Example | Expected output | -| -------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------- | -| `SERIAL_ECHO` | Any basic type is supported (`char`, `uint8_t`, `int16_t`, `int32_t`, `float`, `long`, `const char*`, ...). | For a numeric type it prints the number in decimal. A string is output as a string. | `uint8_t a = 123; SERIAL_ECHO(a); SERIAL_CHAR(' '); SERIAL_ECHO(' '); ` | `123 32` | -| `SERIAL_ECHOLN` | Same as `SERIAL_ECHO` | Do `SERIAL_ECHO`, adding a newline | `int a = 456; SERIAL_ECHOLN(a);` | `456\n` | -| `SERIAL_ECHOPGM` | String / Value pairs | Print a series of string literals and values alternately | `SERIAL_ECHOPGM("Bob", 34);` | `Bob34` | -| `SERIAL_ECHOLNPGM` | Same as `SERIAL_ECHOPGM` | Do `SERIAL_ECHOPGM`, adding a newline | `SERIAL_ECHOPGM("Alice", 56);` | `alice56` | -| `SERIAL_ECHOPGM_P` | Like `SERIAL_ECHOPGM` but takes PGM strings | Print a series of PGM strings and values alternately | `SERIAL_ECHOPGM_P(GET_TEXT(MSG_HELLO), 123);` | `Hello123` | -| `SERIAL_ECHOLNPGM_P` | Same as `SERIAL_ECHOPGM_P` | Do `SERIAL_ECHOPGM_P`, adding a newline | `SERIAL_ECHOLNPGM_P(PSTR("Alice"), 78);` | `alice78\n` | -| `SERIAL_ECHO_START` | None | Prefix an echo line | `SERIAL_ECHO_START();` | `echo:` | -| `SERIAL_ECHO_MSG` | Same as `SERIAL_ECHOLNPGM` | Print a full echo line | `SERIAL_ECHO_MSG("Count is ", count);` | `echo:Count is 3` | -| `SERIAL_ERROR_START` | None | Prefix an error line | `SERIAL_ERROR_START();` | `Error:` | -| `SERIAL_ERROR_MSG` | Same as `SERIAL_ECHOLNPGM` | Print a full error line | `SERIAL_ERROR_MSG("Not found");` | `Error:Not found` | -| `SERIAL_ECHO_SP` | Number of spaces | Print one or more spaces | `SERIAL_ECHO_SP(3)` | ` ` | -| `SERIAL_EOL` | None | Print an end of line | `SERIAL_EOL();` | `\n` | +| MACRO | Parameters | Usage | Example | Expected output | +| --- | --- | --- | --- | --- | +| `SERIAL_ECHO` | Any basic type is supported (`char`, `uint8_t`, `int16_t`, `int32_t`, `float`, `long`, `const char*`, ...). | For a numeric type it prints the number in decimal. A string is output as a string. | `uint8_t a = 123; SERIAL_ECHO(a); SERIAL_CHAR(' '); SERIAL_ECHO(' '); ` | `123 32` | +| `SERIAL_ECHOLN` | Same as `SERIAL_ECHO` | Do `SERIAL_ECHO`, adding a newline | `int a = 456; SERIAL_ECHOLN(a);` | `456\n` | +| `SERIAL_ECHOPGM` | String / Value pairs | Print a series of string literals and values alternately | `SERIAL_ECHOPGM("Bob", 34);` | `Bob34` | +| `SERIAL_ECHOLNPGM` | Same as `SERIAL_ECHOPGM` | Do `SERIAL_ECHOPGM`, adding a newline | `SERIAL_ECHOPGM("Alice", 56);` | `alice56` | +| `SERIAL_ECHOPGM_P` | Like `SERIAL_ECHOPGM` but takes PGM strings | Print a series of PGM strings and values alternately | `SERIAL_ECHOPGM_P(GET_TEXT(MSG_HELLO), 123);` | `Hello123` | +| `SERIAL_ECHOLNPGM_P` | Same as `SERIAL_ECHOPGM_P` | Do `SERIAL_ECHOPGM_P`, adding a newline | `SERIAL_ECHOLNPGM_P(PSTR("Alice"), 78);` | `alice78\n` | +| `SERIAL_ECHO_START` | None | Prefix an echo line | `SERIAL_ECHO_START();` | `echo:` | +| `SERIAL_ECHO_MSG` | Same as `SERIAL_ECHOLNPGM` | Print a full echo line | `SERIAL_ECHO_MSG("Count is ", count);` | `echo:Count is 3` | +| `SERIAL_ERROR_START` | None | Prefix an error line | `SERIAL_ERROR_START();` | `Error:` | +| `SERIAL_ERROR_MSG` | Same as `SERIAL_ECHOLNPGM` | Print a full error line | `SERIAL_ERROR_MSG("Not found");` | `Error:Not found` | +| `SERIAL_ECHO_SP` | Number of spaces | Print one or more spaces | `SERIAL_ECHO_SP(3)` | ` ` | +| `SERIAL_EOL` | None | Print an end of line | `SERIAL_EOL();` | `\n` | _This document was written by [X-Ryl669](https://blog.cyril.by) and is under [CC-SA license](https://creativecommons.org/licenses/by-sa)_ From ffa5adb077e2d56678a3097ec3b769d5e87de2ab Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 8 Jan 2026 16:37:44 -0600 Subject: [PATCH 11/39] =?UTF-8?q?=F0=9F=A9=B9=20Skip=20One-Click=20on=20EE?= =?UTF-8?q?PROM=20error=20(#28260)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #28095 --- Marlin/src/module/settings.cpp | 16 +++++++++++++--- Marlin/src/module/settings.h | 13 +++++++------ Marlin/src/sd/cardreader.cpp | 7 ++++++- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp index e48b215b77..f0ade99082 100644 --- a/Marlin/src/module/settings.cpp +++ b/Marlin/src/module/settings.cpp @@ -1876,8 +1876,13 @@ void MarlinSettings::postprocess() { TERN_(EXTENSIBLE_UI, ExtUI::onSettingsStored(success)); + // Remember the error condition so One-Click Printing can be skipped + #if ENABLED(ONE_CLICK_PRINT) && NONE(EEPROM_AUTO_INIT, EEPROM_INIT_NOW) + working_crc = uint16_t(eeprom_error); + #endif + return success; - } + } // save EEPROM_Error MarlinSettings::check_version() { if (!EEPROM_START(EEPROM_OFFSET)) return ERR_EEPROM_NOPROM; @@ -3077,8 +3082,13 @@ void MarlinSettings::postprocess() { if (!validating && TERN1(EEPROM_BOOT_SILENT, marlin.isRunning())) report(); #endif + // Remember the error condition so One-Click Printing can be skipped + #if ENABLED(ONE_CLICK_PRINT) && NONE(EEPROM_AUTO_INIT, EEPROM_INIT_NOW) + working_crc = uint16_t(eeprom_error); + #endif + return eeprom_error; - } + } // _load #ifdef ARCHIM2_SPI_FLASH_EEPROM_BACKUP_SIZE extern bool restoreEEPROM(); @@ -3146,7 +3156,7 @@ void MarlinSettings::postprocess() { #if ENABLED(AUTO_BED_LEVELING_UBL) - inline void ubl_invalid_slot(const int s) { + static void ubl_invalid_slot(const int s) { DEBUG_ECHOLN(F("?Invalid "), F("slot.\n"), s, F(" mesh slots available.")); UNUSED(s); } diff --git a/Marlin/src/module/settings.h b/Marlin/src/module/settings.h index f181e4014d..a6f3c2850f 100644 --- a/Marlin/src/module/settings.h +++ b/Marlin/src/module/settings.h @@ -67,6 +67,10 @@ class MarlinSettings { static bool load(); // Return 'true' if data was loaded ok static bool validate(); // Return 'true' if EEPROM data is ok + #if ENABLED(ONE_CLICK_PRINT) && NONE(EEPROM_AUTO_INIT, EEPROM_INIT_NOW) + static EEPROM_Error eeprom_status() { return (EEPROM_Error)working_crc; } + #endif + static EEPROM_Error check_version(); static void first_load() { @@ -100,10 +104,8 @@ class MarlinSettings { #else // !EEPROM_SETTINGS - FORCE_INLINE - static bool load() { reset(); report(); return true; } - FORCE_INLINE - static void first_load() { (void)load(); } + FORCE_INLINE static bool load() { reset(); report(); return true; } + FORCE_INLINE static void first_load() { (void)load(); } #endif // !EEPROM_SETTINGS @@ -114,8 +116,7 @@ class MarlinSettings { #if DISABLED(DISABLE_M503) static void report(const bool forReplay=false); #else - FORCE_INLINE - static void report(const bool=false) {} + FORCE_INLINE static void report(const bool=false) {} #endif private: diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp index b6a0c73c09..a284af2c18 100644 --- a/Marlin/src/sd/cardreader.cpp +++ b/Marlin/src/sd/cardreader.cpp @@ -1030,11 +1030,16 @@ void CardReader::write_command(char * const buf) { * Select the newest file and ask the user if they want to print it. */ bool CardReader::one_click_check() { + // Don't proceed if an EEPROM error needs a response + #if ENABLED(EEPROM_SETTINGS) && NONE(EEPROM_AUTO_INIT, EEPROM_INIT_NOW) + if (settings.eeprom_status() != ERR_EEPROM_NOERR) return false; + #endif + const bool found = selectNewestFile(); // Changes the current workDir if found if (found) { //SERIAL_ECHO_MSG(" OCP File: ", longest_filename(), "\n"); //ui.init(); - one_click_print(); // Restores workkDir to root (eventually) + one_click_print(); // Restores workDir to root (eventually) } return found; } From e966b83bb27754c63b67c493f04a2ab52fa0d6ea Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 20 Dec 2025 15:38:15 -0600 Subject: [PATCH 12/39] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Casua?= =?UTF-8?q?l=20types=20casting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/core/types.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Marlin/src/core/types.h b/Marlin/src/core/types.h index b4d7705ffd..0881bc47a1 100644 --- a/Marlin/src/core/types.h +++ b/Marlin/src/core/types.h @@ -579,7 +579,7 @@ struct XYval { FI constexpr XYval asUInt32() const { return { uint32_t(x), uint32_t(y) }; } FI constexpr XYval asInt64() const { return { int64_t(x), int64_t(y) }; } FI constexpr XYval asUInt64() const { return { uint64_t(x), uint64_t(y) }; } - FI constexpr XYval asFloat() const { return { static_cast(x), static_cast(y) }; } + FI constexpr XYval asFloat() const { return { float(x), float(y) }; } // Marlin workspace shifting is done with G92 and M206 FI XYval asLogical() const { XYval o = asFloat(); toLogical(o); return o; } @@ -756,13 +756,13 @@ struct XYZval { FI constexpr XYZval asUInt32() const { return NUM_AXIS_ARRAY(uint32_t(x), uint32_t(y), uint32_t(z), uint32_t(i), uint32_t(j), uint32_t(k), uint32_t(u), uint32_t(v), uint32_t(w)); } FI constexpr XYZval asInt64() const { return NUM_AXIS_ARRAY(int64_t(x), int64_t(y), int64_t(z), int64_t(i), int64_t(j), int64_t(k), int64_t(u), int64_t(v), int64_t(w)); } FI constexpr XYZval asUInt64() const { return NUM_AXIS_ARRAY(uint64_t(x), uint64_t(y), uint64_t(z), uint64_t(i), uint64_t(j), uint64_t(k), uint64_t(u), uint64_t(v), uint64_t(w)); } - FI constexpr XYZval asFloat() const { return NUM_AXIS_ARRAY(static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k), static_cast(u), static_cast(v), static_cast(w)); } + FI constexpr XYZval asFloat() const { return NUM_AXIS_ARRAY(float(x), float(y), float(z), float(i), float(j), float(k), float(u), float(v), float(w)); } // Marlin workspace shifting is done with G92 and M206 FI XYZval asLogical() const { XYZval o = asFloat(); toLogical(o); return o; } FI XYZval asNative() const { XYZval o = asFloat(); toNative(o); return o; } - // In-place cast to types having fewer fields + // In-place reinterpret-cast to types having fewer fields FI operator XYval&() { return *(XYval*)this; } FI operator const XYval&() const { return *(const XYval*)this; } @@ -927,13 +927,13 @@ struct XYZEval { FI constexpr XYZEval asUInt32() const { return LOGICAL_AXIS_ARRAY(uint32_t(e), uint32_t(x), uint32_t(y), uint32_t(z), uint32_t(i), uint32_t(j), uint32_t(k), uint32_t(u), uint32_t(v), uint32_t(w)); } FI constexpr XYZEval asInt64() const { return LOGICAL_AXIS_ARRAY(int64_t(e), int64_t(x), int64_t(y), int64_t(z), int64_t(i), int64_t(j), int64_t(k), int64_t(u), int64_t(v), int64_t(w)); } FI constexpr XYZEval asUInt64() const { return LOGICAL_AXIS_ARRAY(uint64_t(e), uint64_t(x), uint64_t(y), uint64_t(z), uint64_t(i), uint64_t(j), uint64_t(k), uint64_t(u), uint64_t(v), uint64_t(w)); } - FI constexpr XYZEval asFloat() const { return LOGICAL_AXIS_ARRAY(static_cast(e), static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k), static_cast(u), static_cast(v), static_cast(w)); } + FI constexpr XYZEval asFloat() const { return LOGICAL_AXIS_ARRAY(float(e), float(x), float(y), float(z), float(i), float(j), float(k), float(u), float(v), float(w)); } // Marlin workspace shifting is done with G92 and M206 FI XYZEval asLogical() const { XYZEval o = asFloat(); toLogical(o); return o; } FI XYZEval asNative() const { XYZEval o = asFloat(); toNative(o); return o; } - // In-place cast to types having fewer fields + // In-place reinterpret-cast to types having fewer fields FI operator XYval&() { return *(XYval*)this; } FI operator const XYval&() const { return *(const XYval*)this; } FI operator XYZval&() { return *(XYZval*)this; } From 4447c3e44594531f95e2482210a322710764ad51 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Fri, 9 Jan 2026 00:34:57 +0000 Subject: [PATCH 13/39] [cron] Bump distribution date (2026-01-09) --- 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 d832301eee..56acdba638 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 "2026-01-08" +//#define STRING_DISTRIBUTION_DATE "2026-01-09" /** * 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 85ce7e7192..741259b688 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 "2026-01-08" + #define STRING_DISTRIBUTION_DATE "2026-01-09" #endif /** From 606b6fe64f9288b336a259048102b150e99e370f Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Wed, 26 Nov 2025 02:13:45 -0600 Subject: [PATCH 14/39] =?UTF-8?q?=F0=9F=8C=90=20Wide=20Preheat=20strings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/lcd/language/language_en.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index 559b3e339d..ba5228a1f1 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -158,6 +158,7 @@ namespace LanguageNarrow_en { LSTR MSG_PREHEAT_1_END_E = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" End ~"); LSTR MSG_PREHEAT_1_ALL = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" All"); LSTR MSG_PREHEAT_1_BEDONLY = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" Bed"); + LSTR MSG_PREHEAT_1_CHAMBER = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" Chmb"); LSTR MSG_PREHEAT_1_SETTINGS = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" Conf"); LSTR MSG_PREHEAT_2 = _UxGT("Preheat ") PREHEAT_2_LABEL; @@ -1141,6 +1142,14 @@ namespace LanguageNarrow_en { namespace LanguageWide_en { using namespace LanguageNarrow_en; #if LCD_WIDTH >= 20 || HAS_DWIN_E3V2 + LSTR MSG_PREHEAT_1_END = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" Hotend"); + LSTR MSG_PREHEAT_1_END_E = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" Hotend ~"); + LSTR MSG_PREHEAT_1_CHAMBER = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" Chamber"); + LSTR MSG_PREHEAT_1_SETTINGS = _UxGT("Preheat ") PREHEAT_1_LABEL _UxGT(" Settings"); + LSTR MSG_PREHEAT_M_END = _UxGT("Preheat $ Hotend"); + LSTR MSG_PREHEAT_M_END_E = _UxGT("Preheat $ Hotend ~"); + LSTR MSG_PREHEAT_M_CHAMBER = _UxGT("Preheat $ Chamber"); + LSTR MSG_PREHEAT_M_SETTINGS = _UxGT("Preheat $ Settings"); LSTR MSG_HOST_START_PRINT = _UxGT("Start Host Print"); LSTR MSG_PRINTING_OBJECT = _UxGT("Printing Object"); LSTR MSG_CANCEL_OBJECT = _UxGT("Cancel Object"); @@ -1164,8 +1173,6 @@ namespace LanguageWide_en { LSTR MSG_HOMING_FEEDRATE_Y = _UxGT("Y Homing Feedrate"); LSTR MSG_HOMING_FEEDRATE_Z = _UxGT("Z Homing Feedrate"); LSTR MSG_EEPROM_INITIALIZED = _UxGT("Default Settings Restored"); - LSTR MSG_PREHEAT_M_CHAMBER = _UxGT("Preheat $ Chamber"); - LSTR MSG_PREHEAT_M_SETTINGS = _UxGT("Preheat $ Config"); LSTR MSG_FTM_RT_RUNNING = _UxGT("Resonance Test Running..."); LSTR MSG_FTM_RESONANCE_FREQ = _UxGT("Resonance frequency"); #endif From 561c409d5974c2b691c0967efa738e8278f16ae3 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 15 Dec 2025 00:50:36 -0600 Subject: [PATCH 15/39] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Adjus?= =?UTF-8?q?t=20CardReader=20conditions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/sd/cardreader.cpp | 22 ++++++++++++---------- Marlin/src/sd/cardreader.h | 8 ++++---- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp index a284af2c18..426437d9d1 100644 --- a/Marlin/src/sd/cardreader.cpp +++ b/Marlin/src/sd/cardreader.cpp @@ -95,13 +95,13 @@ int16_t CardReader::nrItems = -1; #if ENABLED(SDCARD_SORT_ALPHA) - int16_t CardReader::sort_count; #if ENABLED(SDSORT_GCODE) SortFlag CardReader::sort_alpha; int8_t CardReader::sort_folders; //bool CardReader::sort_reverse; #endif + int16_t CardReader::sort_count; uint8_t *CardReader::sort_order; #if ENABLED(SDSORT_USES_RAM) @@ -159,13 +159,15 @@ CardReader::CardReader() { static uint8_t sort_order_static[SDSORT_LIMIT]; sort_order = sort_order_static; #endif - #if ENABLED(SDSORT_CACHE_NAMES) && DISABLED(SDSORT_DYNAMIC_RAM) - static char sortshort_static[SDSORT_LIMIT][FILENAME_LENGTH]; - sortshort = sortshort_static; - #endif - #if ENABLED(SDSORT_CACHE_NAMES) && !ALL(SDSORT_DYNAMIC_RAM, SDSORT_USES_STACK) - static char sortnames_static[SDSORT_LIMIT][SORTED_LONGNAME_STORAGE]; - sortnames = sortnames_static; + #if ENABLED(SDSORT_CACHE_NAMES) + #if DISABLED(SDSORT_DYNAMIC_RAM) + static char sortshort_static[SDSORT_LIMIT][FILENAME_LENGTH]; + sortshort = sortshort_static; + #endif + #if !ALL(SDSORT_DYNAMIC_RAM, SDSORT_USES_STACK) + static char sortnames_static[SDSORT_LIMIT][SORTED_LONGNAME_STORAGE]; + sortnames = sortnames_static; + #endif #endif sort_count = 0; @@ -1344,7 +1346,7 @@ void CardReader::cdroot() { #define SET_SORTSHORT(I) NOOP #endif #endif - #endif + #endif // SDSORT_USES_RAM /** * Read all the files and produce a sort key @@ -1600,7 +1602,7 @@ void CardReader::cdroot() { } else { sort_order[0] = uint8_t(0); - #if ALL(SDSORT_USES_RAM, SDSORT_CACHE_NAMES) + #if ENABLED(SDSORT_CACHE_NAMES) #if ENABLED(SDSORT_DYNAMIC_RAM) sortnames = new char[1][SORTED_LONGNAME_STORAGE]; sortshort = new char[1][SORTED_SHORTNAME_STORAGE]; diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h index 5956ffd091..fd514a5340 100644 --- a/Marlin/src/sd/cardreader.h +++ b/Marlin/src/sd/cardreader.h @@ -353,17 +353,17 @@ private: // Alphabetical file and folder sorting // #if ENABLED(SDCARD_SORT_ALPHA) - static int16_t sort_count; // Count of sorted items in the current directory + #if ENABLED(SDSORT_GCODE) static SortFlag sort_alpha; // Sorting: REV, OFF, FWD static int8_t sort_folders; // Folder sorting before/none/after //static bool sort_reverse; // Flag to enable / disable reverse sorting #endif - // Pointer to the static or dynamic sort index - static uint8_t *sort_order; + static int16_t sort_count; // Count of sorted items in the current directory + static uint8_t *sort_order; // Pointer to the static or dynamic sort index - #if ALL(SDSORT_USES_RAM, SDSORT_CACHE_NAMES) && DISABLED(SDSORT_DYNAMIC_RAM) + #if ENABLED(SDSORT_CACHE_NAMES) && DISABLED(SDSORT_DYNAMIC_RAM) #define SORTED_LONGNAME_MAXLEN (SDSORT_CACHE_VFATS) * (FILENAME_LENGTH) #define SORTED_LONGNAME_STORAGE (SORTED_LONGNAME_MAXLEN + 1) #else From 33cd1e0767485069d9ab0dda28a6dc269c1e1ec3 Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Sat, 10 Jan 2026 00:28:29 +0100 Subject: [PATCH 16/39] =?UTF-8?q?=F0=9F=90=9B=20Fix,=20optimize=20FTM=20Re?= =?UTF-8?q?sonance=20Test=20(#28266)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/module/ft_motion/resonance_generator.cpp | 14 +++++++++----- Marlin/src/module/ft_motion/resonance_generator.h | 8 +++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Marlin/src/module/ft_motion/resonance_generator.cpp b/Marlin/src/module/ft_motion/resonance_generator.cpp index 40fba11d67..1d2bd553fe 100644 --- a/Marlin/src/module/ft_motion/resonance_generator.cpp +++ b/Marlin/src/module/ft_motion/resonance_generator.cpp @@ -61,7 +61,11 @@ float ResonanceGenerator::fast_sin(float x) { // Negative? The truncation is one too high. if (y < 0.0f) --k; // Correct for negatives - const float r = x - k * M_TAU; // -π <= r <= π + float r = x - k * M_TAU; // -π <= r <= π + if (r > M_PI) + r -= M_TAU; + else if (r < -M_PI) + r += M_TAU; // Cheap polynomial approximation of sin(r) return r * (1.27323954f - 0.405284735f * ABS(r)); @@ -76,8 +80,8 @@ void ResonanceGenerator::fill_stepper_plan_buffer() { while (!ftMotion.stepping.is_full()) { // Calculate current frequency - const float freq = getFrequencyFromTimeline(); - if (freq > rt_params.max_freq) { + current_freq *= freq_mul; + if (current_freq > rt_params.max_freq) { done = true; return; } @@ -85,10 +89,10 @@ void ResonanceGenerator::fill_stepper_plan_buffer() { // Amplitude based on a sinusoidal wave : A = accel / (4 * PI^2 * f^2) //const float accel_magnitude = rt_params.accel_per_hz * freq; //const float amplitude = rt_params.amplitude_correction * accel_magnitude / (4.0f * sq(M_PI) * sq(freq)); - const float amplitude = amplitude_precalc / freq; + const float amplitude = amplitude_precalc / current_freq; // Phase in radians - const float phase = freq * rt_factor; + const float phase = current_freq * rt_factor; // Position Offset : between -A and +A const float pos_offset = amplitude * fast_sin(phase); diff --git a/Marlin/src/module/ft_motion/resonance_generator.h b/Marlin/src/module/ft_motion/resonance_generator.h index 5acc1dc415..06a34e4fd6 100644 --- a/Marlin/src/module/ft_motion/resonance_generator.h +++ b/Marlin/src/module/ft_motion/resonance_generator.h @@ -55,12 +55,16 @@ class ResonanceGenerator { rt_time = t; active = true; done = false; + // Precompute frequency multiplier + current_freq = rt_params.min_freq; + const float inv_octave_duration = 1.0f / rt_params.octave_duration; + freq_mul = exp2f(FTM_TS * inv_octave_duration); } // Return frequency based on timeline float getFrequencyFromTimeline() { // Logarithmic approach with duration per octave - return rt_params.min_freq * 2.0f * exp2f((rt_time / rt_params.octave_duration) - 1); + return rt_params.min_freq * exp2f(timeline / rt_params.octave_duration); } void fill_stepper_plan_buffer(); // Fill stepper plan buffer with trajectory points @@ -76,6 +80,8 @@ class ResonanceGenerator { private: float fast_sin(float x); // Fast sine approximation static float rt_time; // Test timer + float freq_mul; // Frequency multiplier for sine sweeping + float current_freq; // Current frequency being generated in sinusoidal motion static bool active; // Resonance test active static bool done; // Resonance test done }; From 75f0939b4443ad945c1d30c4ff923b75129a873b Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Sat, 10 Jan 2026 01:14:45 +0000 Subject: [PATCH 17/39] [cron] Bump distribution date (2026-01-10) --- 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 56acdba638..db756637f5 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 "2026-01-09" +//#define STRING_DISTRIBUTION_DATE "2026-01-10" /** * 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 741259b688..9ece788407 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 "2026-01-09" + #define STRING_DISTRIBUTION_DATE "2026-01-10" #endif /** From 1e457650f34605c562b84eae35895bc623f09c00 Mon Sep 17 00:00:00 2001 From: David Buezas Date: Sun, 11 Jan 2026 02:44:51 +0100 Subject: [PATCH 18/39] =?UTF-8?q?=F0=9F=90=9B=20Fix=20FT=20Motion=20TMC220?= =?UTF-8?q?8=20shutdown=20(#28257)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Scott Lahteine --- Marlin/src/inc/Conditionals-4-adv.h | 28 +++++++++++++++++++ Marlin/src/module/ft_motion.cpp | 43 +++++++++++++++++++++++++++-- Marlin/src/module/ft_motion.h | 5 ++++ Marlin/src/module/stepper.cpp | 38 ++++++++++++++++--------- buildroot/tests/STM32F103RC_btt | 12 ++++---- 5 files changed, 106 insertions(+), 20 deletions(-) diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h index 75c4efa743..036e426de6 100644 --- a/Marlin/src/inc/Conditionals-4-adv.h +++ b/Marlin/src/inc/Conditionals-4-adv.h @@ -359,6 +359,34 @@ #if ANY(FTM_SHAPER_EI, FTM_SHAPER_2HEI, FTM_SHAPER_3HEI) #define HAS_FTM_EI_SHAPING 1 #endif + + /** + * TMC2208 Direction-Flip Delay + * + * Some TMC2208 / TMC2208_STANDALONE drivers may require a short delay after a DIR change + * to prevent a standstill error, especially when using stealthChop (the standalone default). + * + * When enabled for an axis, FT Motion will hold that axis for > 750µs after a DIR change + * by holding its trajectory coordinate constant for a multiple of FTM_TS frames. For the + * default FTM_FS = 1000, it is a single 1ms frame. + * + * Other axes keep moving normally, and the wait is canceled if the axis flips again. + */ + #if AXIS_DRIVER_TYPE_X(TMC2208) || AXIS_DRIVER_TYPE_X(TMC2208_STANDALONE) + #define FTM_DIR_CHANGE_HOLD_X 1 + #endif + #if AXIS_DRIVER_TYPE_Y(TMC2208) || AXIS_DRIVER_TYPE_Y(TMC2208_STANDALONE) + #define FTM_DIR_CHANGE_HOLD_Y 1 + #endif + #if AXIS_DRIVER_TYPE_Z(TMC2208) || AXIS_DRIVER_TYPE_Z(TMC2208_STANDALONE) + #define FTM_DIR_CHANGE_HOLD_Z 1 + #endif + #if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) + #define FTM_DIR_CHANGE_HOLD_E 1 + #endif + #if ANY(FTM_DIR_CHANGE_HOLD_X, FTM_DIR_CHANGE_HOLD_Y, FTM_DIR_CHANGE_HOLD_Z, FTM_DIR_CHANGE_HOLD_E) + #define HAS_FTM_DIR_CHANGE_HOLD 1 + #endif #endif // Standard Motion diff --git a/Marlin/src/module/ft_motion.cpp b/Marlin/src/module/ft_motion.cpp index f9b7c86998..093fa3b12e 100644 --- a/Marlin/src/module/ft_motion.cpp +++ b/Marlin/src/module/ft_motion.cpp @@ -75,6 +75,10 @@ xyze_pos_t FTMotion::startPos, // (mm) Start position of bl xyze_float_t FTMotion::ratio; // (ratio) Axis move ratio of block float FTMotion::tau = 0.0f; // (s) Time since start of block bool FTMotion::fastForwardUntilMotion = false; // Fast forward time if there is no motion +#if HAS_FTM_DIR_CHANGE_HOLD + xyze_uint_t FTMotion::hold_frames; // Briefly hold motion after direction changes to fix TMC2208 bug + AxisBits FTMotion::last_traj_dir; // Direction of the last trajectory point after shaping, smoothing, ... +#endif // Trajectory generators TrapezoidalTrajectoryGenerator FTMotion::trapezoidalGenerator; @@ -245,7 +249,11 @@ void FTMotion::reset() { TERN_(DISTINCT_E_FACTORS, block_extruder_axis = E_AXIS); moving_axis_flags.reset(); - + last_target_traj.reset(); + #if HAS_FTM_DIR_CHANGE_HOLD + last_traj_dir.reset(); + hold_frames.reset(); + #endif if (did_suspend) stepper.wake_up(); } @@ -412,7 +420,7 @@ bool FTMotion::plan_next_block() { TERN_(FTM_HAS_LIN_ADVANCE, use_advance_lead = current_block->use_advance_lead); axis_move_dir = current_block->direction_bits; - #define _SET_MOVE_END(A) moving_axis_flags.A = moveDist.A ? true : false; + #define _SET_MOVE_END(A) moving_axis_flags.A = bool(moveDist.A); LOGICAL_AXIS_MAP(_SET_MOVE_END); @@ -612,7 +620,38 @@ void FTMotion::fill_stepper_plan_buffer() { // It eliminates idle time when changing smoothing time or shapers and speeds up homing and bed leveling. } else { + + #if HAS_FTM_DIR_CHANGE_HOLD + + // When a flip is detected (and the axis is in stealthChop or is standalone), + // hold that axis' trajectory coordinate constant for at least 750µs. + + #define DIR_FLIP_HOLD_S 0.000'750f + static constexpr uint32_t dir_flip_hold_frames = DIR_FLIP_HOLD_S / (FTM_TS + 1); + + auto start_hold_if_dir_flip = [&](const AxisEnum a) { + const bool dir = traj_coords[a] > last_target_traj[a], + moved = traj_coords[a] != last_target_traj[a], + flipped = moved && (dir != last_traj_dir[a]), + hold = !moved || (flipped && hold_frames[a] > 0); + if (hold) { + if (hold_frames[a]) hold_frames[a]--; + traj_coords[a] = last_target_traj[a]; + } + else { + last_traj_dir[a] = dir; + hold_frames[a] = dir_flip_hold_frames; + } + }; + + #define START_HOLD_IF_DIR_FLIP(A) TERN_(FTM_DIR_CHANGE_HOLD_##A, start_hold_if_dir_flip(_AXIS(A))); + + LOGICAL_AXIS_MAP(START_HOLD_IF_DIR_FLIP); + + #endif // HAS_FTM_DIR_CHANGE_HOLD + fastForwardUntilMotion = false; + // Calculate and store stepper plan in buffer stepping_enqueue(traj_coords); } diff --git a/Marlin/src/module/ft_motion.h b/Marlin/src/module/ft_motion.h index 1d2849f289..ff7e118d1e 100644 --- a/Marlin/src/module/ft_motion.h +++ b/Marlin/src/module/ft_motion.h @@ -332,6 +332,11 @@ class FTMotion { static float tau; // (s) Time since start of block static bool fastForwardUntilMotion; // Fast forward time if there is no motion + #if HAS_FTM_DIR_CHANGE_HOLD + static xyze_uint_t hold_frames; // Briefly hold motion after direction changes to fix TMC2208 bug + static AxisBits last_traj_dir; // Direction of the last trajectory point after shaping, smoothing, ... + #endif + // Trajectory generators static TrapezoidalTrajectoryGenerator trapezoidalGenerator; #if ENABLED(FTM_POLYS) diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 2a10c83800..84924b6f65 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -3376,6 +3376,10 @@ void Stepper::init() { #endif // HAS_ZV_SHAPING +/** + * Position + */ + /** * Set the stepper positions directly in steps * @@ -3502,20 +3506,11 @@ void Stepper::set_axis_position(const AxisEnum a, const int32_t &v) { AVR_ATOMIC_SECTION_END(); } -#endif // HAS_EXTRUDERS +#endif -#if ENABLED(FT_MOTION) - - void Stepper::ftMotion_syncPosition() { - planner.synchronize(); - - // Update stepper positions from the planner - AVR_ATOMIC_SECTION_START(); - count_position = planner.position; - AVR_ATOMIC_SECTION_END(); - } - -#endif // FT_MOTION +/** + * Endstops + */ /** * Record stepper positions and discard the rest of the current block @@ -3561,6 +3556,10 @@ int32_t Stepper::triggered_position(const AxisEnum axis) { return v; } +/** + * Reporting + */ + #if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, MARKFORGED_YX, IS_SCARA, DELTA) #define SAYS_A 1 #endif @@ -3592,6 +3591,15 @@ void Stepper::report_positions() { #if ENABLED(FT_MOTION) + void Stepper::ftMotion_syncPosition() { + planner.synchronize(); + + // Update stepper positions from the planner + AVR_ATOMIC_SECTION_START(); + count_position = planner.position; + AVR_ATOMIC_SECTION_END(); + } + /** * Run stepping for FT Motion from the Stepper ISR at regular short intervals. * @@ -3686,6 +3694,10 @@ void Stepper::report_positions() { #endif // FT_MOTION +/** + * Babystepping + */ + #if ENABLED(BABYSTEPPING) #define _ENABLE_AXIS(A) enable_axis(_AXIS(A)) diff --git a/buildroot/tests/STM32F103RC_btt b/buildroot/tests/STM32F103RC_btt index 7a03dd64fa..9e6019bb91 100755 --- a/buildroot/tests/STM32F103RC_btt +++ b/buildroot/tests/STM32F103RC_btt @@ -11,11 +11,13 @@ set -e # restore_configs opt_set MOTHERBOARD BOARD_BTT_SKR_MINI_E3_V1_0 SERIAL_PORT 1 SERIAL_PORT_2 -1 \ - X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2209 Z_DRIVER_TYPE TMC2209 E0_DRIVER_TYPE TMC2209 \ + X_DRIVER_TYPE TMC2208 Y_DRIVER_TYPE TMC2208 Z_DRIVER_TYPE TMC2208 E0_DRIVER_TYPE TMC2208 \ X_CURRENT_HOME X_CURRENT/2 Y_CURRENT_HOME Y_CURRENT/2 Z_CURRENT_HOME Y_CURRENT/2 -opt_enable CR10_STOCKDISPLAY PINS_DEBUGGING Z_IDLE_HEIGHT EDITABLE_HOMING_CURRENT \ - INPUT_SHAPING_X INPUT_SHAPING_Y FT_MOTION FT_MOTION_MENU FTM_RESONANCE_TEST EMERGENCY_PARSER \ +opt_enable CR10_STOCKDISPLAY EMERGENCY_PARSER Z_IDLE_HEIGHT EDITABLE_HOMING_CURRENT \ + INPUT_SHAPING_X INPUT_SHAPING_Y \ + FT_MOTION FT_MOTION_MENU FTM_RESONANCE_TEST \ BIQU_MICROPROBE_V1 PROBE_ENABLE_DISABLE Z_SAFE_HOMING AUTO_BED_LEVELING_BILINEAR \ - ADAPTIVE_STEP_SMOOTHING LIN_ADVANCE SMOOTH_LIN_ADVANCE NONLINEAR_EXTRUSION + ADAPTIVE_STEP_SMOOTHING LIN_ADVANCE SMOOTH_LIN_ADVANCE NONLINEAR_EXTRUSION \ + PINS_DEBUGGING opt_disable FTM_POLYS -exec_test $1 $2 "BigTreeTech SKR Mini E3 1.0 - TMC2209 HW Serial, FT_MOTION w/out FTM_POLYS" "$3" +exec_test $1 $2 "BigTreeTech SKR Mini E3 1.0 - TMC2208 HW Serial, FT_MOTION w/out FTM_POLYS" "$3" From a5a67325083f5c2eb4e5d7346b1f30574636b93b Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Sun, 11 Jan 2026 02:57:20 +0100 Subject: [PATCH 19/39] =?UTF-8?q?=F0=9F=90=9B=20Fix=20FT=20Motion=20M496?= =?UTF-8?q?=20processing,=20etc.=20(#28261)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Scott Lahteine --- Marlin/src/feature/e_parser.cpp | 2 +- Marlin/src/gcode/feature/ft_motion/M495_M496.cpp | 2 ++ Marlin/src/lcd/menu/menu_motion.cpp | 14 ++++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Marlin/src/feature/e_parser.cpp b/Marlin/src/feature/e_parser.cpp index bda1232154..28a903719c 100644 --- a/Marlin/src/feature/e_parser.cpp +++ b/Marlin/src/feature/e_parser.cpp @@ -150,7 +150,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { case EP_M4: switch (c) { case '1' :state = EP_M41; break; - #if ENABLED(FT_MOTION_RESONANCE_TEST) + #if ENABLED(FTM_RESONANCE_TEST) case '9': state = EP_M49; break; #endif default: state = EP_IGNORE; diff --git a/Marlin/src/gcode/feature/ft_motion/M495_M496.cpp b/Marlin/src/gcode/feature/ft_motion/M495_M496.cpp index 15e1a1fe0f..18787ba32f 100644 --- a/Marlin/src/gcode/feature/ft_motion/M495_M496.cpp +++ b/Marlin/src/gcode/feature/ft_motion/M495_M496.cpp @@ -25,6 +25,7 @@ #if ENABLED(FTM_RESONANCE_TEST) #include "../../gcode.h" +#include "../../../lcd/marlinui.h" #include "../../../module/ft_motion.h" #include "../../../module/ft_motion/resonance_generator.h" @@ -173,6 +174,7 @@ void GcodeSuite::M496() { if (ftMotion.rtg.isActive()) { ftMotion.rtg.abort(); EmergencyParser::rt_stop_by_M496 = false; + ui.refresh(); #if DISABLED(MARLIN_SMALL_BUILD) SERIAL_ECHOLN(F("Resonance Test"), F(" aborted.")); #endif diff --git a/Marlin/src/lcd/menu/menu_motion.cpp b/Marlin/src/lcd/menu/menu_motion.cpp index a9261dc922..59172a4787 100644 --- a/Marlin/src/lcd/menu/menu_motion.cpp +++ b/Marlin/src/lcd/menu/menu_motion.cpp @@ -424,12 +424,18 @@ void menu_move() { if (ftMotion.rtg.isActive() && !ftMotion.rtg.isDone()) { STATIC_ITEM(MSG_FTM_RT_RUNNING); - ACTION_ITEM(MSG_FTM_RT_STOP, []{ ftMotion.rtg.abort(); ui.refresh(); }); + GCODES_ITEM(MSG_FTM_RT_STOP, F("M496")); } else { - GCODES_ITEM_N(X_AXIS, MSG_FTM_RT_START_N, F("M495 X S")); - GCODES_ITEM_N(Y_AXIS, MSG_FTM_RT_START_N, F("M495 Y S")); - GCODES_ITEM_N(Z_AXIS, MSG_FTM_RT_START_N, F("M495 Z S")); + #if HAS_X_AXIS + GCODES_ITEM_N(X_AXIS, MSG_FTM_RT_START_N, F("M495 X S")); + #endif + #if HAS_Y_AXIS + GCODES_ITEM_N(Y_AXIS, MSG_FTM_RT_START_N, F("M495 Y S")); + #endif + #if HAS_Z_AXIS + GCODES_ITEM_N(Z_AXIS, MSG_FTM_RT_START_N, F("M495 Z S")); + #endif SUBMENU(MSG_FTM_RETRIEVE_FREQ, menu_ftm_resonance_freq); } From 468ad6f79e994b45ed3cea6cda4957fcfc6155ae Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 10 Jan 2026 20:24:13 -0600 Subject: [PATCH 20/39] =?UTF-8?q?=F0=9F=8E=A8=20Apostrophe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/HAL/SAMD21/endstop_interrupts.h | 2 +- Marlin/src/HAL/SAMD51/endstop_interrupts.h | 2 +- Marlin/src/HAL/STM32F1/endstop_interrupts.h | 2 +- Marlin/src/lcd/tft_io/st7735.h | 4 ++-- Marlin/src/module/ft_motion.cpp | 4 ++-- Marlin/src/module/planner.cpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Marlin/src/HAL/SAMD21/endstop_interrupts.h b/Marlin/src/HAL/SAMD21/endstop_interrupts.h index 4ef075f8f9..8d8f9cd06f 100644 --- a/Marlin/src/HAL/SAMD21/endstop_interrupts.h +++ b/Marlin/src/HAL/SAMD21/endstop_interrupts.h @@ -32,7 +32,7 @@ * On SAMD21, all pins support external interrupt capability. * Any pin can be used for external interrupts, but there are some restrictions. * At most 16 different external interrupts can be used at one time. - * Further, you can’t just pick any 16 pins to use. This is because every pin on the SAMD21 + * Further, you can't just pick any 16 pins to use. This is because every pin on the SAMD21 * connects to what is called an EXTINT line, and only one pin per EXTINT line can be used for external * interrupts at a time */ diff --git a/Marlin/src/HAL/SAMD51/endstop_interrupts.h b/Marlin/src/HAL/SAMD51/endstop_interrupts.h index 34c238ba5c..4e342f754f 100644 --- a/Marlin/src/HAL/SAMD51/endstop_interrupts.h +++ b/Marlin/src/HAL/SAMD51/endstop_interrupts.h @@ -31,7 +31,7 @@ * On SAMD51, all pins support external interrupt capability. * Any pin can be used for external interrupts, but there are some restrictions. * At most 16 different external interrupts can be used at one time. - * Further, you can’t just pick any 16 pins to use. This is because every pin on the SAMD51 + * Further, you can't just pick any 16 pins to use. This is because every pin on the SAMD51 * connects to what is called an EXTINT line, and only one pin per EXTINT line can be used for external * interrupts at a time */ diff --git a/Marlin/src/HAL/STM32F1/endstop_interrupts.h b/Marlin/src/HAL/STM32F1/endstop_interrupts.h index 6724bf3456..f8f21a4c6d 100644 --- a/Marlin/src/HAL/STM32F1/endstop_interrupts.h +++ b/Marlin/src/HAL/STM32F1/endstop_interrupts.h @@ -27,7 +27,7 @@ * On STM32F, all pins support external interrupt capability. * Any pin can be used for external interrupts, but there are some restrictions. * At most 16 different external interrupts can be used at one time. - * Further, you can’t just pick any 16 pins to use. This is because every pin on the STM32 + * Further, you can't just pick any 16 pins to use. This is because every pin on the STM32 * connects to what is called an EXTI line, and only one pin per EXTI line can be used for external interrupts at a time * Check the Reference Manual of the MCU to confirm which line is used by each pin */ diff --git a/Marlin/src/lcd/tft_io/st7735.h b/Marlin/src/lcd/tft_io/st7735.h index 1b0d23b6c4..f94c82be03 100644 --- a/Marlin/src/lcd/tft_io/st7735.h +++ b/Marlin/src/lcd/tft_io/st7735.h @@ -101,8 +101,8 @@ #define ST7735_NVFCTR1 0xD9 // EEPROM Control Status #define ST7735_NVFCTR2 0xDE // EEPROM Read Command #define ST7735_NVFCTR3 0xDF // EEPROM Write Command -#define ST7735_GMCTRP1 0xE0 // Gamma (‘+’polarity) Correction Characteristics Setting -#define ST7735_GMCTRN1 0xE1 // GMCTRN1 (E1h): Gamma ‘-’polarity Correction Characteristics Setting +#define ST7735_GMCTRP1 0xE0 // Gamma ('+'polarity) Correction Characteristics Setting +#define ST7735_GMCTRN1 0xE1 // GMCTRN1 (E1h): Gamma '-'polarity Correction Characteristics Setting #define ST7735_EXTCTRL 0xF0 // Extension Command Control #define ST7735_VCOM4L 0xFF // Vcom 4 Level Control diff --git a/Marlin/src/module/ft_motion.cpp b/Marlin/src/module/ft_motion.cpp index 093fa3b12e..c0c475a0a0 100644 --- a/Marlin/src/module/ft_motion.cpp +++ b/Marlin/src/module/ft_motion.cpp @@ -594,7 +594,7 @@ void FTMotion::fill_stepper_plan_buffer() { float total_duration = currentGenerator->getTotalDuration(); // If the current plan is empty, it will have zero duration. while (tau + FTM_TS > total_duration) { /** - * We’ve reached the end of the current block. + * We've reached the end of the current block. * * `tau` is the time that has elapsed inside this block. After a block is finished, the next one may * start at any point between *just before* the last sampled time (one step earlier, i.e. `-FTM_TS`) @@ -603,7 +603,7 @@ void FTMotion::fill_stepper_plan_buffer() { * * To account for that uncertainty we simply subtract the duration of the finished block from `tau`. * This brings us back to a time value that is valid for the next block, while still allowing the next - * block’s start to be offset by up to one time step into the past. + * block's start to be offset by up to one time step into the past. */ tau -= total_duration; const bool plan_available = plan_next_block(); diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index b11740b00a..b4e9eb5f09 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -786,7 +786,7 @@ block_t* Planner::get_future_block(const uint8_t offset) { * Calculate trapezoid (or or update FTM) motion parameters for a block. * * `entry_speed` is an optional override in mm/s. - * A value of `0` is a sentinel meaning “do not override the block’s + * A value of `0` is a sentinel meaning “do not override the block's * existing entry speed / initial_rate.” * * This is relied upon by recalculate_trapezoids(), which intentionally From 6ec2a395e1653a9556b995a79fbd5565769d7f39 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Sun, 11 Jan 2026 06:11:29 +0000 Subject: [PATCH 21/39] [cron] Bump distribution date (2026-01-11) --- 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 db756637f5..b2c2e05b5a 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 "2026-01-10" +//#define STRING_DISTRIBUTION_DATE "2026-01-11" /** * 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 9ece788407..80725b2703 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 "2026-01-10" + #define STRING_DISTRIBUTION_DATE "2026-01-11" #endif /** From 3bf6b3dee914f34ab9679953ef4441c1c50570f9 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 11 Jan 2026 01:05:53 -0600 Subject: [PATCH 22/39] =?UTF-8?q?=F0=9F=90=9B=20Fix=20FT=20Motion=20TMC220?= =?UTF-8?q?8=20shutdown=20(2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Followup to #28257 --- Marlin/src/module/ft_motion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/module/ft_motion.cpp b/Marlin/src/module/ft_motion.cpp index c0c475a0a0..d7aeac2aa8 100644 --- a/Marlin/src/module/ft_motion.cpp +++ b/Marlin/src/module/ft_motion.cpp @@ -627,7 +627,7 @@ void FTMotion::fill_stepper_plan_buffer() { // hold that axis' trajectory coordinate constant for at least 750µs. #define DIR_FLIP_HOLD_S 0.000'750f - static constexpr uint32_t dir_flip_hold_frames = DIR_FLIP_HOLD_S / (FTM_TS + 1); + static constexpr uint32_t dir_flip_hold_frames = 1 + (DIR_FLIP_HOLD_S) / (FTM_TS); auto start_hold_if_dir_flip = [&](const AxisEnum a) { const bool dir = traj_coords[a] > last_target_traj[a], From c1e01eee35f7bb2f0fd583f869bdcf0c1daa6060 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Mon, 12 Jan 2026 00:37:14 +0000 Subject: [PATCH 23/39] [cron] Bump distribution date (2026-01-12) --- 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 b2c2e05b5a..d257cd11f5 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 "2026-01-11" +//#define STRING_DISTRIBUTION_DATE "2026-01-12" /** * 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 80725b2703..58ce89c489 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 "2026-01-11" + #define STRING_DISTRIBUTION_DATE "2026-01-12" #endif /** From 2048e8b1734c968635682c491117ced0bf22f412 Mon Sep 17 00:00:00 2001 From: Reimnop <56994757+Reimnop@users.noreply.github.com> Date: Wed, 14 Jan 2026 05:28:16 +0700 Subject: [PATCH 24/39] =?UTF-8?q?=E2=9C=A8=20Permit=20disable=20REINIT=5FN?= =?UTF-8?q?OISY=5FLCD=20(#28273)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Scott Lahteine --- Marlin/Configuration_adv.h | 6 ++++++ Marlin/src/inc/Conditionals-5-post.h | 8 +++----- Marlin/src/inc/Warnings.cpp | 9 +++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index e46a3af2de..94980a28e4 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -1782,6 +1782,12 @@ */ //#define SD_SPI_SPEED SPI_HALF_SPEED + /** + * Reinit the LCD after SD Card insert/remove or when entering the menu. + * Required for some LCDs that use shared SPI with an external SD Card reader. + */ + #define REINIT_NOISY_LCD + // The standard SD detect circuit reads LOW when media is inserted and HIGH when empty. // Enable this option and set to HIGH if your SD cards are incorrectly detected. //#define SD_DETECT_STATE HIGH diff --git a/Marlin/src/inc/Conditionals-5-post.h b/Marlin/src/inc/Conditionals-5-post.h index 024eb42f51..b9eeb3d85a 100644 --- a/Marlin/src/inc/Conditionals-5-post.h +++ b/Marlin/src/inc/Conditionals-5-post.h @@ -605,11 +605,9 @@ #endif #endif - #if HAS_SD_DETECT && NONE(HAS_GRAPHICAL_TFT, LCD_USE_DMA_FSMC, HAS_FSMC_GRAPHICAL_TFT, HAS_SPI_GRAPHICAL_TFT, IS_DWIN_MARLINUI, EXTENSIBLE_UI, HAS_DWIN_E3V2, HAS_U8GLIB_I2C_OLED) - #define REINIT_NOISY_LCD 1 // Have the LCD re-init on SD insertion - #endif - -#endif // HAS_MEDIA +#else // !HAS_MEDIA + #undef REINIT_NOISY_LCD +#endif /** * Power Supply diff --git a/Marlin/src/inc/Warnings.cpp b/Marlin/src/inc/Warnings.cpp index 9c4eabaf6e..1164fe1bb4 100644 --- a/Marlin/src/inc/Warnings.cpp +++ b/Marlin/src/inc/Warnings.cpp @@ -993,3 +993,12 @@ #if ALL(SMOOTH_LIN_ADVANCE, MIXING_EXTRUDER) #warning "SMOOTH_LIN_ADVANCE with MIXING_EXTRUDER is untested. Use with caution." #endif + +/** + * Some LCDs need re-init to deal with flaky SPI bus sharing + */ +#if HAS_SD_DETECT && NONE(REINIT_NOISY_LCD, HAS_GRAPHICAL_TFT, LCD_USE_DMA_FSMC, HAS_FSMC_GRAPHICAL_TFT, HAS_SPI_GRAPHICAL_TFT, IS_DWIN_MARLINUI, EXTENSIBLE_UI, HAS_DWIN_E3V2, HAS_U8GLIB_I2C_OLED) + #warning "It is recommended to enable REINIT_NOISY_LCD with your LCD controller model." +#elif ENABLED(REINIT_NOISY_LCD) + #warning "REINIT_NOISY_LCD is probably not required with your LCD controller model." +#endif From a73b08eba55f24c44a34b572ede9840af194872d Mon Sep 17 00:00:00 2001 From: David Buezas Date: Wed, 14 Jan 2026 00:27:20 +0100 Subject: [PATCH 25/39] =?UTF-8?q?=F0=9F=90=9B=20Fix=20FTM=20E=20value=20ov?= =?UTF-8?q?erflow;=20=F0=9F=94=A7=20FTM=5FDIR=5FCHANGE=5FHOLD=5F*=20(#2827?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Scott Lahteine --- Marlin/Configuration_adv.h | 19 +++++++++++++++++++ Marlin/src/inc/Conditionals-4-adv.h | 24 ------------------------ Marlin/src/module/ft_motion.cpp | 7 +++++-- Marlin/src/module/ft_motion.h | 2 +- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 94980a28e4..31d3b8364b 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -1238,6 +1238,25 @@ #define FTM_FS 1000 // (Hz) Frequency for trajectory generation. #define FTM_MIN_SHAPE_FREQ 20 // (Hz) Minimum shaping frequency, lower consumes more RAM + /** + * TMC2208 / TMC2208_STANDALONE drivers require a brief pause after a DIR change + * to prevent a standstill shutdown when using StealthChop (the standalone default). + * These options cause FT Motion to delay for > 750µs after a DIR change on a given axis. + * Disable only if you are certain that this can never happen with your TMC2208s. + */ + #if AXIS_DRIVER_TYPE_X(TMC2208) || AXIS_DRIVER_TYPE_X(TMC2208_STANDALONE) + #define FTM_DIR_CHANGE_HOLD_X + #endif + #if AXIS_DRIVER_TYPE_Y(TMC2208) || AXIS_DRIVER_TYPE_Y(TMC2208_STANDALONE) + #define FTM_DIR_CHANGE_HOLD_Y + #endif + #if AXIS_DRIVER_TYPE_Z(TMC2208) || AXIS_DRIVER_TYPE_Z(TMC2208_STANDALONE) + #define FTM_DIR_CHANGE_HOLD_Z + #endif + #if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) + #define FTM_DIR_CHANGE_HOLD_E + #endif + #endif // FT_MOTION /** diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h index 036e426de6..5943b513e7 100644 --- a/Marlin/src/inc/Conditionals-4-adv.h +++ b/Marlin/src/inc/Conditionals-4-adv.h @@ -360,30 +360,6 @@ #define HAS_FTM_EI_SHAPING 1 #endif - /** - * TMC2208 Direction-Flip Delay - * - * Some TMC2208 / TMC2208_STANDALONE drivers may require a short delay after a DIR change - * to prevent a standstill error, especially when using stealthChop (the standalone default). - * - * When enabled for an axis, FT Motion will hold that axis for > 750µs after a DIR change - * by holding its trajectory coordinate constant for a multiple of FTM_TS frames. For the - * default FTM_FS = 1000, it is a single 1ms frame. - * - * Other axes keep moving normally, and the wait is canceled if the axis flips again. - */ - #if AXIS_DRIVER_TYPE_X(TMC2208) || AXIS_DRIVER_TYPE_X(TMC2208_STANDALONE) - #define FTM_DIR_CHANGE_HOLD_X 1 - #endif - #if AXIS_DRIVER_TYPE_Y(TMC2208) || AXIS_DRIVER_TYPE_Y(TMC2208_STANDALONE) - #define FTM_DIR_CHANGE_HOLD_Y 1 - #endif - #if AXIS_DRIVER_TYPE_Z(TMC2208) || AXIS_DRIVER_TYPE_Z(TMC2208_STANDALONE) - #define FTM_DIR_CHANGE_HOLD_Z 1 - #endif - #if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE) - #define FTM_DIR_CHANGE_HOLD_E 1 - #endif #if ANY(FTM_DIR_CHANGE_HOLD_X, FTM_DIR_CHANGE_HOLD_Y, FTM_DIR_CHANGE_HOLD_Z, FTM_DIR_CHANGE_HOLD_E) #define HAS_FTM_DIR_CHANGE_HOLD 1 #endif diff --git a/Marlin/src/module/ft_motion.cpp b/Marlin/src/module/ft_motion.cpp index d7aeac2aa8..93101a5a93 100644 --- a/Marlin/src/module/ft_motion.cpp +++ b/Marlin/src/module/ft_motion.cpp @@ -380,7 +380,7 @@ bool FTMotion::plan_next_block() { if (current_block->is_sync_pos()) stepper._set_position(current_block->position); continue; } - ensure_float_precision(); + ensure_extruder_float_precision(); #if ENABLED(POWER_LOSS_RECOVERY) recovery.info.sdpos = current_block->sdpos; @@ -441,7 +441,7 @@ bool FTMotion::plan_next_block() { * resolution = 2^(floor(log2(|x|)) - 23) * By resetting at ±1'000mm (1 meter), we get a minimum resolution of ~ 0.00006mm, enough for smoothing to work well. */ - void FTMotion::ensure_float_precision() { + void FTMotion::ensure_extruder_float_precision() { constexpr float FTM_POSITION_WRAP_THRESHOLD = 1000; // (mm) Reset when position exceeds this to prevent floating point precision loss if (ABS(endPos_prevBlock.E) < FTM_POSITION_WRAP_THRESHOLD) return; @@ -462,6 +462,9 @@ bool FTMotion::plan_next_block() { // Offset linear advance previous position prev_traj_e += offset; + // Make sure the difference is accounted-for in the past + last_target_traj.e += offset; + // Offset stepper current position const int64_t delta_steps_q48_16 = offset * planner.settings.axis_steps_per_mm[block_extruder_axis] * (1ULL << 16); stepping.curr_steps_q48_16.E += delta_steps_q48_16; diff --git a/Marlin/src/module/ft_motion.h b/Marlin/src/module/ft_motion.h index ff7e118d1e..15be0154ae 100644 --- a/Marlin/src/module/ft_motion.h +++ b/Marlin/src/module/ft_motion.h @@ -403,7 +403,7 @@ class FTMotion { static void fill_stepper_plan_buffer(); static xyze_float_t calc_traj_point(const float dist); static bool plan_next_block(); - static void ensure_float_precision() IF_DISABLED(HAS_EXTRUDERS, {}); + static void ensure_extruder_float_precision() IF_DISABLED(HAS_EXTRUDERS, {}); }; // class FTMotion From e0f73f51ee9db2e554dd55e20587b3d239bec002 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Tue, 13 Jan 2026 17:50:05 -0600 Subject: [PATCH 26/39] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Macro?= =?UTF-8?q?s=20*=5FAXIS=5FANY/ALL/NONE=20(#28275)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/core/types.h | 80 ++++++++++++++++++------------ Marlin/src/gcode/calibrate/G28.cpp | 2 +- Marlin/src/module/motion.cpp | 2 +- Marlin/src/module/planner.cpp | 17 ++----- Marlin/src/module/planner.h | 18 +++++-- Marlin/src/module/stepper.cpp | 14 +++--- 6 files changed, 73 insertions(+), 60 deletions(-) diff --git a/Marlin/src/core/types.h b/Marlin/src/core/types.h index 0881bc47a1..159a2e265c 100644 --- a/Marlin/src/core/types.h +++ b/Marlin/src/core/types.h @@ -52,6 +52,11 @@ template struct IF { typedef L type; }; #define MAIN_AXIS_NAMES_LC NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w) #define NUM_AXIS_CALL(G) do { NUM_AXIS_CODE(G(X_AXIS), G(Y_AXIS), G(Z_AXIS), G(I_AXIS), G(J_AXIS), G(K_AXIS), G(U_AXIS), G(V_AXIS), G(W_AXIS)); } while(0) #define STR_AXES_MAIN NUM_AXIS_GANG("X", "Y", "Z", STR_I, STR_J, STR_K, STR_U, STR_V, STR_W) +#define NUM_AXIS_ANY(x, y, z, i, j, k, u, v, w) (false \ + NUM_AXIS_GANG(|| (x), || (y), || (z), || (i), || (j), || (k), || (u), || (v), || (w))) +#define NUM_AXIS_ALL(x, y, z, i, j, k, u, v, w) ((NUM_AXES > 0) \ + NUM_AXIS_GANG(&& (x), && (y), && (z), && (i), && (j), && (k), && (u), && (v), && (w))) +#define NUM_AXIS_NONE(V...) !NUM_AXIS_ANY(V) #define LOGICAL_AXIS_GANG(N,V...) NUM_AXIS_GANG(V) GANG_ITEM_E(N) #define LOGICAL_AXIS_CODE(N,V...) NUM_AXIS_CODE(V) CODE_ITEM_E(N) @@ -71,6 +76,11 @@ template struct IF { typedef L type; }; #define LOGICAL_AXIS_MAP_LC(F) MAP(F, LOGICAL_AXIS_NAMES_LC) #define LOGICAL_AXIS_CALL(G) do { LOGICAL_AXIS_CODE(G(E_AXIS), G(X_AXIS), G(Y_AXIS), G(Z_AXIS), G(I_AXIS), G(J_AXIS), G(K_AXIS), G(U_AXIS), G(V_AXIS), G(W_AXIS)); } while(0) #define STR_AXES_LOGICAL LOGICAL_AXIS_GANG("E", "X", "Y", "Z", STR_I, STR_J, STR_K, STR_U, STR_V, STR_W) +#define LOGICAL_AXIS_ANY(e, x, y, z, i, j, k, u, v, w) (false \ + LOGICAL_AXIS_GANG(|| (e), || (x), || (y), || (z), || (i), || (j), || (k), || (u), || (v), || (w))) +#define LOGICAL_AXIS_ALL(e, x, y, z, i, j, k, u, v, w) ((LOGICAL_AXES > 0) \ + LOGICAL_AXIS_GANG(&& (e), && (x), && (y), && (z), && (i), && (j), && (k), && (u), && (v), && (w))) +#define LOGICAL_AXIS_NONE(V...) !LOGICAL_AXIS_ANY(V) #define NUM_AXIS_PAIRED_LIST(V...) LIST_N(DOUBLE(NUM_AXES), V) #define LOGICAL_AXIS_PAIRED_LIST(EA,EB,V...) NUM_AXIS_PAIRED_LIST(V) LIST_ITEM_E(EA) LIST_ITEM_E(EB) @@ -145,10 +155,14 @@ template struct IF { typedef L type; }; #define XY_ARRAY(V...) ARRAY_N(XY_COUNT, V) #define XY_CODE(V...) CODE_N(XY_COUNT, V) #define XY_GANG(V...) GANG_N(XY_COUNT, V) +#define XY_ANY(x,y) (false XY_GANG(|| (x), || (y))) +#define XY_ALL(x,y) ((NUM_AXES > 0) XY_GANG(&& (x), && (y))) #define XYZ_LIST(V...) LIST_N(XYZ_COUNT, V) #define XYZ_ARRAY(V...) ARRAY_N(XYZ_COUNT, V) #define XYZ_CODE(V...) CODE_N(XYZ_COUNT, V) #define XYZ_GANG(V...) GANG_N(XYZ_COUNT, V) +#define XYZ_ANY(x,y,z) (false XYZ_GANG(|| (x), || (y), || (z))) +#define XYZ_ALL(x,y,z) ((NUM_AXES > 0) XYZ_GANG(&& (x), && (y), && (z))) #if HAS_ROTATIONAL_AXES #define ROTATIONAL_AXIS_GANG(V...) GANG_N(ROTATIONAL_AXES, V) @@ -646,8 +660,8 @@ struct XYval { // Exact comparisons. For floats a "NEAR" operation may be better. FI bool operator==(const XYval &rs) const { return x == rs.x && y == rs.y; } - FI bool operator==(const XYZval &rs) const { return ENABLED(HAS_X_AXIS) XY_GANG(&& x == rs.x, && y == rs.y); } - FI bool operator==(const XYZEval &rs) const { return ENABLED(HAS_X_AXIS) XY_GANG(&& x == rs.x, && y == rs.y); } + FI bool operator==(const XYZval &rs) const { return XY_ALL(x == rs.x, y == rs.y); } + FI bool operator==(const XYZEval &rs) const { return XY_ALL(x == rs.x, y == rs.y); } FI bool operator!=(const XYval &rs) const { return !operator==(rs); } FI bool operator!=(const XYZval &rs) const { return !operator==(rs); } FI bool operator!=(const XYZEval &rs) const { return !operator==(rs); } @@ -661,15 +675,15 @@ struct XYval { FI bool operator> (const XYval &rs) const { return x > rs.x && y > rs.y; } FI bool operator>=(const XYval &rs) const { return x >= rs.x && y >= rs.y; } - FI bool operator< (const XYZval &rs) const { return true XY_GANG(&& x < rs.x, && y < rs.y); } - FI bool operator<=(const XYZval &rs) const { return true XY_GANG(&& x <= rs.x, && y <= rs.y); } - FI bool operator> (const XYZval &rs) const { return true XY_GANG(&& x > rs.x, && y > rs.y); } - FI bool operator>=(const XYZval &rs) const { return true XY_GANG(&& x >= rs.x, && y >= rs.y); } + FI bool operator< (const XYZval &rs) const { return XY_ALL(x < rs.x, y < rs.y); } + FI bool operator<=(const XYZval &rs) const { return XY_ALL(x <= rs.x, y <= rs.y); } + FI bool operator> (const XYZval &rs) const { return XY_ALL(x > rs.x, y > rs.y); } + FI bool operator>=(const XYZval &rs) const { return XY_ALL(x >= rs.x, y >= rs.y); } - FI bool operator< (const XYZEval &rs) const { return true XY_GANG(&& x < rs.x, && y < rs.y); } - FI bool operator<=(const XYZEval &rs) const { return true XY_GANG(&& x <= rs.x, && y <= rs.y); } - FI bool operator> (const XYZEval &rs) const { return true XY_GANG(&& x > rs.x, && y > rs.y); } - FI bool operator>=(const XYZEval &rs) const { return true XY_GANG(&& x >= rs.x, && y >= rs.y); } + FI bool operator< (const XYZEval &rs) const { return XY_ALL(x < rs.x, y < rs.y); } + FI bool operator<=(const XYZEval &rs) const { return XY_ALL(x <= rs.x, y <= rs.y); } + FI bool operator> (const XYZEval &rs) const { return XY_ALL(x > rs.x, y > rs.y); } + FI bool operator>=(const XYZEval &rs) const { return XY_ALL(x >= rs.x, y >= rs.y); } }; @@ -738,7 +752,7 @@ struct XYZval { // Pointer to the data as a simple array explicit FI operator T* () { return pos; } // If any element is true then it's true - FI constexpr operator bool() const { return 0 NUM_AXIS_GANG(|| x, || y, || z, || i, || j, || k, || u, || v, || w); } + FI constexpr operator bool() const { return NUM_AXIS_ANY(x, y, z, i, j, k, u, v, w); } // Smallest element FI constexpr T small() const { return TERN0(HAS_X_AXIS, _MIN(NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w))); } // Largest element @@ -824,22 +838,22 @@ struct XYZval { FI XYZval& operator<<=(const int &p) { NUM_AXIS_CODE(_LSE(x), _LSE(y), _LSE(z), _LSE(i), _LSE(j), _LSE(k), _LSE(u), _LSE(v), _LSE(w)); return *this; } // Exact comparisons. For floats a "NEAR" operation may be better. - FI bool operator==(const XYZEval &rs) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } + FI bool operator==(const XYZEval &rs) const { return NUM_AXIS_ALL(x == rs.x, y == rs.y, z == rs.z, i == rs.i, j == rs.j, k == rs.k, u == rs.u, v == rs.v, w == rs.w); } FI bool operator!=(const XYZEval &rs) const { return !operator==(rs); } // Exact comparison to a single value - FI bool operator==(const T &p) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == p, && y == p, && z == p, && i == p, && j == p, && k == p, && u == p, && v == p, && w == p); } + FI bool operator==(const T &p) const { return NUM_AXIS_ALL(x == p, y == p, z == p, i == p, j == p, k == p, u == p, v == p, w == p); } FI bool operator!=(const T &p) const { return !operator==(p); } - FI bool operator< (const XYZval &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); } - FI bool operator<=(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); } - FI bool operator> (const XYZval &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); } - FI bool operator>=(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); } + FI bool operator< (const XYZval &rs) const { return NUM_AXIS_ALL(x < rs.x, y < rs.y, z < rs.z, i < rs.i, j < rs.j, k < rs.k, u < rs.u, v < rs.v, w < rs.w); } + FI bool operator<=(const XYZval &rs) const { return NUM_AXIS_ALL(x <= rs.x, y <= rs.y, z <= rs.z, i <= rs.i, j <= rs.j, k <= rs.k, u <= rs.u, v <= rs.v, w <= rs.w); } + FI bool operator> (const XYZval &rs) const { return NUM_AXIS_ALL(x > rs.x, y > rs.y, z > rs.z, i > rs.i, j > rs.j, k > rs.k, u > rs.u, v > rs.v, w > rs.w); } + FI bool operator>=(const XYZval &rs) const { return NUM_AXIS_ALL(x >= rs.x, y >= rs.y, z >= rs.z, i >= rs.i, j >= rs.j, k >= rs.k, u >= rs.u, v >= rs.v, w >= rs.w); } - FI bool operator< (const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); } - FI bool operator<=(const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); } - FI bool operator> (const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); } - FI bool operator>=(const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); } + FI bool operator< (const XYZEval &rs) const { return NUM_AXIS_ALL(x < rs.x, y < rs.y, z < rs.z, i < rs.i, j < rs.j, k < rs.k, u < rs.u, v < rs.v, w < rs.w); } + FI bool operator<=(const XYZEval &rs) const { return NUM_AXIS_ALL(x <= rs.x, y <= rs.y, z <= rs.z, i <= rs.i, j <= rs.j, k <= rs.k, u <= rs.u, v <= rs.v, w <= rs.w); } + FI bool operator> (const XYZEval &rs) const { return NUM_AXIS_ALL(x > rs.x, y > rs.y, z > rs.z, i > rs.i, j > rs.j, k > rs.k, u > rs.u, v > rs.v, w > rs.w); } + FI bool operator>=(const XYZEval &rs) const { return NUM_AXIS_ALL(x >= rs.x, y >= rs.y, z >= rs.z, i >= rs.i, j >= rs.j, k >= rs.k, u >= rs.u, v >= rs.v, w >= rs.w); } }; @@ -909,7 +923,7 @@ struct XYZEval { // Pointer to the data as a simple array explicit FI operator T* () { return pos; } // If any element is true then it's true - FI constexpr operator bool() const { return 0 LOGICAL_AXIS_GANG(|| e, || x, || y, || z, || i, || j, || k, || u, || v, || w); } + FI constexpr operator bool() const { return LOGICAL_AXIS_ANY(e, x, y, z, i, j, k, u, v, w); } // Smallest element FI constexpr T small() const { return _MIN(LOGICAL_AXIS_LIST(e, x, y, z, i, j, k, u, v, w)); } // Largest element @@ -998,24 +1012,24 @@ struct XYZEval { FI XYZEval& operator<<=(const int &p) { LOGICAL_AXIS_CODE(_LSE(e), _LSE(x), _LSE(y), _LSE(z), _LSE(i), _LSE(j), _LSE(k), _LSE(u), _LSE(v), _LSE(w)); return *this; } // Exact comparisons. For floats a "NEAR" operation may be better. - FI bool operator==(const XYZval &rs) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } - FI bool operator==(const XYZEval &rs) const { return ANY(HAS_X_AXIS, HAS_EXTRUDERS) LOGICAL_AXIS_GANG(&& e == rs.e, && x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } + FI bool operator==(const XYZval &rs) const { return NUM_AXIS_ALL(x == rs.x, y == rs.y, z == rs.z, i == rs.i, j == rs.j, k == rs.k, u == rs.u, v == rs.v, w == rs.w); } + FI bool operator==(const XYZEval &rs) const { return LOGICAL_AXIS_ALL(e == rs.e, x == rs.x, y == rs.y, z == rs.z, i == rs.i, j == rs.j, k == rs.k, u == rs.u, v == rs.v, w == rs.w); } FI bool operator!=(const XYZval &rs) const { return !operator==(rs); } FI bool operator!=(const XYZEval &rs) const { return !operator==(rs); } // Exact comparison to a single value - FI bool operator==(const T &p) const { return ENABLED(HAS_X_AXIS) LOGICAL_AXIS_GANG(&& e == p, && x == p, && y == p, && z == p, && i == p, && j == p, && k == p, && u == p, && v == p, && w == p); } + FI bool operator==(const T &p) const { return LOGICAL_AXIS_ALL(e == p, x == p, y == p, z == p, i == p, j == p, k == p, u == p, v == p, w == p); } FI bool operator!=(const T &p) const { return !operator==(p); } - FI bool operator< (const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e < rs.e, && x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); } - FI bool operator<=(const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e <= rs.e, && x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); } - FI bool operator> (const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e > rs.e, && x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); } - FI bool operator>=(const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e >= rs.e, && x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); } + FI bool operator< (const XYZEval &rs) const { return LOGICAL_AXIS_ALL(e < rs.e, x < rs.x, y < rs.y, z < rs.z, i < rs.i, j < rs.j, k < rs.k, u < rs.u, v < rs.v, w < rs.w); } + FI bool operator<=(const XYZEval &rs) const { return LOGICAL_AXIS_ALL(e <= rs.e, x <= rs.x, y <= rs.y, z <= rs.z, i <= rs.i, j <= rs.j, k <= rs.k, u <= rs.u, v <= rs.v, w <= rs.w); } + FI bool operator> (const XYZEval &rs) const { return LOGICAL_AXIS_ALL(e > rs.e, x > rs.x, y > rs.y, z > rs.z, i > rs.i, j > rs.j, k > rs.k, u > rs.u, v > rs.v, w > rs.w); } + FI bool operator>=(const XYZEval &rs) const { return LOGICAL_AXIS_ALL(e >= rs.e, x >= rs.x, y >= rs.y, z >= rs.z, i >= rs.i, j >= rs.j, k >= rs.k, u >= rs.u, v >= rs.v, w >= rs.w); } - FI bool operator< (const XYZval &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); } - FI bool operator<=(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); } - FI bool operator> (const XYZval &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); } - FI bool operator>=(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); } + FI bool operator< (const XYZval &rs) const { return NUM_AXIS_ALL(x < rs.x, y < rs.y, z < rs.z, i < rs.i, j < rs.j, k < rs.k, u < rs.u, v < rs.v, w < rs.w); } + FI bool operator<=(const XYZval &rs) const { return NUM_AXIS_ALL(x <= rs.x, y <= rs.y, z <= rs.z, i <= rs.i, j <= rs.j, k <= rs.k, u <= rs.u, v <= rs.v, w <= rs.w); } + FI bool operator> (const XYZval &rs) const { return NUM_AXIS_ALL(x > rs.x, y > rs.y, z > rs.z, i > rs.i, j > rs.j, k > rs.k, u > rs.u, v > rs.v, w > rs.w); } + FI bool operator>=(const XYZval &rs) const { return NUM_AXIS_ALL(x >= rs.x, y >= rs.y, z >= rs.z, i >= rs.i, j >= rs.j, k >= rs.k, u >= rs.u, v >= rs.v, w >= rs.w); } }; diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp index 4a0ecffc4b..dabb53b47b 100644 --- a/Marlin/src/gcode/calibrate/G28.cpp +++ b/Marlin/src/gcode/calibrate/G28.cpp @@ -377,7 +377,7 @@ void GcodeSuite::G28() { float z_homing_height = seenR ? parser.value_linear_units() : Z_CLEARANCE_FOR_HOMING; // Check for any lateral motion that might require clearance - const bool may_skate = seenR NUM_AXIS_GANG(|| doX, || doY, || TERN0(Z_SAFE_HOMING, doZ), || doI, || doJ, || doK, || doU, || doV, || doW); + const bool may_skate = seenR && NUM_AXIS_ANY(doX, doY, TERN0(Z_SAFE_HOMING, doZ), doI, doJ, doK, doU, doV, doW); if (seenR && z_homing_height == 0) { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("R0 = No Z raise"); diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 1012cc4150..8a92482d82 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1438,7 +1438,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) { float get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXES, bool &is_cartesian_move)) { #if NUM_AXES - if (!(NUM_AXIS_GANG(diff.x, || diff.y, /* skip z */, || diff.i, || diff.j, || diff.k, || diff.u, || diff.v, || diff.w))) + if (NUM_AXIS_NONE(diff.x, diff.y, 0, diff.i, diff.j, diff.k, diff.u, diff.v, diff.w)) return TERN0(HAS_Z_AXIS, ABS(diff.z)); #if ENABLED(ARTICULATED_ROBOT_ARM) diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index b4e9eb5f09..414dfc48b1 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -1372,7 +1372,7 @@ void Planner::check_axes_activity() { float high = 0.0f; for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) { const block_t * const block = &block_buffer[b]; - if (NUM_AXIS_GANG(block->steps.x, || block->steps.y, || block->steps.z, || block->steps.i, || block->steps.j, || block->steps.k, || block->steps.u, || block->steps.v, || block->steps.w)) { + if (XYZ_HAS_STEPS(block)) { const float se = float(block->steps.e) / block->step_event_count * block->nominal_speed; // mm/sec NOLESS(high, se); } @@ -2023,12 +2023,7 @@ bool Planner::_populate_block( bool cartesian_move = hints.cartesian_move; #endif - if (true NUM_AXIS_GANG( - && block->steps.a < MIN_STEPS_PER_SEGMENT, && block->steps.b < MIN_STEPS_PER_SEGMENT, && block->steps.c < MIN_STEPS_PER_SEGMENT, - && block->steps.i < MIN_STEPS_PER_SEGMENT, && block->steps.j < MIN_STEPS_PER_SEGMENT, && block->steps.k < MIN_STEPS_PER_SEGMENT, - && block->steps.u < MIN_STEPS_PER_SEGMENT, && block->steps.v < MIN_STEPS_PER_SEGMENT, && block->steps.w < MIN_STEPS_PER_SEGMENT - ) - ) { + if (!XYZ_HAS_ENOUGH_STEPS(block)) { block->millimeters = TERN0(HAS_EXTRUDERS, ABS(dist_mm.e)); } else { @@ -2098,11 +2093,7 @@ bool Planner::_populate_block( E_TERN_(block->extruder = extruder); #if ENABLED(AUTO_POWER_CONTROL) - if (NUM_AXIS_GANG( - block->steps.x, || block->steps.y, || block->steps.z, - || block->steps.i, || block->steps.j, || block->steps.k, - || block->steps.u, || block->steps.v, || block->steps.w - )) powerManager.power_on(); + if (XYZ_HAS_STEPS(block)) powerManager.power_on(); #endif // Enable active axes @@ -2352,7 +2343,7 @@ bool Planner::_populate_block( #if ANY(LIN_ADVANCE, FTM_HAS_LIN_ADVANCE) bool use_adv_lead = false; #endif - if (!ANY_AXIS_MOVES(block)) { // Is this a retract / recover move? + if (!XYZ_HAS_STEPS(block)) { // Is this a retract / recover move? accel = CEIL(settings.retract_acceleration * steps_per_mm); // Convert to: acceleration steps/sec^2 } else { diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index 2838fb90c3..71ba8e18f9 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -1188,10 +1188,18 @@ class Planner { #define PLANNER_XY_FEEDRATE_MM_S 60.0f #endif -#define ANY_AXIS_MOVES(BLOCK) \ - (false NUM_AXIS_GANG( \ - || BLOCK->steps.a, || BLOCK->steps.b, || BLOCK->steps.c, \ - || BLOCK->steps.i, || BLOCK->steps.j, || BLOCK->steps.k, \ - || BLOCK->steps.u, || BLOCK->steps.v, || BLOCK->steps.w)) +#define XYZ_HAS_STEPS(B) NUM_AXIS_ANY( \ + B->steps.a, B->steps.b, B->steps.c, \ + B->steps.i, B->steps.j, B->steps.k, \ + B->steps.u, B->steps.v, B->steps.w) + +#if MIN_STEPS_PER_SEGMENT <= 1 + #define XYZ_HAS_ENOUGH_STEPS XYZ_HAS_STEPS +#else + #define XYZ_HAS_ENOUGH_STEPS(B) NUM_AXIS_ANY( \ + B->steps.a >= MIN_STEPS_PER_SEGMENT, B->steps.b >= MIN_STEPS_PER_SEGMENT, B->steps.c >= MIN_STEPS_PER_SEGMENT, \ + B->steps.i >= MIN_STEPS_PER_SEGMENT, B->steps.j >= MIN_STEPS_PER_SEGMENT, B->steps.k >= MIN_STEPS_PER_SEGMENT, \ + B->steps.u >= MIN_STEPS_PER_SEGMENT, B->steps.v >= MIN_STEPS_PER_SEGMENT, B->steps.w >= MIN_STEPS_PER_SEGMENT) +#endif extern Planner planner; diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 84924b6f65..c8b2f150d1 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -2905,7 +2905,7 @@ void Stepper::isr() { ne.edividend = advance_dividend.e; const float scale = (float(ne.edividend) / advance_divisor) * planner.mm_per_step[E_AXIS_N(current_block->extruder)]; ne.scale_q24 = _BV32(24) * scale; - if (ne.settings.enabled && current_block->direction_bits.e && ANY_AXIS_MOVES(current_block)) { + if (ne.settings.enabled && current_block->direction_bits.e && XYZ_HAS_STEPS(current_block)) { ne.q24.A = _BV32(24) * ne.settings.coeff.A; ne.q24.B = _BV32(24) * ne.settings.coeff.B; ne.q24.C = _BV32(24) * ne.settings.coeff.C; @@ -2959,7 +2959,7 @@ void Stepper::isr() { const bool forward_e = step_rate > 0; #if ENABLED(NONLINEAR_EXTRUSION) - if (ne.settings.enabled && forward_e && ANY_AXIS_MOVES(current_block)) { + if (ne.settings.enabled && forward_e && XYZ_HAS_STEPS(current_block)) { // Maximum polynomial value is just above 1, like 1.05..1.2, less than 2 anyway, so we can use 30 bits for fractional part int32_t vd_q30 = ne.q30.A * sq(step_rate) + ne.q30.B * step_rate; NOLESS(vd_q30, 0); @@ -3676,11 +3676,11 @@ void Stepper::report_positions() { #endif // Only wait for axes without edge stepping - const bool any_wait = false LOGICAL_AXIS_GANG( - || (!e_axis_has_dedge && step_bits.E), - || (!AXIS_HAS_DEDGE(X) && step_bits.X), || (!AXIS_HAS_DEDGE(Y) && step_bits.Y), || (!AXIS_HAS_DEDGE(Z) && step_bits.Z), - || (!AXIS_HAS_DEDGE(I) && step_bits.I), || (!AXIS_HAS_DEDGE(J) && step_bits.J), || (!AXIS_HAS_DEDGE(K) && step_bits.K), - || (!AXIS_HAS_DEDGE(U) && step_bits.U), || (!AXIS_HAS_DEDGE(V) && step_bits.V), || (!AXIS_HAS_DEDGE(W) && step_bits.W) + const bool any_wait = LOGICAL_AXIS_ANY( + !e_axis_has_dedge && step_bits.E, + !AXIS_HAS_DEDGE(X) && step_bits.X, !AXIS_HAS_DEDGE(Y) && step_bits.Y, !AXIS_HAS_DEDGE(Z) && step_bits.Z, + !AXIS_HAS_DEDGE(I) && step_bits.I, !AXIS_HAS_DEDGE(J) && step_bits.J, !AXIS_HAS_DEDGE(K) && step_bits.K, + !AXIS_HAS_DEDGE(U) && step_bits.U, !AXIS_HAS_DEDGE(V) && step_bits.V, !AXIS_HAS_DEDGE(W) && step_bits.W ); // Allow pulses to be registered by stepper drivers From d897251c172574b66ccc0c8d035e9931e126cdf2 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Wed, 14 Jan 2026 00:36:29 +0000 Subject: [PATCH 27/39] [cron] Bump distribution date (2026-01-14) --- 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 d257cd11f5..db5cae869e 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 "2026-01-12" +//#define STRING_DISTRIBUTION_DATE "2026-01-14" /** * 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 58ce89c489..53b1b279ea 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 "2026-01-12" + #define STRING_DISTRIBUTION_DATE "2026-01-14" #endif /** From 834241dfb531261769ce752443c529cdb357882f Mon Sep 17 00:00:00 2001 From: ellensp <530024+ellensp@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:19:45 +1300 Subject: [PATCH 28/39] =?UTF-8?q?=F0=9F=90=9B=20Fix=20ADC=20calculation=20?= =?UTF-8?q?(#28279)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Followup to 89379cd --- Marlin/src/HAL/ESP32/HAL.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Marlin/src/HAL/ESP32/HAL.cpp b/Marlin/src/HAL/ESP32/HAL.cpp index acfec29b2f..9adff634a0 100644 --- a/Marlin/src/HAL/ESP32/HAL.cpp +++ b/Marlin/src/HAL/ESP32/HAL.cpp @@ -271,7 +271,8 @@ void MarlinHAL::adc_start(const pin_t pin) { uint32_t mv; esp_adc_cal_get_voltage((adc_channel_t)chan, &characteristics[attenuations[chan]], &mv); - adc_result = (mv * 1023) * (1000.f / (ADC_REFERENCE_VOLTAGE)); + static constexpr uint32_t adc_divisor = uint32_t((ADC_REFERENCE_VOLTAGE) * 1000UL); + adc_result = (mv * 1023UL) / adc_divisor; // Change the attenuation level based on the new reading adc_atten_t atten; From 2aa4baa5f8d604bb3142f4a055dc000a4ed38b8b Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Thu, 15 Jan 2026 06:12:17 +0000 Subject: [PATCH 29/39] [cron] Bump distribution date (2026-01-15) --- 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 db5cae869e..6fae3abcd3 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 "2026-01-14" +//#define STRING_DISTRIBUTION_DATE "2026-01-15" /** * 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 53b1b279ea..954d00ed6c 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 "2026-01-14" + #define STRING_DISTRIBUTION_DATE "2026-01-15" #endif /** From 4ba13912b736d99f83e444f35d55090841371f8c Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 15 Jan 2026 18:31:26 -0600 Subject: [PATCH 30/39] =?UTF-8?q?=F0=9F=94=A8=20Fix=20build=20env=20chitu?= =?UTF-8?q?=5Ff103=5Fmaple?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ini/stm32f1-maple.ini | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ini/stm32f1-maple.ini b/ini/stm32f1-maple.ini index f6c8b8da56..ee0da386a3 100644 --- a/ini/stm32f1-maple.ini +++ b/ini/stm32f1-maple.ini @@ -342,14 +342,15 @@ lib_ignore = ${STM32F1_maple.lib_ignore} # Chitu boards like Tronxy X5s (STM32F103ZET6) # [env:chitu_f103_maple] -extends = STM32F1_maple -board = marlin_maple_CHITU_F103 -extra_scripts = ${STM32F1_maple.extra_scripts} - pre:buildroot/share/PlatformIO/scripts/STM32F1_create_variant.py - buildroot/share/PlatformIO/scripts/chitu_crypt.py -build_flags = ${STM32F1_maple.build_flags} -DSTM32F1xx -DSTM32_XL_DENSITY -DSTM32_FLASH_SIZE=512 -build_unflags = ${STM32F1_maple.build_unflags} - -DCONFIG_MAPLE_MINI_NO_DISABLE_DEBUG= -DERROR_LED_PORT=GPIOE -DERROR_LED_PIN=6 +extends = STM32F1_maple +board = marlin_maple_CHITU_F103 +extra_scripts = ${STM32F1_maple.extra_scripts} + pre:buildroot/share/PlatformIO/scripts/STM32F1_create_variant.py + buildroot/share/PlatformIO/scripts/chitu_crypt.py +build_flags = ${STM32F1_maple.build_flags} -DSTM32F1xx -DSTM32_XL_DENSITY -DSTM32_FLASH_SIZE=512 +build_unflags = ${STM32F1_maple.build_unflags} + -DCONFIG_MAPLE_MINI_NO_DISABLE_DEBUG= -DERROR_LED_PORT=GPIOE -DERROR_LED_PIN=6 +board_build.crypt_chitu = update.cbd # # Some Chitu V5 boards have a problem with GPIO init. From 3791ca788ba6106bc57ce1738217685ae3ca103c Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 15 Jan 2026 19:31:12 -0600 Subject: [PATCH 31/39] =?UTF-8?q?=F0=9F=94=A8=20Drop=20obsolete=20env=20ST?= =?UTF-8?q?M32F103CB=5Fmalyan=5Fmaple?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/pins/pins.h | 2 +- .../PlatformIO/boards/marlin_malyanM200.json | 35 ------------------- ini/stm32f1-maple.ini | 12 ------- 3 files changed, 1 insertion(+), 48 deletions(-) delete mode 100644 buildroot/share/PlatformIO/boards/marlin_malyanM200.json diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h index 3cc1849acc..4672c92e01 100644 --- a/Marlin/src/pins/pins.h +++ b/Marlin/src/pins/pins.h @@ -555,7 +555,7 @@ #elif MB(STM32F103RE) #include "stm32f1/pins_STM32F1R.h" // STM32F1 env:STM32F103RE env:STM32F103RE_maple #elif MB(MALYAN_M200) - #include "stm32f1/pins_MALYAN_M200.h" // STM32F1 env:STM32F103CB_malyan env:STM32F103CB_malyan_maple + #include "stm32f1/pins_MALYAN_M200.h" // STM32F1 env:STM32F103CB_malyan #elif MB(STM3R_MINI) #include "stm32f1/pins_STM3R_MINI.h" // STM32F1 env:STM32F103VE env:STM32F103RE_maple #elif MB(GTM32_PRO_VB) diff --git a/buildroot/share/PlatformIO/boards/marlin_malyanM200.json b/buildroot/share/PlatformIO/boards/marlin_malyanM200.json deleted file mode 100644 index 4dbf760774..0000000000 --- a/buildroot/share/PlatformIO/boards/marlin_malyanM200.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "build": { - "core": "maple", - "cpu": "cortex-m3", - "extra_flags": "-DARDUINO_GENERIC_STM32F103C -DMCU_STM32F103CB", - "f_cpu": "72000000L", - "hwids": [ - ["0x1EAF", "0x0003"], - ["0x1EAF", "0x0004"] - ], - "ldscript": "jtagOffset.ld", - "mcu": "stm32f103cb", - "variant": "malyanM200", - "vec_tab_addr": "0x8002000" - }, - "debug": { - "jlink_device": "STM32F103CB", - "openocd_target": "stm32f1x", - "svd_path": "STM32F103xx.svd" - }, - "platform": "ststm32", - "frameworks": ["arduino"], - "name": "Malyan STM32F103CB (20k RAM. 128k Flash)", - "upload": { - "disable_flushing": false, - "maximum_ram_size": 20480, - "maximum_size": 131072, - "protocol": "serial", - "require_upload_port": true, - "use_1200bps_touch": false, - "wait_for_upload_port": false - }, - "url": "https://www.st.com/content/st_com/en/products/microcontrollers/stm32-32-bit-arm-cortex-mcus/stm32f1-series/stm32f103/stm32f103cb.html", - "vendor": "Generic" -} diff --git a/ini/stm32f1-maple.ini b/ini/stm32f1-maple.ini index ee0da386a3..36f651b976 100644 --- a/ini/stm32f1-maple.ini +++ b/ini/stm32f1-maple.ini @@ -326,18 +326,6 @@ extra_scripts = ${STM32F1_maple.extra_scripts} buildroot/share/PlatformIO/scripts/jgaurora_a5s_a1_with_bootloader.py build_flags = ${STM32F1_maple.build_flags} -DSTM32F1xx -DSTM32_XL_DENSITY -# -# Malyan M200 (STM32F103CB) -# -[env:STM32F103CB_malyan_maple] -extends = STM32F1_maple -board = marlin_malyanM200 -build_flags = ${STM32F1_maple.build_flags} - -DMCU_STM32F103CB -D__STM32F1__=1 -std=c++1y -DSERIAL_USB -ffunction-sections -fdata-sections - -Wl,--gc-sections -DDEBUG_LEVEL=0 -lib_ignore = ${STM32F1_maple.lib_ignore} - SoftwareSerialM - # # Chitu boards like Tronxy X5s (STM32F103ZET6) # From 1692996979bb668615465a923ac16cc603afe441 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Fri, 16 Jan 2026 06:12:22 +0000 Subject: [PATCH 32/39] [cron] Bump distribution date (2026-01-16) --- 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 6fae3abcd3..40c27724ea 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 "2026-01-15" +//#define STRING_DISTRIBUTION_DATE "2026-01-16" /** * 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 954d00ed6c..6a3814272e 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 "2026-01-15" + #define STRING_DISTRIBUTION_DATE "2026-01-16" #endif /** From dc5fabe3a970c2eafc5a0e8a1c3a3c14017b6a71 Mon Sep 17 00:00:00 2001 From: Andrew <18502096+classicrocker883@users.noreply.github.com> Date: Fri, 16 Jan 2026 20:43:04 -0500 Subject: [PATCH 33/39] =?UTF-8?q?=F0=9F=94=A8=20Update=20use=5Fexample=5Fc?= =?UTF-8?q?onfigs=20refs=20(#28285)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildroot/bin/use_example_configs | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/buildroot/bin/use_example_configs b/buildroot/bin/use_example_configs index 72930d8c45..b017205bdd 100755 --- a/buildroot/bin/use_example_configs +++ b/buildroot/bin/use_example_configs @@ -61,9 +61,9 @@ def copy_config_files(branch, config_path, dest_dir): else: debug_print(f"{fname} not found in {src_dir}") -def fetch_config_files(branch, config_path, dest_dir): +def fetch_config_files(cref, config_path, dest_dir): config_path_url = config_path.replace(' ', '%20') - base_url = f"https://raw.githubusercontent.com/MarlinFirmware/Configurations/{branch}/config/{config_path_url}" + base_url = f"https://raw.githubusercontent.com/MarlinFirmware/Configurations/{cref}/config/{config_path_url}" for file in CONFIG_FILES: url = f"{base_url}/{file}" @@ -80,8 +80,8 @@ def fetch_config_files(branch, config_path, dest_dir): else: raise -def fetch_configs(branch, config_path): - print(f"Fetching {config_path} configurations from {branch}...") +def fetch_configs(cref, config_path): + print(f"Fetching {config_path} configurations from {cref}...") marlin_dir = Path("Marlin") if not marlin_dir.exists(): @@ -89,37 +89,37 @@ def fetch_configs(branch, config_path): sys.exit(1) if os.environ.get('GITHUB_ACTIONS'): # Running on GitHub ? - copy_config_files(branch, config_path, marlin_dir) + copy_config_files(cref, config_path, marlin_dir) else: - fetch_config_files(branch, config_path, marlin_dir) + fetch_config_files(cref, config_path, marlin_dir) def main(): - branch = get_current_branch() - if not branch: + mbranch = get_current_branch() + if not mbranch: print("Not a git repository or no branch found.") sys.exit(1) - if branch.startswith("bugfix-2."): - branch = branch - elif branch.endswith("bugfix-2.1.x"): - branch = "bugfix-2.1.x" - elif branch.endswith("-2.1.x") or branch == "2.1.x": - branch = "latest-2.1.x" - elif branch.endswith("-2.0.x") or branch == "2.0.x": - branch = "latest-2.0.x" - elif branch.endswith("-1.1.x") or branch == "1.1.x": - branch = "latest-1.1.x" - elif branch.endswith("-1.0.x") or branch == "1.0.x": - branch = "latest-1.0.x" + if mbranch.startswith("bugfix-"): + cref = mbranch + elif mbranch.startswith("lts-"): + cref = mbranch.replace("lts-", "latest-") + elif mbranch.endswith("-2.1.x") or mbranch == "2.1.x": + cref = "latest-2.1.x" + elif mbranch.endswith("-2.0.x") or mbranch == "2.0.x": + cref = "latest-2.0.x" + elif mbranch.endswith("-1.1.x") or mbranch == "1.1.x": + cref = "latest-1.1.x" + elif mbranch == "1.0.x": + cref = "latest-1.0.x" else: - branch = "bugfix-2.1.x" + cref = "bugfix-2.1.x" if len(sys.argv) > 1: arg = sys.argv[1] if ':' in arg: part1, part2 = arg.split(':', 1) config_path = part2 - branch = part1 + cref = part1 else: config_path = arg config_path = 'examples/'+config_path @@ -134,7 +134,7 @@ def main(): except FileNotFoundError: print("restore_configs not found, skipping.") - fetch_configs(branch, config_path) + fetch_configs(cref, config_path) if __name__ == "__main__": main() From 803b78f1966a8f63f2f9dfa733bdb92a7fcc6942 Mon Sep 17 00:00:00 2001 From: Andrew <18502096+classicrocker883@users.noreply.github.com> Date: Fri, 16 Jan 2026 20:49:42 -0500 Subject: [PATCH 34/39] =?UTF-8?q?=F0=9F=93=9D=20Apply=20Markdown=20formatt?= =?UTF-8?q?ing=20(#28283)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/code_of_conduct.md | 20 +++---- .github/contributing.md | 109 ++++++++++++++++++---------------- Marlin/src/HAL/HC32/README.md | 12 +++- 3 files changed, 76 insertions(+), 65 deletions(-) diff --git a/.github/code_of_conduct.md b/.github/code_of_conduct.md index 5fd9e0c8ee..f60db7ba39 100644 --- a/.github/code_of_conduct.md +++ b/.github/code_of_conduct.md @@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities diff --git a/.github/contributing.md b/.github/contributing.md index bbf41ea1c8..0648b20135 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -11,18 +11,21 @@ The following is a set of guidelines for contributing to Marlin, hosted by the [ [I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) [How Can I Contribute?](#how-can-i-contribute) - * [Reporting Bugs](#reporting-bugs) - * [Suggesting Features or Changes](#suggesting-features-or-changes) - * [Your First Code Contribution](#your-first-code-contribution) - * [Pull Requests](#pull-requests) + +- [Reporting Bugs](#reporting-bugs) +- [Suggesting Features or Changes](#suggesting-features-or-changes) +- [Your First Code Contribution](#your-first-code-contribution) +- [Pull Requests](#pull-requests) [Styleguides](#styleguides) - * [Git Commit Messages](#git-commit-messages) - * [C++ Coding Standards](#c++-coding-standards) - * [Documentation Styleguide](#documentation) + +- [Git Commit Messages](#git-commit-messages) +- [C++ Coding Standards](#c++-coding-standards) +- [Documentation Styleguide](#documentation) [Additional Notes](#additional-notes) - * [Issue and Pull Request Labels](#issue-and-pull-request-labels) + +- [Issue and Pull Request Labels](#issue-and-pull-request-labels) ## Code of Conduct @@ -31,6 +34,7 @@ This project and everyone participating in it is governed by the [Marlin Code of ## I don't want to read this whole thing I just have a question!!! > [!NOTE] +> > Please don't file an issue to ask a question. You'll get faster results by using the resources below. We have a Message Board and a Facebook group where our knowledgable user community can provide helpful advice if you have questions. @@ -43,10 +47,10 @@ We have a Message Board and a Facebook group where our knowledgable user communi If chat is more your speed, you can join the MarlinFirmware Discord server: -* Use the link https://discord.com/servers/marlin-firmware-461605380783472640 to join up as a General User. -* Even though our Discord is pretty active, it may take a while for community members to respond — please be patient! -* Use the `#general` channel for general questions or discussion about Marlin. -* Other channels exist for certain topics or are limited to Patrons. Check the channel list. +- Use the link https://discord.com/servers/marlin-firmware-461605380783472640 to join up as a General User. +- Even though our Discord is pretty active, it may take a while for community members to respond — please be patient! +- Use the `#general` channel for general questions or discussion about Marlin. +- Other channels exist for certain topics or are limited to Patrons. Check the channel list. ## How Can I Contribute? @@ -57,6 +61,7 @@ This section guides you through submitting a Bug Report for Marlin. Following th Before creating a Bug Report, please test the "nightly" development branch, as you might find out that you don't need to create one. When you are creating a Bug Report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](ISSUE_TEMPLATE/bug_report.yml), the information it asks for helps us resolve issues faster. > [!NOTE] +> > Regressions can happen. If you find a **Closed** issue that seems like your issue, go ahead and open a new issue and include a link to the original issue in the body of your new one. All you need to create a link is the issue number, preceded by #. For example, #8888. #### How Do I Submit A (Good) Bug Report? @@ -65,29 +70,29 @@ Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Explain the problem and include additional details to help maintainers reproduce the problem: -* **Use a clear and descriptive title** for the issue to identify the problem. -* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started Marlin, e.g. which command exactly you used in the terminal, or how you started Marlin otherwise. When listing steps, **don't just say what you did, but explain how you did it**. For example, if you moved the cursor to the end of a line, explain if you used the mouse, or a keyboard shortcut or an Marlin command, and if so which one? -* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets or log output in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). -* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. -* **Explain which behavior you expected to see instead and why.** -* **Include detailed log output** especially for probing and leveling. See below for usage of `DEBUG_LEVELING_FEATURE`. -* **Include screenshots, links to videos, etc.** which clearly demonstrate the problem. -* **Include G-code** (if relevant) that reliably causes the problem to show itself. -* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. +- **Use a clear and descriptive title** for the issue to identify the problem. +- **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started Marlin, e.g. which command exactly you used in the terminal, or how you started Marlin otherwise. When listing steps, **don't just say what you did, but explain how you did it**. For example, if you moved the cursor to the end of a line, explain if you used the mouse, or a keyboard shortcut or an Marlin command, and if so which one? +- **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets or log output in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +- **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +- **Explain which behavior you expected to see instead and why.** +- **Include detailed log output** especially for probing and leveling. See below for usage of `DEBUG_LEVELING_FEATURE`. +- **Include screenshots, links to videos, etc.** which clearly demonstrate the problem. +- **Include G-code** (if relevant) that reliably causes the problem to show itself. +- **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. Provide more context: -* **Can you reproduce the problem with a minimum of options enabled?** -* **Did the problem start happening recently** (e.g. after updating to a new version of Marlin) or was this always a problem? -* If the problem started happening recently, **can you reproduce the problem in an older version of Marlin?** What's the most recent version in which the problem doesn't happen? You can download older versions of Marlin from [the releases page](https://github.com/MarlinFirmware/Marlin/releases). -* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. +- **Can you reproduce the problem with a minimum of options enabled?** +- **Did the problem start happening recently** (e.g. after updating to a new version of Marlin) or was this always a problem? +- If the problem started happening recently, **can you reproduce the problem in an older version of Marlin?** What's the most recent version in which the problem doesn't happen? You can download older versions of Marlin from [the releases page](https://github.com/MarlinFirmware/Marlin/releases). +- **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. Include details about your configuration and environment: -* **Which version of Marlin are you using?** Marlin's exact version and build date can be seen in the startup message when a host connects to Marlin, or in the LCD Info menu (if enabled). -* **What kind of 3D Printer and electronics are you using**? -* **What kind of add-ons (probe, filament sensor) do you have**? -* **Include your Configuration files.** Make a ZIP file containing `Configuration.h` and `Configuration_adv.h` and drop it on your reply. +- **Which version of Marlin are you using?** Marlin's exact version and build date can be seen in the startup message when a host connects to Marlin, or in the LCD Info menu (if enabled). +- **What kind of 3D Printer and electronics are you using**? +- **What kind of add-ons (probe, filament sensor) do you have**? +- **Include your Configuration files.** Make a ZIP file containing `Configuration.h` and `Configuration_adv.h` and drop it on your reply. ### Suggesting Features or Changes @@ -97,52 +102,52 @@ Before creating a suggestion, please check [this list](https://github.com/Marlin #### Before Submitting a Feature Request -* **Check the [Marlin website](https://marlinfw.org/)** for tips — you might discover that the feature is already included. Most importantly, check if you're using [the latest version of Marlin](https://github.com/MarlinFirmware/Marlin/releases) and if you can get the desired behavior by changing [Marlin's config settings](https://marlinfw.org/docs/configuration/configuration.html). -* **Perform a [cursory search](https://github.com/MarlinFirmware/Marlin/issues?q=is%3Aopen+is%3Aissue+label%3A%22T%3A+Feature+Request%22)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. +- **Check the [Marlin website](https://marlinfw.org/)** for tips — you might discover that the feature is already included. Most importantly, check if you're using [the latest version of Marlin](https://github.com/MarlinFirmware/Marlin/releases) and if you can get the desired behavior by changing [Marlin's config settings](https://marlinfw.org/docs/configuration/configuration.html). +- **Perform a [cursory search](https://github.com/MarlinFirmware/Marlin/issues?q=is%3Aopen+is%3Aissue+label%3A%22T%3A+Feature+Request%22)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. #### How Do I Submit A (Good) Feature Request? Feature Requests are tracked as [GitHub issues](https://guides.github.com/features/issues/). Please follow these guidelines in your request: -* **Use a clear and descriptive title** for the issue to identify the suggestion. -* **Provide a step-by-step description of the requested feature** in as much detail as possible. -* **Provide specific examples to demonstrate the steps**. -* **Describe the current behavior** and **explain which behavior you expected to see instead** and why. -* **Include screenshots and links to videos** which demonstrate the feature or point out the part of Marlin to which the request is related. -* **Explain why this feature would be useful** to most Marlin users. -* **Name other firmwares that have this feature, if any.** +- **Use a clear and descriptive title** for the issue to identify the suggestion. +- **Provide a step-by-step description of the requested feature** in as much detail as possible. +- **Provide specific examples to demonstrate the steps**. +- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. +- **Include screenshots and links to videos** which demonstrate the feature or point out the part of Marlin to which the request is related. +- **Explain why this feature would be useful** to most Marlin users. +- **Name other firmwares that have this feature, if any.** ### Your First Code Contribution Unsure where to begin contributing to Marlin? You can start by looking through these `good-first-issue` and `help-wanted` issues: -* [Beginner issues][good-first-issue] - issues which should only require a few lines of code, and a test or two. -* [Help Wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues. +- [Beginner issues][good-first-issue] - issues which should only require a few lines of code, and a test or two. +- [Help Wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues. ### Pull Requests Pull Requests should always be targeted to working branches (e.g., `bugfix-2.1.x` and/or `bugfix-1.1.x`) and never to release branches (e.g., `2.0.x` and/or `1.1.x`). If this is your first Pull Request, please read our [Guide to Pull Requests](https://marlinfw.org/docs/development/getting_started_pull_requests.html) and GitHub's [Pull Request](https://help.github.com/articles/creating-a-pull-request/) documentation. -* Fill in [the required template](pull_request_template.md). -* Don't include issue numbers in the PR title. -* Include pictures, diagrams, and links to videos in your Pull Request to demonstrate your changes, if needed. -* Follow the [Coding Standards](https://marlinfw.org/docs/development/coding_standards.html) posted on our website. -* Document new code with clear and concise comments. -* End all files with a newline. +- Fill in [the required template](pull_request_template.md). +- Don't include issue numbers in the PR title. +- Include pictures, diagrams, and links to videos in your Pull Request to demonstrate your changes, if needed. +- Follow the [Coding Standards](https://marlinfw.org/docs/development/coding_standards.html) posted on our website. +- Document new code with clear and concise comments. +- End all files with a newline. ## Styleguides ### Git Commit Messages -* Use the present tense ("Add feature" not "Added feature"). -* Use the imperative mood ("Move cursor to..." not "Moves cursor to..."). -* Limit the first line to 72 characters or fewer. -* Reference issues and Pull Requests liberally after the first line. +- Use the present tense ("Add feature" not "Added feature"). +- Use the imperative mood ("Move cursor to..." not "Moves cursor to..."). +- Limit the first line to 72 characters or fewer. +- Reference issues and Pull Requests liberally after the first line. ### C++ Coding Standards -* Please read and follow the [Coding Standards](https://marlinfw.org/docs/development/coding_standards.html) posted on our website. Failure to follow these guidelines will delay evaluation and acceptance of Pull Requests. +- Please read and follow the [Coding Standards](https://marlinfw.org/docs/development/coding_standards.html) posted on our website. Failure to follow these guidelines will delay evaluation and acceptance of Pull Requests. ### Documentation -* Guidelines for documentation are still under development. In-general, be clear, concise, and to-the-point. +- Guidelines for documentation are still under development. In-general, be clear, concise, and to-the-point. diff --git a/Marlin/src/HAL/HC32/README.md b/Marlin/src/HAL/HC32/README.md index fe265dcf24..c852edcf6c 100644 --- a/Marlin/src/HAL/HC32/README.md +++ b/Marlin/src/HAL/HC32/README.md @@ -49,9 +49,13 @@ SCB->VTOR = ((uint32_t) APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk); Just searching for `SCB->VTOR` should yield some results. From there, you just need to look at the value that's assigned to it. The example uses `APP_START_ADDRESS`. -> [!NOTE] Some vendors publish incomplete source code. But they sometimes leave version control related files in the repo, which can contain previous version of files that were removed. Find these by including folders like `.git` or `.svn` in your search. +> [!NOTE] +> +> Some vendors publish incomplete source code. But they sometimes leave version control related files in the repo, which can contain previous version of files that were removed. Find these by including folders like `.git` or `.svn` in your search. -> [!NOTE] The example is based on the [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/main/firmware/Sources/.svn/pristine/ec/ec82bcb480b511906bc3e6658450e3a803ab9813.svn-base#L96) which actually includes deleted files in its repo. +> [!NOTE] +> +> The example is based on the [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/main/firmware/Sources/.svn/pristine/ec/ec82bcb480b511906bc3e6658450e3a803ab9813.svn-base#L96) which actually includes deleted files in its repo. 2. Using a linker script @@ -69,7 +73,9 @@ MEMORY } ``` -> [!NOTE] This example is based on [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/d1f23adf96920996b979bc31023d1dce236d05db/firmware/Sources/main/hdsc32core/hc32f46x_flash.ld#L55) +> [!NOTE] +> +> This example is based on [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/d1f23adf96920996b979bc31023d1dce236d05db/firmware/Sources/main/hdsc32core/hc32f46x_flash.ld#L55) ## Documentation on the HC32F460 From ae5ae96e62332e0a607f99b6bbb6bdf577d279e4 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Sat, 17 Jan 2026 06:10:52 +0000 Subject: [PATCH 35/39] [cron] Bump distribution date (2026-01-17) --- 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 40c27724ea..a39743efb2 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 "2026-01-16" +//#define STRING_DISTRIBUTION_DATE "2026-01-17" /** * 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 6a3814272e..7241fddd98 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 "2026-01-16" + #define STRING_DISTRIBUTION_DATE "2026-01-17" #endif /** From 1f4445a096997e9fd410b30c6fb433d1a6444223 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 17 Jan 2026 18:44:22 -0600 Subject: [PATCH 36/39] =?UTF-8?q?=F0=9F=94=A7=20Clarify=20REINIT=5FNOISY?= =?UTF-8?q?=5FLCD=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/inc/Warnings.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Marlin/src/inc/Warnings.cpp b/Marlin/src/inc/Warnings.cpp index 1164fe1bb4..24101168f6 100644 --- a/Marlin/src/inc/Warnings.cpp +++ b/Marlin/src/inc/Warnings.cpp @@ -997,8 +997,11 @@ /** * Some LCDs need re-init to deal with flaky SPI bus sharing */ -#if HAS_SD_DETECT && NONE(REINIT_NOISY_LCD, HAS_GRAPHICAL_TFT, LCD_USE_DMA_FSMC, HAS_FSMC_GRAPHICAL_TFT, HAS_SPI_GRAPHICAL_TFT, IS_DWIN_MARLINUI, EXTENSIBLE_UI, HAS_DWIN_E3V2, HAS_U8GLIB_I2C_OLED) +#if HAS_SD_DETECT && NONE(HAS_GRAPHICAL_TFT, LCD_USE_DMA_FSMC, HAS_FSMC_GRAPHICAL_TFT, HAS_SPI_GRAPHICAL_TFT, IS_DWIN_MARLINUI, EXTENSIBLE_UI, HAS_DWIN_E3V2, HAS_U8GLIB_I2C_OLED) + #define RECOMMEND_REINIT_NOISY_LCD 1 +#endif +#if RECOMMEND_REINIT_NOISY_LCD && DISABLED(REINIT_NOISY_LCD) #warning "It is recommended to enable REINIT_NOISY_LCD with your LCD controller model." -#elif ENABLED(REINIT_NOISY_LCD) +#elif !RECOMMEND_REINIT_NOISY_LCD && ENABLED(REINIT_NOISY_LCD) #warning "REINIT_NOISY_LCD is probably not required with your LCD controller model." #endif From 367e2219e3a06b5ab3dc0c8e2ee77e1f445f4d2a Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Sun, 18 Jan 2026 06:11:21 +0000 Subject: [PATCH 37/39] [cron] Bump distribution date (2026-01-18) --- 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 a39743efb2..b90f4917d5 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 "2026-01-17" +//#define STRING_DISTRIBUTION_DATE "2026-01-18" /** * 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 7241fddd98..1414f113c2 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 "2026-01-17" + #define STRING_DISTRIBUTION_DATE "2026-01-18" #endif /** From 7c29cdb685feae4f9214b9b5cb106a4335fa0cd4 Mon Sep 17 00:00:00 2001 From: DerAndere <26200979+DerAndere1@users.noreply.github.com> Date: Wed, 21 Jan 2026 04:58:28 +0100 Subject: [PATCH 38/39] =?UTF-8?q?=E2=9C=A8=20SOFT=5FFEED=5FHOLD=20(#28265)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/Configuration_adv.h | 39 +++++-- Marlin/src/MarlinCore.cpp | 16 ++- Marlin/src/feature/e_parser.cpp | 8 +- Marlin/src/feature/e_parser.h | 4 + Marlin/src/inc/Conditionals-5-post.h | 5 +- Marlin/src/inc/SanityCheck.h | 12 +- Marlin/src/inc/Warnings.cpp | 7 ++ Marlin/src/module/planner.cpp | 3 + Marlin/src/module/planner.h | 3 +- Marlin/src/module/stepper.cpp | 167 +++++++++++++++++++++++++-- Marlin/src/module/stepper.h | 37 +++++- buildroot/tests/DUE | 5 +- 12 files changed, 270 insertions(+), 36 deletions(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 31d3b8364b..84fbd6f8ed 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -2826,8 +2826,8 @@ * * Adds support for commands: * S000 : Report State and Position while moving. - * P000 : Instant Pause / Hold while moving. - * R000 : Resume from Pause / Hold. + * P000 : Instant Pause / Hold while moving. Enable SOFT_FEED_HOLD for soft deceleration. + * R000 : Resume from Pause / Hold. Enable SOFT_FEED_HOLD for soft acceleration. * * - During Hold all Emergency Parser commands are available, as usual. * - Enable NANODLP_Z_SYNC and NANODLP_ALL_AXIS for move command end-state reports. @@ -4457,15 +4457,38 @@ #endif /** - * Instant freeze / unfreeze functionality - * Potentially useful for rapid stop that allows being resumed. Halts stepper movement. - * Note this does NOT pause spindles, lasers, fans, heaters or any other auxiliary device. - * @section interface + * Freeze / Unfreeze + * + * Pause / Hold that keeps power available and does not stop the spindle can be initiated by + * the FREEZE_PIN. Halts instantly (default) or performs a soft feed hold that decelerates and + * halts movement at FREEZE_JERK (requires SOFT_FEED_HOLD). + * Motion can be resumed by using the FREEZE_PIN. + * + * NOTE: Controls Laser PWM but does NOT pause Spindle, Fans, Heaters or other devices. + * @section freeze */ //#define FREEZE_FEATURE #if ENABLED(FREEZE_FEATURE) - //#define FREEZE_PIN 41 // Override the default (KILL) pin here - #define FREEZE_STATE LOW // State of pin indicating freeze + //#define FREEZE_PIN -1 // Override the default (KILL) pin here + #define FREEZE_STATE LOW // State of pin indicating freeze +#endif + +#if ANY(FREEZE_FEATURE, REALTIME_REPORTING_COMMANDS) + /** + * Command P000 (REALTIME_REPORTING_COMMANDS and EMERGENCY_PARSER) or + * FREEZE_PIN (FREEZE_FEATURE) initiates a soft feed hold that keeps + * power available and does not stop the spindle. + * + * The soft feed hold decelerates and halts movement at FREEZE_JERK. + * Motion can be resumed with command R000 (requires REALTIME_REPORTING_COMMANDS) or + * by using the FREEZE_PIN (requires FREEZE_FEATURE). + * + * NOTE: Affects Laser PWM but DOES NOT pause Spindle, Fans, Heaters or other devices. + */ + //#define SOFT_FEED_HOLD + #if ENABLED(SOFT_FEED_HOLD) + #define FREEZE_JERK 2 // (mm/s) Completely halt when motion has decelerated below this value + #endif #endif /** diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 6d672e3b8c..94d0bce188 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -260,6 +260,10 @@ #include "feature/rs485.h" #endif +#if ENABLED(SOFT_FEED_HOLD) + #include "feature/e_parser.h" +#endif + /** * Spin in place here while keeping temperature processing alive */ @@ -514,8 +518,14 @@ void Marlin::manage_inactivity(const bool no_stepper_sleep/*=false*/) { } #endif - #if ENABLED(FREEZE_FEATURE) - stepper.frozen = READ(FREEZE_PIN) == FREEZE_STATE; + // Handle the FREEZE button + #if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD) + stepper.set_frozen_triggered( + TERN0(FREEZE_FEATURE, READ(FREEZE_PIN) == FREEZE_STATE) + #if ALL(SOFT_FEED_HOLD, REALTIME_REPORTING_COMMANDS) + || realtime_ramping_pause_flag + #endif + ); #endif #if HAS_HOME @@ -1221,7 +1231,7 @@ void setup() { #endif #endif - #if ENABLED(FREEZE_FEATURE) + #if ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) SETUP_LOG("FREEZE_PIN"); #if FREEZE_STATE SET_INPUT_PULLDOWN(FREEZE_PIN); diff --git a/Marlin/src/feature/e_parser.cpp b/Marlin/src/feature/e_parser.cpp index 28a903719c..3744870164 100644 --- a/Marlin/src/feature/e_parser.cpp +++ b/Marlin/src/feature/e_parser.cpp @@ -60,6 +60,10 @@ EmergencyParser emergency_parser; void quickresume_stepper(); #endif +#if ENABLED(SOFT_FEED_HOLD) + bool realtime_ramping_pause_flag = false; +#endif + void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { auto uppercase = [](char c) { return TERN0(GCODE_CASE_INSENSITIVE, WITHIN(c, 'a', 'z')) ? c + 'A' - 'a' : c; @@ -223,8 +227,8 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { #endif #if ENABLED(REALTIME_REPORTING_COMMANDS) case EP_GRBL_STATUS: report_current_position_moving(); break; - case EP_GRBL_PAUSE: quickpause_stepper(); break; - case EP_GRBL_RESUME: quickresume_stepper(); break; + case EP_GRBL_PAUSE: TERN(SOFT_FEED_HOLD, realtime_ramping_pause_flag = true, quickpause_stepper()); break; + case EP_GRBL_RESUME: TERN(SOFT_FEED_HOLD, realtime_ramping_pause_flag = false, quickresume_stepper()); break; #endif #if ENABLED(SOFT_RESET_VIA_SERIAL) case EP_KILL: hal.reboot(); break; diff --git a/Marlin/src/feature/e_parser.h b/Marlin/src/feature/e_parser.h index 9f74a38116..a582ec6c3d 100644 --- a/Marlin/src/feature/e_parser.h +++ b/Marlin/src/feature/e_parser.h @@ -93,3 +93,7 @@ private: }; extern EmergencyParser emergency_parser; + +#if ENABLED(SOFT_FEED_HOLD) + extern bool realtime_ramping_pause_flag; +#endif diff --git a/Marlin/src/inc/Conditionals-5-post.h b/Marlin/src/inc/Conditionals-5-post.h index b9eeb3d85a..55cd16c8d7 100644 --- a/Marlin/src/inc/Conditionals-5-post.h +++ b/Marlin/src/inc/Conditionals-5-post.h @@ -3047,9 +3047,10 @@ #endif // User Interface -#if ENABLED(FREEZE_FEATURE) && !PIN_EXISTS(FREEZE) && PIN_EXISTS(KILL) +#if ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) && !PIN_EXISTS(FREEZE) && PIN_EXISTS(KILL) #define FREEZE_PIN KILL_PIN -#elif PIN_EXISTS(KILL) && TERN1(FREEZE_FEATURE, KILL_PIN != FREEZE_PIN) + #define FREEZE_STOLE_KILL_PIN_WARNING 1 +#elif PIN_EXISTS(KILL) && TERN1(HAS_FREEZE_PIN, KILL_PIN != FREEZE_PIN) #define HAS_KILL 1 #endif #if PIN_EXISTS(HOME) diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 5856723c77..3155127c58 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -579,8 +579,12 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L /** * Instant Freeze */ -#if ENABLED(FREEZE_FEATURE) && !(PIN_EXISTS(FREEZE) && defined(FREEZE_STATE)) - #error "FREEZE_FEATURE requires both FREEZE_PIN and FREEZE_STATE." +#if ENABLED(SOFT_FEED_HOLD) && !defined(FREEZE_JERK) + #error "SOFT_FEED_HOLD requires FREEZE_JERK." +#elif ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) && !(defined(FREEZE_PIN) && defined(FREEZE_STATE)) + #error "FREEZE_FEATURE requires FREEZE_PIN and FREEZE_STATE." +#elif ENABLED(NO_FREEZE_PIN) && !(defined(REALTIME_REPORTING_COMMANDS)) + #error "NO_FREEZE_PIN requires REALTIME_REPORTING_COMMANDS." #endif /** @@ -4501,8 +4505,8 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive." #error "SMOOTH_LIN_ADVANCE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." #elif ENABLED(MIXING_EXTRUDER) #error "MIXING_EXTRUDER is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." - #elif ENABLED(FREEZE_FEATURE) - #error "FREEZE_FEATURE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." + #elif ENABLED(SOFT_FEED_HOLD) + #error "SOFT_FEED_HOLD is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." #elif ENABLED(DIRECT_STEPPING) #error "DIRECT_STEPPING is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it." #elif ENABLED(DIFFERENTIAL_EXTRUDER) diff --git a/Marlin/src/inc/Warnings.cpp b/Marlin/src/inc/Warnings.cpp index 24101168f6..6a097777b0 100644 --- a/Marlin/src/inc/Warnings.cpp +++ b/Marlin/src/inc/Warnings.cpp @@ -1005,3 +1005,10 @@ #elif !RECOMMEND_REINIT_NOISY_LCD && ENABLED(REINIT_NOISY_LCD) #warning "REINIT_NOISY_LCD is probably not required with your LCD controller model." #endif + +/** + * FREEZE_FEATURE may override the KILL_PIN + */ +#if FREEZE_STOLE_KILL_PIN_WARNING + #warning "FREEZE_FEATURE uses KILL_PIN replacing the KILL button. Define a separate FREEZE_PIN if you don't want this behavior." +#endif diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 414dfc48b1..6da11fd8c4 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -2428,6 +2428,9 @@ bool Planner::_populate_block( block->acceleration_steps_per_s2 = accel; #if DISABLED(S_CURVE_ACCELERATION) block->acceleration_rate = uint32_t(accel * (float(_BV32(24)) / (STEPPER_TIMER_RATE))); + #elif ENABLED(SOFT_FEED_HOLD) + // No need to waste time calculating the linear acceleration rate until the freeze_pin is triggered, leave this 0 + block->acceleration_rate = 0; #endif #endif block->acceleration = accel / steps_per_mm; diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index 71ba8e18f9..36b94da77d 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -254,7 +254,8 @@ typedef struct PlannerBlock { #if ENABLED(S_CURVE_ACCELERATION) uint32_t acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used deceleration_time_inverse; - #elif HAS_STANDARD_MOTION + #endif + #if ENABLED(HAS_STANDARD_MOTION) && (DISABLED(S_CURVE_ACCELERATION) || ENABLED(FREEZE_FEATURE)) uint32_t acceleration_rate; // Acceleration rate in (2^24 steps)/timer_ticks*s #endif diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index c8b2f150d1..cfb2617024 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -199,8 +199,14 @@ uint32_t Stepper::acceleration_time, Stepper::deceleration_time; constexpr uint8_t Stepper::oversampling_factor; // = 0 #endif -#if ENABLED(FREEZE_FEATURE) - bool Stepper::frozen; // = false +#if ANY(SOFT_FEED_HOLD, FREEZE_FEATURE) + frozen_state_t Stepper::frozen_state; // Frozen flags +#endif +#if ENABLED(SOFT_FEED_HOLD) + uint32_t Stepper::frozen_time; // How much time has passed since frozen_state was triggered? + #if ENABLED(LASER_FEATURE) + uint8_t frozen_last_laser_power; // Saved laser power prior to halting motion + #endif #endif // Delta error variables for the Bresenham line tracer @@ -1846,7 +1852,11 @@ void Stepper::isr() { if (!current_block || step_events_completed >= step_event_count) return; // Skipping step processing causes motion to freeze - if (TERN0(FREEZE_FEATURE, frozen)) return; + #if ENABLED(SOFT_FEED_HOLD) + if (frozen_state.triggered && frozen_state.solid) return; + #elif ENABLED(FREEZE_FEATURE) + if (frozen_state.state == 0) return; + #endif // Count of pending loops and events for this iteration const uint32_t pending_events = step_event_count - step_events_completed; @@ -2418,6 +2428,10 @@ void Stepper::isr() { // If no queued movements, just wait 1ms for the next block hal_timer_t interval = (STEPPER_TIMER_RATE) / 1000UL; + // Frozen solid?? Exit and do not fetch blocks. + if (TERN0(SOFT_FEED_HOLD, frozen_state.triggered && frozen_state.solid)) + return interval; + // If there is a current block if (current_block) { // If current block is finished, reset pointer and finalize state @@ -2460,11 +2474,16 @@ void Stepper::isr() { // acc_step_rate is in steps/second + // Modify acc_step_rate if the machine is freezing + TERN_(SOFT_FEED_HOLD, check_frozen_time(acc_step_rate)); + // step_rate to timer interval and steps per stepper isr interval = calc_multistep_timer_interval(acc_step_rate << oversampling_factor); acceleration_time += interval; deceleration_time = 0; // Reset since we're doing acceleration first. + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_ACCELERATION, interval)); + // Apply Nonlinear Extrusion, if enabled calc_nonlinear_e(acc_step_rate << oversampling_factor); @@ -2526,10 +2545,14 @@ void Stepper::isr() { #endif + TERN_(SOFT_FEED_HOLD, check_frozen_time(step_rate)); + // step_rate to timer interval and steps per stepper isr interval = calc_multistep_timer_interval(step_rate << oversampling_factor); deceleration_time += interval; + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_DECELERATION, interval)); + // Apply Nonlinear Extrusion, if enabled calc_nonlinear_e(step_rate << oversampling_factor); @@ -2576,21 +2599,25 @@ void Stepper::isr() { else { // Must be in cruise phase otherwise // Calculate the ticks_nominal for this nominal speed, if not done yet - if (ticks_nominal == 0) { + if (ticks_nominal == 0 || TERN0(SOFT_FEED_HOLD, frozen_time)) { + uint32_t step_rate = current_block->nominal_rate; + + TERN_(SOFT_FEED_HOLD, check_frozen_time(step_rate)); + // step_rate to timer interval and loops for the nominal speed - ticks_nominal = calc_multistep_timer_interval(current_block->nominal_rate << oversampling_factor); + ticks_nominal = calc_multistep_timer_interval(step_rate << oversampling_factor); deceleration_time = ticks_nominal / 2; // Prepare for deceleration - IF_DISABLED(S_CURVE_ACCELERATION, acc_step_rate = current_block->nominal_rate); + IF_DISABLED(S_CURVE_ACCELERATION, acc_step_rate = step_rate); TERN_(SMOOTH_LIN_ADVANCE, curr_step_rate = current_block->nominal_rate); // Apply Nonlinear Extrusion, if enabled - calc_nonlinear_e(current_block->nominal_rate << oversampling_factor); + calc_nonlinear_e(step_rate << oversampling_factor); #if HAS_ROUGH_LIN_ADVANCE if (la_active) - la_interval = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling); + la_interval = calc_timer_interval(step_rate >> current_block->la_scaling); #endif // Adjust Laser Power - Cruise @@ -2610,6 +2637,8 @@ void Stepper::isr() { // The timer interval is just the nominal value for the nominal speed interval = ticks_nominal; + + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_CRUISE, interval)); } } @@ -2631,9 +2660,12 @@ void Stepper::isr() { #endif } else { // !current_block + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_STATIONARY, interval)); + #if ENABLED(LASER_FEATURE) + // If no movement in dynamic mode turn Laser off if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC) - cutter.apply_power(0); // No movement in dynamic mode so turn Laser off + cutter.apply_power(0); #endif } @@ -2916,13 +2948,22 @@ void Stepper::isr() { } #endif + uint32_t initial_rate = current_block->initial_rate; + + #if ENABLED(SOFT_FEED_HOLD) + if (frozen_time) check_frozen_time(initial_rate); + #endif + // Calculate the initial timer interval - interval = calc_multistep_timer_interval(current_block->initial_rate << oversampling_factor); + interval = calc_multistep_timer_interval(initial_rate << oversampling_factor); + + TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_ACCELERATION, interval)); + // Initialize ac/deceleration time as if half the time passed. acceleration_time = deceleration_time = interval / 2; // Apply Nonlinear Extrusion, if enabled - calc_nonlinear_e(current_block->initial_rate << oversampling_factor); + calc_nonlinear_e(initial_rate << oversampling_factor); #if ENABLED(LIN_ADVANCE) #if ENABLED(SMOOTH_LIN_ADVANCE) @@ -2930,7 +2971,7 @@ void Stepper::isr() { #else if (la_active) { const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0; - la_interval = calc_timer_interval((current_block->initial_rate + la_step_rate) >> current_block->la_scaling); + la_interval = calc_timer_interval((initial_rate + la_step_rate) >> current_block->la_scaling); } #endif #endif @@ -3876,3 +3917,105 @@ void Stepper::report_positions() { } #endif // BABYSTEPPING + +#if ENABLED(SOFT_FEED_HOLD) + + void Stepper::set_frozen_solid(const bool state) { + if (state == frozen_state.solid) return; + + frozen_state.solid = true; + + #if ENABLED(LASER_FEATURE) + if (state) { + frozen_last_laser_power = cutter.last_power_applied; + cutter.apply_power(0); // No movement in dynamic mode so turn Laser off + } + else + cutter.apply_power(frozen_last_laser_power); // Restore frozen laser power + #endif + + #if ENABLED(REALTIME_REPORTING_COMMANDS) + set_and_report_grblstate(state ? M_HOLD : M_RUNNING); + #endif + } + + void Stepper::check_frozen_time(uint32_t &step_rate) { + // If frozen_time is 0 there is no need to modify the current step_rate + if (!frozen_time) return; + + #if ENABLED(S_CURVE_ACCELERATION) + // If the machine is configured to use S_CURVE_ACCELERATION standard ramp acceleration + // rate will not have been calculated at this point + if (!current_block->acceleration_rate) + current_block->acceleration_rate = uint32_t(current_block->acceleration_steps_per_s2 * (float(1UL << 24) / (STEPPER_TIMER_RATE))); + #endif + + const uint32_t freeze_rate = STEP_MULTIPLY(frozen_time, current_block->acceleration_rate); + const uint32_t min_step_rate = current_block->steps_per_mm * (FREEZE_JERK); + + if (step_rate > freeze_rate) + step_rate -= freeze_rate; + else + step_rate = 0; + + if (step_rate <= min_step_rate) { + set_frozen_solid(true); + step_rate = min_step_rate; + } + } + + void Stepper::check_frozen_state(const FreezePhase phase, const uint32_t interval) { + switch (phase) { + case FREEZE_STATIONARY: + // If triggered while stationary immediately set solid flag + if (frozen_state.triggered) { + frozen_time = 0; + set_frozen_solid(true); + } + else + set_frozen_solid(false); + break; + + case FREEZE_ACCELERATION: + // If frozen state is activated during the acceleration phase of a block we need to double our decceleration efforts + if (frozen_state.triggered) { + if (!frozen_state.solid) frozen_time += interval * 2; + } + else + set_frozen_solid(false); + break; + + case FREEZE_DECELERATION: + // If frozen state is deactivated during the deceleration phase we need to double our acceleration efforts + if (!frozen_state.triggered) { + if (frozen_time) { + if (frozen_time > interval * 2) + frozen_time -= interval * 2; + else + frozen_time = 0; + } + set_frozen_solid(false); + } + break; + + case FREEZE_CRUISE: + // During cruise stage acceleration/deceleration take place at regular rate + if (frozen_state.triggered) { + if (!frozen_state.solid) frozen_time += interval; + } + else { + if (frozen_time) { + if (frozen_time > interval) + frozen_time -= interval; + else { + frozen_time = 0; + ticks_nominal = 0; // Reset ticks_nominal to allow for recalculation of interval at nominal_rate + } + } + set_frozen_solid(false); + } + break; + } + } + +#endif // SOFT_FEED_HOLD diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 6e6761f842..ec567245f0 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -318,6 +318,26 @@ constexpr ena_mask_t enable_overlap[] = { #endif // NONLINEAR_EXTRUSION +#if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD) + + typedef union { + uint8_t state; + struct { bool triggered:1, solid:1; }; + } frozen_state_t; + + enum FrozenState { FROZEN_TRIGGERED, FROZEN_SOLID }; + + #if ENABLED(SOFT_FEED_HOLD) + enum FreezePhase : uint8_t { + FREEZE_STATIONARY, + FREEZE_ACCELERATION, + FREEZE_DECELERATION, + FREEZE_CRUISE + }; + #endif + +#endif + // // Stepper class definition // @@ -367,8 +387,12 @@ class Stepper { static constexpr uint8_t last_moved_extruder = 0; #endif - #if ENABLED(FREEZE_FEATURE) - static bool frozen; // Set this flag to instantly freeze motion + #if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD) + static frozen_state_t frozen_state; // Frozen flags + static void set_frozen_triggered(const bool state) { frozen_state.triggered = state; } + #if ENABLED(SOFT_FEED_HOLD) + static bool is_frozen_triggered() { return frozen_state.triggered; } + #endif #endif #if ENABLED(NONLINEAR_EXTRUSION) @@ -800,6 +824,15 @@ class Stepper { static void ftMotion_stepper(); #endif + #if ENABLED(SOFT_FEED_HOLD) + static uint32_t frozen_time; // How much time passed since frozen_state was triggered? + #if ENABLED(LASER_FEATURE) + static uint8_t frozen_last_laser_power; // Saved laser power prior to halting motion + #endif + static void check_frozen_state(const FreezePhase type, const uint32_t interval); + static void check_frozen_time(uint32_t &step_rate); + static void set_frozen_solid(const bool state); + #endif }; extern Stepper stepper; diff --git a/buildroot/tests/DUE b/buildroot/tests/DUE index d5fcf2e4cb..9b8be3fbf9 100755 --- a/buildroot/tests/DUE +++ b/buildroot/tests/DUE @@ -14,8 +14,9 @@ opt_set MOTHERBOARD BOARD_RAMPS4DUE_EFB \ E0_AUTO_FAN_PIN 8 FANMUX0_PIN 53 EXTRUDER_AUTO_FAN_SPEED 100 \ TEMP_SENSOR_CHAMBER 3 TEMP_CHAMBER_PIN 6 HEATER_CHAMBER_PIN 45 \ BACKLASH_MEASUREMENT_FEEDRATE 600 \ - TRAMMING_POINT_XY '{{20,20},{20,20},{20,20},{20,20},{20,20}}' TRAMMING_POINT_NAME_5 '"Point 5"' -opt_enable S_CURVE_ACCELERATION EEPROM_SETTINGS GCODE_MACROS GCODE_MACROS_IN_EEPROM \ + TRAMMING_POINT_XY '{{20,20},{20,20},{20,20},{20,20},{20,20}}' TRAMMING_POINT_NAME_5 '"Point 5"' \ + FREEZE_PIN 17 +opt_enable S_CURVE_ACCELERATION FREEZE_FEATURE SOFT_FEED_HOLD EEPROM_SETTINGS GCODE_MACROS GCODE_MACROS_IN_EEPROM \ FIX_MOUNTED_PROBE Z_SAFE_HOMING CODEPENDENT_XY_HOMING \ ASSISTED_TRAMMING REPORT_TRAMMING_MM ASSISTED_TRAMMING_WAIT_POSITION \ EEPROM_SETTINGS SDSUPPORT BINARY_FILE_TRANSFER \ From fc641d39b662be61c29b0d1bc5f659094afbbf79 Mon Sep 17 00:00:00 2001 From: thinkyhead Date: Wed, 21 Jan 2026 06:13:25 +0000 Subject: [PATCH 39/39] [cron] Bump distribution date (2026-01-21) --- 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 b90f4917d5..17e522e0c8 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 "2026-01-18" +//#define STRING_DISTRIBUTION_DATE "2026-01-21" /** * 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 1414f113c2..3264b06f06 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 "2026-01-18" + #define STRING_DISTRIBUTION_DATE "2026-01-21" #endif /**