2009-01-28 23:48:21 +01:00
|
|
|
/*
|
2011-01-14 16:45:18 +01:00
|
|
|
stepper.c - stepper motor driver: executes motion plans using stepper motors
|
2009-01-28 23:48:21 +01:00
|
|
|
Part of Grbl
|
|
|
|
|
2013-03-28 17:11:34 +01:00
|
|
|
Copyright (c) 2011-2013 Sungeun K. Jeon
|
2012-12-08 23:00:58 +01:00
|
|
|
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
2011-09-24 15:46:41 +02:00
|
|
|
|
2009-01-28 23:48:21 +01:00
|
|
|
Grbl 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.
|
|
|
|
|
|
|
|
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
New startup script setting. New dry run, check gcode switches. New system state variable. Lots of reorganizing.
(All v0.8 features installed. Still likely buggy, but now thourough
testing will need to start to squash them all. As soon as we're done,
this will be pushed to master and v0.9 development will be started.
Please report ANY issues to us so we can get this rolled out ASAP.)
- User startup script! A user can now save one (up to 5 as compile-time
option) block of g-code in EEPROM memory. This will be run everytime
Grbl resets. Mainly to be used as a way to set your preferences, like
G21, G54, etc.
- New dry run and check g-code switches. Dry run moves ALL motions at
rapids rate ignoring spindle, coolant, and dwell commands. For rapid
physical proofing of your code. The check g-code switch ignores all
motion and provides the user a way to check if there are any errors in
their program that Grbl may not like.
- Program restart! (sort of). Program restart is typically an advanced
feature that allows users to restart a program mid-stream. The check
g-code switch can perform this feature by enabling the switch at the
start of the program, and disabling it at the desired point with some
minimal changes.
- New system state variable. This state variable tracks all of the
different state processes that Grbl performs, i.e. cycle start, feed
hold, homing, etc. This is mainly for making managing of these task
easier and more clear.
- Position lost state variable. Only when homing is enabled, Grbl will
refuse to move until homing is completed and position is known. This is
mainly for safety. Otherwise, it will let users fend for themselves.
- Moved the default settings defines into config.h. The plan is to
eventually create a set of config.h's for particular as-built machines
to help users from doing it themselves.
- Moved around misc defines into .h files. And lots of other little
things.
2012-11-03 18:32:23 +01:00
|
|
|
#include <avr/interrupt.h>
|
2009-01-28 23:48:21 +01:00
|
|
|
#include "stepper.h"
|
2011-02-05 00:55:37 +01:00
|
|
|
#include "config.h"
|
2011-02-05 00:45:41 +01:00
|
|
|
#include "settings.h"
|
2011-02-11 00:34:53 +01:00
|
|
|
#include "planner.h"
|
2013-08-19 17:24:22 +02:00
|
|
|
#include "nuts_bolts.h"
|
2011-01-31 23:04:39 +01:00
|
|
|
|
2012-11-09 03:23:47 +01:00
|
|
|
// Some useful constants
|
|
|
|
#define TICKS_PER_MICROSECOND (F_CPU/1000000)
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
#define RAMP_NOOP_CRUISE 0
|
|
|
|
#define RAMP_ACCEL 1
|
|
|
|
#define RAMP_DECEL 2
|
|
|
|
|
|
|
|
#define LOAD_NOOP 0
|
2013-10-12 18:35:26 +02:00
|
|
|
#define LOAD_SEGMENT 1
|
2013-08-19 17:24:22 +02:00
|
|
|
#define LOAD_BLOCK 2
|
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
#define SEGMENT_NOOP 0
|
|
|
|
#define SEGMENT_END_OF_BLOCK bit(0)
|
|
|
|
#define SEGMENT_ACCEL bit(1)
|
|
|
|
#define SEGMENT_DECEL bit(2)
|
|
|
|
|
|
|
|
#define MINIMUM_STEPS_PER_SEGMENT 1 // Don't change
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-09 17:33:22 +02:00
|
|
|
#define SEGMENT_BUFFER_SIZE 6
|
2011-02-04 22:09:09 +01:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Stepper state variable. Contains running data and trapezoid variables.
|
|
|
|
typedef struct {
|
|
|
|
// Used by the bresenham line algorithm
|
|
|
|
int32_t counter_x, // Counter variables for the bresenham line tracer
|
|
|
|
counter_y,
|
|
|
|
counter_z;
|
2013-10-09 17:33:22 +02:00
|
|
|
uint8_t segment_steps_remaining; // Steps remaining in line segment motion
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
// Used by inverse time algorithm to track step rate
|
2013-10-29 15:31:48 +01:00
|
|
|
int32_t counter_dist; // Inverse time distance traveled since last step event
|
|
|
|
uint32_t ramp_rate; // Inverse time distance traveled per interrupt tick
|
|
|
|
uint32_t dist_per_tick;
|
2012-12-08 23:00:58 +01:00
|
|
|
|
2013-08-19 17:24:22 +02:00
|
|
|
// Used by the stepper driver interrupt
|
|
|
|
uint8_t execute_step; // Flags step execution for each interrupt.
|
|
|
|
uint8_t step_pulse_time; // Step pulse reset time after step rise
|
|
|
|
uint8_t out_bits; // The next stepping-bits to be output
|
|
|
|
uint8_t load_flag;
|
|
|
|
|
2013-10-29 15:31:48 +01:00
|
|
|
uint8_t counter_ramp;
|
2013-08-19 17:24:22 +02:00
|
|
|
uint8_t ramp_type;
|
2012-01-06 18:10:41 +01:00
|
|
|
} stepper_t;
|
|
|
|
static stepper_t st;
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
// Stores stepper common data for executing steps in the segment buffer. Data can change mid-block when the
|
|
|
|
// planner updates the remaining block velocity profile with a more optimal plan or a feedrate override occurs.
|
|
|
|
// NOTE: Normally, this buffer is partially in-use, but, for the worst case scenario, it will never exceed
|
|
|
|
// the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1).
|
2013-08-19 17:24:22 +02:00
|
|
|
typedef struct {
|
2013-10-15 05:21:56 +02:00
|
|
|
int32_t step_events_remaining; // Tracks step event count for the executing planner block
|
2013-10-29 15:31:48 +01:00
|
|
|
uint32_t dist_next_step; // Scaled distance to next step
|
2013-10-15 05:21:56 +02:00
|
|
|
uint32_t initial_rate; // Initialized step rate at re/start of a planner block
|
|
|
|
uint32_t nominal_rate; // The nominal step rate for this block in step_events/minute
|
|
|
|
uint32_t rate_delta; // The steps/minute to add or subtract when changing speed (must be positive)
|
|
|
|
uint32_t current_approx_rate; // Tracks the approximate segment rate to predict steps per segment to execute
|
|
|
|
int32_t decelerate_after; // Tracks when to initiate deceleration according to the planner block
|
2013-08-19 17:24:22 +02:00
|
|
|
float mm_per_step;
|
|
|
|
} st_data_t;
|
2013-10-15 05:21:56 +02:00
|
|
|
static st_data_t segment_data[SEGMENT_BUFFER_SIZE-1];
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
// Primary stepper segment ring buffer. Contains small, short line segments for the stepper algorithm to execute,
|
|
|
|
// which are "checked-out" incrementally from the first block in the planner buffer. Once "checked-out", the steps
|
|
|
|
// in the segments buffer cannot be modified by the planner, where the remaining planner block steps still can.
|
2013-08-19 17:24:22 +02:00
|
|
|
typedef struct {
|
2013-10-09 17:33:22 +02:00
|
|
|
uint8_t n_step; // Number of step events to be executed for this segment
|
|
|
|
uint8_t st_data_index; // Stepper buffer common data index. Uses this information to execute this segment.
|
2013-10-12 18:35:26 +02:00
|
|
|
uint8_t flag; // Stepper algorithm bit-flag for special execution conditions.
|
2013-08-19 17:24:22 +02:00
|
|
|
} st_segment_t;
|
|
|
|
static st_segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
|
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
// Step segment ring buffer indices
|
2013-08-19 17:24:22 +02:00
|
|
|
static volatile uint8_t segment_buffer_tail;
|
2013-10-09 17:33:22 +02:00
|
|
|
static volatile uint8_t segment_buffer_head;
|
2013-08-19 17:24:22 +02:00
|
|
|
static uint8_t segment_next_head;
|
|
|
|
|
|
|
|
static volatile uint8_t busy; // Used to avoid ISR nesting of the "Stepper Driver Interrupt". Should never occur though.
|
|
|
|
static plan_block_t *pl_current_block; // A pointer to the planner block currently being traced
|
|
|
|
static st_segment_t *st_current_segment;
|
|
|
|
static st_data_t *st_current_data;
|
|
|
|
|
2013-10-09 17:33:22 +02:00
|
|
|
// Pointers for the step segment being prepped from the planner buffer. Accessed only by the
|
|
|
|
// main program. Pointers may be planning segments or planner blocks ahead of what being executed.
|
2013-10-15 05:21:56 +02:00
|
|
|
static plan_block_t *pl_prep_block; // Pointer to the planner block being prepped
|
|
|
|
static st_data_t *st_prep_data; // Pointer to the stepper common data being prepped
|
|
|
|
static uint8_t pl_prep_index; // Index of planner block being prepped
|
|
|
|
static uint8_t st_data_prep_index; // Index of stepper common data block being prepped
|
|
|
|
static uint8_t pl_partial_block_flag; // Flag indicating the planner has modified the prepped planner block
|
2013-10-09 17:33:22 +02:00
|
|
|
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
/* __________________________
|
|
|
|
/| |\ _________________ ^
|
|
|
|
/ | | \ /| |\ |
|
|
|
|
/ | | \ / | | \ s
|
|
|
|
/ | | | | | \ p
|
|
|
|
/ | | | | | \ e
|
|
|
|
+-----+------------------------+---+--+---------------+----+ e
|
|
|
|
| BLOCK 1 | BLOCK 2 | d
|
|
|
|
|
|
|
|
time ----->
|
|
|
|
|
|
|
|
The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates by block->rate_delta
|
|
|
|
until reaching cruising speed block->nominal_rate, and/or until step_events_remaining reaches block->decelerate_after
|
|
|
|
after which it decelerates until the block is completed. The driver uses constant acceleration, which is applied as
|
|
|
|
+/- block->rate_delta velocity increments by the midpoint rule at each ACCELERATION_TICKS_PER_SECOND.
|
|
|
|
*/
|
|
|
|
|
2012-11-01 16:37:27 +01:00
|
|
|
// Stepper state initialization. Cycle should only start if the st.cycle_start flag is
|
|
|
|
// enabled. Startup init and limits call this function but shouldn't start the cycle.
|
|
|
|
void st_wake_up()
|
2011-12-09 02:47:48 +01:00
|
|
|
{
|
2011-05-31 13:08:42 +02:00
|
|
|
// Enable steppers by resetting the stepper disable port
|
2012-11-01 16:37:27 +01:00
|
|
|
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) {
|
|
|
|
STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT);
|
|
|
|
} else {
|
|
|
|
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
|
|
|
|
}
|
New startup script setting. New dry run, check gcode switches. New system state variable. Lots of reorganizing.
(All v0.8 features installed. Still likely buggy, but now thourough
testing will need to start to squash them all. As soon as we're done,
this will be pushed to master and v0.9 development will be started.
Please report ANY issues to us so we can get this rolled out ASAP.)
- User startup script! A user can now save one (up to 5 as compile-time
option) block of g-code in EEPROM memory. This will be run everytime
Grbl resets. Mainly to be used as a way to set your preferences, like
G21, G54, etc.
- New dry run and check g-code switches. Dry run moves ALL motions at
rapids rate ignoring spindle, coolant, and dwell commands. For rapid
physical proofing of your code. The check g-code switch ignores all
motion and provides the user a way to check if there are any errors in
their program that Grbl may not like.
- Program restart! (sort of). Program restart is typically an advanced
feature that allows users to restart a program mid-stream. The check
g-code switch can perform this feature by enabling the switch at the
start of the program, and disabling it at the desired point with some
minimal changes.
- New system state variable. This state variable tracks all of the
different state processes that Grbl performs, i.e. cycle start, feed
hold, homing, etc. This is mainly for making managing of these task
easier and more clear.
- Position lost state variable. Only when homing is enabled, Grbl will
refuse to move until homing is completed and position is known. This is
mainly for safety. Otherwise, it will let users fend for themselves.
- Moved the default settings defines into config.h. The plan is to
eventually create a set of config.h's for particular as-built machines
to help users from doing it themselves.
- Moved around misc defines into .h files. And lots of other little
things.
2012-11-03 18:32:23 +01:00
|
|
|
if (sys.state == STATE_CYCLE) {
|
2012-11-01 16:37:27 +01:00
|
|
|
// Initialize stepper output bits
|
2013-08-19 17:24:22 +02:00
|
|
|
st.out_bits = settings.invert_mask;
|
2012-12-08 23:00:58 +01:00
|
|
|
// Initialize step pulse timing from settings.
|
2013-08-19 17:24:22 +02:00
|
|
|
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
|
2012-11-01 16:37:27 +01:00
|
|
|
// Enable stepper driver interrupt
|
2012-12-08 23:00:58 +01:00
|
|
|
st.execute_step = false;
|
2013-08-19 17:24:22 +02:00
|
|
|
st.load_flag = LOAD_BLOCK;
|
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
TCNT2 = 0; // Clear Timer2
|
|
|
|
TIMSK2 |= (1<<OCIE2A); // Enable Timer2 Compare Match A interrupt
|
|
|
|
TCCR2B = (1<<CS21); // Begin Timer2. Full speed, 1/8 prescaler
|
2012-11-01 16:37:27 +01:00
|
|
|
}
|
2011-05-31 13:08:42 +02:00
|
|
|
}
|
|
|
|
|
2013-04-05 17:21:52 +02:00
|
|
|
|
2011-10-12 04:51:04 +02:00
|
|
|
// Stepper shutdown
|
2011-12-09 02:47:48 +01:00
|
|
|
void st_go_idle()
|
|
|
|
{
|
2012-12-11 02:50:18 +01:00
|
|
|
// Disable stepper driver interrupt. Allow Timer0 to finish. It will disable itself.
|
2012-12-08 23:00:58 +01:00
|
|
|
TIMSK2 &= ~(1<<OCIE2A); // Disable Timer2 interrupt
|
|
|
|
TCCR2B = 0; // Disable Timer2
|
2013-03-28 17:11:34 +01:00
|
|
|
busy = false;
|
2012-12-08 23:00:58 +01:00
|
|
|
|
2012-10-22 03:18:24 +02:00
|
|
|
// Disable steppers only upon system alarm activated or by user setting to not be kept enabled.
|
Hard limits, homing direction, pull-off limits after homing, status reports in mm or inches, system alarm, and more.
- Thank you statement added for Alden Hart of Synthetos.
- Hard limits option added, which also works with homing by pulling off
the switches to help prevent unintended triggering. Hard limits use a
interrupt to sense a falling edge pin change and immediately go into
alarm mode, which stops everything and forces the user to issue a reset
(Ctrl-x) or reboot.
- Auto cycle start now a configuration option.
- Alarm mode: A new method to kill all Grbl processes in the event of
something catastrophic or potentially catastropic. Just works with hard
limits for now, but will be expanded to include g-code errors (most
likely) and other events.
- Updated status reports to be configurable in inches or mm mode. Much
more to do here, but this is the first step.
- New settings: auto cycle start, hard limit enable, homing direction
mask (which works the same as the stepper mask), homing pulloff
distance (or distance traveled from homed machine zero to prevent
accidental limit trip).
- Minor memory liberation and calculation speed ups.
2012-10-17 05:29:45 +02:00
|
|
|
if ((settings.stepper_idle_lock_time != 0xff) || bit_istrue(sys.execute,EXEC_ALARM)) {
|
2012-11-01 16:37:27 +01:00
|
|
|
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
|
|
|
|
// stop and not drift from residual inertial forces at the end of the last movement.
|
|
|
|
delay_ms(settings.stepper_idle_lock_time);
|
|
|
|
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) {
|
|
|
|
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
|
|
|
|
} else {
|
|
|
|
STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT);
|
|
|
|
}
|
2012-10-13 21:11:43 +02:00
|
|
|
}
|
2011-02-11 00:34:53 +01:00
|
|
|
}
|
|
|
|
|
2010-12-20 14:01:38 +01:00
|
|
|
|
2013-08-19 17:24:22 +02:00
|
|
|
/* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. It is based
|
2013-10-15 05:21:56 +02:00
|
|
|
on an inverse time stepper algorithm, where a timer ticks at a constant frequency and uses
|
|
|
|
time-distance counters to track when its the approximate time for a step event. For reference,
|
2013-10-29 15:31:48 +01:00
|
|
|
a similar inverse-time algorithm by Pramod Ranade is susceptible to numerical round-off, as
|
|
|
|
described, meaning that some axes steps may not execute correctly for a given multi-axis motion.
|
2013-10-15 05:21:56 +02:00
|
|
|
Grbl's algorithm differs by using a single inverse time-distance counter to manage a
|
|
|
|
Bresenham line algorithm for multi-axis step events, which ensures the number of steps for
|
|
|
|
each axis are executed exactly. In other words, Grbl uses a Bresenham within a Bresenham
|
|
|
|
algorithm, where one tracks time for step events and the other steps for multi-axis moves.
|
|
|
|
Grbl specifically uses the Bresenham algorithm due to its innate mathematical exactness and
|
|
|
|
low computational overhead, requiring simple integer +,- counters only.
|
|
|
|
This interrupt pops blocks from the step segment buffer and executes them by pulsing the
|
|
|
|
stepper pins appropriately. It is supported by The Stepper Port Reset Interrupt which it uses
|
|
|
|
to reset the stepper port after each pulse. The bresenham line tracer algorithm controls all
|
|
|
|
three stepper outputs simultaneously with these two interrupts.
|
2013-08-19 17:24:22 +02:00
|
|
|
*/
|
2013-10-12 18:35:26 +02:00
|
|
|
/* TODO:
|
2013-10-15 05:21:56 +02:00
|
|
|
- Measure time in ISR. Typical and worst-case. Should be virtually identical to last algorithm.
|
|
|
|
There are no major changes to the base operations of this ISR with the new segment buffer.
|
2013-10-12 18:35:26 +02:00
|
|
|
- Write how the acceleration counters work and why they are set at half via mid-point rule.
|
|
|
|
- Determine if placing the position counters elsewhere (or change them to 8-bit variables that
|
|
|
|
are added to the system position counters at the end of a segment) frees up cycles.
|
|
|
|
- Write a blurb about how the acceleration should be handled within the ISR. All of the
|
|
|
|
time/step/ramp counters accurately keep track of the remainders and phasing of the variables
|
|
|
|
with time. This means we do not have to compute them via expensive floating point beforehand.
|
|
|
|
- Need to do an analysis to determine if these counters are really that much cheaper. At least
|
|
|
|
find out when it isn't anymore. Particularly when the ISR is at a very high frequency.
|
2013-10-29 15:31:48 +01:00
|
|
|
- Create NOTE: to describe that the total time in this ISR must be less than the ISR frequency
|
|
|
|
in its worst case scenario.
|
2013-10-12 18:35:26 +02:00
|
|
|
*/
|
2012-12-08 23:00:58 +01:00
|
|
|
ISR(TIMER2_COMPA_vect)
|
|
|
|
{
|
|
|
|
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
|
2011-12-09 02:47:48 +01:00
|
|
|
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
|
2011-01-24 20:55:25 +01:00
|
|
|
|
2012-12-17 00:23:24 +01:00
|
|
|
// Pulse stepper port pins, if flagged. New block dir will always be set one timer tick
|
|
|
|
// before any step pulse due to algorithm design.
|
2012-12-08 23:00:58 +01:00
|
|
|
if (st.execute_step) {
|
|
|
|
st.execute_step = false;
|
2013-08-19 17:24:22 +02:00
|
|
|
STEPPING_PORT = ( STEPPING_PORT & ~(DIRECTION_MASK | STEP_MASK) ) | st.out_bits;
|
|
|
|
TCNT0 = st.step_pulse_time; // Reload Timer0 counter.
|
2012-12-08 23:00:58 +01:00
|
|
|
TCCR0B = (1<<CS21); // Begin Timer0. Full speed, 1/8 prescaler
|
|
|
|
}
|
|
|
|
|
2012-01-16 02:25:12 +01:00
|
|
|
busy = true;
|
2013-08-19 17:24:22 +02:00
|
|
|
sei(); // Re-enable interrupts to allow Stepper Port Reset Interrupt to fire on-time.
|
|
|
|
// NOTE: The remaining code in this ISR will finish before returning to main program.
|
|
|
|
|
|
|
|
// If there is no step segment, attempt to pop one from the stepper buffer
|
|
|
|
if (st.load_flag != LOAD_NOOP) {
|
|
|
|
|
|
|
|
// Anything in the buffer? If so, load and initialize next step segment.
|
|
|
|
if (segment_buffer_head != segment_buffer_tail) {
|
2012-12-17 00:23:24 +01:00
|
|
|
|
2013-10-29 15:31:48 +01:00
|
|
|
// Initialize new step segment and load number of steps to execute
|
2013-08-19 17:24:22 +02:00
|
|
|
st_current_segment = &segment_buffer[segment_buffer_tail];
|
|
|
|
st.segment_steps_remaining = st_current_segment->n_step;
|
|
|
|
|
2013-10-29 15:31:48 +01:00
|
|
|
// If the new segment starts a new planner block, initialize stepper variables and counters.
|
|
|
|
// NOTE: For new segments only, the step counters are not updated to ensure step phasing is continuous.
|
2013-08-19 17:24:22 +02:00
|
|
|
if (st.load_flag == LOAD_BLOCK) {
|
|
|
|
pl_current_block = plan_get_current_block(); // Should always be there. Stepper buffer handles this.
|
2013-10-29 15:31:48 +01:00
|
|
|
st_current_data = &segment_data[segment_buffer[segment_buffer_tail].st_data_index];
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-29 15:31:48 +01:00
|
|
|
// Initialize direction bits for block. Set execute flag to set directions bits upon next ISR tick.
|
2013-08-19 17:24:22 +02:00
|
|
|
st.out_bits = pl_current_block->direction_bits ^ settings.invert_mask;
|
2013-10-29 15:31:48 +01:00
|
|
|
st.execute_step = true;
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
// Initialize Bresenham line counters
|
|
|
|
st.counter_x = (pl_current_block->step_event_count >> 1);
|
|
|
|
st.counter_y = st.counter_x;
|
|
|
|
st.counter_z = st.counter_x;
|
2012-12-08 23:00:58 +01:00
|
|
|
|
2013-10-29 15:31:48 +01:00
|
|
|
// Initialize inverse time, step rate data, and acceleration ramp counters
|
|
|
|
st.counter_dist = st_current_data->dist_next_step; // dist_next_step always greater than ramp_rate.
|
|
|
|
st.ramp_rate = st_current_data->initial_rate;
|
|
|
|
st.counter_ramp = ISR_TICKS_PER_ACCELERATION_TICK/2; // Initialize ramp counter via midpoint rule
|
|
|
|
st.ramp_type = RAMP_NOOP_CRUISE; // Initialize as no ramp operation. Corrected later if necessary.
|
|
|
|
|
|
|
|
// Ensure the initial step rate exceeds the MINIMUM_STEP_RATE.
|
|
|
|
if (st.ramp_rate < MINIMUM_STEP_RATE) { st.dist_per_tick = MINIMUM_STEP_RATE; }
|
|
|
|
else { st.dist_per_tick = st.ramp_rate; }
|
2013-08-19 17:24:22 +02:00
|
|
|
}
|
|
|
|
|
2013-10-29 15:31:48 +01:00
|
|
|
// Check if ramp conditions have changed. If so, update ramp counters and control variables.
|
2013-10-15 05:21:56 +02:00
|
|
|
if ( st_current_segment->flag & (SEGMENT_DECEL | SEGMENT_ACCEL) ) {
|
2013-10-12 18:35:26 +02:00
|
|
|
/* Compute correct ramp count for a ramp change. Upon a switch from acceleration to deceleration,
|
|
|
|
or vice-versa, the new ramp count must be set to trigger the next acceleration tick equal to
|
|
|
|
the number of ramp ISR ticks counted since the last acceleration tick. This is ensures the
|
|
|
|
ramp is executed exactly as the plan dictates. Otherwise, when a ramp begins from a known
|
|
|
|
rate (nominal/cruise or initial), the ramp count must be set to ISR_TICKS_PER_ACCELERATION_TICK/2
|
2013-10-29 15:31:48 +01:00
|
|
|
as mandated by the mid-point rule. For the latter conditions, the ramp count have been
|
|
|
|
initialized such that the following computation is still correct. */
|
|
|
|
st.counter_ramp = ISR_TICKS_PER_ACCELERATION_TICK-st.counter_ramp;
|
2013-10-15 05:21:56 +02:00
|
|
|
if ( st_current_segment->flag & SEGMENT_DECEL ) { st.ramp_type = RAMP_DECEL; }
|
2013-10-12 18:35:26 +02:00
|
|
|
else { st.ramp_type = RAMP_ACCEL; }
|
2012-12-08 23:00:58 +01:00
|
|
|
}
|
|
|
|
|
2013-10-12 18:35:26 +02:00
|
|
|
st.load_flag = LOAD_NOOP; // Segment motion loaded. Set no-operation flag to skip during execution.
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2010-03-03 00:26:48 +01:00
|
|
|
} else {
|
2013-08-19 17:24:22 +02:00
|
|
|
// Can't discard planner block here if a feed hold stops in middle of block.
|
2011-05-31 13:08:42 +02:00
|
|
|
st_go_idle();
|
2012-01-06 18:10:41 +01:00
|
|
|
bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end
|
2012-12-08 23:00:58 +01:00
|
|
|
return; // Nothing to do but exit.
|
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2010-03-03 00:26:48 +01:00
|
|
|
}
|
2012-12-08 23:00:58 +01:00
|
|
|
|
|
|
|
// Adjust inverse time counter for ac/de-celerations
|
2013-10-29 15:31:48 +01:00
|
|
|
if (st.ramp_type) { // Ignored when ramp type is RAMP_NOOP_CRUISE
|
|
|
|
st.counter_ramp--; // Tick acceleration ramp counter
|
|
|
|
if (st.counter_ramp == 0) { // Adjust step rate when its time
|
|
|
|
st.counter_ramp = ISR_TICKS_PER_ACCELERATION_TICK; // Reload ramp counter
|
2013-08-19 17:24:22 +02:00
|
|
|
if (st.ramp_type == RAMP_ACCEL) { // Adjust velocity for acceleration
|
2013-10-29 15:31:48 +01:00
|
|
|
st.ramp_rate += st_current_data->rate_delta;
|
|
|
|
if (st.ramp_rate >= st_current_data->nominal_rate) { // Reached nominal rate.
|
|
|
|
st.ramp_rate = st_current_data->nominal_rate; // Set cruising velocity
|
2013-10-12 18:35:26 +02:00
|
|
|
st.ramp_type = RAMP_NOOP_CRUISE; // Set ramp flag to cruising
|
2013-10-29 15:31:48 +01:00
|
|
|
st.counter_ramp = ISR_TICKS_PER_ACCELERATION_TICK/2; // Re-initialize counter for next ramp change.
|
2012-12-08 23:00:58 +01:00
|
|
|
}
|
2013-10-12 18:35:26 +02:00
|
|
|
} else { // Adjust velocity for deceleration.
|
2013-10-29 15:31:48 +01:00
|
|
|
if (st.ramp_rate > st_current_data->rate_delta) {
|
|
|
|
st.ramp_rate -= st_current_data->rate_delta;
|
2013-10-25 06:12:13 +02:00
|
|
|
} else { // Moving near zero feed rate. Gracefully slow down.
|
2013-10-29 15:31:48 +01:00
|
|
|
st.ramp_rate >>= 1; // Integer divide by 2 until complete. Also prevents overflow.
|
2012-12-08 23:00:58 +01:00
|
|
|
}
|
|
|
|
}
|
2013-10-29 15:31:48 +01:00
|
|
|
// Adjust for minimum step rate, but retain operating ramp rate for accurate velocity tracing.
|
|
|
|
if (st.ramp_rate < MINIMUM_STEP_RATE) { st.dist_per_tick = MINIMUM_STEP_RATE; }
|
|
|
|
else { st.dist_per_tick = st.ramp_rate; }
|
2012-12-08 23:00:58 +01:00
|
|
|
}
|
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
// Iterate inverse time counter. Triggers each Bresenham step event.
|
2013-10-29 15:31:48 +01:00
|
|
|
st.counter_dist -= st.dist_per_tick;
|
2012-12-08 23:00:58 +01:00
|
|
|
|
|
|
|
// Execute Bresenham step event, when it's time to do so.
|
2013-10-29 15:31:48 +01:00
|
|
|
if (st.counter_dist < 0) {
|
|
|
|
st.counter_dist += st_current_data->dist_next_step; // Reload inverse time counter
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
st.out_bits = pl_current_block->direction_bits; // Reset out_bits and reload direction bits
|
2012-12-08 23:00:58 +01:00
|
|
|
st.execute_step = true;
|
|
|
|
|
|
|
|
// Execute step displacement profile by Bresenham line algorithm
|
2013-08-19 17:24:22 +02:00
|
|
|
st.counter_x -= pl_current_block->steps[X_AXIS];
|
2012-12-08 23:00:58 +01:00
|
|
|
if (st.counter_x < 0) {
|
2013-08-19 17:24:22 +02:00
|
|
|
st.out_bits |= (1<<X_STEP_BIT);
|
|
|
|
st.counter_x += pl_current_block->step_event_count;
|
|
|
|
if (st.out_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
|
2012-01-06 18:10:41 +01:00
|
|
|
else { sys.position[X_AXIS]++; }
|
2010-03-02 21:46:51 +01:00
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
st.counter_y -= pl_current_block->steps[Y_AXIS];
|
2012-12-08 23:00:58 +01:00
|
|
|
if (st.counter_y < 0) {
|
2013-08-19 17:24:22 +02:00
|
|
|
st.out_bits |= (1<<Y_STEP_BIT);
|
|
|
|
st.counter_y += pl_current_block->step_event_count;
|
|
|
|
if (st.out_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
|
2012-01-06 18:10:41 +01:00
|
|
|
else { sys.position[Y_AXIS]++; }
|
2010-03-02 21:46:51 +01:00
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
st.counter_z -= pl_current_block->steps[Z_AXIS];
|
2012-12-08 23:00:58 +01:00
|
|
|
if (st.counter_z < 0) {
|
2013-08-19 17:24:22 +02:00
|
|
|
st.out_bits |= (1<<Z_STEP_BIT);
|
|
|
|
st.counter_z += pl_current_block->step_event_count;
|
|
|
|
if (st.out_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
|
2012-01-06 18:10:41 +01:00
|
|
|
else { sys.position[Z_AXIS]++; }
|
2010-03-02 21:46:51 +01:00
|
|
|
}
|
2011-09-24 15:46:41 +02:00
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
// Check step events for trapezoid change or end of block.
|
2013-08-19 17:24:22 +02:00
|
|
|
st.segment_steps_remaining--; // Decrement step events count
|
|
|
|
if (st.segment_steps_remaining == 0) {
|
|
|
|
// Line move is complete, set load line flag to check for new move.
|
|
|
|
// Check if last line move in planner block. Discard if so.
|
2013-10-15 05:21:56 +02:00
|
|
|
if (st_current_segment->flag & SEGMENT_END_OF_BLOCK) {
|
2013-08-19 17:24:22 +02:00
|
|
|
plan_discard_current_block();
|
|
|
|
st.load_flag = LOAD_BLOCK;
|
|
|
|
} else {
|
2013-10-12 18:35:26 +02:00
|
|
|
st.load_flag = LOAD_SEGMENT;
|
2012-12-08 23:00:58 +01:00
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-25 06:12:13 +02:00
|
|
|
// Discard current segment by advancing buffer tail index
|
|
|
|
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
|
2009-02-08 20:40:24 +01:00
|
|
|
}
|
2012-12-08 23:00:58 +01:00
|
|
|
|
2013-08-19 17:24:22 +02:00
|
|
|
st.out_bits ^= settings.invert_mask; // Apply step port invert mask
|
2011-10-12 04:51:04 +02:00
|
|
|
}
|
2011-12-09 02:47:48 +01:00
|
|
|
busy = false;
|
2012-12-08 23:00:58 +01:00
|
|
|
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT;
|
2009-01-28 23:48:21 +01:00
|
|
|
}
|
|
|
|
|
2013-04-05 17:21:52 +02:00
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
// The Stepper Port Reset Interrupt: Timer0 OVF interrupt handles the falling edge of the step
|
|
|
|
// pulse. This should always trigger before the next Timer2 COMPA interrupt and independently
|
2012-12-08 23:00:58 +01:00
|
|
|
// finish, if Timer2 is disabled after completing a move.
|
|
|
|
ISR(TIMER0_OVF_vect)
|
2009-01-28 23:48:21 +01:00
|
|
|
{
|
2010-03-07 20:29:18 +01:00
|
|
|
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK);
|
2012-12-17 00:23:24 +01:00
|
|
|
TCCR0B = 0; // Disable timer until needed.
|
2009-01-28 23:48:21 +01:00
|
|
|
}
|
|
|
|
|
2012-06-27 05:48:42 +02:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Reset and clear stepper subsystem variables
|
|
|
|
void st_reset()
|
|
|
|
{
|
|
|
|
memset(&st, 0, sizeof(st));
|
2013-10-09 17:33:22 +02:00
|
|
|
|
2013-08-19 17:24:22 +02:00
|
|
|
st.load_flag = LOAD_BLOCK;
|
2011-12-09 02:47:48 +01:00
|
|
|
busy = false;
|
2013-10-09 17:33:22 +02:00
|
|
|
|
|
|
|
pl_current_block = NULL; // Planner block pointer used by stepper algorithm
|
|
|
|
pl_prep_block = NULL; // Planner block pointer used by segment buffer
|
|
|
|
pl_prep_index = 0; // Planner buffer indices are also reset to zero.
|
|
|
|
st_data_prep_index = 0;
|
|
|
|
|
2013-08-19 17:24:22 +02:00
|
|
|
segment_buffer_tail = 0;
|
2013-10-09 17:33:22 +02:00
|
|
|
segment_buffer_head = 0; // empty = tail
|
2013-08-19 17:24:22 +02:00
|
|
|
segment_next_head = 1;
|
2013-10-09 17:33:22 +02:00
|
|
|
|
|
|
|
pl_partial_block_flag = false;
|
2011-12-09 02:47:48 +01:00
|
|
|
}
|
|
|
|
|
2013-04-05 17:21:52 +02:00
|
|
|
|
2009-01-28 23:48:21 +01:00
|
|
|
// Initialize and start the stepper motor subsystem
|
|
|
|
void st_init()
|
|
|
|
{
|
2011-12-09 02:47:48 +01:00
|
|
|
// Configure directions of interface pins
|
|
|
|
STEPPING_DDR |= STEPPING_MASK;
|
2010-03-07 20:29:18 +01:00
|
|
|
STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask;
|
2011-05-31 13:08:42 +02:00
|
|
|
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
|
2009-01-28 23:48:21 +01:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Configure Timer 2
|
2012-12-08 23:00:58 +01:00
|
|
|
TIMSK2 &= ~(1<<OCIE2A); // Disable Timer2 interrupt while configuring it
|
|
|
|
TCCR2B = 0; // Disable Timer2 until needed
|
|
|
|
TCNT2 = 0; // Clear Timer2 counter
|
|
|
|
TCCR2A = (1<<WGM21); // Set CTC mode
|
|
|
|
OCR2A = (F_CPU/ISR_TICKS_PER_SECOND)/8 - 1; // Set Timer2 CTC rate
|
2013-03-28 17:11:34 +01:00
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
// Configure Timer 0
|
|
|
|
TIMSK0 &= ~(1<<TOIE0);
|
|
|
|
TCCR0A = 0; // Normal operation
|
|
|
|
TCCR0B = 0; // Disable Timer0 until needed
|
|
|
|
TIMSK0 |= (1<<TOIE0); // Enable overflow interrupt
|
|
|
|
|
2012-11-01 16:37:27 +01:00
|
|
|
// Start in the idle state, but first wake up to check for keep steppers enabled option.
|
|
|
|
st_wake_up();
|
2011-05-31 13:08:42 +02:00
|
|
|
st_go_idle();
|
2009-01-28 23:48:21 +01:00
|
|
|
}
|
|
|
|
|
2010-06-28 23:29:58 +02:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Planner external interface to start stepper interrupt and execute the blocks in queue. Called
|
2012-01-06 18:10:41 +01:00
|
|
|
// by the main program functions: planner auto-start and run-time command execution.
|
2011-12-09 02:47:48 +01:00
|
|
|
void st_cycle_start()
|
2009-01-28 23:48:21 +01:00
|
|
|
{
|
New startup script setting. New dry run, check gcode switches. New system state variable. Lots of reorganizing.
(All v0.8 features installed. Still likely buggy, but now thourough
testing will need to start to squash them all. As soon as we're done,
this will be pushed to master and v0.9 development will be started.
Please report ANY issues to us so we can get this rolled out ASAP.)
- User startup script! A user can now save one (up to 5 as compile-time
option) block of g-code in EEPROM memory. This will be run everytime
Grbl resets. Mainly to be used as a way to set your preferences, like
G21, G54, etc.
- New dry run and check g-code switches. Dry run moves ALL motions at
rapids rate ignoring spindle, coolant, and dwell commands. For rapid
physical proofing of your code. The check g-code switch ignores all
motion and provides the user a way to check if there are any errors in
their program that Grbl may not like.
- Program restart! (sort of). Program restart is typically an advanced
feature that allows users to restart a program mid-stream. The check
g-code switch can perform this feature by enabling the switch at the
start of the program, and disabling it at the desired point with some
minimal changes.
- New system state variable. This state variable tracks all of the
different state processes that Grbl performs, i.e. cycle start, feed
hold, homing, etc. This is mainly for making managing of these task
easier and more clear.
- Position lost state variable. Only when homing is enabled, Grbl will
refuse to move until homing is completed and position is known. This is
mainly for safety. Otherwise, it will let users fend for themselves.
- Moved the default settings defines into config.h. The plan is to
eventually create a set of config.h's for particular as-built machines
to help users from doing it themselves.
- Moved around misc defines into .h files. And lots of other little
things.
2012-11-03 18:32:23 +01:00
|
|
|
if (sys.state == STATE_QUEUED) {
|
|
|
|
sys.state = STATE_CYCLE;
|
2013-10-29 15:31:48 +01:00
|
|
|
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
|
New startup script setting. New dry run, check gcode switches. New system state variable. Lots of reorganizing.
(All v0.8 features installed. Still likely buggy, but now thourough
testing will need to start to squash them all. As soon as we're done,
this will be pushed to master and v0.9 development will be started.
Please report ANY issues to us so we can get this rolled out ASAP.)
- User startup script! A user can now save one (up to 5 as compile-time
option) block of g-code in EEPROM memory. This will be run everytime
Grbl resets. Mainly to be used as a way to set your preferences, like
G21, G54, etc.
- New dry run and check g-code switches. Dry run moves ALL motions at
rapids rate ignoring spindle, coolant, and dwell commands. For rapid
physical proofing of your code. The check g-code switch ignores all
motion and provides the user a way to check if there are any errors in
their program that Grbl may not like.
- Program restart! (sort of). Program restart is typically an advanced
feature that allows users to restart a program mid-stream. The check
g-code switch can perform this feature by enabling the switch at the
start of the program, and disabling it at the desired point with some
minimal changes.
- New system state variable. This state variable tracks all of the
different state processes that Grbl performs, i.e. cycle start, feed
hold, homing, etc. This is mainly for making managing of these task
easier and more clear.
- Position lost state variable. Only when homing is enabled, Grbl will
refuse to move until homing is completed and position is known. This is
mainly for safety. Otherwise, it will let users fend for themselves.
- Moved the default settings defines into config.h. The plan is to
eventually create a set of config.h's for particular as-built machines
to help users from doing it themselves.
- Moved around misc defines into .h files. And lots of other little
things.
2012-11-03 18:32:23 +01:00
|
|
|
st_wake_up();
|
2011-12-09 02:47:48 +01:00
|
|
|
}
|
2009-01-28 23:48:21 +01:00
|
|
|
}
|
2011-10-12 04:51:04 +02:00
|
|
|
|
2013-04-05 17:21:52 +02:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Execute a feed hold with deceleration, only during cycle. Called by main program.
|
|
|
|
void st_feed_hold()
|
|
|
|
{
|
New startup script setting. New dry run, check gcode switches. New system state variable. Lots of reorganizing.
(All v0.8 features installed. Still likely buggy, but now thourough
testing will need to start to squash them all. As soon as we're done,
this will be pushed to master and v0.9 development will be started.
Please report ANY issues to us so we can get this rolled out ASAP.)
- User startup script! A user can now save one (up to 5 as compile-time
option) block of g-code in EEPROM memory. This will be run everytime
Grbl resets. Mainly to be used as a way to set your preferences, like
G21, G54, etc.
- New dry run and check g-code switches. Dry run moves ALL motions at
rapids rate ignoring spindle, coolant, and dwell commands. For rapid
physical proofing of your code. The check g-code switch ignores all
motion and provides the user a way to check if there are any errors in
their program that Grbl may not like.
- Program restart! (sort of). Program restart is typically an advanced
feature that allows users to restart a program mid-stream. The check
g-code switch can perform this feature by enabling the switch at the
start of the program, and disabling it at the desired point with some
minimal changes.
- New system state variable. This state variable tracks all of the
different state processes that Grbl performs, i.e. cycle start, feed
hold, homing, etc. This is mainly for making managing of these task
easier and more clear.
- Position lost state variable. Only when homing is enabled, Grbl will
refuse to move until homing is completed and position is known. This is
mainly for safety. Otherwise, it will let users fend for themselves.
- Moved the default settings defines into config.h. The plan is to
eventually create a set of config.h's for particular as-built machines
to help users from doing it themselves.
- Moved around misc defines into .h files. And lots of other little
things.
2012-11-03 18:32:23 +01:00
|
|
|
if (sys.state == STATE_CYCLE) {
|
|
|
|
sys.state = STATE_HOLD;
|
|
|
|
sys.auto_start = false; // Disable planner auto start upon feed hold.
|
2011-10-12 04:51:04 +02:00
|
|
|
}
|
|
|
|
}
|
2011-12-09 02:47:48 +01:00
|
|
|
|
2013-04-05 17:21:52 +02:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
|
|
|
|
// runtime command execution in the main program, ensuring that the planner re-plans safely.
|
|
|
|
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
|
|
|
|
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
|
|
|
|
// Only the planner de/ac-celerations profiles and stepper rates have been updated.
|
|
|
|
void st_cycle_reinitialize()
|
|
|
|
{
|
2013-08-19 17:24:22 +02:00
|
|
|
// if (pl_current_block != NULL) {
|
2012-01-06 18:10:41 +01:00
|
|
|
// Replan buffer from the feed hold stop location.
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
// TODO: Need to add up all of the step events in the current planner block to give
|
|
|
|
// back to the planner. Should only need it for the current block.
|
|
|
|
// BUT! The planner block millimeters is all changed and may be changed into the next
|
|
|
|
// planner block. The block millimeters would need to be recalculated via step counts
|
|
|
|
// and the mm/step variable.
|
|
|
|
// OR. Do we plan the feed hold itself down with the planner.
|
|
|
|
|
|
|
|
// plan_cycle_reinitialize(st_current_data->step_events_remaining);
|
|
|
|
// st.ramp_type = RAMP_ACCEL;
|
2013-10-29 15:31:48 +01:00
|
|
|
// st.counter_ramp = ISR_TICKS_PER_ACCELERATION_TICK/2;
|
|
|
|
// st.ramp_rate = 0;
|
2013-08-19 17:24:22 +02:00
|
|
|
// sys.state = STATE_QUEUED;
|
|
|
|
// } else {
|
|
|
|
// sys.state = STATE_IDLE;
|
|
|
|
// }
|
New startup script setting. New dry run, check gcode switches. New system state variable. Lots of reorganizing.
(All v0.8 features installed. Still likely buggy, but now thourough
testing will need to start to squash them all. As soon as we're done,
this will be pushed to master and v0.9 development will be started.
Please report ANY issues to us so we can get this rolled out ASAP.)
- User startup script! A user can now save one (up to 5 as compile-time
option) block of g-code in EEPROM memory. This will be run everytime
Grbl resets. Mainly to be used as a way to set your preferences, like
G21, G54, etc.
- New dry run and check g-code switches. Dry run moves ALL motions at
rapids rate ignoring spindle, coolant, and dwell commands. For rapid
physical proofing of your code. The check g-code switch ignores all
motion and provides the user a way to check if there are any errors in
their program that Grbl may not like.
- Program restart! (sort of). Program restart is typically an advanced
feature that allows users to restart a program mid-stream. The check
g-code switch can perform this feature by enabling the switch at the
start of the program, and disabling it at the desired point with some
minimal changes.
- New system state variable. This state variable tracks all of the
different state processes that Grbl performs, i.e. cycle start, feed
hold, homing, etc. This is mainly for making managing of these task
easier and more clear.
- Position lost state variable. Only when homing is enabled, Grbl will
refuse to move until homing is completed and position is known. This is
mainly for safety. Otherwise, it will let users fend for themselves.
- Moved the default settings defines into config.h. The plan is to
eventually create a set of config.h's for particular as-built machines
to help users from doing it themselves.
- Moved around misc defines into .h files. And lots of other little
things.
2012-11-03 18:32:23 +01:00
|
|
|
sys.state = STATE_IDLE;
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
|
2013-10-09 17:33:22 +02:00
|
|
|
/* Prepares step segment buffer. Continuously called from main program.
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-25 06:12:13 +02:00
|
|
|
The segment buffer is an intermediary buffer interface between the execution of steps
|
|
|
|
by the stepper algorithm and the velocity profiles generated by the planner. The stepper
|
|
|
|
algorithm only executes steps within the segment buffer and is filled by the main program
|
|
|
|
when steps are "checked-out" from the first block in the planner buffer. This keeps the
|
|
|
|
step execution and planning optimization processes atomic and protected from each other.
|
|
|
|
The number of steps "checked-out" from the planner buffer and the number of segments in
|
|
|
|
the segment buffer is sized and computed such that no operation in the main program takes
|
|
|
|
longer than the time it takes the stepper algorithm to empty it before refilling it.
|
|
|
|
Currently, the segment buffer conservatively holds roughly up to 40-60 msec of steps.
|
|
|
|
|
|
|
|
NOTE: The segment buffer executes a set number of steps over an approximate time period.
|
|
|
|
If we try to execute over a set time period, it is difficult to guarantee or predict how
|
|
|
|
many steps will execute over it, especially when the step pulse phasing between the
|
|
|
|
neighboring segments are kept consistent. Meaning that, if the last segment step pulses
|
|
|
|
right before its end, the next segment must delay its first pulse so that the step pulses
|
|
|
|
are consistently spaced apart over time to keep the step pulse train nice and smooth.
|
|
|
|
Keeping track of phasing and ensuring that the exact number of steps are executed as
|
|
|
|
defined by the planner block, the related computational overhead gets quickly and
|
|
|
|
prohibitively expensive, especially in real-time.
|
|
|
|
Since the stepper algorithm automatically takes care of the step pulse phasing with
|
|
|
|
its ramp and inverse time counters, we don't have to explicitly and expensively track the
|
|
|
|
exact number of steps, time, or phasing of steps. All we need to do is approximate
|
|
|
|
the number of steps in each segment such that the segment buffer has enough execution time
|
|
|
|
for the main program to do what it needs to do and refill it when it has time. In other
|
|
|
|
words, we just need to compute a cheap approximation of the current velocity and the
|
|
|
|
number of steps over it.
|
2013-08-19 17:24:22 +02:00
|
|
|
*/
|
2013-10-09 17:33:22 +02:00
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
/*
|
|
|
|
TODO: Figure out how to enforce a deceleration when a feedrate override is reduced.
|
|
|
|
The problem is that when an override is reduced, the planner may not plan back to
|
|
|
|
the current rate. Meaning that the velocity profiles for certain conditions no longer
|
|
|
|
are trapezoidal or triangular. For example, if the current block is cruising at a
|
|
|
|
nominal rate and the feedrate override is reduced, the new nominal rate will now be
|
|
|
|
lower. The velocity profile must first decelerate to the new nominal rate and then
|
|
|
|
follow on the new plan. So the remaining velocity profile will have a decelerate,
|
|
|
|
cruise, and another decelerate.
|
|
|
|
Another issue is whether or not a feedrate override reduction causes a deceleration
|
|
|
|
that acts over several planner blocks. For example, say that the plan is already
|
|
|
|
heavily decelerating throughout it, reducing the feedrate will not do much to it. So,
|
|
|
|
how do we determine when to resume the new plan? How many blocks do we have to wait
|
|
|
|
until the new plan intersects with the deceleration curve? One plus though, the
|
|
|
|
deceleration will never be more than the number of blocks in the entire planner buffer,
|
|
|
|
but it theoretically can be equal to it when all planner blocks are decelerating already.
|
|
|
|
*/
|
2013-08-19 17:24:22 +02:00
|
|
|
void st_prep_buffer()
|
|
|
|
{
|
2013-10-29 15:31:48 +01:00
|
|
|
if (sys.state != STATE_QUEUED) { // Block until a motion state is issued
|
2013-08-19 17:24:22 +02:00
|
|
|
while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer.
|
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
// Initialize new segment
|
2013-10-12 18:35:26 +02:00
|
|
|
st_segment_t *prep_segment = &segment_buffer[segment_buffer_head];
|
2013-10-15 05:21:56 +02:00
|
|
|
prep_segment->flag = SEGMENT_NOOP;
|
2013-10-12 18:35:26 +02:00
|
|
|
|
2013-08-19 17:24:22 +02:00
|
|
|
// Determine if we need to load a new planner block.
|
|
|
|
if (pl_prep_block == NULL) {
|
2013-10-09 17:33:22 +02:00
|
|
|
pl_prep_block = plan_get_block_by_index(pl_prep_index); // Query planner for a queued block
|
|
|
|
if (pl_prep_block == NULL) { return; } // No planner blocks. Exit.
|
2013-10-12 18:35:26 +02:00
|
|
|
|
2013-10-25 06:12:13 +02:00
|
|
|
// Increment stepper common data index
|
2013-10-15 05:21:56 +02:00
|
|
|
if ( ++st_data_prep_index == (SEGMENT_BUFFER_SIZE-1) ) { st_data_prep_index = 0; }
|
|
|
|
|
|
|
|
// Check if the planner has re-computed this block mid-execution. If so, push the previous segment
|
|
|
|
// data. Otherwise, prepare a new segment data for the new planner block.
|
2013-10-09 17:33:22 +02:00
|
|
|
if (pl_partial_block_flag) {
|
|
|
|
|
|
|
|
// Prepare new shared segment block data and copy the relevant last segment block data.
|
|
|
|
st_data_t *last_st_prep_data;
|
2013-10-15 05:21:56 +02:00
|
|
|
last_st_prep_data = st_prep_data;
|
2013-10-09 17:33:22 +02:00
|
|
|
st_prep_data = &segment_data[st_data_prep_index];
|
|
|
|
|
|
|
|
st_prep_data->step_events_remaining = last_st_prep_data->step_events_remaining;
|
|
|
|
st_prep_data->rate_delta = last_st_prep_data->rate_delta;
|
2013-10-29 15:31:48 +01:00
|
|
|
st_prep_data->dist_next_step = last_st_prep_data->dist_next_step;
|
2013-10-15 05:21:56 +02:00
|
|
|
st_prep_data->nominal_rate = last_st_prep_data->nominal_rate; // TODO: Feedrate overrides recomputes this.
|
2013-10-09 17:33:22 +02:00
|
|
|
|
|
|
|
st_prep_data->mm_per_step = last_st_prep_data->mm_per_step;
|
|
|
|
|
|
|
|
pl_partial_block_flag = false; // Reset flag
|
|
|
|
|
|
|
|
} else {
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-09 17:33:22 +02:00
|
|
|
// Prepare commonly shared planner block data for the ensuing segment buffer moves ad-hoc, since
|
|
|
|
// the planner buffer can dynamically change the velocity profile data as blocks are added.
|
|
|
|
st_prep_data = &segment_data[st_data_prep_index];
|
|
|
|
|
|
|
|
// Initialize Bresenham variables
|
|
|
|
st_prep_data->step_events_remaining = pl_prep_block->step_event_count;
|
|
|
|
|
|
|
|
// Convert planner block velocity profile data to stepper rate and step distance data.
|
|
|
|
st_prep_data->nominal_rate = ceil(sqrt(pl_prep_block->nominal_speed_sqr)*(INV_TIME_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic)
|
|
|
|
st_prep_data->rate_delta = ceil(pl_prep_block->acceleration*
|
|
|
|
((INV_TIME_MULTIPLIER/(60.0*60.0))/(ISR_TICKS_PER_SECOND*ACCELERATION_TICKS_PER_SECOND))); // (mult*mm/isr_tic/accel_tic)
|
2013-10-29 15:31:48 +01:00
|
|
|
st_prep_data->dist_next_step = ceil((pl_prep_block->millimeters*INV_TIME_MULTIPLIER)/pl_prep_block->step_event_count); // (mult*mm/step)
|
2013-10-09 17:33:22 +02:00
|
|
|
|
|
|
|
// TODO: Check if we really need to store this.
|
|
|
|
st_prep_data->mm_per_step = pl_prep_block->millimeters/pl_prep_block->step_event_count;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert planner entry speed to stepper initial rate.
|
|
|
|
st_prep_data->initial_rate = ceil(sqrt(pl_prep_block->entry_speed_sqr)*(INV_TIME_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic)
|
|
|
|
|
|
|
|
// TODO: Nominal rate changes with feedrate override.
|
|
|
|
// st_prep_data->nominal_rate = ceil(sqrt(pl_prep_block->nominal_speed_sqr)*(INV_TIME_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic)
|
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
st_prep_data->current_approx_rate = st_prep_data->initial_rate;
|
|
|
|
|
|
|
|
// Calculate the planner block velocity profile type, determine deceleration point, and initial ramp.
|
2013-10-09 17:33:22 +02:00
|
|
|
float mm_decelerate_after = plan_calculate_velocity_profile(pl_prep_index);
|
2013-10-12 18:35:26 +02:00
|
|
|
st_prep_data->decelerate_after = ceil( mm_decelerate_after/st_prep_data->mm_per_step );
|
2013-10-15 05:21:56 +02:00
|
|
|
if (st_prep_data->decelerate_after > 0) { // If 0, SEGMENT_DECEL flag is set later.
|
|
|
|
if (st_prep_data->initial_rate != st_prep_data->nominal_rate) { prep_segment->flag = SEGMENT_ACCEL; }
|
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
}
|
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
// Set new segment to point to the current segment data block.
|
2013-10-12 18:35:26 +02:00
|
|
|
prep_segment->st_data_index = st_data_prep_index;
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-25 06:12:13 +02:00
|
|
|
// Approximate the velocity over the new segment using the already computed rate values.
|
|
|
|
// NOTE: This assumes that each segment will have an execution time roughly equal to every ACCELERATION_TICK.
|
|
|
|
// We do this to minimize memory and computational requirements. However, this could easily be replaced with
|
|
|
|
// a more exact approximation or have a unique time per segment, if CPU and memory overhead allows.
|
2013-10-15 05:21:56 +02:00
|
|
|
if (st_prep_data->decelerate_after <= 0) {
|
2013-10-29 15:31:48 +01:00
|
|
|
if (st_prep_data->decelerate_after == 0) { prep_segment->flag = SEGMENT_DECEL; } // Set segment deceleration flag
|
2013-10-15 05:21:56 +02:00
|
|
|
else { st_prep_data->current_approx_rate -= st_prep_data->rate_delta; }
|
|
|
|
if (st_prep_data->current_approx_rate < st_prep_data->rate_delta) { st_prep_data->current_approx_rate >>= 1; }
|
|
|
|
} else {
|
|
|
|
if (st_prep_data->current_approx_rate < st_prep_data->nominal_rate) {
|
|
|
|
st_prep_data->current_approx_rate += st_prep_data->rate_delta;
|
|
|
|
if (st_prep_data->current_approx_rate > st_prep_data->nominal_rate) {
|
|
|
|
st_prep_data->current_approx_rate = st_prep_data->nominal_rate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-09 17:33:22 +02:00
|
|
|
|
2013-10-25 06:12:13 +02:00
|
|
|
// Compute the number of steps in the prepped segment based on the approximate current rate.
|
2013-10-29 15:31:48 +01:00
|
|
|
// NOTE: The dist_next_step divide cancels out the INV_TIME_MULTIPLIER and converts the rate value to steps.
|
2013-10-15 05:21:56 +02:00
|
|
|
prep_segment->n_step = ceil(max(MINIMUM_STEP_RATE,st_prep_data->current_approx_rate)*
|
2013-10-29 15:31:48 +01:00
|
|
|
(ISR_TICKS_PER_SECOND/ACCELERATION_TICKS_PER_SECOND)/st_prep_data->dist_next_step);
|
2013-10-25 06:12:13 +02:00
|
|
|
// NOTE: Ensures it moves for very slow motions, but MINIMUM_STEP_RATE should always set this too. Perhaps
|
|
|
|
// a compile-time check to see if MINIMUM_STEP_RATE is set high enough is all that is needed.
|
|
|
|
prep_segment->n_step = max(prep_segment->n_step,MINIMUM_STEPS_PER_SEGMENT);
|
|
|
|
// NOTE: As long as the ACCELERATION_TICKS_PER_SECOND is valid, n_step should never exceed 255 and overflow.
|
2013-10-15 05:21:56 +02:00
|
|
|
// prep_segment->n_step = min(prep_segment->n_step,MAXIMUM_STEPS_PER_BLOCK); // Prevent unsigned int8 overflow.
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
// Check if n_step exceeds steps remaining in planner block. If so, truncate.
|
2013-10-12 18:35:26 +02:00
|
|
|
if (prep_segment->n_step > st_prep_data->step_events_remaining) {
|
|
|
|
prep_segment->n_step = st_prep_data->step_events_remaining;
|
2013-08-19 17:24:22 +02:00
|
|
|
}
|
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
// Check if n_step crosses decelerate point in block. If so, truncate to ensure the deceleration
|
|
|
|
// ramp counters are set correctly during execution.
|
2013-08-19 17:24:22 +02:00
|
|
|
if (st_prep_data->decelerate_after > 0) {
|
2013-10-12 18:35:26 +02:00
|
|
|
if (prep_segment->n_step > st_prep_data->decelerate_after) {
|
|
|
|
prep_segment->n_step = st_prep_data->decelerate_after;
|
2013-10-09 17:33:22 +02:00
|
|
|
}
|
|
|
|
}
|
2013-10-12 18:35:26 +02:00
|
|
|
|
2013-10-25 06:12:13 +02:00
|
|
|
// Update stepper common data variables.
|
2013-10-15 05:21:56 +02:00
|
|
|
st_prep_data->decelerate_after -= prep_segment->n_step;
|
2013-10-12 18:35:26 +02:00
|
|
|
st_prep_data->step_events_remaining -= prep_segment->n_step;
|
2013-10-15 05:21:56 +02:00
|
|
|
|
|
|
|
// Check for end of planner block
|
2013-08-19 17:24:22 +02:00
|
|
|
if ( st_prep_data->step_events_remaining == 0 ) {
|
2013-10-29 15:31:48 +01:00
|
|
|
|
|
|
|
// TODO: When a feed hold ends, the step_events_remaining will also be zero, even though a block
|
|
|
|
// have partially been completed. We need to flag the stepper algorithm to indicate a stepper shutdown
|
|
|
|
// when complete, but not remove the planner block unless it truly is the end of the block (rare).
|
|
|
|
|
2013-10-15 05:21:56 +02:00
|
|
|
// Set EOB bitflag so stepper algorithm discards the planner block after this segment completes.
|
|
|
|
prep_segment->flag |= SEGMENT_END_OF_BLOCK;
|
2013-10-12 18:35:26 +02:00
|
|
|
// Move planner pointer to next block and flag to load a new block for the next segment.
|
2013-10-25 06:12:13 +02:00
|
|
|
pl_prep_index = plan_next_block_index(pl_prep_index);
|
2013-08-19 17:24:22 +02:00
|
|
|
pl_prep_block = NULL;
|
2013-10-12 18:35:26 +02:00
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
|
2013-10-09 17:33:22 +02:00
|
|
|
// New step segment completed. Increment segment buffer indices.
|
2013-08-19 17:24:22 +02:00
|
|
|
segment_buffer_head = segment_next_head;
|
2013-10-25 06:12:13 +02:00
|
|
|
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
|
2013-10-15 05:21:56 +02:00
|
|
|
|
|
|
|
// long a = prep_segment->n_step;
|
|
|
|
// printInteger(a);
|
|
|
|
// printString(" ");
|
2013-08-19 17:24:22 +02:00
|
|
|
|
|
|
|
}
|
2013-10-29 15:31:48 +01:00
|
|
|
}
|
2013-08-19 17:24:22 +02:00
|
|
|
}
|
2013-10-09 17:33:22 +02:00
|
|
|
|
|
|
|
uint8_t st_get_prep_block_index()
|
|
|
|
{
|
|
|
|
// Returns only the index but doesn't state if the block has been partially executed. How do we simply check for this?
|
|
|
|
return(pl_prep_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void st_fetch_partial_block_parameters(uint8_t block_index, float *millimeters_remaining, uint8_t *is_decelerating)
|
|
|
|
{
|
|
|
|
// if called, can we assume that this always changes and needs to be updated? if so, then
|
|
|
|
// we can perform all of the segment buffer setup tasks here to make sure the next time
|
|
|
|
// the segments are loaded, the st_data buffer is updated correctly.
|
|
|
|
// !!! Make sure that this is always pointing to the correct st_prep_data block.
|
|
|
|
|
|
|
|
// When a mid-block acceleration occurs, we have to make sure the ramp counters are updated
|
|
|
|
// correctly, much in the same fashion as the deceleration counters. Need to think about this
|
|
|
|
// make sure this is right, but i'm pretty sure it is.
|
|
|
|
|
2013-10-12 18:35:26 +02:00
|
|
|
// TODO: NULL means that the segment buffer has just completed a planner block. Clean up!
|
2013-10-09 17:33:22 +02:00
|
|
|
if (pl_prep_block != NULL) {
|
|
|
|
*millimeters_remaining = st_prep_data->step_events_remaining*st_prep_data->mm_per_step;
|
|
|
|
if (st_prep_data->decelerate_after > 0) { *is_decelerating = false; }
|
|
|
|
else { *is_decelerating = true; }
|
|
|
|
|
|
|
|
// Flag for new prep_block when st_prep_buffer() is called after the planner recomputes.
|
|
|
|
pl_partial_block_flag = true;
|
|
|
|
pl_prep_block = NULL;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|