Merge branch 'bugfix-2.1.x' into EBAB_EBAP
This commit is contained in:
@@ -1584,7 +1584,7 @@
|
||||
#if HAS_MARLINUI_U8GLIB
|
||||
//#define BOOT_MARLIN_LOGO_ANIMATED // Animated Marlin logo. Costs ~3260 (or ~940) bytes of flash.
|
||||
#endif
|
||||
#if ANY(HAS_MARLINUI_U8GLIB, TOUCH_UI_FTDI_EVE)
|
||||
#if ANY(HAS_MARLINUI_U8GLIB, TOUCH_UI_FTDI_EVE, HAS_MARLINUI_HD44780)
|
||||
//#define SHOW_CUSTOM_BOOTSCREEN // Show the bitmap in Marlin/_Bootscreen.h on startup.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@
|
||||
* here we define this default string as the date where the latest release
|
||||
* version was tagged.
|
||||
*/
|
||||
//#define STRING_DISTRIBUTION_DATE "2024-05-16"
|
||||
//#define STRING_DISTRIBUTION_DATE "2024-05-18"
|
||||
|
||||
/**
|
||||
* Defines a generic printer name to be output to the LCD after booting Marlin.
|
||||
|
||||
@@ -52,8 +52,6 @@
|
||||
#include HAL_PATH(.., inc/Conditionals_type.h)
|
||||
|
||||
#include "Changes.h"
|
||||
#include "SanityCheck.h"
|
||||
#include HAL_PATH(.., inc/SanityCheck.h)
|
||||
|
||||
// Include all core headers
|
||||
#include "../core/language.h"
|
||||
@@ -65,3 +63,8 @@
|
||||
#endif
|
||||
|
||||
#include "../core/multi_language.h"
|
||||
|
||||
#ifndef __MARLIN_DEPS__
|
||||
#include "SanityCheck.h"
|
||||
#include HAL_PATH(.., inc/SanityCheck.h)
|
||||
#endif
|
||||
|
||||
@@ -388,8 +388,8 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
|
||||
/**
|
||||
* Custom Boot and Status screens
|
||||
*/
|
||||
#if ENABLED(SHOW_CUSTOM_BOOTSCREEN) && NONE(HAS_MARLINUI_U8GLIB, TOUCH_UI_FTDI_EVE, IS_DWIN_MARLINUI)
|
||||
#error "SHOW_CUSTOM_BOOTSCREEN requires Graphical LCD or TOUCH_UI_FTDI_EVE."
|
||||
#if ENABLED(SHOW_CUSTOM_BOOTSCREEN) && NONE(HAS_MARLINUI_HD44780, HAS_MARLINUI_U8GLIB, TOUCH_UI_FTDI_EVE, IS_DWIN_MARLINUI)
|
||||
#error "SHOW_CUSTOM_BOOTSCREEN requires Character LCD, Graphical LCD, or TOUCH_UI_FTDI_EVE."
|
||||
#elif ENABLED(SHOW_CUSTOM_BOOTSCREEN) && DISABLED(SHOW_BOOTSCREEN)
|
||||
#error "SHOW_CUSTOM_BOOTSCREEN requires SHOW_BOOTSCREEN."
|
||||
#elif ENABLED(CUSTOM_STATUS_SCREEN_IMAGE) && !HAS_MARLINUI_U8GLIB
|
||||
@@ -2703,6 +2703,28 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
|
||||
#undef IS_U8GLIB_SSD1306
|
||||
#undef IS_EXTUI
|
||||
|
||||
/**
|
||||
* Make sure LCD language settings are distinct
|
||||
*/
|
||||
#if NUM_LANGUAGES > 1
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_2), STRINGIFY(LCD_LANGUAGE)), "Error: LCD_LANGUAGE_2 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE.");
|
||||
#endif
|
||||
#if NUM_LANGUAGES > 2
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_3), STRINGIFY(LCD_LANGUAGE)), "Error: LCD_LANGUAGE_3 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE.");
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_3), STRINGIFY(LCD_LANGUAGE_2)), "Error: LCD_LANGUAGE_3 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE_2.");
|
||||
#endif
|
||||
#if NUM_LANGUAGES > 3
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_4), STRINGIFY(LCD_LANGUAGE)), "Error: LCD_LANGUAGE_4 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE.");
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_4), STRINGIFY(LCD_LANGUAGE_2)), "Error: LCD_LANGUAGE_4 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE_2.");
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_4), STRINGIFY(LCD_LANGUAGE_3)), "Error: LCD_LANGUAGE_4 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE_3.");
|
||||
#endif
|
||||
#if NUM_LANGUAGES > 4
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_5), STRINGIFY(LCD_LANGUAGE)), "Error: LCD_LANGUAGE_5 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE.");
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_5), STRINGIFY(LCD_LANGUAGE_2)), "Error: LCD_LANGUAGE_5 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE_2.");
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_5), STRINGIFY(LCD_LANGUAGE_3)), "Error: LCD_LANGUAGE_5 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE_3.");
|
||||
static_assert(strcmp(STRINGIFY(LCD_LANGUAGE_5), STRINGIFY(LCD_LANGUAGE_4)), "Error: LCD_LANGUAGE_5 (" STRINGIFY(LCD_LANGUAGE) ") cannot be the same as LCD_LANGUAGE_4.");
|
||||
#endif
|
||||
|
||||
#if ANY(TFT_GENERIC, MKS_TS35_V2_0, MKS_ROBIN_TFT24, MKS_ROBIN_TFT28, MKS_ROBIN_TFT32, MKS_ROBIN_TFT35, MKS_ROBIN_TFT43, MKS_ROBIN_TFT_V1_1R, \
|
||||
TFT_TRONXY_X5SA, ANYCUBIC_TFT35, ANYCUBIC_TFT35, LONGER_LK_TFT28, ANET_ET4_TFT28, ANET_ET5_TFT35, BIQU_BX_TFT70, BTT_TFT35_SPI_V1_0)
|
||||
#if NONE(TFT_COLOR_UI, TFT_CLASSIC_UI, TFT_LVGL_UI)
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
* version was tagged.
|
||||
*/
|
||||
#ifndef STRING_DISTRIBUTION_DATE
|
||||
#define STRING_DISTRIBUTION_DATE "2024-05-16"
|
||||
#define STRING_DISTRIBUTION_DATE "2024-05-18"
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -331,15 +331,24 @@ void MarlinUI::set_custom_characters(const HD44780CharSet screen_charset/*=CHARS
|
||||
|
||||
#endif // HAS_MEDIA
|
||||
|
||||
#if ENABLED(SHOW_BOOTSCREEN)
|
||||
// Set boot screen corner characters
|
||||
if (screen_charset == CHARSET_BOOT) {
|
||||
for (uint8_t i = 4; i--;)
|
||||
createChar_P(i, corner[i]);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{ // Info Screen uses 5 special characters
|
||||
switch (screen_charset) {
|
||||
|
||||
#if ENABLED(SHOW_BOOTSCREEN)
|
||||
case CHARSET_BOOT: {
|
||||
// Set boot screen corner characters
|
||||
for (uint8_t i = 4; i--;) createChar_P(i, corner[i]);
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#if ENABLED(SHOW_CUSTOM_BOOTSCREEN)
|
||||
case CHARSET_BOOT_CUSTOM: {
|
||||
for (uint8_t i = COUNT(customBootChars); i--;)
|
||||
createChar_P(i, customBootChars[i]);
|
||||
} break;
|
||||
#endif
|
||||
|
||||
default: {
|
||||
// Info Screen uses 5 special characters
|
||||
createChar_P(LCD_STR_BEDTEMP[0], bedTemp);
|
||||
createChar_P(LCD_STR_DEGREE[0], degree);
|
||||
createChar_P(LCD_STR_THERMOMETER[0], thermometer);
|
||||
@@ -361,7 +370,9 @@ void MarlinUI::set_custom_characters(const HD44780CharSet screen_charset/*=CHARS
|
||||
createChar_P(LCD_STR_FOLDER[0], folder);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -400,6 +411,42 @@ bool MarlinUI::detected() {
|
||||
return TERN1(DETECT_I2C_LCD_DEVICE, lcd.LcdDetected() == 1);
|
||||
}
|
||||
|
||||
#if ENABLED(SHOW_CUSTOM_BOOTSCREEN)
|
||||
|
||||
#ifndef CUSTOM_BOOTSCREEN_X
|
||||
#define CUSTOM_BOOTSCREEN_X -1
|
||||
#endif
|
||||
#ifndef CUSTOM_BOOTSCREEN_Y
|
||||
#define CUSTOM_BOOTSCREEN_Y ((LCD_HEIGHT - COUNT(custom_boot_lines)) / 2)
|
||||
#endif
|
||||
#ifndef CUSTOM_BOOTSCREEN_TIMEOUT
|
||||
#define CUSTOM_BOOTSCREEN_TIMEOUT 2500
|
||||
#endif
|
||||
|
||||
void MarlinUI::draw_custom_bootscreen(const uint8_t/*=0*/) {
|
||||
set_custom_characters(CHARSET_BOOT_CUSTOM);
|
||||
lcd.clear();
|
||||
const int8_t sx = CUSTOM_BOOTSCREEN_X;
|
||||
const uint8_t sy = CUSTOM_BOOTSCREEN_Y;
|
||||
for (lcd_uint_t i = 0; i < COUNT(custom_boot_lines); ++i) {
|
||||
PGM_P const pstr = (PGM_P)pgm_read_ptr(&custom_boot_lines[i]);
|
||||
const uint8_t clen = utf8_strlen_P(pstr);
|
||||
const lcd_uint_t x = sx >= 0 ? sx : (LCD_WIDTH - clen) / 2;
|
||||
for (lcd_uint_t j = 0; j < clen; ++j) {
|
||||
const lchar_t c = pgm_read_byte(&pstr[j]);
|
||||
lcd_put_lchar(x + j, sy + i, c == '\x08' ? '\x00' : c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shows the custom bootscreen and delays
|
||||
void MarlinUI::show_custom_bootscreen() {
|
||||
draw_custom_bootscreen();
|
||||
safe_delay(CUSTOM_BOOTSCREEN_TIMEOUT);
|
||||
}
|
||||
|
||||
#endif // SHOW_CUSTOM_BOOTSCREEN
|
||||
|
||||
#if HAS_SLOW_BUTTONS
|
||||
uint8_t MarlinUI::read_slow_buttons() {
|
||||
#if ENABLED(LCD_I2C_TYPE_MCP23017)
|
||||
@@ -466,6 +513,8 @@ void MarlinUI::clear_lcd() { lcd.clear(); }
|
||||
}
|
||||
|
||||
void MarlinUI::show_bootscreen() {
|
||||
TERN_(SHOW_CUSTOM_BOOTSCREEN, show_custom_bootscreen());
|
||||
|
||||
set_custom_characters(CHARSET_BOOT);
|
||||
lcd.clear();
|
||||
|
||||
@@ -660,9 +709,6 @@ FORCE_INLINE void _draw_bed_status(const bool blink) {
|
||||
lcd_put_u8str(F("K"));
|
||||
#else
|
||||
lcd_put_u8str(cutter_power2str(cutter.unitPower));
|
||||
#if CUTTER_UNIT_IS(PERCENT)
|
||||
lcd_put_u8str(F("%"));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
lcd_put_u8str(F(" "));
|
||||
|
||||
@@ -27,6 +27,20 @@
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(SHOW_CUSTOM_BOOTSCREEN)
|
||||
|
||||
#include "../../../_Bootscreen.h"
|
||||
|
||||
#ifdef CUSTOM_BOOTSCREEN_Y
|
||||
#define CUSTOM_BOOT_LAST COUNT(custom_boot_lines) + CUSTOM_BOOTSCREEN_Y
|
||||
#else
|
||||
#define CUSTOM_BOOT_LAST COUNT(custom_boot_lines)
|
||||
#endif
|
||||
|
||||
static_assert(CUSTOM_BOOT_LAST <= LCD_HEIGHT, "custom_boot_lines (plus CUSTOM_BOOTSCREEN_Y) doesn't fit on the selected LCD.");
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(LCD_I2C_TYPE_PCF8575)
|
||||
|
||||
// NOTE: These are register-mapped pins on the PCF8575 controller, not Arduino pins.
|
||||
|
||||
@@ -114,7 +114,7 @@ lcd_uint_t expand_u8str_P(char * const outstr, PGM_P const ptpl, const int8_t in
|
||||
* Return the number of characters emitted
|
||||
*/
|
||||
lcd_uint_t lcd_put_u8str_P(PGM_P const ptpl, const int8_t ind, const char *cstr/*=nullptr*/, FSTR_P const fstr/*=nullptr*/, const lcd_uint_t maxlen/*=LCD_WIDTH*/) {
|
||||
char estr[maxlen + 2];
|
||||
char estr[maxlen * LANG_CHARSIZE + 2];
|
||||
const lcd_uint_t outlen = expand_u8str_P(estr, ptpl, ind, cstr, fstr, maxlen);
|
||||
lcd_put_u8str_max(estr, maxlen * (MENU_FONT_WIDTH));
|
||||
return outlen;
|
||||
|
||||
@@ -105,7 +105,8 @@ typedef bool (*statusResetFunc_t)();
|
||||
enum HD44780CharSet : uint8_t {
|
||||
CHARSET_MENU,
|
||||
CHARSET_INFO,
|
||||
CHARSET_BOOT
|
||||
CHARSET_BOOT,
|
||||
CHARSET_BOOT_CUSTOM
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ void home_delta() {
|
||||
#endif
|
||||
|
||||
// Move all carriages together linearly until an endstop is hit.
|
||||
current_position.z = DIFF_TERN(USE_PROBE_FOR_Z_HOMING, delta_height + 10, probe.offset.z);
|
||||
current_position.z = DIFF_TERN(HAS_BED_PROBE, delta_height + 10, probe.offset.z);
|
||||
line_to_current_position(homing_feedrate(Z_AXIS));
|
||||
planner.synchronize();
|
||||
TERN_(HAS_DELTA_SENSORLESS_PROBING, endstops.report_states());
|
||||
|
||||
@@ -762,7 +762,7 @@ void FTMotion::convertToSteps(const uint32_t idx) {
|
||||
#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, E_AXIS_N(current_block->extruder)), // May be eliminated if guaranteed positive.
|
||||
TOSTEPS(e, E_AXIS_N(stepper.current_block->extruder)), // 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)
|
||||
@@ -771,7 +771,7 @@ void FTMotion::convertToSteps(const uint32_t idx) {
|
||||
#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, E_AXIS_N(current_block->extruder)),
|
||||
TOSTEPS(e, E_AXIS_N(stepper.current_block->extruder)),
|
||||
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)
|
||||
|
||||
@@ -892,7 +892,7 @@ void restore_feedrate_and_scaling() {
|
||||
#elif ENABLED(DELTA)
|
||||
|
||||
soft_endstop.min[axis] = base_min_pos(axis);
|
||||
soft_endstop.max[axis] = (axis == Z_AXIS) ? DIFF_TERN(USE_PROBE_FOR_Z_HOMING, delta_height, probe.offset.z) : base_home_pos(axis);
|
||||
soft_endstop.max[axis] = (axis == Z_AXIS) ? DIFF_TERN(HAS_BED_PROBE, delta_height, probe.offset.z) : base_max_pos(axis);
|
||||
|
||||
switch (axis) {
|
||||
case X_AXIS:
|
||||
@@ -1637,8 +1637,7 @@ void prepare_line_to_destination() {
|
||||
SERIAL_ECHO_START();
|
||||
msg.echoln();
|
||||
|
||||
msg.setf(GET_TEXT_F(MSG_HOME_FIRST), need);
|
||||
ui.set_status(msg);
|
||||
ui.status_printf(0, GET_TEXT_F(MSG_HOME_FIRST), need);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2467,7 +2466,7 @@ void set_axis_is_at_home(const AxisEnum axis) {
|
||||
#if ANY(MORGAN_SCARA, AXEL_TPARA)
|
||||
scara_set_axis_is_at_home(axis);
|
||||
#elif ENABLED(DELTA)
|
||||
current_position[axis] = (axis == Z_AXIS) ? DIFF_TERN(USE_PROBE_FOR_Z_HOMING, delta_height, probe.offset.z) : base_home_pos(axis);
|
||||
current_position[axis] = (axis == Z_AXIS) ? DIFF_TERN(HAS_BED_PROBE, delta_height, probe.offset.z) : base_home_pos(axis);
|
||||
#else
|
||||
current_position[axis] = SUM_TERN(HAS_HOME_OFFSET, base_home_pos(axis), home_offset[axis]);
|
||||
#endif
|
||||
|
||||
+204
-118
@@ -128,6 +128,7 @@ Planner planner;
|
||||
block_t Planner::block_buffer[BLOCK_BUFFER_SIZE];
|
||||
volatile uint8_t Planner::block_buffer_head, // Index of the next block to be pushed
|
||||
Planner::block_buffer_nonbusy, // Index of the first non-busy block
|
||||
Planner::block_buffer_planned, // Index of the optimally planned block
|
||||
Planner::block_buffer_tail; // Index of the busy block, if any
|
||||
uint16_t Planner::cleaning_buffer_counter; // A counter to disable queuing of blocks
|
||||
uint8_t Planner::delay_before_delivering; // Delay block delivery so initial blocks in an empty queue may merge
|
||||
@@ -729,6 +730,8 @@ void Planner::init() {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MINIMAL_STEP_RATE 120
|
||||
|
||||
/**
|
||||
* Get the current block for processing
|
||||
* and mark the block as busy.
|
||||
@@ -765,6 +768,10 @@ block_t* Planner::get_current_block() {
|
||||
// As this block is busy, advance the nonbusy block pointer
|
||||
block_buffer_nonbusy = next_block_index(block_buffer_tail);
|
||||
|
||||
// Push block_buffer_planned pointer, if encountered.
|
||||
if (block_buffer_tail == block_buffer_planned)
|
||||
block_buffer_planned = block_buffer_nonbusy;
|
||||
|
||||
// Return the block
|
||||
return block;
|
||||
}
|
||||
@@ -777,29 +784,29 @@ block_t* Planner::get_current_block() {
|
||||
|
||||
/**
|
||||
* Calculate trapezoid parameters, multiplying the entry- and exit-speeds
|
||||
* by the provided factors. If entry_factor is 0 don't change the initial_rate.
|
||||
* Assumes that the implied initial_rate and final_rate are no less than
|
||||
* sqrt(block->acceleration_steps_per_s2 / 2). This is ensured through
|
||||
* minimum_planner_speed_sqr / min_entry_speed_sqr though note there's one
|
||||
* exception in recalculate_trapezoids().
|
||||
* by the provided factors. Requires that initial_rate and final_rate are
|
||||
* no less than sqrt(block->acceleration_steps_per_s2 / 2), which is ensured
|
||||
* through minimum_planner_speed_sqr in _populate_block().
|
||||
**
|
||||
* ############ VERY IMPORTANT ############
|
||||
* NOTE that the PRECONDITION to call this function is that the block is
|
||||
* NOT BUSY and it is marked as RECALCULATE. That WARRANTIES the Stepper ISR
|
||||
* is not and will not use the block while we modify it.
|
||||
* is not and will not use the block while we modify it, so it is safe to
|
||||
* alter its values.
|
||||
*/
|
||||
void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t entry_factor, const_float_t exit_factor) {
|
||||
|
||||
uint32_t initial_rate = LROUND(block->nominal_rate * entry_factor); // (steps per second)
|
||||
if (initial_rate == 0) initial_rate = block->initial_rate;
|
||||
NOMORE(initial_rate, block->nominal_rate);
|
||||
uint32_t initial_rate = LROUND(block->nominal_rate * entry_factor),
|
||||
final_rate = LROUND(block->nominal_rate * exit_factor); // (steps per second)
|
||||
|
||||
uint32_t final_rate = block->nominal_rate; // (steps per second)
|
||||
if (exit_factor < 1.0f) final_rate *= exit_factor;
|
||||
// Limit minimal step rate (Otherwise the timer will overflow.)
|
||||
NOLESS(initial_rate, uint32_t(MINIMAL_STEP_RATE));
|
||||
// Legacy check against supposed timer overflow. However Stepper::calc_timer_interval() already
|
||||
// should protect against it. But removing this code produces judder in direction-switching
|
||||
// moves. This is because the current discrete stepping math diverges from physical motion under
|
||||
// constant acceleration when acceleration_steps_per_s2 is large compared to initial/final_rate.
|
||||
NOLESS(initial_rate, uint32_t(MINIMAL_STEP_RATE)); // Enforce the minimum speed
|
||||
NOLESS(final_rate, uint32_t(MINIMAL_STEP_RATE));
|
||||
NOLESS(block->nominal_rate, (uint32_t)MINIMAL_STEP_RATE);
|
||||
NOMORE(initial_rate, block->nominal_rate); // NOTE: The nominal rate may be less than MINIMAL_STEP_RATE!
|
||||
NOMORE(final_rate, block->nominal_rate);
|
||||
|
||||
#if ANY(S_CURVE_ACCELERATION, LIN_ADVANCE)
|
||||
// If we have some plateau time, the cruise rate will be the nominal rate
|
||||
@@ -818,8 +825,9 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||
const float half_inverse_accel = 0.5f * inverse_accel,
|
||||
nominal_rate_sq = FLOAT_SQ(block->nominal_rate),
|
||||
// Steps required for acceleration, deceleration to/from nominal rate
|
||||
decelerate_steps_float = half_inverse_accel * (nominal_rate_sq - FLOAT_SQ(final_rate));
|
||||
float accelerate_steps_float = half_inverse_accel * (nominal_rate_sq - FLOAT_SQ(initial_rate));
|
||||
decelerate_steps_float = half_inverse_accel * (nominal_rate_sq - FLOAT_SQ(final_rate)),
|
||||
accelerate_steps_float = half_inverse_accel * (nominal_rate_sq - FLOAT_SQ(initial_rate));
|
||||
// Aims to fully reach nominal and final rates
|
||||
accelerate_steps = CEIL(accelerate_steps_float);
|
||||
decelerate_steps = CEIL(decelerate_steps_float);
|
||||
|
||||
@@ -934,16 +942,16 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||
*
|
||||
* Recalculates the motion plan according to the following basic guidelines:
|
||||
*
|
||||
* 1. Go over blocks sequentially in reverse order and maximize the entry junction speed:
|
||||
* a. Entry speed should stay below/at the pre-computed maximum junction speed limit
|
||||
* b. Aim for the maximum entry speed which is the one reverse-computed from its exit speed
|
||||
* (next->entry_speed) if assuming maximum deceleration over the full block travel distance
|
||||
* c. The last (newest appended) block uses safe_exit_speed exit speed (there's no 'next')
|
||||
* 2. Go over blocks in chronological (forward) order and fix the exit junction speed:
|
||||
* a. Exit speed (next->entry_speed) must be below/at the maximum exit speed forward-computed
|
||||
* from its entry speed if assuming maximum acceleration over the full block travel distance
|
||||
* b. Exit speed should stay above/at the pre-computed minimum junction speed limit
|
||||
* 3. Convert entry / exit speeds (mm/s) into final/initial steps/s
|
||||
* 1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
|
||||
* (i.e. current->entry_speed) such that:
|
||||
* a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
|
||||
* neighboring blocks.
|
||||
* b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
|
||||
* with a maximum allowable deceleration over the block travel distance.
|
||||
* c. The last (or newest appended) block is planned from safe_exit_speed_sqr.
|
||||
* 2. Go over every block in chronological (forward) order and dial down junction speed values if
|
||||
* a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
|
||||
* acceleration over the block travel distance.
|
||||
*
|
||||
* When these stages are complete, the planner will have maximized the velocity profiles throughout the all
|
||||
* of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
|
||||
@@ -951,22 +959,28 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||
* are possible. If a new block is added to the buffer, the plan is recomputed according to the said
|
||||
* guidelines for a new optimal plan.
|
||||
*
|
||||
* To increase computational efficiency of these guidelines:
|
||||
* 1. We keep track of which blocks need calculation (block->flag.recalculate)
|
||||
* 2. We stop the reverse pass on the first block whose entry_speed == max_entry_speed. As soon
|
||||
* as that happens, there can be no further increases (ensured by the previous recalculate)
|
||||
* 3. On the forward pass we skip through to the first block with a modified exit speed
|
||||
* (next->entry_speed)
|
||||
* 4. On the forward pass if we encounter a full acceleration block that limits its exit speed
|
||||
* (next->entry_speed) we also update the maximum for that junction (next->max_entry_speed)
|
||||
* so it's never updated again
|
||||
* 5. We use speed squared (ex: entry_speed_sqr in mm^2/s^2) in acceleration limit computations
|
||||
* 6. We don't recompute sqrt(entry_speed_sqr) if the block's entry speed didn't change
|
||||
* To increase computational efficiency of these guidelines, a set of planner block pointers have been
|
||||
* created to indicate stop-compute points for when the planner guidelines cannot logically make any further
|
||||
* changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
|
||||
* planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
|
||||
* bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
|
||||
* added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
|
||||
* them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
|
||||
* point) are all accelerating, they are all optimal and can not be altered by a new block added to the
|
||||
* planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
|
||||
* junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
|
||||
* used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
|
||||
* recomputed as stated in the general guidelines.
|
||||
*
|
||||
* Planner buffer index mapping:
|
||||
* - block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
|
||||
* - block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
|
||||
* the buffer is full or empty. As described for standard ring buffers, this block is always empty.
|
||||
* - block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
|
||||
* streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
|
||||
* planner buffer that don't change with the addition of a new block, as describe above. In addition,
|
||||
* this block can never be less than block_buffer_tail and will always be pushed forward and maintain
|
||||
* this requirement when encountered by the Planner::release_current_block() routine during a cycle.
|
||||
*
|
||||
* NOTE: Since the planner only computes on what's in the planner buffer, some motions with many short
|
||||
* segments (e.g., complex curves) may seem to move slowly. This is because there simply isn't
|
||||
@@ -989,8 +1003,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||
*/
|
||||
|
||||
// The kernel called by recalculate() when scanning the plan from last to first entry.
|
||||
// Returns true if it could increase the current block's entry speed.
|
||||
bool Planner::reverse_pass_kernel(block_t * const current, const block_t * const next, const_float_t safe_exit_speed_sqr) {
|
||||
void Planner::reverse_pass_kernel(block_t * const current, const block_t * const next, const_float_t safe_exit_speed_sqr) {
|
||||
// We need to recalculate only for the last block added or if next->entry_speed_sqr changed.
|
||||
if (!next || next->flag.recalculate) {
|
||||
// And only if we're not already at max entry speed.
|
||||
@@ -1008,135 +1021,194 @@ bool Planner::reverse_pass_kernel(block_t * const current, const block_t * const
|
||||
// become BUSY just before being marked RECALCULATE, so check for that!
|
||||
if (stepper.is_block_busy(current)) {
|
||||
// Block became busy. Clear the RECALCULATE flag (no point in
|
||||
// recalculating BUSY blocks).
|
||||
// recalculating BUSY blocks). And don't set its speed, as it can't
|
||||
// be updated at this time.
|
||||
current->flag.recalculate = false;
|
||||
}
|
||||
else {
|
||||
// Block is not BUSY so this is ahead of the Stepper ISR:
|
||||
|
||||
// Just Set the new entry speed.
|
||||
current->entry_speed_sqr = new_entry_speed_sqr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* recalculate() needs to go over the current plan twice.
|
||||
* Once in reverse and once forward. This implements the reverse pass that
|
||||
* coarsely maximizes the entry speeds starting from last block.
|
||||
* Requires there's at least one block with flag.recalculate in the buffer.
|
||||
* Once in reverse and once forward. This implements the reverse pass.
|
||||
*/
|
||||
void Planner::reverse_pass(const_float_t safe_exit_speed_sqr) {
|
||||
// Initialize block index to the last block in the planner buffer.
|
||||
// This last block will have flag.recalculate set.
|
||||
uint8_t block_index = prev_block_index(block_buffer_head);
|
||||
|
||||
// The ISR may change block_buffer_nonbusy so get a stable local copy.
|
||||
uint8_t nonbusy_block_index = block_buffer_nonbusy;
|
||||
// Read the index of the last buffer planned block.
|
||||
// The ISR may change it so get a stable local copy.
|
||||
uint8_t planned_block_index = block_buffer_planned;
|
||||
|
||||
// If there was a race condition and block_buffer_planned was incremented
|
||||
// or was pointing at the head (queue empty) break loop now and avoid
|
||||
// planning already consumed blocks
|
||||
if (planned_block_index == block_buffer_head) return;
|
||||
|
||||
// Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
|
||||
// block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
|
||||
// NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
|
||||
const block_t *next = nullptr;
|
||||
// Don't try to change the entry speed of the first non-busy block.
|
||||
while (block_index != nonbusy_block_index) {
|
||||
while (block_index != planned_block_index) {
|
||||
|
||||
// Perform the reverse pass
|
||||
block_t *current = &block_buffer[block_index];
|
||||
|
||||
// Only process movement blocks
|
||||
if (current->is_move()) {
|
||||
// If no entry speed increase was possible we end the reverse pass.
|
||||
if (!reverse_pass_kernel(current, next, safe_exit_speed_sqr)) return;
|
||||
reverse_pass_kernel(current, next, safe_exit_speed_sqr);
|
||||
next = current;
|
||||
}
|
||||
|
||||
// Advance to the next
|
||||
block_index = prev_block_index(block_index);
|
||||
|
||||
// The ISR could advance block_buffer_nonbusy while we were doing the reverse pass.
|
||||
// The ISR could advance the block_buffer_planned while we were doing the reverse pass.
|
||||
// We must try to avoid using an already consumed block as the last one - So follow
|
||||
// changes to the pointer and make sure to limit the loop to the currently busy block
|
||||
while (nonbusy_block_index != block_buffer_nonbusy) {
|
||||
while (planned_block_index != block_buffer_planned) {
|
||||
|
||||
// If we reached the busy block or an already processed block, break the loop now
|
||||
if (block_index == nonbusy_block_index) return;
|
||||
if (block_index == planned_block_index) return;
|
||||
|
||||
// Advance the pointer, following the busy block
|
||||
nonbusy_block_index = next_block_index(nonbusy_block_index);
|
||||
planned_block_index = next_block_index(planned_block_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The kernel called during the forward pass. Assumes current->flag.recalculate.
|
||||
void Planner::forward_pass_kernel(const block_t * const previous, block_t * const current) {
|
||||
// Check if the previous block is accelerating.
|
||||
if (previous->entry_speed_sqr < current->entry_speed_sqr) {
|
||||
// Compute the maximum achievable speed if the previous block was fully accelerating.
|
||||
float new_exit_speed_sqr = max_allowable_speed_sqr(-previous->acceleration, previous->entry_speed_sqr, previous->millimeters);
|
||||
// The kernel called by recalculate() when scanning the plan from first to last entry.
|
||||
void Planner::forward_pass_kernel(const block_t * const previous, block_t * const current, const uint8_t block_index) {
|
||||
// Check against previous speed only on current->entry_speed_sqr changes (or if first time).
|
||||
if (current->flag.recalculate) {
|
||||
// If the previous block is accelerating check if it's too short to complete the full speed
|
||||
// change then adjust the entry speed accordingly. Entry speeds have already been maximized.
|
||||
if (previous->entry_speed_sqr < current->entry_speed_sqr) {
|
||||
float new_entry_speed_sqr = max_allowable_speed_sqr(-previous->acceleration, previous->entry_speed_sqr, previous->millimeters);
|
||||
|
||||
if (new_exit_speed_sqr < current->entry_speed_sqr) {
|
||||
// Current entry speed limited by full acceleration from previous entry speed.
|
||||
// If true, previous block is full-acceleration and we can move the planned pointer forward.
|
||||
if (new_entry_speed_sqr < current->entry_speed_sqr) {
|
||||
// Current entry speed limited by full acceleration from previous entry speed.
|
||||
// Make sure entry speed not lower than minimum_planner_speed_sqr.
|
||||
NOLESS(new_entry_speed_sqr, current->min_entry_speed_sqr);
|
||||
current->entry_speed_sqr = new_entry_speed_sqr;
|
||||
|
||||
// Make sure entry speed not lower than minimum_planner_speed_sqr.
|
||||
NOLESS(new_exit_speed_sqr, current->min_entry_speed_sqr);
|
||||
current->entry_speed_sqr = new_exit_speed_sqr;
|
||||
// Ensure we don't try updating entry_speed_sqr again.
|
||||
current->max_entry_speed_sqr = new_exit_speed_sqr;
|
||||
// Set optimal plan pointer.
|
||||
block_buffer_planned = block_index;
|
||||
}
|
||||
else {
|
||||
// Previous entry speed has been maximized.
|
||||
block_buffer_planned = prev_block_index(block_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The fully optimized entry speed is our new minimum speed.
|
||||
current->min_entry_speed_sqr = current->entry_speed_sqr;
|
||||
// Any block set at its maximum entry speed also creates an optimal plan up to this
|
||||
// point in the buffer. When the plan is bracketed by either the beginning of the
|
||||
// buffer and a maximum entry speed or two maximum entry speeds, every block in between
|
||||
// cannot logically be further improved. Hence, we don't have to recompute them anymore.
|
||||
if (current->entry_speed_sqr == current->max_entry_speed_sqr)
|
||||
block_buffer_planned = block_index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the forward pass and recalculate the trapezoid speed profiles for all blocks in the plan
|
||||
* according to entry/exit speeds.
|
||||
* recalculate() needs to go over the current plan twice.
|
||||
* Once in reverse and once forward. This implements the forward pass.
|
||||
*/
|
||||
void Planner::forward_pass() {
|
||||
|
||||
// Forward Pass: Forward plan the acceleration curve from the planned pointer onward.
|
||||
// Also scans for optimal plan breakpoints and appropriately updates the planned pointer.
|
||||
|
||||
// Begin at buffer planned pointer. Note that block_buffer_planned can be modified
|
||||
// by the stepper ISR, so read it ONCE. It it guaranteed that block_buffer_planned
|
||||
// will never lead head, so the loop is safe to execute. Also note that the forward
|
||||
// pass will never modify the values at the tail.
|
||||
uint8_t block_index = block_buffer_planned;
|
||||
|
||||
block_t *block;
|
||||
const block_t * previous = nullptr;
|
||||
while (block_index != block_buffer_head) {
|
||||
|
||||
// Perform the forward pass
|
||||
block = &block_buffer[block_index];
|
||||
|
||||
// Only process movement blocks
|
||||
if (block->is_move()) {
|
||||
// If there's no previous block or the previous block is not
|
||||
// BUSY (thus, modifiable) run the forward_pass_kernel. Otherwise,
|
||||
// the previous block became BUSY, so assume the current block's
|
||||
// entry speed can't be altered (since that would also require
|
||||
// updating the exit speed of the previous block).
|
||||
if (previous && !stepper.is_block_busy(previous))
|
||||
forward_pass_kernel(previous, block, block_index);
|
||||
previous = block;
|
||||
}
|
||||
// Advance to the previous
|
||||
block_index = next_block_index(block_index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate the trapezoid speed profiles for all blocks in the plan
|
||||
* according to the entry_factor for each junction. Must be called by
|
||||
* recalculate() after updating the blocks.
|
||||
*/
|
||||
void Planner::recalculate_trapezoids(const_float_t safe_exit_speed_sqr) {
|
||||
// Start with the block that's about to execute or is executing.
|
||||
// The tail may be changed by the ISR so get a local copy.
|
||||
uint8_t block_index = block_buffer_tail,
|
||||
head_block_index = block_buffer_head;
|
||||
// Since there could be a sync block in the head of the queue, and the
|
||||
// next loop must not recalculate the head block (as it needs to be
|
||||
// specially handled), scan backwards to the first non-SYNC block.
|
||||
while (head_block_index != block_index) {
|
||||
|
||||
// Go back (head always point to the first free block)
|
||||
const uint8_t prev_index = prev_block_index(head_block_index);
|
||||
|
||||
// Get the pointer to the block
|
||||
block_t *prev = &block_buffer[prev_index];
|
||||
|
||||
// It the block is a move, we're done with this loop
|
||||
if (prev->is_move()) break;
|
||||
|
||||
// Examine the previous block. This and all following are SYNC blocks
|
||||
head_block_index = prev_index;
|
||||
}
|
||||
|
||||
// Go from the tail (currently executed block) to the first block, without including it)
|
||||
block_t *block = nullptr, *next = nullptr;
|
||||
float next_entry_speed = 0.0f;
|
||||
float current_entry_speed = 0.0f, next_entry_speed = 0.0f;
|
||||
while (block_index != head_block_index) {
|
||||
|
||||
next = &block_buffer[block_index];
|
||||
|
||||
// Only process movement blocks
|
||||
if (next->is_move()) {
|
||||
// Check if the next block's entry speed changed
|
||||
if (next->flag.recalculate) {
|
||||
if (!block) {
|
||||
// 'next' is the first move due to either being the first added move or due to the planner
|
||||
// having completely fallen behind. Revert any reverse pass change.
|
||||
next->entry_speed_sqr = next->min_entry_speed_sqr;
|
||||
next_entry_speed = SQRT(next->min_entry_speed_sqr);
|
||||
}
|
||||
else {
|
||||
// Try to fix exit speed which requires trapezoid recalculation
|
||||
block->flag.recalculate = true;
|
||||
next_entry_speed = SQRT(next->entry_speed_sqr);
|
||||
|
||||
// But there is an inherent race condition here, as the block may have
|
||||
// become BUSY just before being marked RECALCULATE, so check for that!
|
||||
if (stepper.is_block_busy(block)) {
|
||||
// Block is BUSY so we can't change the exit speed. Revert any reverse pass change.
|
||||
next->entry_speed_sqr = next->min_entry_speed_sqr;
|
||||
if (!next->initial_rate) {
|
||||
// 'next' was never calculated. Planner is falling behind so for maximum efficiency
|
||||
// set next's stepping speed directly and forgo checking against min_entry_speed_sqr.
|
||||
// calculate_trapezoid_for_block() can handle it, albeit sub-optimally.
|
||||
next->initial_rate = block->final_rate;
|
||||
}
|
||||
// Note that at this point next_entry_speed is (still) 0.
|
||||
}
|
||||
else {
|
||||
// Block is not BUSY: we won the race against the ISR or recalculate was already set
|
||||
if (block) {
|
||||
|
||||
if (next->entry_speed_sqr != next->min_entry_speed_sqr)
|
||||
forward_pass_kernel(block, next);
|
||||
// If the next block is marked to RECALCULATE, also mark the previously-fetched one
|
||||
if (next->flag.recalculate) block->flag.recalculate = true;
|
||||
|
||||
const float current_entry_speed = next_entry_speed;
|
||||
next_entry_speed = SQRT(next->entry_speed_sqr);
|
||||
// Recalculate if current block entry or exit junction speed has changed.
|
||||
if (block->flag.recalculate) {
|
||||
|
||||
// But there is an inherent race condition here, as the block maybe
|
||||
// became BUSY, just before it was marked as RECALCULATE, so check
|
||||
// if that is the case!
|
||||
if (!stepper.is_block_busy(block)) {
|
||||
// Block is not BUSY, we won the race against the Stepper ISR:
|
||||
|
||||
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
|
||||
const float nomr = 1.0f / block->nominal_speed;
|
||||
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
|
||||
}
|
||||
@@ -1148,18 +1220,30 @@ void Planner::recalculate_trapezoids(const_float_t safe_exit_speed_sqr) {
|
||||
}
|
||||
|
||||
block = next;
|
||||
current_entry_speed = next_entry_speed;
|
||||
}
|
||||
|
||||
block_index = next_block_index(block_index);
|
||||
}
|
||||
|
||||
// Last/newest block in buffer. The above guarantees it's a move block.
|
||||
if (block && block->flag.recalculate) {
|
||||
const float current_entry_speed = next_entry_speed;
|
||||
// Last/newest block in buffer. Always recalculated.
|
||||
if (block) {
|
||||
next_entry_speed = SQRT(safe_exit_speed_sqr);
|
||||
|
||||
const float nomr = 1.0f / block->nominal_speed;
|
||||
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
|
||||
// Mark the next(last) block as RECALCULATE, to prevent the Stepper ISR running it.
|
||||
// As the last block is always recalculated here, there is a chance the block isn't
|
||||
// marked as RECALCULATE yet. That's the reason for the following line.
|
||||
block->flag.recalculate = true;
|
||||
|
||||
// But there is an inherent race condition here, as the block maybe
|
||||
// became BUSY, just before it was marked as RECALCULATE, so check
|
||||
// if that is the case!
|
||||
if (!stepper.is_block_busy(block)) {
|
||||
// Block is not BUSY, we won the race against the Stepper ISR:
|
||||
|
||||
const float nomr = 1.0f / block->nominal_speed;
|
||||
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
|
||||
}
|
||||
|
||||
// Reset block to ensure its trapezoid is computed - The stepper is free to use
|
||||
// the block from now on.
|
||||
@@ -1167,10 +1251,14 @@ void Planner::recalculate_trapezoids(const_float_t safe_exit_speed_sqr) {
|
||||
}
|
||||
}
|
||||
|
||||
// Requires there's at least one block with flag.recalculate in the buffer
|
||||
void Planner::recalculate(const_float_t safe_exit_speed_sqr) {
|
||||
reverse_pass(safe_exit_speed_sqr);
|
||||
// The forward pass is done as part of recalculate_trapezoids()
|
||||
// Initialize block index to the last block in the planner buffer.
|
||||
const uint8_t block_index = prev_block_index(block_buffer_head);
|
||||
// If there is just one block, no planning can be done. Avoid it!
|
||||
if (block_index != block_buffer_planned) {
|
||||
reverse_pass(safe_exit_speed_sqr);
|
||||
forward_pass();
|
||||
}
|
||||
recalculate_trapezoids(safe_exit_speed_sqr);
|
||||
}
|
||||
|
||||
@@ -1579,7 +1667,7 @@ void Planner::quick_stop() {
|
||||
const bool was_enabled = stepper.suspend();
|
||||
|
||||
// Drop all queue entries
|
||||
block_buffer_nonbusy = block_buffer_head = block_buffer_tail;
|
||||
block_buffer_nonbusy = block_buffer_planned = block_buffer_head = block_buffer_tail;
|
||||
|
||||
// Restart the block delay for the first movement - As the queue was
|
||||
// forced to empty, there's no risk the ISR will touch this.
|
||||
@@ -2741,8 +2829,6 @@ bool Planner::_populate_block(
|
||||
block->entry_speed_sqr = minimum_planner_speed_sqr;
|
||||
// Set min entry speed. Rarely it could be higher than the previous nominal speed but that's ok.
|
||||
block->min_entry_speed_sqr = minimum_planner_speed_sqr;
|
||||
// Zero the initial_rate to indicate that calculate_trapezoid_for_block() hasn't been called yet.
|
||||
block->initial_rate = 0;
|
||||
|
||||
block->flag.recalculate = true;
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ typedef struct PlannerBlock {
|
||||
#endif
|
||||
|
||||
// Settings for the trapezoid generator
|
||||
uint32_t accelerate_before, // The index of the step event on which to start cruising
|
||||
uint32_t accelerate_before, // The index of the step event where cruising starts
|
||||
decelerate_start; // The index of the step event on which to start decelerating
|
||||
|
||||
#if ENABLED(S_CURVE_ACCELERATION)
|
||||
@@ -261,7 +261,6 @@ typedef struct PlannerBlock {
|
||||
final_adv_steps; // Advance steps for exit speed pressure
|
||||
#endif
|
||||
|
||||
#define MINIMAL_STEP_RATE _MAX((STEPPER_TIMER_RATE / HAL_TIMER_TYPE_MAX), 1) // steps/s max. To prevent timer overflow, slowest is 1 step/s
|
||||
uint32_t nominal_rate, // The nominal step rate for this block in step_events/sec
|
||||
initial_rate, // The jerk-adjusted step rate at start of block
|
||||
final_rate, // The minimal rate at exit
|
||||
@@ -443,6 +442,7 @@ class Planner {
|
||||
static block_t block_buffer[BLOCK_BUFFER_SIZE];
|
||||
static volatile uint8_t block_buffer_head, // Index of the next block to be pushed
|
||||
block_buffer_nonbusy, // Index of the first non busy block
|
||||
block_buffer_planned, // Index of the optimally planned block
|
||||
block_buffer_tail; // Index of the busy block, if any
|
||||
static uint16_t cleaning_buffer_counter; // A counter to disable queuing of blocks
|
||||
static uint8_t delay_before_delivering; // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks
|
||||
@@ -804,7 +804,7 @@ class Planner {
|
||||
FORCE_INLINE static uint8_t nonbusy_movesplanned() { return block_dec_mod(block_buffer_head, block_buffer_nonbusy); }
|
||||
|
||||
// Remove all blocks from the buffer
|
||||
FORCE_INLINE static void clear_block_buffer() { block_buffer_nonbusy = block_buffer_head = block_buffer_tail = 0; }
|
||||
FORCE_INLINE static void clear_block_buffer() { block_buffer_nonbusy = block_buffer_planned = block_buffer_head = block_buffer_tail = 0; }
|
||||
|
||||
// Check if movement queue is full
|
||||
FORCE_INLINE static bool is_full() { return block_buffer_tail == next_block_index(block_buffer_head); }
|
||||
@@ -1083,10 +1083,11 @@ class Planner {
|
||||
|
||||
static void calculate_trapezoid_for_block(block_t * const block, const_float_t entry_factor, const_float_t exit_factor);
|
||||
|
||||
static bool reverse_pass_kernel(block_t * const current, const block_t * const next, const_float_t safe_exit_speed_sqr);
|
||||
static void forward_pass_kernel(const block_t * const previous, block_t * const current);
|
||||
static void reverse_pass_kernel(block_t * const current, const block_t * const next, const_float_t safe_exit_speed_sqr);
|
||||
static void forward_pass_kernel(const block_t * const previous, block_t * const current, uint8_t block_index);
|
||||
|
||||
static void reverse_pass(const_float_t safe_exit_speed_sqr);
|
||||
static void forward_pass();
|
||||
|
||||
static void recalculate_trapezoids(const_float_t safe_exit_speed_sqr);
|
||||
|
||||
|
||||
@@ -58,10 +58,16 @@
|
||||
*
|
||||
* time ----->
|
||||
*
|
||||
* The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates
|
||||
* while step_events_completed < block->accelerate_before, then starts cruising at constant speed while
|
||||
* step_events_completed < block->decelerate_start, then it decelerates until the trapezoid generator is reset.
|
||||
* The slope of acceleration is calculated using v = u + at where t is the accumulated timer values of the steps so far.
|
||||
* The speed over time graph forms a TRAPEZOID. The slope of acceleration is calculated by
|
||||
* v = u + t
|
||||
* where 't' is the accumulated timer values of the steps so far.
|
||||
*
|
||||
* The Stepper ISR dynamically executes acceleration, deceleration, and cruising according to the block parameters.
|
||||
* - Start at block->initial_rate.
|
||||
* - Accelerate while step_events_completed < block->accelerate_before.
|
||||
* - Cruise while step_events_completed < block->decelerate_start.
|
||||
* - Decelerate after that, until all steps are completed.
|
||||
* - Reset the trapezoid generator.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -2569,25 +2575,6 @@ hal_timer_t Stepper::block_phase_isr() {
|
||||
// The timer interval is just the nominal value for the nominal speed
|
||||
interval = ticks_nominal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust Laser Power - Cruise
|
||||
* power - direct or floor adjusted active laser power.
|
||||
*/
|
||||
#if ENABLED(LASER_POWER_TRAP)
|
||||
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
|
||||
if (step_events_completed + 1 == accelerate_before) {
|
||||
if (planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled) {
|
||||
if (current_block->laser.trap_ramp_entry_incr > 0) {
|
||||
current_block->laser.trap_ramp_active_pwr = current_block->laser.power;
|
||||
cutter.apply_power(current_block->laser.power);
|
||||
}
|
||||
}
|
||||
// Not a powered move.
|
||||
else cutter.apply_power(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLED(LASER_FEATURE)
|
||||
|
||||
@@ -391,8 +391,8 @@ class Stepper {
|
||||
static xyze_long_t advance_dividend;
|
||||
static uint32_t advance_divisor,
|
||||
step_events_completed, // The number of step events executed in the current block
|
||||
accelerate_before, // The point from where we need to stop acceleration
|
||||
decelerate_start, // The point from where we need to start decelerating
|
||||
accelerate_before, // The count at which to start cruising
|
||||
decelerate_start, // The count at which to start decelerating
|
||||
step_event_count; // The total event count for the current block
|
||||
|
||||
#if ANY(HAS_MULTI_EXTRUDER, MIXING_EXTRUDER)
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
* PC6 | 1 2 | PC7
|
||||
* PA2 | 3 4 | PA3
|
||||
* PB13 5 6 | PB14
|
||||
* N/C | 7 8 | PB12
|
||||
* PB15 | 7 8 | PB12
|
||||
* GND | 9 10 | 5V
|
||||
* ------
|
||||
* EXP1
|
||||
@@ -199,7 +199,7 @@
|
||||
#define EXP1_04_PIN PA3
|
||||
#define EXP1_05_PIN PB13
|
||||
#define EXP1_06_PIN PB14
|
||||
#define EXP1_07_PIN -1
|
||||
#define EXP1_07_PIN PB15
|
||||
#define EXP1_08_PIN PB12
|
||||
|
||||
#if ENABLED(CR10_STOCKDISPLAY) // LCD used for C2
|
||||
|
||||
+2
-1
@@ -49,8 +49,9 @@ build_flags = ${env:linux_native.build_flags} -Werror
|
||||
[simulator_common]
|
||||
platform = native
|
||||
framework =
|
||||
build_flags = ${common.build_flags} -std=gnu++17 -D__PLAT_NATIVE_SIM__ -DU8G_HAL_LINKS
|
||||
build_flags = ${common.build_flags} -std=gnu++17
|
||||
-I/usr/include/SDL2 -IMarlin -IMarlin/src/HAL/NATIVE_SIM/u8g
|
||||
-D__PLAT_NATIVE_SIM__ -DU8G_HAL_LINKS -DGLM_ENABLE_EXPERIMENTAL
|
||||
build_src_flags = -Wall -Wno-expansion-to-defined -Wno-deprecated-declarations -Wcast-align
|
||||
release_flags = -g0 -O3 -flto
|
||||
debug_build_flags = -fstack-protector-strong -g -g3 -ggdb
|
||||
|
||||
Reference in New Issue
Block a user