forked from Mirrors/Marlin
🚸 Optimize FTM menu code, use some setters (#28170)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
@@ -177,6 +177,7 @@ template <class L, class R> struct IF<true, L, R> { typedef L type; };
|
||||
#define CARTES_CODE(x,y,z,e) XYZ_CODE(x,y,z) CODE_ITEM_E(e)
|
||||
#define CARTES_GANG(x,y,z,e) XYZ_GANG(x,y,z) GANG_ITEM_E(e)
|
||||
#define CARTES_AXIS_NAMES CARTES_LIST(X,Y,Z,E)
|
||||
#define CARTES_AXIS_NAMES_LC CARTES_LIST(x,y,z,e)
|
||||
#define CARTES_MAP(F) MAP(F, CARTES_AXIS_NAMES)
|
||||
#if CARTES_COUNT
|
||||
#define CARTES_COMMA ,
|
||||
|
||||
@@ -215,8 +215,7 @@ void GcodeSuite::M493() {
|
||||
if (parser.seen('S')) {
|
||||
const bool active = parser.value_bool();
|
||||
if (active != c.active) {
|
||||
stepper.ftMotion_syncPosition();
|
||||
c.active = active;
|
||||
ftMotion.toggle();
|
||||
flag.report = true;
|
||||
}
|
||||
}
|
||||
@@ -258,21 +257,10 @@ void GcodeSuite::M493() {
|
||||
// Dynamic frequency mode parameter.
|
||||
if (parser.seenval('D')) {
|
||||
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y) || AXIS_IS_SHAPING(Z) || AXIS_IS_SHAPING(E)) {
|
||||
const dynFreqMode_t val = dynFreqMode_t(parser.value_byte());
|
||||
switch (val) {
|
||||
#if HAS_DYNAMIC_FREQ_MM
|
||||
case dynFreqMode_Z_BASED:
|
||||
#endif
|
||||
#if HAS_DYNAMIC_FREQ_G
|
||||
case dynFreqMode_MASS_BASED:
|
||||
#endif
|
||||
case dynFreqMode_DISABLED:
|
||||
c.dynFreqMode = val;
|
||||
flag.report = true;
|
||||
break;
|
||||
default:
|
||||
SERIAL_ECHOLN(F("?Invalid "), F("(D)ynamic Frequency Mode value."));
|
||||
break;
|
||||
switch (c.setDynFreqMode(parser.value_byte())) {
|
||||
case 0: break; // Same value, no update
|
||||
case 1: flag.report = true; break; // New value, updated
|
||||
default: SERIAL_ECHOLN(F("?Invalid "), F("(D)ynamic Frequency Mode value.")); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -89,12 +89,8 @@ void GcodeSuite::M494() {
|
||||
|
||||
// Parse trajectory type parameter.
|
||||
if (parser.seenval('T')) {
|
||||
const int val = parser.value_int();
|
||||
if (WITHIN(val, 0, 2)) {
|
||||
planner.synchronize();
|
||||
ftMotion.setTrajectoryType((TrajectoryType)val);
|
||||
if (ftMotion.updateTrajectoryType(TrajectoryType(parser.value_int())))
|
||||
report = true;
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLN(F("?Invalid "), F("(T)rajectory type value. Use 0=TRAPEZOIDAL, 1=POLY5, 2=POLY6"));
|
||||
}
|
||||
@@ -116,11 +112,8 @@ void GcodeSuite::M494() {
|
||||
|
||||
#define SMOOTH_SET(A,N) \
|
||||
if (parser.seenval(CHARIFY(A))) { \
|
||||
const float val = parser.value_float(); \
|
||||
if (WITHIN(val, 0.0f, FTM_MAX_SMOOTHING_TIME)) { \
|
||||
ftMotion.set_smoothing_time(_AXIS(A), val); \
|
||||
if (ftMotion.set_smoothing_time(_AXIS(A), parser.value_float())) \
|
||||
report = true; \
|
||||
} \
|
||||
else \
|
||||
SERIAL_ECHOLNPGM("?Invalid ", C(N), " smoothing time (", C(CHARIFY(A)), ") value."); \
|
||||
}
|
||||
|
||||
@@ -4483,7 +4483,7 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
|
||||
#error "FT_MOTION does not currently support MIXING_EXTRUDER."
|
||||
#endif
|
||||
#if !HAS_X_AXIS
|
||||
static_assert(FTM_DEFAULT_SHAPER_X != ftMotionShaper_NONE, "Without any linear axes FTM_DEFAULT_SHAPER_X must be ftMotionShaper_NONE.");
|
||||
static_assert(FTM_DEFAULT_SHAPER_X == ftMotionShaper_NONE, "Without any linear axes FTM_DEFAULT_SHAPER_X must be ftMotionShaper_NONE.");
|
||||
#endif
|
||||
#if HAS_DYNAMIC_FREQ_MM
|
||||
static_assert(FTM_DEFAULT_DYNFREQ_MODE != dynFreqMode_Z_BASED, "dynFreqMode_Z_BASED requires a Z axis.");
|
||||
|
||||
@@ -947,6 +947,7 @@ namespace LanguageNarrow_en {
|
||||
LSTR MSG_FTM_VTOL_N = _UxGT("@ Vib. Level");
|
||||
LSTR MSG_FTM_SMOOTH_TIME_N = _UxGT("@ Smoothing Time");
|
||||
LSTR MSG_FTM_POLY6_OVERSHOOT = _UxGT("@ Poly6 Overshoot");
|
||||
LSTR MSG_FTM_CONFIGURE_AXIS_N = _UxGT("Configure @ Axis");
|
||||
|
||||
LSTR MSG_FTM_RESONANCE_TEST = _UxGT("Resonance Test");
|
||||
LSTR MSG_FTM_RT_RUNNING = _UxGT("Res. Test Running...");
|
||||
|
||||
@@ -59,8 +59,8 @@ class MenuItemBase {
|
||||
static const char* itemStringC;
|
||||
|
||||
// Store an index and string for later substitution
|
||||
FORCE_INLINE static void init(const int8_t ind=0, FSTR_P const fstr=nullptr) { itemIndex = ind; itemStringF = fstr; itemStringC = nullptr; }
|
||||
FORCE_INLINE static void init(const int8_t ind, const char * const cstr) { itemIndex = ind; itemStringC = cstr; itemStringF = nullptr; }
|
||||
FORCE_INLINE static void init(const int8_t ind=-1, FSTR_P const fstr=nullptr) { itemStringF = fstr; itemStringC = nullptr; if (ind >= 0) itemIndex = ind; }
|
||||
FORCE_INLINE static void init(const int8_t ind, const char * const cstr) { itemStringC = cstr; itemStringF = nullptr; if (ind >= 0) itemIndex = ind; }
|
||||
|
||||
// Implementation-specific:
|
||||
// Draw an item either selected (pre_char) or not (space) with post_char
|
||||
|
||||
@@ -462,9 +462,11 @@ class MenuItem_bool : public MenuEditItemBase {
|
||||
#elif ENABLED(GENERIC_BACK_MENU_ITEM)
|
||||
#define BACK_ITEM_F(V...) MENU_ITEM_F(back, GET_TEXT_F(MSG_BACK))
|
||||
#define BACK_ITEM(V...) MENU_ITEM(back, MSG_BACK)
|
||||
#define BACK_ITEM_N BACK_ITEM
|
||||
#else
|
||||
#define BACK_ITEM_F(FLABEL) MENU_ITEM_F(back, FLABEL)
|
||||
#define BACK_ITEM(LABEL) MENU_ITEM(back, LABEL)
|
||||
#define BACK_ITEM_N(N, LABEL) MENU_ITEM_N(back, N, LABEL)
|
||||
#endif
|
||||
|
||||
#define ACTION_ITEM_N_S_F(N, S, FLABEL, ACTION) MENU_ITEM_N_S_F(function, N, S, FLABEL, ACTION)
|
||||
|
||||
@@ -331,47 +331,57 @@ void menu_move() {
|
||||
FSTR_P get_dyn_freq_mode_name() {
|
||||
switch (ftMotion.cfg.dynFreqMode) {
|
||||
default:
|
||||
case dynFreqMode_DISABLED: return GET_TEXT_F(MSG_LCD_OFF);
|
||||
case dynFreqMode_Z_BASED: return GET_TEXT_F(MSG_FTM_Z_BASED);
|
||||
case dynFreqMode_MASS_BASED: return GET_TEXT_F(MSG_FTM_MASS_BASED);
|
||||
case dynFreqMode_DISABLED: return GET_TEXT_F(MSG_LCD_OFF);
|
||||
#if HAS_DYNAMIC_FREQ_MM
|
||||
case dynFreqMode_Z_BASED: return GET_TEXT_F(MSG_FTM_Z_BASED);
|
||||
#endif
|
||||
#if HAS_DYNAMIC_FREQ_G
|
||||
case dynFreqMode_MASS_BASED: return GET_TEXT_F(MSG_FTM_MASS_BASED);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ftm_menu_set_shaper(ftMotionShaper_t &outShaper, const ftMotionShaper_t s) {
|
||||
outShaper = s;
|
||||
void ftm_menu_set_shaper(const ftMotionShaper_t s) {
|
||||
ftMotion.cfg.shaper[MenuItemBase::itemIndex] = s;
|
||||
ftMotion.update_shaping_params();
|
||||
ui.go_back();
|
||||
}
|
||||
|
||||
#define MENU_FTM_SHAPER(A) \
|
||||
inline void menu_ftm_shaper_##A() { \
|
||||
const ftMotionShaper_t shaper = ftMotion.cfg.shaper.A; \
|
||||
START_MENU(); \
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION); \
|
||||
if (shaper != ftMotionShaper_NONE) ACTION_ITEM(MSG_LCD_OFF, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_NONE ); }); \
|
||||
if (shaper != ftMotionShaper_ZV) ACTION_ITEM(MSG_FTM_ZV, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_ZV ); }); \
|
||||
if (shaper != ftMotionShaper_ZVD) ACTION_ITEM(MSG_FTM_ZVD, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_ZVD ); }); \
|
||||
if (shaper != ftMotionShaper_ZVDD) ACTION_ITEM(MSG_FTM_ZVDD, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_ZVDD ); }); \
|
||||
if (shaper != ftMotionShaper_ZVDDD) ACTION_ITEM(MSG_FTM_ZVDDD,[]{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_ZVDDD ); }); \
|
||||
if (shaper != ftMotionShaper_EI) ACTION_ITEM(MSG_FTM_EI, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_EI ); }); \
|
||||
if (shaper != ftMotionShaper_2HEI) ACTION_ITEM(MSG_FTM_2HEI, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_2HEI ); }); \
|
||||
if (shaper != ftMotionShaper_3HEI) ACTION_ITEM(MSG_FTM_3HEI, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_3HEI ); }); \
|
||||
if (shaper != ftMotionShaper_MZV) ACTION_ITEM(MSG_FTM_MZV, []{ ftm_menu_set_shaper(ftMotion.cfg.shaper.A, ftMotionShaper_MZV ); }); \
|
||||
END_MENU(); \
|
||||
}
|
||||
void menu_ftm_shaper() {
|
||||
const int8_t axis = MenuItemBase::itemIndex;
|
||||
const ftMotionShaper_t shaper = ftMotion.cfg.shaper[axis];
|
||||
|
||||
START_MENU();
|
||||
BACK_ITEM_N(axis, MSG_FTM_CONFIGURE_AXIS_N);
|
||||
|
||||
if (shaper != ftMotionShaper_NONE) ACTION_ITEM_N(axis, MSG_LCD_OFF, []{ ftm_menu_set_shaper(ftMotionShaper_NONE) ; });
|
||||
if (shaper != ftMotionShaper_ZV) ACTION_ITEM_N(axis, MSG_FTM_ZV, []{ ftm_menu_set_shaper(ftMotionShaper_ZV) ; });
|
||||
if (shaper != ftMotionShaper_ZVD) ACTION_ITEM_N(axis, MSG_FTM_ZVD, []{ ftm_menu_set_shaper(ftMotionShaper_ZVD) ; });
|
||||
if (shaper != ftMotionShaper_ZVDD) ACTION_ITEM_N(axis, MSG_FTM_ZVDD, []{ ftm_menu_set_shaper(ftMotionShaper_ZVDD) ; });
|
||||
if (shaper != ftMotionShaper_ZVDDD) ACTION_ITEM_N(axis, MSG_FTM_ZVDDD,[]{ ftm_menu_set_shaper(ftMotionShaper_ZVDDD) ; });
|
||||
if (shaper != ftMotionShaper_EI) ACTION_ITEM_N(axis, MSG_FTM_EI, []{ ftm_menu_set_shaper(ftMotionShaper_EI) ; });
|
||||
if (shaper != ftMotionShaper_2HEI) ACTION_ITEM_N(axis, MSG_FTM_2HEI, []{ ftm_menu_set_shaper(ftMotionShaper_2HEI) ; });
|
||||
if (shaper != ftMotionShaper_3HEI) ACTION_ITEM_N(axis, MSG_FTM_3HEI, []{ ftm_menu_set_shaper(ftMotionShaper_3HEI) ; });
|
||||
if (shaper != ftMotionShaper_MZV) ACTION_ITEM_N(axis, MSG_FTM_MZV, []{ ftm_menu_set_shaper(ftMotionShaper_MZV) ; });
|
||||
|
||||
SHAPED_MAP(MENU_FTM_SHAPER);
|
||||
#if ENABLED(FTM_POLYS)
|
||||
void menu_ftm_trajectory_generator() {
|
||||
const TrajectoryType current_type = ftMotion.getTrajectoryType();
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION);
|
||||
if (current_type != TrajectoryType::TRAPEZOIDAL) ACTION_ITEM(MSG_FTM_TRAPEZOIDAL, []{ planner.synchronize(); ftMotion.setTrajectoryType(TrajectoryType::TRAPEZOIDAL); ui.go_back(); });
|
||||
if (current_type != TrajectoryType::POLY5) ACTION_ITEM(MSG_FTM_POLY5, []{ planner.synchronize(); ftMotion.setTrajectoryType(TrajectoryType::POLY5); ui.go_back(); });
|
||||
if (current_type != TrajectoryType::POLY6) ACTION_ITEM(MSG_FTM_POLY6, []{ planner.synchronize(); ftMotion.setTrajectoryType(TrajectoryType::POLY6); ui.go_back(); });
|
||||
END_MENU();
|
||||
}
|
||||
|
||||
#if ENABLED(FTM_POLYS)
|
||||
|
||||
void menu_ftm_trajectory_generator() {
|
||||
const TrajectoryType traj_type = ftMotion.getTrajectoryType();
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION);
|
||||
|
||||
if (traj_type != TrajectoryType::TRAPEZOIDAL) ACTION_ITEM(MSG_FTM_TRAPEZOIDAL, []{ ftMotion.updateTrajectoryType(TrajectoryType::TRAPEZOIDAL); ui.go_back(); });
|
||||
if (traj_type != TrajectoryType::POLY5) ACTION_ITEM(MSG_FTM_POLY5, []{ ftMotion.updateTrajectoryType(TrajectoryType::POLY5); ui.go_back(); });
|
||||
if (traj_type != TrajectoryType::POLY6) ACTION_ITEM(MSG_FTM_POLY6, []{ ftMotion.updateTrajectoryType(TrajectoryType::POLY6); ui.go_back(); });
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
|
||||
#endif // FTM_POLYS
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
@@ -401,6 +411,7 @@ void menu_move() {
|
||||
GCODES_ITEM_N(Z_AXIS, MSG_FTM_RT_START_N, F("M495 Z S"));
|
||||
SUBMENU(MSG_FTM_RETRIEVE_FREQ, menu_ftm_resonance_freq);
|
||||
}
|
||||
|
||||
END_MENU();
|
||||
}
|
||||
|
||||
@@ -412,14 +423,14 @@ void menu_move() {
|
||||
const dynFreqMode_t dmode = ftMotion.cfg.dynFreqMode;
|
||||
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION);
|
||||
BACK_ITEM_N(MenuItemBase::itemIndex, MSG_FTM_CONFIGURE_AXIS_N);
|
||||
|
||||
if (dmode != dynFreqMode_DISABLED) ACTION_ITEM(MSG_LCD_OFF, []{ ftMotion.cfg.dynFreqMode = dynFreqMode_DISABLED; ui.go_back(); });
|
||||
if (dmode != dynFreqMode_DISABLED) ACTION_ITEM(MSG_LCD_OFF, []{ (void)ftMotion.cfg.setDynFreqMode(dynFreqMode_DISABLED); ui.go_back(); });
|
||||
#if HAS_DYNAMIC_FREQ_MM
|
||||
if (dmode != dynFreqMode_Z_BASED) ACTION_ITEM(MSG_FTM_Z_BASED, []{ ftMotion.cfg.dynFreqMode = dynFreqMode_Z_BASED; ui.go_back(); });
|
||||
if (dmode != dynFreqMode_Z_BASED) ACTION_ITEM(MSG_FTM_Z_BASED, []{ (void)ftMotion.cfg.setDynFreqMode(dynFreqMode_Z_BASED); ui.go_back(); });
|
||||
#endif
|
||||
#if HAS_DYNAMIC_FREQ_G
|
||||
if (dmode != dynFreqMode_MASS_BASED) ACTION_ITEM(MSG_FTM_MASS_BASED, []{ ftMotion.cfg.dynFreqMode = dynFreqMode_MASS_BASED; ui.go_back(); });
|
||||
if (dmode != dynFreqMode_MASS_BASED) ACTION_ITEM(MSG_FTM_MASS_BASED, []{ (void)ftMotion.cfg.setDynFreqMode(dynFreqMode_MASS_BASED); ui.go_back(); });
|
||||
#endif
|
||||
|
||||
END_MENU();
|
||||
@@ -427,75 +438,43 @@ void menu_move() {
|
||||
|
||||
#endif // HAS_DYNAMIC_FREQ
|
||||
|
||||
// Suppress warning about storing a stack address in a static string pointer
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdangling-pointer"
|
||||
|
||||
#if ALL(__AVR__, HAS_MARLINUI_U8GLIB) && DISABLED(OPTIMIZE_FT_MOTION_FOR_SIZE)
|
||||
#define CACHE_FOR_SPEED 1
|
||||
#endif
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
#define _SMOO_MENU_ITEM(A) do{ \
|
||||
editable.decimal = c.smoothingTime.A; \
|
||||
EDIT_ITEM_FAST_N(float43, _AXIS(A), MSG_FTM_SMOOTH_TIME_N, &editable.decimal, 0.0f, FTM_MAX_SMOOTHING_TIME, []{ ftMotion.set_smoothing_time(_AXIS(A), editable.decimal); }); \
|
||||
}while(0);
|
||||
#endif
|
||||
|
||||
void menu_ft_motion() {
|
||||
// Define stuff ahead of the menu loop
|
||||
void menu_ftm_axis(const AxisEnum axis) {
|
||||
ft_config_t &c = ftMotion.cfg;
|
||||
|
||||
#ifdef __AVR__
|
||||
// Copy Flash strings to RAM for C-string substitution
|
||||
// For U8G paged rendering check and skip extra string copy
|
||||
#if HAS_X_AXIS
|
||||
MString<20> shaper_name;
|
||||
#if CACHE_FOR_SPEED
|
||||
int8_t prev_a = -1;
|
||||
#endif
|
||||
auto _shaper_name = [&](const AxisEnum a) {
|
||||
if (TERN1(CACHE_FOR_SPEED, a != prev_a)) {
|
||||
TERN_(CACHE_FOR_SPEED, prev_a = a);
|
||||
shaper_name = get_shaper_name(a);
|
||||
}
|
||||
return shaper_name;
|
||||
};
|
||||
#endif
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
MString<20> dmode;
|
||||
#if CACHE_FOR_SPEED
|
||||
bool got_d = false;
|
||||
#endif
|
||||
auto _dmode = [&]{
|
||||
if (TERN1(CACHE_FOR_SPEED, !got_d)) {
|
||||
TERN_(CACHE_FOR_SPEED, got_d = true);
|
||||
dmode = get_dyn_freq_mode_name();
|
||||
}
|
||||
return dmode;
|
||||
};
|
||||
#endif
|
||||
MString<20> traj_name;
|
||||
#if CACHE_FOR_SPEED
|
||||
bool got_t = false;
|
||||
#endif
|
||||
auto _traj_name = [&]{
|
||||
if (TERN1(CACHE_FOR_SPEED, !got_t)) {
|
||||
TERN_(CACHE_FOR_SPEED, got_t = true);
|
||||
traj_name = ftMotion.getTrajectoryName();
|
||||
}
|
||||
return traj_name;
|
||||
};
|
||||
#else
|
||||
auto _shaper_name = [](const AxisEnum a) { return get_shaper_name(a); };
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
auto _dmode = []{ return get_dyn_freq_mode_name(); };
|
||||
#endif
|
||||
#if ENABLED(FTM_POLYS)
|
||||
auto _traj_name = []{ return ftMotion.getTrajectoryName(); };
|
||||
#endif
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_FIXED_TIME_MOTION);
|
||||
|
||||
if (axis == X_AXIS || axis == Y_AXIS || TERN0(FTM_SHAPER_Z, axis == Z_AXIS) || TERN0(FTM_SHAPER_E, axis == E_AXIS)) {
|
||||
SUBMENU_N_S(axis, get_shaper_name(axis), MSG_FTM_CMPN_MODE, menu_ftm_shaper);
|
||||
if (IS_SHAPING(c.shaper[axis])) {
|
||||
EDIT_ITEM_FAST_N(float42_52, axis, MSG_FTM_BASE_FREQ_N, &c.baseFreq[axis], FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params);
|
||||
EDIT_ITEM_FAST_N(float42_52, axis, MSG_FTM_ZETA_N, &c.zeta[axis], 0.0f, 1.0f, ftMotion.update_shaping_params);
|
||||
if (IS_EISHAPING(c.shaper[axis]))
|
||||
EDIT_ITEM_FAST_N(float42_52, axis, MSG_FTM_VTOL_N, &c.vtol[axis], 0.0f, 1.0f, ftMotion.update_shaping_params);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
editable.decimal = c.smoothingTime[axis];
|
||||
EDIT_ITEM_FAST_N(float43, axis, MSG_FTM_SMOOTH_TIME_N, &editable.decimal, 0.0f, FTM_MAX_SMOOTHING_TIME, []{ (void)ftMotion.set_smoothing_time(AxisEnum(MenuItemBase::itemIndex), editable.decimal); });
|
||||
#endif
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
if (axis == X_AXIS || axis == Y_AXIS) {
|
||||
SUBMENU_N_S(axis, get_dyn_freq_mode_name(), MSG_FTM_DYN_MODE, menu_ftm_dyn_mode);
|
||||
if (c.dynFreqMode != dynFreqMode_DISABLED)
|
||||
EDIT_ITEM_FAST_N(float42_52, axis, MSG_FTM_DFREQ_K_N, &c.dynFreqK[axis], 0.0f, 20.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
END_MENU();
|
||||
} // menu_ftm_axis
|
||||
|
||||
#define _FTM_AXIS_SUBMENU(A) SUBMENU_N(_AXIS(A), MSG_FTM_CONFIGURE_AXIS_N, []{ menu_ftm_axis(_AXIS(A)); });
|
||||
|
||||
void menu_ft_motion() {
|
||||
ft_config_t &c = ftMotion.cfg;
|
||||
|
||||
START_MENU();
|
||||
BACK_ITEM(MSG_MOTION);
|
||||
|
||||
@@ -505,41 +484,23 @@ void menu_move() {
|
||||
#endif
|
||||
|
||||
// Show only when FT Motion is active (or optionally always show)
|
||||
if (c.active || ENABLED(FT_MOTION_NO_MENU_TOGGLE)) {
|
||||
if (TERN(FT_MOTION_NO_MENU_TOGGLE, true, c.active)) {
|
||||
|
||||
#if ENABLED(FTM_POLYS)
|
||||
SUBMENU_S(_traj_name(), MSG_FTM_TRAJECTORY, menu_ftm_trajectory_generator);
|
||||
SUBMENU_S(ftMotion.getTrajectoryName(), MSG_FTM_TRAJECTORY, menu_ftm_trajectory_generator);
|
||||
if (ftMotion.getTrajectoryType() == TrajectoryType::POLY6)
|
||||
EDIT_ITEM(float42_52, MSG_FTM_POLY6_OVERSHOOT, &c.poly6_acceleration_overshoot, 1.25f, 1.875f);
|
||||
#endif
|
||||
|
||||
#define SHAPER_MENU_ITEM(A) \
|
||||
SUBMENU_N_S(_AXIS(A), _shaper_name(_AXIS(A)), MSG_FTM_CMPN_MODE, menu_ftm_shaper_##A); \
|
||||
if (AXIS_IS_SHAPING(A)) { \
|
||||
EDIT_ITEM_FAST_N(float42_52, _AXIS(A), MSG_FTM_BASE_FREQ_N, &c.baseFreq.A, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params); \
|
||||
EDIT_ITEM_FAST_N(float42_52, _AXIS(A), MSG_FTM_ZETA_N, &c.zeta.A, 0.0f, 1.0f, ftMotion.update_shaping_params); \
|
||||
if (AXIS_IS_EISHAPING(A)) \
|
||||
EDIT_ITEM_FAST_N(float42_52, _AXIS(A), MSG_FTM_VTOL_N, &c.vtol.A, 0.0f, 1.0f, ftMotion.update_shaping_params); \
|
||||
}
|
||||
SHAPED_MAP(SHAPER_MENU_ITEM);
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
SUBMENU_S(_dmode(), MSG_FTM_DYN_MODE, menu_ftm_dyn_mode);
|
||||
if (c.dynFreqMode != dynFreqMode_DISABLED) {
|
||||
#define _DYN_MENU_ITEM(A) EDIT_ITEM_FAST_N(float42_52, _AXIS(A), MSG_FTM_DFREQ_K_N, &c.dynFreqK.A, 0.0f, 20.0f);
|
||||
SHAPED_MAP(_DYN_MENU_ITEM);
|
||||
}
|
||||
#endif
|
||||
CARTES_MAP(_FTM_AXIS_SUBMENU);
|
||||
|
||||
EDIT_ITEM(bool, MSG_FTM_AXIS_SYNC, &c.axis_sync_enabled);
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
CARTES_MAP(_SMOO_MENU_ITEM);
|
||||
#endif
|
||||
|
||||
#if ENABLED(FTM_RESONANCE_TEST)
|
||||
SUBMENU(MSG_FTM_RESONANCE_TEST, menu_ftm_resonance_test);
|
||||
#endif
|
||||
}
|
||||
|
||||
END_MENU();
|
||||
} // menu_ft_motion
|
||||
|
||||
@@ -553,34 +514,6 @@ void menu_move() {
|
||||
// Copy Flash strings to RAM for C-string substitution
|
||||
// For U8G paged rendering check and skip extra string copy
|
||||
|
||||
#if HAS_X_AXIS
|
||||
#if CACHE_FOR_SPEED
|
||||
int8_t prev_a = -1;
|
||||
#endif
|
||||
MString<20> shaper_name;
|
||||
auto _shaper_name = [&](const AxisEnum a) {
|
||||
if (TERN1(CACHE_FOR_SPEED, a != prev_a)) {
|
||||
TERN_(CACHE_FOR_SPEED, prev_a = a);
|
||||
shaper_name = get_shaper_name(a);
|
||||
}
|
||||
return shaper_name;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
#if CACHE_FOR_SPEED
|
||||
bool got_d = false;
|
||||
#endif
|
||||
MString<20> dmode;
|
||||
auto _dmode = [&]{
|
||||
if (TERN1(CACHE_FOR_SPEED, !got_d)) {
|
||||
TERN_(CACHE_FOR_SPEED, got_d = true);
|
||||
dmode = get_dyn_freq_mode_name();
|
||||
}
|
||||
return dmode;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if ENABLED(FTM_POLYS)
|
||||
#if CACHE_FOR_SPEED
|
||||
bool got_t = false;
|
||||
@@ -597,10 +530,6 @@ void menu_move() {
|
||||
|
||||
#else // !__AVR__
|
||||
|
||||
auto _shaper_name = [](const AxisEnum a) { return get_shaper_name(a); };
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
auto _dmode = []{ return get_dyn_freq_mode_name(); };
|
||||
#endif
|
||||
#if ENABLED(FTM_POLYS)
|
||||
auto _traj_name = []{ return ftMotion.getTrajectoryName(); };
|
||||
#endif
|
||||
@@ -616,22 +545,11 @@ void menu_move() {
|
||||
EDIT_ITEM(float42_52, MSG_FTM_POLY6_OVERSHOOT, &c.poly6_acceleration_overshoot, 1.25f, 1.875f);
|
||||
#endif
|
||||
|
||||
#define _CMPM_MENU_ITEM(A) SUBMENU_N_S(_AXIS(A), _shaper_name(_AXIS(A)), MSG_FTM_CMPN_MODE, menu_ftm_shaper_##A);
|
||||
SHAPED_MAP(_CMPM_MENU_ITEM);
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
SUBMENU_S(_dmode(), MSG_FTM_DYN_MODE, menu_ftm_dyn_mode);
|
||||
#endif
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
CARTES_MAP(_SMOO_MENU_ITEM);
|
||||
#endif
|
||||
SHAPED_MAP(_FTM_AXIS_SUBMENU);
|
||||
|
||||
END_MENU();
|
||||
} // menu_tune_ft_motion
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif // FT_MOTION_MENU
|
||||
|
||||
void menu_motion() {
|
||||
|
||||
@@ -192,7 +192,7 @@ void FTMotion::loop() {
|
||||
|
||||
void FTMotion::update_shaping_params() {
|
||||
#define UPDATE_SHAPER(A) \
|
||||
shaping.A.ena = ftMotion.cfg.shaper.A != ftMotionShaper_NONE; \
|
||||
shaping.A.ena = IS_SHAPING(ftMotion.cfg.shaper.A); \
|
||||
shaping.A.set_axis_shaping_A(cfg.shaper.A, cfg.zeta.A, cfg.vtol.A); \
|
||||
shaping.A.set_axis_shaping_N(cfg.shaper.A, cfg.baseFreq.A, cfg.zeta.A);
|
||||
|
||||
@@ -204,19 +204,19 @@ void FTMotion::loop() {
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
|
||||
#include "planner.h"
|
||||
|
||||
void FTMotion::update_smoothing_params() {
|
||||
#define _SMOOTH_PARAM(A) smoothing.A.set_smoothing_time(cfg.smoothingTime.A);
|
||||
#define _SMOOTH_PARAM(A) smoothing.A.set_time(cfg.smoothingTime.A);
|
||||
CARTES_MAP(_SMOOTH_PARAM);
|
||||
smoothing.refresh_largest_delay_samples();
|
||||
}
|
||||
|
||||
void FTMotion::set_smoothing_time(uint8_t axis, const float s_time) {
|
||||
#define _SMOOTH_CASE(A) case _AXIS(A): cfg.smoothingTime.A = s_time; break;
|
||||
switch (axis) {
|
||||
default:
|
||||
CARTES_MAP(_SMOOTH_CASE);
|
||||
}
|
||||
bool FTMotion::set_smoothing_time(const AxisEnum axis, const float s_time) {
|
||||
if (!WITHIN(s_time, 0.0f, FTM_MAX_SMOOTHING_TIME)) return false;
|
||||
cfg.smoothingTime[axis] = s_time;
|
||||
update_smoothing_params();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // FTM_SMOOTHING
|
||||
@@ -304,6 +304,21 @@ void FTMotion::init() {
|
||||
}
|
||||
}
|
||||
|
||||
// Update trajectory generator type from G-code or UI
|
||||
bool FTMotion::updateTrajectoryType(const TrajectoryType type) {
|
||||
if (type == trajectoryType) return false;
|
||||
switch (type) {
|
||||
default: return false;
|
||||
case TrajectoryType::TRAPEZOIDAL:
|
||||
case TrajectoryType::POLY5:
|
||||
case TrajectoryType::POLY6:
|
||||
break;
|
||||
}
|
||||
planner.synchronize();
|
||||
setTrajectoryType(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // FTM_POLYS
|
||||
|
||||
FSTR_P FTMotion::getTrajectoryName() {
|
||||
|
||||
@@ -95,15 +95,43 @@ typedef struct FTConfig {
|
||||
static constexpr TrajectoryType trajectory_type = TrajectoryType::TRAPEZOIDAL;
|
||||
#endif
|
||||
|
||||
#if HAS_STANDARD_MOTION
|
||||
bool setActive(const bool a) {
|
||||
if (a == active) return false;
|
||||
stepper.ftMotion_syncPosition();
|
||||
active = a;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_FTM_SHAPING
|
||||
|
||||
constexpr bool goodZeta(const float z) { return WITHIN(z, 0.01f, 1.0f); }
|
||||
constexpr bool goodVtol(const float v) { return WITHIN(v, 0.00f, 1.0f); }
|
||||
|
||||
#if HAS_DYNAMIC_FREQ
|
||||
|
||||
uint8_t setDynFreqMode(const uint8_t m) {
|
||||
if (dynFreqMode_t(m) == dynFreqMode) return 0;
|
||||
switch (dynFreqMode_t(m)) {
|
||||
default: return 2;
|
||||
TERN_(HAS_DYNAMIC_FREQ_MM, case dynFreqMode_Z_BASED:)
|
||||
TERN_(HAS_DYNAMIC_FREQ_G, case dynFreqMode_MASS_BASED:)
|
||||
case dynFreqMode_DISABLED:
|
||||
planner.synchronize();
|
||||
dynFreqMode = dynFreqMode_t(m);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool modeUsesDynFreq() const {
|
||||
return (TERN0(HAS_DYNAMIC_FREQ_MM, dynFreqMode == dynFreqMode_Z_BASED)
|
||||
|| TERN0(HAS_DYNAMIC_FREQ_G, dynFreqMode == dynFreqMode_MASS_BASED));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HAS_DYNAMIC_FREQ
|
||||
|
||||
#endif // HAS_FTM_SHAPING
|
||||
|
||||
constexpr bool goodBaseFreq(const float f) { return WITHIN(f, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2); }
|
||||
@@ -158,9 +186,9 @@ class FTMotion {
|
||||
TERN_(HAS_FTM_SHAPING, update_shaping_params());
|
||||
|
||||
#if ENABLED(FTM_SMOOTHING)
|
||||
#define _SET_SMOOTH(A) set_smoothing_time(_AXIS(A), FTM_SMOOTHING_TIME_##A);
|
||||
CARTES_MAP(_SET_SMOOTH);
|
||||
#undef _SET_SMOOTH
|
||||
#define _RESET_SMOOTH(A) (void)set_smoothing_time(_AXIS(A), FTM_SMOOTHING_TIME_##A);
|
||||
CARTES_MAP(_RESET_SMOOTH);
|
||||
#undef _RESET_SMOOTH
|
||||
#endif
|
||||
|
||||
TERN_(FTM_POLYS, setTrajectoryType(TrajectoryType::FTM_TRAJECTORY_TYPE));
|
||||
@@ -188,7 +216,7 @@ class FTMotion {
|
||||
// Refresh alpha and delay samples used by smoothing functions.
|
||||
static void update_smoothing_params();
|
||||
// Setters for smoothingTime that update alpha and delay
|
||||
static void set_smoothing_time(uint8_t axis, const float s_time);
|
||||
static bool set_smoothing_time(const AxisEnum axis, const float s_time);
|
||||
#endif
|
||||
|
||||
static void reset(); // Reset all states of the fixed time conversion to defaults.
|
||||
@@ -197,14 +225,17 @@ class FTMotion {
|
||||
#if ALL(FT_MOTION, HAS_STANDARD_MOTION)
|
||||
static bool toggle() {
|
||||
stepper.ftMotion_syncPosition();
|
||||
FLIP(cfg.active);
|
||||
cfg.setActive(!cfg.active);
|
||||
update_shaping_params();
|
||||
return cfg.active;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Trajectory generator selection
|
||||
static void setTrajectoryType(const TrajectoryType type);
|
||||
#if ENABLED(FTM_POLYS)
|
||||
static void setTrajectoryType(const TrajectoryType type);
|
||||
static bool updateTrajectoryType(const TrajectoryType type);
|
||||
#endif
|
||||
static TrajectoryType getTrajectoryType() { return TERN(FTM_POLYS, trajectoryType, TrajectoryType::TRAPEZOIDAL); }
|
||||
static FSTR_P getTrajectoryName();
|
||||
|
||||
@@ -295,10 +326,10 @@ extern FTMotion ftMotion; // Use ftMotion.thing, not FTMotion::thing.
|
||||
bool isactive;
|
||||
FTMotionDisableInScope() {
|
||||
isactive = ftMotion.cfg.active;
|
||||
ftMotion.cfg.active = false;
|
||||
ftMotion.cfg.setActive(false);
|
||||
}
|
||||
~FTMotionDisableInScope() {
|
||||
ftMotion.cfg.active = isactive;
|
||||
ftMotion.cfg.setActive(isactive);
|
||||
if (isactive) ftMotion.init();
|
||||
}
|
||||
} FTMotionDisableInScope_t;
|
||||
|
||||
@@ -41,8 +41,10 @@ enum dynFreqMode_t : uint8_t {
|
||||
dynFreqMode_MASS_BASED = 2
|
||||
};
|
||||
|
||||
#define AXIS_IS_SHAPING(A) TERN0(FTM_SHAPER_##A, (ftMotion.cfg.shaper.A != ftMotionShaper_NONE))
|
||||
#define AXIS_IS_EISHAPING(A) TERN0(FTM_SHAPER_##A, WITHIN(ftMotion.cfg.shaper.A, ftMotionShaper_EI, ftMotionShaper_3HEI))
|
||||
#define IS_SHAPING(S) (S != ftMotionShaper_NONE)
|
||||
#define IS_EISHAPING(S) WITHIN(S, ftMotionShaper_EI, ftMotionShaper_3HEI)
|
||||
#define AXIS_IS_SHAPING(A) TERN0(FTM_SHAPER_##A, IS_SHAPING(ftMotion.cfg.shaper.A))
|
||||
#define AXIS_IS_EISHAPING(A) TERN0(FTM_SHAPER_##A, IS_EISHAPING(ftMotion.cfg.shaper.A))
|
||||
|
||||
// Emitters for code that only cares about shaped XYZE
|
||||
#if HAS_FTM_SHAPING
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "smoothing.h"
|
||||
|
||||
// Set smoothing time and recalculate alpha and delay.
|
||||
void AxisSmoothing::set_smoothing_time(const float s_time) {
|
||||
void AxisSmoothing::set_time(const float s_time) {
|
||||
if (s_time > 0.001f) {
|
||||
alpha = 1.0f - expf(-(FTM_TS) * (FTM_SMOOTHING_ORDER) / s_time );
|
||||
delay_samples = s_time * FTM_FS;
|
||||
|
||||
@@ -24,7 +24,13 @@
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
typedef struct FTSmoothedAxes {
|
||||
float CARTES_AXIS_NAMES;
|
||||
union {
|
||||
struct { float CARTES_AXIS_NAMES; };
|
||||
struct { float CARTES_AXIS_NAMES_LC; };
|
||||
float data[CARTES_COUNT];
|
||||
};
|
||||
float& operator[](const int n) { return data[n]; }
|
||||
const float& operator[](const int n) const { return data[n]; }
|
||||
} ft_smoothed_float_t;
|
||||
|
||||
// Smoothing data for each axis
|
||||
@@ -34,7 +40,7 @@ typedef struct AxisSmoothing {
|
||||
float smoothing_pass[FTM_SMOOTHING_ORDER] = { 0.0f }; // Last value of each of the exponential smoothing passes
|
||||
float alpha = 0.0f; // Pre-calculated alpha for smoothing.
|
||||
uint32_t delay_samples = 0; // Pre-calculated delay in samples for smoothing.
|
||||
void set_smoothing_time(const float s_time); // Set smoothing time, recalculate alpha and delay.
|
||||
void set_time(const float s_time); // Set smoothing time, recalculate alpha and delay.
|
||||
} axis_smoothing_t;
|
||||
|
||||
typedef struct Smoothing {
|
||||
|
||||
@@ -31,18 +31,24 @@ FORCE_INLINE constexpr uint32_t a_times_b_shift_16(const uint32_t a, const uint3
|
||||
return (hi * b) + ((lo * b) >> 16);
|
||||
}
|
||||
#define FTM_NEVER uint32_t(UINT16_MAX) // Reserved number to indicate "no ticks in this frame" (FRAME_TICKS_FP+1 would work too)
|
||||
constexpr uint32_t FRAME_TICKS = STEPPER_TIMER_RATE / FTM_FS; // Timer ticks in a frame
|
||||
static_assert(FRAME_TICKS < FTM_NEVER, "(STEPPER_TIMER_RATE / FTM_FS) must be < 2^16 (otherwise fixed-point numbers exceed uint16 vars).");
|
||||
constexpr uint32_t FTM_Q_INT = 32u - __builtin_clz(FRAME_TICKS + 1); // Bits to represent the max value (duration of a frame, +1 one for FTM_NEVER).
|
||||
constexpr uint32_t FRAME_TICKS = STEPPER_TIMER_RATE / FTM_FS; // Timer ticks per frame (by default, 1kHz)
|
||||
constexpr uint32_t TICKS_BITS = __builtin_clzl(FRAME_TICKS + 1UL); // Bits to represent the max value (duration of a frame, +1 one for FTM_NEVER).
|
||||
constexpr uint32_t FTM_Q_INT = 32u - TICKS_BITS; // Bits remaining
|
||||
// "clz" counts leading zeroes.
|
||||
constexpr uint32_t FTM_Q = 16 - FTM_Q_INT; // uint16 interval fractional bits.
|
||||
constexpr uint32_t FTM_Q = 16u - FTM_Q_INT; // uint16 interval fractional bits.
|
||||
// Intervals buffer has fixed point numbers with the point on this position
|
||||
|
||||
static_assert(FRAME_TICKS < FTM_NEVER, "(STEPPER_TIMER_RATE / FTM_FS) must be < " STRINGIFY(FTM_NEVER) " to fit 16-bit fixed-point numbers.");
|
||||
static_assert(FRAME_TICKS != 2000 || FTM_Q_INT == 11, "FTM_Q_INT should be 11");
|
||||
static_assert(FRAME_TICKS != 2000 || FTM_Q == 5, "FTM_Q should be 5");
|
||||
static_assert(FRAME_TICKS != 25000 || FTM_Q_INT == 15, "FTM_Q_INT should be 15");
|
||||
static_assert(FRAME_TICKS != 25000 || FTM_Q == 1, "FTM_Q should be 1");
|
||||
|
||||
// The _FP and _fp suffixes mean the number is in fixed point format with the point at the FTM_Q position.
|
||||
// See: https://en.wikipedia.org/wiki/Fixed-point_arithmetic
|
||||
// E.g number_fp = number << FTM_Q
|
||||
// number == (number_fp >> FTM_Q)
|
||||
constexpr uint32_t ONE_FP = 1 << FTM_Q; // Number 1 in fixed point format
|
||||
// e.g., number_fp = number << FTM_Q
|
||||
// number == (number_fp >> FTM_Q)
|
||||
constexpr uint32_t ONE_FP = 1UL << FTM_Q; // Number 1 in fixed point format
|
||||
constexpr uint32_t FP_FLOOR_MASK = ~(ONE_FP - 1); // Bit mask to do FLOOR in fixed point
|
||||
constexpr uint32_t FRAME_TICKS_FP = FRAME_TICKS << FTM_Q; // Ticks in a frame in fixed point
|
||||
|
||||
|
||||
Reference in New Issue
Block a user