mirror of
https://github.com/MarlinFirmware/Marlin.git
synced 2026-03-05 01:24:48 -07:00
Merge 19d837026f into a6ebfe14cc
This commit is contained in:
commit
f06f71bbeb
23 changed files with 9069 additions and 277 deletions
3770
Marlin/2Configuration.h
Normal file
3770
Marlin/2Configuration.h
Normal file
File diff suppressed because it is too large
Load diff
4834
Marlin/2Configuration_adv.h
Normal file
4834
Marlin/2Configuration_adv.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1204,7 +1204,7 @@
|
|||
#define FTM_SHAPING_ZETA_E 0.03f // Zeta used by input shapers for E axis
|
||||
#define FTM_SHAPING_V_TOL_E 0.05f // Vibration tolerance used by EI input shapers for E axis
|
||||
|
||||
//#define FTM_RESONANCE_TEST // Sine sweep motion for resonance study
|
||||
//#define RESONANCE_TEST // Sine sweep motion for resonance study
|
||||
|
||||
//#define FTM_SMOOTHING // Smoothing can reduce artifacts and make steppers quieter
|
||||
// on sharp corners, but too much will round corners.
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
// Static data members
|
||||
bool EmergencyParser::killed_by_M112, // = false
|
||||
EmergencyParser::quickstop_by_M410,
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
EmergencyParser::rt_stop_by_M496, // = false
|
||||
#endif
|
||||
#if HAS_MEDIA
|
||||
|
|
@ -151,7 +151,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) {
|
|||
case EP_M4:
|
||||
switch (c) {
|
||||
case '1' :state = EP_M41; break;
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
case '9': state = EP_M49; break;
|
||||
#endif
|
||||
default: state = EP_IGNORE;
|
||||
|
|
@ -160,7 +160,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) {
|
|||
|
||||
case EP_M41: state = (c == '0') ? EP_M410 : EP_IGNORE; break;
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
case EP_M49: state = (c == '6') ? EP_M496 : EP_IGNORE; break;
|
||||
#endif
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) {
|
|||
case EP_M108: marlin.end_waiting(); break;
|
||||
case EP_M112: killed_by_M112 = true; break;
|
||||
case EP_M410: quickstop_by_M410 = true; break;
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
case EP_M496: rt_stop_by_M496 = true; break;
|
||||
#endif
|
||||
#if ENABLED(EP_BABYSTEPPING)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
#if HAS_MEDIA
|
||||
EP_M5, EP_M52, EP_M524,
|
||||
#endif
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
EP_M49, EP_M496,
|
||||
#endif
|
||||
#if ENABLED(EP_BABYSTEPPING)
|
||||
|
|
@ -67,7 +67,7 @@ public:
|
|||
static bool killed_by_M112;
|
||||
static bool quickstop_by_M410;
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
static bool rt_stop_by_M496;
|
||||
#endif
|
||||
|
||||
|
|
|
|||
179
Marlin/src/feature/resonance/resonance_generator.cpp
Normal file
179
Marlin/src/feature/resonance/resonance_generator.cpp
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
|
||||
#if ENABLED(FT_MOTION)
|
||||
#include "../../module/ft_motion.h"
|
||||
#endif
|
||||
|
||||
#include "resonance_generator.h"
|
||||
#include "../../gcode/gcode.h" // for home_all_axes
|
||||
|
||||
resonance_test_params_t ResonanceGenerator::rt_params; // Resonance test parameters
|
||||
|
||||
bool ResonanceGenerator::active = false; // Resonance test active
|
||||
bool ResonanceGenerator::done = false; // Resonance test done
|
||||
int32_t ResonanceGenerator::freq_to_phase_fp;
|
||||
|
||||
#if HAS_STANDARD_MOTION
|
||||
block_t ResonanceGenerator::block;
|
||||
#endif
|
||||
|
||||
ResonanceGenerator rtg;
|
||||
|
||||
ResonanceGenerator::ResonanceGenerator() {}
|
||||
|
||||
void ResonanceGenerator::start() {
|
||||
gcode.home_all_axes(); // Always home axes first
|
||||
motion.blocking_move_xy(X_CENTER, Y_CENTER, Z_CLEARANCE_FOR_HOMING);
|
||||
|
||||
rt_params.start_pos = motion.position;
|
||||
active = true;
|
||||
done = false;
|
||||
|
||||
// Clamp Z-axis acceleration for safety
|
||||
if (rt_params.axis == Z_AXIS)
|
||||
NOMORE(rt_params.accel_per_hz, 15.0f);
|
||||
|
||||
// Calculate time constant for sine sweep
|
||||
const float rt_time = rt_params.octave_duration * (logf(RATIO) / logf(2.0f));
|
||||
|
||||
#if HAS_STANDARD_MOTION
|
||||
if (TERN1(FT_MOTION, !ftMotion.cfg.active)) {
|
||||
block.reset();
|
||||
block.initial_rate = (rt_params.axis == Z_AXIS) ? 2000 : 8000;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Precompute fixed-point sine sweep parameters
|
||||
amplitude_precalc_fp = F2FP((rt_params.amplitude_correction * rt_params.accel_per_hz * 0.25f) / sq(M_PI));
|
||||
current_freq_fp = F2FP(rt_params.min_freq);
|
||||
freq_to_phase_fp = F2FP(2.0f * M_PI * rt_time);
|
||||
max_freq_fp = F2FP(rt_params.max_freq);
|
||||
phase_fp = 0;
|
||||
}
|
||||
|
||||
void ResonanceGenerator::abort() {
|
||||
reset();
|
||||
#if(HAS_STANDARD_MOTION)
|
||||
if (!TERN0(FT_MOTION, ftMotion.cfg.active))
|
||||
return;
|
||||
#endif
|
||||
#if ENABLED(FT_MOTION)
|
||||
ftMotion.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ResonanceGenerator::reset() {
|
||||
rt_params = resonance_test_params_t();
|
||||
|
||||
#if HAS_STANDARD_MOTION
|
||||
if (!TERN0(FT_MOTION, ftMotion.cfg.active))
|
||||
block.reset();
|
||||
#endif
|
||||
active = false;
|
||||
done = false;
|
||||
}
|
||||
|
||||
float ResonanceGenerator::calc_next_pos() {
|
||||
// Phase accumulation and wrapping within [0, 2π)
|
||||
phase_fp += (int32_t)(((int64_t)current_freq_fp * freq_to_phase_fp) >> FP_BITS);
|
||||
if (phase_fp >= M_TAU_FP) phase_fp -= M_TAU_FP;
|
||||
else if (phase_fp < 0) phase_fp += M_TAU_FP;
|
||||
|
||||
// -π <= r_fp <= π
|
||||
int32_t r_fp = (phase_fp > M_PI_FP) ? phase_fp - M_TAU_FP : phase_fp;
|
||||
|
||||
// Calculate windowing polynomial: 1.0 - 0.101321184 * r²
|
||||
int32_t poly_fp = FP_ONE - ((C0101321184_FP * ((r_fp * r_fp) >> FP_BITS)) >> FP_BITS);
|
||||
|
||||
// Combine amplitude, phase, and polynomial and return new position
|
||||
int32_t amplitude_fp = (int32_t)(((int64_t)amplitude_precalc_fp * FP_ONE) / current_freq_fp);
|
||||
int32_t pos_fp = (int32_t)((((int64_t)amplitude_fp * r_fp) >> FP_BITS) * poly_fp) >> FP_BITS;
|
||||
|
||||
return FP2F(pos_fp);
|
||||
}
|
||||
|
||||
#if ENABLED(FT_MOTION)
|
||||
void ResonanceGenerator::fill_stepper_plan_buffer() {
|
||||
xyze_pos_t traj_coords = rt_params.start_pos;
|
||||
// Save starting position, avoid cumulative errors
|
||||
const float start_pos = rt_params.start_pos[rt_params.axis];
|
||||
|
||||
while (!ftMotion.stepping.is_full()) {
|
||||
// Calculate current frequency with exponential sweep
|
||||
current_freq_fp += current_freq_fp >> 16;
|
||||
if (current_freq_fp > max_freq_fp) {
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Resonate the axis being tested
|
||||
traj_coords[rt_params.axis] = start_pos + calc_next_pos();
|
||||
|
||||
// Store in buffer
|
||||
ftMotion.stepping_enqueue(traj_coords);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_STANDARD_MOTION
|
||||
block_t *ResonanceGenerator::generate_resonance_block() {
|
||||
// Static variables to retain state between calls and avoid cumulative errors
|
||||
static float prev_pos = 0.0f, step_accumulator = 0.0f;
|
||||
|
||||
const float step_mm = planner.settings.axis_steps_per_mm[rt_params.axis];
|
||||
const uint8_t axis_bit = 1 << rt_params.axis;
|
||||
|
||||
// Calculate current frequency with exponential sweep
|
||||
current_freq_fp += current_freq_fp >> 16;
|
||||
if (current_freq_fp > max_freq_fp) {
|
||||
done = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Calculate position and accumulate steps
|
||||
float current_pos = calc_next_pos();
|
||||
step_accumulator += (current_pos - prev_pos) * step_mm;
|
||||
prev_pos = current_pos;
|
||||
|
||||
// Extract steps
|
||||
int32_t delta_steps = (int32_t)floor(step_accumulator);
|
||||
step_accumulator -= delta_steps;
|
||||
uint32_t abs_steps = abs(delta_steps);
|
||||
|
||||
// Update block
|
||||
block.steps[rt_params.axis] = abs_steps;
|
||||
block.step_event_count = abs_steps;
|
||||
if (delta_steps < 0)
|
||||
block.direction_bits |= axis_bit;
|
||||
else
|
||||
block.direction_bits &= ~axis_bit;
|
||||
|
||||
return █
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RESONANCE_TEST
|
||||
|
|
@ -25,11 +25,29 @@
|
|||
|
||||
#include <math.h>
|
||||
|
||||
#ifndef M_TAU
|
||||
#define M_TAU (2.0f * M_PI)
|
||||
#if HAS_STANDARD_MOTION
|
||||
#include "../../module/planner.h"
|
||||
#endif
|
||||
|
||||
typedef struct FTMResonanceTestParams {
|
||||
// Fixed-point configuration
|
||||
#define FP_BITS 16 // 16 bits for fractional part
|
||||
#define FP_ONE (1UL << FP_BITS) // Fixed-point 1.0
|
||||
|
||||
// Convert float to fixed-point
|
||||
#define F2FP(x) ((int32_t)((x) * FP_ONE + 0.5f))
|
||||
|
||||
// Convert fixed-point to float
|
||||
#define FP2F(x) ((float)(x) / FP_ONE)
|
||||
|
||||
// Fixed-point constants
|
||||
#define M_TAU_FP F2FP(2.0f * M_PI)
|
||||
#define M_PI_FP F2FP(M_PI)
|
||||
#define C0101321184_FP F2FP(0.101321184f)
|
||||
|
||||
// For rt_time calculation, perfect match between octave duration and frequency sweep
|
||||
#define RATIO (1.0f + 1.0f / 65536.0f) // 1 + 1/2^16
|
||||
|
||||
typedef struct ResonanceTestParams {
|
||||
AxisEnum axis = NO_AXIS_ENUM; // Axis to test
|
||||
float min_freq = 5.0f; // Minimum frequency [Hz]
|
||||
float max_freq = 100.0f; // Maximum frequency [Hz]
|
||||
|
|
@ -37,27 +55,18 @@ typedef struct FTMResonanceTestParams {
|
|||
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;
|
||||
} resonance_test_params_t;
|
||||
|
||||
class ResonanceGenerator {
|
||||
public:
|
||||
static ftm_resonance_test_params_t rt_params; // Resonance test parameters
|
||||
static float timeline; // Timeline Value to calculate resonance frequency
|
||||
static resonance_test_params_t rt_params; // Resonance test parameters
|
||||
float timeline; // Timeline Value to calculate resonance frequency
|
||||
|
||||
ResonanceGenerator();
|
||||
|
||||
void reset();
|
||||
|
||||
void start(const xyze_pos_t &spos, const float t) {
|
||||
rt_params.start_pos = spos;
|
||||
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);
|
||||
}
|
||||
void start();
|
||||
|
||||
// Return frequency based on timeline
|
||||
float getFrequencyFromTimeline() {
|
||||
|
|
@ -65,7 +74,13 @@ class ResonanceGenerator {
|
|||
return rt_params.min_freq * exp2f(timeline / rt_params.octave_duration);
|
||||
}
|
||||
|
||||
void fill_stepper_plan_buffer(); // Fill stepper plan buffer with trajectory points
|
||||
#if HAS_STANDARD_MOTION
|
||||
block_t *generate_resonance_block(); // Generate planner block for standard motion
|
||||
#endif
|
||||
|
||||
#if ENABLED(FT_MOTION)
|
||||
void fill_stepper_plan_buffer(); // Fill stepper plan buffer with trajectory points
|
||||
#endif
|
||||
|
||||
void setActive(const bool state) { active = state; }
|
||||
bool isActive() const { return active; }
|
||||
|
|
@ -76,10 +91,22 @@ class ResonanceGenerator {
|
|||
void abort(); // Abort resonance test
|
||||
|
||||
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
|
||||
float calc_next_pos(); // Calculate next position point based on current frequency
|
||||
|
||||
// Fixed-point variables
|
||||
int32_t amplitude_precalc_fp; // Fixed-point amplitude precalculation
|
||||
int32_t current_freq_fp; // Fixed-point current frequency
|
||||
|
||||
// Phase variables (in radians, stored as fixed-point)
|
||||
int32_t phase_fp; // Fixed-point phase accumulator
|
||||
static int32_t freq_to_phase_fp; // Fixed-point frequency to phase conversion
|
||||
|
||||
int32_t max_freq_fp; // Fixed-point maximum frequency
|
||||
#if HAS_STANDARD_MOTION
|
||||
static block_t block;
|
||||
#endif
|
||||
static bool active; // Resonance test active
|
||||
static bool done; // Resonance test done
|
||||
};
|
||||
|
||||
extern ResonanceGenerator rtg;
|
||||
|
|
@ -22,15 +22,14 @@
|
|||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
|
||||
#include "../../gcode.h"
|
||||
#include "../../../lcd/marlinui.h"
|
||||
#include "../../../module/ft_motion.h"
|
||||
#include "../../../module/ft_motion/resonance_generator.h"
|
||||
#include "../../../feature/resonance/resonance_generator.h"
|
||||
|
||||
void say_resonance_test() {
|
||||
const ftm_resonance_test_params_t &p = ftMotion.rtg.rt_params;
|
||||
const resonance_test_params_t &p = rtg.rt_params;
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLN(F("M495 "), F("Resonance Test"));
|
||||
SERIAL_ECHOLNPGM(" Axis: ", p.axis == NO_AXIS_ENUM ? C('-') : C(AXIS_CHAR(p.axis)));
|
||||
|
|
@ -64,7 +63,7 @@ void say_resonance_test() {
|
|||
void GcodeSuite::M495() {
|
||||
if (!parser.seen_any()) return say_resonance_test();
|
||||
|
||||
ftm_resonance_test_params_t &p = ftMotion.rtg.rt_params;
|
||||
resonance_test_params_t &p = rtg.rt_params;
|
||||
|
||||
const bool seenX = parser.seen_test('X'), seenY = parser.seen_test('Y'), seenZ = parser.seen_test('Z');
|
||||
|
||||
|
|
@ -137,8 +136,8 @@ void GcodeSuite::M495() {
|
|||
if (parser.seenval('G')) {
|
||||
const float val = parser.value_float();
|
||||
if (WITHIN(val, 0, 100)) {
|
||||
ftMotion.rtg.timeline = val;
|
||||
SERIAL_ECHOLNPGM("Resonance Frequency set to ", ftMotion.rtg.getFrequencyFromTimeline(), " Hz");
|
||||
rtg.timeline = val;
|
||||
SERIAL_ECHOLNPGM("Resonance Frequency set to ", rtg.getFrequencyFromTimeline(), " Hz");
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHOLN(F("?Invalid "), F("Timeline value (0..100 s)"));
|
||||
|
|
@ -146,23 +145,18 @@ void GcodeSuite::M495() {
|
|||
}
|
||||
|
||||
if (parser.seen_test('S')) {
|
||||
if (ftMotion.cfg.active) {
|
||||
if (p.axis != NO_AXIS_ENUM) {
|
||||
if (p.max_freq > p.min_freq) {
|
||||
SERIAL_ECHOLN(F("Starting "), F("Resonance Test"));
|
||||
ftMotion.start_resonance_test();
|
||||
// The function returns immediately, the test runs in the background.
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHOLNPGM("?End Frequency must be greater than Start Frequency");
|
||||
}
|
||||
if (p.axis != NO_AXIS_ENUM) {
|
||||
if (p.max_freq > p.min_freq) {
|
||||
SERIAL_ECHOLN(F("Starting "), F("Resonance Test"));
|
||||
rtg.start();
|
||||
// The function returns immediately, the test runs in the background.
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHOLN(F("?Specify X, Y, or Z axis"), F(" first"));
|
||||
SERIAL_ECHOLNPGM("?End Frequency must be greater than Start Frequency");
|
||||
}
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHOLN(F("?Activate FT Motion to run the "), F("Resonance Test"));
|
||||
SERIAL_ECHOLN(F("?Specify X, Y, or Z axis"), F(" first"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -171,8 +165,8 @@ void GcodeSuite::M495() {
|
|||
* M496: Abort the resonance test (via Emergency Parser)
|
||||
*/
|
||||
void GcodeSuite::M496() {
|
||||
if (ftMotion.rtg.isActive()) {
|
||||
ftMotion.rtg.abort();
|
||||
if (rtg.isActive()) {
|
||||
rtg.abort();
|
||||
EmergencyParser::rt_stop_by_M496 = false;
|
||||
ui.refresh();
|
||||
#if DISABLED(MARLIN_SMALL_BUILD)
|
||||
|
|
@ -185,4 +179,4 @@ void GcodeSuite::M496() {
|
|||
#endif
|
||||
}
|
||||
|
||||
#endif // FTM_RESONANCE_TEST
|
||||
#endif // RESONANCE_TEST
|
||||
|
|
@ -924,7 +924,7 @@ void GcodeSuite::process_parsed_command(bool no_ok/*=false*/) {
|
|||
#if ANY(FTM_SMOOTHING, FTM_POLYS)
|
||||
case 494: M494(); break; // M494: Fixed-Time Motion extras
|
||||
#endif
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
case 495: M495(); break; // M495: Resonance test for Input Shaping
|
||||
case 496: M496(); break; // M496: Abort resonance test
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -252,8 +252,8 @@
|
|||
* M485 - Send RS485 packets (Requires RS485_SERIAL_PORT)
|
||||
* M486 - Identify and cancel objects. (Requires CANCEL_OBJECTS)
|
||||
* M493 - Set / Report input FT Motion/Shaping parameters. (Requires FT_MOTION)
|
||||
* M495 - Set / Start resonance test. (Requires FTM_RESONANCE_TEST)
|
||||
* M496 - Abort resonance test. (Requires FTM_RESONANCE_TEST)
|
||||
* M495 - Set / Start resonance test. (Requires RESONANCE_TEST)
|
||||
* M496 - Abort resonance test. (Requires RESONANCE_TEST)
|
||||
* M500 - Store parameters in EEPROM. (Requires EEPROM_SETTINGS)
|
||||
* M501 - Restore parameters from EEPROM. (Requires EEPROM_SETTINGS)
|
||||
* M502 - Revert to the default "factory settings". ** Does not write them to EEPROM! **
|
||||
|
|
@ -1124,11 +1124,12 @@ private:
|
|||
static void M493_report(const bool forReplay=true);
|
||||
static void M494();
|
||||
static void M494_report(const bool forReplay=true);
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#endif
|
||||
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
static void M495();
|
||||
static void M495_report(const bool forReplay=true);
|
||||
static void M496();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void M500();
|
||||
|
|
|
|||
|
|
@ -4511,8 +4511,8 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
|
|||
static_assert(FTM_SMOOTHING_TIME_Z <= FTM_MAX_SMOOTHING_TIME, "FTM_SMOOTHING_TIME_Z must be <= FTM_MAX_SMOOTHING_TIME.");
|
||||
static_assert(FTM_SMOOTHING_TIME_E <= FTM_MAX_SMOOTHING_TIME, "FTM_SMOOTHING_TIME_E must be <= FTM_MAX_SMOOTHING_TIME.");
|
||||
#endif
|
||||
#if ENABLED(FTM_RESONANCE_TEST) && DISABLED(EMERGENCY_PARSER)
|
||||
#error "EMERGENCY_PARSER is required with FTM_RESONANCE_TEST (to cancel the test)."
|
||||
#if ENABLED(RESONANCE_TEST) && DISABLED(EMERGENCY_PARSER)
|
||||
#error "EMERGENCY_PARSER is required with RESONANCE_TEST (to cancel the test)."
|
||||
#endif
|
||||
#if !HAS_STANDARD_MOTION
|
||||
#if ENABLED(SMOOTH_LIN_ADVANCE)
|
||||
|
|
|
|||
|
|
@ -931,13 +931,13 @@ namespace LanguageNarrow_en {
|
|||
LSTR MSG_FTM_SMOOTH_TIME_N = _UxGT("@ Smoothing Time");
|
||||
LSTR MSG_FTM_POLY6_OVERSHOOT = _UxGT("@ Poly6 Overshoot");
|
||||
LSTR MSG_FTM_CONFIGURE_AXIS_N = _UxGT("Configure @ Axis");
|
||||
LSTR MSG_FTM_RESONANCE_TEST = _UxGT("Resonance Test");
|
||||
LSTR MSG_FTM_RT_RUNNING = _UxGT("Res. Test Running...");
|
||||
LSTR MSG_FTM_RT_START_N = _UxGT("Start @ Axis Test");
|
||||
LSTR MSG_FTM_RT_STOP = _UxGT("Abort Test");
|
||||
LSTR MSG_FTM_RETRIEVE_FREQ = _UxGT("Calc. Res. Freq.");
|
||||
LSTR MSG_FTM_RESONANCE_FREQ = _UxGT("Resonance Freq.");
|
||||
LSTR MSG_FTM_TIMELINE_FREQ = _UxGT("Timeline (s)");
|
||||
LSTR MSG_RESONANCE_TEST = _UxGT("Resonance Test");
|
||||
LSTR MSG_RT_RUNNING = _UxGT("Res. Test Running...");
|
||||
LSTR MSG_RT_START_N = _UxGT("Start @ Axis Test");
|
||||
LSTR MSG_RT_STOP = _UxGT("Abort Test");
|
||||
LSTR MSG_RETRIEVE_FREQ = _UxGT("Calc. Res. Freq.");
|
||||
LSTR MSG_RESONANCE_FREQ = _UxGT("Resonance Freq.");
|
||||
LSTR MSG_TIMELINE_FREQ = _UxGT("Timeline (s)");
|
||||
LSTR MSG_TOUCH_CALIBRATION = _UxGT("Touch Calibration");
|
||||
LSTR MSG_MARLIN = _UxGT("Marlin");
|
||||
LSTR MSG_PID_P = _UxGT("PID-P");
|
||||
|
|
@ -1099,8 +1099,8 @@ namespace LanguageWide_en {
|
|||
LSTR MSG_EEPROM_INITIALIZED = _UxGT("Default Settings Restored");
|
||||
LSTR MSG_MEDIA_MENU_SD = _UxGT("Select from SD Card");
|
||||
LSTR MSG_MEDIA_MENU_USB = _UxGT("Select from USB Drive");
|
||||
LSTR MSG_FTM_RT_RUNNING = _UxGT("Resonance Test Running...");
|
||||
LSTR MSG_FTM_RESONANCE_FREQ = _UxGT("Resonance frequency");
|
||||
LSTR MSG_RT_RUNNING = _UxGT("Resonance Test Running...");
|
||||
LSTR MSG_RESONANCE_FREQ = _UxGT("Resonance frequency");
|
||||
LSTR MSG_HOMING_FEEDRATE_X = _UxGT("X Homing Feedrate");
|
||||
LSTR MSG_HOMING_FEEDRATE_Y = _UxGT("Y Homing Feedrate");
|
||||
LSTR MSG_HOMING_FEEDRATE_Z = _UxGT("Z Homing Feedrate");
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ namespace LanguageNarrow_it {
|
|||
|
||||
constexpr uint8_t CHARSIZE = 1;
|
||||
LSTR LANGUAGE = _UxGT("Italiano");
|
||||
|
||||
LSTR WELCOME_MSG = MACHINE_NAME_SUBST _UxGT(" pronta."); // (MACHINE_NAME_SUBST) Ready.
|
||||
LSTR MSG_MEDIA_INSERTED = MEDIA_TYPE_IT _UxGT(" inserita"); // (MEDIA_TYPE_EN) Inserted
|
||||
LSTR MSG_MEDIA_REMOVED = MEDIA_TYPE_IT _UxGT(" rimossa"); // (MEDIA_TYPE_EN) Removed
|
||||
|
|
@ -894,13 +893,13 @@ namespace LanguageNarrow_it {
|
|||
LSTR MSG_FTM_VTOL_N = _UxGT("Livello vib. @"); // @ Vib. Level
|
||||
LSTR MSG_FTM_SMOOTH_TIME_N = _UxGT("@ Tempo smorzamento"); // @ Smoothing Time
|
||||
LSTR MSG_FTM_POLY6_OVERSHOOT = _UxGT("@ Overshoot Poly6"); // @ Poly6 Overshoot
|
||||
LSTR MSG_FTM_RESONANCE_TEST = _UxGT("Test risonanza"); // Resonance Test
|
||||
LSTR MSG_FTM_RT_RUNNING = _UxGT("Test ris.in corso..."); // Res. Test Running...
|
||||
LSTR MSG_FTM_RT_START_N = _UxGT("Avvia Test Asse @"); // Start @ Axis Test
|
||||
LSTR MSG_FTM_RT_STOP = _UxGT("Annulla Test"); // Abort Test
|
||||
LSTR MSG_FTM_RETRIEVE_FREQ = _UxGT("Calc. Res. Freq."); // Calc. Res. Freq.
|
||||
LSTR MSG_FTM_RESONANCE_FREQ = _UxGT("Freq.Risonanza"); // Resonance Freq.
|
||||
LSTR MSG_FTM_TIMELINE_FREQ = _UxGT("Cronologia (s)"); // Timeline (s)
|
||||
LSTR MSG_RESONANCE_TEST = _UxGT("Test risonanza"); // Resonance Test
|
||||
LSTR MSG_RT_RUNNING = _UxGT("Test ris.in corso..."); // Res. Test Running...
|
||||
LSTR MSG_RT_START_N = _UxGT("Avvia Test Asse @"); // Start @ Axis Test
|
||||
LSTR MSG_RT_STOP = _UxGT("Annulla Test"); // Abort Test
|
||||
LSTR MSG_RETRIEVE_FREQ = _UxGT("Calc. Res. Freq."); // Calc. Res. Freq.
|
||||
LSTR MSG_RESONANCE_FREQ = _UxGT("Freq.Risonanza"); // Resonance Freq.
|
||||
LSTR MSG_TIMELINE_FREQ = _UxGT("Cronologia (s)"); // Timeline (s)
|
||||
LSTR MSG_TOUCH_CALIBRATION = _UxGT("Calibrazione touch"); // Touch Calibration
|
||||
LSTR DGUS_MSG_NOT_WHILE_PRINTING = _UxGT("Non ammesso durante la stampa"); // Not allowed during print
|
||||
LSTR DGUS_MSG_NOT_WHILE_IDLE = _UxGT("Non ammesso mentre è in riposo"); // Not allowed while idle
|
||||
|
|
|
|||
|
|
@ -308,6 +308,46 @@ void menu_move() {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
#include "../../feature/resonance/resonance_generator.h"
|
||||
|
||||
void menu_resonance_freq() {
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_RESONANCE_TEST);
|
||||
|
||||
STATIC_ITEM(MSG_RETRIEVE_FREQ);
|
||||
EDIT_ITEM(float62, MSG_TIMELINE_FREQ, &rtg.timeline, 0.0f, 600.0f);
|
||||
PSTRING_ITEM(MSG_RESONANCE_FREQ, ftostr53_63(rtg.getFrequencyFromTimeline()), SS_FULL);
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
|
||||
void menu_resonance_test() {
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_MOTION);
|
||||
|
||||
if (rtg.isActive() && !rtg.isDone()) {
|
||||
STATIC_ITEM(MSG_RT_RUNNING);
|
||||
GCODES_ITEM(MSG_RT_STOP, F("M496"));
|
||||
}
|
||||
else {
|
||||
#if HAS_X_AXIS
|
||||
GCODES_ITEM_N(X_AXIS, MSG_RT_START_N, F("M495 X S"));
|
||||
#endif
|
||||
#if HAS_Y_AXIS
|
||||
GCODES_ITEM_N(Y_AXIS, MSG_RT_START_N, F("M495 Y S"));
|
||||
#endif
|
||||
#if HAS_Z_AXIS
|
||||
GCODES_ITEM_N(Z_AXIS, MSG_RT_START_N, F("M495 Z S"));
|
||||
#endif
|
||||
SUBMENU(MSG_RETRIEVE_FREQ, menu_resonance_freq);
|
||||
}
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
|
||||
#endif // RESONANCE_TEST
|
||||
|
||||
#if ENABLED(FT_MOTION_MENU)
|
||||
|
||||
#include "../../module/ft_motion.h"
|
||||
|
|
@ -405,45 +445,6 @@ void menu_move() {
|
|||
|
||||
#endif // FTM_POLYS
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
|
||||
void menu_ftm_resonance_freq() {
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_FTM_RESONANCE_TEST);
|
||||
|
||||
STATIC_ITEM(MSG_FTM_RETRIEVE_FREQ);
|
||||
EDIT_ITEM(float62, MSG_FTM_TIMELINE_FREQ, &ftMotion.rtg.timeline, 0.0f, 600.0f);
|
||||
PSTRING_ITEM(MSG_FTM_RESONANCE_FREQ, ftostr53_63(ftMotion.rtg.getFrequencyFromTimeline()), SS_FULL);
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
|
||||
void menu_ftm_resonance_test() {
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION);
|
||||
|
||||
if (ftMotion.rtg.isActive() && !ftMotion.rtg.isDone()) {
|
||||
STATIC_ITEM(MSG_FTM_RT_RUNNING);
|
||||
GCODES_ITEM(MSG_FTM_RT_STOP, F("M496"));
|
||||
}
|
||||
else {
|
||||
#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);
|
||||
}
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
|
||||
#endif // FTM_RESONANCE_TEST
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
|
||||
void menu_ftm_dyn_mode() {
|
||||
|
|
@ -558,10 +559,6 @@ void menu_move() {
|
|||
EDIT_ITEM(bool, MSG_FTM_AXIS_SYNC, &editable.state, []{
|
||||
queue.inject(TS(F("M493"), IAXIS_CHAR(MenuItemBase::itemIndex), 'T', int(editable.state)));
|
||||
});
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
SUBMENU(MSG_FTM_RESONANCE_TEST, menu_ftm_resonance_test);
|
||||
#endif
|
||||
}
|
||||
|
||||
END_MENU();
|
||||
|
|
@ -650,6 +647,13 @@ void menu_motion() {
|
|||
SUBMENU(MSG_FIXED_TIME_MOTION, menu_ft_motion);
|
||||
#endif
|
||||
|
||||
//
|
||||
// M495 Resonance Test
|
||||
//
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
SUBMENU(MSG_RESONANCE_TEST, menu_resonance_test);
|
||||
#endif
|
||||
|
||||
//
|
||||
// Pen up/down menu
|
||||
//
|
||||
|
|
|
|||
|
|
@ -37,10 +37,6 @@
|
|||
#include "ft_motion/trajectory_poly5.h"
|
||||
#include "ft_motion/trajectory_poly6.h"
|
||||
#endif
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#include "ft_motion/resonance_generator.h"
|
||||
#include "../gcode/gcode.h" // for home_all_axes
|
||||
#endif
|
||||
|
||||
#include "stepper.h" // Access stepper block queue function and abort status.
|
||||
#include "endstops.h"
|
||||
|
|
@ -89,11 +85,6 @@ TrapezoidalTrajectoryGenerator FTMotion::trapezoidalGenerator;
|
|||
TrajectoryGenerator* FTMotion::currentGenerator = &FTMotion::trapezoidalGenerator;
|
||||
#endif
|
||||
|
||||
// Resonance Test
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
ResonanceGenerator FTMotion::rtg; // Resonance trajectory generator instance
|
||||
#endif
|
||||
|
||||
#if FTM_HAS_LIN_ADVANCE
|
||||
bool FTMotion::use_advance_lead;
|
||||
#endif
|
||||
|
|
@ -167,9 +158,9 @@ void FTMotion::loop() {
|
|||
* 4. Signal ready for new block.
|
||||
*/
|
||||
|
||||
const bool using_resonance = TERN(FTM_RESONANCE_TEST, rtg.isActive(), false);
|
||||
const bool using_resonance = TERN(RESONANCE_TEST, rtg.isActive(), false);
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
if (using_resonance) {
|
||||
// Resonance Test has priority over normal ft_motion operation.
|
||||
// Process resonance test if active. When it's done, generate the last data points for a clean ending.
|
||||
|
|
@ -684,25 +675,4 @@ void FTMotion::fill_stepper_plan_buffer() {
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
|
||||
// Start Resonance Testing
|
||||
void FTMotion::start_resonance_test() {
|
||||
motion.home_if_needed(); // Ensure known axes first
|
||||
|
||||
ftm_resonance_test_params_t &p = rtg.rt_params;
|
||||
|
||||
// Safe Acceleration per Hz for Z axis
|
||||
if (p.axis == Z_AXIS && p.accel_per_hz > 15.0f)
|
||||
p.accel_per_hz = 15.0f;
|
||||
|
||||
// Always move to the center of the bed
|
||||
motion.blocking_move_xy(X_CENTER, Y_CENTER, Z_CLEARANCE_FOR_HOMING);
|
||||
|
||||
// Start test at the current position with the configured time-step
|
||||
rtg.start(motion.position, FTM_TS);
|
||||
}
|
||||
|
||||
#endif // FTM_RESONANCE_TEST
|
||||
|
||||
#endif // FT_MOTION
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
#include "ft_motion/trajectory_poly5.h"
|
||||
#include "ft_motion/trajectory_poly6.h"
|
||||
#endif
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
#include "ft_motion/resonance_generator.h"
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
#include "../feature/resonance/resonance_generator.h"
|
||||
#endif
|
||||
|
||||
#if HAS_FTM_SHAPING
|
||||
|
|
@ -238,10 +238,6 @@ typedef struct FTConfig {
|
|||
*/
|
||||
class FTMotion {
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
friend void ResonanceGenerator::fill_stepper_plan_buffer();
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// Public variables
|
||||
|
|
@ -268,10 +264,6 @@ class FTMotion {
|
|||
// Public methods
|
||||
static void init();
|
||||
static void loop(); // Controller main, to be invoked from non-isr task.
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
static void start_resonance_test(); // Start a resonance test with given parameters
|
||||
static ResonanceGenerator rtg; // Resonance trajectory generator instance
|
||||
#endif
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
// Refresh alpha and delay samples used by smoothing functions.
|
||||
|
|
|
|||
|
|
@ -1,112 +0,0 @@
|
|||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
|
||||
#include "../ft_motion.h"
|
||||
#include "resonance_generator.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
ftm_resonance_test_params_t ResonanceGenerator::rt_params; // Resonance test parameters
|
||||
|
||||
bool ResonanceGenerator::active = false; // Resonance test active
|
||||
bool ResonanceGenerator::done = false; // Resonance test done
|
||||
float ResonanceGenerator::rt_time = FTM_TS; // Resonance test timer
|
||||
float ResonanceGenerator::timeline = 0.0f;
|
||||
|
||||
ResonanceGenerator::ResonanceGenerator() {}
|
||||
|
||||
void ResonanceGenerator::abort() {
|
||||
reset();
|
||||
ftMotion.reset();
|
||||
}
|
||||
|
||||
void ResonanceGenerator::reset() {
|
||||
rt_params = ftm_resonance_test_params_t();
|
||||
rt_time = FTM_TS;
|
||||
active = false;
|
||||
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<int>(y); // Truncates toward zero
|
||||
|
||||
// Negative? The truncation is one too high.
|
||||
if (y < 0.0f) --k; // Correct for negatives
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void ResonanceGenerator::fill_stepper_plan_buffer() {
|
||||
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
|
||||
current_freq *= freq_mul;
|
||||
if (current_freq > rt_params.max_freq) {
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 / current_freq;
|
||||
|
||||
// Phase in radians
|
||||
const float phase = current_freq * rt_factor;
|
||||
|
||||
// Position Offset : between -A and +A
|
||||
const float pos_offset = amplitude * fast_sin(phase);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // FTM_RESONANCE_TEST
|
||||
|
|
@ -74,5 +74,5 @@ protected:
|
|||
*/
|
||||
enum class TrajectoryType : uint8_t {
|
||||
TRAPEZOIDAL, POLY5, POLY6
|
||||
OPTARG(FTM_RESONANCE_TEST, RESONANCE)
|
||||
OPTARG(RESONANCE_TEST, RESONANCE)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ Stepper stepper; // Singleton
|
|||
#include "ft_motion.h"
|
||||
#endif
|
||||
|
||||
#if (ENABLED(RESONANCE_TEST) && HAS_STANDARD_MOTION)
|
||||
#include "../feature/resonance/resonance_generator.h"
|
||||
#endif
|
||||
|
||||
#include "../lcd/marlinui.h"
|
||||
#include "../gcode/queue.h"
|
||||
#include "../sd/cardreader.h"
|
||||
|
|
@ -618,6 +622,117 @@ bool Stepper::disable_axis(const AxisEnum axis) {
|
|||
return can_disable;
|
||||
}
|
||||
|
||||
#if (ENABLED(RESONANCE_TEST) && HAS_STANDARD_MOTION)
|
||||
hal_timer_t Stepper::resonance_block_phase_isr() {
|
||||
|
||||
const hal_timer_t time_spent = HAL_timer_get_count(MF_TIMER_STEP);
|
||||
#if MULTISTEPPING_LIMIT > 1
|
||||
if (steps_per_isr > 1 && time_spent_out_isr >= time_spent_in_isr + time_spent)
|
||||
steps_per_isr >>= 1;
|
||||
#endif
|
||||
time_spent_in_isr = -time_spent; // Unsigned but guaranteed to be +ve when needed
|
||||
time_spent_out_isr = 0;
|
||||
|
||||
hal_timer_t interval = 0;
|
||||
|
||||
// If current block is not finished, continue with it
|
||||
if (step_events_completed < step_event_count)
|
||||
return calc_multistep_timer_interval(current_block->initial_rate);
|
||||
|
||||
// Current block is finished, reset pointer
|
||||
current_block = nullptr;
|
||||
|
||||
// Generate a new block
|
||||
if ((current_block = rtg.generate_resonance_block())) {
|
||||
// Apply direction
|
||||
DIR_WAIT_BEFORE();
|
||||
const uint8_t axis = rtg.rt_params.axis;
|
||||
const bool fwd = current_block->direction_bits[axis];
|
||||
switch (axis) {
|
||||
case X_AXIS: X_APPLY_DIR(fwd, false); break;
|
||||
case Y_AXIS: Y_APPLY_DIR(fwd, false); break;
|
||||
case Z_AXIS: Z_APPLY_DIR(fwd, false); break;
|
||||
}
|
||||
|
||||
step_event_count = current_block->step_event_count;
|
||||
step_events_completed = 0;
|
||||
interval = calc_multistep_timer_interval(current_block->initial_rate);
|
||||
}
|
||||
else
|
||||
rtg.abort();
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
void Stepper::resonance_pulse_phase_isr() {
|
||||
|
||||
// If there is no current block, do nothing
|
||||
if (!current_block || step_events_completed >= step_event_count) return;
|
||||
|
||||
// Count of pending loops and events for this iteration
|
||||
const uint32_t pending_events = step_event_count - step_events_completed;
|
||||
uint8_t events_to_do = _MIN(pending_events, steps_per_isr);
|
||||
|
||||
// Just update the value we will get at the end of the loop
|
||||
step_events_completed += events_to_do;
|
||||
|
||||
#define RESONANCE_STEP_SEQUENCE(A) do { \
|
||||
A##_APPLY_STEP(STEP_STATE_##A, false); \
|
||||
START_TIMED_PULSE(); \
|
||||
AWAIT_HIGH_PULSE(); \
|
||||
A##_APPLY_STEP(!STEP_STATE_##A, false); \
|
||||
} while(0)
|
||||
|
||||
USING_TIMED_PULSE();
|
||||
|
||||
const uint8_t axis = rtg.rt_params.axis;
|
||||
|
||||
switch (axis) {
|
||||
case X_AXIS:
|
||||
#if ISR_MULTI_STEPS
|
||||
RESONANCE_STEP_SEQUENCE(X);
|
||||
while (--events_to_do) {
|
||||
AWAIT_LOW_PULSE();
|
||||
RESONANCE_STEP_SEQUENCE(X);
|
||||
}
|
||||
#else
|
||||
do {
|
||||
RESONANCE_STEP_SEQUENCE(X);
|
||||
} while (--events_to_do);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case Y_AXIS:
|
||||
#if ISR_MULTI_STEPS
|
||||
RESONANCE_STEP_SEQUENCE(Y);
|
||||
while (--events_to_do) {
|
||||
AWAIT_LOW_PULSE();
|
||||
RESONANCE_STEP_SEQUENCE(Y);
|
||||
}
|
||||
#else
|
||||
do {
|
||||
RESONANCE_STEP_SEQUENCE(Y);
|
||||
} while (--events_to_do);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case Z_AXIS:
|
||||
#if ISR_MULTI_STEPS
|
||||
RESONANCE_STEP_SEQUENCE(Z);
|
||||
while (--events_to_do) {
|
||||
AWAIT_LOW_PULSE();
|
||||
RESONANCE_STEP_SEQUENCE(Z);
|
||||
}
|
||||
#else
|
||||
do {
|
||||
RESONANCE_STEP_SEQUENCE(Z);
|
||||
} while (--events_to_do);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
|
||||
void Stepper::enable_extruder(E_TERN_(const uint8_t eindex)) {
|
||||
|
|
@ -1646,6 +1761,19 @@ void Stepper::isr() {
|
|||
#if HAS_STANDARD_MOTION
|
||||
|
||||
if (!using_ftMotion) {
|
||||
#if ENABLED(RESONANCE_TEST)
|
||||
if (rtg.isActive()) {
|
||||
if (!nextMainISR) resonance_pulse_phase_isr();
|
||||
|
||||
hal.isr_on();
|
||||
if (!nextMainISR) nextMainISR = resonance_block_phase_isr();
|
||||
|
||||
interval = hal_timer_t(STEPPER_TIMER_RATE * 0.03); // Max wait of 30ms regardless of stepper timer frequency
|
||||
NOMORE(interval, nextMainISR); // Time until the next Pulse / Block phase
|
||||
nextMainISR -= interval;
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
|
||||
TERN_(HAS_ZV_SHAPING, shaping_isr()); // Do Shaper stepping, if needed
|
||||
|
||||
|
|
@ -1709,6 +1837,7 @@ void Stepper::isr() {
|
|||
TERN_(BABYSTEPPING, if (nextBabystepISR != BABYSTEP_NEVER) nextBabystepISR -= interval);
|
||||
|
||||
}
|
||||
TERN_(RESONANCE_TEST, })
|
||||
|
||||
#endif // HAS_STANDARD_MOTION
|
||||
|
||||
|
|
|
|||
|
|
@ -579,6 +579,11 @@ class Stepper {
|
|||
static hal_timer_t block_phase_isr();
|
||||
#endif
|
||||
|
||||
#if (ENABLED(RESONANCE_TEST) && HAS_STANDARD_MOTION)
|
||||
static void resonance_pulse_phase_isr();
|
||||
static hal_timer_t resonance_block_phase_isr();
|
||||
#endif
|
||||
|
||||
#if HAS_ZV_SHAPING
|
||||
static void shaping_isr();
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ opt_set MOTHERBOARD BOARD_I3DBEEZ9_V1 SERIAL_PORT -1 \
|
|||
EXTRUDERS 3 TEMP_SENSOR_1 1 TEMP_SENSOR_2 1 \
|
||||
E0_AUTO_FAN_PIN PC10 E1_AUTO_FAN_PIN PC11 E2_AUTO_FAN_PIN PC12 \
|
||||
X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2130
|
||||
opt_enable FT_MOTION FTM_SMOOTHING FTM_HOME_AND_PROBE FTM_RESONANCE_TEST FT_MOTION_MENU \
|
||||
opt_enable FT_MOTION FTM_SMOOTHING FTM_HOME_AND_PROBE RESONANCE_TEST FT_MOTION_MENU \
|
||||
REPRAP_DISCOUNT_SMART_CONTROLLER EMERGENCY_PARSER EEPROM_SETTINGS \
|
||||
BLTOUCH AUTO_BED_LEVELING_3POINT Z_SAFE_HOMING PINS_DEBUGGING
|
||||
opt_disable FTM_SHAPER_ZVDDD FTM_SHAPER_MZV
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ opt_set MOTHERBOARD BOARD_BTT_SKR_MINI_E3_V1_0 SERIAL_PORT 1 SERIAL_PORT_2 -1 \
|
|||
X_CURRENT_HOME X_CURRENT/2 Y_CURRENT_HOME Y_CURRENT/2 Z_CURRENT_HOME Y_CURRENT/2
|
||||
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 \
|
||||
FT_MOTION FT_MOTION_MENU 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 \
|
||||
PINS_DEBUGGING
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ PLATFORM_M997_SUPPORT = build_src_filter=+<src/gcode/control/M9
|
|||
HAS_TOOLCHANGE = build_src_filter=+<src/gcode/control/T.cpp>
|
||||
FT_MOTION = build_src_filter=+<src/module/ft_motion.cpp> +<src/module/ft_motion> -<src/module/ft_motion/smoothing.cpp> +<src/gcode/feature/ft_motion> -<src/gcode/feature/ft_motion/M495_M496.cpp> -<src/module/ft_motion/trajectory_poly6.cpp>
|
||||
FTM_SMOOTHING = build_src_filter=+<src/module/ft_motion/smoothing.cpp>
|
||||
FTM_RESONANCE_TEST = build_src_filter=+<src/gcode/feature/ft_motion/M495_M496.cpp>
|
||||
RESONANCE_TEST = build_src_filter=+<src/feature/resonance> +<src/gcode/feature/resonance>
|
||||
FTM_POLYS = build_src_filter=+<src/module/ft_motion/trajectory_poly6.cpp>
|
||||
HAS_LIN_ADVANCE_K = build_src_filter=+<src/gcode/feature/advance>
|
||||
PHOTO_GCODE = build_src_filter=+<src/gcode/feature/camera>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue