Merge branch 'MarlinFirmware:bugfix-2.1.x' into bugfix-2.1.x-February2

This commit is contained in:
Andrew 2025-09-16 21:50:59 -04:00 committed by GitHub
commit 5c5ffb28a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 304 additions and 215 deletions

View file

@ -1150,16 +1150,18 @@
#if ENABLED(FT_MOTION)
//#define FTM_IS_DEFAULT_MOTION // Use FT Motion as the factory default?
#define FTM_DEFAULT_DYNFREQ_MODE dynFreqMode_DISABLED // Default mode of dynamic frequency calculation. (DISABLED, Z_BASED, MASS_BASED)
#define FTM_DEFAULT_SHAPER_X ftMotionShaper_NONE // Default shaper mode on X axis (NONE, ZV, ZVD, ZVDD, ZVDDD, EI, 2HEI, 3HEI, MZV)
#define FTM_DEFAULT_SHAPER_Y ftMotionShaper_NONE // Default shaper mode on Y axis
#define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers
#define FTM_SHAPING_DEFAULT_FREQ_Y 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_K 0.0f // Default linear advance gain. (Acceleration-based scaling factor.)
#define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis
#define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis
#define FTM_DEFAULT_SHAPER_X ftMotionShaper_NONE // Default shaper mode on X axis (NONE, ZV, ZVD, ZVDD, ZVDDD, EI, 2HEI, 3HEI, MZV)
#define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers
#define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis
#define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis
#define FTM_DEFAULT_SHAPER_Y ftMotionShaper_NONE // Default shaper mode on Y axis
#define FTM_SHAPING_DEFAULT_FREQ_Y 37.0f // (Hz) Default peak frequency used by input shapers
#define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis
#define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis
//#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 parameters
@ -1192,7 +1194,6 @@
#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

View file

