🐛⚡️ FT_MOTION improvements (#26074)
Co-Authored-By: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
parent
0ede16cd5a
commit
67d7562609
|
@ -1121,42 +1121,62 @@
|
||||||
#if ENABLED(FT_MOTION)
|
#if ENABLED(FT_MOTION)
|
||||||
#define FTM_DEFAULT_MODE ftMotionMode_DISABLED // Default mode of fixed time control. (Enums in ft_types.h)
|
#define FTM_DEFAULT_MODE ftMotionMode_DISABLED // Default mode of fixed time control. (Enums in ft_types.h)
|
||||||
#define FTM_DEFAULT_DYNFREQ_MODE dynFreqMode_DISABLED // Default mode of dynamic frequency calculation. (Enums in ft_types.h)
|
#define FTM_DEFAULT_DYNFREQ_MODE dynFreqMode_DISABLED // Default mode of dynamic frequency calculation. (Enums in ft_types.h)
|
||||||
#define FTM_SHAPING_DEFAULT_X_FREQ 37.0f // (Hz) Default peak frequency used by input shapers.
|
#define FTM_SHAPING_DEFAULT_X_FREQ 37.0f // (Hz) Default peak frequency used by input shapers
|
||||||
#define FTM_SHAPING_DEFAULT_Y_FREQ 37.0f // (Hz) Default peak frequency used by input shapers.
|
#define FTM_SHAPING_DEFAULT_Y_FREQ 37.0f // (Hz) Default peak frequency used by input shapers
|
||||||
#define FTM_LINEAR_ADV_DEFAULT_ENA false // Default linear advance enable (true) or disable (false).
|
#define FTM_LINEAR_ADV_DEFAULT_ENA false // Default linear advance enable (true) or disable (false)
|
||||||
#define FTM_LINEAR_ADV_DEFAULT_K 0.0f // Default linear advance gain.
|
#define FTM_LINEAR_ADV_DEFAULT_K 0.0f // Default linear advance gain
|
||||||
#define FTM_SHAPING_ZETA 0.1f // Zeta used by input shapers.
|
#define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis
|
||||||
#define FTM_SHAPING_V_TOL 0.05f // Vibration tolerance used by EI input shapers.
|
#define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis
|
||||||
|
|
||||||
|
#define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis
|
||||||
|
#define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis
|
||||||
|
|
||||||
|
//#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 parameters
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advanced configuration
|
* Advanced configuration
|
||||||
*/
|
*/
|
||||||
#define FTM_BATCH_SIZE 100 // Batch size for trajectory generation;
|
#define FTM_UNIFIED_BWS // DON'T DISABLE unless you use Ulendo FBS (not implemented)
|
||||||
#define FTM_WINDOW_SIZE 200 // Window size for trajectory generation.
|
#if ENABLED(FTM_UNIFIED_BWS)
|
||||||
#define FTM_FS 1000 // (Hz) Frequency for trajectory generation. (1 / FTM_TS)
|
#define FTM_BW_SIZE 100 // Unified Window and Batch size with a ratio of 2
|
||||||
#define FTM_TS 0.001f // (s) Time step for trajectory generation. (1 / FTM_FS)
|
#else
|
||||||
#define FTM_STEPPER_FS 20000 // (Hz) Frequency for stepper I/O update.
|
#define FTM_WINDOW_SIZE 200 // Custom Window size for trajectory generation needed by Ulendo FBS
|
||||||
#define FTM_MIN_TICKS ((STEPPER_TIMER_RATE) / (FTM_STEPPER_FS)) // Minimum stepper ticks between steps.
|
#define FTM_BATCH_SIZE 100 // Custom Batch size for trajectory generation needed by Ulendo FBS
|
||||||
#define FTM_MIN_SHAPE_FREQ 10 // Minimum shaping frequency.
|
#endif
|
||||||
#define FTM_ZMAX 100 // Maximum delays for shaping functions (even numbers only!).
|
|
||||||
// Calculate as:
|
|
||||||
// 1/2 * (FTM_FS / FTM_MIN_SHAPE_FREQ) for ZV.
|
|
||||||
// (FTM_FS / FTM_MIN_SHAPE_FREQ) for ZVD, MZV.
|
|
||||||
// 3/2 * (FTM_FS / FTM_MIN_SHAPE_FREQ) for 2HEI.
|
|
||||||
// 2 * (FTM_FS / FTM_MIN_SHAPE_FREQ) for 3HEI.
|
|
||||||
#define FTM_STEPS_PER_UNIT_TIME 20 // Interpolated stepper commands per unit time.
|
|
||||||
// Calculate as (FTM_STEPPER_FS / FTM_FS).
|
|
||||||
#define FTM_CTS_COMPARE_VAL 10 // Comparison value used in interpolation algorithm.
|
|
||||||
// Calculate as (FTM_STEPS_PER_UNIT_TIME / 2).
|
|
||||||
// These values may be configured to adjust duration of loop().
|
|
||||||
#define FTM_STEPS_PER_LOOP 60 // Number of stepper commands to generate each loop().
|
|
||||||
#define FTM_POINTS_PER_LOOP 100 // Number of trajectory points to generate each loop().
|
|
||||||
|
|
||||||
// This value may be configured to adjust duration to consume the command buffer.
|
#define FTM_FS 1000 // (Hz) Frequency for trajectory generation. (Reciprocal of FTM_TS)
|
||||||
// Try increasing this value if stepper motion is not smooth.
|
#define FTM_TS 0.001f // (s) Time step for trajectory generation. (Reciprocal of FTM_FS)
|
||||||
#define FTM_STEPPERCMD_BUFF_SIZE 1000 // Size of the stepper command buffers.
|
|
||||||
|
|
||||||
//#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 parameters.
|
// These values may be configured to adjust the duration of loop().
|
||||||
|
#define FTM_STEPS_PER_LOOP 60 // Number of stepper commands to generate each loop()
|
||||||
|
#define FTM_POINTS_PER_LOOP 100 // Number of trajectory points to generate each loop()
|
||||||
|
|
||||||
|
#if DISABLED(COREXY)
|
||||||
|
#define FTM_STEPPER_FS 20000 // (Hz) Frequency for stepper I/O update
|
||||||
|
|
||||||
|
// Use this to adjust the time required to consume the command buffer.
|
||||||
|
// Try increasing this value if stepper motion is choppy.
|
||||||
|
#define FTM_STEPPERCMD_BUFF_SIZE 3000 // Size of the stepper command buffers
|
||||||
|
// (FTM_STEPS_PER_LOOP * FTM_POINTS_PER_LOOP) is a good start
|
||||||
|
// If you run out of memory, fall back to 3000 and increase progressively
|
||||||
|
#else
|
||||||
|
// CoreXY motion needs a larger buffer size. These values are based on our testing.
|
||||||
|
#define FTM_STEPPER_FS 30000
|
||||||
|
#define FTM_STEPPERCMD_BUFF_SIZE 6000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FTM_STEPS_PER_UNIT_TIME (FTM_STEPPER_FS / FTM_FS) // Interpolated stepper commands per unit time
|
||||||
|
#define FTM_CTS_COMPARE_VAL (FTM_STEPS_PER_UNIT_TIME / 2) // Comparison value used in interpolation algorithm
|
||||||
|
#define FTM_MIN_TICKS ((STEPPER_TIMER_RATE) / (FTM_STEPPER_FS)) // Minimum stepper ticks between steps
|
||||||
|
|
||||||
|
#define FTM_MIN_SHAPE_FREQ 10 // Minimum shaping frequency
|
||||||
|
#define FTM_RATIO (FTM_FS / FTM_MIN_SHAPE_FREQ) // Factor for use in FTM_ZMAX. DON'T CHANGE.
|
||||||
|
#define FTM_ZMAX (FTM_RATIO * 2) // Maximum delays for shaping functions (even numbers only!)
|
||||||
|
// Calculate as:
|
||||||
|
// ZV : FTM_RATIO / 2
|
||||||
|
// ZVD, MZV : FTM_RATIO
|
||||||
|
// 2HEI : FTM_RATIO * 3 / 2
|
||||||
|
// 3HEI : FTM_RATIO * 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -57,9 +57,9 @@
|
||||||
* 41 - Counter-Clockwise M4
|
* 41 - Counter-Clockwise M4
|
||||||
* 50 - Clockwise M5
|
* 50 - Clockwise M5
|
||||||
* 51 - Counter-Clockwise M5
|
* 51 - Counter-Clockwise M5
|
||||||
*
|
**/
|
||||||
*/
|
|
||||||
void GcodeSuite::G35() {
|
void GcodeSuite::G35() {
|
||||||
|
|
||||||
DEBUG_SECTION(log_G35, "G35", DEBUGGING(LEVELING));
|
DEBUG_SECTION(log_G35, "G35", DEBUGGING(LEVELING));
|
||||||
|
|
||||||
if (DEBUGGING(LEVELING)) log_machine_info();
|
if (DEBUGGING(LEVELING)) log_machine_info();
|
||||||
|
|
|
@ -225,6 +225,7 @@ public:
|
||||||
* There's no extra effect if you have a fixed Z probe.
|
* There's no extra effect if you have a fixed Z probe.
|
||||||
*/
|
*/
|
||||||
G29_TYPE GcodeSuite::G29() {
|
G29_TYPE GcodeSuite::G29() {
|
||||||
|
|
||||||
DEBUG_SECTION(log_G29, "G29", DEBUGGING(LEVELING));
|
DEBUG_SECTION(log_G29, "G29", DEBUGGING(LEVELING));
|
||||||
|
|
||||||
// Leveling state is persistent when done manually with multiple G29 commands
|
// Leveling state is persistent when done manually with multiple G29 commands
|
||||||
|
|
|
@ -64,6 +64,7 @@ inline void echo_not_entered(const char c) { SERIAL_CHAR(c); SERIAL_ECHOLNPGM("
|
||||||
* S5 Reset and disable mesh
|
* S5 Reset and disable mesh
|
||||||
*/
|
*/
|
||||||
void GcodeSuite::G29() {
|
void GcodeSuite::G29() {
|
||||||
|
|
||||||
DEBUG_SECTION(log_G29, "G29", true);
|
DEBUG_SECTION(log_G29, "G29", true);
|
||||||
|
|
||||||
// G29 Q is also available if debugging
|
// G29 Q is also available if debugging
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
* R Flag to recalculate points based on current probe offsets
|
* R Flag to recalculate points based on current probe offsets
|
||||||
*/
|
*/
|
||||||
void GcodeSuite::G34() {
|
void GcodeSuite::G34() {
|
||||||
|
|
||||||
DEBUG_SECTION(log_G34, "G34", DEBUGGING(LEVELING));
|
DEBUG_SECTION(log_G34, "G34", DEBUGGING(LEVELING));
|
||||||
if (DEBUGGING(LEVELING)) log_machine_info();
|
if (DEBUGGING(LEVELING)) log_machine_info();
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include "../../gcode.h"
|
#include "../../gcode.h"
|
||||||
#include "../../../module/ft_motion.h"
|
#include "../../../module/ft_motion.h"
|
||||||
|
#include "../../../module/stepper.h"
|
||||||
|
|
||||||
void say_shaping() {
|
void say_shaping() {
|
||||||
// FT Enabled
|
// FT Enabled
|
||||||
|
@ -39,6 +40,8 @@ void say_shaping() {
|
||||||
default: break;
|
default: break;
|
||||||
case ftMotionMode_ZV: SERIAL_ECHOPGM("ZV"); break;
|
case ftMotionMode_ZV: SERIAL_ECHOPGM("ZV"); break;
|
||||||
case ftMotionMode_ZVD: SERIAL_ECHOPGM("ZVD"); break;
|
case ftMotionMode_ZVD: SERIAL_ECHOPGM("ZVD"); break;
|
||||||
|
case ftMotionMode_ZVDD: SERIAL_ECHOPGM("ZVDD"); break;
|
||||||
|
case ftMotionMode_ZVDDD: SERIAL_ECHOPGM("ZVDDD"); break;
|
||||||
case ftMotionMode_EI: SERIAL_ECHOPGM("EI"); break;
|
case ftMotionMode_EI: SERIAL_ECHOPGM("EI"); break;
|
||||||
case ftMotionMode_2HEI: SERIAL_ECHOPGM("2 Hump EI"); break;
|
case ftMotionMode_2HEI: SERIAL_ECHOPGM("2 Hump EI"); break;
|
||||||
case ftMotionMode_3HEI: SERIAL_ECHOPGM("3 Hump EI"); break;
|
case ftMotionMode_3HEI: SERIAL_ECHOPGM("3 Hump EI"); break;
|
||||||
|
@ -155,20 +158,19 @@ void GcodeSuite::M493() {
|
||||||
|
|
||||||
if (!parser.seen_any())
|
if (!parser.seen_any())
|
||||||
flag.report_h = true;
|
flag.report_h = true;
|
||||||
else
|
|
||||||
planner.synchronize();
|
|
||||||
|
|
||||||
// Parse 'S' mode parameter.
|
// Parse 'S' mode parameter.
|
||||||
if (parser.seenval('S')) {
|
if (parser.seenval('S')) {
|
||||||
const ftMotionMode_t oldmm = ftMotion.cfg.mode,
|
const ftMotionMode_t newmm = (ftMotionMode_t)parser.value_byte();
|
||||||
newmm = (ftMotionMode_t)parser.value_byte();
|
|
||||||
|
|
||||||
if (newmm != oldmm) {
|
if (newmm != ftMotion.cfg.mode) {
|
||||||
switch (newmm) {
|
switch (newmm) {
|
||||||
default: SERIAL_ECHOLNPGM("?Invalid control mode [S] value."); return;
|
default: SERIAL_ECHOLNPGM("?Invalid control mode [S] value."); return;
|
||||||
#if HAS_X_AXIS
|
#if HAS_X_AXIS
|
||||||
case ftMotionMode_ZV:
|
case ftMotionMode_ZV:
|
||||||
case ftMotionMode_ZVD:
|
case ftMotionMode_ZVD:
|
||||||
|
case ftMotionMode_ZVDD:
|
||||||
|
case ftMotionMode_ZVDDD:
|
||||||
case ftMotionMode_EI:
|
case ftMotionMode_EI:
|
||||||
case ftMotionMode_2HEI:
|
case ftMotionMode_2HEI:
|
||||||
case ftMotionMode_3HEI:
|
case ftMotionMode_3HEI:
|
||||||
|
@ -177,11 +179,10 @@ void GcodeSuite::M493() {
|
||||||
//case ftMotionMode_DISCTF:
|
//case ftMotionMode_DISCTF:
|
||||||
flag.update_n = flag.update_a = true;
|
flag.update_n = flag.update_a = true;
|
||||||
#endif
|
#endif
|
||||||
case ftMotionMode_DISABLED:
|
case ftMotionMode_DISABLED: flag.reset_ft = true;
|
||||||
case ftMotionMode_ENABLED:
|
case ftMotionMode_ENABLED:
|
||||||
ftMotion.cfg.mode = newmm;
|
ftMotion.cfg.mode = newmm;
|
||||||
flag.report_h = true;
|
flag.report_h = true;
|
||||||
if (oldmm == ftMotionMode_DISABLED) flag.reset_ft = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,6 +194,7 @@ void GcodeSuite::M493() {
|
||||||
if (parser.seen('P')) {
|
if (parser.seen('P')) {
|
||||||
const bool val = parser.value_bool();
|
const bool val = parser.value_bool();
|
||||||
ftMotion.cfg.linearAdvEna = val;
|
ftMotion.cfg.linearAdvEna = val;
|
||||||
|
flag.report_h = true;
|
||||||
SERIAL_ECHO_TERNARY(val, "Linear Advance ", "en", "dis", "abled.\n");
|
SERIAL_ECHO_TERNARY(val, "Linear Advance ", "en", "dis", "abled.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,22 +218,16 @@ void GcodeSuite::M493() {
|
||||||
if (ftMotion.cfg.modeHasShaper()) {
|
if (ftMotion.cfg.modeHasShaper()) {
|
||||||
const dynFreqMode_t val = dynFreqMode_t(parser.value_byte());
|
const dynFreqMode_t val = dynFreqMode_t(parser.value_byte());
|
||||||
switch (val) {
|
switch (val) {
|
||||||
|
#if HAS_DYNAMIC_FREQ_MM
|
||||||
|
case dynFreqMode_Z_BASED:
|
||||||
|
#endif
|
||||||
|
#if HAS_DYNAMIC_FREQ_G
|
||||||
|
case dynFreqMode_MASS_BASED:
|
||||||
|
#endif
|
||||||
case dynFreqMode_DISABLED:
|
case dynFreqMode_DISABLED:
|
||||||
ftMotion.cfg.dynFreqMode = val;
|
ftMotion.cfg.dynFreqMode = val;
|
||||||
flag.report_h = true;
|
flag.report_h = true;
|
||||||
break;
|
break;
|
||||||
#if HAS_DYNAMIC_FREQ_MM
|
|
||||||
case dynFreqMode_Z_BASED:
|
|
||||||
ftMotion.cfg.dynFreqMode = val;
|
|
||||||
flag.report_h = true;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
#if HAS_DYNAMIC_FREQ_G
|
|
||||||
case dynFreqMode_MASS_BASED:
|
|
||||||
ftMotion.cfg.dynFreqMode = val;
|
|
||||||
flag.report_h = true;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
SERIAL_ECHOLNPGM("?Invalid Dynamic Frequency Mode [D] value.");
|
SERIAL_ECHOLNPGM("?Invalid Dynamic Frequency Mode [D] value.");
|
||||||
break;
|
break;
|
||||||
|
@ -279,6 +275,36 @@ void GcodeSuite::M493() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Parse zeta parameter (X axis).
|
||||||
|
if (parser.seenval('I')) {
|
||||||
|
const float val = parser.value_float();
|
||||||
|
if (ftMotion.cfg.modeHasShaper()) {
|
||||||
|
if (WITHIN(val, 0.01f, 1.0f)) {
|
||||||
|
ftMotion.cfg.zeta[0] = val;
|
||||||
|
flag.update_n = flag.update_a = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SERIAL_ECHOLNPGM("Invalid X zeta [", AS_CHAR('I'), "] value."); // Zeta out of range.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SERIAL_ECHOLNPGM("Wrong mode for zeta parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse vtol parameter (X axis).
|
||||||
|
if (parser.seenval('Q')) {
|
||||||
|
const float val = parser.value_float();
|
||||||
|
if (ftMotion.cfg.modeHasShaper() && IS_EI_MODE(ftMotion.cfg.mode)) {
|
||||||
|
if (WITHIN(val, 0.00f, 1.0f)) {
|
||||||
|
ftMotion.cfg.vtol[0] = val;
|
||||||
|
flag.update_a = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SERIAL_ECHOLNPGM("Invalid X vtol [", AS_CHAR('Q'), "] value."); // VTol out of range.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SERIAL_ECHOLNPGM("Wrong mode for vtol parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAS_X_AXIS
|
#endif // HAS_X_AXIS
|
||||||
|
|
||||||
#if HAS_Y_AXIS
|
#if HAS_Y_AXIS
|
||||||
|
@ -310,15 +336,50 @@ void GcodeSuite::M493() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Parse zeta parameter (Y axis).
|
||||||
|
if (parser.seenval('J')) {
|
||||||
|
const float val = parser.value_float();
|
||||||
|
if (ftMotion.cfg.modeHasShaper()) {
|
||||||
|
if (WITHIN(val, 0.01f, 1.0f)) {
|
||||||
|
ftMotion.cfg.zeta[1] = val;
|
||||||
|
flag.update_n = flag.update_a = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SERIAL_ECHOLNPGM("Invalid Y zeta [", AS_CHAR('J'), "] value."); // Zeta Out of range
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SERIAL_ECHOLNPGM("Wrong mode for zeta parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse vtol parameter (Y axis).
|
||||||
|
if (parser.seenval('R')) {
|
||||||
|
const float val = parser.value_float();
|
||||||
|
if (ftMotion.cfg.modeHasShaper() && IS_EI_MODE(ftMotion.cfg.mode)) {
|
||||||
|
if (WITHIN(val, 0.00f, 1.0f)) {
|
||||||
|
ftMotion.cfg.vtol[1] = val;
|
||||||
|
flag.update_a = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SERIAL_ECHOLNPGM("Invalid Y vtol [", AS_CHAR('R'), "] value."); // VTol out of range.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SERIAL_ECHOLNPGM("Wrong mode for vtol parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAS_Y_AXIS
|
#endif // HAS_Y_AXIS
|
||||||
|
|
||||||
#if HAS_X_AXIS
|
planner.synchronize();
|
||||||
if (flag.update_n) ftMotion.refreshShapingN();
|
|
||||||
if (flag.update_a) ftMotion.updateShapingA();
|
|
||||||
#endif
|
|
||||||
if (flag.reset_ft) ftMotion.reset();
|
|
||||||
if (flag.report_h) say_shaping();
|
|
||||||
|
|
||||||
|
if (flag.update_n) ftMotion.refreshShapingN();
|
||||||
|
|
||||||
|
if (flag.update_a) ftMotion.updateShapingA();
|
||||||
|
|
||||||
|
if (flag.reset_ft) {
|
||||||
|
stepper.ftMotion_syncPosition();
|
||||||
|
ftMotion.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag.report_h) say_shaping();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FT_MOTION
|
#endif // FT_MOTION
|
||||||
|
|
|
@ -105,6 +105,7 @@ inline bool G38_run_probe() {
|
||||||
* G38.5 - Probe away from workpiece, stop on contact break
|
* G38.5 - Probe away from workpiece, stop on contact break
|
||||||
*/
|
*/
|
||||||
void GcodeSuite::G38(const int8_t subcode) {
|
void GcodeSuite::G38(const int8_t subcode) {
|
||||||
|
|
||||||
// Get X Y Z E F
|
// Get X Y Z E F
|
||||||
get_destination_from_command();
|
get_destination_from_command();
|
||||||
|
|
||||||
|
|
|
@ -1144,7 +1144,7 @@
|
||||||
#elif HAS_DRIVER(A4988)
|
#elif HAS_DRIVER(A4988)
|
||||||
#define MINIMUM_STEPPER_POST_DIR_DELAY 200
|
#define MINIMUM_STEPPER_POST_DIR_DELAY 200
|
||||||
#elif HAS_TRINAMIC_CONFIG || HAS_TRINAMIC_STANDALONE
|
#elif HAS_TRINAMIC_CONFIG || HAS_TRINAMIC_STANDALONE
|
||||||
#define MINIMUM_STEPPER_POST_DIR_DELAY 60
|
#define MINIMUM_STEPPER_POST_DIR_DELAY 70
|
||||||
#else
|
#else
|
||||||
#define MINIMUM_STEPPER_POST_DIR_DELAY 0 // Expect at least 10µS since one Stepper ISR must transpire
|
#define MINIMUM_STEPPER_POST_DIR_DELAY 0 // Expect at least 10µS since one Stepper ISR must transpire
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -802,6 +802,8 @@ namespace LanguageNarrow_en {
|
||||||
LSTR MSG_FTM_MODE = _UxGT("Motion Mode:");
|
LSTR MSG_FTM_MODE = _UxGT("Motion Mode:");
|
||||||
LSTR MSG_FTM_ZV = _UxGT("ZV");
|
LSTR MSG_FTM_ZV = _UxGT("ZV");
|
||||||
LSTR MSG_FTM_ZVD = _UxGT("ZVD");
|
LSTR MSG_FTM_ZVD = _UxGT("ZVD");
|
||||||
|
LSTR MSG_FTM_ZVDD = _UxGT("ZVDD");
|
||||||
|
LSTR MSG_FTM_ZVDDD = _UxGT("ZVDDD");
|
||||||
LSTR MSG_FTM_EI = _UxGT("EI");
|
LSTR MSG_FTM_EI = _UxGT("EI");
|
||||||
LSTR MSG_FTM_2HEI = _UxGT("2HEI");
|
LSTR MSG_FTM_2HEI = _UxGT("2HEI");
|
||||||
LSTR MSG_FTM_3HEI = _UxGT("3HEI");
|
LSTR MSG_FTM_3HEI = _UxGT("3HEI");
|
||||||
|
@ -813,8 +815,8 @@ namespace LanguageNarrow_en {
|
||||||
LSTR MSG_FTM_MASS_BASED = _UxGT("Mass-based");
|
LSTR MSG_FTM_MASS_BASED = _UxGT("Mass-based");
|
||||||
LSTR MSG_FTM_BASE_FREQ_N = _UxGT("@ Base Freq.");
|
LSTR MSG_FTM_BASE_FREQ_N = _UxGT("@ Base Freq.");
|
||||||
LSTR MSG_FTM_DFREQ_K_N = _UxGT("@ Dyn. Freq.");
|
LSTR MSG_FTM_DFREQ_K_N = _UxGT("@ Dyn. Freq.");
|
||||||
LSTR MSG_FTM_ZETA = _UxGT("Damping");
|
LSTR MSG_FTM_ZETA_N = _UxGT("@ Damping");
|
||||||
LSTR MSG_FTM_VTOL = _UxGT("Vib. Level");
|
LSTR MSG_FTM_VTOL_N = _UxGT("@ Vib. Level");
|
||||||
|
|
||||||
LSTR MSG_LEVEL_X_AXIS = _UxGT("Level X Axis");
|
LSTR MSG_LEVEL_X_AXIS = _UxGT("Level X Axis");
|
||||||
LSTR MSG_AUTO_CALIBRATE = _UxGT("Auto Calibrate");
|
LSTR MSG_AUTO_CALIBRATE = _UxGT("Auto Calibrate");
|
||||||
|
|
|
@ -342,6 +342,8 @@ void menu_move() {
|
||||||
#if HAS_X_AXIS
|
#if HAS_X_AXIS
|
||||||
if (mode != ftMotionMode_ZV) ACTION_ITEM(MSG_FTM_ZV, []{ ftm_menu_setShaping(ftMotionMode_ZV); });
|
if (mode != ftMotionMode_ZV) ACTION_ITEM(MSG_FTM_ZV, []{ ftm_menu_setShaping(ftMotionMode_ZV); });
|
||||||
if (mode != ftMotionMode_ZVD) ACTION_ITEM(MSG_FTM_ZVD, []{ ftm_menu_setShaping(ftMotionMode_ZVD); });
|
if (mode != ftMotionMode_ZVD) ACTION_ITEM(MSG_FTM_ZVD, []{ ftm_menu_setShaping(ftMotionMode_ZVD); });
|
||||||
|
if (mode != ftMotionMode_ZVDD) ACTION_ITEM(MSG_FTM_ZVDD, []{ ftm_menu_setShaping(ftMotionMode_ZVDD); });
|
||||||
|
if (mode != ftMotionMode_ZVDDD) ACTION_ITEM(MSG_FTM_ZVDDD,[]{ ftm_menu_setShaping(ftMotionMode_ZVDDD); });
|
||||||
if (mode != ftMotionMode_EI) ACTION_ITEM(MSG_FTM_EI, []{ ftm_menu_setShaping(ftMotionMode_EI); });
|
if (mode != ftMotionMode_EI) ACTION_ITEM(MSG_FTM_EI, []{ ftm_menu_setShaping(ftMotionMode_EI); });
|
||||||
if (mode != ftMotionMode_2HEI) ACTION_ITEM(MSG_FTM_2HEI, []{ ftm_menu_setShaping(ftMotionMode_2HEI); });
|
if (mode != ftMotionMode_2HEI) ACTION_ITEM(MSG_FTM_2HEI, []{ ftm_menu_setShaping(ftMotionMode_2HEI); });
|
||||||
if (mode != ftMotionMode_3HEI) ACTION_ITEM(MSG_FTM_3HEI, []{ ftm_menu_setShaping(ftMotionMode_3HEI); });
|
if (mode != ftMotionMode_3HEI) ACTION_ITEM(MSG_FTM_3HEI, []{ ftm_menu_setShaping(ftMotionMode_3HEI); });
|
||||||
|
@ -384,6 +386,8 @@ void menu_move() {
|
||||||
case ftMotionMode_ENABLED: ftmode = GET_TEXT_F(MSG_LCD_ON); break;
|
case ftMotionMode_ENABLED: ftmode = GET_TEXT_F(MSG_LCD_ON); break;
|
||||||
case ftMotionMode_ZV: ftmode = GET_TEXT_F(MSG_FTM_ZV); break;
|
case ftMotionMode_ZV: ftmode = GET_TEXT_F(MSG_FTM_ZV); break;
|
||||||
case ftMotionMode_ZVD: ftmode = GET_TEXT_F(MSG_FTM_ZVD); break;
|
case ftMotionMode_ZVD: ftmode = GET_TEXT_F(MSG_FTM_ZVD); break;
|
||||||
|
case ftMotionMode_ZVDD: ftmode = GET_TEXT_F(MSG_FTM_ZVDD); break;
|
||||||
|
case ftMotionMode_ZVDDD: ftmode = GET_TEXT_F(MSG_FTM_ZVDDD);break;
|
||||||
case ftMotionMode_EI: ftmode = GET_TEXT_F(MSG_FTM_EI); break;
|
case ftMotionMode_EI: ftmode = GET_TEXT_F(MSG_FTM_EI); break;
|
||||||
case ftMotionMode_2HEI: ftmode = GET_TEXT_F(MSG_FTM_2HEI); break;
|
case ftMotionMode_2HEI: ftmode = GET_TEXT_F(MSG_FTM_2HEI); break;
|
||||||
case ftMotionMode_3HEI: ftmode = GET_TEXT_F(MSG_FTM_3HEI); break;
|
case ftMotionMode_3HEI: ftmode = GET_TEXT_F(MSG_FTM_3HEI); break;
|
||||||
|
@ -416,10 +420,21 @@ void menu_move() {
|
||||||
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_BASE_FREQ_N, &c.baseFreq[Y_AXIS], FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.refreshShapingN);
|
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_BASE_FREQ_N, &c.baseFreq[Y_AXIS], FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.refreshShapingN);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
EDIT_ITEM_FAST(float42_52, MSG_FTM_ZETA, &c.zeta, 0.0f, 1.0f, ftMotion.refreshShapingN);
|
#if HAS_X_AXIS
|
||||||
|
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_ZETA_N, &c.zeta[0], 0.0f, 1.0f, ftMotion.refreshShapingN);
|
||||||
|
#endif
|
||||||
|
#if HAS_Y_AXIS
|
||||||
|
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_ZETA_N, &c.zeta[1], 0.0f, 1.0f, ftMotion.refreshShapingN);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (WITHIN(c.mode, ftMotionMode_EI, ftMotionMode_3HEI))
|
if (IS_EI_MODE(c.mode)) {
|
||||||
EDIT_ITEM_FAST(float42_52, MSG_FTM_VTOL, &c.vtol, 0.0f, 1.0f, ftMotion.refreshShapingN);
|
#if HAS_X_AXIS
|
||||||
|
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_VTOL_N, &c.vtol[0], 0.0f, 1.0f, ftMotion.refreshShapingN);
|
||||||
|
#endif
|
||||||
|
#if HAS_Y_AXIS
|
||||||
|
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_VTOL_N, &c.vtol[1], 0.0f, 1.0f, ftMotion.refreshShapingN);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#if HAS_DYNAMIC_FREQ
|
#if HAS_DYNAMIC_FREQ
|
||||||
SUBMENU(MSG_FTM_DYN_MODE, menu_ftm_dyn_mode);
|
SUBMENU(MSG_FTM_DYN_MODE, menu_ftm_dyn_mode);
|
||||||
|
@ -437,7 +452,7 @@ void menu_move() {
|
||||||
|
|
||||||
#if HAS_EXTRUDERS
|
#if HAS_EXTRUDERS
|
||||||
EDIT_ITEM(bool, MSG_LINEAR_ADVANCE, &c.linearAdvEna);
|
EDIT_ITEM(bool, MSG_LINEAR_ADVANCE, &c.linearAdvEna);
|
||||||
EDIT_ITEM(float42_52, MSG_ADVANCE_K, &c.linearAdvK, 0, 10);
|
if (c.linearAdvEna) EDIT_ITEM(float42_52, MSG_ADVANCE_K, &c.linearAdvK, 0, 10);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
END_MENU();
|
END_MENU();
|
||||||
|
|
|
@ -32,6 +32,8 @@ FTMotion ftMotion;
|
||||||
#if !HAS_X_AXIS
|
#if !HAS_X_AXIS
|
||||||
static_assert(FTM_DEFAULT_MODE == ftMotionMode_ZV, "ftMotionMode_ZV requires at least one linear axis.");
|
static_assert(FTM_DEFAULT_MODE == ftMotionMode_ZV, "ftMotionMode_ZV requires at least one linear axis.");
|
||||||
static_assert(FTM_DEFAULT_MODE == ftMotionMode_ZVD, "ftMotionMode_ZVD requires at least one linear axis.");
|
static_assert(FTM_DEFAULT_MODE == ftMotionMode_ZVD, "ftMotionMode_ZVD requires at least one linear axis.");
|
||||||
|
static_assert(FTM_DEFAULT_MODE == ftMotionMode_ZVDD, "ftMotionMode_ZVD requires at least one linear axis.");
|
||||||
|
static_assert(FTM_DEFAULT_MODE == ftMotionMode_ZVDDD, "ftMotionMode_ZVD requires at least one linear axis.");
|
||||||
static_assert(FTM_DEFAULT_MODE == ftMotionMode_EI, "ftMotionMode_EI requires at least one linear axis.");
|
static_assert(FTM_DEFAULT_MODE == ftMotionMode_EI, "ftMotionMode_EI requires at least one linear axis.");
|
||||||
static_assert(FTM_DEFAULT_MODE == ftMotionMode_2HEI, "ftMotionMode_2HEI requires at least one linear axis.");
|
static_assert(FTM_DEFAULT_MODE == ftMotionMode_2HEI, "ftMotionMode_2HEI requires at least one linear axis.");
|
||||||
static_assert(FTM_DEFAULT_MODE == ftMotionMode_3HEI, "ftMotionMode_3HEI requires at least one linear axis.");
|
static_assert(FTM_DEFAULT_MODE == ftMotionMode_3HEI, "ftMotionMode_3HEI requires at least one linear axis.");
|
||||||
|
@ -52,9 +54,7 @@ FTMotion ftMotion;
|
||||||
|
|
||||||
ft_config_t FTMotion::cfg;
|
ft_config_t FTMotion::cfg;
|
||||||
bool FTMotion::busy; // = false
|
bool FTMotion::busy; // = false
|
||||||
ft_command_t FTMotion::stepperCmdBuff[FTM_STEPPERCMD_BUFF_SIZE] = {0U}; // Buffer of stepper commands.
|
ft_command_t FTMotion::stepperCmdBuff[FTM_STEPPERCMD_BUFF_SIZE] = {0U}; // Stepper commands buffer.
|
||||||
hal_timer_t FTMotion::stepperCmdBuff_StepRelativeTi[FTM_STEPPERCMD_BUFF_SIZE] = {0U}; // Buffer of the stepper command timing.
|
|
||||||
uint8_t FTMotion::stepperCmdBuff_ApplyDir[FTM_STEPPERCMD_DIR_SIZE] = {0U}; // Buffer of whether DIR needs to be updated.
|
|
||||||
uint32_t FTMotion::stepperCmdBuff_produceIdx = 0, // Index of next stepper command write to the buffer.
|
uint32_t FTMotion::stepperCmdBuff_produceIdx = 0, // Index of next stepper command write to the buffer.
|
||||||
FTMotion::stepperCmdBuff_consumeIdx = 0; // Index of next stepper command read from the buffer.
|
FTMotion::stepperCmdBuff_consumeIdx = 0; // Index of next stepper command read from the buffer.
|
||||||
|
|
||||||
|
@ -62,10 +62,10 @@ bool FTMotion::sts_stepperBusy = false; // The stepper buffer has items
|
||||||
|
|
||||||
// Private variables.
|
// Private variables.
|
||||||
// NOTE: These are sized for Ulendo FBS use.
|
// NOTE: These are sized for Ulendo FBS use.
|
||||||
xyze_trajectory_t FTMotion::traj; // = {0.0f} Storage for fixed-time-based trajectory.
|
xyze_trajectory_t FTMotion::traj; // = {0.0f} Storage for fixed-time-based trajectory.
|
||||||
xyze_trajectoryMod_t FTMotion::trajMod; // = {0.0f} Storage for modified fixed-time-based trajectory.
|
xyze_trajectoryMod_t FTMotion::trajMod; // = {0.0f} Storage for modified fixed-time-based trajectory.
|
||||||
|
xyze_trajectoryWin_t FTMotion::trajWin; // = {0.0f} Storage for fixed time trajectory window.
|
||||||
|
|
||||||
block_t* FTMotion::current_block_cpy = nullptr; // Pointer to current block being processed.
|
|
||||||
bool FTMotion::blockProcRdy = false, // Indicates a block is ready to be processed.
|
bool FTMotion::blockProcRdy = false, // Indicates a block is ready to be processed.
|
||||||
FTMotion::blockProcRdy_z1 = false, // Storage for the previous indicator.
|
FTMotion::blockProcRdy_z1 = false, // Storage for the previous indicator.
|
||||||
FTMotion::blockProcDn = false; // Indicates current block is done being processed.
|
FTMotion::blockProcDn = false; // Indicates current block is done being processed.
|
||||||
|
@ -76,7 +76,7 @@ bool FTMotion::batchRdy = false; // Indicates a batch of the fixe
|
||||||
bool FTMotion::batchRdyForInterp = false; // Indicates the batch is done being post processed,
|
bool FTMotion::batchRdyForInterp = false; // Indicates the batch is done being post processed,
|
||||||
// if applicable, and is ready to be converted to step commands.
|
// if applicable, and is ready to be converted to step commands.
|
||||||
bool FTMotion::runoutEna = false; // True if runout of the block hasn't been done and is allowed.
|
bool FTMotion::runoutEna = false; // True if runout of the block hasn't been done and is allowed.
|
||||||
bool FTMotion::runout = false; // Indicates if runout is in progress.
|
bool FTMotion::blockDataIsRunout = false; // Indicates the last loaded block variables are for a runout.
|
||||||
|
|
||||||
// Trapezoid data variables.
|
// Trapezoid data variables.
|
||||||
xyze_pos_t FTMotion::startPosn, // (mm) Start position of block
|
xyze_pos_t FTMotion::startPosn, // (mm) Start position of block
|
||||||
|
@ -98,15 +98,13 @@ uint32_t FTMotion::max_intervals; // Total number of data points t
|
||||||
// Make vector variables.
|
// Make vector variables.
|
||||||
uint32_t FTMotion::makeVector_idx = 0, // Index of fixed time trajectory generation of the overall block.
|
uint32_t FTMotion::makeVector_idx = 0, // Index of fixed time trajectory generation of the overall block.
|
||||||
FTMotion::makeVector_idx_z1 = 0, // Storage for the previously calculated index above.
|
FTMotion::makeVector_idx_z1 = 0, // Storage for the previously calculated index above.
|
||||||
FTMotion::makeVector_batchIdx = FTM_BATCH_SIZE; // Index of fixed time trajectory generation within the batch.
|
FTMotion::makeVector_batchIdx = 0; // Index of fixed time trajectory generation within the batch.
|
||||||
|
|
||||||
// Interpolation variables.
|
// Interpolation variables.
|
||||||
xyze_long_t FTMotion::steps = { 0 }; // Step count accumulator.
|
xyze_long_t FTMotion::steps = { 0 }; // Step count accumulator.
|
||||||
xyze_stepDir_t FTMotion::dirState = LOGICAL_AXIS_ARRAY_1(stepDirState_NOT_SET); // Memory of the currently set step direction of the axis.
|
|
||||||
|
|
||||||
uint32_t FTMotion::interpIdx = 0, // Index of current data point being interpolated.
|
uint32_t FTMotion::interpIdx = 0, // Index of current data point being interpolated.
|
||||||
FTMotion::interpIdx_z1 = 0; // Storage for the previously calculated index above.
|
FTMotion::interpIdx_z1 = 0; // Storage for the previously calculated index above.
|
||||||
hal_timer_t FTMotion::nextStepTicks = FTM_MIN_TICKS; // Accumulator for the next step time (in ticks).
|
|
||||||
|
|
||||||
// Shaping variables.
|
// Shaping variables.
|
||||||
#if HAS_X_AXIS
|
#if HAS_X_AXIS
|
||||||
|
@ -125,8 +123,6 @@ hal_timer_t FTMotion::nextStepTicks = FTM_MIN_TICKS; // Accumulator for the nex
|
||||||
float FTMotion::e_advanced_z1 = 0.0f; // (ms) Unit delay of advanced extruder position.
|
float FTMotion::e_advanced_z1 = 0.0f; // (ms) Unit delay of advanced extruder position.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr uint32_t last_batchIdx = (FTM_WINDOW_SIZE) - (FTM_BATCH_SIZE);
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------//
|
//-----------------------------------------------------------------//
|
||||||
// Function definitions.
|
// Function definitions.
|
||||||
//-----------------------------------------------------------------//
|
//-----------------------------------------------------------------//
|
||||||
|
@ -134,8 +130,7 @@ constexpr uint32_t last_batchIdx = (FTM_WINDOW_SIZE) - (FTM_BATCH_SIZE);
|
||||||
// Public functions.
|
// Public functions.
|
||||||
|
|
||||||
// Sets controller states to begin processing a block.
|
// Sets controller states to begin processing a block.
|
||||||
void FTMotion::startBlockProc(block_t * const current_block) {
|
void FTMotion::startBlockProc() {
|
||||||
current_block_cpy = current_block;
|
|
||||||
blockProcRdy = true;
|
blockProcRdy = true;
|
||||||
blockProcDn = false;
|
blockProcDn = false;
|
||||||
runoutEna = true;
|
runoutEna = true;
|
||||||
|
@ -144,30 +139,16 @@ void FTMotion::startBlockProc(block_t * const current_block) {
|
||||||
// Move any free data points to the stepper buffer even if a full batch isn't ready.
|
// Move any free data points to the stepper buffer even if a full batch isn't ready.
|
||||||
void FTMotion::runoutBlock() {
|
void FTMotion::runoutBlock() {
|
||||||
|
|
||||||
if (runoutEna && !batchRdy) { // If the window is full already (block intervals was a multiple of
|
if (!runoutEna) return;
|
||||||
// the batch size), or runout is not enabled, no runout is needed.
|
|
||||||
// Fill out the trajectory window with the last position calculated.
|
|
||||||
if (makeVector_batchIdx > last_batchIdx)
|
|
||||||
for (uint32_t i = makeVector_batchIdx; i < (FTM_WINDOW_SIZE); i++) {
|
|
||||||
LOGICAL_AXIS_CODE(
|
|
||||||
traj.e[i] = traj.e[makeVector_batchIdx - 1],
|
|
||||||
traj.x[i] = traj.x[makeVector_batchIdx - 1],
|
|
||||||
traj.y[i] = traj.y[makeVector_batchIdx - 1],
|
|
||||||
traj.z[i] = traj.z[makeVector_batchIdx - 1],
|
|
||||||
traj.i[i] = traj.i[makeVector_batchIdx - 1],
|
|
||||||
traj.j[i] = traj.j[makeVector_batchIdx - 1],
|
|
||||||
traj.k[i] = traj.k[makeVector_batchIdx - 1],
|
|
||||||
traj.u[i] = traj.u[makeVector_batchIdx - 1],
|
|
||||||
traj.v[i] = traj.v[makeVector_batchIdx - 1],
|
|
||||||
traj.w[i] = traj.w[makeVector_batchIdx - 1]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
makeVector_batchIdx = last_batchIdx;
|
startPosn = endPosn_prevBlock;
|
||||||
batchRdy = true;
|
ratio.reset();
|
||||||
runout = true;
|
|
||||||
}
|
max_intervals = cfg.modeHasShaper() ? shaper_intervals : 0;
|
||||||
runoutEna = false;
|
if (max_intervals <= TERN(FTM_UNIFIED_BWS, FTM_BW_SIZE, min_max_intervals - (FTM_BATCH_SIZE))) max_intervals = min_max_intervals;
|
||||||
|
max_intervals += TERN(FTM_UNIFIED_BWS, FTM_BW_SIZE, FTM_WINDOW_SIZE) - makeVector_batchIdx;
|
||||||
|
blockProcRdy = blockDataIsRunout = true;
|
||||||
|
runoutEna = blockProcDn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Controller main, to be invoked from non-isr task.
|
// Controller main, to be invoked from non-isr task.
|
||||||
|
@ -184,81 +165,63 @@ void FTMotion::loop() {
|
||||||
if (sts_stepperBusy) return; // Wait until motion buffers are emptied
|
if (sts_stepperBusy) return; // Wait until motion buffers are emptied
|
||||||
reset();
|
reset();
|
||||||
blockProcDn = true; // Set queueing to look for next block.
|
blockProcDn = true; // Set queueing to look for next block.
|
||||||
runoutEna = false; // Disabling running out this block, since we want to halt the motion.
|
|
||||||
stepper.abort_current_block = false; // Abort finished.
|
stepper.abort_current_block = false; // Abort finished.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Planner processing and block conversion.
|
// Planner processing and block conversion.
|
||||||
if (!blockProcRdy && !runout) stepper.ftMotion_BlockQueueUpdate();
|
if (!blockProcRdy) stepper.ftMotion_blockQueueUpdate();
|
||||||
|
|
||||||
if (blockProcRdy) {
|
if (blockProcRdy) {
|
||||||
if (!blockProcRdy_z1) loadBlockData(current_block_cpy); // One-shot.
|
if (!blockProcRdy_z1) { // One-shot.
|
||||||
|
if (!blockDataIsRunout) loadBlockData(stepper.current_block);
|
||||||
|
else blockDataIsRunout = false;
|
||||||
|
}
|
||||||
while (!blockProcDn && !batchRdy && (makeVector_idx - makeVector_idx_z1 < (FTM_POINTS_PER_LOOP)))
|
while (!blockProcDn && !batchRdy && (makeVector_idx - makeVector_idx_z1 < (FTM_POINTS_PER_LOOP)))
|
||||||
makeVector();
|
makeVector();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runout && !batchRdy) { // The lower half of the window has been runout.
|
|
||||||
// Runout the upper half of the window: the upper half has been shifted into the lower
|
|
||||||
// half. Fill out the upper half so another batch can be processed.
|
|
||||||
for (uint32_t i = last_batchIdx; i < (FTM_WINDOW_SIZE) - 1; i++) {
|
|
||||||
LOGICAL_AXIS_CODE(
|
|
||||||
traj.e[i] = traj.e[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.x[i] = traj.x[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.y[i] = traj.y[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.z[i] = traj.z[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.i[i] = traj.i[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.j[i] = traj.j[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.k[i] = traj.k[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.u[i] = traj.u[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.v[i] = traj.v[(FTM_WINDOW_SIZE) - 1],
|
|
||||||
traj.w[i] = traj.w[(FTM_WINDOW_SIZE) - 1]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
batchRdy = true;
|
|
||||||
runout = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FBS / post processing.
|
// FBS / post processing.
|
||||||
if (batchRdy && !batchRdyForInterp) {
|
if (batchRdy && !batchRdyForInterp) {
|
||||||
|
|
||||||
// Call Ulendo FBS here.
|
// Call Ulendo FBS here.
|
||||||
|
|
||||||
// Copy the uncompensated vectors. (XY done, other axes uncompensated)
|
#if ENABLED(FTM_UNIFIED_BWS)
|
||||||
LOGICAL_AXIS_CODE(
|
trajMod = traj; // Copy the uncompensated vectors.
|
||||||
memcpy(trajMod.e, &traj.e[FTM_BATCH_SIZE], sizeof(trajMod.e)),
|
traj = trajWin; // Move the window to traj
|
||||||
memcpy(trajMod.x, &traj.x[FTM_BATCH_SIZE], sizeof(trajMod.x)),
|
#else
|
||||||
memcpy(trajMod.y, &traj.y[FTM_BATCH_SIZE], sizeof(trajMod.y)),
|
// Copy the uncompensated vectors.
|
||||||
memcpy(trajMod.z, &traj.z[FTM_BATCH_SIZE], sizeof(trajMod.z)),
|
#define TCOPY(A) memcpy(trajMod.A, traj.A, sizeof(trajMod.A))
|
||||||
memcpy(trajMod.i, &traj.i[FTM_BATCH_SIZE], sizeof(trajMod.i)),
|
LOGICAL_AXIS_CODE(
|
||||||
memcpy(trajMod.j, &traj.j[FTM_BATCH_SIZE], sizeof(trajMod.j)),
|
TCOPY(e),
|
||||||
memcpy(trajMod.k, &traj.k[FTM_BATCH_SIZE], sizeof(trajMod.k)),
|
TCOPY(x), TCOPY(y), TCOPY(z),
|
||||||
memcpy(trajMod.u, &traj.u[FTM_BATCH_SIZE], sizeof(trajMod.u)),
|
TCOPY(i), TCOPY(j), TCOPY(k),
|
||||||
memcpy(trajMod.v, &traj.v[FTM_BATCH_SIZE], sizeof(trajMod.v)),
|
TCOPY(u), TCOPY(v), TCOPY(w)
|
||||||
memcpy(trajMod.w, &traj.w[FTM_BATCH_SIZE], sizeof(trajMod.w))
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// Shift the time series back in the window for (shaped) X and Y
|
// Shift the time series back in the window
|
||||||
TERN_(HAS_X_AXIS, memcpy(traj.x, &traj.x[FTM_BATCH_SIZE], sizeof(traj.x) / 2));
|
#define TSHIFT(A) memcpy(traj.A, trajWin.A, sizeof(trajWin.A))
|
||||||
TERN_(HAS_Y_AXIS, memcpy(traj.y, &traj.y[FTM_BATCH_SIZE], sizeof(traj.y) / 2));
|
LOGICAL_AXIS_CODE(
|
||||||
|
TSHIFT(e),
|
||||||
// Z...W and E Disabled! Uncompensated so the lower half is not used.
|
TSHIFT(x), TSHIFT(y), TSHIFT(z),
|
||||||
//TERN_(HAS_Z_AXIS, memcpy(&traj.z[0], &traj.z[FTM_BATCH_SIZE], sizeof(traj.z) / 2));
|
TSHIFT(i), TSHIFT(j), TSHIFT(k),
|
||||||
|
TSHIFT(u), TSHIFT(v), TSHIFT(w)
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
// ... data is ready in trajMod.
|
// ... data is ready in trajMod.
|
||||||
batchRdyForInterp = true;
|
batchRdyForInterp = true;
|
||||||
|
|
||||||
batchRdy = false; // Clear so that makeVector() may resume generating points.
|
batchRdy = false; // Clear so makeVector() can resume generating points.
|
||||||
|
}
|
||||||
} // if (batchRdy && !batchRdyForInterp)
|
|
||||||
|
|
||||||
// Interpolation.
|
// Interpolation.
|
||||||
while ( batchRdyForInterp
|
while ( batchRdyForInterp
|
||||||
&& ( stepperCmdBuffItems() < ((FTM_STEPPERCMD_BUFF_SIZE) - (FTM_STEPS_PER_UNIT_TIME)) )
|
&& ( stepperCmdBuffItems() < (FTM_STEPPERCMD_BUFF_SIZE) - (FTM_STEPS_PER_UNIT_TIME) )
|
||||||
&& ( (interpIdx - interpIdx_z1) < (FTM_STEPS_PER_LOOP) )
|
&& ( interpIdx - interpIdx_z1 < (FTM_STEPS_PER_LOOP) )
|
||||||
) {
|
) {
|
||||||
convertToSteps(interpIdx);
|
convertToSteps(interpIdx);
|
||||||
|
|
||||||
if (++interpIdx == FTM_BATCH_SIZE) {
|
if (++interpIdx == TERN(FTM_UNIFIED_BWS, FTM_BW_SIZE, FTM_BATCH_SIZE)) {
|
||||||
batchRdyForInterp = false;
|
batchRdyForInterp = false;
|
||||||
interpIdx = 0;
|
interpIdx = 0;
|
||||||
}
|
}
|
||||||
|
@ -277,76 +240,153 @@ void FTMotion::loop() {
|
||||||
// Refresh the gains used by shaping functions.
|
// Refresh the gains used by shaping functions.
|
||||||
// To be called on init or mode or zeta change.
|
// To be called on init or mode or zeta change.
|
||||||
|
|
||||||
void FTMotion::Shaping::updateShapingA(const_float_t zeta/*=cfg.zeta*/, const_float_t vtol/*=cfg.vtol*/) {
|
void FTMotion::Shaping::updateShapingA(float zeta[]/*=cfg.zeta*/, float vtol[]/*=cfg.vtol*/) {
|
||||||
|
|
||||||
const float K = exp(-zeta * M_PI / sqrt(1.0f - sq(zeta))),
|
const float Kx = exp(-zeta[0] * M_PI / sqrt(1.0f - sq(zeta[0]))),
|
||||||
K2 = sq(K);
|
Ky = exp(-zeta[1] * M_PI / sqrt(1.0f - sq(zeta[1]))),
|
||||||
|
Kx2 = sq(Kx),
|
||||||
|
Ky2 = sq(Ky);
|
||||||
|
|
||||||
switch (cfg.mode) {
|
switch (cfg.mode) {
|
||||||
|
|
||||||
case ftMotionMode_ZV:
|
case ftMotionMode_ZV:
|
||||||
max_i = 1U;
|
max_i = 1U;
|
||||||
x.Ai[0] = 1.0f / (1.0f + K);
|
x.Ai[0] = 1.0f / (1.0f + Kx);
|
||||||
x.Ai[1] = x.Ai[0] * K;
|
x.Ai[1] = x.Ai[0] * Kx;
|
||||||
|
|
||||||
|
y.Ai[0] = 1.0f / (1.0f + Ky);
|
||||||
|
y.Ai[1] = y.Ai[0] * Ky;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ftMotionMode_ZVD:
|
case ftMotionMode_ZVD:
|
||||||
max_i = 2U;
|
max_i = 2U;
|
||||||
x.Ai[0] = 1.0f / ( 1.0f + 2.0f * K + K2 );
|
x.Ai[0] = 1.0f / (1.0f + 2.0f * Kx + Kx2);
|
||||||
x.Ai[1] = x.Ai[0] * 2.0f * K;
|
x.Ai[1] = x.Ai[0] * 2.0f * Kx;
|
||||||
x.Ai[2] = x.Ai[0] * K2;
|
x.Ai[2] = x.Ai[0] * Kx2;
|
||||||
|
|
||||||
|
y.Ai[0] = 1.0f / (1.0f + 2.0f * Ky + Ky2);
|
||||||
|
y.Ai[1] = y.Ai[0] * 2.0f * Ky;
|
||||||
|
y.Ai[2] = y.Ai[0] * Ky2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ftMotionMode_ZVDD:
|
||||||
|
max_i = 3U;
|
||||||
|
x.Ai[0] = 1.0f / (1.0f + 3.0f * Kx + 3.0f * Kx2 + cu(Kx));
|
||||||
|
x.Ai[1] = x.Ai[0] * 3.0f * Kx;
|
||||||
|
x.Ai[2] = x.Ai[0] * 3.0f * Kx2;
|
||||||
|
x.Ai[3] = x.Ai[0] * cu(Kx);
|
||||||
|
|
||||||
|
y.Ai[0] = 1.0f / (1.0f + 3.0f * Ky + 3.0f * Ky2 + cu(Ky));
|
||||||
|
y.Ai[1] = y.Ai[0] * 3.0f * Ky;
|
||||||
|
y.Ai[2] = y.Ai[0] * 3.0f * Ky2;
|
||||||
|
y.Ai[3] = y.Ai[0] * cu(Ky);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ftMotionMode_ZVDDD:
|
||||||
|
max_i = 4U;
|
||||||
|
x.Ai[0] = 1.0f / (1.0f + 4.0f * Kx + 6.0f * Kx2 + 4.0f * cu(Kx) + sq(Kx2));
|
||||||
|
x.Ai[1] = x.Ai[0] * 4.0f * Kx;
|
||||||
|
x.Ai[2] = x.Ai[0] * 6.0f * Kx2;
|
||||||
|
x.Ai[3] = x.Ai[0] * 4.0f * cu(Kx);
|
||||||
|
x.Ai[4] = x.Ai[0] * sq(Kx2);
|
||||||
|
|
||||||
|
y.Ai[0] = 1.0f / (1.0f + 4.0f * Ky + 6.0f * Ky2 + 4.0f * cu(Ky) + sq(Ky2));
|
||||||
|
y.Ai[1] = y.Ai[0] * 4.0f * Ky;
|
||||||
|
y.Ai[2] = y.Ai[0] * 6.0f * Ky2;
|
||||||
|
y.Ai[3] = y.Ai[0] * 4.0f * cu(Ky);
|
||||||
|
y.Ai[4] = y.Ai[0] * sq(Ky2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ftMotionMode_EI: {
|
case ftMotionMode_EI: {
|
||||||
max_i = 2U;
|
max_i = 2U;
|
||||||
x.Ai[0] = 0.25f * (1.0f + vtol);
|
x.Ai[0] = 0.25f * (1.0f + vtol[0]);
|
||||||
x.Ai[1] = 0.50f * (1.0f - vtol) * K;
|
x.Ai[1] = 0.50f * (1.0f - vtol[0]) * Kx;
|
||||||
x.Ai[2] = x.Ai[0] * K2;
|
x.Ai[2] = x.Ai[0] * Kx2;
|
||||||
const float A_adj = 1.0f / (x.Ai[0] + x.Ai[1] + x.Ai[2]);
|
|
||||||
for (uint32_t i = 0U; i < 3U; i++) { x.Ai[i] *= A_adj; }
|
y.Ai[0] = 0.25f * (1.0f + vtol[1]);
|
||||||
} break;
|
y.Ai[1] = 0.50f * (1.0f - vtol[1]) * Ky;
|
||||||
|
y.Ai[2] = y.Ai[0] * Ky2;
|
||||||
|
|
||||||
|
const float X_adj = 1.0f / (x.Ai[0] + x.Ai[1] + x.Ai[2]);
|
||||||
|
const float Y_adj = 1.0f / (y.Ai[0] + y.Ai[1] + y.Ai[2]);
|
||||||
|
for (uint32_t i = 0U; i < 3U; i++) {
|
||||||
|
x.Ai[i] *= X_adj;
|
||||||
|
y.Ai[i] *= Y_adj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ftMotionMode_2HEI: {
|
case ftMotionMode_2HEI: {
|
||||||
max_i = 3U;
|
max_i = 3U;
|
||||||
const float vtol2 = sq(vtol);
|
const float vtolx2 = sq(vtol[0]);
|
||||||
const float X = pow(vtol2 * (sqrt(1.0f - vtol2) + 1.0f), 1.0f / 3.0f);
|
const float X = pow(vtolx2 * (sqrt(1.0f - vtolx2) + 1.0f), 1.0f / 3.0f);
|
||||||
x.Ai[0] = ( 3.0f * sq(X) + 2.0f * X + 3.0f * vtol2 ) / (16.0f * X);
|
x.Ai[0] = (3.0f * sq(X) + 2.0f * X + 3.0f * vtolx2) / (16.0f * X);
|
||||||
x.Ai[1] = ( 0.5f - x.Ai[0] ) * K;
|
x.Ai[1] = (0.5f - x.Ai[0]) * Kx;
|
||||||
x.Ai[2] = x.Ai[1] * K;
|
x.Ai[2] = x.Ai[1] * Kx;
|
||||||
x.Ai[3] = x.Ai[0] * cu(K);
|
x.Ai[3] = x.Ai[0] * cu(Kx);
|
||||||
const float A_adj = 1.0f / (x.Ai[0] + x.Ai[1] + x.Ai[2] + x.Ai[3]);
|
|
||||||
for (uint32_t i = 0U; i < 4U; i++) { x.Ai[i] *= A_adj; }
|
const float vtoly2 = sq(vtol[1]);
|
||||||
} break;
|
const float Y = pow(vtoly2 * (sqrt(1.0f - vtoly2) + 1.0f), 1.0f / 3.0f);
|
||||||
|
y.Ai[0] = (3.0f * sq(Y) + 2.0f * Y + 3.0f * vtoly2) / (16.0f * Y);
|
||||||
|
y.Ai[1] = (0.5f - y.Ai[0]) * Ky;
|
||||||
|
y.Ai[2] = y.Ai[1] * Ky;
|
||||||
|
y.Ai[3] = y.Ai[0] * cu(Ky);
|
||||||
|
|
||||||
|
const float X_adj = 1.0f / (x.Ai[0] + x.Ai[1] + x.Ai[2] + x.Ai[3]);
|
||||||
|
const float Y_adj = 1.0f / (y.Ai[0] + y.Ai[1] + y.Ai[2] + y.Ai[3]);
|
||||||
|
for (uint32_t i = 0U; i < 4U; i++) {
|
||||||
|
x.Ai[i] *= X_adj;
|
||||||
|
y.Ai[i] *= Y_adj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ftMotionMode_3HEI: {
|
case ftMotionMode_3HEI: {
|
||||||
max_i = 4U;
|
max_i = 4U;
|
||||||
x.Ai[0] = 0.0625f * ( 1.0f + 3.0f * vtol + 2.0f * sqrt( 2.0f * ( vtol + 1.0f ) * vtol ) );
|
x.Ai[0] = 0.0625f * ( 1.0f + 3.0f * vtol[0] + 2.0f * sqrt( 2.0f * ( vtol[0] + 1.0f ) * vtol[0] ) );
|
||||||
x.Ai[1] = 0.25f * ( 1.0f - vtol ) * K;
|
x.Ai[1] = 0.25f * ( 1.0f - vtol[0] ) * Kx;
|
||||||
x.Ai[2] = ( 0.5f * ( 1.0f + vtol ) - 2.0f * x.Ai[0] ) * K2;
|
x.Ai[2] = ( 0.5f * ( 1.0f + vtol[0] ) - 2.0f * x.Ai[0] ) * Kx2;
|
||||||
x.Ai[3] = x.Ai[1] * K2;
|
x.Ai[3] = x.Ai[1] * Kx2;
|
||||||
x.Ai[4] = x.Ai[0] * sq(K2);
|
x.Ai[4] = x.Ai[0] * sq(Kx2);
|
||||||
const float A_adj = 1.0f / (x.Ai[0] + x.Ai[1] + x.Ai[2] + x.Ai[3] + x.Ai[4]);
|
|
||||||
for (uint32_t i = 0U; i < 5U; i++) { x.Ai[i] *= A_adj; }
|
y.Ai[0] = 0.0625f * (1.0f + 3.0f * vtol[1] + 2.0f * sqrt(2.0f * (vtol[1] + 1.0f) * vtol[1]));
|
||||||
} break;
|
y.Ai[1] = 0.25f * (1.0f - vtol[1]) * Ky;
|
||||||
|
y.Ai[2] = (0.5f * (1.0f + vtol[1]) - 2.0f * y.Ai[0]) * Ky2;
|
||||||
|
y.Ai[3] = y.Ai[1] * Ky2;
|
||||||
|
y.Ai[4] = y.Ai[0] * sq(Ky2);
|
||||||
|
|
||||||
|
const float X_adj = 1.0f / (x.Ai[0] + x.Ai[1] + x.Ai[2] + x.Ai[3] + x.Ai[4]);
|
||||||
|
const float Y_adj = 1.0f / (y.Ai[0] + y.Ai[1] + y.Ai[2] + y.Ai[3] + y.Ai[4]);
|
||||||
|
for (uint32_t i = 0U; i < 5U; i++) {
|
||||||
|
x.Ai[i] *= X_adj;
|
||||||
|
y.Ai[i] *= Y_adj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ftMotionMode_MZV: {
|
case ftMotionMode_MZV: {
|
||||||
max_i = 2U;
|
max_i = 2U;
|
||||||
const float B = 1.4142135623730950488016887242097f * K;
|
const float Bx = 1.4142135623730950488016887242097f * Kx;
|
||||||
x.Ai[0] = 1.0f / (1.0f + B + K2);
|
x.Ai[0] = 1.0f / (1.0f + Bx + Kx2);
|
||||||
x.Ai[1] = x.Ai[0] * B;
|
x.Ai[1] = x.Ai[0] * Bx;
|
||||||
x.Ai[2] = x.Ai[0] * K2;
|
x.Ai[2] = x.Ai[0] * Kx2;
|
||||||
} break;
|
|
||||||
|
const float By = 1.4142135623730950488016887242097f * Ky;
|
||||||
|
y.Ai[0] = 1.0f / (1.0f + By + Ky2);
|
||||||
|
y.Ai[1] = y.Ai[0] * By;
|
||||||
|
y.Ai[2] = y.Ai[0] * Ky2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
for (uint32_t i = 0U; i < 5U; i++) x.Ai[i] = 0.0f;
|
ZERO(x.Ai);
|
||||||
|
ZERO(y.Ai);
|
||||||
max_i = 0;
|
max_i = 0;
|
||||||
}
|
}
|
||||||
#if HAS_Y_AXIS
|
|
||||||
memcpy(y.Ai, x.Ai, sizeof(x.Ai)); // For now, zeta and vtol are shared across x and y.
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FTMotion::updateShapingA(const_float_t zeta/*=cfg.zeta*/, const_float_t vtol/*=cfg.vtol*/) {
|
void FTMotion::updateShapingA(float zeta[]/*=cfg.zeta*/, float vtol[]/*=cfg.vtol*/) {
|
||||||
shaping.updateShapingA(zeta, vtol);
|
shaping.updateShapingA(zeta, vtol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,11 +404,13 @@ void FTMotion::loop() {
|
||||||
Ni[1] = round((0.5f / f / df) * (FTM_FS));
|
Ni[1] = round((0.5f / f / df) * (FTM_FS));
|
||||||
Ni[2] = Ni[1] + Ni[1];
|
Ni[2] = Ni[1] + Ni[1];
|
||||||
break;
|
break;
|
||||||
|
case ftMotionMode_ZVDD:
|
||||||
case ftMotionMode_2HEI:
|
case ftMotionMode_2HEI:
|
||||||
Ni[1] = round((0.5f / f / df) * (FTM_FS));
|
Ni[1] = round((0.5f / f / df) * (FTM_FS));
|
||||||
Ni[2] = Ni[1] + Ni[1];
|
Ni[2] = Ni[1] + Ni[1];
|
||||||
Ni[3] = Ni[2] + Ni[1];
|
Ni[3] = Ni[2] + Ni[1];
|
||||||
break;
|
break;
|
||||||
|
case ftMotionMode_ZVDDD:
|
||||||
case ftMotionMode_3HEI:
|
case ftMotionMode_3HEI:
|
||||||
Ni[1] = round((0.5f / f / df) * (FTM_FS));
|
Ni[1] = round((0.5f / f / df) * (FTM_FS));
|
||||||
Ni[2] = Ni[1] + Ni[1];
|
Ni[2] = Ni[1] + Ni[1];
|
||||||
|
@ -383,10 +425,14 @@ void FTMotion::loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FTMotion::updateShapingN(const_float_t xf OPTARG(HAS_Y_AXIS, const_float_t yf), const_float_t zeta/*=cfg.zeta*/) {
|
void FTMotion::updateShapingN(const_float_t xf OPTARG(HAS_Y_AXIS, const_float_t yf), float zeta[]/*=cfg.zeta*/) {
|
||||||
const float df = sqrt(1.0f - sq(zeta));
|
const float xdf = sqrt(1.0f - sq(zeta[0]));
|
||||||
shaping.x.updateShapingN(xf, df);
|
shaping.x.updateShapingN(xf, xdf);
|
||||||
TERN_(HAS_Y_AXIS, shaping.y.updateShapingN(yf, df));
|
|
||||||
|
#if HAS_Y_AXIS
|
||||||
|
const float ydf = sqrt(1.0f - sq(zeta[1]));
|
||||||
|
shaping.y.updateShapingN(yf, ydf);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAS_X_AXIS
|
#endif // HAS_X_AXIS
|
||||||
|
@ -396,27 +442,24 @@ void FTMotion::reset() {
|
||||||
|
|
||||||
stepperCmdBuff_produceIdx = stepperCmdBuff_consumeIdx = 0;
|
stepperCmdBuff_produceIdx = stepperCmdBuff_consumeIdx = 0;
|
||||||
|
|
||||||
traj.reset(); // Reset trajectory history
|
traj.reset();
|
||||||
trajMod.reset(); // Reset modified trajectory history
|
trajWin.reset();
|
||||||
|
|
||||||
blockProcRdy = blockProcRdy_z1 = blockProcDn = false;
|
blockProcRdy = blockProcRdy_z1 = blockProcDn = false;
|
||||||
batchRdy = batchRdyForInterp = false;
|
batchRdy = batchRdyForInterp = false;
|
||||||
runoutEna = false;
|
runoutEna = false;
|
||||||
runout = false;
|
|
||||||
|
|
||||||
endPosn_prevBlock.reset();
|
endPosn_prevBlock.reset();
|
||||||
|
|
||||||
makeVector_idx = makeVector_idx_z1 = 0;
|
makeVector_idx = makeVector_idx_z1 = 0;
|
||||||
makeVector_batchIdx = FTM_BATCH_SIZE;
|
makeVector_batchIdx = 0;
|
||||||
|
|
||||||
steps.reset();
|
steps.reset();
|
||||||
interpIdx = interpIdx_z1 = 0;
|
interpIdx = interpIdx_z1 = 0;
|
||||||
dirState = LOGICAL_AXIS_ARRAY_1(stepDirState_NOT_SET);
|
|
||||||
nextStepTicks = FTM_MIN_TICKS;
|
|
||||||
|
|
||||||
#if HAS_X_AXIS
|
#if HAS_X_AXIS
|
||||||
for (uint32_t i = 0U; i < (FTM_ZMAX); i++)
|
ZERO(shaping.x.d_zi);
|
||||||
shaping.x.d_zi[i] = TERN_(HAS_Y_AXIS, shaping.y.d_zi[i] =) 0.0f;
|
TERN_(HAS_Y_AXIS, ZERO(shaping.y.d_zi));
|
||||||
shaping.zi_idx = 0;
|
shaping.zi_idx = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -445,40 +488,26 @@ void FTMotion::loadBlockData(block_t * const current_block) {
|
||||||
const float totalLength = current_block->millimeters,
|
const float totalLength = current_block->millimeters,
|
||||||
oneOverLength = 1.0f / totalLength;
|
oneOverLength = 1.0f / totalLength;
|
||||||
|
|
||||||
const AxisBits direction = current_block->direction_bits;
|
|
||||||
|
|
||||||
startPosn = endPosn_prevBlock;
|
startPosn = endPosn_prevBlock;
|
||||||
xyze_pos_t moveDist = LOGICAL_AXIS_ARRAY(
|
xyze_pos_t moveDist = LOGICAL_AXIS_ARRAY(
|
||||||
current_block->steps.e / planner.settings.axis_steps_per_mm[E_AXIS_N(current_block->extruder)],
|
current_block->steps.e * planner.mm_per_step[E_AXIS_N(current_block->extruder)] * (current_block->direction_bits.e ? 1 : -1),
|
||||||
current_block->steps.x / planner.settings.axis_steps_per_mm[X_AXIS],
|
current_block->steps.x * planner.mm_per_step[X_AXIS] * (current_block->direction_bits.x ? 1 : -1),
|
||||||
current_block->steps.y / planner.settings.axis_steps_per_mm[Y_AXIS],
|
current_block->steps.y * planner.mm_per_step[Y_AXIS] * (current_block->direction_bits.y ? 1 : -1),
|
||||||
current_block->steps.z / planner.settings.axis_steps_per_mm[Z_AXIS],
|
current_block->steps.z * planner.mm_per_step[Z_AXIS] * (current_block->direction_bits.z ? 1 : -1),
|
||||||
current_block->steps.i / planner.settings.axis_steps_per_mm[I_AXIS],
|
current_block->steps.i * planner.mm_per_step[I_AXIS] * (current_block->direction_bits.i ? 1 : -1),
|
||||||
current_block->steps.j / planner.settings.axis_steps_per_mm[J_AXIS],
|
current_block->steps.j * planner.mm_per_step[J_AXIS] * (current_block->direction_bits.j ? 1 : -1),
|
||||||
current_block->steps.k / planner.settings.axis_steps_per_mm[K_AXIS],
|
current_block->steps.k * planner.mm_per_step[K_AXIS] * (current_block->direction_bits.k ? 1 : -1),
|
||||||
current_block->steps.u / planner.settings.axis_steps_per_mm[U_AXIS],
|
current_block->steps.u * planner.mm_per_step[U_AXIS] * (current_block->direction_bits.u ? 1 : -1),
|
||||||
current_block->steps.v / planner.settings.axis_steps_per_mm[V_AXIS],
|
current_block->steps.v * planner.mm_per_step[V_AXIS] * (current_block->direction_bits.v ? 1 : -1),
|
||||||
current_block->steps.w / planner.settings.axis_steps_per_mm[W_AXIS]
|
current_block->steps.w * planner.mm_per_step[W_AXIS] * (current_block->direction_bits.w ? 1 : -1)
|
||||||
);
|
|
||||||
|
|
||||||
LOGICAL_AXIS_CODE(
|
|
||||||
if (!direction.e) moveDist.e *= -1.0f,
|
|
||||||
if (!direction.x) moveDist.x *= -1.0f,
|
|
||||||
if (!direction.y) moveDist.y *= -1.0f,
|
|
||||||
if (!direction.z) moveDist.z *= -1.0f,
|
|
||||||
if (!direction.i) moveDist.i *= -1.0f,
|
|
||||||
if (!direction.j) moveDist.j *= -1.0f,
|
|
||||||
if (!direction.k) moveDist.k *= -1.0f,
|
|
||||||
if (!direction.u) moveDist.u *= -1.0f,
|
|
||||||
if (!direction.v) moveDist.v *= -1.0f,
|
|
||||||
if (!direction.w) moveDist.w *= -1.0f
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ratio = moveDist * oneOverLength;
|
ratio = moveDist * oneOverLength;
|
||||||
|
|
||||||
|
/* Keep for comprehension
|
||||||
const float spm = totalLength / current_block->step_event_count; // (steps/mm) Distance for each step
|
const float spm = totalLength / current_block->step_event_count; // (steps/mm) Distance for each step
|
||||||
f_s = spm * current_block->initial_rate; // (steps/s) Start feedrate
|
f_s = spm * current_block->initial_rate, // (steps/s) Start feedrate
|
||||||
const float f_e = spm * current_block->final_rate; // (steps/s) End feedrate
|
f_e = spm * current_block->final_rate; // (steps/s) End feedrate
|
||||||
|
|
||||||
const float a = current_block->acceleration, // (mm/s^2) Same magnitude for acceleration or deceleration
|
const float a = current_block->acceleration, // (mm/s^2) Same magnitude for acceleration or deceleration
|
||||||
oneby2a = 1.0f / (2.0f * a), // (s/mm) Time to accelerate or decelerate one mm (i.e., oneby2a * 2
|
oneby2a = 1.0f / (2.0f * a), // (s/mm) Time to accelerate or decelerate one mm (i.e., oneby2a * 2
|
||||||
|
@ -499,25 +528,46 @@ void FTMotion::loadBlockData(block_t * const current_block) {
|
||||||
|
|
||||||
const float T1 = (F_n - f_s) / a, // (s) Accel Time = difference in feedrate over acceleration
|
const float T1 = (F_n - f_s) / a, // (s) Accel Time = difference in feedrate over acceleration
|
||||||
T3 = (F_n - f_e) / a; // (s) Decel Time = difference in feedrate over acceleration
|
T3 = (F_n - f_e) / a; // (s) Decel Time = difference in feedrate over acceleration
|
||||||
|
*/
|
||||||
|
|
||||||
N1 = ceil(T1 * (FTM_FS)); // Accel datapoints based on Hz frequency
|
const float spm = totalLength / current_block->step_event_count,
|
||||||
N2 = ceil(T2 * (FTM_FS)); // Coast
|
f_s = spm * current_block->initial_rate,
|
||||||
N3 = ceil(T3 * (FTM_FS)); // Decel
|
f_e = spm * current_block->final_rate;
|
||||||
|
|
||||||
const float T1_P = N1 * (FTM_TS), // (s) Accel datapoints x timestep resolution
|
const float accel = current_block->acceleration,
|
||||||
T2_P = N2 * (FTM_TS), // (s) Coast
|
oneOverAccel = 1.0f / accel;
|
||||||
T3_P = N3 * (FTM_TS); // (s) Decel
|
|
||||||
|
|
||||||
// Calculate the reachable feedrate at the end of the accel phase
|
float F_n = current_block->nominal_speed;
|
||||||
// totalLength is the total distance to travel in mm
|
const float ldiff = totalLength + 0.5f * oneOverAccel * (sq(f_s) + sq(f_e));
|
||||||
// f_s is the starting feedrate in mm/s
|
|
||||||
// f_e is the ending feedrate in mm/s
|
float T2 = ldiff / F_n - oneOverAccel * F_n;
|
||||||
// T1_P is the time spent accelerating in seconds
|
if (T2 < 0.0f) {
|
||||||
// T2_P is the time spent coasting in seconds
|
T2 = 0.0f;
|
||||||
// T3_P is the time spent decelerating in seconds
|
F_n = SQRT(ldiff * accel);
|
||||||
// f_s * T1_P is the distance traveled during the accel phase
|
}
|
||||||
// f_e * T3_P is the distance traveled during the decel phase
|
|
||||||
//
|
const float T1 = (F_n - f_s) * oneOverAccel,
|
||||||
|
T3 = (F_n - f_e) * oneOverAccel;
|
||||||
|
|
||||||
|
N1 = ceil(T1 * (FTM_FS)); // Accel datapoints based on Hz frequency
|
||||||
|
N2 = ceil(T2 * (FTM_FS)); // Coast
|
||||||
|
N3 = ceil(T3 * (FTM_FS)); // Decel
|
||||||
|
|
||||||
|
const float T1_P = N1 * (FTM_TS), // (s) Accel datapoints x timestep resolution
|
||||||
|
T2_P = N2 * (FTM_TS), // (s) Coast
|
||||||
|
T3_P = N3 * (FTM_TS); // (s) Decel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the reachable feedrate at the end of the accel phase.
|
||||||
|
* totalLength is the total distance to travel in mm
|
||||||
|
* f_s : (mm/s) Starting feedrate
|
||||||
|
* f_e : (mm/s) Ending feedrate
|
||||||
|
* T1_P : (sec) Time spent accelerating
|
||||||
|
* T2_P : (sec) Time spent coasting
|
||||||
|
* T3_P : (sec) Time spent decelerating
|
||||||
|
* f_s * T1_P : (mm) Distance traveled during the accel phase
|
||||||
|
* f_e * T3_P : (mm) Distance traveled during the decel phase
|
||||||
|
*/
|
||||||
F_P = (2.0f * totalLength - f_s * T1_P - f_e * T3_P) / (T1_P + 2.0f * T2_P + T3_P); // (mm/s) Feedrate at the end of the accel phase
|
F_P = (2.0f * totalLength - f_s * T1_P - f_e * T3_P) / (T1_P + 2.0f * T2_P + T3_P); // (mm/s) Feedrate at the end of the accel phase
|
||||||
|
|
||||||
// Calculate the acceleration and deceleration rates
|
// Calculate the acceleration and deceleration rates
|
||||||
|
@ -531,77 +581,69 @@ void FTMotion::loadBlockData(block_t * const current_block) {
|
||||||
// Calculate the distance traveled during the decel phase
|
// Calculate the distance traveled during the decel phase
|
||||||
s_2e = s_1e + F_P * T2_P;
|
s_2e = s_1e + F_P * T2_P;
|
||||||
|
|
||||||
// One less than (Accel + Coasting + Decel) datapoints
|
// Accel + Coasting + Decel datapoints
|
||||||
max_intervals = N1 + N2 + N3 - 1U;
|
max_intervals = N1 + N2 + N3;
|
||||||
|
|
||||||
endPosn_prevBlock += moveDist;
|
endPosn_prevBlock += moveDist;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate data points of the trajectory.
|
// Generate data points of the trajectory.
|
||||||
void FTMotion::makeVector() {
|
void FTMotion::makeVector() {
|
||||||
float accel_k = 0.0f; // (mm/s^2) Acceleration K factor
|
float accel_k = 0.0f; // (mm/s^2) Acceleration K factor
|
||||||
float tau = (makeVector_idx + 1) * (FTM_TS); // (s) Time since start of block
|
float tau = (makeVector_idx + 1) * (FTM_TS); // (s) Time since start of block
|
||||||
float dist = 0.0f; // (mm) Distance traveled
|
float dist = 0.0f; // (mm) Distance traveled
|
||||||
|
|
||||||
if (makeVector_idx < N1) {
|
if (makeVector_idx < N1) {
|
||||||
// Acceleration phase
|
// Acceleration phase
|
||||||
dist = (f_s * tau) + (0.5f * accel_P * sq(tau)); // (mm) Distance traveled for acceleration phase
|
dist = (f_s * tau) + (0.5f * accel_P * sq(tau)); // (mm) Distance traveled for acceleration phase since start of block
|
||||||
accel_k = accel_P; // (mm/s^2) Acceleration K factor from Accel phase
|
accel_k = accel_P; // (mm/s^2) Acceleration K factor from Accel phase
|
||||||
}
|
}
|
||||||
else if (makeVector_idx >= N1 && makeVector_idx < (N1 + N2)) {
|
else if (makeVector_idx < (N1 + N2)) {
|
||||||
// Coasting phase
|
// Coasting phase
|
||||||
dist = s_1e + F_P * (tau - N1 * (FTM_TS)); // (mm) Distance traveled for coasting phase
|
dist = s_1e + F_P * (tau - N1 * (FTM_TS)); // (mm) Distance traveled for coasting phase since start of block
|
||||||
//accel_k = 0.0f;
|
//accel_k = 0.0f;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Deceleration phase
|
// Deceleration phase
|
||||||
const float tau_ = tau - (N1 + N2) * (FTM_TS); // (s) Time since start of decel phase
|
tau -= (N1 + N2) * (FTM_TS); // (s) Time since start of decel phase
|
||||||
dist = s_2e + F_P * tau_ + 0.5f * decel_P * sq(tau_); // (mm) Distance traveled for deceleration phase
|
dist = s_2e + F_P * tau + 0.5f * decel_P * sq(tau); // (mm) Distance traveled for deceleration phase since start of block
|
||||||
accel_k = decel_P; // (mm/s^2) Acceleration K factor from Decel phase
|
accel_k = decel_P; // (mm/s^2) Acceleration K factor from Decel phase
|
||||||
}
|
}
|
||||||
|
|
||||||
NUM_AXIS_CODE(
|
LOGICAL_AXIS_CODE(
|
||||||
traj.x[makeVector_batchIdx] = startPosn.x + ratio.x * dist,
|
trajWin.e[makeVector_batchIdx] = startPosn.e + ratio.e * dist,
|
||||||
traj.y[makeVector_batchIdx] = startPosn.y + ratio.y * dist,
|
trajWin.x[makeVector_batchIdx] = startPosn.x + ratio.x * dist,
|
||||||
traj.z[makeVector_batchIdx] = startPosn.z + ratio.z * dist,
|
trajWin.y[makeVector_batchIdx] = startPosn.y + ratio.y * dist,
|
||||||
traj.i[makeVector_batchIdx] = startPosn.i + ratio.i * dist,
|
trajWin.z[makeVector_batchIdx] = startPosn.z + ratio.z * dist,
|
||||||
traj.j[makeVector_batchIdx] = startPosn.j + ratio.j * dist,
|
trajWin.i[makeVector_batchIdx] = startPosn.i + ratio.i * dist,
|
||||||
traj.k[makeVector_batchIdx] = startPosn.k + ratio.k * dist,
|
trajWin.j[makeVector_batchIdx] = startPosn.j + ratio.j * dist,
|
||||||
traj.u[makeVector_batchIdx] = startPosn.u + ratio.u * dist,
|
trajWin.k[makeVector_batchIdx] = startPosn.k + ratio.k * dist,
|
||||||
traj.v[makeVector_batchIdx] = startPosn.v + ratio.v * dist,
|
trajWin.u[makeVector_batchIdx] = startPosn.u + ratio.u * dist,
|
||||||
traj.w[makeVector_batchIdx] = startPosn.w + ratio.w * dist
|
trajWin.v[makeVector_batchIdx] = startPosn.v + ratio.v * dist,
|
||||||
|
trajWin.w[makeVector_batchIdx] = startPosn.w + ratio.w * dist
|
||||||
);
|
);
|
||||||
|
|
||||||
#if HAS_EXTRUDERS
|
#if HAS_EXTRUDERS
|
||||||
const float new_raw_z1 = startPosn.e + ratio.e * dist;
|
|
||||||
if (cfg.linearAdvEna) {
|
if (cfg.linearAdvEna) {
|
||||||
float dedt_adj = (new_raw_z1 - e_raw_z1) * (FTM_FS);
|
float dedt_adj = (trajWin.e[makeVector_batchIdx] - e_raw_z1) * (FTM_FS);
|
||||||
if (ratio.e > 0.0f) dedt_adj += accel_k * cfg.linearAdvK;
|
if (ratio.e > 0.0f) dedt_adj += accel_k * cfg.linearAdvK;
|
||||||
|
|
||||||
|
e_raw_z1 = trajWin.e[makeVector_batchIdx];
|
||||||
e_advanced_z1 += dedt_adj * (FTM_TS);
|
e_advanced_z1 += dedt_adj * (FTM_TS);
|
||||||
traj.e[makeVector_batchIdx] = e_advanced_z1;
|
trajWin.e[makeVector_batchIdx] = e_advanced_z1;
|
||||||
|
|
||||||
e_raw_z1 = new_raw_z1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
traj.e[makeVector_batchIdx] = new_raw_z1;
|
|
||||||
// Alternatively: ed[makeVector_batchIdx] = startPosn.e + (ratio.e * dist) / (N1 + N2 + N3);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Update shaping parameters if needed.
|
// Update shaping parameters if needed.
|
||||||
#if HAS_DYNAMIC_FREQ_MM
|
|
||||||
static float zd_z1 = 0.0f;
|
|
||||||
#endif
|
|
||||||
switch (cfg.dynFreqMode) {
|
switch (cfg.dynFreqMode) {
|
||||||
|
|
||||||
#if HAS_DYNAMIC_FREQ_MM
|
#if HAS_DYNAMIC_FREQ_MM
|
||||||
case dynFreqMode_Z_BASED:
|
case dynFreqMode_Z_BASED:
|
||||||
if (traj.z[makeVector_batchIdx] != zd_z1) { // Only update if Z changed.
|
if (trajWin.z[makeVector_batchIdx] != 0.0f) { // Only update if Z changed.
|
||||||
const float xf = cfg.baseFreq[X_AXIS] + cfg.dynFreqK[X_AXIS] * traj.z[makeVector_batchIdx],
|
const float xf = cfg.baseFreq[X_AXIS] + cfg.dynFreqK[X_AXIS] * trajWin.z[makeVector_batchIdx]
|
||||||
yf = cfg.baseFreq[Y_AXIS] + cfg.dynFreqK[Y_AXIS] * traj.z[makeVector_batchIdx];
|
OPTARG(HAS_Y_AXIS, yf = cfg.baseFreq[Y_AXIS] + cfg.dynFreqK[Y_AXIS] * trajWin.z[makeVector_batchIdx]);
|
||||||
updateShapingN(_MAX(xf, FTM_MIN_SHAPE_FREQ), _MAX(yf, FTM_MIN_SHAPE_FREQ));
|
updateShapingN(_MAX(xf, FTM_MIN_SHAPE_FREQ) OPTARG(HAS_Y_AXIS, _MAX(yf, FTM_MIN_SHAPE_FREQ)));
|
||||||
zd_z1 = traj.z[makeVector_batchIdx];
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
@ -610,8 +652,8 @@ void FTMotion::makeVector() {
|
||||||
case dynFreqMode_MASS_BASED:
|
case dynFreqMode_MASS_BASED:
|
||||||
// Update constantly. The optimization done for Z value makes
|
// Update constantly. The optimization done for Z value makes
|
||||||
// less sense for E, as E is expected to constantly change.
|
// less sense for E, as E is expected to constantly change.
|
||||||
updateShapingN( cfg.baseFreq[X_AXIS] + cfg.dynFreqK[X_AXIS] * traj.e[makeVector_batchIdx]
|
updateShapingN( cfg.baseFreq[X_AXIS] + cfg.dynFreqK[X_AXIS] * trajWin.e[makeVector_batchIdx]
|
||||||
OPTARG(HAS_Y_AXIS, cfg.baseFreq[Y_AXIS] + cfg.dynFreqK[Y_AXIS] * traj.e[makeVector_batchIdx]) );
|
OPTARG(HAS_Y_AXIS, cfg.baseFreq[Y_AXIS] + cfg.dynFreqK[Y_AXIS] * trajWin.e[makeVector_batchIdx]) );
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -621,18 +663,18 @@ void FTMotion::makeVector() {
|
||||||
// Apply shaping if in mode.
|
// Apply shaping if in mode.
|
||||||
#if HAS_X_AXIS
|
#if HAS_X_AXIS
|
||||||
if (cfg.modeHasShaper()) {
|
if (cfg.modeHasShaper()) {
|
||||||
shaping.x.d_zi[shaping.zi_idx] = traj.x[makeVector_batchIdx];
|
shaping.x.d_zi[shaping.zi_idx] = trajWin.x[makeVector_batchIdx];
|
||||||
traj.x[makeVector_batchIdx] *= shaping.x.Ai[0];
|
trajWin.x[makeVector_batchIdx] *= shaping.x.Ai[0];
|
||||||
#if HAS_Y_AXIS
|
#if HAS_Y_AXIS
|
||||||
shaping.y.d_zi[shaping.zi_idx] = traj.y[makeVector_batchIdx];
|
shaping.y.d_zi[shaping.zi_idx] = trajWin.y[makeVector_batchIdx];
|
||||||
traj.y[makeVector_batchIdx] *= shaping.y.Ai[0];
|
trajWin.y[makeVector_batchIdx] *= shaping.y.Ai[0];
|
||||||
#endif
|
#endif
|
||||||
for (uint32_t i = 1U; i <= shaping.max_i; i++) {
|
for (uint32_t i = 1U; i <= shaping.max_i; i++) {
|
||||||
const uint32_t udiffx = shaping.zi_idx - shaping.x.Ni[i];
|
const uint32_t udiffx = shaping.zi_idx - shaping.x.Ni[i];
|
||||||
traj.x[makeVector_batchIdx] += shaping.x.Ai[i] * shaping.x.d_zi[shaping.x.Ni[i] > shaping.zi_idx ? (FTM_ZMAX) + udiffx : udiffx];
|
trajWin.x[makeVector_batchIdx] += shaping.x.Ai[i] * shaping.x.d_zi[shaping.x.Ni[i] > shaping.zi_idx ? (FTM_ZMAX) + udiffx : udiffx];
|
||||||
#if HAS_Y_AXIS
|
#if HAS_Y_AXIS
|
||||||
const uint32_t udiffy = shaping.zi_idx - shaping.y.Ni[i];
|
const uint32_t udiffy = shaping.zi_idx - shaping.y.Ni[i];
|
||||||
traj.y[makeVector_batchIdx] += shaping.y.Ai[i] * shaping.y.d_zi[shaping.y.Ni[i] > shaping.zi_idx ? (FTM_ZMAX) + udiffy : udiffy];
|
trajWin.y[makeVector_batchIdx] += shaping.y.Ai[i] * shaping.y.d_zi[shaping.y.Ni[i] > shaping.zi_idx ? (FTM_ZMAX) + udiffy : udiffy];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (++shaping.zi_idx == (FTM_ZMAX)) shaping.zi_idx = 0;
|
if (++shaping.zi_idx == (FTM_ZMAX)) shaping.zi_idx = 0;
|
||||||
|
@ -640,18 +682,38 @@ void FTMotion::makeVector() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Filled up the queue with regular and shaped steps
|
// Filled up the queue with regular and shaped steps
|
||||||
if (++makeVector_batchIdx == (FTM_WINDOW_SIZE)) {
|
if (++makeVector_batchIdx == TERN(FTM_UNIFIED_BWS, FTM_BW_SIZE, (FTM_WINDOW_SIZE - FTM_BATCH_SIZE))) {
|
||||||
makeVector_batchIdx = last_batchIdx;
|
makeVector_batchIdx = 0;
|
||||||
batchRdy = true;
|
batchRdy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (makeVector_idx == max_intervals) {
|
if (++makeVector_idx == max_intervals) {
|
||||||
blockProcDn = true;
|
blockProcDn = true;
|
||||||
blockProcRdy = false;
|
blockProcRdy = false;
|
||||||
makeVector_idx = 0;
|
makeVector_idx = 0;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
makeVector_idx++;
|
|
||||||
|
/**
|
||||||
|
* Convert to steps
|
||||||
|
* - Commands are written in a bitmask with step and dir as single bits.
|
||||||
|
* - Tests for delta are moved outside the loop.
|
||||||
|
* - Two functions are used for command computation with an array of function pointers.
|
||||||
|
*/
|
||||||
|
static void (*command_set[NUM_AXES TERN0(HAS_EXTRUDERS, +1)])(int32_t&, int32_t&, ft_command_t&, int32_t, int32_t);
|
||||||
|
|
||||||
|
static void command_set_pos(int32_t &e, int32_t &s, ft_command_t &b, int32_t bd, int32_t bs) {
|
||||||
|
if (e < FTM_CTS_COMPARE_VAL) return;
|
||||||
|
s++;
|
||||||
|
b |= bd | bs;
|
||||||
|
e -= FTM_STEPS_PER_UNIT_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void command_set_neg(int32_t &e, int32_t &s, ft_command_t &b, int32_t bd, int32_t bs) {
|
||||||
|
if (e > -(FTM_CTS_COMPARE_VAL)) return;
|
||||||
|
s--;
|
||||||
|
b |= bs;
|
||||||
|
e += FTM_STEPS_PER_UNIT_TIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolates single data point to stepper commands.
|
// Interpolates single data point to stepper commands.
|
||||||
|
@ -660,154 +722,61 @@ void FTMotion::convertToSteps(const uint32_t idx) {
|
||||||
|
|
||||||
//#define STEPS_ROUNDING
|
//#define STEPS_ROUNDING
|
||||||
#if ENABLED(STEPS_ROUNDING)
|
#if ENABLED(STEPS_ROUNDING)
|
||||||
const xyze_float_t steps_tar = LOGICAL_AXIS_ARRAY(
|
#define TOSTEPS(A,B) int32_t(trajMod.A[idx] * planner.settings.axis_steps_per_mm[B] + (trajMod.A[idx] < 0.0f ? -0.5f : 0.5f))
|
||||||
trajMod.e[idx] * planner.settings.axis_steps_per_mm[E_AXIS_N(current_block->extruder)] + (trajMod.e[idx] < 0.0f ? -0.5f : 0.5f), // May be eliminated if guaranteed positive.
|
const xyze_long_t steps_tar = LOGICAL_AXIS_ARRAY(
|
||||||
trajMod.x[idx] * planner.settings.axis_steps_per_mm[X_AXIS] + (trajMod.x[idx] < 0.0f ? -0.5f : 0.5f),
|
TOSTEPS(e, E_AXIS_N(current_block->extruder)), // May be eliminated if guaranteed positive.
|
||||||
trajMod.y[idx] * planner.settings.axis_steps_per_mm[Y_AXIS] + (trajMod.y[idx] < 0.0f ? -0.5f : 0.5f),
|
TOSTEPS(x, X_AXIS), TOSTEPS(y, Y_AXIS), TOSTEPS(z, Z_AXIS),
|
||||||
trajMod.z[idx] * planner.settings.axis_steps_per_mm[Z_AXIS] + (trajMod.z[idx] < 0.0f ? -0.5f : 0.5f),
|
TOSTEPS(i, I_AXIS), TOSTEPS(j, J_AXIS), TOSTEPS(k, K_AXIS),
|
||||||
trajMod.i[idx] * planner.settings.axis_steps_per_mm[I_AXIS] + (trajMod.i[idx] < 0.0f ? -0.5f : 0.5f),
|
TOSTEPS(u, U_AXIS), TOSTEPS(v, V_AXIS), TOSTEPS(w, W_AXIS)
|
||||||
trajMod.j[idx] * planner.settings.axis_steps_per_mm[J_AXIS] + (trajMod.j[idx] < 0.0f ? -0.5f : 0.5f),
|
|
||||||
trajMod.k[idx] * planner.settings.axis_steps_per_mm[K_AXIS] + (trajMod.k[idx] < 0.0f ? -0.5f : 0.5f),
|
|
||||||
trajMod.u[idx] * planner.settings.axis_steps_per_mm[U_AXIS] + (trajMod.u[idx] < 0.0f ? -0.5f : 0.5f),
|
|
||||||
trajMod.v[idx] * planner.settings.axis_steps_per_mm[V_AXIS] + (trajMod.v[idx] < 0.0f ? -0.5f : 0.5f),
|
|
||||||
trajMod.w[idx] * planner.settings.axis_steps_per_mm[W_AXIS] + (trajMod.w[idx] < 0.0f ? -0.5f : 0.5f),
|
|
||||||
);
|
);
|
||||||
xyze_long_t delta = xyze_long_t(steps_tar) - steps;
|
xyze_long_t delta = steps_tar - steps;
|
||||||
//const xyze_long_t delta = LOGICAL_AXIS_ARRAY(
|
|
||||||
// int32_t(steps_tar.e) - steps.e,
|
|
||||||
// int32_t(steps_tar.x) - steps.x,
|
|
||||||
// int32_t(steps_tar.y) - steps.y,
|
|
||||||
// int32_t(steps_tar.z) - steps.z,
|
|
||||||
// int32_t(steps_tar.i) - steps.i,
|
|
||||||
// int32_t(steps_tar.j) - steps.j,
|
|
||||||
// int32_t(steps_tar.k) - steps.k,
|
|
||||||
// int32_t(steps_tar.u) - steps.u,
|
|
||||||
// int32_t(steps_tar.v) - steps.v,
|
|
||||||
// int32_t(steps_tar.w) - steps.w
|
|
||||||
//);
|
|
||||||
#else
|
#else
|
||||||
|
#define TOSTEPS(A,B) int32_t(trajMod.A[idx] * planner.settings.axis_steps_per_mm[B]) - steps.A
|
||||||
xyze_long_t delta = LOGICAL_AXIS_ARRAY(
|
xyze_long_t delta = LOGICAL_AXIS_ARRAY(
|
||||||
int32_t(trajMod.e[idx] * planner.settings.axis_steps_per_mm[E_AXIS_N(current_block->extruder)]) - steps.e,
|
TOSTEPS(e, E_AXIS_N(current_block->extruder)),
|
||||||
int32_t(trajMod.x[idx] * planner.settings.axis_steps_per_mm[X_AXIS]) - steps.x,
|
TOSTEPS(x, X_AXIS), TOSTEPS(y, Y_AXIS), TOSTEPS(z, Z_AXIS),
|
||||||
int32_t(trajMod.y[idx] * planner.settings.axis_steps_per_mm[Y_AXIS]) - steps.y,
|
TOSTEPS(i, I_AXIS), TOSTEPS(j, J_AXIS), TOSTEPS(k, K_AXIS),
|
||||||
int32_t(trajMod.z[idx] * planner.settings.axis_steps_per_mm[Z_AXIS]) - steps.z,
|
TOSTEPS(u, U_AXIS), TOSTEPS(v, V_AXIS), TOSTEPS(w, W_AXIS)
|
||||||
int32_t(trajMod.i[idx] * planner.settings.axis_steps_per_mm[I_AXIS]) - steps.i,
|
|
||||||
int32_t(trajMod.j[idx] * planner.settings.axis_steps_per_mm[J_AXIS]) - steps.j,
|
|
||||||
int32_t(trajMod.k[idx] * planner.settings.axis_steps_per_mm[K_AXIS]) - steps.k,
|
|
||||||
int32_t(trajMod.u[idx] * planner.settings.axis_steps_per_mm[U_AXIS]) - steps.u,
|
|
||||||
int32_t(trajMod.v[idx] * planner.settings.axis_steps_per_mm[V_AXIS]) - steps.v,
|
|
||||||
int32_t(trajMod.w[idx] * planner.settings.axis_steps_per_mm[W_AXIS]) - steps.w
|
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool any_dirChange = (false
|
LOGICAL_AXIS_CODE(
|
||||||
LOGICAL_AXIS_GANG(
|
command_set[E_AXIS_N(current_block->extruder)] = delta.e >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.e > 0 && dirState.e != stepDirState_POS) || (delta.e < 0 && dirState.e != stepDirState_NEG),
|
command_set[X_AXIS] = delta.x >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.x > 0 && dirState.x != stepDirState_POS) || (delta.x < 0 && dirState.x != stepDirState_NEG),
|
command_set[Y_AXIS] = delta.y >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.y > 0 && dirState.y != stepDirState_POS) || (delta.y < 0 && dirState.y != stepDirState_NEG),
|
command_set[Z_AXIS] = delta.z >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.z > 0 && dirState.z != stepDirState_POS) || (delta.z < 0 && dirState.z != stepDirState_NEG),
|
command_set[I_AXIS] = delta.i >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.i > 0 && dirState.i != stepDirState_POS) || (delta.i < 0 && dirState.i != stepDirState_NEG),
|
command_set[J_AXIS] = delta.j >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.j > 0 && dirState.j != stepDirState_POS) || (delta.j < 0 && dirState.j != stepDirState_NEG),
|
command_set[K_AXIS] = delta.k >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.k > 0 && dirState.k != stepDirState_POS) || (delta.k < 0 && dirState.k != stepDirState_NEG),
|
command_set[U_AXIS] = delta.u >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.u > 0 && dirState.u != stepDirState_POS) || (delta.u < 0 && dirState.u != stepDirState_NEG),
|
command_set[V_AXIS] = delta.v >= 0 ? command_set_pos : command_set_neg,
|
||||||
|| (delta.v > 0 && dirState.v != stepDirState_POS) || (delta.v < 0 && dirState.v != stepDirState_NEG),
|
command_set[W_AXIS] = delta.w >= 0 ? command_set_pos : command_set_neg
|
||||||
|| (delta.w > 0 && dirState.w != stepDirState_POS) || (delta.w < 0 && dirState.w != stepDirState_NEG)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for (uint32_t i = 0U; i < (FTM_STEPS_PER_UNIT_TIME); i++) {
|
for (uint32_t i = 0U; i < (FTM_STEPS_PER_UNIT_TIME); i++) {
|
||||||
|
|
||||||
// TODO: (?) Since the *delta variables will not change,
|
|
||||||
// the comparison may be done once before iterating at
|
|
||||||
// expense of storage and lines of code.
|
|
||||||
|
|
||||||
bool anyStep = false;
|
|
||||||
|
|
||||||
// Commands are written in a bitmask with step and dir as single bits
|
|
||||||
auto COMMAND_SET = [&](auto &d, auto &e, auto &s, auto &b, auto bd, auto bs) {
|
|
||||||
if (d >= 0) {
|
|
||||||
if (e + d < (FTM_CTS_COMPARE_VAL)) {
|
|
||||||
e += d;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
s++;
|
|
||||||
b |= bd | bs;
|
|
||||||
e += d - (FTM_STEPS_PER_UNIT_TIME);
|
|
||||||
anyStep = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ((e + d) > -(FTM_CTS_COMPARE_VAL)) {
|
|
||||||
e += d;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
s--;
|
|
||||||
b |= bs;
|
|
||||||
e += d + (FTM_STEPS_PER_UNIT_TIME);
|
|
||||||
anyStep = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Init all step/dir bits to 0 (defaulting to reverse/negative motion)
|
// Init all step/dir bits to 0 (defaulting to reverse/negative motion)
|
||||||
stepperCmdBuff[stepperCmdBuff_produceIdx] = 0;
|
stepperCmdBuff[stepperCmdBuff_produceIdx] = 0;
|
||||||
|
|
||||||
|
err_P += delta;
|
||||||
|
|
||||||
// Set up step/dir bits for all axes
|
// Set up step/dir bits for all axes
|
||||||
LOGICAL_AXIS_CODE(
|
LOGICAL_AXIS_CODE(
|
||||||
COMMAND_SET(delta.e, err_P.e, steps.e, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_E), _BV(FT_BIT_STEP_E)),
|
command_set[E_AXIS_N(current_block->extruder)](err_P.e, steps.e, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_E), _BV(FT_BIT_STEP_E)),
|
||||||
COMMAND_SET(delta.x, err_P.x, steps.x, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_X), _BV(FT_BIT_STEP_X)),
|
command_set[X_AXIS](err_P.x, steps.x, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_X), _BV(FT_BIT_STEP_X)),
|
||||||
COMMAND_SET(delta.y, err_P.y, steps.y, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_Y), _BV(FT_BIT_STEP_Y)),
|
command_set[Y_AXIS](err_P.y, steps.y, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_Y), _BV(FT_BIT_STEP_Y)),
|
||||||
COMMAND_SET(delta.z, err_P.z, steps.z, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_Z), _BV(FT_BIT_STEP_Z)),
|
command_set[Z_AXIS](err_P.z, steps.z, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_Z), _BV(FT_BIT_STEP_Z)),
|
||||||
COMMAND_SET(delta.i, err_P.i, steps.i, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_I), _BV(FT_BIT_STEP_I)),
|
command_set[I_AXIS](err_P.i, steps.i, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_I), _BV(FT_BIT_STEP_I)),
|
||||||
COMMAND_SET(delta.j, err_P.j, steps.j, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_J), _BV(FT_BIT_STEP_J)),
|
command_set[J_AXIS](err_P.j, steps.j, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_J), _BV(FT_BIT_STEP_J)),
|
||||||
COMMAND_SET(delta.k, err_P.k, steps.k, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_K), _BV(FT_BIT_STEP_K)),
|
command_set[K_AXIS](err_P.k, steps.k, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_K), _BV(FT_BIT_STEP_K)),
|
||||||
COMMAND_SET(delta.u, err_P.u, steps.u, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_U), _BV(FT_BIT_STEP_U)),
|
command_set[U_AXIS](err_P.u, steps.u, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_U), _BV(FT_BIT_STEP_U)),
|
||||||
COMMAND_SET(delta.v, err_P.v, steps.v, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_V), _BV(FT_BIT_STEP_V)),
|
command_set[V_AXIS](err_P.v, steps.v, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_V), _BV(FT_BIT_STEP_V)),
|
||||||
COMMAND_SET(delta.w, err_P.w, steps.w, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_W), _BV(FT_BIT_STEP_W)),
|
command_set[W_AXIS](err_P.w, steps.w, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_W), _BV(FT_BIT_STEP_W)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!anyStep) {
|
if (++stepperCmdBuff_produceIdx == (FTM_STEPPERCMD_BUFF_SIZE))
|
||||||
nextStepTicks += (FTM_MIN_TICKS);
|
stepperCmdBuff_produceIdx = 0;
|
||||||
}
|
|
||||||
else {
|
|
||||||
stepperCmdBuff_StepRelativeTi[stepperCmdBuff_produceIdx] = nextStepTicks;
|
|
||||||
|
|
||||||
const uint8_t dir_index = stepperCmdBuff_produceIdx >> 3,
|
|
||||||
dir_bit = stepperCmdBuff_produceIdx & 0x7;
|
|
||||||
if (any_dirChange) {
|
|
||||||
|
|
||||||
SBI(stepperCmdBuff_ApplyDir[dir_index], dir_bit);
|
|
||||||
|
|
||||||
auto DIR_SET = [&](auto &d, auto &c, auto &b, auto bd) {
|
|
||||||
if (d > 0) { b |= bd; c = stepDirState_POS; } else { c = stepDirState_NEG; }
|
|
||||||
};
|
|
||||||
|
|
||||||
LOGICAL_AXIS_CODE(
|
|
||||||
DIR_SET(delta.e, dirState.e, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_E)),
|
|
||||||
DIR_SET(delta.x, dirState.x, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_X)),
|
|
||||||
DIR_SET(delta.y, dirState.y, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_Y)),
|
|
||||||
DIR_SET(delta.z, dirState.z, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_Z)),
|
|
||||||
DIR_SET(delta.i, dirState.i, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_I)),
|
|
||||||
DIR_SET(delta.j, dirState.j, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_J)),
|
|
||||||
DIR_SET(delta.k, dirState.k, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_K)),
|
|
||||||
DIR_SET(delta.u, dirState.u, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_U)),
|
|
||||||
DIR_SET(delta.v, dirState.v, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_V)),
|
|
||||||
DIR_SET(delta.w, dirState.w, stepperCmdBuff[stepperCmdBuff_produceIdx], _BV(FT_BIT_DIR_W)),
|
|
||||||
);
|
|
||||||
|
|
||||||
any_dirChange = false;
|
|
||||||
}
|
|
||||||
else { // ...no direction change.
|
|
||||||
CBI(stepperCmdBuff_ApplyDir[dir_index], dir_bit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stepperCmdBuff_produceIdx == (FTM_STEPPERCMD_BUFF_SIZE) - 1)
|
|
||||||
stepperCmdBuff_produceIdx = 0;
|
|
||||||
else
|
|
||||||
stepperCmdBuff_produceIdx++;
|
|
||||||
|
|
||||||
nextStepTicks = FTM_MIN_TICKS;
|
|
||||||
}
|
|
||||||
} // FTM_STEPS_PER_UNIT_TIME loop
|
} // FTM_STEPS_PER_UNIT_TIME loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,6 @@
|
||||||
|
|
||||||
#include "ft_types.h"
|
#include "ft_types.h"
|
||||||
|
|
||||||
#define FTM_STEPPERCMD_DIR_SIZE ((FTM_STEPPERCMD_BUFF_SIZE + 7) / 8)
|
|
||||||
|
|
||||||
#if HAS_X_AXIS && (HAS_Z_AXIS || HAS_EXTRUDERS)
|
#if HAS_X_AXIS && (HAS_Z_AXIS || HAS_EXTRUDERS)
|
||||||
#define HAS_DYNAMIC_FREQ 1
|
#define HAS_DYNAMIC_FREQ 1
|
||||||
#if HAS_Z_AXIS
|
#if HAS_Z_AXIS
|
||||||
|
@ -39,28 +37,30 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct FTConfig {
|
typedef struct FTConfig {
|
||||||
ftMotionMode_t mode = FTM_DEFAULT_MODE; // Mode / active compensation mode configuration.
|
ftMotionMode_t mode = FTM_DEFAULT_MODE; // Mode / active compensation mode configuration.
|
||||||
|
|
||||||
bool modeHasShaper() { return WITHIN(mode, 10U, 19U); }
|
bool modeHasShaper() { return WITHIN(mode, 10U, 19U); }
|
||||||
|
|
||||||
#if HAS_X_AXIS
|
#if HAS_X_AXIS
|
||||||
float baseFreq[1 + ENABLED(HAS_Y_AXIS)] = // Base frequency. [Hz]
|
float baseFreq[1 + ENABLED(HAS_Y_AXIS)] = // Base frequency. [Hz]
|
||||||
{ FTM_SHAPING_DEFAULT_X_FREQ OPTARG(HAS_Y_AXIS, FTM_SHAPING_DEFAULT_Y_FREQ) };
|
{ FTM_SHAPING_DEFAULT_X_FREQ OPTARG(HAS_Y_AXIS, FTM_SHAPING_DEFAULT_Y_FREQ) };
|
||||||
|
|
||||||
|
float zeta[1 + ENABLED(HAS_Y_AXIS)] = // Damping factor
|
||||||
|
{ FTM_SHAPING_ZETA_X OPTARG(HAS_Y_AXIS, FTM_SHAPING_ZETA_Y) };
|
||||||
|
float vtol[1 + ENABLED(HAS_Y_AXIS)] = // Vibration Level
|
||||||
|
{ FTM_SHAPING_V_TOL_X OPTARG(HAS_Y_AXIS, FTM_SHAPING_V_TOL_Y) };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float zeta = FTM_SHAPING_ZETA; // Damping factor
|
#if HAS_DYNAMIC_FREQ
|
||||||
float vtol = FTM_SHAPING_V_TOL; // Vibration Level
|
dynFreqMode_t dynFreqMode = FTM_DEFAULT_DYNFREQ_MODE; // Dynamic frequency mode configuration.
|
||||||
|
float dynFreqK[1 + ENABLED(HAS_Y_AXIS)] = { 0.0f }; // Scaling / gain for dynamic frequency. [Hz/mm] or [Hz/g]
|
||||||
#if HAS_DYNAMIC_FREQ
|
|
||||||
dynFreqMode_t dynFreqMode = FTM_DEFAULT_DYNFREQ_MODE; // Dynamic frequency mode configuration.
|
|
||||||
float dynFreqK[1 + ENABLED(HAS_Y_AXIS)] = { 0.0f }; // Scaling / gain for dynamic frequency. [Hz/mm] or [Hz/g]
|
|
||||||
#else
|
#else
|
||||||
static constexpr dynFreqMode_t dynFreqMode = dynFreqMode_DISABLED;
|
static constexpr dynFreqMode_t dynFreqMode = dynFreqMode_DISABLED;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAS_EXTRUDERS
|
#if HAS_EXTRUDERS
|
||||||
bool linearAdvEna = FTM_LINEAR_ADV_DEFAULT_ENA; // Linear advance enable configuration.
|
bool linearAdvEna = FTM_LINEAR_ADV_DEFAULT_ENA; // Linear advance enable configuration.
|
||||||
float linearAdvK = FTM_LINEAR_ADV_DEFAULT_K; // Linear advance gain.
|
float linearAdvK = FTM_LINEAR_ADV_DEFAULT_K; // Linear advance gain.
|
||||||
#endif
|
#endif
|
||||||
} ft_config_t;
|
} ft_config_t;
|
||||||
|
|
||||||
|
@ -78,8 +78,11 @@ class FTMotion {
|
||||||
TERN_(HAS_X_AXIS, cfg.baseFreq[X_AXIS] = FTM_SHAPING_DEFAULT_X_FREQ);
|
TERN_(HAS_X_AXIS, cfg.baseFreq[X_AXIS] = FTM_SHAPING_DEFAULT_X_FREQ);
|
||||||
TERN_(HAS_Y_AXIS, cfg.baseFreq[Y_AXIS] = FTM_SHAPING_DEFAULT_Y_FREQ);
|
TERN_(HAS_Y_AXIS, cfg.baseFreq[Y_AXIS] = FTM_SHAPING_DEFAULT_Y_FREQ);
|
||||||
|
|
||||||
cfg.zeta = FTM_SHAPING_ZETA; // Damping factor
|
TERN_(HAS_X_AXIS, cfg.zeta[X_AXIS] = FTM_SHAPING_ZETA_X);
|
||||||
cfg.vtol = FTM_SHAPING_V_TOL; // Vibration Level
|
TERN_(HAS_Y_AXIS, cfg.zeta[Y_AXIS] = FTM_SHAPING_ZETA_Y);
|
||||||
|
|
||||||
|
TERN_(HAS_X_AXIS, cfg.vtol[X_AXIS] = FTM_SHAPING_V_TOL_X);
|
||||||
|
TERN_(HAS_Y_AXIS, cfg.vtol[Y_AXIS] = FTM_SHAPING_V_TOL_Y);
|
||||||
|
|
||||||
#if HAS_DYNAMIC_FREQ
|
#if HAS_DYNAMIC_FREQ
|
||||||
cfg.dynFreqMode = FTM_DEFAULT_DYNFREQ_MODE;
|
cfg.dynFreqMode = FTM_DEFAULT_DYNFREQ_MODE;
|
||||||
|
@ -99,47 +102,45 @@ class FTMotion {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ft_command_t stepperCmdBuff[FTM_STEPPERCMD_BUFF_SIZE]; // Buffer of stepper commands.
|
static ft_command_t stepperCmdBuff[FTM_STEPPERCMD_BUFF_SIZE]; // Buffer of stepper commands.
|
||||||
static hal_timer_t stepperCmdBuff_StepRelativeTi[FTM_STEPPERCMD_BUFF_SIZE]; // Buffer of the stepper command timing.
|
static uint32_t stepperCmdBuff_produceIdx, // Index of next stepper command write to the buffer.
|
||||||
static uint8_t stepperCmdBuff_ApplyDir[FTM_STEPPERCMD_DIR_SIZE]; // Buffer of whether DIR needs to be updated.
|
stepperCmdBuff_consumeIdx; // Index of next stepper command read from the buffer.
|
||||||
static uint32_t stepperCmdBuff_produceIdx, // Index of next stepper command write to the buffer.
|
|
||||||
stepperCmdBuff_consumeIdx; // Index of next stepper command read from the buffer.
|
|
||||||
|
|
||||||
static bool sts_stepperBusy; // The stepper buffer has items and is in use.
|
static bool sts_stepperBusy; // The stepper buffer has items and is in use.
|
||||||
|
|
||||||
|
|
||||||
// Public methods
|
// Public methods
|
||||||
static void init();
|
static void init();
|
||||||
static void startBlockProc(block_t * const current_block); // Set controller states to begin processing a block.
|
static void startBlockProc(); // Set controller states to begin processing a block.
|
||||||
static bool getBlockProcDn() { return blockProcDn; } // Return true if the controller no longer needs the current block.
|
static bool getBlockProcDn() { return blockProcDn; } // Return true if the controller no longer needs the current block.
|
||||||
static void runoutBlock(); // Move any free data points to the stepper buffer even if a full batch isn't ready.
|
static void runoutBlock(); // Move any free data points to the stepper buffer even if a full batch isn't ready.
|
||||||
static void loop(); // Controller main, to be invoked from non-isr task.
|
static void loop(); // Controller main, to be invoked from non-isr task.
|
||||||
|
|
||||||
#if HAS_X_AXIS
|
#if HAS_X_AXIS
|
||||||
// Refresh the gains used by shaping functions.
|
// Refresh the gains used by shaping functions.
|
||||||
// To be called on init or mode or zeta change.
|
// To be called on init or mode or zeta change.
|
||||||
static void updateShapingA(const_float_t zeta=cfg.zeta, const_float_t vtol=cfg.vtol);
|
static void updateShapingA(float zeta[]=cfg.zeta, float vtol[]=cfg.vtol);
|
||||||
|
|
||||||
// Refresh the indices used by shaping functions.
|
// Refresh the indices used by shaping functions.
|
||||||
// To be called when frequencies change.
|
// To be called when frequencies change.
|
||||||
static void updateShapingN(const_float_t xf OPTARG(HAS_Y_AXIS, const_float_t yf), const_float_t zeta=cfg.zeta);
|
static void updateShapingN(const_float_t xf OPTARG(HAS_Y_AXIS, const_float_t yf), float zeta[]=cfg.zeta);
|
||||||
|
|
||||||
static void refreshShapingN() { updateShapingN(cfg.baseFreq[X_AXIS] OPTARG(HAS_Y_AXIS, cfg.baseFreq[Y_AXIS])); }
|
static void refreshShapingN() { updateShapingN(cfg.baseFreq[X_AXIS] OPTARG(HAS_Y_AXIS, cfg.baseFreq[Y_AXIS])); }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void reset(); // Resets all states of the fixed time conversion to defaults.
|
static void reset(); // Reset all states of the fixed time conversion to defaults.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static xyze_trajectory_t traj;
|
static xyze_trajectory_t traj;
|
||||||
static xyze_trajectoryMod_t trajMod;
|
static xyze_trajectoryMod_t trajMod;
|
||||||
|
static xyze_trajectoryWin_t trajWin;
|
||||||
|
|
||||||
static block_t *current_block_cpy;
|
|
||||||
static bool blockProcRdy, blockProcRdy_z1, blockProcDn;
|
static bool blockProcRdy, blockProcRdy_z1, blockProcDn;
|
||||||
static bool batchRdy, batchRdyForInterp;
|
static bool batchRdy, batchRdyForInterp;
|
||||||
static bool runoutEna;
|
static bool runoutEna;
|
||||||
static bool runout;
|
static bool blockDataIsRunout;
|
||||||
|
|
||||||
// Trapezoid data variables.
|
// Trapezoid data variables.
|
||||||
static xyze_pos_t startPosn, // (mm) Start position of block
|
static xyze_pos_t startPosn, // (mm) Start position of block
|
||||||
|
@ -154,6 +155,11 @@ class FTMotion {
|
||||||
static uint32_t N1, N2, N3;
|
static uint32_t N1, N2, N3;
|
||||||
static uint32_t max_intervals;
|
static uint32_t max_intervals;
|
||||||
|
|
||||||
|
static constexpr uint32_t _ftm_size = TERN(FTM_UNIFIED_BWS, FTM_BW_SIZE, FTM_BATCH_SIZE),
|
||||||
|
_ftm_wind = TERN(FTM_UNIFIED_BWS, 2, ceil((FTM_WINDOW_SIZE) / _ftm_size)),
|
||||||
|
shaper_intervals = _ftm_size * ceil((FTM_ZMAX) / _ftm_size),
|
||||||
|
min_max_intervals = _ftm_size * _ftm_wind;
|
||||||
|
|
||||||
// Make vector variables.
|
// Make vector variables.
|
||||||
static uint32_t makeVector_idx,
|
static uint32_t makeVector_idx,
|
||||||
makeVector_idx_z1,
|
makeVector_idx_z1,
|
||||||
|
@ -164,9 +170,6 @@ class FTMotion {
|
||||||
interpIdx_z1;
|
interpIdx_z1;
|
||||||
|
|
||||||
static xyze_long_t steps;
|
static xyze_long_t steps;
|
||||||
static xyze_stepDir_t dirState;
|
|
||||||
|
|
||||||
static hal_timer_t nextStepTicks;
|
|
||||||
|
|
||||||
// Shaping variables.
|
// Shaping variables.
|
||||||
#if HAS_X_AXIS
|
#if HAS_X_AXIS
|
||||||
|
@ -188,7 +191,7 @@ class FTMotion {
|
||||||
axis_shaping_t y;
|
axis_shaping_t y;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void updateShapingA(const_float_t zeta=cfg.zeta, const_float_t vtol=cfg.vtol);
|
void updateShapingA(float zeta[]=cfg.zeta, float vtol[]=cfg.vtol);
|
||||||
|
|
||||||
} shaping_t;
|
} shaping_t;
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,12 @@ typedef enum FXDTICtrlMode : uint8_t {
|
||||||
//ftMotionMode_ULENDO_FBS = 2U,
|
//ftMotionMode_ULENDO_FBS = 2U,
|
||||||
ftMotionMode_ZV = 10U,
|
ftMotionMode_ZV = 10U,
|
||||||
ftMotionMode_ZVD = 11U,
|
ftMotionMode_ZVD = 11U,
|
||||||
ftMotionMode_EI = 12U,
|
ftMotionMode_ZVDD = 12U,
|
||||||
ftMotionMode_2HEI = 13U,
|
ftMotionMode_ZVDDD = 13U,
|
||||||
ftMotionMode_3HEI = 14U,
|
ftMotionMode_EI = 14U,
|
||||||
ftMotionMode_MZV = 15U,
|
ftMotionMode_2HEI = 15U,
|
||||||
|
ftMotionMode_3HEI = 16U,
|
||||||
|
ftMotionMode_MZV = 17U,
|
||||||
//ftMotionMode_DISCTF = 20U
|
//ftMotionMode_DISCTF = 20U
|
||||||
} ftMotionMode_t;
|
} ftMotionMode_t;
|
||||||
|
|
||||||
|
@ -42,16 +44,17 @@ enum dynFreqMode_t : uint8_t {
|
||||||
dynFreqMode_MASS_BASED = 2U
|
dynFreqMode_MASS_BASED = 2U
|
||||||
};
|
};
|
||||||
|
|
||||||
enum stepDirState_t : uint8_t {
|
#define IS_EI_MODE(N) WITHIN(N, ftMotionMode_EI, ftMotionMode_3HEI)
|
||||||
stepDirState_NOT_SET = 0U,
|
|
||||||
stepDirState_POS = 1U,
|
|
||||||
stepDirState_NEG = 2U
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct XYZEarray<float, FTM_WINDOW_SIZE> xyze_trajectory_t;
|
#if ENABLED(FTM_UNIFIED_BWS)
|
||||||
typedef struct XYZEarray<float, FTM_BATCH_SIZE> xyze_trajectoryMod_t;
|
typedef struct XYZEarray<float, FTM_BW_SIZE> xyze_trajectory_t;
|
||||||
|
typedef struct XYZEarray<float, FTM_BW_SIZE> xyze_trajectoryMod_t;
|
||||||
typedef struct XYZEval<stepDirState_t> xyze_stepDir_t;
|
typedef struct XYZEarray<float, FTM_BW_SIZE> xyze_trajectoryWin_t;
|
||||||
|
#else
|
||||||
|
typedef struct XYZEarray<float, FTM_WINDOW_SIZE> xyze_trajectory_t;
|
||||||
|
typedef struct XYZEarray<float, FTM_BATCH_SIZE> xyze_trajectoryMod_t;
|
||||||
|
typedef struct XYZEarray<float, (FTM_WINDOW_SIZE - FTM_BATCH_SIZE)> xyze_trajectoryWin_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
LIST_N(DOUBLE(LOGICAL_AXES),
|
LIST_N(DOUBLE(LOGICAL_AXES),
|
||||||
|
|
|
@ -33,10 +33,6 @@
|
||||||
#include "../lcd/marlinui.h"
|
#include "../lcd/marlinui.h"
|
||||||
#include "../inc/MarlinConfig.h"
|
#include "../inc/MarlinConfig.h"
|
||||||
|
|
||||||
#if ENABLED(FT_MOTION)
|
|
||||||
#include "ft_motion.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if IS_SCARA
|
#if IS_SCARA
|
||||||
#include "../libs/buzzer.h"
|
#include "../libs/buzzer.h"
|
||||||
#include "../lcd/marlinui.h"
|
#include "../lcd/marlinui.h"
|
||||||
|
@ -2095,21 +2091,6 @@ void prepare_line_to_destination() {
|
||||||
|
|
||||||
void homeaxis(const AxisEnum axis) {
|
void homeaxis(const AxisEnum axis) {
|
||||||
|
|
||||||
#if ENABLED(FT_MOTION)
|
|
||||||
// Disable ft-motion for homing
|
|
||||||
struct OnExit {
|
|
||||||
ftMotionMode_t oldmm;
|
|
||||||
OnExit() {
|
|
||||||
oldmm = ftMotion.cfg.mode;
|
|
||||||
ftMotion.cfg.mode = ftMotionMode_DISABLED;
|
|
||||||
}
|
|
||||||
~OnExit() {
|
|
||||||
ftMotion.cfg.mode = oldmm;
|
|
||||||
ftMotion.init();
|
|
||||||
}
|
|
||||||
} on_exit;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ANY(MORGAN_SCARA, MP_SCARA)
|
#if ANY(MORGAN_SCARA, MP_SCARA)
|
||||||
// Only Z homing (with probe) is permitted
|
// Only Z homing (with probe) is permitted
|
||||||
if (axis != Z_AXIS) { BUZZ(100, 880); return; }
|
if (axis != Z_AXIS) { BUZZ(100, 880); return; }
|
||||||
|
|
|
@ -1492,11 +1492,6 @@ void Stepper::isr() {
|
||||||
uint8_t max_loops = 10;
|
uint8_t max_loops = 10;
|
||||||
|
|
||||||
#if ENABLED(FT_MOTION)
|
#if ENABLED(FT_MOTION)
|
||||||
static bool ftMotion_stepCmdRdy = false; // Indicates a step command was loaded from the
|
|
||||||
// buffers and is ready to be output.
|
|
||||||
static bool ftMotion_applyDir = false; // Indicates the DIR output should be set.
|
|
||||||
static ft_command_t ftMotion_stepCmd = 0U; // Storage for the step command to be output.
|
|
||||||
static uint32_t ftMotion_nextAuxISR = 0U; // Storage for the next ISR of the auxilliary tasks.
|
|
||||||
const bool using_ftMotion = ftMotion.cfg.mode;
|
const bool using_ftMotion = ftMotion.cfg.mode;
|
||||||
#else
|
#else
|
||||||
constexpr bool using_ftMotion = false;
|
constexpr bool using_ftMotion = false;
|
||||||
|
@ -1508,57 +1503,19 @@ void Stepper::isr() {
|
||||||
// Enable ISRs to reduce USART processing latency
|
// Enable ISRs to reduce USART processing latency
|
||||||
hal.isr_on();
|
hal.isr_on();
|
||||||
|
|
||||||
hal_timer_t interval;
|
hal_timer_t interval = 0;
|
||||||
|
|
||||||
#if ENABLED(FT_MOTION)
|
#if ENABLED(FT_MOTION)
|
||||||
|
|
||||||
if (using_ftMotion) {
|
if (using_ftMotion) {
|
||||||
if (!nextMainISR) {
|
if (!nextMainISR) {
|
||||||
if (abort_current_block) {
|
nextMainISR = FTM_MIN_TICKS;
|
||||||
ftMotion_stepCmdRdy = false; // If a command was ready, cancel it.
|
ftMotion_stepper();
|
||||||
ftMotion.sts_stepperBusy = false; // Set busy false to allow a reset.
|
|
||||||
nextMainISR = 0.01f * (STEPPER_TIMER_RATE); // Come back in 10 msec.
|
|
||||||
}
|
|
||||||
else { // !(abort_current_block)
|
|
||||||
if (ftMotion_stepCmdRdy) {
|
|
||||||
ftMotion_stepper(ftMotion_applyDir, ftMotion_stepCmd);
|
|
||||||
ftMotion_stepCmdRdy = false;
|
|
||||||
}
|
|
||||||
// Check if there is data in the buffers.
|
|
||||||
if (ftMotion.stepperCmdBuff_produceIdx != ftMotion.stepperCmdBuff_consumeIdx) {
|
|
||||||
|
|
||||||
ftMotion.sts_stepperBusy = true;
|
|
||||||
|
|
||||||
// "Pop" one command from the command buffer.
|
|
||||||
ftMotion_stepCmd = ftMotion.stepperCmdBuff[ftMotion.stepperCmdBuff_consumeIdx];
|
|
||||||
const uint8_t dir_index = ftMotion.stepperCmdBuff_consumeIdx >> 3,
|
|
||||||
dir_bit = ftMotion.stepperCmdBuff_consumeIdx & 0x7;
|
|
||||||
ftMotion_applyDir = TEST(ftMotion.stepperCmdBuff_ApplyDir[dir_index], dir_bit);
|
|
||||||
nextMainISR = ftMotion.stepperCmdBuff_StepRelativeTi[ftMotion.stepperCmdBuff_consumeIdx];
|
|
||||||
ftMotion_stepCmdRdy = true;
|
|
||||||
|
|
||||||
if (++ftMotion.stepperCmdBuff_consumeIdx == (FTM_STEPPERCMD_BUFF_SIZE))
|
|
||||||
ftMotion.stepperCmdBuff_consumeIdx = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
else { // Buffer empty.
|
|
||||||
ftMotion.sts_stepperBusy = false;
|
|
||||||
nextMainISR = 0.01f * (STEPPER_TIMER_RATE); // Come back in 10 msec.
|
|
||||||
}
|
|
||||||
} // !(abort_current_block)
|
|
||||||
} // if (!nextMainISR)
|
|
||||||
|
|
||||||
// Define 2.5 msec task for auxiliary functions.
|
|
||||||
if (!ftMotion_nextAuxISR) {
|
|
||||||
endstops.update();
|
endstops.update();
|
||||||
TERN_(BABYSTEPPING, if (babystep.has_steps()) babystepping_isr());
|
TERN_(BABYSTEPPING, if (babystep.has_steps()) babystepping_isr());
|
||||||
ftMotion_refreshAxisDidMove();
|
|
||||||
ftMotion_nextAuxISR = 0.0025f * (STEPPER_TIMER_RATE);
|
|
||||||
}
|
}
|
||||||
|
interval = nextMainISR;
|
||||||
interval = _MIN(nextMainISR, ftMotion_nextAuxISR);
|
|
||||||
nextMainISR -= interval;
|
nextMainISR -= interval;
|
||||||
ftMotion_nextAuxISR -= interval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -3365,6 +3322,28 @@ void Stepper::set_axis_position(const AxisEnum a, const int32_t &v) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLED(FT_MOTION)
|
||||||
|
|
||||||
|
void Stepper::ftMotion_syncPosition() {
|
||||||
|
//planner.synchronize(); planner already synchronized in M493
|
||||||
|
|
||||||
|
#ifdef __AVR__
|
||||||
|
// Protect the access to the position. Only required for AVR, as
|
||||||
|
// any 32bit CPU offers atomic access to 32bit variables
|
||||||
|
const bool was_enabled = suspend();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Update stepper positions from the planner
|
||||||
|
count_position = planner.position;
|
||||||
|
|
||||||
|
#ifdef __AVR__
|
||||||
|
// Reenable Stepper ISR
|
||||||
|
if (was_enabled) wake_up();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FT_MOTION
|
||||||
|
|
||||||
// Signal endstops were triggered - This function can be called from
|
// Signal endstops were triggered - This function can be called from
|
||||||
// an ISR context (Temperature, Stepper or limits ISR), so we must
|
// an ISR context (Temperature, Stepper or limits ISR), so we must
|
||||||
// be very careful here. If the interrupt being preempted was the
|
// be very careful here. If the interrupt being preempted was the
|
||||||
|
@ -3459,65 +3438,70 @@ void Stepper::report_positions() {
|
||||||
#if ENABLED(FT_MOTION)
|
#if ENABLED(FT_MOTION)
|
||||||
|
|
||||||
// Set stepper I/O for fixed time controller.
|
// Set stepper I/O for fixed time controller.
|
||||||
void Stepper::ftMotion_stepper(const bool applyDir, const ft_command_t command) {
|
void Stepper::ftMotion_stepper() {
|
||||||
|
|
||||||
|
// Check if the buffer is empty.
|
||||||
|
ftMotion.sts_stepperBusy = (ftMotion.stepperCmdBuff_produceIdx != ftMotion.stepperCmdBuff_consumeIdx);
|
||||||
|
if (!ftMotion.sts_stepperBusy) return;
|
||||||
|
|
||||||
|
// "Pop" one command from current motion buffer
|
||||||
|
// Use one byte to restore one stepper command in the format:
|
||||||
|
// |X_step|X_direction|Y_step|Y_direction|Z_step|Z_direction|E_step|E_direction|
|
||||||
|
const ft_command_t command = ftMotion.stepperCmdBuff[ftMotion.stepperCmdBuff_consumeIdx];
|
||||||
|
if (++ftMotion.stepperCmdBuff_consumeIdx == (FTM_STEPPERCMD_BUFF_SIZE)) ftMotion.stepperCmdBuff_consumeIdx = 0U;
|
||||||
|
|
||||||
|
if (abort_current_block) return;
|
||||||
|
|
||||||
USING_TIMED_PULSE();
|
USING_TIMED_PULSE();
|
||||||
|
|
||||||
const xyze_bool_t axis_step = LOGICAL_AXIS_ARRAY(
|
axis_did_move = LOGICAL_AXIS_ARRAY(
|
||||||
TEST(command, FT_BIT_STEP_E),
|
TEST(command, FT_BIT_STEP_E),
|
||||||
TEST(command, FT_BIT_STEP_X), TEST(command, FT_BIT_STEP_Y), TEST(command, FT_BIT_STEP_Z),
|
TEST(command, FT_BIT_STEP_X), TEST(command, FT_BIT_STEP_Y), TEST(command, FT_BIT_STEP_Z),
|
||||||
TEST(command, FT_BIT_STEP_I), TEST(command, FT_BIT_STEP_J), TEST(command, FT_BIT_STEP_K),
|
TEST(command, FT_BIT_STEP_I), TEST(command, FT_BIT_STEP_J), TEST(command, FT_BIT_STEP_K),
|
||||||
TEST(command, FT_BIT_STEP_U), TEST(command, FT_BIT_STEP_V), TEST(command, FT_BIT_STEP_W)
|
TEST(command, FT_BIT_STEP_U), TEST(command, FT_BIT_STEP_V), TEST(command, FT_BIT_STEP_W)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
last_direction_bits = LOGICAL_AXIS_ARRAY(
|
||||||
|
axis_did_move.e ? TEST(command, FT_BIT_DIR_E) : last_direction_bits.e,
|
||||||
|
axis_did_move.x ? TEST(command, FT_BIT_DIR_X) : last_direction_bits.x,
|
||||||
|
axis_did_move.y ? TEST(command, FT_BIT_DIR_Y) : last_direction_bits.y,
|
||||||
|
axis_did_move.z ? TEST(command, FT_BIT_DIR_Z) : last_direction_bits.z,
|
||||||
|
axis_did_move.i ? TEST(command, FT_BIT_DIR_I) : last_direction_bits.i,
|
||||||
|
axis_did_move.j ? TEST(command, FT_BIT_DIR_J) : last_direction_bits.j,
|
||||||
|
axis_did_move.k ? TEST(command, FT_BIT_DIR_K) : last_direction_bits.k,
|
||||||
|
axis_did_move.u ? TEST(command, FT_BIT_DIR_U) : last_direction_bits.u,
|
||||||
|
axis_did_move.v ? TEST(command, FT_BIT_DIR_V) : last_direction_bits.v,
|
||||||
|
axis_did_move.w ? TEST(command, FT_BIT_DIR_W) : last_direction_bits.w
|
||||||
|
);
|
||||||
|
|
||||||
// Apply directions (which will apply to the entire linear move)
|
// Apply directions (which will apply to the entire linear move)
|
||||||
AxisBits axis_dir = last_direction_bits;
|
LOGICAL_AXIS_CODE(
|
||||||
if (applyDir) {
|
E_APPLY_DIR(last_direction_bits.e, false),
|
||||||
axis_dir = LOGICAL_AXIS_ARRAY(
|
X_APPLY_DIR(last_direction_bits.x, false), Y_APPLY_DIR(last_direction_bits.y, false), Z_APPLY_DIR(last_direction_bits.z, false),
|
||||||
TEST(command, FT_BIT_DIR_E),
|
I_APPLY_DIR(last_direction_bits.i, false), J_APPLY_DIR(last_direction_bits.j, false), K_APPLY_DIR(last_direction_bits.k, false),
|
||||||
TEST(command, FT_BIT_DIR_X), TEST(command, FT_BIT_DIR_Y), TEST(command, FT_BIT_DIR_Z),
|
U_APPLY_DIR(last_direction_bits.u, false), V_APPLY_DIR(last_direction_bits.v, false), W_APPLY_DIR(last_direction_bits.w, false)
|
||||||
TEST(command, FT_BIT_DIR_I), TEST(command, FT_BIT_DIR_J), TEST(command, FT_BIT_DIR_K),
|
);
|
||||||
TEST(command, FT_BIT_DIR_U), TEST(command, FT_BIT_DIR_V), TEST(command, FT_BIT_DIR_W)
|
|
||||||
);
|
DIR_WAIT_AFTER();
|
||||||
LOGICAL_AXIS_CODE(
|
|
||||||
E_APPLY_DIR(axis_dir.e, false),
|
|
||||||
X_APPLY_DIR(axis_dir.x, false), Y_APPLY_DIR(axis_dir.y, false), Z_APPLY_DIR(axis_dir.z, false),
|
|
||||||
I_APPLY_DIR(axis_dir.i, false), J_APPLY_DIR(axis_dir.j, false), K_APPLY_DIR(axis_dir.k, false),
|
|
||||||
U_APPLY_DIR(axis_dir.u, false), V_APPLY_DIR(axis_dir.v, false), W_APPLY_DIR(axis_dir.w, false)
|
|
||||||
);
|
|
||||||
last_direction_bits = axis_dir;
|
|
||||||
DIR_WAIT_AFTER();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a step pulse
|
// Start a step pulse
|
||||||
LOGICAL_AXIS_CODE(
|
LOGICAL_AXIS_CODE(
|
||||||
if (axis_step.e) E_APPLY_STEP(STEP_STATE_E, false),
|
E_APPLY_STEP(axis_did_move.e, false),
|
||||||
if (axis_step.x) X_APPLY_STEP(STEP_STATE_X, false), if (axis_step.y) Y_APPLY_STEP(STEP_STATE_Y, false),
|
X_APPLY_STEP(axis_did_move.x, false), Y_APPLY_STEP(axis_did_move.y, false), Z_APPLY_STEP(axis_did_move.z, false),
|
||||||
if (axis_step.z) Z_APPLY_STEP(STEP_STATE_Z, false), if (axis_step.i) I_APPLY_STEP(STEP_STATE_I, false),
|
I_APPLY_STEP(axis_did_move.i, false), J_APPLY_STEP(axis_did_move.j, false), K_APPLY_STEP(axis_did_move.k, false),
|
||||||
if (axis_step.j) J_APPLY_STEP(STEP_STATE_J, false), if (axis_step.k) K_APPLY_STEP(STEP_STATE_K, false),
|
U_APPLY_STEP(axis_did_move.u, false), V_APPLY_STEP(axis_did_move.v, false), W_APPLY_STEP(axis_did_move.w, false)
|
||||||
if (axis_step.u) U_APPLY_STEP(STEP_STATE_U, false), if (axis_step.v) V_APPLY_STEP(STEP_STATE_V, false),
|
|
||||||
if (axis_step.w) W_APPLY_STEP(STEP_STATE_W, false)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Begin waiting for the minimum pulse duration
|
// Begin waiting for the minimum pulse duration
|
||||||
START_TIMED_PULSE();
|
START_TIMED_PULSE();
|
||||||
|
|
||||||
// Update axis direction adders
|
// Update step counts
|
||||||
count_direction = LOGICAL_AXIS_ARRAY(
|
|
||||||
int8_t(axis_dir.e ? 1 : -1),
|
|
||||||
int8_t(axis_dir.x ? 1 : -1), int8_t(axis_dir.y ? 1 : -1), int8_t(axis_dir.z ? 1 : -1),
|
|
||||||
int8_t(axis_dir.i ? 1 : -1), int8_t(axis_dir.j ? 1 : -1), int8_t(axis_dir.k ? 1 : -1),
|
|
||||||
int8_t(axis_dir.u ? 1 : -1), int8_t(axis_dir.v ? 1 : -1), int8_t(axis_dir.w ? 1 : -1)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update stepper counts - required for various operations
|
|
||||||
LOGICAL_AXIS_CODE(
|
LOGICAL_AXIS_CODE(
|
||||||
if (axis_step.e) count_position.e += count_direction.e,
|
if (axis_did_move.e) count_position.e += last_direction_bits.e ? 1 : -1, if (axis_did_move.x) count_position.x += last_direction_bits.x ? 1 : -1,
|
||||||
if (axis_step.x) count_position.x += count_direction.x, if (axis_step.y) count_position.y += count_direction.y,
|
if (axis_did_move.y) count_position.y += last_direction_bits.y ? 1 : -1, if (axis_did_move.z) count_position.z += last_direction_bits.z ? 1 : -1,
|
||||||
if (axis_step.z) count_position.z += count_direction.z, if (axis_step.i) count_position.i += count_direction.i,
|
if (axis_did_move.i) count_position.i += last_direction_bits.i ? 1 : -1, if (axis_did_move.j) count_position.j += last_direction_bits.j ? 1 : -1,
|
||||||
if (axis_step.j) count_position.j += count_direction.j, if (axis_step.k) count_position.k += count_direction.k,
|
if (axis_did_move.k) count_position.k += last_direction_bits.k ? 1 : -1, if (axis_did_move.u) count_position.u += last_direction_bits.u ? 1 : -1,
|
||||||
if (axis_step.u) count_position.u += count_direction.u, if (axis_step.v) count_position.v += count_direction.v,
|
if (axis_did_move.v) count_position.v += last_direction_bits.v ? 1 : -1, if (axis_did_move.w) count_position.w += last_direction_bits.w ? 1 : -1
|
||||||
if (axis_step.w) count_position.w += count_direction.w
|
|
||||||
);
|
);
|
||||||
|
|
||||||
#if HAS_EXTRUDERS
|
#if HAS_EXTRUDERS
|
||||||
|
@ -3532,10 +3516,10 @@ void Stepper::report_positions() {
|
||||||
|
|
||||||
// Only wait for axes without edge stepping
|
// Only wait for axes without edge stepping
|
||||||
const bool any_wait = false LOGICAL_AXIS_GANG(
|
const bool any_wait = false LOGICAL_AXIS_GANG(
|
||||||
|| (!e_axis_has_dedge && axis_step.e),
|
|| (!e_axis_has_dedge && axis_did_move.e),
|
||||||
|| (!AXIS_HAS_DEDGE(X) && axis_step.x), || (!AXIS_HAS_DEDGE(Y) && axis_step.y), || (!AXIS_HAS_DEDGE(Z) && axis_step.z),
|
|| (!AXIS_HAS_DEDGE(X) && axis_did_move.x), || (!AXIS_HAS_DEDGE(Y) && axis_did_move.y), || (!AXIS_HAS_DEDGE(Z) && axis_did_move.z),
|
||||||
|| (!AXIS_HAS_DEDGE(I) && axis_step.i), || (!AXIS_HAS_DEDGE(J) && axis_step.j), || (!AXIS_HAS_DEDGE(K) && axis_step.k),
|
|| (!AXIS_HAS_DEDGE(I) && axis_did_move.i), || (!AXIS_HAS_DEDGE(J) && axis_did_move.j), || (!AXIS_HAS_DEDGE(K) && axis_did_move.k),
|
||||||
|| (!AXIS_HAS_DEDGE(U) && axis_step.u), || (!AXIS_HAS_DEDGE(V) && axis_step.v), || (!AXIS_HAS_DEDGE(W) && axis_step.w)
|
|| (!AXIS_HAS_DEDGE(U) && axis_did_move.u), || (!AXIS_HAS_DEDGE(V) && axis_did_move.v), || (!AXIS_HAS_DEDGE(W) && axis_did_move.w)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Allow pulses to be registered by stepper drivers
|
// Allow pulses to be registered by stepper drivers
|
||||||
|
@ -3543,80 +3527,46 @@ void Stepper::report_positions() {
|
||||||
|
|
||||||
// Stop pulses. Axes with DEDGE will do nothing, assuming STEP_STATE_* is HIGH
|
// Stop pulses. Axes with DEDGE will do nothing, assuming STEP_STATE_* is HIGH
|
||||||
LOGICAL_AXIS_CODE(
|
LOGICAL_AXIS_CODE(
|
||||||
if (axis_step.e) E_APPLY_STEP(!STEP_STATE_E, false),
|
E_APPLY_STEP(!STEP_STATE_E, false),
|
||||||
if (axis_step.x) X_APPLY_STEP(!STEP_STATE_X, false), if (axis_step.y) Y_APPLY_STEP(!STEP_STATE_Y, false),
|
X_APPLY_STEP(!STEP_STATE_X, false), Y_APPLY_STEP(!STEP_STATE_Y, false), Z_APPLY_STEP(!STEP_STATE_Z, false),
|
||||||
if (axis_step.z) Z_APPLY_STEP(!STEP_STATE_Z, false), if (axis_step.i) I_APPLY_STEP(!STEP_STATE_I, false),
|
I_APPLY_STEP(!STEP_STATE_I, false), J_APPLY_STEP(!STEP_STATE_J, false), K_APPLY_STEP(!STEP_STATE_K, false),
|
||||||
if (axis_step.j) J_APPLY_STEP(!STEP_STATE_J, false), if (axis_step.k) K_APPLY_STEP(!STEP_STATE_K, false),
|
U_APPLY_STEP(!STEP_STATE_U, false), V_APPLY_STEP(!STEP_STATE_V, false), W_APPLY_STEP(!STEP_STATE_W, false)
|
||||||
if (axis_step.u) U_APPLY_STEP(!STEP_STATE_U, false), if (axis_step.v) V_APPLY_STEP(!STEP_STATE_V, false),
|
|
||||||
if (axis_step.w) W_APPLY_STEP(!STEP_STATE_W, false)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
} // Stepper::ftMotion_stepper
|
} // Stepper::ftMotion_stepper
|
||||||
|
|
||||||
void Stepper::ftMotion_BlockQueueUpdate() {
|
void Stepper::ftMotion_blockQueueUpdate() {
|
||||||
|
|
||||||
if (current_block) {
|
if (current_block) {
|
||||||
// If the current block is not done processing, return right away
|
// If the current block is not done processing, return right away
|
||||||
if (!ftMotion.getBlockProcDn()) return;
|
if (!ftMotion.getBlockProcDn()) return;
|
||||||
|
|
||||||
axis_did_move.reset();
|
axis_did_move.reset();
|
||||||
current_block = nullptr;
|
planner.release_current_block();
|
||||||
discard_current_block();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!current_block) { // No current block
|
// Check the buffer for a new block
|
||||||
|
current_block = planner.get_current_block();
|
||||||
|
|
||||||
// Check the buffer for a new block
|
if (current_block) {
|
||||||
current_block = planner.get_current_block();
|
// Sync block? Sync the stepper counts and return
|
||||||
|
while (current_block->is_sync()) {
|
||||||
|
TERN_(LASER_FEATURE, if (!(current_block->is_fan_sync() || current_block->is_pwr_sync()))) _set_position(current_block->position);
|
||||||
|
|
||||||
|
planner.release_current_block();
|
||||||
|
|
||||||
if (current_block) {
|
// Try to get a new block
|
||||||
// Sync block? Sync the stepper counts and return
|
if (!(current_block = planner.get_current_block()))
|
||||||
while (current_block->is_sync()) {
|
return; // No queued blocks.
|
||||||
if (!(current_block->is_fan_sync() || current_block->is_pwr_sync())) _set_position(current_block->position);
|
|
||||||
discard_current_block();
|
|
||||||
|
|
||||||
// Try to get a new block
|
|
||||||
if (!(current_block = planner.get_current_block()))
|
|
||||||
return; // No more queued movements!image.png
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is needed by motor_direction() and subsequently bed leveling (somehow)
|
|
||||||
// update it here, even though it will may be out of sync with step commands
|
|
||||||
last_direction_bits = current_block->direction_bits;
|
|
||||||
|
|
||||||
ftMotion.startBlockProc(current_block);
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ftMotion.runoutBlock();
|
|
||||||
return; // No queued blocks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // if (!current_block)
|
ftMotion.startBlockProc();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
} // Stepper::ftMotion_BlockQueueUpdate()
|
ftMotion.runoutBlock();
|
||||||
|
|
||||||
// Debounces the axis move indication to account for potential
|
} // Stepper::ftMotion_blockQueueUpdate()
|
||||||
// delay between the block information and the stepper commands
|
|
||||||
void Stepper::ftMotion_refreshAxisDidMove() {
|
|
||||||
|
|
||||||
// Set the debounce time in seconds.
|
|
||||||
#define AXIS_DID_MOVE_DEB 5 // TODO: The debounce time should be calculated if possible,
|
|
||||||
// or the set conditions should be changed from the block to
|
|
||||||
// the motion trajectory or motor commands.
|
|
||||||
|
|
||||||
AxisBits didmove;
|
|
||||||
static abce_ulong_t debounce{0};
|
|
||||||
auto debounce_axis = [&](const AxisEnum axis) {
|
|
||||||
if (current_block->steps[axis]) debounce[axis] = (AXIS_DID_MOVE_DEB) * 400; // divide by 0.0025f */
|
|
||||||
if (debounce[axis]) { didmove.bset(axis); debounce[axis]--; }
|
|
||||||
};
|
|
||||||
#define _DEBOUNCE(N) debounce_axis(AxisEnum(N));
|
|
||||||
|
|
||||||
if (current_block) { REPEAT(LOGICAL_AXES, _DEBOUNCE); }
|
|
||||||
|
|
||||||
axis_did_move = didmove;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FT_MOTION
|
#endif // FT_MOTION
|
||||||
|
|
||||||
|
|
|
@ -655,7 +655,9 @@ class Stepper {
|
||||||
|
|
||||||
#if ENABLED(FT_MOTION)
|
#if ENABLED(FT_MOTION)
|
||||||
// Manage the planner
|
// Manage the planner
|
||||||
static void ftMotion_BlockQueueUpdate();
|
static void ftMotion_blockQueueUpdate();
|
||||||
|
// Set current position in steps when reset flag is set in M493 and planner already synchronized
|
||||||
|
static void ftMotion_syncPosition();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAS_ZV_SHAPING
|
#if HAS_ZV_SHAPING
|
||||||
|
@ -694,8 +696,7 @@ class Stepper {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ENABLED(FT_MOTION)
|
#if ENABLED(FT_MOTION)
|
||||||
static void ftMotion_stepper(const bool applyDir, const ft_command_t command);
|
static void ftMotion_stepper();
|
||||||
static void ftMotion_refreshAxisDidMove();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue