Merge branch 'dev' of https://github.com/grbl/grbl into dev
Conflicts: limits.c
This commit is contained in:
parent
3054b2df77
commit
903b462b2e
@ -24,7 +24,7 @@ Grbl includes full acceleration management with look ahead. That means the contr
|
|||||||
- Soft limits: Checks if any motion command exceeds workspace limits. Alarms out when found. Another safety feature, but, unlike hard limits, position does not get lost, as it forces a feed hold before erroring out.
|
- Soft limits: Checks if any motion command exceeds workspace limits. Alarms out when found. Another safety feature, but, unlike hard limits, position does not get lost, as it forces a feed hold before erroring out.
|
||||||
- Pin mapping: In an effort for Grbl to be compatible with other AVR architectures, such as the 1280 or 2560, a new pin_map.h configuration file has been created to allow Grbl to be compiled for them. This is currently user supported, so your mileage may vary. If you run across a bug, please let us know or better send us a fix! Thanks in advance!
|
- Pin mapping: In an effort for Grbl to be compatible with other AVR architectures, such as the 1280 or 2560, a new pin_map.h configuration file has been created to allow Grbl to be compiled for them. This is currently user supported, so your mileage may vary. If you run across a bug, please let us know or better send us a fix! Thanks in advance!
|
||||||
- New Grbl SIMULATOR by @jgeisler: A completely independent wrapper of the Grbl main source code that may be compiled as an executable on a computer. No Arduino required. Simply simulates the responses of Grbl as if it was on an Arduino. May be used for many things: checking out how Grbl works, pre-process moves for GUI graphics, debugging of new features, etc. Much left to do, but potentially very powerful, as the dummy AVR variables can be written to output anything you need.
|
- New Grbl SIMULATOR by @jgeisler: A completely independent wrapper of the Grbl main source code that may be compiled as an executable on a computer. No Arduino required. Simply simulates the responses of Grbl as if it was on an Arduino. May be used for many things: checking out how Grbl works, pre-process moves for GUI graphics, debugging of new features, etc. Much left to do, but potentially very powerful, as the dummy AVR variables can be written to output anything you need.
|
||||||
- Homing routine updated: Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. Also reduces soft limits CPU overhead.
|
- Homing routine updated: Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. Now tied directly into the main planner and stepper modules to reduce flash space and allow maximum speeds during seeking.
|
||||||
- Feedrate overrides: In the works, but planner has begun to be re-factored for this feature.
|
- Feedrate overrides: In the works, but planner has begun to be re-factored for this feature.
|
||||||
- Jogging controls: Methodology needs to be to figured out first. Could be dropped due to flash space concerns. Last item on the agenda.
|
- Jogging controls: Methodology needs to be to figured out first. Could be dropped due to flash space concerns. Last item on the agenda.
|
||||||
|
|
||||||
|
48
config.h
48
config.h
@ -47,13 +47,13 @@
|
|||||||
#define CMD_STATUS_REPORT '?'
|
#define CMD_STATUS_REPORT '?'
|
||||||
#define CMD_FEED_HOLD '!'
|
#define CMD_FEED_HOLD '!'
|
||||||
#define CMD_CYCLE_START '~'
|
#define CMD_CYCLE_START '~'
|
||||||
#define CMD_RESET 0x18 // ctrl-x
|
#define CMD_RESET 0x18 // ctrl-x.
|
||||||
|
|
||||||
// Uncomment the following define if you are using hardware that drives high when your limits
|
// Uncomment the following define if you are using hardware that drives high when your limits
|
||||||
// are reached. You will need to ensure that you have appropriate pull-down resistors on the
|
// are reached. You will need to ensure that you have appropriate pull-down resistors on the
|
||||||
// limit switch input pins, or that your hardware drives the pins low when they are open (non-
|
// limit switch input pins, or that your hardware drives the pins low when they are open (non-
|
||||||
// triggered).
|
// triggered).
|
||||||
// #define LIMIT_SWITCHES_ACTIVE_HIGH
|
// #define LIMIT_SWITCHES_ACTIVE_HIGH // Uncomment to enable
|
||||||
|
|
||||||
// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
|
// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
|
||||||
// the user to perform the homing cycle (or override the locks) before doing anything else. This is
|
// the user to perform the homing cycle (or override the locks) before doing anything else. This is
|
||||||
@ -88,27 +88,22 @@
|
|||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
// ADVANCED CONFIGURATION OPTIONS:
|
// ADVANCED CONFIGURATION OPTIONS:
|
||||||
|
|
||||||
// The "Stepper Driver Interrupt" employs an inverse time algorithm to manage the Bresenham line
|
// The temporal resolution of the acceleration management subsystem. A higher number gives smoother
|
||||||
// stepping algorithm. The value ISR_TICKS_PER_SECOND is the frequency(Hz) at which the inverse time
|
// acceleration, particularly noticeable on machines that run at very high feedrates, but may negatively
|
||||||
// algorithm ticks at. Recommended step frequencies are limited by the inverse time frequency by
|
// impact performance. The correct value for this parameter is machine dependent, so it's advised to
|
||||||
// approximately 0.75-0.9 * ISR_TICK_PER_SECOND. Meaning for 30kHz, the max step frequency is roughly
|
// set this only as high as needed. Approximate successful values can widely range from 50 to 200 or more.
|
||||||
// 22.5-27kHz, but 30kHz is still possible, just not optimal. An Arduino can safely complete a single
|
#define ACCELERATION_TICKS_PER_SECOND 100
|
||||||
// interrupt of the current stepper driver algorithm theoretically up to a frequency of 35-40kHz, but
|
|
||||||
// CPU overhead increases exponentially as this frequency goes up. So there will be little left for
|
|
||||||
// other processes like arcs.
|
|
||||||
#define ISR_TICKS_PER_SECOND 30000L // Integer (Hz)
|
|
||||||
|
|
||||||
// The temporal resolution of the acceleration management subsystem. Higher number give smoother
|
// Creates a delay between the direction pin setting and corresponding step pulse by creating
|
||||||
// acceleration but may impact performance. If you run at very high feedrates (>15kHz or so) and
|
// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare)
|
||||||
// very high accelerations, this will reduce the error between how the planner plans the velocity
|
// sets the direction pins, and does not immediately set the stepper pins, as it would in
|
||||||
// profiles and how the stepper program actually performs them. The correct value for this parameter
|
// normal operation. The Timer2 compare fires next to set the stepper pins after the step
|
||||||
// is machine dependent, so it's advised to set this only as high as needed. Approximate successful
|
// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed
|
||||||
// values can widely range from 50 to 200 or more. Cannot be greater than ISR_TICKS_PER_SECOND/2.
|
// by the step pulse time plus the step pulse delay. (Thanks langwadt for the idea!)
|
||||||
// NOTE: Ramp count variable type in stepper module may need to be updated if changed.
|
// NOTE: Uncomment to enable. The recommended delay must be > 3us, and, when added with the
|
||||||
#define ACCELERATION_TICKS_PER_SECOND 120L
|
// user-supplied step pulse time, the total time must not exceed 127us. Reported successful
|
||||||
|
// values for certain setups have ranged from 5 to 20us.
|
||||||
// NOTE: Make sure this value is less than 256, when adjusting both dependent parameters.
|
// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.
|
||||||
#define ISR_TICKS_PER_ACCELERATION_TICK (ISR_TICKS_PER_SECOND/ACCELERATION_TICKS_PER_SECOND)
|
|
||||||
|
|
||||||
// Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at
|
// Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at
|
||||||
// every buffer block junction, except for starting from rest and end of the buffer, which are always
|
// every buffer block junction, except for starting from rest and end of the buffer, which are always
|
||||||
@ -145,7 +140,7 @@
|
|||||||
// block velocity profile is traced exactly. The size of this buffer governs how much step
|
// block velocity profile is traced exactly. The size of this buffer governs how much step
|
||||||
// execution lead time there is for other Grbl processes have to compute and do their thing
|
// execution lead time there is for other Grbl processes have to compute and do their thing
|
||||||
// before having to come back and refill this buffer, currently at ~50msec of step moves.
|
// before having to come back and refill this buffer, currently at ~50msec of step moves.
|
||||||
// #define SEGMENT_BUFFER_SIZE 7 // Uncomment to override default in stepper.h.
|
// #define SEGMENT_BUFFER_SIZE 6 // Uncomment to override default in stepper.h.
|
||||||
|
|
||||||
// Line buffer size from the serial input stream to be executed. Also, governs the size of
|
// Line buffer size from the serial input stream to be executed. Also, governs the size of
|
||||||
// each of the startup blocks, as they are each stored as a string of this size. Make sure
|
// each of the startup blocks, as they are each stored as a string of this size. Make sure
|
||||||
@ -176,6 +171,7 @@
|
|||||||
// case, please report any successes to grbl administrators!
|
// case, please report any successes to grbl administrators!
|
||||||
// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.
|
// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.
|
||||||
|
|
||||||
|
#define ENABLE_SOFTWARE_DEBOUNCE
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -184,9 +180,9 @@
|
|||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:
|
// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:
|
||||||
|
|
||||||
#if (ISR_TICKS_PER_ACCELERATION_TICK > 255)
|
// #if (ISR_TICKS_PER_ACCELERATION_TICK > 255)
|
||||||
#error Parameters ACCELERATION_TICKS / ISR_TICKS must be < 256 to prevent integer overflow.
|
// #error Parameters ACCELERATION_TICKS / ISR_TICKS must be < 256 to prevent integer overflow.
|
||||||
#endif
|
// #endif
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
#endif
|
#endif
|
||||||
|
164
limits.c
164
limits.c
@ -22,6 +22,7 @@
|
|||||||
#include <util/delay.h>
|
#include <util/delay.h>
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/wdt.h>
|
||||||
#include "stepper.h"
|
#include "stepper.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "nuts_bolts.h"
|
#include "nuts_bolts.h"
|
||||||
@ -40,17 +41,29 @@ void limits_init()
|
|||||||
|
|
||||||
#ifndef LIMIT_SWITCHES_ACTIVE_HIGH
|
#ifndef LIMIT_SWITCHES_ACTIVE_HIGH
|
||||||
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
|
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
|
||||||
#else // LIMIT_SWITCHES_ACTIVE_HIGH
|
#else
|
||||||
LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
|
LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
|
||||||
#endif // !LIMIT_SWITCHES_ACTIVE_HIGH
|
#endif
|
||||||
|
|
||||||
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
|
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
|
||||||
LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
|
LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
|
||||||
PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
|
PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
|
||||||
} else {
|
} else {
|
||||||
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable
|
limits_disable();
|
||||||
PCICR &= ~(1 << LIMIT_INT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_SOFTWARE_DEBOUNCE
|
||||||
|
MCUSR &= ~(1<<WDRF);
|
||||||
|
WDTCSR |= (1<<WDCE) | (1<<WDE);
|
||||||
|
WDTCSR = (1<<WDP0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void limits_disable()
|
||||||
|
{
|
||||||
|
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable specific pins of the Pin Change Interrupt
|
||||||
|
PCICR &= ~(1 << LIMIT_INT); // Disable Pin Change Interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -63,48 +76,83 @@ void limits_init()
|
|||||||
// homing cycles and will not respond correctly. Upon user request or need, there may be a
|
// homing cycles and will not respond correctly. Upon user request or need, there may be a
|
||||||
// special pinout for an e-stop, but it is generally recommended to just directly connect
|
// special pinout for an e-stop, but it is generally recommended to just directly connect
|
||||||
// your e-stop switch to the Arduino reset pin, since it is the most correct way to do this.
|
// your e-stop switch to the Arduino reset pin, since it is the most correct way to do this.
|
||||||
ISR(LIMIT_INT_vect)
|
#ifdef ENABLE_SOFTWARE_DEBOUNCE
|
||||||
{
|
ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
|
||||||
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
|
ISR(WDT_vect)
|
||||||
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
|
{
|
||||||
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
|
WDTCSR &= ~(1<<WDIE);
|
||||||
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
|
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
|
||||||
// limit setting if their limits are constantly triggering after a reset and move their axes.
|
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
|
||||||
if (sys.state != STATE_ALARM) {
|
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
|
||||||
if (bit_isfalse(sys.execute,EXEC_ALARM)) {
|
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
|
||||||
mc_reset(); // Initiate system kill.
|
// limit setting if their limits are constantly triggering after a reset and move their axes.
|
||||||
sys.execute |= EXEC_CRIT_EVENT; // Indicate hard limit critical event
|
if (sys.state != STATE_ALARM) {
|
||||||
|
if (bit_isfalse(sys.execute,EXEC_ALARM)) {
|
||||||
|
#ifndef LIMIT_SWITCHES_ACTIVE_HIGH
|
||||||
|
if ((LIMIT_PIN & LIMIT_MASK) ^ LIMIT_MASK) {
|
||||||
|
#else
|
||||||
|
if (LIMIT_PIN & LIMIT_MASK) {
|
||||||
|
#endif
|
||||||
|
mc_reset(); // Initiate system kill.
|
||||||
|
sys.execute |= EXEC_CRIT_EVENT; // Indicate hard limit critical event
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
#else
|
||||||
|
ISR(LIMIT_INT_vect)
|
||||||
|
{
|
||||||
|
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
|
||||||
|
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
|
||||||
|
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
|
||||||
|
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
|
||||||
|
// limit setting if their limits are constantly triggering after a reset and move their axes.
|
||||||
|
if (sys.state != STATE_ALARM) {
|
||||||
|
if (bit_isfalse(sys.execute,EXEC_ALARM)) {
|
||||||
|
mc_reset(); // Initiate system kill.
|
||||||
|
sys.execute |= EXEC_CRIT_EVENT; // Indicate hard limit critical event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Moves all specified axes in same specified direction (positive=true, negative=false)
|
// Moves specified cycle axes all at homing rate, either approaching or disengaging the limit
|
||||||
// and at the homing rate. Homing is a special motion case, where there is only an
|
// switches. Homing is a special motion case, where there is only an acceleration followed
|
||||||
// acceleration followed by abrupt asynchronous stops by each axes reaching their limit
|
// by abrupt asynchronous stops by each axes reaching their limit switch independently. The
|
||||||
// switch independently. Instead of shoehorning homing cycles into the main stepper
|
// asynchronous stops are handled by a system level axis lock mask, which prevents the stepper
|
||||||
// algorithm and overcomplicate things, a stripped-down, lite version of the stepper
|
// algorithm from executing step pulses.
|
||||||
// algorithm is written here. This also lets users hack and tune this code freely for
|
|
||||||
// their own particular needs without affecting the rest of Grbl.
|
|
||||||
// NOTE: Only the abort runtime command can interrupt this process.
|
// NOTE: Only the abort runtime command can interrupt this process.
|
||||||
static void homing_cycle(uint8_t cycle_mask, bool pos_dir, bool invert_pin, float homing_rate)
|
void limits_go_home(uint8_t cycle_mask, bool approach, bool invert_pin, float homing_rate)
|
||||||
{
|
{
|
||||||
if (sys.execute & EXEC_RESET) { return; }
|
if (sys.execute & EXEC_RESET) { return; }
|
||||||
uint8_t limit_state;
|
uint8_t limit_state;
|
||||||
#ifndef LIMIT_SWITCHES_ACTIVE_HIGH
|
#ifndef LIMIT_SWITCHES_ACTIVE_HIGH
|
||||||
invert_pin = !invert_pin;
|
invert_pin = !invert_pin;
|
||||||
#endif
|
#endif
|
||||||
|
// Determine travel distance to the furthest homing switch based on user max travel settings.
|
||||||
|
float max_travel = settings.max_travel[X_AXIS];
|
||||||
|
if (max_travel < settings.max_travel[Y_AXIS]) { max_travel = settings.max_travel[Y_AXIS]; }
|
||||||
|
if (max_travel < settings.max_travel[Z_AXIS]) { max_travel = settings.max_travel[Z_AXIS]; }
|
||||||
|
max_travel *= 1.25; // Ensure homing switches engaged by over-estimating max travel.
|
||||||
|
if (approach) { max_travel = -max_travel; }
|
||||||
|
|
||||||
// Compute target location for homing all axes. Homing axis lock will freeze non-cycle axes.
|
// Set target location and rate for active axes.
|
||||||
float target[N_AXIS];
|
float target[N_AXIS];
|
||||||
target[X_AXIS] = settings.max_travel[X_AXIS];
|
uint8_t n_active_axis = 0;
|
||||||
if (target[X_AXIS] < settings.max_travel[Y_AXIS]) { target[X_AXIS] = settings.max_travel[Y_AXIS]; }
|
uint8_t i;
|
||||||
if (target[X_AXIS] < settings.max_travel[Z_AXIS]) { target[X_AXIS] = settings.max_travel[Z_AXIS]; }
|
for (i=0; i<N_AXIS; i++) {
|
||||||
target[X_AXIS] *= 2.0;
|
if (bit_istrue(cycle_mask,bit(i))) {
|
||||||
if (pos_dir) { target[X_AXIS] = -target[X_AXIS]; }
|
n_active_axis++;
|
||||||
target[Y_AXIS] = target[X_AXIS];
|
target[i] = max_travel;
|
||||||
target[Z_AXIS] = target[X_AXIS];
|
} else {
|
||||||
homing_rate *= 1.7320; // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
|
target[i] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bit_istrue(settings.homing_dir_mask,(1<<X_LIMIT_BIT))) { target[X_AXIS] = -target[X_AXIS]; }
|
||||||
|
if (bit_istrue(settings.homing_dir_mask,(1<<Y_LIMIT_BIT))) { target[Y_AXIS] = -target[Y_AXIS]; }
|
||||||
|
if (bit_istrue(settings.homing_dir_mask,(1<<Z_LIMIT_BIT))) { target[Z_AXIS] = -target[Z_AXIS]; }
|
||||||
|
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
|
||||||
|
|
||||||
// Setup homing axis locks based on cycle mask.
|
// Setup homing axis locks based on cycle mask.
|
||||||
uint8_t axislock = (STEPPING_MASK & ~STEP_MASK);
|
uint8_t axislock = (STEPPING_MASK & ~STEP_MASK);
|
||||||
@ -117,55 +165,27 @@ static void homing_cycle(uint8_t cycle_mask, bool pos_dir, bool invert_pin, floa
|
|||||||
plan_buffer_line(target, homing_rate, false); // Bypass mc_line(). Directly plan homing motion.
|
plan_buffer_line(target, homing_rate, false); // Bypass mc_line(). Directly plan homing motion.
|
||||||
st_prep_buffer(); // Prep first segment from newly planned block.
|
st_prep_buffer(); // Prep first segment from newly planned block.
|
||||||
st_wake_up(); // Initiate motion
|
st_wake_up(); // Initiate motion
|
||||||
while (STEP_MASK & axislock) {
|
do {
|
||||||
// Check limit state.
|
// Check limit state. Lock out cycle axes when they change.
|
||||||
limit_state = LIMIT_PIN;
|
limit_state = LIMIT_PIN;
|
||||||
if (invert_pin) { limit_state ^= LIMIT_MASK; }
|
if (invert_pin) { limit_state ^= LIMIT_MASK; }
|
||||||
if (axislock & (1<<X_STEP_BIT)) {
|
// if (axislock & (1<<X_STEP_BIT)) {
|
||||||
if (limit_state & (1<<X_LIMIT_BIT)) { axislock &= ~(1<<X_STEP_BIT); }
|
if (limit_state & (1<<X_LIMIT_BIT)) { axislock &= ~(1<<X_STEP_BIT); }
|
||||||
}
|
// }
|
||||||
if (axislock & (1<<Y_STEP_BIT)) {
|
// if (axislock & (1<<Y_STEP_BIT)) {
|
||||||
if (limit_state & (1<<Y_LIMIT_BIT)) { axislock &= ~(1<<Y_STEP_BIT); }
|
if (limit_state & (1<<Y_LIMIT_BIT)) { axislock &= ~(1<<Y_STEP_BIT); }
|
||||||
}
|
// }
|
||||||
if (axislock & (1<<Z_STEP_BIT)) {
|
// if (axislock & (1<<Z_STEP_BIT)) {
|
||||||
if (limit_state & (1<<Z_LIMIT_BIT)) { axislock &= ~(1<<Z_STEP_BIT); }
|
if (limit_state & (1<<Z_LIMIT_BIT)) { axislock &= ~(1<<Z_STEP_BIT); }
|
||||||
}
|
// }
|
||||||
sys.homing_axis_lock = axislock;
|
sys.homing_axis_lock = axislock;
|
||||||
st_prep_buffer(); // Check and prep one segment. NOTE: Should take no longer than 200us.
|
st_prep_buffer(); // Check and prep one segment. NOTE: Should take no longer than 200us.
|
||||||
if (sys.execute & EXEC_RESET) { return; }
|
if (sys.execute & EXEC_RESET) { return; }
|
||||||
}
|
} while (STEP_MASK & axislock);
|
||||||
st_go_idle(); // Disable steppers. Axes motion should already be locked.
|
st_go_idle(); // Disable steppers. Axes motion should already be locked.
|
||||||
plan_init(); // Reset planner buffer. Ensure homing motion is cleared.
|
plan_reset(); // Reset planner buffer. Ensure homing motion is cleared.
|
||||||
st_reset(); // Reset step segment buffer. Ensure homing motion is cleared.
|
st_reset(); // Reset step segment buffer. Ensure homing motion is cleared.
|
||||||
delay_ms(settings.homing_debounce_delay);
|
delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate.
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void limits_go_home()
|
|
||||||
{
|
|
||||||
plan_init(); // Reset planner buffer before beginning homing cycles.
|
|
||||||
|
|
||||||
// Search to engage all axes limit switches at faster homing seek rate.
|
|
||||||
homing_cycle(HOMING_SEARCH_CYCLE_0, true, false, settings.homing_seek_rate); // Search cycle 0
|
|
||||||
#ifdef HOMING_SEARCH_CYCLE_1
|
|
||||||
homing_cycle(HOMING_SEARCH_CYCLE_1, true, false, settings.homing_seek_rate); // Search cycle 1
|
|
||||||
#endif
|
|
||||||
#ifdef HOMING_SEARCH_CYCLE_2
|
|
||||||
homing_cycle(HOMING_SEARCH_CYCLE_2, true, false, settings.homing_seek_rate); // Search cycle 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Now in proximity of all limits. Carefully leave and approach switches in multiple cycles
|
|
||||||
// to precisely hone in on the machine zero location. Moves at slower homing feed rate.
|
|
||||||
int8_t n_cycle = N_HOMING_LOCATE_CYCLE;
|
|
||||||
while (n_cycle--) {
|
|
||||||
// Leave all switches to release them. After cycles complete, this is machine zero.
|
|
||||||
homing_cycle(HOMING_LOCATE_CYCLE, false, true, settings.homing_feed_rate);
|
|
||||||
|
|
||||||
if (n_cycle > 0) {
|
|
||||||
// Re-approach all switches to re-engage them.
|
|
||||||
homing_cycle(HOMING_LOCATE_CYCLE, true, false, settings.homing_feed_rate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
6
limits.h
6
limits.h
@ -25,8 +25,10 @@
|
|||||||
// Initialize the limits module
|
// Initialize the limits module
|
||||||
void limits_init();
|
void limits_init();
|
||||||
|
|
||||||
// Perform the homing cycle
|
void limits_disable();
|
||||||
void limits_go_home();
|
|
||||||
|
// Perform one portion of the homing cycle based on the input settings.
|
||||||
|
void limits_go_home(uint8_t cycle_mask, bool approach, bool invert_pin, float homing_rate);
|
||||||
|
|
||||||
// Check for soft limit violations
|
// Check for soft limit violations
|
||||||
void limits_soft_check(float *target);
|
void limits_soft_check(float *target);
|
||||||
|
28
main.c
28
main.c
@ -24,7 +24,6 @@
|
|||||||
as being a consistent sounding board for the future of accessible and free CNC. */
|
as being a consistent sounding board for the future of accessible and free CNC. */
|
||||||
|
|
||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
#include <avr/pgmspace.h>
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "planner.h"
|
#include "planner.h"
|
||||||
#include "nuts_bolts.h"
|
#include "nuts_bolts.h"
|
||||||
@ -52,7 +51,17 @@ int main(void)
|
|||||||
|
|
||||||
memset(&sys, 0, sizeof(sys)); // Clear all system variables
|
memset(&sys, 0, sizeof(sys)); // Clear all system variables
|
||||||
sys.abort = true; // Set abort to complete initialization
|
sys.abort = true; // Set abort to complete initialization
|
||||||
sys.state = STATE_INIT; // Set alarm state to indicate unknown initial position
|
|
||||||
|
// Check for power-up and set system alarm if homing is enabled to force homing cycle
|
||||||
|
// by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
|
||||||
|
// startup scripts, but allows access to settings and internal commands. Only a homing
|
||||||
|
// cycle '$H' or kill alarm locks '$X' will disable the alarm.
|
||||||
|
// NOTE: The startup script will run after successful completion of the homing cycle, but
|
||||||
|
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
|
||||||
|
// things uncontrollably. Very bad.
|
||||||
|
#ifdef HOMING_INIT_LOCK
|
||||||
|
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
|
||||||
|
#endif
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
|
|
||||||
@ -62,12 +71,12 @@ int main(void)
|
|||||||
if (sys.abort) {
|
if (sys.abort) {
|
||||||
// Reset system.
|
// Reset system.
|
||||||
serial_reset_read_buffer(); // Clear serial read buffer
|
serial_reset_read_buffer(); // Clear serial read buffer
|
||||||
plan_init(); // Clear block buffer and planner variables
|
|
||||||
gc_init(); // Set g-code parser to default state
|
gc_init(); // Set g-code parser to default state
|
||||||
protocol_init(); // Clear incoming line data and execute startup lines
|
protocol_init(); // Clear incoming line data and execute startup lines
|
||||||
spindle_init();
|
spindle_init();
|
||||||
coolant_init();
|
coolant_init();
|
||||||
limits_init();
|
limits_init();
|
||||||
|
plan_reset(); // Clear block buffer and planner variables
|
||||||
st_reset(); // Clear stepper subsystem variables.
|
st_reset(); // Clear stepper subsystem variables.
|
||||||
|
|
||||||
// Sync cleared gcode and planner positions to current system position, which is only
|
// Sync cleared gcode and planner positions to current system position, which is only
|
||||||
@ -80,23 +89,12 @@ int main(void)
|
|||||||
sys.execute = 0;
|
sys.execute = 0;
|
||||||
if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; }
|
if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; }
|
||||||
|
|
||||||
// Check for power-up and set system alarm if homing is enabled to force homing cycle
|
|
||||||
// by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
|
|
||||||
// startup scripts, but allows access to settings and internal commands. Only a homing
|
|
||||||
// cycle '$H' or kill alarm locks '$X' will disable the alarm.
|
|
||||||
// NOTE: The startup script will run after successful completion of the homing cycle, but
|
|
||||||
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
|
|
||||||
// things uncontrollably. Very bad.
|
|
||||||
#ifdef HOMING_INIT_LOCK
|
|
||||||
if (sys.state == STATE_INIT && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Check for and report alarm state after a reset, error, or an initial power up.
|
// Check for and report alarm state after a reset, error, or an initial power up.
|
||||||
if (sys.state == STATE_ALARM) {
|
if (sys.state == STATE_ALARM) {
|
||||||
report_feedback_message(MESSAGE_ALARM_LOCK);
|
report_feedback_message(MESSAGE_ALARM_LOCK);
|
||||||
} else {
|
} else {
|
||||||
// All systems go. Set system to ready and execute startup script.
|
// All systems go. Set system to ready and execute startup script.
|
||||||
sys.state = STATE_IDLE;
|
sys.state = STATE_IDLE; // Clear all state flags.
|
||||||
protocol_execute_startup();
|
protocol_execute_startup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,12 +213,38 @@ void mc_dwell(float seconds)
|
|||||||
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
|
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
|
||||||
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
|
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
|
||||||
// executing the homing cycle. This prevents incorrect buffered plans after homing.
|
// executing the homing cycle. This prevents incorrect buffered plans after homing.
|
||||||
void mc_go_home()
|
void mc_homing_cycle()
|
||||||
{
|
{
|
||||||
sys.state = STATE_HOMING; // Set system state variable
|
sys.state = STATE_HOMING; // Set system state variable
|
||||||
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable hard limits pin change register for cycle duration
|
limits_disable(); // Disable hard limits pin change register for cycle duration
|
||||||
|
plan_reset(); // Reset planner buffer before beginning homing routine.
|
||||||
|
st_reset(); // Reset step segment buffer before beginning homing routine.
|
||||||
|
|
||||||
limits_go_home(); // Perform homing routine.
|
// -------------------------------------------------------------------------------------
|
||||||
|
// Perform homing routine. NOTE: Special motion case. Only system reset works.
|
||||||
|
|
||||||
|
// Search to engage all axes limit switches at faster homing seek rate.
|
||||||
|
limits_go_home(HOMING_SEARCH_CYCLE_0, true, false, settings.homing_seek_rate); // Search cycle 0
|
||||||
|
#ifdef HOMING_SEARCH_CYCLE_1
|
||||||
|
limits_go_home(HOMING_SEARCH_CYCLE_1, true, false, settings.homing_seek_rate); // Search cycle 1
|
||||||
|
#endif
|
||||||
|
#ifdef HOMING_SEARCH_CYCLE_2
|
||||||
|
limits_go_home(HOMING_SEARCH_CYCLE_2, true, false, settings.homing_seek_rate); // Search cycle 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Now in proximity of all limits. Carefully leave and approach switches in multiple cycles
|
||||||
|
// to precisely hone in on the machine zero location. Moves at slower homing feed rate.
|
||||||
|
int8_t n_cycle = N_HOMING_LOCATE_CYCLE;
|
||||||
|
while (n_cycle--) {
|
||||||
|
// Leave all switches to release them. After cycles complete, this is machine zero.
|
||||||
|
limits_go_home(HOMING_LOCATE_CYCLE, false, true, settings.homing_feed_rate);
|
||||||
|
|
||||||
|
if (n_cycle > 0) {
|
||||||
|
// Re-approach all switches to re-engage them.
|
||||||
|
limits_go_home(HOMING_LOCATE_CYCLE, true, false, settings.homing_feed_rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
protocol_execute_runtime(); // Check for reset and set system abort.
|
protocol_execute_runtime(); // Check for reset and set system abort.
|
||||||
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
|
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
|
||||||
@ -254,12 +280,13 @@ void mc_go_home()
|
|||||||
mc_line(pulloff_target, settings.homing_seek_rate, false);
|
mc_line(pulloff_target, settings.homing_seek_rate, false);
|
||||||
st_cycle_start(); // Move it. Nothing should be in the buffer except this motion.
|
st_cycle_start(); // Move it. Nothing should be in the buffer except this motion.
|
||||||
plan_synchronize(); // Make sure the motion completes.
|
plan_synchronize(); // Make sure the motion completes.
|
||||||
|
// NOTE: Stepper idle lock resumes normal functionality after cycle.
|
||||||
|
|
||||||
// The gcode parser position circumvented by the pull-off maneuver, so sync position now.
|
// The gcode parser position circumvented by the pull-off maneuver, so sync position now.
|
||||||
gc_sync_position();
|
gc_sync_position();
|
||||||
|
|
||||||
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
|
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
|
||||||
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { LIMIT_PCMSK |= LIMIT_MASK; }
|
limits_init();
|
||||||
// Finished!
|
// Finished!
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,10 +321,9 @@ void mc_reset()
|
|||||||
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps
|
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps
|
||||||
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
|
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
|
||||||
// violated, by which, all bets are off.
|
// violated, by which, all bets are off.
|
||||||
switch (sys.state) {
|
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) {
|
||||||
case STATE_CYCLE: case STATE_HOLD: case STATE_HOMING: // case STATE_JOG:
|
sys.execute |= EXEC_ALARM; // Execute alarm state.
|
||||||
sys.execute |= EXEC_ALARM; // Execute alarm state.
|
st_go_idle(); // Execute alarm force kills steppers. Position likely lost.
|
||||||
st_go_idle(); // Execute alarm force kills steppers. Position likely lost.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
|
|||||||
void mc_dwell(float seconds);
|
void mc_dwell(float seconds);
|
||||||
|
|
||||||
// Perform homing cycle to locate machine zero. Requires limit switches.
|
// Perform homing cycle to locate machine zero. Requires limit switches.
|
||||||
void mc_go_home();
|
void mc_homing_cycle();
|
||||||
|
|
||||||
// Performs system reset. If in motion state, kills all motion and sets system alarm.
|
// Performs system reset. If in motion state, kills all motion and sets system alarm.
|
||||||
void mc_reset();
|
void mc_reset();
|
||||||
|
17
nuts_bolts.h
17
nuts_bolts.h
@ -74,15 +74,14 @@
|
|||||||
// Define system state bit map. The state variable primarily tracks the individual functions
|
// Define system state bit map. The state variable primarily tracks the individual functions
|
||||||
// of Grbl to manage each without overlapping. It is also used as a messaging flag for
|
// of Grbl to manage each without overlapping. It is also used as a messaging flag for
|
||||||
// critical events.
|
// critical events.
|
||||||
#define STATE_IDLE 0 // Must be zero.
|
#define STATE_IDLE 0 // Must be zero. No flags.
|
||||||
#define STATE_INIT 1 // Initial power up state.
|
#define STATE_QUEUED bit(0) // Indicates buffered blocks, awaiting cycle start.
|
||||||
#define STATE_QUEUED 2 // Indicates buffered blocks, awaiting cycle start.
|
#define STATE_CYCLE bit(1) // Cycle is running
|
||||||
#define STATE_CYCLE 3 // Cycle is running
|
#define STATE_HOLD bit(2) // Executing feed hold
|
||||||
#define STATE_HOLD 4 // Executing feed hold
|
#define STATE_HOMING bit(3) // Performing homing cycle
|
||||||
#define STATE_HOMING 5 // Performing homing cycle
|
#define STATE_ALARM bit(4) // In alarm state. Locks out all g-code processes. Allows settings access.
|
||||||
#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes. Allows settings access.
|
#define STATE_CHECK_MODE bit(5) // G-code check mode. Locks out planner and motion only.
|
||||||
#define STATE_CHECK_MODE 7 // G-code check mode. Locks out planner and motion only.
|
// #define STATE_JOG bit(6) // Jogging mode is unique like homing.
|
||||||
// #define STATE_JOG 8 // Jogging mode is unique like homing.
|
|
||||||
|
|
||||||
// Define global system variables
|
// Define global system variables
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -207,7 +207,7 @@ static void planner_recalculate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void plan_init()
|
void plan_reset()
|
||||||
{
|
{
|
||||||
memset(&pl, 0, sizeof(pl)); // Clear planner struct
|
memset(&pl, 0, sizeof(pl)); // Clear planner struct
|
||||||
block_buffer_tail = 0;
|
block_buffer_tail = 0;
|
||||||
@ -255,7 +255,7 @@ uint8_t plan_check_full_buffer()
|
|||||||
// during a synchronize call, if it should happen. Also, waits for clean cycle end.
|
// during a synchronize call, if it should happen. Also, waits for clean cycle end.
|
||||||
void plan_synchronize()
|
void plan_synchronize()
|
||||||
{
|
{
|
||||||
while (plan_get_current_block() || sys.state == STATE_CYCLE) {
|
while (plan_get_current_block() || (sys.state == STATE_CYCLE)) {
|
||||||
protocol_execute_runtime(); // Check and execute run-time commands
|
protocol_execute_runtime(); // Check and execute run-time commands
|
||||||
if (sys.abort) { return; } // Check for system abort
|
if (sys.abort) { return; } // Check for system abort
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,8 @@ typedef struct {
|
|||||||
// Fields used by the bresenham algorithm for tracing the line
|
// Fields used by the bresenham algorithm for tracing the line
|
||||||
// NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values.
|
// NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values.
|
||||||
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
|
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
|
||||||
int32_t steps[N_AXIS]; // Step count along each axis
|
uint32_t steps[N_AXIS]; // Step count along each axis
|
||||||
int32_t step_event_count; // The maximum step axis count and number of steps required to complete this block.
|
uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block.
|
||||||
|
|
||||||
// Fields used by the motion planner to manage acceleration
|
// Fields used by the motion planner to manage acceleration
|
||||||
float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2
|
float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2
|
||||||
@ -49,8 +49,8 @@ typedef struct {
|
|||||||
} plan_block_t;
|
} plan_block_t;
|
||||||
|
|
||||||
|
|
||||||
// Initialize the motion plan subsystem
|
// Initialize and reset the motion plan subsystem
|
||||||
void plan_init();
|
void plan_reset();
|
||||||
|
|
||||||
// Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
|
// Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
|
||||||
// in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
|
// in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
|
||||||
|
@ -235,8 +235,8 @@ uint8_t protocol_execute_line(char *line)
|
|||||||
case 'H' : // Perform homing cycle
|
case 'H' : // Perform homing cycle
|
||||||
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
|
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
|
||||||
// Only perform homing if Grbl is idle or lost.
|
// Only perform homing if Grbl is idle or lost.
|
||||||
if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) {
|
if ( sys.state == STATE_IDLE || sys.state == STATE_ALARM ) {
|
||||||
mc_go_home();
|
mc_homing_cycle();
|
||||||
if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing.
|
if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing.
|
||||||
} else { return(STATUS_IDLE_ERROR); }
|
} else { return(STATUS_IDLE_ERROR); }
|
||||||
} else { return(STATUS_SETTING_DISABLED); }
|
} else { return(STATUS_SETTING_DISABLED); }
|
||||||
|
1
report.c
1
report.c
@ -308,7 +308,6 @@ void report_realtime_status()
|
|||||||
// Report current machine state
|
// Report current machine state
|
||||||
switch (sys.state) {
|
switch (sys.state) {
|
||||||
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
|
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
|
||||||
// case STATE_INIT: printPgmString(PSTR("<Init")); break; // Never observed
|
|
||||||
case STATE_QUEUED: printPgmString(PSTR("<Queue")); break;
|
case STATE_QUEUED: printPgmString(PSTR("<Queue")); break;
|
||||||
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
|
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
|
||||||
case STATE_HOLD: printPgmString(PSTR("<Hold")); break;
|
case STATE_HOLD: printPgmString(PSTR("<Hold")); break;
|
||||||
|
366
stepper.c
366
stepper.c
@ -26,11 +26,9 @@
|
|||||||
#include "planner.h"
|
#include "planner.h"
|
||||||
#include "nuts_bolts.h"
|
#include "nuts_bolts.h"
|
||||||
|
|
||||||
|
|
||||||
// Some useful constants.
|
// Some useful constants.
|
||||||
#define TICKS_PER_MICROSECOND (F_CPU/1000000)
|
|
||||||
#define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) // min/segment
|
#define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) // min/segment
|
||||||
#define STEP_FTOL_MULTIPLIER 100000 // Multiplier converts floating point step rate to long
|
|
||||||
// integer for stepper algorithm step-distance counter.
|
|
||||||
|
|
||||||
#define RAMP_ACCEL 0
|
#define RAMP_ACCEL 0
|
||||||
#define RAMP_CRUISE 1
|
#define RAMP_CRUISE 1
|
||||||
@ -44,8 +42,8 @@
|
|||||||
// discarded when entirely consumed and completed by the segment buffer.
|
// discarded when entirely consumed and completed by the segment buffer.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t direction_bits;
|
uint8_t direction_bits;
|
||||||
int32_t steps[N_AXIS];
|
uint32_t steps[N_AXIS];
|
||||||
int32_t step_event_count;
|
uint32_t step_event_count;
|
||||||
} st_block_t;
|
} st_block_t;
|
||||||
static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1];
|
static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1];
|
||||||
// TODO: Directly adjust this parameters to stop motion of individual axes for the homing cycle.
|
// TODO: Directly adjust this parameters to stop motion of individual axes for the homing cycle.
|
||||||
@ -56,29 +54,30 @@ static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1];
|
|||||||
// planner buffer. Once "checked-out", the steps in the segments buffer cannot be modified by
|
// 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.
|
// the planner, where the remaining planner block steps still can.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t n_step; // Number of step events to be executed for this segment
|
uint16_t n_step; // Number of step events to be executed for this segment
|
||||||
uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
|
uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
|
||||||
int32_t phase_dist; // Remaining step fraction to tick before completing segment.
|
uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
|
||||||
int32_t dist_per_tick; // Step distance traveled per ISR tick, aka step rate.
|
uint8_t prescaler;
|
||||||
} segment_t;
|
} segment_t;
|
||||||
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
|
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
|
||||||
|
|
||||||
// Stepper state variable. Contains running data and trapezoid variables.
|
// Stepper state variable. Contains running data and trapezoid variables.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// Used by the bresenham line algorithm
|
// Used by the bresenham line algorithm
|
||||||
int32_t counter_x, // Counter variables for the bresenham line tracer
|
uint32_t counter_x, // Counter variables for the bresenham line tracer
|
||||||
counter_y,
|
counter_y,
|
||||||
counter_z;
|
counter_z;
|
||||||
|
|
||||||
// Used by inverse time algorithm to track step rate
|
#if STEP_PULSE_DELAY > 0
|
||||||
int32_t counter_dist; // Inverse time distance traveled since last step event
|
uint8_t step_bits; // Stores out_bits output to complete the step pulse delay
|
||||||
|
#endif
|
||||||
|
|
||||||
// Used by the stepper driver interrupt
|
// Used by the stepper driver interrupt
|
||||||
uint8_t execute_step; // Flags step execution for each 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 step_pulse_time; // Step pulse reset time after step rise
|
||||||
uint8_t out_bits; // The next stepping-bits to be output
|
uint8_t out_bits; // The next stepping-bits to be output
|
||||||
|
|
||||||
uint8_t step_count; // Steps remaining in line segment motion
|
uint16_t step_count; // Steps remaining in line segment motion
|
||||||
uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block.
|
uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block.
|
||||||
st_block_t *exec_block; // Pointer to the block data for the segment being executed
|
st_block_t *exec_block; // Pointer to the block data for the segment being executed
|
||||||
segment_t *exec_segment; // Pointer to the segment being executed
|
segment_t *exec_segment; // Pointer to the segment being executed
|
||||||
@ -106,6 +105,10 @@ typedef struct {
|
|||||||
|
|
||||||
float step_per_mm; // Current planner block step/millimeter conversion scalar
|
float step_per_mm; // Current planner block step/millimeter conversion scalar
|
||||||
float steps_remaining;
|
float steps_remaining;
|
||||||
|
float dt_remainder;
|
||||||
|
|
||||||
|
float mm_per_step;
|
||||||
|
float minimum_mm;
|
||||||
|
|
||||||
uint8_t ramp_type; // Current segment ramp state
|
uint8_t ramp_type; // Current segment ramp state
|
||||||
float mm_complete; // End of velocity profile from end of current planner block in (mm).
|
float mm_complete; // End of velocity profile from end of current planner block in (mm).
|
||||||
@ -166,16 +169,23 @@ void st_wake_up()
|
|||||||
} else {
|
} else {
|
||||||
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
|
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
|
||||||
}
|
}
|
||||||
if ((sys.state == STATE_CYCLE) || (sys.state == STATE_HOMING)){
|
if (sys.state & (STATE_CYCLE | STATE_HOMING)){
|
||||||
// Initialize stepper output bits
|
// Initialize stepper output bits
|
||||||
st.out_bits = settings.invert_mask;
|
st.out_bits = settings.invert_mask;
|
||||||
// Initialize step pulse timing from settings.
|
|
||||||
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
|
// Initialize step pulse timing from settings. Here to ensure updating after re-writing.
|
||||||
|
#ifdef STEP_PULSE_DELAY
|
||||||
|
// Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope.
|
||||||
|
st.step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
|
||||||
|
// Set delay between direction pin write and step command.
|
||||||
|
OCR2A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
|
||||||
|
#else // Normal operation
|
||||||
|
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
|
||||||
|
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Enable stepper driver interrupt
|
// Enable stepper driver interrupt
|
||||||
TCNT2 = 0; // Clear Timer2
|
TIMSK1 |= (1<<OCIE1A);
|
||||||
TIMSK2 |= (1<<OCIE2A); // Enable Timer2 Compare Match A interrupt
|
|
||||||
TCCR2B = (1<<CS21); // Begin Timer2. Full speed, 1/8 prescaler
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,12 +194,13 @@ void st_wake_up()
|
|||||||
void st_go_idle()
|
void st_go_idle()
|
||||||
{
|
{
|
||||||
// Disable stepper driver interrupt. Allow Timer0 to finish. It will disable itself.
|
// Disable stepper driver interrupt. Allow Timer0 to finish. It will disable itself.
|
||||||
TIMSK2 &= ~(1<<OCIE2A); // Disable Timer2 interrupt
|
TIMSK1 &= ~(1<<OCIE1A);
|
||||||
TCCR2B = 0; // Disable Timer2
|
// TIMSK2 &= ~(1<<OCIE2A); // Disable Timer2 interrupt
|
||||||
|
// TCCR2B = 0; // Disable Timer2
|
||||||
busy = false;
|
busy = false;
|
||||||
|
|
||||||
// Disable steppers only upon system alarm activated or by user setting to not be kept enabled.
|
// Disable steppers only upon system alarm activated or by user setting to not be kept enabled.
|
||||||
if ((settings.stepper_idle_lock_time != 0xff) || bit_istrue(sys.execute,EXEC_ALARM)) {
|
if (((settings.stepper_idle_lock_time != 0xff) || bit_istrue(sys.execute,EXEC_ALARM)) && sys.state != STATE_HOMING) {
|
||||||
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
|
// 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.
|
// stop and not drift from residual inertial forces at the end of the last movement.
|
||||||
delay_ms(settings.stepper_idle_lock_time);
|
delay_ms(settings.stepper_idle_lock_time);
|
||||||
@ -202,42 +213,48 @@ void st_go_idle()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. It is based
|
/* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. Grbl employs
|
||||||
on an inverse time stepper algorithm, where a timer ticks at a constant frequency and uses
|
the venerable Bresenham line algorithm to manage and exactly synchronize multi-axis moves.
|
||||||
time-distance counters to track when its the approximate time for a step event. For reference,
|
Unlike the popular DDA algorithm, the Bresenham algorithm is not susceptible to numerical
|
||||||
a similar inverse-time algorithm by Pramod Ranade is susceptible to numerical round-off, as
|
round-off errors and only requires fast integer counters, meaning low computational overhead
|
||||||
described, meaning that some axes steps may not execute correctly for a given multi-axis motion.
|
and maximizing the Arduino's capabilities. However, the downside of the Bresenham algorithm
|
||||||
Grbl's algorithm differs by using a single inverse time-distance counter to manage a
|
is, for certain multi-axis motions, the non-dominant axes may suffer from un-smooth step
|
||||||
Bresenham line algorithm for multi-axis step events, which ensures the number of steps for
|
pulse trains, which can lead to strange audible noises or shaking. This is particularly
|
||||||
each axis are executed exactly. In other words, Grbl uses a Bresenham within a Bresenham
|
noticeable or may cause motion issues at low step frequencies (<1kHz), but usually not at
|
||||||
algorithm, where one tracks time for step events and the other steps for multi-axis moves.
|
higher frequencies.
|
||||||
Grbl specifically uses the Bresenham algorithm due to its innate mathematical exactness and
|
This interrupt is simple and dumb by design. All the computational heavy-lifting, as in
|
||||||
low computational overhead, requiring simple integer +,- counters only.
|
determining accelerations, is performed elsewhere. This interrupt pops pre-computed segments,
|
||||||
This interrupt pops blocks from the step segment buffer and executes them by pulsing the
|
defined as constant velocity over n number of steps, from the step segment buffer and then
|
||||||
stepper pins appropriately. It is supported by The Stepper Port Reset Interrupt which it uses
|
executes them by pulsing the stepper pins appropriately via the Bresenham algorithm. This
|
||||||
to reset the stepper port after each pulse. The bresenham line tracer algorithm controls all
|
ISR is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port
|
||||||
three stepper outputs simultaneously with these two interrupts.
|
after each pulse. The bresenham line tracer algorithm controls all stepper outputs
|
||||||
|
simultaneously with these two interrupts.
|
||||||
|
|
||||||
NOTE: This interrupt must be as efficient as possible and complete before the next ISR tick,
|
NOTE: This interrupt must be as efficient as possible and complete before the next ISR tick,
|
||||||
which for Grbl is 33.3usec at a 30kHz ISR rate. Oscilloscope measured time in ISR is 5usec
|
which for Grbl must be less than 33.3usec (@30kHz ISR rate). Oscilloscope measured time in
|
||||||
typical and 25usec maximum, well below requirement.
|
ISR is 5usec typical and 25usec maximum, well below requirement.
|
||||||
*/
|
*/
|
||||||
// TODO: Replace direct updating of the int32 position counters in the ISR somehow. Perhaps use smaller
|
// TODO: Replace direct updating of the int32 position counters in the ISR somehow. Perhaps use smaller
|
||||||
// int8 variables and update position counters only when a segment completes. This can get complicated
|
// int8 variables and update position counters only when a segment completes. This can get complicated
|
||||||
// with probing and homing cycles that require true real-time positions.
|
// with probing and homing cycles that require true real-time positions.
|
||||||
ISR(TIMER2_COMPA_vect)
|
ISR(TIMER1_COMPA_vect)
|
||||||
{
|
{
|
||||||
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
|
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
|
||||||
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
|
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
|
||||||
|
|
||||||
// Pulse stepper port pins, if flagged. New block dir will always be set one timer tick
|
// Set the direction pins a couple of nanoseconds before we step the steppers
|
||||||
// before any step pulse due to algorithm design.
|
STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (st.out_bits & DIRECTION_MASK);
|
||||||
if (st.execute_step) {
|
// Then pulse the stepping pins
|
||||||
st.execute_step = false;
|
#ifdef STEP_PULSE_DELAY
|
||||||
STEPPING_PORT = ( STEPPING_PORT & ~(DIRECTION_MASK | STEP_MASK) ) | st.out_bits;
|
st.step_bits = (STEPPING_PORT & ~STEP_MASK) | st.out_bits; // Store out_bits to prevent overwriting.
|
||||||
TCNT0 = st.step_pulse_time; // Reload Timer0 counter.
|
#else // Normal operation
|
||||||
TCCR0B = (1<<CS21); // Begin Timer0. Full speed, 1/8 prescaler
|
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | st.out_bits;
|
||||||
}
|
#endif
|
||||||
|
|
||||||
|
// Enable step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after
|
||||||
|
// exactly settings.pulse_microseconds microseconds, independent of the main Timer1 prescaler.
|
||||||
|
TCNT2 = st.step_pulse_time; // Reload timer counter
|
||||||
|
TCCR2B = (1<<CS21); // Begin timer2. Full speed, 1/8 prescaler
|
||||||
|
|
||||||
busy = true;
|
busy = true;
|
||||||
sei(); // Re-enable interrupts to allow Stepper Port Reset Interrupt to fire on-time.
|
sei(); // Re-enable interrupts to allow Stepper Port Reset Interrupt to fire on-time.
|
||||||
@ -245,89 +262,72 @@ ISR(TIMER2_COMPA_vect)
|
|||||||
|
|
||||||
// If there is no step segment, attempt to pop one from the stepper buffer
|
// If there is no step segment, attempt to pop one from the stepper buffer
|
||||||
if (st.exec_segment == NULL) {
|
if (st.exec_segment == NULL) {
|
||||||
|
|
||||||
// Anything in the buffer? If so, load and initialize next step segment.
|
// Anything in the buffer? If so, load and initialize next step segment.
|
||||||
if (segment_buffer_head != segment_buffer_tail) {
|
if (segment_buffer_head != segment_buffer_tail) {
|
||||||
|
|
||||||
// Initialize new step segment and load number of steps to execute
|
// Initialize new step segment and load number of steps to execute
|
||||||
st.exec_segment = &segment_buffer[segment_buffer_tail];
|
st.exec_segment = &segment_buffer[segment_buffer_tail];
|
||||||
st.step_count = st.exec_segment->n_step;
|
// Initialize step segment timing per step and load number of steps to execute.
|
||||||
|
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (st.exec_segment->prescaler<<CS10);
|
||||||
|
OCR1A = st.exec_segment->cycles_per_tick;
|
||||||
|
st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow.
|
||||||
// If the new segment starts a new planner block, initialize stepper variables and counters.
|
// If the new segment starts a new planner block, initialize stepper variables and counters.
|
||||||
// NOTE: When the segment data index changes, this indicates a new planner block.
|
// NOTE: When the segment data index changes, this indicates a new planner block.
|
||||||
if ( st.exec_block_index != st.exec_segment->st_block_index ) {
|
if ( st.exec_block_index != st.exec_segment->st_block_index ) {
|
||||||
st.exec_block_index = st.exec_segment->st_block_index;
|
st.exec_block_index = st.exec_segment->st_block_index;
|
||||||
st.exec_block = &st_block_buffer[st.exec_block_index];
|
st.exec_block = &st_block_buffer[st.exec_block_index];
|
||||||
|
|
||||||
// Initialize direction bits for block. Set execute flag to set directions bits upon next ISR tick.
|
|
||||||
st.out_bits = st.exec_block->direction_bits ^ settings.invert_mask;
|
|
||||||
st.execute_step = true;
|
|
||||||
|
|
||||||
// Initialize Bresenham line and distance counters
|
// Initialize Bresenham line and distance counters
|
||||||
st.counter_x = (st.exec_block->step_event_count >> 1);
|
st.counter_x = (st.exec_block->step_event_count >> 1);
|
||||||
st.counter_y = st.counter_x;
|
st.counter_y = st.counter_x;
|
||||||
st.counter_z = st.counter_x;
|
st.counter_z = st.counter_x;
|
||||||
st.counter_dist = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Segment buffer empty. Shutdown.
|
// Segment buffer empty. Shutdown.
|
||||||
st_go_idle();
|
st_go_idle();
|
||||||
bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end
|
bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end
|
||||||
return; // Nothing to do but exit.
|
return; // Nothing to do but exit.
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate inverse time counter. Triggers each Bresenham step event.
|
// Reset out_bits and reload direction bits
|
||||||
st.counter_dist += st.exec_segment->dist_per_tick;
|
st.out_bits = st.exec_block->direction_bits;
|
||||||
|
|
||||||
// Execute Bresenham step event, when it's time to do so.
|
// Execute step displacement profile by Bresenham line algorithm
|
||||||
if (st.counter_dist > STEP_FTOL_MULTIPLIER) {
|
if (st.step_count > 0) {
|
||||||
if (st.step_count > 0) { // Block phase correction from executing step.
|
st.step_count--; // Decrement step events count
|
||||||
st.counter_dist -= STEP_FTOL_MULTIPLIER; // Reload inverse time counter
|
|
||||||
st.step_count--; // Decrement step events count
|
|
||||||
|
|
||||||
// Execute step displacement profile by Bresenham line algorithm
|
st.counter_x += st.exec_block->steps[X_AXIS];
|
||||||
st.execute_step = true;
|
if (st.counter_x > st.exec_block->step_event_count) {
|
||||||
st.out_bits = st.exec_block->direction_bits; // Reset out_bits and reload direction bits
|
st.out_bits |= (1<<X_STEP_BIT);
|
||||||
st.counter_x -= st.exec_block->steps[X_AXIS];
|
st.counter_x -= st.exec_block->step_event_count;
|
||||||
if (st.counter_x < 0) {
|
if (st.out_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
|
||||||
st.out_bits |= (1<<X_STEP_BIT);
|
else { sys.position[X_AXIS]++; }
|
||||||
st.counter_x += st.exec_block->step_event_count;
|
|
||||||
if (st.out_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
|
|
||||||
else { sys.position[X_AXIS]++; }
|
|
||||||
}
|
|
||||||
st.counter_y -= st.exec_block->steps[Y_AXIS];
|
|
||||||
if (st.counter_y < 0) {
|
|
||||||
st.out_bits |= (1<<Y_STEP_BIT);
|
|
||||||
st.counter_y += st.exec_block->step_event_count;
|
|
||||||
if (st.out_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
|
|
||||||
else { sys.position[Y_AXIS]++; }
|
|
||||||
}
|
|
||||||
st.counter_z -= st.exec_block->steps[Z_AXIS];
|
|
||||||
if (st.counter_z < 0) {
|
|
||||||
st.out_bits |= (1<<Z_STEP_BIT);
|
|
||||||
st.counter_z += st.exec_block->step_event_count;
|
|
||||||
if (st.out_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
|
|
||||||
else { sys.position[Z_AXIS]++; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block any axis with limit switch active from moving.
|
|
||||||
if (sys.state == STATE_HOMING) { st.out_bits &= sys.homing_axis_lock; }
|
|
||||||
|
|
||||||
st.out_bits ^= settings.invert_mask; // Apply step port invert mask
|
|
||||||
}
|
}
|
||||||
|
st.counter_y += st.exec_block->steps[Y_AXIS];
|
||||||
|
if (st.counter_y > st.exec_block->step_event_count) {
|
||||||
|
st.out_bits |= (1<<Y_STEP_BIT);
|
||||||
|
st.counter_y -= st.exec_block->step_event_count;
|
||||||
|
if (st.out_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
|
||||||
|
else { sys.position[Y_AXIS]++; }
|
||||||
|
}
|
||||||
|
st.counter_z += st.exec_block->steps[Z_AXIS];
|
||||||
|
if (st.counter_z > st.exec_block->step_event_count) {
|
||||||
|
st.out_bits |= (1<<Z_STEP_BIT);
|
||||||
|
st.counter_z -= st.exec_block->step_event_count;
|
||||||
|
if (st.out_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
|
||||||
|
else { sys.position[Z_AXIS]++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// During a homing cycle, lock out and prevent desired axes from moving.
|
||||||
|
if (sys.state == STATE_HOMING) { st.out_bits &= sys.homing_axis_lock; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.step_count == 0) {
|
if (st.step_count == 0) {
|
||||||
if (st.counter_dist > st.exec_segment->phase_dist) {
|
// Segment is complete. Discard current segment and advance segment indexing.
|
||||||
// Segment is complete. Discard current segment and advance segment indexing.
|
st.exec_segment = NULL;
|
||||||
st.exec_segment = NULL;
|
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
|
||||||
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
st.out_bits ^= settings.invert_mask; // Apply step port invert mask
|
||||||
busy = false;
|
busy = false;
|
||||||
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT;
|
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT;
|
||||||
}
|
}
|
||||||
@ -336,13 +336,40 @@ ISR(TIMER2_COMPA_vect)
|
|||||||
/* The Stepper Port Reset Interrupt: Timer0 OVF interrupt handles the falling edge of the step
|
/* 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
|
pulse. This should always trigger before the next Timer2 COMPA interrupt and independently
|
||||||
finish, if Timer2 is disabled after completing a move. */
|
finish, if Timer2 is disabled after completing a move. */
|
||||||
ISR(TIMER0_OVF_vect)
|
// ISR(TIMER0_OVF_vect)
|
||||||
|
// {
|
||||||
|
// STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK);
|
||||||
|
// TCCR0B = 0; // Disable timer until needed.
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// This interrupt is set up by ISR_TIMER1_COMPAREA when it sets the motor port bits. It resets
|
||||||
|
// the motor port after a short period (settings.pulse_microseconds) completing one step cycle.
|
||||||
|
// NOTE: Interrupt collisions between the serial and stepper interrupts can cause delays by
|
||||||
|
// a few microseconds, if they execute right before one another. Not a big deal, but can
|
||||||
|
// cause issues at high step rates if another high frequency asynchronous interrupt is
|
||||||
|
// added to Grbl.
|
||||||
|
ISR(TIMER2_OVF_vect)
|
||||||
{
|
{
|
||||||
|
// Reset stepping pins (leave the direction pins)
|
||||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK);
|
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK);
|
||||||
TCCR0B = 0; // Disable timer until needed.
|
TCCR2B = 0; // Disable Timer2 to prevent re-entering this interrupt when it's not needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef STEP_PULSE_DELAY
|
||||||
|
// This interrupt is used only when STEP_PULSE_DELAY is enabled. Here, the step pulse is
|
||||||
|
// initiated after the STEP_PULSE_DELAY time period has elapsed. The ISR TIMER2_OVF interrupt
|
||||||
|
// will then trigger after the appropriate settings.pulse_microseconds, as in normal operation.
|
||||||
|
// The new timing between direction, step pulse, and step complete events are setup in the
|
||||||
|
// st_wake_up() routine.
|
||||||
|
ISR(TIMER2_COMPA_vect)
|
||||||
|
{
|
||||||
|
STEPPING_PORT = st.step_bits; // Begin step pulse.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Reset and clear stepper subsystem variables
|
// Reset and clear stepper subsystem variables
|
||||||
void st_reset()
|
void st_reset()
|
||||||
{
|
{
|
||||||
@ -366,18 +393,22 @@ void st_init()
|
|||||||
STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask;
|
STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask;
|
||||||
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
|
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
|
||||||
|
|
||||||
// Configure Timer 2
|
// Configure Timer 1
|
||||||
TIMSK2 &= ~(1<<OCIE2A); // Disable Timer2 interrupt while configuring it
|
TCCR1B &= ~(1<<WGM13); // waveform generation = 0100 = CTC
|
||||||
TCCR2B = 0; // Disable Timer2 until needed
|
TCCR1B |= (1<<WGM12);
|
||||||
TCNT2 = 0; // Clear Timer2 counter
|
TCCR1A &= ~(1<<WGM11);
|
||||||
TCCR2A = (1<<WGM21); // Set CTC mode
|
TCCR1A &= ~(1<<WGM10);
|
||||||
OCR2A = (F_CPU/ISR_TICKS_PER_SECOND)/8 - 1; // Set Timer2 CTC rate
|
TCCR1A &= ~(3<<COM1A0); // output mode = 00 (disconnected)
|
||||||
|
TCCR1A &= ~(3<<COM1B0);
|
||||||
|
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (1<<CS10);
|
||||||
|
|
||||||
// Configure Timer 0
|
// Configure Timer 2
|
||||||
TIMSK0 &= ~(1<<TOIE0);
|
TCCR2A = 0; // Normal operation
|
||||||
TCCR0A = 0; // Normal operation
|
TCCR2B = 0; // Disable timer until needed.
|
||||||
TCCR0B = 0; // Disable Timer0 until needed
|
TIMSK2 |= (1<<TOIE2); // Enable Timer2 Overflow interrupt
|
||||||
TIMSK0 |= (1<<TOIE0); // Enable overflow interrupt
|
#ifdef STEP_PULSE_DELAY
|
||||||
|
TIMSK2 |= (1<<OCIE2A); // Enable Timer2 Compare Match A interrupt
|
||||||
|
#endif
|
||||||
|
|
||||||
// Start in the idle state, but first wake up to check for keep steppers enabled option.
|
// Start in the idle state, but first wake up to check for keep steppers enabled option.
|
||||||
st_wake_up();
|
st_wake_up();
|
||||||
@ -479,15 +510,17 @@ void st_prep_buffer()
|
|||||||
// Prepare and copy Bresenham algorithm segment data from the new planner block, so that
|
// Prepare and copy Bresenham algorithm segment data from the new planner block, so that
|
||||||
// when the segment buffer completes the planner block, it may be discarded immediately.
|
// when the segment buffer completes the planner block, it may be discarded immediately.
|
||||||
st_prep_block = &st_block_buffer[prep.st_block_index];
|
st_prep_block = &st_block_buffer[prep.st_block_index];
|
||||||
|
st_prep_block->direction_bits = pl_block->direction_bits;
|
||||||
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS];
|
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS];
|
||||||
st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS];
|
st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS];
|
||||||
st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS];
|
st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS];
|
||||||
st_prep_block->direction_bits = pl_block->direction_bits;
|
|
||||||
st_prep_block->step_event_count = pl_block->step_event_count;
|
st_prep_block->step_event_count = pl_block->step_event_count;
|
||||||
|
|
||||||
// Initialize segment buffer data for generating the segments.
|
// Initialize segment buffer data for generating the segments.
|
||||||
prep.steps_remaining = st_prep_block->step_event_count;
|
prep.steps_remaining = pl_block->step_event_count;
|
||||||
prep.step_per_mm = prep.steps_remaining/pl_block->millimeters;
|
prep.step_per_mm = prep.steps_remaining/pl_block->millimeters;
|
||||||
|
prep.mm_per_step = pl_block->millimeters/prep.steps_remaining;
|
||||||
|
prep.minimum_mm = pl_block->millimeters-prep.mm_per_step;
|
||||||
|
|
||||||
if (sys.state == STATE_HOLD) {
|
if (sys.state == STATE_HOLD) {
|
||||||
// Override planner block entry speed and enforce deceleration during feed hold.
|
// Override planner block entry speed and enforce deceleration during feed hold.
|
||||||
@ -495,6 +528,8 @@ void st_prep_buffer()
|
|||||||
pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed;
|
pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed;
|
||||||
}
|
}
|
||||||
else { prep.current_speed = sqrt(pl_block->entry_speed_sqr); }
|
else { prep.current_speed = sqrt(pl_block->entry_speed_sqr); }
|
||||||
|
|
||||||
|
prep.dt_remainder = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------------------
|
/* ---------------------------------------------------------------------------------
|
||||||
@ -575,17 +610,18 @@ void st_prep_buffer()
|
|||||||
the end of planner block (typical) or mid-block at the end of a forced deceleration,
|
the end of planner block (typical) or mid-block at the end of a forced deceleration,
|
||||||
such as from a feed hold.
|
such as from a feed hold.
|
||||||
*/
|
*/
|
||||||
|
float dt_max = DT_SEGMENT;
|
||||||
float dt = 0.0;
|
float dt = 0.0;
|
||||||
float mm_remaining = pl_block->millimeters;
|
float mm_remaining = pl_block->millimeters;
|
||||||
float time_var = DT_SEGMENT; // Time worker variable
|
float time_var = dt_max; // Time worker variable
|
||||||
float mm_var; // mm-Distance worker variable
|
float mm_var; // mm-Distance worker variable
|
||||||
float speed_var; // Speed work variable.
|
float speed_var; // Speed worker variable.
|
||||||
do {
|
do {
|
||||||
switch (prep.ramp_type) {
|
switch (prep.ramp_type) {
|
||||||
case RAMP_ACCEL:
|
case RAMP_ACCEL:
|
||||||
// NOTE: Acceleration ramp only computes during first do-while loop.
|
// NOTE: Acceleration ramp only computes during first do-while loop.
|
||||||
speed_var = pl_block->acceleration*DT_SEGMENT;
|
speed_var = pl_block->acceleration*dt_max;
|
||||||
mm_remaining -= DT_SEGMENT*(prep.current_speed + 0.5*speed_var);
|
mm_remaining -= dt_max*(prep.current_speed + 0.5*speed_var);
|
||||||
if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp.
|
if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp.
|
||||||
// Acceleration-cruise, acceleration-deceleration ramp junction, or end of block.
|
// Acceleration-cruise, acceleration-deceleration ramp junction, or end of block.
|
||||||
mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB
|
mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB
|
||||||
@ -618,15 +654,18 @@ void st_prep_buffer()
|
|||||||
if (mm_var > prep.mm_complete) { // Deceleration only.
|
if (mm_var > prep.mm_complete) { // Deceleration only.
|
||||||
mm_remaining = mm_var;
|
mm_remaining = mm_var;
|
||||||
prep.current_speed -= speed_var;
|
prep.current_speed -= speed_var;
|
||||||
break; // Segment complete. Exit switch-case statement.
|
break; // Segment complete. Exit switch-case statement. Continue do-while loop.
|
||||||
}
|
}
|
||||||
} // End of block or end of forced-deceleration.
|
} // End of block or end of forced-deceleration.
|
||||||
time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed);
|
time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed);
|
||||||
mm_remaining = prep.mm_complete;
|
mm_remaining = prep.mm_complete;
|
||||||
}
|
}
|
||||||
dt += time_var; // Add computed ramp time to total segment time.
|
dt += time_var; // Add computed ramp time to total segment time.
|
||||||
if (dt < DT_SEGMENT) { time_var = DT_SEGMENT - dt; } // **Incomplete** At ramp junction.
|
if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction.
|
||||||
else { break; } // **Complete** Exit loop. Segment execution time maxed.
|
else if (mm_remaining > prep.minimum_mm) { // Check for slow segments with zero steps.
|
||||||
|
dt_max += DT_SEGMENT; // Increase segment time to ensure at least one step in segment.
|
||||||
|
time_var = dt_max - dt;
|
||||||
|
} else { break; } // **Complete** Exit loop. Segment execution time maxed.
|
||||||
} while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete.
|
} while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete.
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------------
|
||||||
@ -639,48 +678,69 @@ void st_prep_buffer()
|
|||||||
Fortunately, this scenario is highly unlikely and unrealistic in CNC machines
|
Fortunately, this scenario is highly unlikely and unrealistic in CNC machines
|
||||||
supported by Grbl (i.e. exceeding 10 meters axis travel at 200 step/mm).
|
supported by Grbl (i.e. exceeding 10 meters axis travel at 200 step/mm).
|
||||||
*/
|
*/
|
||||||
// Use time_var to pre-compute dt inversion with integer multiplier.
|
uint32_t cycles;
|
||||||
time_var = (STEP_FTOL_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))/dt; // (ftol_mult/isr_tic)
|
float steps_remaining = prep.step_per_mm*mm_remaining;
|
||||||
if (mm_remaining > 0.0) { // Block still incomplete. Distance remaining to be executed.
|
|
||||||
float steps_remaining = prep.step_per_mm*mm_remaining;
|
|
||||||
prep_segment->dist_per_tick = ceil( (prep.steps_remaining-steps_remaining)*time_var ); // (ftol_mult*step/isr_tic)
|
|
||||||
|
|
||||||
// Compute number of steps to execute and segment step phase correction.
|
float inv_rate = dt/(prep.steps_remaining-steps_remaining);
|
||||||
prep_segment->phase_dist = ceil(STEP_FTOL_MULTIPLIER*(ceil(steps_remaining)-steps_remaining));
|
cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (ftol_mult*step/isr_tic)
|
||||||
prep_segment->n_step = ceil(prep.steps_remaining)-ceil(steps_remaining);
|
|
||||||
|
|
||||||
// Update step execution variables.
|
// Compute number of steps to execute and segment step phase correction.
|
||||||
if (mm_remaining == prep.mm_complete) {
|
prep_segment->n_step = ceil(prep.steps_remaining)-ceil(steps_remaining);
|
||||||
|
|
||||||
|
// Determine end of segment conditions. Setup initial conditions for next segment.
|
||||||
|
if (mm_remaining > prep.mm_complete) {
|
||||||
|
// Normal operation. Block incomplete. Distance remaining to be executed.
|
||||||
|
prep.minimum_mm = prep.mm_per_step*floor(steps_remaining);
|
||||||
|
pl_block->millimeters = mm_remaining;
|
||||||
|
prep.steps_remaining = steps_remaining;
|
||||||
|
} else {
|
||||||
|
// End of planner block or forced-termination. No more distance to be executed.
|
||||||
|
if (mm_remaining > 0.0) { // At end of forced-termination.
|
||||||
// NOTE: Currently only feed holds qualify for this scenario. May change with overrides.
|
// NOTE: Currently only feed holds qualify for this scenario. May change with overrides.
|
||||||
prep.current_speed = 0.0;
|
prep.current_speed = 0.0;
|
||||||
prep.steps_remaining = ceil(steps_remaining);
|
prep.steps_remaining = ceil(steps_remaining);
|
||||||
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
|
prep.minimum_mm = prep.steps_remaining-prep.mm_per_step;
|
||||||
|
pl_block->millimeters = prep.steps_remaining*prep.mm_per_step; // Update with full steps.
|
||||||
plan_cycle_reinitialize();
|
plan_cycle_reinitialize();
|
||||||
sys.state = STATE_QUEUED; // End cycle.
|
sys.state = STATE_QUEUED; // End cycle.
|
||||||
} else {
|
} else { // End of planner block
|
||||||
pl_block->millimeters = mm_remaining;
|
// The planner block is complete. All steps are set to be executed in the segment buffer.
|
||||||
prep.steps_remaining = steps_remaining;
|
pl_block = NULL;
|
||||||
}
|
plan_discard_current_block();
|
||||||
|
|
||||||
} else { // End of block.
|
if (sys.state == STATE_HOLD) {
|
||||||
// Set to execute the remaining steps and no phase correction upon finishing the block.
|
if (prep.current_speed == 0.0) {
|
||||||
prep_segment->dist_per_tick = ceil( prep.steps_remaining*time_var ); // (mult*step/isr_tic)
|
// TODO: Check if the segment buffer gets initialized correctly.
|
||||||
prep_segment->phase_dist = 0;
|
plan_cycle_reinitialize();
|
||||||
prep_segment->n_step = ceil(prep.steps_remaining);
|
sys.state = STATE_QUEUED;
|
||||||
|
}
|
||||||
// The planner block is complete. All steps are set to be executed in the segment buffer.
|
|
||||||
// TODO: Broken with feed holds. Need to recalculate the planner buffer at this time.
|
|
||||||
pl_block = NULL;
|
|
||||||
plan_discard_current_block();
|
|
||||||
|
|
||||||
if (sys.state == STATE_HOLD) {
|
|
||||||
if (prep.current_speed == 0.0) {
|
|
||||||
plan_cycle_reinitialize();
|
|
||||||
sys.state = STATE_QUEUED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Compile-time checks to what prescalers need to be compiled in.
|
||||||
|
if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
|
||||||
|
prep_segment->prescaler = 1; // prescaler: 0
|
||||||
|
prep_segment->cycles_per_tick = cycles;
|
||||||
|
} else {
|
||||||
|
prep_segment->prescaler = 2; // prescaler: 8
|
||||||
|
if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
|
||||||
|
prep_segment->cycles_per_tick = cycles >> 3;
|
||||||
|
// } else if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
|
||||||
|
// prep_segment->prescaler = 3; // prescaler: 64
|
||||||
|
// prep_segment->cycles_per_tick = cycles >> 6;
|
||||||
|
// } else if (cycles < (1UL << 24)) { // < 16777216 (1.05sec@16MHz)
|
||||||
|
// prep_segment->prescaler = 4; // prescaler: 256
|
||||||
|
// prep_segment->cycles_per_tick = (cycles >> 8);
|
||||||
|
// } else {
|
||||||
|
// prep_segment->prescaler = 5; // prescaler: 1024
|
||||||
|
// if (cycles < (1UL << 26)) { // < 67108864 (4.19sec@16MHz)
|
||||||
|
// prep_segment->cycles_per_tick = (cycles >> 10);
|
||||||
|
} else { // Just set the slowest speed possible.
|
||||||
|
prep_segment->cycles_per_tick = 0xffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// New step segment initialization completed. Increment segment buffer indices.
|
// New step segment initialization completed. Increment segment buffer indices.
|
||||||
segment_buffer_head = segment_next_head;
|
segment_buffer_head = segment_next_head;
|
||||||
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
|
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
|
||||||
@ -689,7 +749,7 @@ void st_prep_buffer()
|
|||||||
// if (blength < 0) { blength += SEGMENT_BUFFER_SIZE; }
|
// if (blength < 0) { blength += SEGMENT_BUFFER_SIZE; }
|
||||||
// printInteger(blength);
|
// printInteger(blength);
|
||||||
|
|
||||||
if ((sys.state == STATE_HOMING) || (sys.state == STATE_QUEUED)) { return; } // Force only one prepped segment.
|
if (sys.state & (STATE_QUEUED | STATE_HOMING)) { return; } // Force exit or one prepped segment.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,10 +22,8 @@
|
|||||||
#ifndef stepper_h
|
#ifndef stepper_h
|
||||||
#define stepper_h
|
#define stepper_h
|
||||||
|
|
||||||
#include <avr/io.h>
|
|
||||||
|
|
||||||
#ifndef SEGMENT_BUFFER_SIZE
|
#ifndef SEGMENT_BUFFER_SIZE
|
||||||
#define SEGMENT_BUFFER_SIZE 7
|
#define SEGMENT_BUFFER_SIZE 6
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Initialize and setup the stepper motor subsystem
|
// Initialize and setup the stepper motor subsystem
|
||||||
|
Loading…
Reference in New Issue
Block a user