diff --git a/config.h b/config.h index d1587fa..3791b31 100644 --- a/config.h +++ b/config.h @@ -210,7 +210,7 @@ // available RAM, like when re-compiling for a Mega or Sanguino. Or decrease if the Arduino // begins to crash due to the lack of available RAM or if the CPU is having trouble keeping // up with planning new incoming motions as they are executed. -// #define BLOCK_BUFFER_SIZE 18 // Uncomment to override default in planner.h. +// #define BLOCK_BUFFER_SIZE 17 // Uncomment to override default in planner.h. // 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 @@ -220,7 +220,7 @@ // can be too small and g-code blocks can get truncated. Officially, the g-code standards // support up to 256 characters. In future versions, this default will be increased, when // we know how much extra memory space we can re-invest into this. -// #define LINE_BUFFER_SIZE 50 // Uncomment to override default in protocol.h +// #define LINE_BUFFER_SIZE 70 // Uncomment to override default in protocol.h // Serial send and receive buffer size. The receive buffer is often used as another streaming // buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming diff --git a/gcode.c b/gcode.c index b17a34a..d87ce52 100644 --- a/gcode.c +++ b/gcode.c @@ -245,7 +245,8 @@ uint8_t gc_execute_line(char *line) // If there were any errors parsing this line, we will return right away with the bad news if (gc.status_code) { return(gc.status_code); } - + uint8_t i; + /* Execute Commands: Perform by order of execution defined in NIST RS274-NGC.v3, Table 8, pg.41. */ // ([F]: Set feed rate.) @@ -290,7 +291,6 @@ uint8_t gc_execute_line(char *line) else { int_value = gc.coord_select; } // Index P0 as the active coordinate system float coord_data[N_AXIS]; if (!settings_read_coord_data(int_value,coord_data)) { return(STATUS_SETTING_READ_FAIL); } - uint8_t i; // Update axes defined only in block. Always in machine coordinates. Can change non-active system. for (i=0; i= LINE_BUFFER_SIZE-1) { - // Throw away any characters beyond the end of the line buffer + // Detect line buffer overflow. Report error and reset line buffer. + report_status_message(STATUS_OVERFLOW); + protocol_reset_line_buffer(); } else if (c >= 'a' && c <= 'z') { // Upcase lowercase line[char_counter++] = c-'a'+'A'; } else { diff --git a/protocol.h b/protocol.h index 209d19d..4d90c1c 100644 --- a/protocol.h +++ b/protocol.h @@ -30,7 +30,7 @@ // memory space we can invest into here or we re-write the g-code parser not to have his // buffer. #ifndef LINE_BUFFER_SIZE - #define LINE_BUFFER_SIZE 50 + #define LINE_BUFFER_SIZE 70 #endif // Initialize the serial protocol diff --git a/report.c b/report.c index c115dc7..8c90a17 100644 --- a/report.c +++ b/report.c @@ -76,6 +76,8 @@ void report_status_message(uint8_t status_code) printPgmString(PSTR("Alarm lock")); break; case STATUS_SOFT_LIMIT_ERROR: printPgmString(PSTR("Homing not enabled")); break; + case STATUS_OVERFLOW: + printPgmString(PSTR("Line overflow")); break; } printPgmString(PSTR("\r\n")); } diff --git a/report.h b/report.h index cd7f42d..7dcfc47 100644 --- a/report.h +++ b/report.h @@ -36,6 +36,7 @@ #define STATUS_IDLE_ERROR 11 #define STATUS_ALARM_LOCK 12 #define STATUS_SOFT_LIMIT_ERROR 13 +#define STATUS_OVERFLOW 14 // Define Grbl alarm codes. Less than zero to distinguish alarm error from status error. #define ALARM_LIMIT_ERROR -1 diff --git a/stepper.c b/stepper.c index cac20f3..acb3282 100644 --- a/stepper.c +++ b/stepper.c @@ -78,6 +78,7 @@ static volatile uint8_t busy; // True when "Stepper Driver Interrupt" is being // 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. + // 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() @@ -101,6 +102,7 @@ void st_wake_up() } } + // Stepper shutdown void st_go_idle() { @@ -297,6 +299,7 @@ ISR(TIMER2_COMPA_vect) // SPINDLE_ENABLE_PORT ^= 1<. +*/ + +#include +#include "stepper.h" +#include "config.h" +#include "settings.h" +#include "planner.h" + +// Some useful constants +#define TICKS_PER_MICROSECOND (F_CPU/1000000) +#define CRUISE_RAMP 0 +#define ACCEL_RAMP 1 +#define DECEL_RAMP 2 + +// 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; + uint32_t event_count; // Total event count. Retained for feed holds. + uint32_t step_events_remaining; // Steps remaining in motion + + // Used by Pramod Ranade inverse time algorithm + int32_t delta_d; // Ranade distance traveled per interrupt tick + int32_t d_counter; // Ranade distance traveled since last step event + uint8_t ramp_count; // Acceleration interrupt tick counter. + uint8_t ramp_type; // Ramp type variable. + uint8_t execute_step; // Flags step execution for each interrupt. + +} stepper_t; +static stepper_t st; +static block_t *current_block; // A pointer to the block currently being traced + +// Used by the stepper driver interrupt +static uint8_t step_pulse_time; // Step pulse reset time after step rise +static uint8_t out_bits; // The next stepping-bits to be output + +// NOTE: If the main interrupt is guaranteed to be complete before the next interrupt, then +// this blocking variable is no longer needed. Only here for safety reasons. +static volatile uint8_t busy; // True when "Stepper Driver Interrupt" is being serviced. Used to avoid retriggering that handler. + +// __________________________ +// /| |\ _________________ ^ +// / | | \ /| |\ | +// / | | \ / | | \ 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. + + +// 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() +{ + // Enable steppers by resetting the stepper disable port + if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { + STEPPERS_DISABLE_PORT |= (1<> 3); + // Enable stepper driver interrupt + st.execute_step = false; + TCNT0 = 0; // Clear Timer2 + TIMSK0 |= (1<d_next; + + // Load next step + out_bits = current_block->direction_bits; // Reset out_bits and reload direction bits + st.execute_step = true; + + // Execute step displacement profile by Bresenham line algorithm + st.counter_x -= current_block->steps_x; + if (st.counter_x < 0) { + out_bits |= (1<steps_y; + if (st.counter_y < 0) { + out_bits |= (1<steps_z; + if (st.counter_z < 0) { + out_bits |= (1<direction_bits ^ settings.invert_mask; + st.execute_step = true; // Set flag to set direction bits. + + // Initialize Bresenham variables + st.counter_x = (current_block->step_event_count >> 1); + st.counter_y = st.counter_x; + st.counter_z = st.counter_x; + st.event_count = current_block->step_event_count; + st.step_events_remaining = st.event_count; + + // During feed hold, do not update inverse time counter, rate, or ramp type. Keep decelerating. + if (sys.state == STATE_CYCLE) { + // Initialize Ranade variables + st.d_counter = current_block->d_next; + st.delta_d = current_block->initial_rate; + st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK/2; + + // Initialize ramp type. + if (st.step_events_remaining == current_block->decelerate_after) { st.ramp_type = DECEL_RAMP; } + else if (st.delta_d == current_block->nominal_rate) { st.ramp_type = CRUISE_RAMP; } + else { st.ramp_type = ACCEL_RAMP; } + } + + } else { + st_go_idle(); + bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end + return; // Nothing to do but exit. + } + } + + // Adjust inverse time counter for ac/de-celerations + if (st.ramp_type) { + // Tick acceleration ramp counter + st.ramp_count--; + if (st.ramp_count == 0) { + st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK; // Reload ramp counter + if (st.ramp_type == ACCEL_RAMP) { // Adjust velocity for acceleration + st.delta_d += current_block->rate_delta; + if (st.delta_d >= current_block->nominal_rate) { // Reached cruise state. + st.ramp_type = CRUISE_RAMP; + st.delta_d = current_block->nominal_rate; // Set cruise velocity + } + } else if (st.ramp_type == DECEL_RAMP) { // Adjust velocity for deceleration + if (st.delta_d > current_block->rate_delta) { + st.delta_d -= current_block->rate_delta; + } else { + st.delta_d >>= 1; // Integer divide by 2 until complete. Also prevents overflow. + } + } + } + } + + + // Check for feed hold state and execute accordingly. + if (sys.state == STATE_HOLD) { + if (st.ramp_type != DECEL_RAMP) { + st.ramp_type = DECEL_RAMP; + st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK/2; + } + if (st.delta_d <= current_block->rate_delta) { + st_go_idle(); + bit_true(sys.execute,EXEC_CYCLE_STOP); + return; + } + } + + if (st.ramp_type != DECEL_RAMP) { + // Acceleration and cruise handled by ramping. Just check for deceleration. + if (st.step_events_remaining <= current_block->decelerate_after) { + st.ramp_type = DECEL_RAMP; + if (st.step_events_remaining == current_block->decelerate_after) { + if (st.delta_d == current_block->nominal_rate) { + st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK/2; // Set ramp counter for trapezoid + } else { + st.ramp_count = ISR_TICKS_PER_ACCELERATION_TICK-st.ramp_count; // Set ramp counter for triangle + } + } + } + } + } else { + + busy = false; +} + + +// The Stepper Port Reset Interrupt: Timer2 OVF interrupt handles the falling edge of the +// step pulse. This should always trigger before the next Timer0 COMPA interrupt and independently +// finish, if Timer0 is disabled after completing a move. +ISR(TIMER2_OVF_vect) +{ + STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK); + TCCR2B = 0; // Disable timer until needed. +} + + +// Reset and clear stepper subsystem variables +void st_reset() +{ + memset(&st, 0, sizeof(st)); + current_block = NULL; + busy = false; +} + + +// Initialize and start the stepper motor subsystem +void st_init() +{ + // Configure directions of interface pins + STEPPING_DDR |= STEPPING_MASK; + STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask; + STEPPERS_DISABLE_DDR |= 1<