gnea\grbl-Mega  1.0f
Source Code Documentation ( Internal Workings )
planner.c
Go to the documentation of this file.
1 /*
2  planner.c - buffers movement commands and manages the acceleration profile plan
3  Part of Grbl
4 
5  Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
6  Copyright (c) 2009-2011 Simen Svale Skogsrud
7  Copyright (c) 2011 Jens Geisler
8 
9  Grbl is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation, either version 3 of the License, or
12  (at your option) any later version.
13 
14  Grbl is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with Grbl. If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include "grbl.h"
24 
25 
26 static plan_block_t block_buffer[BLOCK_BUFFER_SIZE];
27 static uint8_t block_buffer_tail;
28 static uint8_t block_buffer_head;
29 static uint8_t next_buffer_head;
30 static uint8_t block_buffer_planned;
31 typedef struct {
33  int32_t position[N_AXIS];
34 // from g-code position for movements requiring multiple line motions,
35 // i.e. arcs, canned cycles, and backlash compensation.
36  float previous_unit_vec[N_AXIS];
38 } planner_t;
39 static planner_t pl;
40 
42 uint8_t plan_next_block_index(uint8_t block_index)
43 {
44  block_index++;
45  if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
46  return(block_index);
47 }
48 
50 static uint8_t plan_prev_block_index(uint8_t block_index)
51 {
52  if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
53  block_index--;
54  return(block_index);
55 }
56 
57 
112 
124 static void planner_recalculate()
125 {
126 // Initialize block index to the last block in the planner buffer.
127  uint8_t block_index = plan_prev_block_index(block_buffer_head);
128 
129 // Bail. Can't do anything with one only one plan-able block.
130  if (block_index == block_buffer_planned) { return; }
131 
132 // Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
133 // block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
134 //
136  float entry_speed_sqr;
137  plan_block_t *next;
138  plan_block_t *current = &block_buffer[block_index];
139 
140 // Calculate maximum entry speed for last block in buffer, where the exit speed is always zero.
141  current->entry_speed_sqr = min( current->max_entry_speed_sqr, 2*current->acceleration*current->millimeters);
142 
143  block_index = plan_prev_block_index(block_index);
144  if (block_index == block_buffer_planned) {
145 // Check if the first block is the tail. If so, notify stepper to update its current parameters.
146  if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
147  } else {
148  while (block_index != block_buffer_planned) {
149  next = current;
150  current = &block_buffer[block_index];
151  block_index = plan_prev_block_index(block_index);
152 
153 // Check if next block is the tail block(=planned block). If so, update current stepper parameters.
154  if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
155 
156 // Compute maximum entry speed decelerating over the current block from its exit speed.
157  if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
158  entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters;
159  if (entry_speed_sqr < current->max_entry_speed_sqr) {
160  current->entry_speed_sqr = entry_speed_sqr;
161  } else {
162  current->entry_speed_sqr = current->max_entry_speed_sqr;
163  }
164  }
165  }
166  }
167 
168 // Forward Pass: Forward plan the acceleration curve from the planned pointer onward.
169 // Also scans for optimal plan breakpoints and appropriately updates the planned pointer.
170  next = &block_buffer[block_buffer_planned];
171  block_index = plan_next_block_index(block_buffer_planned);
172  while (block_index != block_buffer_head) {
173  current = next;
174  next = &block_buffer[block_index];
175 
176 // Any acceleration detected in the forward pass automatically moves the optimal planned
177 // pointer forward, since everything before this is all optimal. In other words, nothing
178 // can improve the plan from the buffer tail to the planned pointer by logic.
179  if (current->entry_speed_sqr < next->entry_speed_sqr) {
180  entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters;
181 // If true, current block is full-acceleration and we can move the planned pointer forward.
182  if (entry_speed_sqr < next->entry_speed_sqr) {
183  next->entry_speed_sqr = entry_speed_sqr;
184  block_buffer_planned = block_index;
185  }
186  }
187 
188 // Any block set at its maximum entry speed also creates an optimal plan up to this
189 // point in the buffer. When the plan is bracketed by either the beginning of the
190 // buffer and a maximum entry speed or two maximum entry speeds, every block in between
191 // cannot logically be further improved. Hence, we don't have to recompute them anymore.
192  if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; }
193  block_index = plan_next_block_index( block_index );
194  }
195 }
196 
197 
199 {
200  memset(&pl, 0, sizeof(planner_t));
202 }
203 
204 
206 {
207  block_buffer_tail = 0;
208  block_buffer_head = 0;
209  next_buffer_head = 1;
210  block_buffer_planned = 0;
211 }
212 
213 
215 {
216  if (block_buffer_head != block_buffer_tail) {
217  uint8_t block_index = plan_next_block_index( block_buffer_tail );
218 // Push block_buffer_planned pointer, if encountered.
219  if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; }
220  block_buffer_tail = block_index;
221  }
222 }
223 
226 {
227  return(&block_buffer[block_buffer_head]);
228 }
229 
232 {
233  if (block_buffer_head == block_buffer_tail) { return(NULL); }
234  return(&block_buffer[block_buffer_tail]);
235 }
236 
237 
239 {
240  uint8_t block_index = plan_next_block_index(block_buffer_tail);
241  if (block_index == block_buffer_head) { return( 0.0 ); }
242  return( block_buffer[block_index].entry_speed_sqr );
243 }
244 
247 {
248  if (block_buffer_tail == next_buffer_head) { return(true); }
249  return(false);
250 }
251 
253 //
256 {
257  float nominal_speed = block->programmed_rate;
258  if (block->condition & PL_COND_FLAG_RAPID_MOTION) { nominal_speed *= (0.01*sys.r_override); }
259  else {
260  if (!(block->condition & PL_COND_FLAG_NO_FEED_OVERRIDE)) { nominal_speed *= (0.01*sys.f_override); }
261  if (nominal_speed > block->rapid_rate) { nominal_speed = block->rapid_rate; }
262  }
263  if (nominal_speed > MINIMUM_FEED_RATE) { return(nominal_speed); }
264  return(MINIMUM_FEED_RATE);
265 }
266 
268 // previous and current nominal speeds and max junction speed.
269 static void plan_compute_profile_parameters(plan_block_t *block, float nominal_speed, float prev_nominal_speed)
270 {
271 // Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds.
272  if (nominal_speed > prev_nominal_speed) { block->max_entry_speed_sqr = prev_nominal_speed*prev_nominal_speed; }
273  else { block->max_entry_speed_sqr = nominal_speed*nominal_speed; }
275 }
276 
279 {
280  uint8_t block_index = block_buffer_tail;
281  plan_block_t *block;
282  float nominal_speed;
283  float prev_nominal_speed = SOME_LARGE_VALUE;
284  while (block_index != block_buffer_head) {
285  block = &block_buffer[block_index];
286  nominal_speed = plan_compute_profile_nominal_speed(block);
287  plan_compute_profile_parameters(block, nominal_speed, prev_nominal_speed);
288  prev_nominal_speed = nominal_speed;
289  block_index = plan_next_block_index(block_index);
290  }
291  pl.previous_nominal_speed = prev_nominal_speed;
292 }
293 
294 
302 
310 uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
311 {
312 // Prepare and initialize new block. Copy relevant pl_data for block execution.
313  plan_block_t *block = &block_buffer[block_buffer_head];
314  memset(block,0,sizeof(plan_block_t));
315  block->condition = pl_data->condition;
316  block->spindle_speed = pl_data->spindle_speed;
317  block->line_number = pl_data->line_number;
318 
319 // Compute and store initial move distance data.
320  int32_t target_steps[N_AXIS], position_steps[N_AXIS];
321  float unit_vec[N_AXIS], delta_mm;
322  uint8_t idx;
323 
324 // Copy position data based on type of motion being planned.
325  if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) {
326  #ifdef COREXY
327  position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
328  position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
329  position_steps[Z_AXIS] = sys_position[Z_AXIS];
330  #else
331  memcpy(position_steps, sys_position, sizeof(sys_position));
332  #endif
333  } else { memcpy(position_steps, pl.position, sizeof(pl.position)); }
334 
335  #ifdef COREXY
336  target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]);
337  target_steps[B_MOTOR] = lround(target[B_MOTOR]*settings.steps_per_mm[B_MOTOR]);
338  block->steps[A_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
339  block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
340  #endif
341 
342  for (idx=0; idx<N_AXIS; idx++) {
343 // Calculate target position in absolute steps, number of steps for each axis, and determine max step events.
344 // Also, compute individual axes distance for move and prep unit vector calculations.
345 //
347  #ifdef COREXY
348  if ( !(idx == A_MOTOR) && !(idx == B_MOTOR) ) {
349  target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
350  block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
351  }
352  block->step_event_count = max(block->step_event_count, block->steps[idx]);
353  if (idx == A_MOTOR) {
354  delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] + target_steps[Y_AXIS]-position_steps[Y_AXIS])/settings.steps_per_mm[idx];
355  } else if (idx == B_MOTOR) {
356  delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] - target_steps[Y_AXIS]+position_steps[Y_AXIS])/settings.steps_per_mm[idx];
357  } else {
358  delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
359  }
360  #else
361  target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
362  block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
363  block->step_event_count = max(block->step_event_count, block->steps[idx]);
364  delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
365  #endif
366  unit_vec[idx] = delta_mm;
367 
368 // Set direction bits. Bit enabled always means direction is negative.
369  if (delta_mm < 0.0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
370  }
371 
372 // Bail if this is a zero-length block. Highly unlikely to occur.
373  if (block->step_event_count == 0) { return(PLAN_EMPTY_BLOCK); }
374 
375 // Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
376 // down such that no individual axes maximum values are exceeded with respect to the line direction.
377 //
379 // if they are also orthogonal/independent. Operates on the absolute value of the unit vector.
383 
384 // Store programmed rate.
385  if (block->condition & PL_COND_FLAG_RAPID_MOTION) { block->programmed_rate = block->rapid_rate; }
386  else {
387  block->programmed_rate = pl_data->feed_rate;
388  if (block->condition & PL_COND_FLAG_INVERSE_TIME) { block->programmed_rate *= block->millimeters; }
389  }
390 
392  if ((block_buffer_head == block_buffer_tail) || (block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
393 
394 // Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later.
395 // If system motion, the system motion block always is assumed to start from rest and end at a complete stop.
396  block->entry_speed_sqr = 0.0;
397  block->max_junction_speed_sqr = 0.0;
398 
399  } else {
400 // Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
401 // Let a circle be tangent to both previous and current path line segments, where the junction
402 // deviation is defined as the distance from the junction to the closest edge of the circle,
403 // colinear with the circle center. The circular segment joining the two paths represents the
404 // path of centripetal acceleration. Solve for max velocity based on max acceleration about the
405 // radius of the circle, defined indirectly by junction deviation. This may be also viewed as
406 // path width or max_jerk in the previous Grbl version. This approach does not actually deviate
407 // from path, but used as a robust way to compute cornering speeds, as it takes into account the
408 // nonlinearities of both the junction angle and junction velocity.
409  //
410 //
412 // mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
413 // stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
414 // is exactly the same. Instead of motioning all the way to junction point, the machine will
415 // just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
416 // a continuous mode path, but ARM-based microcontrollers most certainly do.
417  //
418 //
420 // changed dynamically during operation nor can the line move geometry. This must be kept in
421 // memory in the event of a feedrate override changing the nominal speeds of blocks, which can
422 // change the overall maximum entry speed conditions of all blocks.
423 
424  float junction_unit_vec[N_AXIS];
425  float junction_cos_theta = 0.0;
426  for (idx=0; idx<N_AXIS; idx++) {
427  junction_cos_theta -= pl.previous_unit_vec[idx]*unit_vec[idx];
428  junction_unit_vec[idx] = unit_vec[idx]-pl.previous_unit_vec[idx];
429  }
430 
431 //
433  if (junction_cos_theta > 0.999999) {
434 // For a 0 degree acute junction, just set minimum junction speed.
436  } else {
437  if (junction_cos_theta < -0.999999) {
438 // Junction is a straight line or 180 degrees. Junction speed is infinite.
440  } else {
441  convert_delta_vector_to_unit_vector(junction_unit_vec);
442  float junction_acceleration = limit_value_by_axis_maximum(settings.acceleration, junction_unit_vec);
443  float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta));
445  (junction_acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
446  }
447  }
448  }
449 
450 // Block system motion from updating this data to ensure next g-code motion is computed correctly.
451  if (!(block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
452  float nominal_speed = plan_compute_profile_nominal_speed(block);
453  plan_compute_profile_parameters(block, nominal_speed, pl.previous_nominal_speed);
454  pl.previous_nominal_speed = nominal_speed;
455 
456 // Update previous path unit_vector and planner position.
457  memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec));
458  memcpy(pl.position, target_steps, sizeof(target_steps));
459 
460 // New block is all set. Update buffer head and next buffer head indices.
461  block_buffer_head = next_buffer_head;
462  next_buffer_head = plan_next_block_index(block_buffer_head);
463 
464 // Finish up by recalculating the plan with the new block.
465  planner_recalculate();
466  }
467  return(PLAN_OK);
468 }
469 
472 {
474 // this function needs to be updated to accomodate the difference.
475  uint8_t idx;
476  for (idx=0; idx<N_AXIS; idx++) {
477  #ifdef COREXY
478  if (idx==X_AXIS) {
479  pl.position[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
480  } else if (idx==Y_AXIS) {
481  pl.position[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
482  } else {
483  pl.position[idx] = sys_position[idx];
484  }
485  #else
486  pl.position[idx] = sys_position[idx];
487  #endif
488  }
489 }
490 
493 {
494  if (block_buffer_head >= block_buffer_tail) { return((BLOCK_BUFFER_SIZE-1)-(block_buffer_head-block_buffer_tail)); }
495  return((block_buffer_tail-block_buffer_head-1));
496 }
497 
499 //
502 {
503  if (block_buffer_head >= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); }
504  return(BLOCK_BUFFER_SIZE - (block_buffer_tail-block_buffer_head));
505 }
506 
508 // Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
510 {
511 // Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer.
513  block_buffer_planned = block_buffer_tail;
514  planner_recalculate();
515 }
plan_block_t * plan_get_current_block()
Returns address of first planner block, if available. Called by various main program functions...
Definition: planner.c:231
int32_t line_number
Block line number for real-time reporting. Copied from pl_line_data.
Definition: planner.h:56
if(axis_words)
STEP 3: Error-check all commands and values passed in this block. This step ensures all of the comma...
Definition: gcode.c:367
float programmed_rate
Programmed rate of this block (mm/min).
Definition: planner.h:71
void plan_sync_position()
Reset the planner position vectors. Called by the system abort/initialization routine.
Definition: planner.c:471
#define N_AXIS
Axis array index values. Must start with 0 and be continuous.
Definition: nuts_bolts.h:30
uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position in mi...
Definition: planner.c:310
int32_t position[N_AXIS]
The planner position of the tool in absolute steps. Kept separate.
Definition: planner.c:33
#define MINIMUM_JUNCTION_SPEED
Used by variable spindle output only. This forces the PWM output to a minimum duty cycle when enabled...
Definition: config.h:326
#define Y_AXIS
Definition: nuts_bolts.h:32
float acceleration[N_AXIS]
Definition: settings.h:77
uint8_t direction_bits
The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
Definition: planner.h:52
Planner data prototype. Must be used when passing new motions to the planner.
Definition: planner.h:78
#define MINIMUM_FEED_RATE
Sets the minimum feed rate the planner will allow. Any value below it will be set to this minimum...
Definition: config.h:331
float spindle_speed
Desired spindle speed through line motion.
Definition: planner.h:80
void plan_cycle_reinitialize()
Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail...
Definition: planner.c:509
float plan_compute_profile_nominal_speed(plan_block_t *block)
Computes and returns block nominal speed based on running condition and override values.
Definition: planner.c:255
void plan_update_velocity_profile_parameters()
Re-calculates buffered motions profile parameters upon a motion-based override change.
Definition: planner.c:278
float convert_delta_vector_to_unit_vector(float *vector)
Definition: nuts_bolts.c:160
uint8_t condition
Block bitflag variable defining block run conditions. Copied from pl_line_data.
Definition: planner.h:55
#define PL_COND_FLAG_RAPID_MOTION
Define planner data condition flags. Used to denote running conditions of a block.
Definition: planner.h:33
float plan_get_exec_block_exit_speed_sqr()
Called by step segment buffer when computing executing block velocity profile.
Definition: planner.c:238
float entry_speed_sqr
The current planned entry speed at block junction in (mm/min)^2.
Definition: planner.h:60
#define BLOCK_BUFFER_SIZE
The number of linear motions that can be in the plan at any give time.
Definition: planner.h:27
float millimeters
The remaining distance for this block to be executed in (mm).
Definition: planner.h:64
void plan_reset()
Initialize and reset the motion plan subsystem.
Definition: planner.c:198
uint8_t f_override
Feed rate override value in percent.
Definition: system.h:119
float acceleration
Axis-limit adjusted line acceleration in (mm/min^2). Does not change.
Definition: planner.h:63
float previous_nominal_speed
Nominal speed of previous path line segment.
Definition: planner.c:37
#define PLAN_EMPTY_BLOCK
Definition: planner.h:31
#define PL_COND_FLAG_SYSTEM_MOTION
Single motion. Circumvents planner state. Used by home/park.
Definition: planner.h:34
uint8_t get_direction_pin_mask(uint8_t axis_idx)
Returns direction pin mask according to Grbl internal axis indexing.
Definition: settings.c:316
#define X_AXIS
Axis indexing value.
Definition: nuts_bolts.h:31
#define PLAN_OK
Returned status message from planner.
Definition: planner.h:30
float spindle_speed
Block spindle speed. Copied from pl_line_data.
Definition: planner.h:74
#define max(a, b)
Definition: nuts_bolts.h:53
float junction_deviation
Definition: settings.h:86
plan_block_t * plan_get_system_motion_block()
Returns address of planner buffer block used by system motions. Called by segment generator...
Definition: planner.c:225
#define Z_AXIS
Definition: nuts_bolts.h:33
float limit_value_by_axis_maximum(float *max_value, float *unit_vec)
Definition: nuts_bolts.c:176
system_t sys
Declare system global variable structure.
Definition: main.c:25
int32_t sys_position[N_AXIS]
NOTE: These position variables may need to be declared as volatiles, if problems arise.
Definition: system.h:130
float steps_per_mm[N_AXIS]
Definition: settings.h:75
float rapid_rate
Axis-limit adjusted maximum rate for this block direction in (mm/min)
Definition: planner.h:70
float max_entry_speed_sqr
Maximum allowable entry speed based on the minimum of junction limit and.
Definition: planner.h:61
uint8_t plan_get_block_buffer_available()
Returns the number of available blocks are in the planner buffer.
Definition: planner.c:492
void plan_discard_current_block()
Called when the current block is no longer needed. Discards the block and makes the memory...
Definition: planner.c:214
int32_t line_number
Desired line number to report when executing.
Definition: planner.h:81
float max_junction_speed_sqr
NOTE: This value may be altered by stepper algorithm during execution.
Definition: planner.h:69
float previous_unit_vec[N_AXIS]
Unit vector of previous path line segment.
Definition: planner.c:36
uint32_t step_event_count
The maximum step axis count and number of steps required to complete this block.
Definition: planner.h:51
uint8_t plan_get_block_buffer_count()
Returns the number of active blocks are in the planner buffer.
Definition: planner.c:501
float max_rate[N_AXIS]
Definition: settings.h:76
#define PL_COND_FLAG_INVERSE_TIME
Interprets feed rate value as inverse time when set.
Definition: planner.h:36
memset(pl_data, 0, sizeof(plan_line_data_t))
Zero pl_data struct.
memcpy(block_coord_system, gc_state.coord_system, sizeof(gc_state.coord_system))
uint8_t r_override
Rapids override value in percent.
Definition: system.h:120
#define PL_COND_FLAG_NO_FEED_OVERRIDE
Motion does not honor feed override.
Definition: planner.h:35
settings_t settings
Definition: settings.c:24
#define min(a, b)
Definition: nuts_bolts.h:54
float feed_rate
Desired feed rate for line motion. Value is ignored, if rapid motion.
Definition: planner.h:79
Define planner variables.
Definition: planner.c:32
uint8_t condition
Bitflag variable to indicate planner conditions. See defines above.
Definition: planner.h:82
uint8_t plan_check_full_buffer()
Returns the availability status of the block ring buffer. True, if full.
Definition: planner.c:246
This struct stores a linear movement of a g-code block motion with its critical "nominal" values...
Definition: planner.h:46
void st_update_plan_block_parameters()
Called by planner_recalculate() when the executing block is updated by the new plan.
Definition: stepper.c:526
void plan_reset_buffer()
Reset buffer only.
Definition: planner.c:205
uint32_t steps[N_AXIS]
NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values...
Definition: planner.h:50
#define SOME_LARGE_VALUE
Definition: nuts_bolts.h:28
plan_line_data_t * pl_data
Definition: gcode.c:859
uint8_t plan_next_block_index(uint8_t block_index)
Returns the index of the next block in the ring buffer. Also called by stepper segment buffer...
Definition: planner.c:42