2018-11-16 03:32:01 -06:00
/**
* Marlin 3 D Printer Firmware
* Copyright ( C ) 2016 MarlinFirmware [ https : //github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl .
* Copyright ( C ) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/**
* planner . cpp
*
* Buffer movement commands and manage the acceleration profile plan
*
* Derived from Grbl
* Copyright ( c ) 2009 - 2011 Simen Svale Skogsrud
*
* The ring buffer implementation gleaned from the wiring_serial library by David A . Mellis .
*
*
* Reasoning behind the mathematics in this module ( in the key of ' Mathematica ' ) :
*
* s = = speed , a = = acceleration , t = = time , d = = distance
*
* Basic definitions :
* Speed [ s_ , a_ , t_ ] : = s + ( a * t )
* Travel [ s_ , a_ , t_ ] : = Integrate [ Speed [ s , a , t ] , t ]
*
* Distance to reach a specific speed with a constant acceleration :
* Solve [ { Speed [ s , a , t ] = = m , Travel [ s , a , t ] = = d } , d , t ]
* d - > ( m ^ 2 - s ^ 2 ) / ( 2 a ) - - > estimate_acceleration_distance ( )
*
* Speed after a given distance of travel with constant acceleration :
* Solve [ { Speed [ s , a , t ] = = m , Travel [ s , a , t ] = = d } , m , t ]
* m - > Sqrt [ 2 a d + s ^ 2 ]
*
* DestinationSpeed [ s_ , a_ , d_ ] : = Sqrt [ 2 a d + s ^ 2 ]
*
* When to start braking ( di ) to reach a specified destination speed ( s2 ) after accelerating
* from initial speed s1 without ever stopping at a plateau :
* Solve [ { DestinationSpeed [ s1 , a , di ] = = DestinationSpeed [ s2 , a , d - di ] } , di ]
* di - > ( 2 a d - s1 ^ 2 + s2 ^ 2 ) / ( 4 a ) - - > intersection_distance ( )
*
* IntersectionDistance [ s1_ , s2_ , a_ , d_ ] : = ( 2 a d - s1 ^ 2 + s2 ^ 2 ) / ( 4 a )
*
*/
2019-01-31 05:25:28 -06:00
# include "MarlinConfig.h"
2018-11-16 03:32:01 -06:00
# include "planner.h"
# include "stepper.h"
# include "temperature.h"
# include "ultralcd.h"
# include "language.h"
2019-01-31 05:25:28 -06:00
# include "ubl.h"
# include "gcode.h"
2018-11-16 03:32:01 -06:00
# include "Marlin.h"
# if ENABLED(MESH_BED_LEVELING)
# include "mesh_bed_leveling.h"
# endif
Planner planner ;
// public:
/**
* A ring buffer of moves described in steps
*/
block_t Planner : : block_buffer [ BLOCK_BUFFER_SIZE ] ;
2019-01-31 05:25:28 -06:00
volatile uint8_t Planner : : block_buffer_head = 0 , // Index of the next block to be pushed
Planner : : block_buffer_tail = 0 ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
float Planner : : max_feedrate_mm_s [ XYZE_N ] , // Max speeds in mm per second
Planner : : axis_steps_per_mm [ XYZE_N ] ,
Planner : : steps_to_mm [ XYZE_N ] ;
2018-11-16 03:32:01 -06:00
# if ENABLED(DISTINCT_E_FACTORS)
uint8_t Planner : : last_extruder = 0 ; // Respond to extruder change
# endif
int16_t Planner : : flow_percentage [ EXTRUDERS ] = ARRAY_BY_EXTRUDERS1 ( 100 ) ; // Extrusion factor for each extruder
2019-01-31 05:25:28 -06:00
float Planner : : e_factor [ EXTRUDERS ] ; // The flow percentage and volumetric multiplier combine to scale E movement
2018-11-16 03:32:01 -06:00
# if DISABLED(NO_VOLUMETRICS)
float Planner : : filament_size [ EXTRUDERS ] , // diameter of filament (in millimeters), typically around 1.75 or 2.85, 0 disables the volumetric calculations for the extruder
2019-01-31 05:25:28 -06:00
Planner : : volumetric_area_nominal = CIRCLE_AREA ( ( DEFAULT_NOMINAL_FILAMENT_DIA ) * 0.5 ) , // Nominal cross-sectional area
2018-11-16 03:32:01 -06:00
Planner : : volumetric_multiplier [ EXTRUDERS ] ; // Reciprocal of cross-sectional area of filament (in mm^2). Pre-calculated to reduce computation in the planner
# endif
2019-01-31 05:25:28 -06:00
uint32_t Planner : : max_acceleration_steps_per_s2 [ XYZE_N ] ,
Planner : : max_acceleration_mm_per_s2 [ XYZE_N ] ; // Use M201 to override by software
uint32_t Planner : : min_segment_time_us ;
// Initialized by settings.load()
float Planner : : min_feedrate_mm_s ,
Planner : : acceleration , // Normal acceleration mm/s^2 DEFAULT ACCELERATION for all printing moves. M204 SXXXX
Planner : : retract_acceleration , // Retract acceleration mm/s^2 filament pull-back and push-forward while standing still in the other axes M204 TXXXX
Planner : : travel_acceleration , // Travel acceleration mm/s^2 DEFAULT ACCELERATION for all NON printing moves. M204 MXXXX
Planner : : max_jerk [ XYZE ] , // The largest speed change requiring no acceleration
Planner : : min_travel_feedrate_mm_s ;
2018-11-16 03:32:01 -06:00
# if HAS_LEVELING
bool Planner : : leveling_active = false ; // Flag that auto bed leveling is enabled
# if ABL_PLANAR
matrix_3x3 Planner : : bed_level_matrix ; // Transform to compensate for bed level
# endif
# if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
float Planner : : z_fade_height , // Initialized by settings.load()
Planner : : inverse_z_fade_height ,
Planner : : last_fade_z ;
# endif
# else
constexpr bool Planner : : leveling_active ;
# endif
# if ENABLED(SKEW_CORRECTION)
# if ENABLED(SKEW_CORRECTION_GCODE)
float Planner : : xy_skew_factor ;
# else
constexpr float Planner : : xy_skew_factor ;
# endif
# if ENABLED(SKEW_CORRECTION_FOR_Z) && ENABLED(SKEW_CORRECTION_GCODE)
float Planner : : xz_skew_factor , Planner : : yz_skew_factor ;
# else
constexpr float Planner : : xz_skew_factor , Planner : : yz_skew_factor ;
# endif
# endif
# if ENABLED(AUTOTEMP)
float Planner : : autotemp_max = 250 ,
Planner : : autotemp_min = 210 ,
2019-01-31 05:25:28 -06:00
Planner : : autotemp_factor = 0.1 ;
2018-11-16 03:32:01 -06:00
bool Planner : : autotemp_enabled = false ;
# endif
// private:
int32_t Planner : : position [ NUM_AXIS ] = { 0 } ;
uint32_t Planner : : cutoff_long ;
float Planner : : previous_speed [ NUM_AXIS ] ,
2019-01-31 05:25:28 -06:00
Planner : : previous_nominal_speed ;
2018-11-16 03:32:01 -06:00
# if ENABLED(DISABLE_INACTIVE_EXTRUDER)
uint8_t Planner : : g_uc_extruder_last_move [ EXTRUDERS ] = { 0 } ;
# endif
# ifdef XY_FREQUENCY_LIMIT
// Old direction bits. Used for speed calculations
unsigned char Planner : : old_direction_bits = 0 ;
// Segment times (in µs). Used for speed calculations
uint32_t Planner : : axis_segment_time_us [ 2 ] [ 3 ] = { { MAX_FREQ_TIME_US + 1 , 0 , 0 } , { MAX_FREQ_TIME_US + 1 , 0 , 0 } } ;
# endif
# if ENABLED(LIN_ADVANCE)
2019-01-31 05:25:28 -06:00
float Planner : : extruder_advance_k , // Initialized by settings.load()
Planner : : advance_ed_ratio , // Initialized by settings.load()
Planner : : position_float [ XYZE ] , // Needed for accurate maths. Steps cannot be used!
Planner : : lin_dist_xy ,
Planner : : lin_dist_e ;
2018-11-16 03:32:01 -06:00
# endif
# if ENABLED(ULTRA_LCD)
volatile uint32_t Planner : : block_buffer_runtime_us = 0 ;
# endif
/**
* Class and Instance Methods
*/
Planner : : Planner ( ) { init ( ) ; }
void Planner : : init ( ) {
2019-01-31 05:25:28 -06:00
block_buffer_head = block_buffer_tail = 0 ;
2018-11-16 03:32:01 -06:00
ZERO ( position ) ;
2019-01-31 05:25:28 -06:00
# if ENABLED(LIN_ADVANCE)
2018-11-16 03:32:01 -06:00
ZERO ( position_float ) ;
# endif
ZERO ( previous_speed ) ;
2019-01-31 05:25:28 -06:00
previous_nominal_speed = 0.0 ;
2018-11-16 03:32:01 -06:00
# if ABL_PLANAR
bed_level_matrix . set_to_identity ( ) ;
# endif
}
# define MINIMAL_STEP_RATE 120
/**
* Calculate trapezoid parameters , multiplying the entry - and exit - speeds
* by the provided factors .
*/
void Planner : : calculate_trapezoid_for_block ( block_t * const block , const float & entry_factor , const float & exit_factor ) {
uint32_t initial_rate = CEIL ( block - > nominal_rate * entry_factor ) ,
final_rate = CEIL ( block - > nominal_rate * exit_factor ) ; // (steps per second)
// Limit minimal step rate (Otherwise the timer will overflow.)
2019-01-31 05:25:28 -06:00
NOLESS ( initial_rate , MINIMAL_STEP_RATE ) ;
NOLESS ( final_rate , MINIMAL_STEP_RATE ) ;
2018-11-16 03:32:01 -06:00
const int32_t accel = block - > acceleration_steps_per_s2 ;
// Steps required for acceleration, deceleration to/from nominal rate
2019-01-31 05:25:28 -06:00
int32_t accelerate_steps = CEIL ( estimate_acceleration_distance ( initial_rate , block - > nominal_rate , accel ) ) ,
decelerate_steps = FLOOR ( estimate_acceleration_distance ( block - > nominal_rate , final_rate , - accel ) ) ,
2018-11-16 03:32:01 -06:00
// Steps between acceleration and deceleration, if any
2019-01-31 05:25:28 -06:00
plateau_steps = block - > step_event_count - accelerate_steps - decelerate_steps ;
2018-11-16 03:32:01 -06:00
// Does accelerate_steps + decelerate_steps exceed step_event_count?
// Then we can't possibly reach the nominal rate, there will be no cruising.
// Use intersection_distance() to calculate accel / braking time in order to
// reach the final_rate exactly at the end of this block.
if ( plateau_steps < 0 ) {
2019-01-31 05:25:28 -06:00
accelerate_steps = CEIL ( intersection_distance ( initial_rate , final_rate , accel , block - > step_event_count ) ) ;
NOLESS ( accelerate_steps , 0 ) ; // Check limits due to numerical round-off
accelerate_steps = min ( ( uint32_t ) accelerate_steps , block - > step_event_count ) ; //(We can cast here to unsigned, because the above line ensures that we are above zero)
2018-11-16 03:32:01 -06:00
plateau_steps = 0 ;
}
2019-01-31 05:25:28 -06:00
// block->accelerate_until = accelerate_steps;
// block->decelerate_after = accelerate_steps+plateau_steps;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
CRITICAL_SECTION_START ; // Fill variables used by the stepper in a critical section
if ( ! TEST ( block - > flag , BLOCK_BIT_BUSY ) ) { // Don't update variables if block is busy.
block - > accelerate_until = accelerate_steps ;
block - > decelerate_after = accelerate_steps + plateau_steps ;
block - > initial_rate = initial_rate ;
block - > final_rate = final_rate ;
}
CRITICAL_SECTION_END ;
2018-11-16 03:32:01 -06:00
}
2019-01-31 05:25:28 -06:00
// "Junction jerk" in this context is the immediate change in speed at the junction of two blocks.
// This method will calculate the junction jerk as the euclidean distance between the nominal
// velocities of the respective blocks.
//inline float junction_jerk(block_t *before, block_t *after) {
// return SQRT(
// POW((before->speed_x-after->speed_x), 2)+POW((before->speed_y-after->speed_y), 2));
//}
2018-11-16 03:32:01 -06:00
// The kernel called by recalculate() when scanning the plan from last to first entry.
void Planner : : reverse_pass_kernel ( block_t * const current , const block_t * const next ) {
2019-01-31 05:25:28 -06:00
if ( ! current | | ! next ) return ;
// If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
// If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
// check for maximum allowable speed reductions to ensure maximum possible planned speed.
float max_entry_speed = current - > max_entry_speed ;
if ( current - > entry_speed ! = max_entry_speed ) {
// If nominal length true, max junction speed is guaranteed to be reached. Only compute
// for max allowable speed if block is decelerating and nominal length is false.
current - > entry_speed = ( TEST ( current - > flag , BLOCK_BIT_NOMINAL_LENGTH ) | | max_entry_speed < = next - > entry_speed )
? max_entry_speed
: min ( max_entry_speed , max_allowable_speed ( - current - > acceleration , next - > entry_speed , current - > millimeters ) ) ;
SBI ( current - > flag , BLOCK_BIT_RECALCULATE ) ;
2018-11-16 03:32:01 -06:00
}
}
/**
* recalculate ( ) needs to go over the current plan twice .
* Once in reverse and once forward . This implements the reverse pass .
*/
void Planner : : reverse_pass ( ) {
2019-01-31 05:25:28 -06:00
if ( movesplanned ( ) > 3 ) {
const uint8_t endnr = BLOCK_MOD ( block_buffer_tail + 2 ) ; // tail is running. tail+1 shouldn't be altered because it's connected to the running block.
// tail+2 because the index is not yet advanced when checked
uint8_t blocknr = prev_block_index ( block_buffer_head ) ;
block_t * current = & block_buffer [ blocknr ] ;
do {
const block_t * const next = current ;
blocknr = prev_block_index ( blocknr ) ;
current = & block_buffer [ blocknr ] ;
if ( TEST ( current - > flag , BLOCK_BIT_START_FROM_FULL_HALT ) ) // Up to this every block is already optimized.
break ;
2018-11-16 03:32:01 -06:00
reverse_pass_kernel ( current , next ) ;
2019-01-31 05:25:28 -06:00
} while ( blocknr ! = endnr ) ;
2018-11-16 03:32:01 -06:00
}
}
// The kernel called by recalculate() when scanning the plan from first to last entry.
2019-01-31 05:25:28 -06:00
void Planner : : forward_pass_kernel ( const block_t * const previous , block_t * const current ) {
if ( ! previous ) return ;
// If the previous block is an acceleration block, but it is not long enough to complete the
// full speed change within the block, we need to adjust the entry speed accordingly. Entry
// speeds have already been reset, maximized, and reverse planned by reverse planner.
// If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck.
if ( ! TEST ( previous - > flag , BLOCK_BIT_NOMINAL_LENGTH ) ) {
if ( previous - > entry_speed < current - > entry_speed ) {
float entry_speed = min ( current - > entry_speed ,
max_allowable_speed ( - previous - > acceleration , previous - > entry_speed , previous - > millimeters ) ) ;
// Check for junction speed change
if ( current - > entry_speed ! = entry_speed ) {
current - > entry_speed = entry_speed ;
2018-11-16 03:32:01 -06:00
SBI ( current - > flag , BLOCK_BIT_RECALCULATE ) ;
}
}
}
}
/**
* recalculate ( ) needs to go over the current plan twice .
* Once in reverse and once forward . This implements the forward pass .
*/
void Planner : : forward_pass ( ) {
2019-01-31 05:25:28 -06:00
block_t * block [ 3 ] = { NULL , NULL , NULL } ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
for ( uint8_t b = block_buffer_tail ; b ! = block_buffer_head ; b = next_block_index ( b ) ) {
block [ 0 ] = block [ 1 ] ;
block [ 1 ] = block [ 2 ] ;
block [ 2 ] = & block_buffer [ b ] ;
forward_pass_kernel ( block [ 0 ] , block [ 1 ] ) ;
2018-11-16 03:32:01 -06:00
}
2019-01-31 05:25:28 -06:00
forward_pass_kernel ( block [ 1 ] , block [ 2 ] ) ;
2018-11-16 03:32:01 -06:00
}
/**
* 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 ( ) {
2019-01-31 05:25:28 -06:00
int8_t block_index = block_buffer_tail ;
block_t * current , * next = NULL ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
while ( block_index ! = block_buffer_head ) {
current = next ;
2018-11-16 03:32:01 -06:00
next = & block_buffer [ block_index ] ;
2019-01-31 05:25:28 -06:00
if ( current ) {
// Recalculate if current block entry or exit junction speed has changed.
if ( TEST ( current - > flag , BLOCK_BIT_RECALCULATE ) | | TEST ( next - > flag , BLOCK_BIT_RECALCULATE ) ) {
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
const float nomr = 1.0 / current - > nominal_speed ;
calculate_trapezoid_for_block ( current , current - > entry_speed * nomr , next - > entry_speed * nomr ) ;
CBI ( current - > flag , BLOCK_BIT_RECALCULATE ) ; // Reset current only to ensure next trapezoid is computed
2018-11-16 03:32:01 -06:00
}
}
block_index = next_block_index ( block_index ) ;
}
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
if ( next ) {
2019-01-31 05:25:28 -06:00
const float nomr = 1.0 / next - > nominal_speed ;
calculate_trapezoid_for_block ( next , next - > entry_speed * nomr , ( MINIMUM_PLANNER_SPEED ) * nomr ) ;
2018-11-16 03:32:01 -06:00
CBI ( next - > flag , BLOCK_BIT_RECALCULATE ) ;
}
}
2019-01-31 05:25:28 -06:00
/*
* Recalculate the motion plan according to the following algorithm :
*
* 1. Go over every block in reverse order . . .
*
* Calculate a junction speed reduction ( block_t . entry_factor ) so :
*
* a . The junction jerk is within the set limit , and
*
* b . No speed reduction within one block requires faster
* deceleration than the one , true constant acceleration .
*
* 2. Go over every block in chronological order . . .
*
* Dial down junction speed reduction values if :
* a . The speed increase within one block would require faster
* acceleration than the one , true constant acceleration .
*
* After that , all blocks will have an entry_factor allowing all speed changes to
* be performed using only the one , true constant acceleration , and where no junction
* jerk is jerkier than the set limit , Jerky . Finally it will :
*
* 3. Recalculate " trapezoids " for all blocks .
*/
2018-11-16 03:32:01 -06:00
void Planner : : recalculate ( ) {
2019-01-31 05:25:28 -06:00
reverse_pass ( ) ;
forward_pass ( ) ;
2018-11-16 03:32:01 -06:00
recalculate_trapezoids ( ) ;
}
2019-01-31 05:25:28 -06:00
2018-11-16 03:32:01 -06:00
# if ENABLED(AUTOTEMP)
void Planner : : getHighESpeed ( ) {
static float oldt = 0 ;
if ( ! autotemp_enabled ) return ;
if ( thermalManager . degTargetHotend ( 0 ) + 2 < autotemp_min ) return ; // probably temperature set to zero.
float high = 0.0 ;
for ( uint8_t b = block_buffer_tail ; b ! = block_buffer_head ; b = next_block_index ( b ) ) {
block_t * block = & block_buffer [ b ] ;
2019-01-31 05:25:28 -06:00
if ( block - > steps [ X_AXIS ] | | block - > steps [ Y_AXIS ] | | block - > steps [ Z_AXIS ] ) {
float se = ( float ) block - > steps [ E_AXIS ] / block - > step_event_count * block - > nominal_speed ; // mm/sec;
2018-11-16 03:32:01 -06:00
NOLESS ( high , se ) ;
}
}
float t = autotemp_min + high * autotemp_factor ;
t = constrain ( t , autotemp_min , autotemp_max ) ;
2019-01-31 05:25:28 -06:00
if ( t < oldt ) t = t * ( 1 - ( AUTOTEMP_OLDWEIGHT ) ) + oldt * ( AUTOTEMP_OLDWEIGHT ) ;
2018-11-16 03:32:01 -06:00
oldt = t ;
thermalManager . setTargetHotend ( t , 0 ) ;
}
# endif // AUTOTEMP
/**
* Maintain fans , paste extruder pressure ,
*/
void Planner : : check_axes_activity ( ) {
unsigned char axis_active [ NUM_AXIS ] = { 0 } ,
tail_fan_speed [ FAN_COUNT ] ;
# if ENABLED(BARICUDA)
# if HAS_HEATER_1
uint8_t tail_valve_pressure ;
# endif
# if HAS_HEATER_2
uint8_t tail_e_to_p_pressure ;
# endif
# endif
2019-01-31 05:25:28 -06:00
if ( blocks_queued ( ) ) {
2018-11-16 03:32:01 -06:00
# if FAN_COUNT > 0
for ( uint8_t i = 0 ; i < FAN_COUNT ; i + + )
tail_fan_speed [ i ] = block_buffer [ block_buffer_tail ] . fan_speed [ i ] ;
# endif
block_t * block ;
# if ENABLED(BARICUDA)
block = & block_buffer [ block_buffer_tail ] ;
# if HAS_HEATER_1
tail_valve_pressure = block - > valve_pressure ;
# endif
# if HAS_HEATER_2
tail_e_to_p_pressure = block - > e_to_p_pressure ;
# endif
# endif
for ( uint8_t b = block_buffer_tail ; b ! = block_buffer_head ; b = next_block_index ( b ) ) {
block = & block_buffer [ b ] ;
LOOP_XYZE ( i ) if ( block - > steps [ i ] ) axis_active [ i ] + + ;
}
}
else {
# if FAN_COUNT > 0
for ( uint8_t i = 0 ; i < FAN_COUNT ; i + + ) tail_fan_speed [ i ] = fanSpeeds [ i ] ;
# endif
# if ENABLED(BARICUDA)
# if HAS_HEATER_1
tail_valve_pressure = baricuda_valve_pressure ;
# endif
# if HAS_HEATER_2
tail_e_to_p_pressure = baricuda_e_to_p_pressure ;
# endif
# endif
}
# if ENABLED(DISABLE_X)
if ( ! axis_active [ X_AXIS ] ) disable_X ( ) ;
# endif
# if ENABLED(DISABLE_Y)
if ( ! axis_active [ Y_AXIS ] ) disable_Y ( ) ;
# endif
# if ENABLED(DISABLE_Z)
if ( ! axis_active [ Z_AXIS ] ) disable_Z ( ) ;
# endif
# if ENABLED(DISABLE_E)
if ( ! axis_active [ E_AXIS ] ) disable_e_steppers ( ) ;
# endif
# if FAN_COUNT > 0
# if FAN_KICKSTART_TIME > 0
static millis_t fan_kick_end [ FAN_COUNT ] = { 0 } ;
# define KICKSTART_FAN(f) \
if ( tail_fan_speed [ f ] ) { \
millis_t ms = millis ( ) ; \
if ( fan_kick_end [ f ] = = 0 ) { \
fan_kick_end [ f ] = ms + FAN_KICKSTART_TIME ; \
tail_fan_speed [ f ] = 255 ; \
} else if ( PENDING ( ms , fan_kick_end [ f ] ) ) \
tail_fan_speed [ f ] = 255 ; \
} else fan_kick_end [ f ] = 0
# if HAS_FAN0
KICKSTART_FAN ( 0 ) ;
# endif
# if HAS_FAN1
KICKSTART_FAN ( 1 ) ;
# endif
# if HAS_FAN2
KICKSTART_FAN ( 2 ) ;
# endif
# endif // FAN_KICKSTART_TIME > 0
2019-01-31 05:25:28 -06:00
# ifdef FAN_MIN_PWM
# define CALC_FAN_SPEED(f) (tail_fan_speed[f] ? ( FAN_MIN_PWM + (tail_fan_speed[f] * (255 - FAN_MIN_PWM)) / 255 ) : 0)
2018-11-16 03:32:01 -06:00
# else
# define CALC_FAN_SPEED(f) tail_fan_speed[f]
# endif
# if ENABLED(FAN_SOFT_PWM)
# if HAS_FAN0
thermalManager . soft_pwm_amount_fan [ 0 ] = CALC_FAN_SPEED ( 0 ) ;
# endif
# if HAS_FAN1
thermalManager . soft_pwm_amount_fan [ 1 ] = CALC_FAN_SPEED ( 1 ) ;
# endif
# if HAS_FAN2
thermalManager . soft_pwm_amount_fan [ 2 ] = CALC_FAN_SPEED ( 2 ) ;
# endif
# else
# if HAS_FAN0
analogWrite ( FAN_PIN , CALC_FAN_SPEED ( 0 ) ) ;
# endif
# if HAS_FAN1
analogWrite ( FAN1_PIN , CALC_FAN_SPEED ( 1 ) ) ;
# endif
# if HAS_FAN2
analogWrite ( FAN2_PIN , CALC_FAN_SPEED ( 2 ) ) ;
# endif
# endif
# endif // FAN_COUNT > 0
# if ENABLED(AUTOTEMP)
getHighESpeed ( ) ;
# endif
# if ENABLED(BARICUDA)
# if HAS_HEATER_1
analogWrite ( HEATER_1_PIN , tail_valve_pressure ) ;
# endif
# if HAS_HEATER_2
analogWrite ( HEATER_2_PIN , tail_e_to_p_pressure ) ;
# endif
# endif
}
# if DISABLED(NO_VOLUMETRICS)
/**
* Get a volumetric multiplier from a filament diameter .
* This is the reciprocal of the circular cross - section area .
* Return 1.0 with volumetric off or a diameter of 0.0 .
*/
inline float calculate_volumetric_multiplier ( const float & diameter ) {
2019-01-31 05:25:28 -06:00
return ( parser . volumetric_enabled & & diameter ) ? 1.0 / CIRCLE_AREA ( diameter * 0.5 ) : 1.0 ;
2018-11-16 03:32:01 -06:00
}
/**
* Convert the filament sizes into volumetric multipliers .
* The multiplier converts a given E value into a length .
*/
void Planner : : calculate_volumetric_multipliers ( ) {
for ( uint8_t i = 0 ; i < COUNT ( filament_size ) ; i + + ) {
volumetric_multiplier [ i ] = calculate_volumetric_multiplier ( filament_size [ i ] ) ;
refresh_e_factor ( i ) ;
}
}
# endif // !NO_VOLUMETRICS
# if ENABLED(FILAMENT_WIDTH_SENSOR)
/**
* Convert the ratio value given by the filament width sensor
* into a volumetric multiplier . Conversion differs when using
* linear extrusion vs volumetric extrusion .
*/
void Planner : : calculate_volumetric_for_width_sensor ( const int8_t encoded_ratio ) {
// Reconstitute the nominal/measured ratio
2019-01-31 05:25:28 -06:00
const float nom_meas_ratio = 1.0 + 0.01 * encoded_ratio ,
2018-11-16 03:32:01 -06:00
ratio_2 = sq ( nom_meas_ratio ) ;
volumetric_multiplier [ FILAMENT_SENSOR_EXTRUDER_NUM ] = parser . volumetric_enabled
2019-01-31 05:25:28 -06:00
? ratio_2 / CIRCLE_AREA ( filament_width_nominal * 0.5 ) // Volumetric uses a true volumetric multiplier
: ratio_2 ; // Linear squares the ratio, which scales the volume
2018-11-16 03:32:01 -06:00
refresh_e_factor ( FILAMENT_SENSOR_EXTRUDER_NUM ) ;
}
# endif
2019-01-31 05:25:28 -06:00
# if PLANNER_LEVELING
2018-11-16 03:32:01 -06:00
/**
* rx , ry , rz - Cartesian positions in mm
* Leveled XYZ on completion
*/
void Planner : : apply_leveling ( float & rx , float & ry , float & rz ) {
# if ENABLED(SKEW_CORRECTION)
skew ( rx , ry , rz ) ;
# endif
if ( ! leveling_active ) return ;
# if ABL_PLANAR
float dx = rx - ( X_TILT_FULCRUM ) ,
dy = ry - ( Y_TILT_FULCRUM ) ;
apply_rotation_xyz ( bed_level_matrix , dx , dy , rz ) ;
rx = dx + X_TILT_FULCRUM ;
ry = dy + Y_TILT_FULCRUM ;
2019-01-31 05:25:28 -06:00
# else
2018-11-16 03:32:01 -06:00
# if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
const float fade_scaling_factor = fade_scaling_factor_for_z ( rz ) ;
2019-01-31 05:25:28 -06:00
if ( ! fade_scaling_factor ) return ;
# elif HAS_MESH
2018-11-16 03:32:01 -06:00
constexpr float fade_scaling_factor = 1.0 ;
# endif
# if ENABLED(AUTO_BED_LEVELING_BILINEAR)
const float raw [ XYZ ] = { rx , ry , 0 } ;
# endif
rz + = (
2019-01-31 05:25:28 -06:00
# if ENABLED(AUTO_BED_LEVELING_UBL)
ubl . get_z_correction ( rx , ry ) * fade_scaling_factor
# elif ENABLED(MESH_BED_LEVELING)
2018-11-16 03:32:01 -06:00
mbl . get_z ( rx , ry
# if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
, fade_scaling_factor
# endif
)
# elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
2019-01-31 05:25:28 -06:00
bilinear_z_offset ( raw ) * fade_scaling_factor
# else
0
2018-11-16 03:32:01 -06:00
# endif
) ;
# endif
}
void Planner : : unapply_leveling ( float raw [ XYZ ] ) {
2019-01-31 05:25:28 -06:00
# if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
const float fade_scaling_factor = fade_scaling_factor_for_z ( raw [ Z_AXIS ] ) ;
# else
constexpr float fade_scaling_factor = 1.0 ;
# endif
if ( leveling_active & & fade_scaling_factor ) {
2018-11-16 03:32:01 -06:00
# if ABL_PLANAR
matrix_3x3 inverse = matrix_3x3 : : transpose ( bed_level_matrix ) ;
float dx = raw [ X_AXIS ] - ( X_TILT_FULCRUM ) ,
dy = raw [ Y_AXIS ] - ( Y_TILT_FULCRUM ) ;
apply_rotation_xyz ( inverse , dx , dy , raw [ Z_AXIS ] ) ;
raw [ X_AXIS ] = dx + X_TILT_FULCRUM ;
raw [ Y_AXIS ] = dy + Y_TILT_FULCRUM ;
2019-01-31 05:25:28 -06:00
# else // !ABL_PLANAR
2018-11-16 03:32:01 -06:00
raw [ Z_AXIS ] - = (
2019-01-31 05:25:28 -06:00
# if ENABLED(AUTO_BED_LEVELING_UBL)
ubl . get_z_correction ( raw [ X_AXIS ] , raw [ Y_AXIS ] ) * fade_scaling_factor
# elif ENABLED(MESH_BED_LEVELING)
2018-11-16 03:32:01 -06:00
mbl . get_z ( raw [ X_AXIS ] , raw [ Y_AXIS ]
# if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
, fade_scaling_factor
# endif
)
# elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
2019-01-31 05:25:28 -06:00
bilinear_z_offset ( raw ) * fade_scaling_factor
# else
0
2018-11-16 03:32:01 -06:00
# endif
) ;
2019-01-31 05:25:28 -06:00
# endif // !ABL_PLANAR
2018-11-16 03:32:01 -06:00
}
# if ENABLED(SKEW_CORRECTION)
unskew ( raw [ X_AXIS ] , raw [ Y_AXIS ] , raw [ Z_AXIS ] ) ;
# endif
}
# endif // PLANNER_LEVELING
/**
* Planner : : _buffer_steps
*
2019-01-31 05:25:28 -06:00
* Add a new linear movement to the buffer ( in terms of steps ) .
2018-11-16 03:32:01 -06:00
*
2019-01-31 05:25:28 -06:00
* target - target position in steps units
* fr_mm_s - ( target ) speed of the move
* extruder - target extruder
2018-11-16 03:32:01 -06:00
*/
2019-01-31 05:25:28 -06:00
void Planner : : _buffer_steps ( const int32_t ( & target ) [ XYZE ] , float fr_mm_s , const uint8_t extruder ) {
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
const int32_t da = target [ X_AXIS ] - position [ X_AXIS ] ,
db = target [ Y_AXIS ] - position [ Y_AXIS ] ,
dc = target [ Z_AXIS ] - position [ Z_AXIS ] ;
2018-11-16 03:32:01 -06:00
int32_t de = target [ E_AXIS ] - position [ E_AXIS ] ;
/* <-- add a slash to enable
2019-01-31 05:25:28 -06:00
SERIAL_ECHOPAIR ( " _buffer_steps FR: " , fr_mm_s ) ;
2018-11-16 03:32:01 -06:00
SERIAL_ECHOPAIR ( " A: " , target [ A_AXIS ] ) ;
SERIAL_ECHOPAIR ( " ( " , da ) ;
SERIAL_ECHOPAIR ( " steps) B: " , target [ B_AXIS ] ) ;
SERIAL_ECHOPAIR ( " ( " , db ) ;
SERIAL_ECHOPAIR ( " steps) C: " , target [ C_AXIS ] ) ;
SERIAL_ECHOPAIR ( " ( " , dc ) ;
SERIAL_ECHOPAIR ( " steps) E: " , target [ E_AXIS ] ) ;
SERIAL_ECHOPAIR ( " ( " , de ) ;
SERIAL_ECHOLNPGM ( " steps) " ) ;
//*/
2019-01-31 05:25:28 -06:00
// If LIN_ADVANCE is disabled then do E move prevention with integers
// Otherwise it's done in _buffer_segment.
# if DISABLED(LIN_ADVANCE) && (ENABLED(PREVENT_COLD_EXTRUSION) || ENABLED(PREVENT_LENGTHY_EXTRUDE))
2018-11-16 03:32:01 -06:00
if ( de ) {
# if ENABLED(PREVENT_COLD_EXTRUSION)
if ( thermalManager . tooColdToExtrude ( extruder ) ) {
2019-01-31 05:25:28 -06:00
position [ E_AXIS ] = target [ E_AXIS ] ; // Behave as if the move really took place, but ignore E part
2018-11-16 03:32:01 -06:00
de = 0 ; // no difference
SERIAL_ECHO_START ( ) ;
SERIAL_ECHOLNPGM ( MSG_ERR_COLD_EXTRUDE_STOP ) ;
}
# endif // PREVENT_COLD_EXTRUSION
# if ENABLED(PREVENT_LENGTHY_EXTRUDE)
2019-01-31 05:25:28 -06:00
if ( labs ( de * e_factor [ extruder ] ) > ( int32_t ) axis_steps_per_mm [ E_AXIS_N ] * ( EXTRUDE_MAXLENGTH ) ) { // It's not important to get max. extrusion length in a precision < 1mm, so save some cycles and cast to int
position [ E_AXIS ] = target [ E_AXIS ] ; // Behave as if the move really took place, but ignore E part
2018-11-16 03:32:01 -06:00
de = 0 ; // no difference
SERIAL_ECHO_START ( ) ;
SERIAL_ECHOLNPGM ( MSG_ERR_LONG_EXTRUDE_STOP ) ;
}
# endif // PREVENT_LENGTHY_EXTRUDE
}
2019-01-31 05:25:28 -06:00
# endif // !LIN_ADVANCE && (PREVENT_COLD_EXTRUSION || PREVENT_LENGTHY_EXTRUDE)
2018-11-16 03:32:01 -06:00
// Compute direction bit-mask for this block
uint8_t dm = 0 ;
# if CORE_IS_XY
if ( da < 0 ) SBI ( dm , X_HEAD ) ; // Save the real Extruder (head) direction in X Axis
if ( db < 0 ) SBI ( dm , Y_HEAD ) ; // ...and Y
if ( dc < 0 ) SBI ( dm , Z_AXIS ) ;
if ( da + db < 0 ) SBI ( dm , A_AXIS ) ; // Motor A direction
if ( CORESIGN ( da - db ) < 0 ) SBI ( dm , B_AXIS ) ; // Motor B direction
# elif CORE_IS_XZ
if ( da < 0 ) SBI ( dm , X_HEAD ) ; // Save the real Extruder (head) direction in X Axis
if ( db < 0 ) SBI ( dm , Y_AXIS ) ;
if ( dc < 0 ) SBI ( dm , Z_HEAD ) ; // ...and Z
if ( da + dc < 0 ) SBI ( dm , A_AXIS ) ; // Motor A direction
if ( CORESIGN ( da - dc ) < 0 ) SBI ( dm , C_AXIS ) ; // Motor C direction
# elif CORE_IS_YZ
if ( da < 0 ) SBI ( dm , X_AXIS ) ;
if ( db < 0 ) SBI ( dm , Y_HEAD ) ; // Save the real Extruder (head) direction in Y Axis
if ( dc < 0 ) SBI ( dm , Z_HEAD ) ; // ...and Z
if ( db + dc < 0 ) SBI ( dm , B_AXIS ) ; // Motor B direction
if ( CORESIGN ( db - dc ) < 0 ) SBI ( dm , C_AXIS ) ; // Motor C direction
# else
if ( da < 0 ) SBI ( dm , X_AXIS ) ;
if ( db < 0 ) SBI ( dm , Y_AXIS ) ;
if ( dc < 0 ) SBI ( dm , Z_AXIS ) ;
# endif
if ( de < 0 ) SBI ( dm , E_AXIS ) ;
const float esteps_float = de * e_factor [ extruder ] ;
2019-01-31 05:25:28 -06:00
const int32_t esteps = abs ( esteps_float ) + 0.5 ;
// Calculate the buffer head after we push this byte
const uint8_t next_buffer_head = next_block_index ( block_buffer_head ) ;
// If the buffer is full: good! That means we are well ahead of the robot.
// Rest here until there is room in the buffer.
while ( block_buffer_tail = = next_buffer_head ) idle ( ) ;
// Prepare to set up new block
block_t * block = & block_buffer [ block_buffer_head ] ;
2018-11-16 03:32:01 -06:00
// Clear all flags, including the "busy" bit
block - > flag = 0x00 ;
// Set direction bits
block - > direction_bits = dm ;
// Number of steps for each axis
// See http://www.corexy.com/theory.html
# if CORE_IS_XY
2019-01-31 05:25:28 -06:00
block - > steps [ A_AXIS ] = labs ( da + db ) ;
block - > steps [ B_AXIS ] = labs ( da - db ) ;
block - > steps [ Z_AXIS ] = labs ( dc ) ;
2018-11-16 03:32:01 -06:00
# elif CORE_IS_XZ
2019-01-31 05:25:28 -06:00
block - > steps [ A_AXIS ] = labs ( da + dc ) ;
block - > steps [ Y_AXIS ] = labs ( db ) ;
block - > steps [ C_AXIS ] = labs ( da - dc ) ;
2018-11-16 03:32:01 -06:00
# elif CORE_IS_YZ
2019-01-31 05:25:28 -06:00
block - > steps [ X_AXIS ] = labs ( da ) ;
block - > steps [ B_AXIS ] = labs ( db + dc ) ;
block - > steps [ C_AXIS ] = labs ( db - dc ) ;
2018-11-16 03:32:01 -06:00
# else
// default non-h-bot planning
2019-01-31 05:25:28 -06:00
block - > steps [ X_AXIS ] = labs ( da ) ;
block - > steps [ Y_AXIS ] = labs ( db ) ;
block - > steps [ Z_AXIS ] = labs ( dc ) ;
2018-11-16 03:32:01 -06:00
# endif
block - > steps [ E_AXIS ] = esteps ;
2019-01-31 05:25:28 -06:00
block - > step_event_count = MAX4 ( block - > steps [ X_AXIS ] , block - > steps [ Y_AXIS ] , block - > steps [ Z_AXIS ] , esteps ) ;
2018-11-16 03:32:01 -06:00
// Bail if this is a zero-length block
2019-01-31 05:25:28 -06:00
if ( block - > step_event_count < MIN_STEPS_PER_SEGMENT ) return ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
// For a mixing extruder, get a magnified step_event_count for each
2018-11-16 03:32:01 -06:00
# if ENABLED(MIXING_EXTRUDER)
for ( uint8_t i = 0 ; i < MIXING_STEPPERS ; i + + )
2019-01-31 05:25:28 -06:00
block - > mix_event_count [ i ] = mixing_factor [ i ] * block - > step_event_count ;
2018-11-16 03:32:01 -06:00
# endif
# if FAN_COUNT > 0
for ( uint8_t i = 0 ; i < FAN_COUNT ; i + + ) block - > fan_speed [ i ] = fanSpeeds [ i ] ;
# endif
# if ENABLED(BARICUDA)
block - > valve_pressure = baricuda_valve_pressure ;
block - > e_to_p_pressure = baricuda_e_to_p_pressure ;
# endif
block - > active_extruder = extruder ;
2019-01-31 05:25:28 -06:00
//enable active axes
2018-11-16 03:32:01 -06:00
# if CORE_IS_XY
if ( block - > steps [ A_AXIS ] | | block - > steps [ B_AXIS ] ) {
enable_X ( ) ;
enable_Y ( ) ;
}
# if DISABLED(Z_LATE_ENABLE)
if ( block - > steps [ Z_AXIS ] ) enable_Z ( ) ;
# endif
# elif CORE_IS_XZ
if ( block - > steps [ A_AXIS ] | | block - > steps [ C_AXIS ] ) {
enable_X ( ) ;
enable_Z ( ) ;
}
if ( block - > steps [ Y_AXIS ] ) enable_Y ( ) ;
# elif CORE_IS_YZ
if ( block - > steps [ B_AXIS ] | | block - > steps [ C_AXIS ] ) {
enable_Y ( ) ;
enable_Z ( ) ;
}
if ( block - > steps [ X_AXIS ] ) enable_X ( ) ;
2019-01-31 05:25:28 -06:00
# else
2018-11-16 03:32:01 -06:00
if ( block - > steps [ X_AXIS ] ) enable_X ( ) ;
if ( block - > steps [ Y_AXIS ] ) enable_Y ( ) ;
# if DISABLED(Z_LATE_ENABLE)
if ( block - > steps [ Z_AXIS ] ) enable_Z ( ) ;
# endif
# endif
// Enable extruder(s)
if ( esteps ) {
# if ENABLED(DISABLE_INACTIVE_EXTRUDER) // Enable only the selected extruder
# define DISABLE_IDLE_E(N) if (!g_uc_extruder_last_move[N]) disable_E##N();
for ( uint8_t i = 0 ; i < EXTRUDERS ; i + + )
if ( g_uc_extruder_last_move [ i ] > 0 ) g_uc_extruder_last_move [ i ] - - ;
2019-01-31 05:25:28 -06:00
switch ( extruder ) {
2018-11-16 03:32:01 -06:00
case 0 :
2019-01-31 05:25:28 -06:00
enable_E0 ( ) ;
g_uc_extruder_last_move [ 0 ] = ( BLOCK_BUFFER_SIZE ) * 2 ;
# if ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE)
if ( extruder_duplication_enabled ) {
enable_E1 ( ) ;
g_uc_extruder_last_move [ 1 ] = ( BLOCK_BUFFER_SIZE ) * 2 ;
}
# endif
2018-11-16 03:32:01 -06:00
# if EXTRUDERS > 1
DISABLE_IDLE_E ( 1 ) ;
# if EXTRUDERS > 2
DISABLE_IDLE_E ( 2 ) ;
# if EXTRUDERS > 3
DISABLE_IDLE_E ( 3 ) ;
# if EXTRUDERS > 4
DISABLE_IDLE_E ( 4 ) ;
# endif // EXTRUDERS > 4
# endif // EXTRUDERS > 3
# endif // EXTRUDERS > 2
# endif // EXTRUDERS > 1
break ;
# if EXTRUDERS > 1
case 1 :
2019-01-31 05:25:28 -06:00
enable_E1 ( ) ;
g_uc_extruder_last_move [ 1 ] = ( BLOCK_BUFFER_SIZE ) * 2 ;
2018-11-16 03:32:01 -06:00
DISABLE_IDLE_E ( 0 ) ;
# if EXTRUDERS > 2
DISABLE_IDLE_E ( 2 ) ;
# if EXTRUDERS > 3
DISABLE_IDLE_E ( 3 ) ;
# if EXTRUDERS > 4
DISABLE_IDLE_E ( 4 ) ;
# endif // EXTRUDERS > 4
# endif // EXTRUDERS > 3
# endif // EXTRUDERS > 2
break ;
# if EXTRUDERS > 2
case 2 :
2019-01-31 05:25:28 -06:00
enable_E2 ( ) ;
g_uc_extruder_last_move [ 2 ] = ( BLOCK_BUFFER_SIZE ) * 2 ;
2018-11-16 03:32:01 -06:00
DISABLE_IDLE_E ( 0 ) ;
DISABLE_IDLE_E ( 1 ) ;
# if EXTRUDERS > 3
DISABLE_IDLE_E ( 3 ) ;
# if EXTRUDERS > 4
DISABLE_IDLE_E ( 4 ) ;
# endif
# endif
break ;
# if EXTRUDERS > 3
case 3 :
2019-01-31 05:25:28 -06:00
enable_E3 ( ) ;
g_uc_extruder_last_move [ 3 ] = ( BLOCK_BUFFER_SIZE ) * 2 ;
2018-11-16 03:32:01 -06:00
DISABLE_IDLE_E ( 0 ) ;
DISABLE_IDLE_E ( 1 ) ;
DISABLE_IDLE_E ( 2 ) ;
# if EXTRUDERS > 4
DISABLE_IDLE_E ( 4 ) ;
# endif
break ;
# if EXTRUDERS > 4
case 4 :
2019-01-31 05:25:28 -06:00
enable_E4 ( ) ;
g_uc_extruder_last_move [ 4 ] = ( BLOCK_BUFFER_SIZE ) * 2 ;
2018-11-16 03:32:01 -06:00
DISABLE_IDLE_E ( 0 ) ;
DISABLE_IDLE_E ( 1 ) ;
DISABLE_IDLE_E ( 2 ) ;
DISABLE_IDLE_E ( 3 ) ;
break ;
# endif // EXTRUDERS > 4
# endif // EXTRUDERS > 3
# endif // EXTRUDERS > 2
# endif // EXTRUDERS > 1
}
# else
enable_E0 ( ) ;
enable_E1 ( ) ;
enable_E2 ( ) ;
enable_E3 ( ) ;
enable_E4 ( ) ;
# endif
}
if ( esteps )
NOLESS ( fr_mm_s , min_feedrate_mm_s ) ;
else
NOLESS ( fr_mm_s , min_travel_feedrate_mm_s ) ;
/**
* This part of the code calculates the total length of the movement .
* For cartesian bots , the X_AXIS is the real X movement and same for Y_AXIS .
* But for corexy bots , that is not true . The " X_AXIS " and " Y_AXIS " motors ( that should be named to A_AXIS
* and B_AXIS ) cannot be used for X and Y length , because A = X + Y and B = X - Y .
* So we need to create other 2 " AXIS " , named X_HEAD and Y_HEAD , meaning the real displacement of the Head .
* Having the real displacement of the head , we can calculate the total movement length and apply the desired speed .
*/
# if IS_CORE
float delta_mm [ Z_HEAD + 1 ] ;
# if CORE_IS_XY
delta_mm [ X_HEAD ] = da * steps_to_mm [ A_AXIS ] ;
delta_mm [ Y_HEAD ] = db * steps_to_mm [ B_AXIS ] ;
delta_mm [ Z_AXIS ] = dc * steps_to_mm [ Z_AXIS ] ;
delta_mm [ A_AXIS ] = ( da + db ) * steps_to_mm [ A_AXIS ] ;
delta_mm [ B_AXIS ] = CORESIGN ( da - db ) * steps_to_mm [ B_AXIS ] ;
# elif CORE_IS_XZ
delta_mm [ X_HEAD ] = da * steps_to_mm [ A_AXIS ] ;
delta_mm [ Y_AXIS ] = db * steps_to_mm [ Y_AXIS ] ;
delta_mm [ Z_HEAD ] = dc * steps_to_mm [ C_AXIS ] ;
delta_mm [ A_AXIS ] = ( da + dc ) * steps_to_mm [ A_AXIS ] ;
delta_mm [ C_AXIS ] = CORESIGN ( da - dc ) * steps_to_mm [ C_AXIS ] ;
# elif CORE_IS_YZ
delta_mm [ X_AXIS ] = da * steps_to_mm [ X_AXIS ] ;
delta_mm [ Y_HEAD ] = db * steps_to_mm [ B_AXIS ] ;
delta_mm [ Z_HEAD ] = dc * steps_to_mm [ C_AXIS ] ;
delta_mm [ B_AXIS ] = ( db + dc ) * steps_to_mm [ B_AXIS ] ;
delta_mm [ C_AXIS ] = CORESIGN ( db - dc ) * steps_to_mm [ C_AXIS ] ;
# endif
# else
2019-01-31 05:25:28 -06:00
float delta_mm [ XYZE ] ;
delta_mm [ X_AXIS ] = da * steps_to_mm [ X_AXIS ] ;
delta_mm [ Y_AXIS ] = db * steps_to_mm [ Y_AXIS ] ;
delta_mm [ Z_AXIS ] = dc * steps_to_mm [ Z_AXIS ] ;
2018-11-16 03:32:01 -06:00
# endif
delta_mm [ E_AXIS ] = esteps_float * steps_to_mm [ E_AXIS_N ] ;
2019-01-31 05:25:28 -06:00
if ( block - > steps [ X_AXIS ] < MIN_STEPS_PER_SEGMENT & & block - > steps [ Y_AXIS ] < MIN_STEPS_PER_SEGMENT & & block - > steps [ Z_AXIS ] < MIN_STEPS_PER_SEGMENT ) {
block - > millimeters = FABS ( delta_mm [ E_AXIS ] ) ;
2018-11-16 03:32:01 -06:00
}
2019-01-31 05:25:28 -06:00
else {
2018-11-16 03:32:01 -06:00
block - > millimeters = SQRT (
# if CORE_IS_XY
sq ( delta_mm [ X_HEAD ] ) + sq ( delta_mm [ Y_HEAD ] ) + sq ( delta_mm [ Z_AXIS ] )
# elif CORE_IS_XZ
sq ( delta_mm [ X_HEAD ] ) + sq ( delta_mm [ Y_AXIS ] ) + sq ( delta_mm [ Z_HEAD ] )
# elif CORE_IS_YZ
sq ( delta_mm [ X_AXIS ] ) + sq ( delta_mm [ Y_HEAD ] ) + sq ( delta_mm [ Z_HEAD ] )
# else
sq ( delta_mm [ X_AXIS ] ) + sq ( delta_mm [ Y_AXIS ] ) + sq ( delta_mm [ Z_AXIS ] )
# endif
) ;
}
2019-01-31 05:25:28 -06:00
const float inverse_millimeters = 1.0 / block - > millimeters ; // Inverse millimeters to remove multiple divides
2018-11-16 03:32:01 -06:00
// Calculate inverse time for this move. No divide by zero due to previous checks.
// Example: At 120mm/s a 60mm move takes 0.5s. So this will give 2.0.
float inverse_secs = fr_mm_s * inverse_millimeters ;
2019-01-31 05:25:28 -06:00
const uint8_t moves_queued = movesplanned ( ) ;
2018-11-16 03:32:01 -06:00
// Slow down when the buffer starts to empty, rather than wait at the corner for a buffer refill
# if ENABLED(SLOWDOWN) || ENABLED(ULTRA_LCD) || defined(XY_FREQUENCY_LIMIT)
// Segment time im micro seconds
2019-01-31 05:25:28 -06:00
uint32_t segment_time_us = LROUND ( 1000000.0 / inverse_secs ) ;
2018-11-16 03:32:01 -06:00
# endif
# if ENABLED(SLOWDOWN)
if ( WITHIN ( moves_queued , 2 , ( BLOCK_BUFFER_SIZE ) / 2 - 1 ) ) {
if ( segment_time_us < min_segment_time_us ) {
// buffer is draining, add extra time. The amount of time added increases if the buffer is still emptied more.
const uint32_t nst = segment_time_us + LROUND ( 2 * ( min_segment_time_us - segment_time_us ) / moves_queued ) ;
2019-01-31 05:25:28 -06:00
inverse_secs = 1000000.0 / nst ;
2018-11-16 03:32:01 -06:00
# if defined(XY_FREQUENCY_LIMIT) || ENABLED(ULTRA_LCD)
segment_time_us = nst ;
# endif
}
}
# endif
# if ENABLED(ULTRA_LCD)
2019-01-31 05:25:28 -06:00
CRITICAL_SECTION_START
block_buffer_runtime_us + = segment_time_us ;
CRITICAL_SECTION_END
2018-11-16 03:32:01 -06:00
# endif
2019-01-31 05:25:28 -06:00
block - > nominal_speed = block - > millimeters * inverse_secs ; // (mm/sec) Always > 0
2018-11-16 03:32:01 -06:00
block - > nominal_rate = CEIL ( block - > step_event_count * inverse_secs ) ; // (step/sec) Always > 0
# if ENABLED(FILAMENT_WIDTH_SENSOR)
static float filwidth_e_count = 0 , filwidth_delay_dist = 0 ;
//FMM update ring buffer used for delay with filament measurements
if ( extruder = = FILAMENT_SENSOR_EXTRUDER_NUM & & filwidth_delay_index [ 1 ] > = 0 ) { //only for extruder with filament sensor and if ring buffer is initialized
constexpr int MMD_CM = MAX_MEASUREMENT_DELAY + 1 , MMD_MM = MMD_CM * 10 ;
// increment counters with next move in e axis
filwidth_e_count + = delta_mm [ E_AXIS ] ;
filwidth_delay_dist + = delta_mm [ E_AXIS ] ;
// Only get new measurements on forward E movement
if ( ! UNEAR_ZERO ( filwidth_e_count ) ) {
// Loop the delay distance counter (modulus by the mm length)
while ( filwidth_delay_dist > = MMD_MM ) filwidth_delay_dist - = MMD_MM ;
// Convert into an index into the measurement array
2019-01-31 05:25:28 -06:00
filwidth_delay_index [ 0 ] = int8_t ( filwidth_delay_dist * 0.1 ) ;
2018-11-16 03:32:01 -06:00
// If the index has changed (must have gone forward)...
if ( filwidth_delay_index [ 0 ] ! = filwidth_delay_index [ 1 ] ) {
filwidth_e_count = 0 ; // Reset the E movement counter
const int8_t meas_sample = thermalManager . widthFil_to_size_ratio ( ) ;
do {
filwidth_delay_index [ 1 ] = ( filwidth_delay_index [ 1 ] + 1 ) % MMD_CM ; // The next unused slot
measurement_delay [ filwidth_delay_index [ 1 ] ] = meas_sample ; // Store the measurement
} while ( filwidth_delay_index [ 0 ] ! = filwidth_delay_index [ 1 ] ) ; // More slots to fill?
}
}
}
# endif
// Calculate and limit speed in mm/sec for each axis
2019-01-31 05:25:28 -06:00
float current_speed [ NUM_AXIS ] , speed_factor = 1.0 ; // factor <1 decreases speed
LOOP_XYZE ( i ) {
const float cs = FABS ( ( current_speed [ i ] = delta_mm [ i ] * inverse_secs ) ) ;
2018-11-16 03:32:01 -06:00
# if ENABLED(DISTINCT_E_FACTORS)
if ( i = = E_AXIS ) i + = extruder ;
# endif
if ( cs > max_feedrate_mm_s [ i ] ) NOMORE ( speed_factor , max_feedrate_mm_s [ i ] / cs ) ;
}
// Max segment time in µs.
# ifdef XY_FREQUENCY_LIMIT
// Check and limit the xy direction change frequency
const unsigned char direction_change = block - > direction_bits ^ old_direction_bits ;
old_direction_bits = block - > direction_bits ;
segment_time_us = LROUND ( ( float ) segment_time_us / speed_factor ) ;
uint32_t xs0 = axis_segment_time_us [ X_AXIS ] [ 0 ] ,
xs1 = axis_segment_time_us [ X_AXIS ] [ 1 ] ,
xs2 = axis_segment_time_us [ X_AXIS ] [ 2 ] ,
ys0 = axis_segment_time_us [ Y_AXIS ] [ 0 ] ,
ys1 = axis_segment_time_us [ Y_AXIS ] [ 1 ] ,
ys2 = axis_segment_time_us [ Y_AXIS ] [ 2 ] ;
if ( TEST ( direction_change , X_AXIS ) ) {
xs2 = axis_segment_time_us [ X_AXIS ] [ 2 ] = xs1 ;
xs1 = axis_segment_time_us [ X_AXIS ] [ 1 ] = xs0 ;
xs0 = 0 ;
}
xs0 = axis_segment_time_us [ X_AXIS ] [ 0 ] = xs0 + segment_time_us ;
if ( TEST ( direction_change , Y_AXIS ) ) {
ys2 = axis_segment_time_us [ Y_AXIS ] [ 2 ] = axis_segment_time_us [ Y_AXIS ] [ 1 ] ;
ys1 = axis_segment_time_us [ Y_AXIS ] [ 1 ] = axis_segment_time_us [ Y_AXIS ] [ 0 ] ;
ys0 = 0 ;
}
ys0 = axis_segment_time_us [ Y_AXIS ] [ 0 ] = ys0 + segment_time_us ;
const uint32_t max_x_segment_time = MAX3 ( xs0 , xs1 , xs2 ) ,
max_y_segment_time = MAX3 ( ys0 , ys1 , ys2 ) ,
2019-01-31 05:25:28 -06:00
min_xy_segment_time = min ( max_x_segment_time , max_y_segment_time ) ;
2018-11-16 03:32:01 -06:00
if ( min_xy_segment_time < MAX_FREQ_TIME_US ) {
const float low_sf = speed_factor * min_xy_segment_time / ( MAX_FREQ_TIME_US ) ;
NOMORE ( speed_factor , low_sf ) ;
}
# endif // XY_FREQUENCY_LIMIT
// Correct the speed
2019-01-31 05:25:28 -06:00
if ( speed_factor < 1.0 ) {
LOOP_XYZE ( i ) current_speed [ i ] * = speed_factor ;
block - > nominal_speed * = speed_factor ;
2018-11-16 03:32:01 -06:00
block - > nominal_rate * = speed_factor ;
}
// Compute and limit the acceleration rate for the trapezoid generator.
const float steps_per_mm = block - > step_event_count * inverse_millimeters ;
uint32_t accel ;
2019-01-31 05:25:28 -06:00
if ( ! block - > steps [ X_AXIS ] & & ! block - > steps [ Y_AXIS ] & & ! block - > steps [ Z_AXIS ] ) {
2018-11-16 03:32:01 -06:00
// convert to: acceleration steps/sec^2
accel = CEIL ( retract_acceleration * steps_per_mm ) ;
}
else {
# define LIMIT_ACCEL_LONG(AXIS,INDX) do{ \
if ( block - > steps [ AXIS ] & & max_acceleration_steps_per_s2 [ AXIS + INDX ] < accel ) { \
const uint32_t comp = max_acceleration_steps_per_s2 [ AXIS + INDX ] * block - > step_event_count ; \
if ( accel * block - > steps [ AXIS ] > comp ) accel = comp / block - > steps [ AXIS ] ; \
} \
} while ( 0 )
# define LIMIT_ACCEL_FLOAT(AXIS,INDX) do{ \
if ( block - > steps [ AXIS ] & & max_acceleration_steps_per_s2 [ AXIS + INDX ] < accel ) { \
const float comp = ( float ) max_acceleration_steps_per_s2 [ AXIS + INDX ] * ( float ) block - > step_event_count ; \
if ( ( float ) accel * ( float ) block - > steps [ AXIS ] > comp ) accel = comp / ( float ) block - > steps [ AXIS ] ; \
} \
} while ( 0 )
// Start with print or travel acceleration
accel = CEIL ( ( esteps ? acceleration : travel_acceleration ) * steps_per_mm ) ;
# if ENABLED(DISTINCT_E_FACTORS)
# define ACCEL_IDX extruder
# else
# define ACCEL_IDX 0
# endif
// Limit acceleration per axis
if ( block - > step_event_count < = cutoff_long ) {
2019-01-31 05:25:28 -06:00
LIMIT_ACCEL_LONG ( X_AXIS , 0 ) ;
LIMIT_ACCEL_LONG ( Y_AXIS , 0 ) ;
LIMIT_ACCEL_LONG ( Z_AXIS , 0 ) ;
2018-11-16 03:32:01 -06:00
LIMIT_ACCEL_LONG ( E_AXIS , ACCEL_IDX ) ;
}
else {
2019-01-31 05:25:28 -06:00
LIMIT_ACCEL_FLOAT ( X_AXIS , 0 ) ;
LIMIT_ACCEL_FLOAT ( Y_AXIS , 0 ) ;
LIMIT_ACCEL_FLOAT ( Z_AXIS , 0 ) ;
2018-11-16 03:32:01 -06:00
LIMIT_ACCEL_FLOAT ( E_AXIS , ACCEL_IDX ) ;
}
}
block - > acceleration_steps_per_s2 = accel ;
block - > acceleration = accel / steps_per_mm ;
2019-01-31 05:25:28 -06:00
block - > acceleration_rate = ( long ) ( accel * 16777216.0 / ( ( F_CPU ) * 0.125 ) ) ; // * 8.388608
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
// Initial limit on the segment entry velocity
float vmax_junction ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
#if 0 // Use old jerk for now
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
float junction_deviation = 0.1 ;
// Compute path unit vector
double unit_vec [ XYZ ] = {
delta_mm [ X_AXIS ] * inverse_millimeters ,
delta_mm [ Y_AXIS ] * inverse_millimeters ,
delta_mm [ Z_AXIS ] * inverse_millimeters
2018-11-16 03:32:01 -06:00
} ;
2019-01-31 05:25:28 -06:00
/*
Compute maximum allowable entry speed at junction by centripetal acceleration approximation .
Let a circle be tangent to both previous and current path line segments , where the junction
deviation is defined as the distance from the junction to the closest edge of the circle ,
collinear with the circle center .
The circular segment joining the two paths represents the path of centripetal acceleration .
Solve for max velocity based on max acceleration about the radius of the circle , defined
indirectly by junction deviation .
This may be also viewed as path width or max_jerk in the previous grbl version . This approach
does not actually deviate from path , but used as a robust way to compute cornering speeds , as
it takes into account the nonlinearities of both the junction angle and junction velocity .
*/
vmax_junction = MINIMUM_PLANNER_SPEED ; // Set default max junction speed
2018-11-16 03:32:01 -06:00
// Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles.
2019-01-31 05:25:28 -06:00
if ( moves_queued & & ! UNEAR_ZERO ( previous_nominal_speed ) ) {
2018-11-16 03:32:01 -06:00
// Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
2019-01-31 05:25:28 -06:00
const float cos_theta = - previous_unit_vec [ X_AXIS ] * unit_vec [ X_AXIS ]
- previous_unit_vec [ Y_AXIS ] * unit_vec [ Y_AXIS ]
- previous_unit_vec [ Z_AXIS ] * unit_vec [ Z_AXIS ] ;
// Skip and use default max junction speed for 0 degree acute junction.
if ( cos_theta < 0.95 ) {
vmax_junction = min ( previous_nominal_speed , block - > nominal_speed ) ;
// Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds.
if ( cos_theta > - 0.95 ) {
// Compute maximum junction velocity based on maximum acceleration and junction deviation
float sin_theta_d2 = SQRT ( 0.5 * ( 1.0 - cos_theta ) ) ; // Trig half angle identity. Always positive.
NOMORE ( vmax_junction , SQRT ( block - > acceleration * junction_deviation * sin_theta_d2 / ( 1.0 - sin_theta_d2 ) ) ) ;
2018-11-16 03:32:01 -06:00
}
}
}
2019-01-31 05:25:28 -06:00
# endif
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
/**
* Adapted from Průša MKS firmware
* https : //github.com/prusa3d/Prusa-Firmware
*
* Start with a safe speed ( from which the machine may halt to stop immediately ) .
*/
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
// Exit speed limited by a jerk to full halt of a previous last segment
static float previous_safe_speed ;
float safe_speed = block - > nominal_speed ;
uint8_t limited = 0 ;
LOOP_XYZE ( i ) {
const float jerk = FABS ( current_speed [ i ] ) , maxj = max_jerk [ i ] ;
if ( jerk > maxj ) {
if ( limited ) {
const float mjerk = maxj * block - > nominal_speed ;
if ( jerk * safe_speed > mjerk ) safe_speed = mjerk / jerk ;
}
else {
+ + limited ;
safe_speed = maxj ;
2018-11-16 03:32:01 -06:00
}
}
2019-01-31 05:25:28 -06:00
}
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
if ( moves_queued & & ! UNEAR_ZERO ( previous_nominal_speed ) ) {
// Estimate a maximum velocity allowed at a joint of two successive segments.
// If this maximum velocity allowed is lower than the minimum of the entry / exit safe velocities,
// then the machine is not coasting anymore and the safe entry / exit velocities shall be used.
// The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum.
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
vmax_junction = min ( block - > nominal_speed , previous_nominal_speed ) ;
// Factor to multiply the previous / current nominal velocities to get componentwise limited velocities.
float v_factor = 1 ;
limited = 0 ;
// Now limit the jerk in all axes.
const float smaller_speed_factor = vmax_junction / previous_nominal_speed ;
LOOP_XYZE ( axis ) {
// Limit an axis. We have to differentiate: coasting, reversal of an axis, full stop.
float v_exit = previous_speed [ axis ] * smaller_speed_factor ,
v_entry = current_speed [ axis ] ;
if ( limited ) {
v_exit * = v_factor ;
v_entry * = v_factor ;
}
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
// Calculate jerk depending on whether the axis is coasting in the same direction or reversing.
const float jerk = ( v_exit > v_entry )
? // coasting axis reversal
( ( v_entry > 0 | | v_exit < 0 ) ? ( v_exit - v_entry ) : max ( v_exit , - v_entry ) )
: // v_exit <= v_entry coasting axis reversal
( ( v_entry < 0 | | v_exit > 0 ) ? ( v_entry - v_exit ) : max ( - v_exit , v_entry ) ) ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
if ( jerk > max_jerk [ axis ] ) {
v_factor * = max_jerk [ axis ] / jerk ;
+ + limited ;
2018-11-16 03:32:01 -06:00
}
}
2019-01-31 05:25:28 -06:00
if ( limited ) vmax_junction * = v_factor ;
// Now the transition velocity is known, which maximizes the shared exit / entry velocity while
// respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints.
const float vmax_junction_threshold = vmax_junction * 0.99f ;
if ( previous_safe_speed > vmax_junction_threshold & & safe_speed > vmax_junction_threshold ) {
// Not coasting. The machine will stop and start the movements anyway,
// better to start the segment from start.
SBI ( block - > flag , BLOCK_BIT_START_FROM_FULL_HALT ) ;
2018-11-16 03:32:01 -06:00
vmax_junction = safe_speed ;
2019-01-31 05:25:28 -06:00
}
}
else {
SBI ( block - > flag , BLOCK_BIT_START_FROM_FULL_HALT ) ;
vmax_junction = safe_speed ;
}
2018-11-16 03:32:01 -06:00
// Max entry speed of this block equals the max exit speed of the previous block.
2019-01-31 05:25:28 -06:00
block - > max_entry_speed = vmax_junction ;
2018-11-16 03:32:01 -06:00
// Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED.
2019-01-31 05:25:28 -06:00
const float v_allowable = max_allowable_speed ( - block - > acceleration , MINIMUM_PLANNER_SPEED , block - > millimeters ) ;
block - > entry_speed = min ( vmax_junction , v_allowable ) ;
2018-11-16 03:32:01 -06:00
// Initialize planner efficiency flags
// Set flag if block will always reach maximum junction speed regardless of entry/exit speeds.
// If a block can de/ac-celerate from nominal speed to zero within the length of the block, then
// the current block and next block junction speeds are guaranteed to always be at their maximum
// junction speeds in deceleration and acceleration, respectively. This is due to how the current
// block nominal speed limits both the current and next maximum junction speeds. Hence, in both
// the reverse and forward planners, the corresponding block junction speed will always be at the
// the maximum junction speed and may always be ignored for any speed reduction checks.
2019-01-31 05:25:28 -06:00
block - > flag | = BLOCK_FLAG_RECALCULATE | ( block - > nominal_speed < = v_allowable ? BLOCK_FLAG_NOMINAL_LENGTH : 0 ) ;
2018-11-16 03:32:01 -06:00
// Update previous path unit_vector and nominal speed
COPY ( previous_speed , current_speed ) ;
2019-01-31 05:25:28 -06:00
previous_nominal_speed = block - > nominal_speed ;
previous_safe_speed = safe_speed ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
# if ENABLED(LIN_ADVANCE)
/**
*
* Use LIN_ADVANCE for blocks if all these are true :
*
* esteps & & ( block - > steps [ X_AXIS ] | | block - > steps [ Y_AXIS ] ) : This is a print move
*
* extruder_advance_k : There is an advance factor set .
*
* esteps ! = block - > step_event_count : A problem occurs if the move before a retract is too small .
* In that case , the retract and move will be executed together .
* This leads to too many advance steps due to a huge e_acceleration .
* The math is good , but we must avoid retract moves with advance !
* lin_dist_e > 0 : Extruder is running forward ( e . g . , for " Wipe while retracting " ( Slic3r ) or " Combing " ( Cura ) moves )
*/
block - > use_advance_lead = esteps & & ( block - > steps [ X_AXIS ] | | block - > steps [ Y_AXIS ] )
& & extruder_advance_k
& & ( uint32_t ) esteps ! = block - > step_event_count
& & lin_dist_e > 0 ;
if ( block - > use_advance_lead )
block - > abs_adv_steps_multiplier8 = LROUND (
extruder_advance_k
* ( UNEAR_ZERO ( advance_ed_ratio ) ? lin_dist_e / lin_dist_xy : advance_ed_ratio ) // Use the fixed ratio, if set
* ( block - > nominal_speed / ( float ) block - > nominal_rate )
* axis_steps_per_mm [ E_AXIS_N ] * 256.0
) ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
# endif // LIN_ADVANCE
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
const float bnsr = 1.0 / block - > nominal_speed ;
calculate_trapezoid_for_block ( block , block - > entry_speed * bnsr , safe_speed * bnsr ) ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
// Move buffer head
block_buffer_head = next_buffer_head ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
// Update the position (only when a move was queued)
static_assert ( COUNT ( target ) > 1 , " Parameter to _buffer_steps must be (&target)[XYZE]! " ) ;
COPY ( position , target ) ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
recalculate ( ) ;
2018-11-16 03:32:01 -06:00
2019-01-31 05:25:28 -06:00
} // _buffer_steps()
2018-11-16 03:32:01 -06:00
/**
* Planner : : buffer_segment
*
* Add a new linear movement to the buffer in axis units .
*
* Leveling and kinematics should be applied ahead of calling this .
*
2019-01-31 05:25:28 -06:00
* a , b , c , e - target positions in mm and / or degrees
* fr_mm_s - ( target ) speed of the move
* extruder - target extruder
2018-11-16 03:32:01 -06:00
*/
2019-01-31 05:25:28 -06:00
void Planner : : buffer_segment ( const float & a , const float & b , const float & c , const float & e , const float & fr_mm_s , const uint8_t extruder ) {
2018-11-16 03:32:01 -06:00
// When changing extruders recalculate steps corresponding to the E position
# if ENABLED(DISTINCT_E_FACTORS)
if ( last_extruder ! = extruder & & axis_steps_per_mm [ E_AXIS_N ] ! = axis_steps_per_mm [ E_AXIS + last_extruder ] ) {
position [ E_AXIS ] = LROUND ( position [ E_AXIS ] * axis_steps_per_mm [ E_AXIS_N ] * steps_to_mm [ E_AXIS + last_extruder ] ) ;
last_extruder = extruder ;
}
# endif
// The target position of the tool in absolute steps
// Calculate target position in absolute steps
2019-01-31 05:25:28 -06:00
const int32_t target [ XYZE ] = {
LROUND ( a * axis_steps_per_mm [ X_AXIS ] ) ,
LROUND ( b * axis_steps_per_mm [ Y_AXIS ] ) ,
LROUND ( c * axis_steps_per_mm [ Z_AXIS ] ) ,
2018-11-16 03:32:01 -06:00
LROUND ( e * axis_steps_per_mm [ E_AXIS_N ] )
} ;
// DRYRUN prevents E moves from taking place
if ( DEBUGGING ( DRYRUN ) ) {
2019-01-31 05:25:28 -06:00
position [ E_AXIS ] = target [ E_AXIS ] ;
# if ENABLED(LIN_ADVANCE)
position_float [ E_AXIS ] = e ;
# endif
2018-11-16 03:32:01 -06:00
}
2019-01-31 05:25:28 -06:00
# if ENABLED(LIN_ADVANCE)
lin_dist_e = e - position_float [ E_AXIS ] ;
# endif
// If LIN_ADVANCE is enabled then do E move prevention with floats
// Otherwise it's done in _buffer_steps.
# if ENABLED(LIN_ADVANCE) && (ENABLED(PREVENT_COLD_EXTRUSION) || ENABLED(PREVENT_LENGTHY_EXTRUDE))
if ( lin_dist_e ) {
# if ENABLED(PREVENT_COLD_EXTRUSION)
if ( thermalManager . tooColdToExtrude ( extruder ) ) {
position_float [ E_AXIS ] = e ; // Behave as if the move really took place, but ignore E part
position [ E_AXIS ] = target [ E_AXIS ] ;
lin_dist_e = 0 ;
SERIAL_ECHO_START ( ) ;
SERIAL_ECHOLNPGM ( MSG_ERR_COLD_EXTRUDE_STOP ) ;
}
# endif // PREVENT_COLD_EXTRUSION
# if ENABLED(PREVENT_LENGTHY_EXTRUDE)
if ( lin_dist_e * e_factor [ extruder ] > ( EXTRUDE_MAXLENGTH ) ) {
position_float [ E_AXIS ] = e ; // Behave as if the move really took place, but ignore E part
position [ E_AXIS ] = target [ E_AXIS ] ;
lin_dist_e = 0 ;
SERIAL_ECHO_START ( ) ;
SERIAL_ECHOLNPGM ( MSG_ERR_LONG_EXTRUDE_STOP ) ;
}
# endif // PREVENT_LENGTHY_EXTRUDE
}
# endif // LIN_ADVANCE && (PREVENT_COLD_EXTRUSION || PREVENT_LENGTHY_EXTRUDE)
# if ENABLED(LIN_ADVANCE)
if ( lin_dist_e > 0 )
lin_dist_xy = HYPOT ( a - position_float [ X_AXIS ] , b - position_float [ Y_AXIS ] ) ;
# endif
2018-11-16 03:32:01 -06:00
/* <-- add a slash to enable
SERIAL_ECHOPAIR ( " buffer_segment FR: " , fr_mm_s ) ;
# if IS_KINEMATIC
SERIAL_ECHOPAIR ( " A: " , a ) ;
SERIAL_ECHOPAIR ( " ( " , position [ A_AXIS ] ) ;
SERIAL_ECHOPAIR ( " -> " , target [ A_AXIS ] ) ;
SERIAL_ECHOPAIR ( " ) B: " , b ) ;
# else
SERIAL_ECHOPAIR ( " X: " , a ) ;
SERIAL_ECHOPAIR ( " ( " , position [ X_AXIS ] ) ;
SERIAL_ECHOPAIR ( " -> " , target [ X_AXIS ] ) ;
SERIAL_ECHOPAIR ( " ) Y: " , b ) ;
# endif
SERIAL_ECHOPAIR ( " ( " , position [ Y_AXIS ] ) ;
SERIAL_ECHOPAIR ( " -> " , target [ Y_AXIS ] ) ;
2019-01-31 05:25:28 -06:00
# if ENABLED(DELTA)
2018-11-16 03:32:01 -06:00
SERIAL_ECHOPAIR ( " ) C: " , c ) ;
# else
SERIAL_ECHOPAIR ( " ) Z: " , c ) ;
# endif
SERIAL_ECHOPAIR ( " ( " , position [ Z_AXIS ] ) ;
SERIAL_ECHOPAIR ( " -> " , target [ Z_AXIS ] ) ;
SERIAL_ECHOPAIR ( " ) E: " , e ) ;
SERIAL_ECHOPAIR ( " ( " , position [ E_AXIS ] ) ;
SERIAL_ECHOPAIR ( " -> " , target [ E_AXIS ] ) ;
SERIAL_ECHOLNPGM ( " ) " ) ;
//*/
2019-01-31 05:25:28 -06:00
// Always split the first move into two (if not homing or probing)
if ( ! blocks_queued ( ) ) {
# define _BETWEEN(A) (position[A##_AXIS] + target[A##_AXIS]) >> 1
const int32_t between [ XYZE ] = { _BETWEEN ( X ) , _BETWEEN ( Y ) , _BETWEEN ( Z ) , _BETWEEN ( E ) } ;
DISABLE_STEPPER_DRIVER_INTERRUPT ( ) ;
# if ENABLED(LIN_ADVANCE)
lin_dist_xy * = 0.5 ;
lin_dist_e * = 0.5 ;
# endif
_buffer_steps ( between , fr_mm_s , extruder ) ;
# if ENABLED(LIN_ADVANCE)
position_float [ X_AXIS ] = ( position_float [ X_AXIS ] + a ) * 0.5 ;
position_float [ Y_AXIS ] = ( position_float [ Y_AXIS ] + b ) * 0.5 ;
//position_float[Z_AXIS] = (position_float[Z_AXIS] + c) * 0.5;
position_float [ E_AXIS ] = ( position_float [ E_AXIS ] + e ) * 0.5 ;
# endif
const uint8_t next = block_buffer_head ;
_buffer_steps ( target , fr_mm_s , extruder ) ;
SBI ( block_buffer [ next ] . flag , BLOCK_BIT_CONTINUED ) ;
ENABLE_STEPPER_DRIVER_INTERRUPT ( ) ;
}
else
_buffer_steps ( target , fr_mm_s , extruder ) ;
2018-11-16 03:32:01 -06:00
stepper . wake_up ( ) ;
2019-01-31 05:25:28 -06:00
# if ENABLED(LIN_ADVANCE)
position_float [ X_AXIS ] = a ;
position_float [ Y_AXIS ] = b ;
//position_float[Z_AXIS] = c;
position_float [ E_AXIS ] = e ;
# endif
2018-11-16 03:32:01 -06:00
} // buffer_segment()
/**
* Directly set the planner XYZ position ( and stepper positions )
* converting mm ( or angles for SCARA ) into steps .
*
* On CORE machines stepper ABC will be translated from the given XYZ .
*/
2019-01-31 05:25:28 -06:00
void Planner : : _set_position_mm ( const float & a , const float & b , const float & c , const float & e ) {
2018-11-16 03:32:01 -06:00
# if ENABLED(DISTINCT_E_FACTORS)
2019-01-31 05:25:28 -06:00
# define _EINDEX (E_AXIS + active_extruder)
2018-11-16 03:32:01 -06:00
last_extruder = active_extruder ;
# else
2019-01-31 05:25:28 -06:00
# define _EINDEX E_AXIS
2018-11-16 03:32:01 -06:00
# endif
2019-01-31 05:25:28 -06:00
const int32_t na = position [ X_AXIS ] = LROUND ( a * axis_steps_per_mm [ X_AXIS ] ) ,
nb = position [ Y_AXIS ] = LROUND ( b * axis_steps_per_mm [ Y_AXIS ] ) ,
nc = position [ Z_AXIS ] = LROUND ( c * axis_steps_per_mm [ Z_AXIS ] ) ,
ne = position [ E_AXIS ] = LROUND ( e * axis_steps_per_mm [ _EINDEX ] ) ;
# if ENABLED(LIN_ADVANCE)
position_float [ X_AXIS ] = a ;
position_float [ Y_AXIS ] = b ;
//position_float[Z_AXIS] = c;
2018-11-16 03:32:01 -06:00
position_float [ E_AXIS ] = e ;
# endif
2019-01-31 05:25:28 -06:00
stepper . set_position ( na , nb , nc , ne ) ;
previous_nominal_speed = 0.0 ; // Resets planner junction speeds. Assumes start from rest.
ZERO ( previous_speed ) ;
2018-11-16 03:32:01 -06:00
}
void Planner : : set_position_mm_kinematic ( const float ( & cart ) [ XYZE ] ) {
# if PLANNER_LEVELING
float raw [ XYZ ] = { cart [ X_AXIS ] , cart [ Y_AXIS ] , cart [ Z_AXIS ] } ;
apply_leveling ( raw ) ;
# else
const float ( & raw ) [ XYZE ] = cart ;
# endif
# if IS_KINEMATIC
inverse_kinematics ( raw ) ;
2019-01-31 05:25:28 -06:00
_set_position_mm ( delta [ A_AXIS ] , delta [ B_AXIS ] , delta [ C_AXIS ] , cart [ E_AXIS ] ) ;
2018-11-16 03:32:01 -06:00
# else
2019-01-31 05:25:28 -06:00
_set_position_mm ( raw [ X_AXIS ] , raw [ Y_AXIS ] , raw [ Z_AXIS ] , cart [ E_AXIS ] ) ;
2018-11-16 03:32:01 -06:00
# endif
}
2019-01-31 05:25:28 -06:00
/**
* Sync from the stepper positions . ( e . g . , after an interrupted move )
*/
void Planner : : sync_from_steppers ( ) {
LOOP_XYZE ( i ) {
position [ i ] = stepper . position ( ( AxisEnum ) i ) ;
# if ENABLED(LIN_ADVANCE)
position_float [ i ] = position [ i ] * steps_to_mm [ i
# if ENABLED(DISTINCT_E_FACTORS)
+ ( i = = E_AXIS ? active_extruder : 0 )
# endif
] ;
# endif
}
}
2018-11-16 03:32:01 -06:00
/**
* Setters for planner position ( also setting stepper position ) .
*/
void Planner : : set_position_mm ( const AxisEnum axis , const float & v ) {
# if ENABLED(DISTINCT_E_FACTORS)
const uint8_t axis_index = axis + ( axis = = E_AXIS ? active_extruder : 0 ) ;
last_extruder = active_extruder ;
# else
const uint8_t axis_index = axis ;
# endif
2019-01-31 05:25:28 -06:00
position [ axis ] = LROUND ( v * axis_steps_per_mm [ axis_index ] ) ;
# if ENABLED(LIN_ADVANCE)
2018-11-16 03:32:01 -06:00
position_float [ axis ] = v ;
# endif
2019-01-31 05:25:28 -06:00
stepper . set_position ( axis , v ) ;
previous_speed [ axis ] = 0.0 ;
2018-11-16 03:32:01 -06:00
}
// Recalculate the steps/s^2 acceleration rates, based on the mm/s^2
void Planner : : reset_acceleration_rates ( ) {
# if ENABLED(DISTINCT_E_FACTORS)
2019-01-31 05:25:28 -06:00
# define HIGHEST_CONDITION (i < E_AXIS || i == E_AXIS + active_extruder)
2018-11-16 03:32:01 -06:00
# else
2019-01-31 05:25:28 -06:00
# define HIGHEST_CONDITION true
2018-11-16 03:32:01 -06:00
# endif
uint32_t highest_rate = 1 ;
2019-01-31 05:25:28 -06:00
LOOP_XYZE_N ( i ) {
2018-11-16 03:32:01 -06:00
max_acceleration_steps_per_s2 [ i ] = max_acceleration_mm_per_s2 [ i ] * axis_steps_per_mm [ i ] ;
2019-01-31 05:25:28 -06:00
if ( HIGHEST_CONDITION ) NOLESS ( highest_rate , max_acceleration_steps_per_s2 [ i ] ) ;
2018-11-16 03:32:01 -06:00
}
2019-01-31 05:25:28 -06:00
cutoff_long = 4294967295UL / highest_rate ;
2018-11-16 03:32:01 -06:00
}
// Recalculate position, steps_to_mm if axis_steps_per_mm changes!
void Planner : : refresh_positioning ( ) {
2019-01-31 05:25:28 -06:00
LOOP_XYZE_N ( i ) steps_to_mm [ i ] = 1.0 / axis_steps_per_mm [ i ] ;
2018-11-16 03:32:01 -06:00
set_position_mm_kinematic ( current_position ) ;
reset_acceleration_rates ( ) ;
}
# if ENABLED(AUTOTEMP)
void Planner : : autotemp_M104_M109 ( ) {
2019-01-31 05:25:28 -06:00
autotemp_enabled = parser . seen ( ' F ' ) ;
if ( autotemp_enabled ) autotemp_factor = parser . value_celsius_diff ( ) ;
2018-11-16 03:32:01 -06:00
if ( parser . seen ( ' S ' ) ) autotemp_min = parser . value_celsius ( ) ;
if ( parser . seen ( ' B ' ) ) autotemp_max = parser . value_celsius ( ) ;
}
# endif