@ -41,7 +41,7 @@
* here we define this default string as the date where the latest release
* version was tagged.
*/
//#define STRING_DISTRIBUTION_DATE "2025-09-09"
//#define STRING_DISTRIBUTION_DATE "2025-09-15"
/**
* The protocol for communication to the host. Protocol indicates communication

View file

@ -71,13 +71,13 @@ static uint8_t SPI_speed = 0;
static uint8_t swSpiTransfer(uint8_t b, const uint8_t spi_speed, const pin_t sck_pin, const pin_t miso_pin, const pin_t mosi_pin) {
for (uint8_t i = 0; i < 8; i++) {
WRITE_PIN(mosi_pin, !!(b & 0x80));
WRITE_PIN(sck_pin, TERN(U8G_SPI_USE_MODE_3, LOW, HIGH));
DELAY_CYCLES(SPI_SPEED);
WRITE_PIN(sck_pin, HIGH);
WRITE_PIN(mosi_pin, !!(b & 0x80));
DELAY_CYCLES(SPI_SPEED);
b <<= 1;
if (miso_pin >= 0 && READ_PIN(miso_pin)) b |= 1;
WRITE_PIN(sck_pin, LOW);
WRITE_PIN(sck_pin, TERN(U8G_SPI_USE_MODE_3, HIGH, LOW));
DELAY_CYCLES(SPI_SPEED);
}
return b;
@ -85,7 +85,7 @@ static uint8_t swSpiTransfer(uint8_t b, const uint8_t spi_speed, const pin_t sck
static uint8_t swSpiInit(const uint8_t spiRate, const pin_t sck_pin, const pin_t mosi_pin) {
WRITE_PIN(mosi_pin, HIGH);
WRITE_PIN(sck_pin, LOW);
WRITE_PIN(sck_pin, TERN(U8G_SPI_USE_MODE_3, HIGH, LOW));
return spiRate;
}
@ -93,11 +93,11 @@ static void u8g_com_st7920_write_byte_sw_spi(uint8_t rs, uint8_t val) {
static uint8_t rs_last_state = 255;
if (rs != rs_last_state) {
// Transfer Data (FA) or Command (F8)
swSpiTransfer(rs ? 0x0FA : 0x0F8, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL);
swSpiTransfer(rs ? 0xFA : 0xF8, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL);
rs_last_state = rs;
DELAY_US(40); // Give the controller time to process the data: 20 is bad, 30 is OK, 40 is safe
}
swSpiTransfer(val & 0x0F0, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL);
swSpiTransfer(val & 0xF0, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL);
swSpiTransfer(val << 4, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL);
}
@ -169,5 +169,32 @@ uint8_t u8g_com_ST7920_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void
}
#endif
#if ENABLED(LIGHTWEIGHT_UI)
#define ST7920_CS() { WRITE(LCD_PINS_RS, HIGH); }
#define ST7920_NCS() { WRITE(LCD_PINS_RS, LOW); }
#define ST7920_SET_CMD() { ST7920_SWSPI_SND_8BIT(0xF8); }
#define ST7920_SET_DAT() { ST7920_SWSPI_SND_8BIT(0xFA); }
#define ST7920_WRITE_BYTE(a) { ST7920_SWSPI_SND_8BIT((uint8_t)((a)&0xF0u)); ST7920_SWSPI_SND_8BIT((uint8_t)((a)<<4U)); }
#define ST7920_DAT(V) !!((V) & 0x80)
#define ST7920_SND_BIT(...) do{ \
WRITE(LCD_PINS_D4, LOW); \
WRITE(LCD_PINS_EN, ST7920_DAT(val)); \
WRITE(LCD_PINS_D4, HIGH); \
val <<= 1; }while(0);
void ST7920_SWSPI_SND_8BIT(uint8_t val) {
REPEAT(8, ST7920_SND_BIT);
}
void ST7920_cs() { ST7920_CS(); }
void ST7920_ncs() { ST7920_NCS(); }
void ST7920_set_cmd() { ST7920_SET_CMD(); }
void ST7920_set_dat() { ST7920_SET_DAT(); }
void ST7920_write_byte(const uint8_t val) { ST7920_WRITE_BYTE(val); }
#endif // LIGHTWEIGHT_UI
#endif // IS_U8GLIB_ST7920
#endif // __PLAT_NATIVE_SIM__

View file

@ -127,7 +127,7 @@ uint8_t swSpiTransfer_mode_3(uint8_t b, const uint8_t spi_speed, const pin_t sck
static uint8_t SPI_speed = 0;
static uint8_t swSpiInit(const uint8_t spi_speed, const uint8_t clk_pin, const uint8_t mosi_pin) {
return spi_speed;
return spi_speed;
}
static void u8g_sw_spi_shift_out(uint8_t dataPin, uint8_t clockPin, uint8_t val) {

View file

@ -89,7 +89,9 @@
#if HAS_TMCX1X0
static uint32_t get_pwm_scale(TMC2130Stepper &st) { return st.PWM_SCALE(); }
#if ENABLED(TMC_DEBUG)
static uint32_t get_pwm_scale(TMC2130Stepper &st) { return st.PWM_SCALE(); }
#endif
static TMC_driver_data get_driver_data(TMC2130Stepper &st) {
constexpr uint8_t OT_bp = 25, OTPW_bp = 26;
@ -148,7 +150,9 @@
#if HAS_DRIVER(TMC2240)
static uint32_t get_pwm_scale(TMC2240Stepper &st) { return st.PWM_SCALE(); }
#if ENABLED(TMC_DEBUG)
static uint32_t get_pwm_scale(TMC2240Stepper &st) { return st.PWM_SCALE(); }
#endif
static TMC_driver_data get_driver_data(TMC2240Stepper &st) {
constexpr uint8_t OT_bp = 25, OTPW_bp = 26;
@ -207,7 +211,9 @@
#if HAS_TMC220x
static uint32_t get_pwm_scale(TMC2208Stepper &st) { return st.pwm_scale_sum(); }
#if ENABLED(TMC_DEBUG)
static uint32_t get_pwm_scale(TMC2208Stepper &st) { return st.pwm_scale_sum(); }
#endif
static TMC_driver_data get_driver_data(TMC2208Stepper &st) {
constexpr uint8_t OTPW_bp = 0, OT_bp = 1;
@ -242,7 +248,9 @@
#if HAS_DRIVER(TMC2660)
static uint32_t get_pwm_scale(TMC2660Stepper) { return 0; }
#if ENABLED(TMC_DEBUG)
static uint32_t get_pwm_scale(TMC2660Stepper) { return 0; }
#endif
static TMC_driver_data get_driver_data(TMC2660Stepper &st) {
constexpr uint8_t OT_bp = 1, OTPW_bp = 2;

View file

@ -61,13 +61,13 @@ void say_shaping() {
// FT Shaping
#if HAS_X_AXIS
if (AXIS_HAS_SHAPER(X)) {
if (AXIS_IS_SHAPING(X)) {
SERIAL_ECHOPGM(" with " AXIS_0_NAME);
say_shaper_type(X_AXIS);
}
#endif
#if HAS_Y_AXIS
if (AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(Y)) {
SERIAL_ECHOPGM(" and with " AXIS_1_NAME);
say_shaper_type(Y_AXIS);
}
@ -80,7 +80,7 @@ void say_shaping() {
dynamic = z_based || g_based;
// FT Dynamic Frequency Mode
if (AXIS_HAS_SHAPER(X) || AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y)) {
#if HAS_DYNAMIC_FREQ
SERIAL_ECHOPGM("Dynamic Frequency Mode ");
switch (ftMotion.cfg.dynFreqMode) {
@ -263,7 +263,7 @@ void GcodeSuite::M493() {
// Dynamic frequency mode parameter.
if (parser.seenval('D')) {
if (AXIS_HAS_SHAPER(X) || AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y)) {
const dynFreqMode_t val = dynFreqMode_t(parser.value_byte());
switch (val) {
#if HAS_DYNAMIC_FREQ_MM
@ -297,7 +297,7 @@ void GcodeSuite::M493() {
// Parse frequency parameter (X axis).
if (parser.seenval('A')) {
if (AXIS_HAS_SHAPER(X)) {
if (AXIS_IS_SHAPING(X)) {
const float val = parser.value_float();
// TODO: Frequency minimum is dependent on the shaper used; the above check isn't always correct.
if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) {
@ -326,7 +326,7 @@ void GcodeSuite::M493() {
// Parse zeta parameter (X axis).
if (parser.seenval('I')) {
const float val = parser.value_float();
if (AXIS_HAS_SHAPER(X)) {
if (AXIS_IS_SHAPING(X)) {
if (WITHIN(val, 0.01f, 1.0f)) {
ftMotion.cfg.zeta[0] = val;
flag.update = true;
@ -341,7 +341,7 @@ void GcodeSuite::M493() {
// Parse vtol parameter (X axis).
if (parser.seenval('Q')) {
const float val = parser.value_float();
if (AXIS_HAS_EISHAPER(X)) {
if (AXIS_IS_EISHAPING(X)) {
if (WITHIN(val, 0.00f, 1.0f)) {
ftMotion.cfg.vtol[0] = val;
flag.update = true;
@ -359,7 +359,7 @@ void GcodeSuite::M493() {
// Parse frequency parameter (Y axis).
if (parser.seenval('B')) {
if (AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(Y)) {
const float val = parser.value_float();
if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) {
ftMotion.cfg.baseFreq.y = val;
@ -387,7 +387,7 @@ void GcodeSuite::M493() {
// Parse zeta parameter (Y axis).
if (parser.seenval('J')) {
const float val = parser.value_float();
if (AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(Y)) {
if (WITHIN(val, 0.01f, 1.0f)) {
ftMotion.cfg.zeta[1] = val;
flag.update = true;
@ -402,7 +402,7 @@ void GcodeSuite::M493() {
// Parse vtol parameter (Y axis).
if (parser.seenval('R')) {
const float val = parser.value_float();
if (AXIS_HAS_EISHAPER(Y)) {
if (AXIS_IS_EISHAPING(Y)) {
if (WITHIN(val, 0.00f, 1.0f)) {
ftMotion.cfg.vtol[1] = val;
flag.update = true;

View file

@ -95,15 +95,16 @@ void GcodeSuite::M920() {
void GcodeSuite::M920_report(const bool forReplay/*=true*/) {
TERN_(MARLIN_SMALL_BUILD, return);
report_heading(forReplay, F(STR_HOMING_CURRENT));
auto say_M920 = [](const bool forReplay, int8_t index=-1) {
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M920");
if (index >= 0) SERIAL_ECHOPGM(" " I_PARAM_STR, index);
};
#if X_SENSORLESS || Y_SENSORLESS || Z_SENSORLESS
report_heading(forReplay, F(STR_HOMING_CURRENT));
auto say_M920 = [](const bool forReplay, int8_t index=-1) {
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M920");
if (index >= 0) SERIAL_ECHOPGM(" " I_PARAM_STR, index);
};
#if X2_SENSORLESS || Y2_SENSORLESS || Z2_SENSORLESS || Z3_SENSORLESS || Z4_SENSORLESS
say_M920(forReplay, 0);
#else
@ -123,23 +124,26 @@ void GcodeSuite::M920_report(const bool forReplay/*=true*/) {
TERN_(V_SENSORLESS, SERIAL_ECHOPGM_P(SP_V_STR, homing_current_mA.V));
TERN_(W_SENSORLESS, SERIAL_ECHOPGM_P(SP_W_STR, homing_current_mA.W));
SERIAL_EOL();
#endif
#if X2_SENSORLESS || Y2_SENSORLESS || Z2_SENSORLESS
say_M920(forReplay, 1);
TERN_(X2_SENSORLESS, SERIAL_ECHOPGM_P(SP_X_STR, homing_current_mA.X2));
TERN_(Y2_SENSORLESS, SERIAL_ECHOPGM_P(SP_Y_STR, homing_current_mA.Y2));
TERN_(Z2_SENSORLESS, SERIAL_ECHOPGM_P(SP_Z_STR, homing_current_mA.Z2));
SERIAL_EOL();
#endif
#if Z3_SENSORLESS
say_M920(forReplay, 2);
SERIAL_ECHOLNPGM(" Z", homing_current_mA.Z3);
#endif
#if Z4_SENSORLESS
say_M920(forReplay, 3);
SERIAL_ECHOLNPGM(" Z", homing_current_mA.Z4);
#endif
#if X2_SENSORLESS || Y2_SENSORLESS || Z2_SENSORLESS
say_M920(forReplay, 1);
TERN_(X2_SENSORLESS, SERIAL_ECHOPGM_P(SP_X_STR, homing_current_mA.X2));
TERN_(Y2_SENSORLESS, SERIAL_ECHOPGM_P(SP_Y_STR, homing_current_mA.Y2));
TERN_(Z2_SENSORLESS, SERIAL_ECHOPGM_P(SP_Z_STR, homing_current_mA.Z2));
SERIAL_EOL();
#endif
#if Z3_SENSORLESS
say_M920(forReplay, 2);
SERIAL_ECHOLNPGM(" Z", homing_current_mA.Z3);
#endif
#if Z4_SENSORLESS
say_M920(forReplay, 3);
SERIAL_ECHOLNPGM(" Z", homing_current_mA.Z4);
#endif
#endif // X_SENSORLESS || Y_SENSORLESS || Z_SENSORLESS
}
#endif // EDITABLE_HOMING_CURRENT

View file

@ -308,10 +308,6 @@
#endif
#if ANY(FYSETC_MINI_12864, MKS_MINI_12864)
#define U8G_SPI_USE_MODE_3 1
#endif
// ST7920-based graphical displays
#if ANY(IS_RRD_FG_SC, LCD_FOR_MELZI, SILVER_GATE_GLCD_CONTROLLER)
#define DOGLCD
@ -319,6 +315,10 @@
#define IS_RRD_SC 1
#endif
#if ANY(FYSETC_MINI_12864, MKS_MINI_12864) || ALL(__PLAT_NATIVE_SIM__, IS_U8GLIB_ST7920)
#define U8G_SPI_USE_MODE_3 1
#endif
// ST7565 / 64128N graphical displays
#if ANY(MAKRPANEL, MINIPANEL)
#define IS_ULTIPANEL 1

View file

@ -42,7 +42,7 @@
* version was tagged.
*/
#ifndef STRING_DISTRIBUTION_DATE
#define STRING_DISTRIBUTION_DATE "2025-09-09"
#define STRING_DISTRIBUTION_DATE "2025-09-15"
#endif
/**

View file

@ -478,13 +478,7 @@ void ST7920_Lite_Status_Screen::draw_fan_icon(const bool whichIcon) {
}
void ST7920_Lite_Status_Screen::draw_heat_icon(const bool whichIcon, const bool heating) {
set_ddram_address(
#if HOTENDS == 1
DDRAM_LINE_2
#else
DDRAM_LINE_3
#endif
);
set_ddram_address((HOTENDS < 2) ? DDRAM_LINE_2 : DDRAM_LINE_3);
begin_data();
if (heating)
write_word(whichIcon ? CGRAM_ICON_1_WORD : CGRAM_ICON_2_WORD);
@ -920,7 +914,7 @@ void ST7920_Lite_Status_Screen::update(const bool forceUpdate) {
cs();
update_indicators(forceUpdate);
update_status_or_position(forceUpdate);
update_progress(forceUpdate);
TERN_(HAS_PRINT_PROGRESS, update_progress(forceUpdate));
ncs();
}

View file

@ -31,7 +31,7 @@ class ST7920_Lite_Status_Screen {
} current_bits;
static void cs() { ST7920_cs(); current_bits.synced = false; }
static void ncs() { ST7920_cs(); current_bits.synced = false; }
static void ncs() { ST7920_ncs(); current_bits.synced = false; }
static void sync_cmd() { ST7920_set_cmd(); }
static void sync_dat() { ST7920_set_dat(); }
static void write_byte(const uint8_t w) { ST7920_write_byte(w); }

View file

@ -91,24 +91,17 @@
#define ST7920_DAT(V) ((V) & 0x80)
#endif
#define ST7920_SND_BIT do{ \
#define ST7920_SND_BIT(...) do{ \
WRITE(ST7920_CLK_PIN, LOW); ST7920_DELAY_1; \
WRITE(ST7920_DAT_PIN, ST7920_DAT(val)); ST7920_DELAY_2; \
WRITE(ST7920_CLK_PIN, HIGH); ST7920_DELAY_3; \
val <<= 1; }while(0)
val <<= 1; }while(0);
// Optimize this code with -O3
#pragma GCC optimize (3)
void ST7920_SWSPI_SND_8BIT(uint8_t val) {
ST7920_SND_BIT; // 1
ST7920_SND_BIT; // 2
ST7920_SND_BIT; // 3
ST7920_SND_BIT; // 4
ST7920_SND_BIT; // 5
ST7920_SND_BIT; // 6
ST7920_SND_BIT; // 7
ST7920_SND_BIT; // 8
REPEAT(8, ST7920_SND_BIT);
}
uint8_t u8g_dev_rrd_st7920_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) {

View file

@ -312,7 +312,7 @@ void menu_move() {
#include "../../module/ft_motion.h"
FSTR_P get_shaper_name(const AxisEnum axis=X_AXIS) {
FSTR_P get_shaper_name(const AxisEnum axis) {
switch (ftMotion.cfg.shaper[axis]) {
default: return nullptr;
case ftMotionShaper_NONE: return GET_TEXT_F(MSG_LCD_OFF);
@ -457,20 +457,20 @@ void menu_move() {
#if HAS_X_AXIS
SUBMENU_N_S(X_AXIS, _shaper_name(X_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_x);
if (AXIS_HAS_SHAPER(X)) {
if (AXIS_IS_SHAPING(X)) {
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_BASE_FREQ_N, &c.baseFreq.x, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params);
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_ZETA_N, &c.zeta.x, 0.0f, 1.0f, ftMotion.update_shaping_params);
if (AXIS_HAS_EISHAPER(X))
if (AXIS_IS_EISHAPING(X))
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_VTOL_N, &c.vtol.x, 0.0f, 1.0f, ftMotion.update_shaping_params);
}
#endif
#if HAS_Y_AXIS
SUBMENU_N_S(Y_AXIS, _shaper_name(Y_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_y);
if (AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(Y)) {
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_BASE_FREQ_N, &c.baseFreq.y, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params);
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_ZETA_N, &c.zeta.y, 0.0f, 1.0f, ftMotion.update_shaping_params);
if (AXIS_HAS_EISHAPER(Y))
if (AXIS_IS_EISHAPING(Y))
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_VTOL_N, &c.vtol.y, 0.0f, 1.0f, ftMotion.update_shaping_params);
}
#endif

View file

@ -53,15 +53,14 @@ AxisBits FTMotion::axis_move_dir;
xyze_trajectory_t FTMotion::traj; // = {0.0f} Storage for fixed-time-based trajectory.
xyze_trajectoryMod_t FTMotion::trajMod; // = {0.0f} Storage for fixed time trajectory window.
bool FTMotion::blockProcRdy = false; // Set when new block data is loaded from stepper module into FTM, ...
// ... and reset when block is completely converted to FTM trajectory.
bool FTMotion::batchRdy = false; // Indicates a batch of the fixed time trajectory...
// ... has been generated, is now available in the upper -
// batch of traj.x[], y, z ... e vectors, and is ready to be
// post processed, if applicable, then interpolated. Reset when the
// data has been shifted out.
bool FTMotion::batchRdyForInterp = false; // Indicates the batch is done being post processed...
// ... if applicable, and is ready to be converted to step commands.
bool FTMotion::blockProcRdy = false; // Set when new block data is loaded from stepper module into FTM,
// and reset when block is completely converted to FTM trajectory.
bool FTMotion::batchRdy = false; // Indicates a batch of the fixed time trajectory has been
// generated, is now available in the upper-batch of traj.A[], and
// is ready to be post-processed (if applicable) and interpolated.
// Reset once the data has been shifted out.
bool FTMotion::batchRdyForInterp = false; // Indicates the batch is done being post processed
// (if applicable) and is ready to be converted to step commands.
// Trapezoid data variables.
xyze_pos_t FTMotion::startPos, // (mm) Start position of block
@ -81,11 +80,12 @@ uint32_t FTMotion::N1, // Number of data points in the
uint32_t FTMotion::max_intervals; // Total number of data points that will be generated from block.
// Make vector variables.
uint32_t FTMotion::makeVector_idx = 0, // Index of fixed time trajectory generation of the overall block.
FTMotion::makeVector_batchIdx = 0; // Index of fixed time trajectory generation within the batch.
uint32_t FTMotion::traj_idx_get = 0, // Index of fixed time trajectory generation of the overall block.
FTMotion::traj_idx_set = 0; // Index of fixed time trajectory generation within the batch.
// Interpolation variables.
xyze_long_t FTMotion::steps = { 0 }; // Step count accumulator.
xyze_long_t FTMotion::step_error_q10 = { 0 }; // Fractional remainder in q10.21 format
uint32_t FTMotion::interpIdx = 0; // Index of current data point being interpolated.
@ -170,14 +170,14 @@ void FTMotion::loop() {
if (blockProcRdy) {
if (!batchRdy) makeVector(); // may clear blockProcRdy
if (!batchRdy) generateTrajectoryPointsFromBlock(); // may clear blockProcRdy
// Check if the block has been completely converted:
if (!blockProcRdy) {
discard_planner_block_protected();
if (!batchRdy && !planner.has_blocks_queued()) {
runoutBlock();
makeVector(); // Additional call to guarantee batchRdy is set this loop.
generateTrajectoryPointsFromBlock(); // Additional call to guarantee batchRdy is set this loop.
}
}
}
@ -202,13 +202,13 @@ void FTMotion::loop() {
// ... data is ready in trajMod.
batchRdyForInterp = true;
batchRdy = false; // Clear so makeVector() can resume generating points.
batchRdy = false; // Clear so generateTrajectoryPointsFromBlock() can resume generating points.
}
// Interpolation (generation of step commands from fixed time trajectory).
while (batchRdyForInterp
&& (stepperCmdBuffItems() < (FTM_STEPPERCMD_BUFF_SIZE) - (FTM_STEPS_PER_UNIT_TIME))) {
convertToSteps(interpIdx);
generateStepsFromTrajectory(interpIdx);
if (++interpIdx == FTM_BATCH_SIZE) {
batchRdyForInterp = false;
interpIdx = 0;
@ -269,11 +269,8 @@ void FTMotion::loop() {
Ai[2] = Ai[0] * K2;
const float adj = 1.0f / (Ai[0] + Ai[1] + Ai[2]);
for (uint32_t i = 0U; i < 3U; i++) {
Ai[i] *= adj;
}
}
break;
for (uint32_t i = 0; i < 3U; i++) Ai[i] *= adj;
} break;
case ftMotionShaper_2HEI: {
max_i = 3U;
@ -285,11 +282,8 @@ void FTMotion::loop() {
Ai[3] = Ai[0] * K3;
const float adj = 1.0f / (Ai[0] + Ai[1] + Ai[2] + Ai[3]);
for (uint32_t i = 0U; i < 4U; i++) {
Ai[i] *= adj;
}
}
break;
for (uint32_t i = 0; i < 4U; i++) Ai[i] *= adj;
} break;
case ftMotionShaper_3HEI: {
max_i = 4U;
@ -300,11 +294,8 @@ void FTMotion::loop() {
Ai[4] = Ai[0] * K4;
const float adj = 1.0f / (Ai[0] + Ai[1] + Ai[2] + Ai[3] + Ai[4]);
for (uint32_t i = 0U; i < 5U; i++) {
Ai[i] *= adj;
}
}
break;
for (uint32_t i = 0; i < 5U; i++) Ai[i] *= adj;
} break;
case ftMotionShaper_MZV: {
max_i = 2U;
@ -358,13 +349,13 @@ void FTMotion::loop() {
void FTMotion::update_shaping_params() {
#if HAS_X_AXIS
if ((shaping.x.ena = AXIS_HAS_SHAPER(X))) {
if ((shaping.x.ena = AXIS_IS_SHAPING(X))) {
shaping.x.set_axis_shaping_A(cfg.shaper.x, cfg.zeta.x, cfg.vtol.x);
shaping.x.set_axis_shaping_N(cfg.shaper.x, cfg.baseFreq.x, cfg.zeta.x);
}
#endif
#if HAS_Y_AXIS
if ((shaping.y.ena = AXIS_HAS_SHAPER(Y))) {
if ((shaping.y.ena = AXIS_IS_SHAPING(Y))) {
shaping.y.set_axis_shaping_A(cfg.shaper.y, cfg.zeta.y, cfg.vtol.y);
shaping.y.set_axis_shaping_N(cfg.shaper.y, cfg.baseFreq.y, cfg.zeta.y);
}
@ -384,10 +375,11 @@ void FTMotion::reset() {
endPos_prevBlock.reset();
makeVector_idx = 0;
makeVector_batchIdx = TERN(FTM_UNIFIED_BWS, 0, _MIN(BATCH_SIDX_IN_WINDOW, FTM_BATCH_SIZE));
traj_idx_get = 0;
traj_idx_set = TERN(FTM_UNIFIED_BWS, 0, _MIN(BATCH_SIDX_IN_WINDOW, FTM_BATCH_SIZE));
steps.reset();
step_error_q10.reset();
interpIdx = 0;
#if HAS_FTM_SHAPING
@ -429,7 +421,7 @@ void FTMotion::runoutBlock() {
startPos = endPos_prevBlock;
ratio.reset();
const int32_t n_to_fill_batch = (FTM_WINDOW_SIZE) - makeVector_batchIdx;
const int32_t n_to_fill_batch = (FTM_WINDOW_SIZE) - traj_idx_set;
// This line or function is to be modified for FBS use; do not optimize out.
const int32_t n_to_settle_shaper = num_samples_shaper_settle();
@ -463,9 +455,7 @@ void FTMotion::loadBlockData(block_t * const current_block) {
oneOverLength = 1.0f / totalLength;
startPos = endPos_prevBlock;
const xyze_pos_t& moveDist = current_block->dist_mm;
ratio = moveDist * oneOverLength;
const float spm = totalLength / current_block->step_event_count; // (steps/mm) Distance for each step
@ -563,20 +553,20 @@ void FTMotion::loadBlockData(block_t * const current_block) {
}
// Generate data points of the trajectory.
void FTMotion::makeVector() {
void FTMotion::generateTrajectoryPointsFromBlock() {
do {
float tau = (makeVector_idx + 1) * (FTM_TS); // (s) Time since start of block
float tau = (traj_idx_get + 1) * (FTM_TS); // (s) Time since start of block
float dist = 0.0f; // (mm) Distance traveled
#if HAS_EXTRUDERS
float accel_k = 0.0f; // (mm/s^2) Acceleration K factor
#endif
if (makeVector_idx < N1) {
if (traj_idx_get < N1) {
// Acceleration phase
dist = (f_s * tau) + (0.5f * accel_P * sq(tau)); // (mm) Distance traveled for acceleration phase since start of block
TERN_(HAS_EXTRUDERS, accel_k = accel_P); // (mm/s^2) Acceleration K factor from Accel phase
}
else if (makeVector_idx < (N1 + N2)) {
else if (traj_idx_get < (N1 + N2)) {
// Coasting phase
dist = s_1e + F_P * (tau - N1 * (FTM_TS)); // (mm) Distance traveled for coasting phase since start of block
//TERN_(HAS_EXTRUDERS, accel_k = 0.0f);
@ -588,17 +578,17 @@ void FTMotion::makeVector() {
TERN_(HAS_EXTRUDERS, accel_k = decel_P); // (mm/s^2) Acceleration K factor from Decel phase
}
#define _SET_TRAJ(q) traj.q[makeVector_batchIdx] = startPos.q + ratio.q * dist;
#define _SET_TRAJ(q) traj.q[traj_idx_set] = startPos.q + ratio.q * dist;
LOGICAL_AXIS_MAP_LC(_SET_TRAJ);
#if HAS_EXTRUDERS
if (cfg.linearAdvEna) {
float dedt_adj = (traj.e[makeVector_batchIdx] - e_raw_z1) * (FTM_FS);
float dedt_adj = (traj.e[traj_idx_set] - e_raw_z1) * (FTM_FS);
if (ratio.e > 0.0f) dedt_adj += accel_k * cfg.linearAdvK * 0.0001f;
e_raw_z1 = traj.e[makeVector_batchIdx];
e_raw_z1 = traj.e[traj_idx_set];
e_advanced_z1 += dedt_adj * (FTM_TS);
traj.e[makeVector_batchIdx] = e_advanced_z1;
traj.e[traj_idx_set] = e_advanced_z1;
}
#endif
@ -609,7 +599,7 @@ void FTMotion::makeVector() {
#if HAS_DYNAMIC_FREQ_MM
case dynFreqMode_Z_BASED: {
static float oldz = 0.0f;
const float z = traj.z[makeVector_batchIdx];
const float z = traj.z[traj_idx_set];
if (z != oldz) { // Only update if Z changed.
oldz = z;
#if HAS_X_AXIS
@ -629,10 +619,10 @@ void FTMotion::makeVector() {
// Update constantly. The optimization done for Z value makes
// less sense for E, as E is expected to constantly change.
#if HAS_X_AXIS
shaping.x.set_axis_shaping_N(cfg.shaper.x, cfg.baseFreq.x + cfg.dynFreqK.x * traj.e[makeVector_batchIdx], cfg.zeta.x);
shaping.x.set_axis_shaping_N(cfg.shaper.x, cfg.baseFreq.x + cfg.dynFreqK.x * traj.e[traj_idx_set], cfg.zeta.x);
#endif
#if HAS_Y_AXIS
shaping.y.set_axis_shaping_N(cfg.shaper.y, cfg.baseFreq.y + cfg.dynFreqK.y * traj.e[makeVector_batchIdx], cfg.zeta.y);
shaping.y.set_axis_shaping_N(cfg.shaper.y, cfg.baseFreq.y + cfg.dynFreqK.y * traj.e[traj_idx_set], cfg.zeta.y);
#endif
break;
#endif
@ -644,109 +634,105 @@ void FTMotion::makeVector() {
#if HAS_FTM_SHAPING
#if HAS_X_AXIS
if (shaping.x.ena) {
shaping.x.d_zi[shaping.zi_idx] = traj.x[makeVector_batchIdx];
traj.x[makeVector_batchIdx] *= shaping.x.Ai[0];
shaping.x.d_zi[shaping.zi_idx] = traj.x[traj_idx_set];
traj.x[traj_idx_set] *= shaping.x.Ai[0];
for (uint32_t i = 1U; i <= shaping.x.max_i; 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];
traj.x[traj_idx_set] += shaping.x.Ai[i] * shaping.x.d_zi[shaping.x.Ni[i] > shaping.zi_idx ? (FTM_ZMAX) + udiffx : udiffx];
}
}
#endif
#if HAS_Y_AXIS
if (shaping.y.ena) {
shaping.y.d_zi[shaping.zi_idx] = traj.y[makeVector_batchIdx];
traj.y[makeVector_batchIdx] *= shaping.y.Ai[0];
shaping.y.d_zi[shaping.zi_idx] = traj.y[traj_idx_set];
traj.y[traj_idx_set] *= shaping.y.Ai[0];
for (uint32_t i = 1U; i <= shaping.y.max_i; 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];
traj.y[traj_idx_set] += shaping.y.Ai[i] * shaping.y.d_zi[shaping.y.Ni[i] > shaping.zi_idx ? (FTM_ZMAX) + udiffy : udiffy];
}
}
#endif
if (++shaping.zi_idx == (FTM_ZMAX)) shaping.zi_idx = 0;
#endif // HAS_FTM_SHAPING
// Filled up the queue with regular and shaped steps
if (++makeVector_batchIdx == FTM_WINDOW_SIZE) {
makeVector_batchIdx = BATCH_SIDX_IN_WINDOW;
if (++traj_idx_set == FTM_WINDOW_SIZE) {
traj_idx_set = BATCH_SIDX_IN_WINDOW;
batchRdy = true;
}
if (++makeVector_idx == max_intervals) {
if (++traj_idx_get == max_intervals) {
blockProcRdy = false;
makeVector_idx = 0;
traj_idx_get = 0;
}
} while (blockProcRdy && !batchRdy);
}
} // generateTrajectoryPointsFromBlock
/**
* 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.
* @brief Interpolate a single trajectory data point into stepper commands.
* @param idx The index of the trajectory point to convert.
*
* Calculate the required stepper movements for each axis based on the
* difference between the current and previous trajectory points.
* Add up to one stepper command to the buffer with STEP/DIR bits for all axes.
*/
static void (*command_set[LOGICAL_AXES])(int32_t&, int32_t&, ft_command_t&, int32_t, int32_t);
void FTMotion::generateStepsFromTrajectory(const uint32_t idx) {
constexpr float INV_FTM_STEPS_PER_UNIT_TIME = 1.0f / (FTM_STEPS_PER_UNIT_TIME);
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;
}
// q10 per-stepper-slot increment toward this samples target step count.
// (traj * steps_per_mm - steps) = steps still due at the start of this UNIT_TIME.
// Convert to q10 (×2^10), then subtract the current accumulator error: step_error_q10 / FTM_STEPS_PER_UNIT_TIME.
// Over FTM_STEPS_PER_UNIT_TIME stepper-slots this sums to the exact target (no drift).
// Any fraction of a step that may remain will be accounted for by the next UNIT_TIME
#define TOSTEPS_q10(A, B) int32_t( \
(trajMod.A[idx] * planner.settings.axis_steps_per_mm[B] - steps.A) * _BV(10) \
- step_error_q10.A * INV_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;
}
xyze_long_t delta_q10 = LOGICAL_AXIS_ARRAY(
TOSTEPS_q10(e, block_extruder_axis),
TOSTEPS_q10(x, X_AXIS), TOSTEPS_q10(y, Y_AXIS), TOSTEPS_q10(z, Z_AXIS),
TOSTEPS_q10(i, I_AXIS), TOSTEPS_q10(j, J_AXIS), TOSTEPS_q10(k, K_AXIS),
TOSTEPS_q10(u, U_AXIS), TOSTEPS_q10(v, V_AXIS), TOSTEPS_q10(w, W_AXIS)
);
// Interpolates single data point to stepper commands.
void FTMotion::convertToSteps(const uint32_t idx) {
xyze_long_t err_P = { 0 };
// Fixed-point denominator for step accumulation
constexpr int32_t denom_q10 = (FTM_STEPS_PER_UNIT_TIME) << 10;
//#define STEPS_ROUNDING
#if ENABLED(STEPS_ROUNDING)
#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))
const xyze_long_t steps_tar = LOGICAL_AXIS_ARRAY(
TOSTEPS(e, block_extruder_axis), // May be eliminated if guaranteed positive.
TOSTEPS(x, X_AXIS), TOSTEPS(y, Y_AXIS), TOSTEPS(z, Z_AXIS),
TOSTEPS(i, I_AXIS), TOSTEPS(j, J_AXIS), TOSTEPS(k, K_AXIS),
TOSTEPS(u, U_AXIS), TOSTEPS(v, V_AXIS), TOSTEPS(w, W_AXIS)
);
xyze_long_t delta = steps_tar - steps;
#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(
TOSTEPS(e, block_extruder_axis),
TOSTEPS(x, X_AXIS), TOSTEPS(y, Y_AXIS), TOSTEPS(z, Z_AXIS),
TOSTEPS(i, I_AXIS), TOSTEPS(j, J_AXIS), TOSTEPS(k, K_AXIS),
TOSTEPS(u, U_AXIS), TOSTEPS(v, V_AXIS), TOSTEPS(w, W_AXIS)
);
#endif
// 1. Subtract one whole step from the accumulated distance
// 2. Accumulate one positive or negative step
// 3. Set the step and direction bits for the stepper command
#define RUN_AXIS(A) \
do { \
if (step_error_q10.A >= denom_q10) { \
step_error_q10.A -= denom_q10; \
steps.A++; \
cmd |= _BV(FT_BIT_DIR_##A) | _BV(FT_BIT_STEP_##A); \
} \
if (step_error_q10.A <= -denom_q10) { \
step_error_q10.A += denom_q10; \
steps.A--; \
cmd |= _BV(FT_BIT_STEP_##A); /* neg dir implicit */ \
} \
} while (0);
#define _COMMAND_SET(AXIS) command_set[_AXIS(AXIS)] = delta[_AXIS(AXIS)] >= 0 ? command_set_pos : command_set_neg;
LOGICAL_AXIS_MAP(_COMMAND_SET);
for (uint32_t i = 0; i < uint32_t(FTM_STEPS_PER_UNIT_TIME); i++) {
// Reference the next stepper command in the circular buffer
ft_command_t& cmd = stepperCmdBuff[stepperCmdBuff_produceIdx];
for (uint32_t i = 0U; i < (FTM_STEPS_PER_UNIT_TIME); i++) {
ft_command_t &cmd = stepperCmdBuff[stepperCmdBuff_produceIdx];
// Init all step/dir bits to 0 (defaulting to reverse/negative motion)
// Init the command to no STEP (Reverse DIR)
cmd = 0;
// Accumulate the errors for all axes
err_P += delta;
// Accumulate the "error" for all axes according the fixed-point distance
step_error_q10 += delta_q10;
// Set up step/dir bits for all axes
#define _COMMAND_RUN(A) command_set[_AXIS(A)](err_P.A, steps.A, cmd, _BV(FT_BIT_DIR_##A), _BV(FT_BIT_STEP_##A));
LOGICAL_AXIS_MAP(_COMMAND_RUN);
// Where the error has accumulated whole axis steps, add them to the command
LOGICAL_AXIS_MAP(RUN_AXIS);
// Next circular buffer index
if (++stepperCmdBuff_produceIdx == (FTM_STEPPERCMD_BUFF_SIZE))
stepperCmdBuff_produceIdx = 0;
} // FTM_STEPS_PER_UNIT_TIME loop
}
}
#endif // FT_MOTION

View file

@ -56,12 +56,14 @@ typedef struct FTConfig {
#else
static constexpr dynFreqMode_t dynFreqMode = dynFreqMode_DISABLED;
#endif
#endif // HAS_FTM_SHAPING
#if HAS_EXTRUDERS
bool linearAdvEna = FTM_LINEAR_ADV_DEFAULT_ENA; // Linear advance enable configuration.
float linearAdvK = FTM_LINEAR_ADV_DEFAULT_K; // Linear advance gain.
#endif
} ft_config_t;
class FTMotion {
@ -160,14 +162,15 @@ class FTMotion {
// Number of batches needed to propagate the current trajectory to the stepper.
static constexpr uint32_t PROP_BATCHES = CEIL((FTM_WINDOW_SIZE) / (FTM_BATCH_SIZE)) - 1;
// Make vector variables.
static uint32_t makeVector_idx,
makeVector_batchIdx;
// generateTrajectoryPointsFromBlock variables.
static uint32_t traj_idx_get,
traj_idx_set;
// Interpolation variables.
static uint32_t interpIdx;
static xyze_long_t steps;
static xyze_long_t step_error_q10;
#if ENABLED(DISTINCT_E_FACTORS)
static uint8_t block_extruder_axis; // Cached extruder axis index
@ -214,8 +217,8 @@ class FTMotion {
static void runoutBlock();
static int32_t stepperCmdBuffItems();
static void loadBlockData(block_t *const current_block);
static void makeVector();
static void convertToSteps(const uint32_t idx);
static void generateTrajectoryPointsFromBlock();
static void generateStepsFromTrajectory(const uint32_t idx);
FORCE_INLINE static int32_t num_samples_shaper_settle() { return ( shaping.x.ena || shaping.y.ena ) ? FTM_ZMAX : 0; }

View file

@ -41,8 +41,8 @@ enum dynFreqMode_t : uint8_t {
dynFreqMode_MASS_BASED = 2
};
#define AXIS_HAS_SHAPER(A) (ftMotion.cfg.shaper[_AXIS(A)] != ftMotionShaper_NONE)
#define AXIS_HAS_EISHAPER(A) WITHIN(ftMotion.cfg.shaper[_AXIS(A)], ftMotionShaper_EI, ftMotionShaper_3HEI)
#define AXIS_IS_SHAPING(A) (ftMotion.cfg.shaper[_AXIS(A)] != ftMotionShaper_NONE)
#define AXIS_IS_EISHAPING(A) WITHIN(ftMotion.cfg.shaper[_AXIS(A)], ftMotionShaper_EI, ftMotionShaper_3HEI)
typedef struct XYZEarray<float, FTM_WINDOW_SIZE> xyze_trajectory_t;
typedef struct XYZEarray<float, FTM_BATCH_SIZE> xyze_trajectoryMod_t;

View file

@ -3007,7 +3007,7 @@ hal_timer_t Stepper::block_phase_isr() {
static int32_t smoothed_vals[SMOOTH_LIN_ADV_EXP_ORDER] = {0};
for (uint8_t i = 0; i < SMOOTH_LIN_ADV_EXP_ORDER; i++) {
// Approximate gaussian smoothing via higher order exponential smoothing
// Approximate Gaussian smoothing via higher order exponential smoothing
smoothed_vals[i] += MULT_Q(30, la_step_rate - smoothed_vals[i], extruder_advance_alpha_q30[E_INDEX_N(active_extruder)]);
la_step_rate = smoothed_vals[i];
}

View file

@ -23,18 +23,56 @@ if pioutil.is_pio_build():
LD_FLASH_OFFSET = board.get("build.offset")
marlin.relocate_vtab(LD_FLASH_OFFSET)
# Flash size
maximum_flash_size = board.get("upload.maximum_size") // 1024
# Chip total flash (bytes) from board JSON
_max_flash_bytes = int(board.get("upload.maximum_size"))
# Keep STM32_FLASH_SIZE as the chip total (KiB)
maximum_flash_size = _max_flash_bytes // 1024
marlin.replace_define('STM32_FLASH_SIZE', maximum_flash_size)
# Also compute available flash after bootloader for Project Inspect
try:
_offset_int = int(str(LD_FLASH_OFFSET), 0)
except Exception:
_offset_int = 0
if _max_flash_bytes and _offset_int:
_avail = _max_flash_bytes - _offset_int
if _avail > 0:
# Update in-memory manifest so Advanced Memory Usage shows the correct total
try:
board.manifest.setdefault("upload", {})
board.manifest["upload"]["maximum_size"] = _avail
except Exception:
pass
# Get upload.maximum_ram_size (defined by /buildroot/share/PlatformIO/boards/VARIOUS.json)
maximum_ram_size = board.get("upload.maximum_ram_size")
for i, flag in enumerate(env["LINKFLAGS"]):
if "-Wl,--defsym=LD_FLASH_OFFSET" in flag:
env["LINKFLAGS"][i] = "-Wl,--defsym=LD_FLASH_OFFSET=" + LD_FLASH_OFFSET
if "-Wl,--defsym=LD_MAX_DATA_SIZE" in flag:
env["LINKFLAGS"][i] = "-Wl,--defsym=LD_MAX_DATA_SIZE=" + str(maximum_ram_size - 40)
# Helper: replace existing -Wl,--defsym=NAME=... or append if missing
def _upsert_defsym(name, value):
key = "-Wl,--defsym=" + name
if isinstance(value, int):
val = key + "=" + (hex(value) if value >= 10 else str(value))
else:
val = key + "=" + str(value)
found = False
for i, flag in enumerate(env["LINKFLAGS"]):
if key in flag:
env["LINKFLAGS"][i] = val
found = True
break
if not found:
env.Append(LINKFLAGS=[val])
# Provide the symbols the linker script expects:
# ORIGIN = 0x08000000 + LD_FLASH_OFFSET
# LENGTH = LD_MAX_SIZE - LD_FLASH_OFFSET
_upsert_defsym("LD_FLASH_OFFSET", _offset_int)
_upsert_defsym("LD_MAX_SIZE", _max_flash_bytes)
if maximum_ram_size:
_upsert_defsym("LD_MAX_DATA_SIZE", int(maximum_ram_size) - 40)
#
# For build.encrypt_mks rename and encode the firmware file.
@ -62,8 +100,37 @@ if pioutil.is_pio_build():
from pathlib import Path
from datetime import datetime
from os import path
_newpath = Path(target[0].dir.path, datetime.now().strftime(new_name.replace('{date}', '%Y%m%d').replace('{time}', '%H%M%S')))
Path(target[0].path).replace(_newpath)
env['PROGNAME'] = path.splitext(_newpath)[0]
# Build the timestamped base name from your template (may already include ".bin")
base = datetime.now().strftime(new_name.replace('{date}', '%Y%m%d').replace('{time}', '%H%M%S'))
# Ensure correct extensions for both outputs
if base.lower().endswith('.bin'):
stem = base[:-4] # strip ".bin" for the ELF stem
bin_name = base
else:
stem = base
bin_name = base + '.bin'
elf_name = stem + '.elf'
# Current files produced by PlatformIO
bin_old = Path(target[0].path) # e.g. .pio/build/<env>/firmware.bin
elf_old = Path(source[0].path) # e.g. .pio/build/<env>/firmware.elf
# New paths in the same directories
bin_new = Path(target[0].dir.path, bin_name)
elf_new = Path(source[0].dir.path, elf_name)
# Rename both
bin_old.replace(bin_new)
elf_old.replace(elf_new)
# Update PROGNAME (base without extension) for any later steps that read it
env['PROGNAME'] = path.splitext(str(bin_new))[0]
# Optional: log the results
print(f"FIRMWARE BIN: {bin_new}")
print(f"FIRMWARE ELF: {elf_new}")
marlin.add_post_action(rename_target)

View file

@ -125,6 +125,12 @@ if pioutil.is_pio_build():
#
rm_ofile("inc", "Warnings")
#
# Renew date/time
#
rm_ofile("gcode/host", "M115")
rm_ofile("lcd/menu", "menu_info")
#
# Rebuild 'settings.cpp' for EEPROM_INIT_NOW
#

View file

@ -39,7 +39,7 @@ USES_LIQUIDCRYSTAL_I2C = marcoschwartz/LiquidCrystal_I2C@1.1.4
USES_LIQUIDTWI2 = LiquidTWI2@1.2.7
HAS_LCDPRINT = build_src_filter=+<src/lcd/lcdprint.cpp>
HAS_MARLINUI_HD44780 = build_src_filter=+<src/lcd/HD44780>
HAS_MARLINUI_U8GLIB = marlinfirmware/U8glib-HAL@0.5.4
HAS_MARLINUI_U8GLIB = marlinfirmware/U8glib-HAL@0.5.5
build_src_filter=+<src/lcd/dogm>
HAS_(FSMC|SPI|LTDC)_TFT = build_src_filter=+<src/lcd/tft_io>
I2C_EEPROM = build_src_filter=+<src/HAL/shared/eeprom_if_i2c.cpp>

View file

@ -58,7 +58,7 @@ debug_build_flags = -fstack-protector-strong -g -g3 -ggdb
lib_compat_mode = off
build_src_filter = ${common.default_src_filter} +<src/HAL/NATIVE_SIM>
lib_deps = ${common.lib_deps}
MarlinSimUI=https://github.com/p3p/MarlinSimUI/archive/af62611296.zip
MarlinSimUI=https://github.com/p3p/MarlinSimUI/archive/2e71215018.zip
Adafruit NeoPixel=https://github.com/p3p/Adafruit_NeoPixel/archive/c6b319f447.zip
LiquidCrystal=https://github.com/p3p/LiquidCrystal/archive/322fb5fc23.zip
extra_scripts = ${common.extra_scripts}