blob: 49ff7229143138a9924bd5779daa40e303d1c1e3 [file] [log] [blame]
Luigi Santivetti69972f92019-11-12 22:55:40 +00001/*
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
26static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
27static uint8_t block_buffer_tail; // Index of the block to process now
28static uint8_t block_buffer_head; // Index of the next block to be pushed
29static uint8_t next_buffer_head; // Index of the next buffer head
30static uint8_t block_buffer_planned; // Index of the optimally planned block
31
32// Define planner variables
33typedef struct {
34 int32_t position[N_AXIS]; // The planner position of the tool in absolute steps. Kept separate
35 // from g-code position for movements requiring multiple line motions,
36 // i.e. arcs, canned cycles, and backlash compensation.
37 float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment
38 float previous_nominal_speed; // Nominal speed of previous path line segment
39} planner_t;
40static planner_t pl;
41
42
43// Returns the index of the next block in the ring buffer. Also called by stepper segment buffer.
44uint8_t plan_next_block_index(uint8_t block_index)
45{
46 block_index++;
47 if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
48 return(block_index);
49}
50
51
52// Returns the index of the previous block in the ring buffer
53static uint8_t plan_prev_block_index(uint8_t block_index)
54{
55 if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
56 block_index--;
57 return(block_index);
58}
59
60
61/* PLANNER SPEED DEFINITION
62 +--------+ <- current->nominal_speed
63 / \
64 current->entry_speed -> + \
65 | + <- next->entry_speed (aka exit speed)
66 +-------------+
67 time -->
68
69 Recalculates the motion plan according to the following basic guidelines:
70
71 1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
72 (i.e. current->entry_speed) such that:
73 a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
74 neighboring blocks.
75 b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
76 with a maximum allowable deceleration over the block travel distance.
77 c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
78 2. Go over every block in chronological (forward) order and dial down junction speed values if
79 a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
80 acceleration over the block travel distance.
81
82 When these stages are complete, the planner will have maximized the velocity profiles throughout the all
83 of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
84 other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
85 are possible. If a new block is added to the buffer, the plan is recomputed according to the said
86 guidelines for a new optimal plan.
87
88 To increase computational efficiency of these guidelines, a set of planner block pointers have been
89 created to indicate stop-compute points for when the planner guidelines cannot logically make any further
90 changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
91 planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
92 bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
93 added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
94 them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
95 point) are all accelerating, they are all optimal and can not be altered by a new block added to the
96 planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
97 junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
98 used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
99 recomputed as stated in the general guidelines.
100
101 Planner buffer index mapping:
102 - block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
103 - block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
104 the buffer is full or empty. As described for standard ring buffers, this block is always empty.
105 - next_buffer_head: Points to next planner buffer block after the buffer head block. When equal to the
106 buffer tail, this indicates the buffer is full.
107 - block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
108 streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
109 planner buffer that don't change with the addition of a new block, as describe above. In addition,
110 this block can never be less than block_buffer_tail and will always be pushed forward and maintain
111 this requirement when encountered by the plan_discard_current_block() routine during a cycle.
112
113 NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
114 line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't
115 enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then
116 decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this happens and
117 becomes an annoyance, there are a few simple solutions: (1) Maximize the machine acceleration. The planner
118 will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line
119 motion(s) distance per block to a desired tolerance. The more combined distance the planner has to use,
120 the faster it can go. (3) Maximize the planner buffer size. This also will increase the combined distance
121 for the planner to compute over. It also increases the number of computations the planner has to perform
122 to compute an optimal plan, so select carefully. The Arduino 328p memory is already maxed out, but future
123 ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more.
124
125*/
126static void planner_recalculate()
127{
128 // Initialize block index to the last block in the planner buffer.
129 uint8_t block_index = plan_prev_block_index(block_buffer_head);
130
131 // Bail. Can't do anything with one only one plan-able block.
132 if (block_index == block_buffer_planned) { return; }
133
134 // Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
135 // block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
136 // NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
137 float entry_speed_sqr;
138 plan_block_t *next;
139 plan_block_t *current = &block_buffer[block_index];
140
141 // Calculate maximum entry speed for last block in buffer, where the exit speed is always zero.
142 current->entry_speed_sqr = min( current->max_entry_speed_sqr, 2*current->acceleration*current->millimeters);
143
144 block_index = plan_prev_block_index(block_index);
145 if (block_index == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete.
146 // Check if the first block is the tail. If so, notify stepper to update its current parameters.
147 if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
148 } else { // Three or more plan-able blocks
149 while (block_index != block_buffer_planned) {
150 next = current;
151 current = &block_buffer[block_index];
152 block_index = plan_prev_block_index(block_index);
153
154 // Check if next block is the tail block(=planned block). If so, update current stepper parameters.
155 if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
156
157 // Compute maximum entry speed decelerating over the current block from its exit speed.
158 if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
159 entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters;
160 if (entry_speed_sqr < current->max_entry_speed_sqr) {
161 current->entry_speed_sqr = entry_speed_sqr;
162 } else {
163 current->entry_speed_sqr = current->max_entry_speed_sqr;
164 }
165 }
166 }
167 }
168
169 // Forward Pass: Forward plan the acceleration curve from the planned pointer onward.
170 // Also scans for optimal plan breakpoints and appropriately updates the planned pointer.
171 next = &block_buffer[block_buffer_planned]; // Begin at buffer planned pointer
172 block_index = plan_next_block_index(block_buffer_planned);
173 while (block_index != block_buffer_head) {
174 current = next;
175 next = &block_buffer[block_index];
176
177 // Any acceleration detected in the forward pass automatically moves the optimal planned
178 // pointer forward, since everything before this is all optimal. In other words, nothing
179 // can improve the plan from the buffer tail to the planned pointer by logic.
180 if (current->entry_speed_sqr < next->entry_speed_sqr) {
181 entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters;
182 // If true, current block is full-acceleration and we can move the planned pointer forward.
183 if (entry_speed_sqr < next->entry_speed_sqr) {
184 next->entry_speed_sqr = entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this.
185 block_buffer_planned = block_index; // Set optimal plan pointer.
186 }
187 }
188
189 // Any block set at its maximum entry speed also creates an optimal plan up to this
190 // point in the buffer. When the plan is bracketed by either the beginning of the
191 // buffer and a maximum entry speed or two maximum entry speeds, every block in between
192 // cannot logically be further improved. Hence, we don't have to recompute them anymore.
193 if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; }
194 block_index = plan_next_block_index( block_index );
195 }
196}
197
198
199void plan_reset()
200{
201 memset(&pl, 0, sizeof(planner_t)); // Clear planner struct
202 plan_reset_buffer();
203}
204
205
206void plan_reset_buffer()
207{
208 block_buffer_tail = 0;
209 block_buffer_head = 0; // Empty = tail
210 next_buffer_head = 1; // plan_next_block_index(block_buffer_head)
211 block_buffer_planned = 0; // = block_buffer_tail;
212}
213
214
215void plan_discard_current_block()
216{
217 if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer.
218 uint8_t block_index = plan_next_block_index( block_buffer_tail );
219 // Push block_buffer_planned pointer, if encountered.
220 if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; }
221 block_buffer_tail = block_index;
222 }
223}
224
225
226// Returns address of planner buffer block used by system motions. Called by segment generator.
227plan_block_t *plan_get_system_motion_block()
228{
229 return(&block_buffer[block_buffer_head]);
230}
231
232
233// Returns address of first planner block, if available. Called by various main program functions.
234plan_block_t *plan_get_current_block()
235{
236 if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
237 return(&block_buffer[block_buffer_tail]);
238}
239
240
241float plan_get_exec_block_exit_speed_sqr()
242{
243 uint8_t block_index = plan_next_block_index(block_buffer_tail);
244 if (block_index == block_buffer_head) { return( 0.0 ); }
245 return( block_buffer[block_index].entry_speed_sqr );
246}
247
248
249// Returns the availability status of the block ring buffer. True, if full.
250uint8_t plan_check_full_buffer()
251{
252 if (block_buffer_tail == next_buffer_head) { return(true); }
253 return(false);
254}
255
256
257// Computes and returns block nominal speed based on running condition and override values.
258// NOTE: All system motion commands, such as homing/parking, are not subject to overrides.
259float plan_compute_profile_nominal_speed(plan_block_t *block)
260{
261 float nominal_speed = block->programmed_rate;
262 if (block->condition & PL_COND_FLAG_RAPID_MOTION) { nominal_speed *= (0.01*sys.r_override); }
263 else {
264 if (!(block->condition & PL_COND_FLAG_NO_FEED_OVERRIDE)) { nominal_speed *= (0.01*sys.f_override); }
265 if (nominal_speed > block->rapid_rate) { nominal_speed = block->rapid_rate; }
266 }
267 if (nominal_speed > MINIMUM_FEED_RATE) { return(nominal_speed); }
268 return(MINIMUM_FEED_RATE);
269}
270
271
272// Computes and updates the max entry speed (sqr) of the block, based on the minimum of the junction's
273// previous and current nominal speeds and max junction speed.
274static void plan_compute_profile_parameters(plan_block_t *block, float nominal_speed, float prev_nominal_speed)
275{
276 // Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds.
277 if (nominal_speed > prev_nominal_speed) { block->max_entry_speed_sqr = prev_nominal_speed*prev_nominal_speed; }
278 else { block->max_entry_speed_sqr = nominal_speed*nominal_speed; }
279 if (block->max_entry_speed_sqr > block->max_junction_speed_sqr) { block->max_entry_speed_sqr = block->max_junction_speed_sqr; }
280}
281
282
283// Re-calculates buffered motions profile parameters upon a motion-based override change.
284void plan_update_velocity_profile_parameters()
285{
286 uint8_t block_index = block_buffer_tail;
287 plan_block_t *block;
288 float nominal_speed;
289 float prev_nominal_speed = SOME_LARGE_VALUE; // Set high for first block nominal speed calculation.
290 while (block_index != block_buffer_head) {
291 block = &block_buffer[block_index];
292 nominal_speed = plan_compute_profile_nominal_speed(block);
293 plan_compute_profile_parameters(block, nominal_speed, prev_nominal_speed);
294 prev_nominal_speed = nominal_speed;
295 block_index = plan_next_block_index(block_index);
296 }
297 pl.previous_nominal_speed = prev_nominal_speed; // Update prev nominal speed for next incoming block.
298}
299
300
301/* Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
302 in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
303 rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
304 All position data passed to the planner must be in terms of machine position to keep the planner
305 independent of any coordinate system changes and offsets, which are handled by the g-code parser.
306 NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control.
307 In other words, the buffer head is never equal to the buffer tail. Also the feed rate input value
308 is used in three ways: as a normal feed rate if invert_feed_rate is false, as inverse time if
309 invert_feed_rate is true, or as seek/rapids rate if the feed_rate value is negative (and
310 invert_feed_rate always false).
311 The system motion condition tells the planner to plan a motion in the always unused block buffer
312 head. It avoids changing the planner state and preserves the buffer to ensure subsequent gcode
313 motions are still planned correctly, while the stepper module only points to the block buffer head
314 to execute the special system motion. */
315uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
316{
317 // Prepare and initialize new block. Copy relevant pl_data for block execution.
318 plan_block_t *block = &block_buffer[block_buffer_head];
319 memset(block,0,sizeof(plan_block_t)); // Zero all block values.
320 block->condition = pl_data->condition;
321 #ifdef VARIABLE_SPINDLE
322 block->spindle_speed = pl_data->spindle_speed;
323 #endif
324 #ifdef USE_LINE_NUMBERS
325 block->line_number = pl_data->line_number;
326 #endif
327
328 // Compute and store initial move distance data.
329 int32_t target_steps[N_AXIS], position_steps[N_AXIS];
330 float unit_vec[N_AXIS], delta_mm;
331 uint8_t idx;
332
333 // Copy position data based on type of motion being planned.
334 if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) {
335 #ifdef COREXY
336 position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
337 position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
338 position_steps[Z_AXIS] = sys_position[Z_AXIS];
339 #else
340 memcpy(position_steps, sys_position, sizeof(sys_position));
341 #endif
342 } else { memcpy(position_steps, pl.position, sizeof(pl.position)); }
343
344 #ifdef COREXY
345 target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]);
346 target_steps[B_MOTOR] = lround(target[B_MOTOR]*settings.steps_per_mm[B_MOTOR]);
347 block->steps[A_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
348 block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
349 #endif
350
351 for (idx=0; idx<N_AXIS; idx++) {
352 // Calculate target position in absolute steps, number of steps for each axis, and determine max step events.
353 // Also, compute individual axes distance for move and prep unit vector calculations.
354 // NOTE: Computes true distance from converted step values.
355 #ifdef COREXY
356 if ( !(idx == A_MOTOR) && !(idx == B_MOTOR) ) {
357 target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
358 block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
359 }
360 block->step_event_count = max(block->step_event_count, block->steps[idx]);
361 if (idx == A_MOTOR) {
362 delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] + target_steps[Y_AXIS]-position_steps[Y_AXIS])/settings.steps_per_mm[idx];
363 } else if (idx == B_MOTOR) {
364 delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] - target_steps[Y_AXIS]+position_steps[Y_AXIS])/settings.steps_per_mm[idx];
365 } else {
366 delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
367 }
368 #else
369 target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
370 block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
371 block->step_event_count = max(block->step_event_count, block->steps[idx]);
372 delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
373 #endif
374 unit_vec[idx] = delta_mm; // Store unit vector numerator
375
376 // Set direction bits. Bit enabled always means direction is negative.
377 if (delta_mm < 0.0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
378 }
379
380 // Bail if this is a zero-length block. Highly unlikely to occur.
381 if (block->step_event_count == 0) { return(PLAN_EMPTY_BLOCK); }
382
383 // Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
384 // down such that no individual axes maximum values are exceeded with respect to the line direction.
385 // NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes,
386 // if they are also orthogonal/independent. Operates on the absolute value of the unit vector.
387 block->millimeters = convert_delta_vector_to_unit_vector(unit_vec);
388 block->acceleration = limit_value_by_axis_maximum(settings.acceleration, unit_vec);
389 block->rapid_rate = limit_value_by_axis_maximum(settings.max_rate, unit_vec);
390
391 // Store programmed rate.
392 if (block->condition & PL_COND_FLAG_RAPID_MOTION) { block->programmed_rate = block->rapid_rate; }
393 else {
394 block->programmed_rate = pl_data->feed_rate;
395 if (block->condition & PL_COND_FLAG_INVERSE_TIME) { block->programmed_rate *= block->millimeters; }
396 }
397
398 // TODO: Need to check this method handling zero junction speeds when starting from rest.
399 if ((block_buffer_head == block_buffer_tail) || (block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
400
401 // Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later.
402 // If system motion, the system motion block always is assumed to start from rest and end at a complete stop.
403 block->entry_speed_sqr = 0.0;
404 block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity.
405
406 } else {
407 // Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
408 // Let a circle be tangent to both previous and current path line segments, where the junction
409 // deviation is defined as the distance from the junction to the closest edge of the circle,
410 // colinear with the circle center. The circular segment joining the two paths represents the
411 // path of centripetal acceleration. Solve for max velocity based on max acceleration about the
412 // radius of the circle, defined indirectly by junction deviation. This may be also viewed as
413 // path width or max_jerk in the previous Grbl version. This approach does not actually deviate
414 // from path, but used as a robust way to compute cornering speeds, as it takes into account the
415 // nonlinearities of both the junction angle and junction velocity.
416 //
417 // NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
418 // mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
419 // stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
420 // is exactly the same. Instead of motioning all the way to junction point, the machine will
421 // just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
422 // a continuous mode path, but ARM-based microcontrollers most certainly do.
423 //
424 // NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
425 // changed dynamically during operation nor can the line move geometry. This must be kept in
426 // memory in the event of a feedrate override changing the nominal speeds of blocks, which can
427 // change the overall maximum entry speed conditions of all blocks.
428
429 float junction_unit_vec[N_AXIS];
430 float junction_cos_theta = 0.0;
431 for (idx=0; idx<N_AXIS; idx++) {
432 junction_cos_theta -= pl.previous_unit_vec[idx]*unit_vec[idx];
433 junction_unit_vec[idx] = unit_vec[idx]-pl.previous_unit_vec[idx];
434 }
435
436 // NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
437 if (junction_cos_theta > 0.999999) {
438 // For a 0 degree acute junction, just set minimum junction speed.
439 block->max_junction_speed_sqr = MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED;
440 } else {
441 if (junction_cos_theta < -0.999999) {
442 // Junction is a straight line or 180 degrees. Junction speed is infinite.
443 block->max_junction_speed_sqr = SOME_LARGE_VALUE;
444 } else {
445 convert_delta_vector_to_unit_vector(junction_unit_vec);
446 float junction_acceleration = limit_value_by_axis_maximum(settings.acceleration, junction_unit_vec);
447 float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive.
448 block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
449 (junction_acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
450 }
451 }
452 }
453
454 // Block system motion from updating this data to ensure next g-code motion is computed correctly.
455 if (!(block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
456 float nominal_speed = plan_compute_profile_nominal_speed(block);
457 plan_compute_profile_parameters(block, nominal_speed, pl.previous_nominal_speed);
458 pl.previous_nominal_speed = nominal_speed;
459
460 // Update previous path unit_vector and planner position.
461 memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[]
462 memcpy(pl.position, target_steps, sizeof(target_steps)); // pl.position[] = target_steps[]
463
464 // New block is all set. Update buffer head and next buffer head indices.
465 block_buffer_head = next_buffer_head;
466 next_buffer_head = plan_next_block_index(block_buffer_head);
467
468 // Finish up by recalculating the plan with the new block.
469 planner_recalculate();
470 }
471 return(PLAN_OK);
472}
473
474
475// Reset the planner position vectors. Called by the system abort/initialization routine.
476void plan_sync_position()
477{
478 // TODO: For motor configurations not in the same coordinate frame as the machine position,
479 // this function needs to be updated to accomodate the difference.
480 uint8_t idx;
481 for (idx=0; idx<N_AXIS; idx++) {
482 #ifdef COREXY
483 if (idx==X_AXIS) {
484 pl.position[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
485 } else if (idx==Y_AXIS) {
486 pl.position[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
487 } else {
488 pl.position[idx] = sys_position[idx];
489 }
490 #else
491 pl.position[idx] = sys_position[idx];
492 #endif
493 }
494}
495
496
497// Returns the number of available blocks are in the planner buffer.
498uint8_t plan_get_block_buffer_available()
499{
500 if (block_buffer_head >= block_buffer_tail) { return((BLOCK_BUFFER_SIZE-1)-(block_buffer_head-block_buffer_tail)); }
501 return((block_buffer_tail-block_buffer_head-1));
502}
503
504
505// Returns the number of active blocks are in the planner buffer.
506// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h
507uint8_t plan_get_block_buffer_count()
508{
509 if (block_buffer_head >= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); }
510 return(BLOCK_BUFFER_SIZE - (block_buffer_tail-block_buffer_head));
511}
512
513
514// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail.
515// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
516void plan_cycle_reinitialize()
517{
518 // Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer.
519 st_update_plan_block_parameters();
520 block_buffer_planned = block_buffer_tail;
521 planner_recalculate();
522}