diff --git a/config.h b/config.h index 56f4b9d..ac68acb 100644 --- a/config.h +++ b/config.h @@ -27,37 +27,38 @@ #define BAUD_RATE 9600 // Define pin-assignments -#define STEPPERS_DISABLE_DDR DDRB -#define STEPPERS_DISABLE_PORT PORTB -#define STEPPERS_DISABLE_BIT 0 - #define STEPPING_DDR DDRD #define STEPPING_PORT PORTD -#define X_STEP_BIT 2 -#define Y_STEP_BIT 3 -#define Z_STEP_BIT 4 -#define X_DIRECTION_BIT 5 -#define Y_DIRECTION_BIT 6 -#define Z_DIRECTION_BIT 7 +#define X_STEP_BIT 2 // Uno Digital Pin 2 +#define Y_STEP_BIT 3 // Uno Digital Pin 3 +#define Z_STEP_BIT 4 // Uno Digital Pin 4 +#define X_DIRECTION_BIT 5 // Uno Digital Pin 5 +#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6 +#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7 -#define LIMIT_DDR DDRB +#define STEPPERS_DISABLE_DDR DDRB +#define STEPPERS_DISABLE_PORT PORTB +#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8 + +#define LIMIT_DDR DDRB #define LIMIT_PIN PINB -#define X_LIMIT_BIT 1 -#define Y_LIMIT_BIT 2 -#define Z_LIMIT_BIT 3 +#define X_LIMIT_BIT 1 // Uno Digital Pin 9 +#define Y_LIMIT_BIT 2 // Uno Digital Pin 10 +#define Z_LIMIT_BIT 3 // Uno Digital Pin 11 #define SPINDLE_ENABLE_DDR DDRB #define SPINDLE_ENABLE_PORT PORTB -#define SPINDLE_ENABLE_BIT 4 +#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12 #define SPINDLE_DIRECTION_DDR DDRB #define SPINDLE_DIRECTION_PORT PORTB -#define SPINDLE_DIRECTION_BIT 5 +#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 // Define runtime command special characters. These characters are 'picked-off' directly from the // serial read data stream and are not passed to the grbl line execution parser. Select characters // that do not and must not exist in the streamed g-code program. ASCII control characters may be -// used, if they are available per user setup. +// used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in +// g-code programs, maybe selected for interface programs. // TODO: Solidify these default characters. Temporary for now. #define CMD_STATUS_REPORT '?' #define CMD_FEED_HOLD '!' @@ -70,8 +71,8 @@ // entering g-code into grbl, i.e. locating part zero or simple manual machining. If the axes drift, // grbl has no way to know this has happened, since stepper motors are open-loop control. Depending // on the machine, this parameter may need to be larger or smaller than the default time. -// NOTE: If defined 0, the delay will not be compiled. -#define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer >= 0 +// NOTE: If commented out, the delay will not be compiled. +#define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer > 0 // The temporal resolution of the acceleration management subsystem. Higher number give smoother // acceleration but may impact performance. @@ -109,4 +110,20 @@ // time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays. #define DWELL_TIME_STEP 50 // Integer (milliseconds) + +// ----------------------------------------------- + +// TODO: The following options are set as compile-time options for now, until the next EEPROM +// settings version has solidified. +#define CYCLE_AUTO_START 1 // Cycle auto-start boolean flag for the planner. +#define BLOCK_DELETE_ENABLE 0 // Block delete enable/disable flag during g-code parsing +#define REPORT_INCH_MODE 0 // Status reporting unit mode (1 = inch, 0 = mm) +#if REPORT_INCH_MODE + #define DECIMAL_PLACES 3 + #define DECIMAL_MULTIPLIER 1000 // 10^DECIMAL_PLACES +#else + #define DECIMAL_PLACES 2 // mm-mode + #define DECIMAL_MULTIPLIER 100 +#endif + #endif diff --git a/gcode.c b/gcode.c index 1936015..b9ca1f4 100644 --- a/gcode.c +++ b/gcode.c @@ -32,8 +32,6 @@ #include "errno.h" #include "protocol.h" -#define MM_PER_INCH (25.4) - #define NEXT_ACTION_DEFAULT 0 #define NEXT_ACTION_DWELL 1 #define NEXT_ACTION_GO_HOME 2 diff --git a/main.c b/main.c index a1b0b1d..17732ac 100644 --- a/main.c +++ b/main.c @@ -4,8 +4,7 @@ Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2011 Sungeun K. Jeon - Copyright (c) 2011 Jens Geisler - + Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -20,11 +19,8 @@ along with Grbl. If not, see . */ -#include -#include #include #include -#include #include "config.h" #include "planner.h" #include "nuts_bolts.h" @@ -34,15 +30,11 @@ #include "gcode.h" #include "protocol.h" #include "limits.h" - #include "settings.h" #include "serial.h" -#include "print.h" - -// Declare system global variables -uint8_t sys_abort; // Global system abort flag -volatile uint8_t sys_state; // Global system state variable +// Declare system global variable structure +system_t sys; int main(void) { @@ -50,17 +42,18 @@ int main(void) sei(); // Enable interrupts serial_init(BAUD_RATE); // Setup serial baud rate and interrupts st_init(); // Setup stepper pins and interrupt timers - sys_abort = true; // Set abort to complete initialization + + sys.abort = true; // Set abort to complete initialization while(1) { - // Upon a system abort, the main program will return to this loop. Once here, it is safe to - // re-initialize the system. Upon startup, the system will automatically reset to finish the - // initialization process. - if (sys_abort) { - // Execute system reset - sys_state = 0; // Reset system state - sys_abort = false; // Release system abort + // Execute system reset upon a system abort, where the main program will return to this loop. + // Once here, it is safe to re-initialize the system. At startup, the system will automatically + // reset to finish the initialization process. + if (sys.abort) { + + // Clear all system variables + memset(&sys, 0, sizeof(sys)); // Reset system. serial_reset_read_buffer(); // Clear serial read buffer @@ -70,19 +63,14 @@ int main(void) gc_init(); // Set g-code parser to default state spindle_init(); limits_init(); - - // TODO: For now, the stepper subsystem tracks the absolute stepper position from the point - // of power up or hard reset. This reset is a soft reset, where the information of the current - // position is not lost after a system abort. This is not guaranteed to be correct, since - // during an abort, the steppers can lose steps in the immediate stop. However, if a feed - // hold is performed before a system abort, this position should be correct. In the next few - // updates, this soft reset feature will be fleshed out along with the status reporting and - // jogging features. - st_reset(); // Clear stepper subsystem variables. Machine position variable is not reset. - - // Print grbl initialization message - printPgmString(PSTR("\r\nGrbl " GRBL_VERSION)); - printPgmString(PSTR("\r\n'$' to dump current settings\r\n")); + st_reset(); // Clear stepper subsystem variables. + + // Set system runtime defaults + // TODO: Eventual move to EEPROM from config.h when all of the new settings are worked out. + // Mainly to avoid having to maintain several different versions. + #ifdef CYCLE_AUTO_START + sys.auto_start = true; + #endif } protocol_execute_runtime(); diff --git a/motion_control.c b/motion_control.c index a5985c4..12c929e 100644 --- a/motion_control.c +++ b/motion_control.c @@ -33,8 +33,6 @@ #include "limits.h" #include "protocol.h" -#include "print.h" - // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // (1 minute)/feed_rate time. @@ -56,17 +54,19 @@ void mc_line(double x, double y, double z, double feed_rate, uint8_t invert_feed // Remain in this loop until there is room in the buffer. do { protocol_execute_runtime(); // Check for any run-time commands - if (sys_abort) { return; } // Bail, if system abort. + if (sys.abort) { return; } // Bail, if system abort. } while ( plan_check_full_buffer() ); plan_buffer_line(x, y, z, feed_rate, invert_feed_rate); - // Auto-cycle start. - // TODO: Determine a more efficient and robust way of implementing the auto-starting the cycle. - // For example, only auto-starting when the buffer is full; if there was only one g-code command - // sent during manual operation; or if there is buffer starvation, making sure it minimizes any - // dwelling/motion hiccups. Additionally, these situations must not auto-start during a feed hold. - // Only the cycle start runtime command should be able to restart the cycle after a feed hold. - st_cycle_start(); + // Auto-cycle start immediately after planner finishes. Enabled/disabled by grbl settings. During + // a feed hold, auto-start is disabled momentarily until the cycle is resumed by the cycle-start + // runtime command. + // NOTE: This is allows the user to decide to exclusively use the cycle start runtime command to + // begin motion or let grbl auto-start it for them. This is useful when: manually cycle-starting + // when the buffer is completely full and primed; auto-starting, if there was only one g-code + // command sent during manual operation; or if a system is prone to buffer starvation, auto-start + // helps make sure it minimizes any dwelling/motion hiccups and keeps the cycle going. + if (sys.auto_start) { st_cycle_start(); } } @@ -167,7 +167,7 @@ void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, ui mc_line(arc_target[X_AXIS], arc_target[Y_AXIS], arc_target[Z_AXIS], feed_rate, invert_feed_rate); // Bail mid-circle on system abort. Runtime command check already performed by mc_line. - if (sys_abort) { return; } + if (sys.abort) { return; } } // Ensure last segment arrives at target location. mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, invert_feed_rate); @@ -183,7 +183,7 @@ void mc_dwell(double seconds) while (i > 0) { // NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds. protocol_execute_runtime(); - if (sys_abort) { return; } + if (sys.abort) { return; } _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment i--; } diff --git a/nuts_bolts.h b/nuts_bolts.h index 665de25..edbb96c 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -32,27 +32,50 @@ #define Y_AXIS 1 #define Z_AXIS 2 +#define MM_PER_INCH (25.4) + +// Useful macros #define clear_vector(a) memset(a, 0, sizeof(a)) #define clear_vector_double(a) memset(a, 0.0, sizeof(a)) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) -// Define system state bit map. Used internally by runtime protocol as runtime command flags. -// NOTE: The system state is an unsigned 8-bit volatile variable and has a 8 flag limit. The default -// flags are always false, so the runtime protocol only needs to check for a non-zero state value to +// Bit field and masking macros +#define bit(n) (1 << n) +#define bit_true(x,mask) (x |= mask) +#define bit_false(x,mask) (x &= ~mask) +#define bit_toggle(x,mask) (x ^= mask) +#define bit_istrue(x,mask) ((x & mask) != 0) +#define bit_isfalse(x,mask) ((x & mask) == 0) + +// Define system executor bit map. Used internally by runtime protocol as runtime command flags, +// which notifies the main program to execute the specified runtime command asynchronously. +// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default +// flags are always false, so the runtime protocol only needs to check for a non-zero value to // know when there is a runtime command to execute. -#define BIT_STATUS_REPORT 1 // bit 00000001 -#define BIT_CYCLE_START 2 // bit 00000010 -#define BIT_FEED_HOLD 4 // bit 00000100 -#define BIT_RESET 8 // bit 00001000 -#define BIT_REPLAN_CYCLE 16 // bit 00010000 -// #define 32 // bit 00100000 -// #define 64 // bit 01000000 -// #define 128 // bit 10000000 +#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001 +#define EXEC_CYCLE_START bit(1) // bitmask 00000010 +#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100 +#define EXEC_FEED_HOLD bit(3) // bitmask 00001000 +#define EXEC_RESET bit(4) // bitmask 00010000 +// #define bit(5) // bitmask 00100000 +// #define bit(6) // bitmask 01000000 +// #define bit(7) // bitmask 10000000 // Define global system variables -extern uint8_t sys_abort; // Global system abort flag -extern volatile uint8_t sys_state; // Global system state variable +typedef struct { + uint8_t abort; // System abort flag. Forces exit back to main loop for reset. + uint8_t feed_hold; // Feed hold flag. Held true during feed hold. Released when ready to resume. + uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings. + + int32_t position[3]; // Real-time machine position vector in steps. This may need to be a volatile + // variable, if problems arise. Subject to change. Need to add coordinate offset + // functionality to correctly track part zero and machine zero. + + volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program. + volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. +} system_t; +extern system_t sys; // Read a floating point value from a string. Line points to the input buffer, char_counter // is the indexer pointing to the current character of the line, while double_ptr is diff --git a/planner.c b/planner.c index aeba0cd..772e168 100644 --- a/planner.c +++ b/planner.c @@ -34,7 +34,7 @@ #include "protocol.h" // The number of linear motions that can be in the plan at any give time -#define BLOCK_BUFFER_SIZE 20 +#define BLOCK_BUFFER_SIZE 18 static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions static volatile uint8_t block_buffer_head; // Index of the next block to be pushed @@ -336,7 +336,7 @@ void plan_synchronize() { while(plan_get_current_block()) { 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 } } diff --git a/planner.h b/planner.h index fc692b5..451fdf9 100644 --- a/planner.h +++ b/planner.h @@ -35,7 +35,7 @@ typedef struct { // Fields used by the motion planner to manage acceleration double nominal_speed; // The nominal speed for this block in mm/min - double entry_speed; // Entry speed at previous-current junction in mm/min + double entry_speed; // Entry speed at previous-current block junction in mm/min double max_entry_speed; // Maximum allowable junction entry speed in mm/min double millimeters; // The total travel of this block in mm uint8_t recalculate_flag; // Planner flag to recalculate trapezoids on entry junction diff --git a/print.c b/print.c index 05524a3..f509e88 100644 --- a/print.c +++ b/print.c @@ -3,6 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,72 +25,107 @@ #include #include +#include "config.h" #include "serial.h" -#ifndef DECIMAL_PLACES -#define DECIMAL_PLACES 3 -#endif - void printString(const char *s) { - while (*s) - serial_write(*s++); + while (*s) + serial_write(*s++); } // Print a string stored in PGM-memory void printPgmString(const char *s) { char c; - while ((c = pgm_read_byte_near(s++))) - serial_write(c); + while ((c = pgm_read_byte_near(s++))) + serial_write(c); } -void printIntegerInBase(unsigned long n, unsigned long base) +// void printIntegerInBase(unsigned long n, unsigned long base) +// { +// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. +// unsigned long i = 0; +// +// if (n == 0) { +// serial_write('0'); +// return; +// } +// +// while (n > 0) { +// buf[i++] = n % base; +// n /= base; +// } +// +// for (; i > 0; i--) +// serial_write(buf[i - 1] < 10 ? +// '0' + buf[i - 1] : +// 'A' + buf[i - 1] - 10); +// } + +void print_uint8_base2(uint8_t n) { - unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. - unsigned long i = 0; + unsigned char buf[8]; + uint8_t i = 0; - if (n == 0) { - serial_write('0'); - return; - } - - while (n > 0) { - buf[i++] = n % base; - n /= base; + for (; i < 8; i++) { + buf[i] = n & 1; + n >>= 1; } for (; i > 0; i--) - serial_write(buf[i - 1] < 10 ? - '0' + buf[i - 1] : - 'A' + buf[i - 1] - 10); + serial_write('0' + buf[i - 1]); +} + +static void print_uint32_base10(unsigned long n) +{ + unsigned char buf[32]; + uint8_t i = 0; + + if (n == 0) { + serial_write('0'); + return; + } + + while (n > 0) { + buf[i++] = n % 10; + n /= 10; + } + + for (; i > 0; i--) + serial_write('0' + buf[i - 1]); } void printInteger(long n) { - if (n < 0) { - serial_write('-'); - n = -n; - } - - printIntegerInBase(n, 10); + if (n < 0) { + serial_write('-'); + n = -n; + } + print_uint32_base10(n); } -// A very simple void printFloat(double n) { - double integer_part, fractional_part; - uint8_t decimal_part; - fractional_part = modf(n, &integer_part); - printInteger(integer_part); + if (n < 0) { + serial_write('-'); + n = -n; + } + n += 0.5/DECIMAL_MULTIPLIER; // Add rounding factor + + long integer_part; + integer_part = (int)n; + print_uint32_base10(integer_part); + serial_write('.'); - fractional_part *= 10; + + n -= integer_part; int decimals = DECIMAL_PLACES; + uint8_t decimal_part; while(decimals-- > 0) { - decimal_part = floor(fractional_part); + n *= 10; + decimal_part = (int) n; serial_write('0'+decimal_part); - fractional_part -= decimal_part; - fractional_part *= 10; + n -= decimal_part; } } - diff --git a/print.h b/print.h index b2581ba..ff4ea99 100644 --- a/print.h +++ b/print.h @@ -3,6 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,7 +31,7 @@ void printPgmString(const char *s); void printInteger(long n); -void printIntegerInBase(unsigned long n, unsigned long base); +void print_uint8_base2(uint8_t n); void printFloat(double n); diff --git a/protocol.c b/protocol.c index da31da8..7b812f3 100644 --- a/protocol.c +++ b/protocol.c @@ -69,15 +69,39 @@ void protocol_status_report() // may be distance to go on block, processed block id, and feed rate. A secondary, non-critical // status report may include g-code state, i.e. inch mode, plane mode, absolute mode, etc. // The report generated must be as short as possible, yet still provide the user easily readable - // information, i.e. 'x0.23 y120.4 z2.4'. This is necessary as it minimizes the computational + // information, i.e. 'x0.23,y120.4,z2.4'. This is necessary as it minimizes the computational // overhead and allows grbl to keep running smoothly, especially with g-code programs with fast, - // short line segments and interface setups that require real-time status reports (10-20Hz). - printString("Query Received.\r\n"); // Notify that it's working. + // short line segments and interface setups that require real-time status reports (5-20Hz). + // Additionally, during an abort, the steppers are immediately stopped regardless of what they + // are doing. If they are moving, the abort stop can cause grbl to lose steps. However, if a feed + // hold is performed before a system abort, the steppers will steadily decelerate at the max + // acceleration rate, hence the stopped machine position will be maintained and correct. + + + // Bare-bones status report. Provides real-time machine position relative to the initialization + // or system reset location (0,0,0), not a home position. This section is under construction and + // the following are needed: coordinate offsets/updating of machine position relative to home, work + // coordinate position?, user setting of output units (mm|inch), compressed (non-human readable) + // data for interfaces?, save last known position in EEPROM? + #if REPORT_INCH_MODE + printString("x"); printFloat(sys.position[X_AXIS]/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); + printString(",y"); printFloat(sys.position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); + printString(",z"); printFloat(sys.position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); + #else + printString("x"); printFloat(sys.position[X_AXIS]/(settings.steps_per_mm[X_AXIS])); + printString(",y"); printFloat(sys.position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS])); + printString(",z"); printFloat(sys.position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS])); + #endif + printString("\r\n"); } void protocol_init() { + // Print grbl initialization message + printPgmString(PSTR("\r\nGrbl " GRBL_VERSION)); + printPgmString(PSTR("\r\n'$' to dump current settings\r\n")); + char_counter = 0; // Reset line input iscomment = false; } @@ -87,40 +111,46 @@ void protocol_init() // program, primarily where there may be a while loop waiting for a buffer to clear space or any // point where the execution time from the last check point may be more than a fraction of a second. // This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code -// parsing and planning functions. -// NOTE: The sys_state variable flags are set by the serial read subprogram, except where noted. +// parsing and planning functions. This function also serves as an interface for the interrupts to +// set the system runtime flags, where only the main program to handles them, removing the need to +// define more computationally-expensive volatile variables. +// NOTE: The sys.execute variable flags are set by the serial read subprogram, except where noted. void protocol_execute_runtime() { - if (sys_state) { // Enter only if any bit flag is enabled + if (sys.execute) { // Enter only if any bit flag is true + uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times // System abort. Steppers have already been force stopped. - if (sys_state & BIT_RESET) { - sys_abort = true; + if (rt_exec & EXEC_RESET) { + sys.abort = true; return; // Nothing else to do but exit. } // Execute and serial print status - if (sys_state & BIT_STATUS_REPORT) { + if (rt_exec & EXEC_STATUS_REPORT) { + bit_false(sys.execute,EXEC_STATUS_REPORT); protocol_status_report(); - sys_state ^= BIT_STATUS_REPORT; // Toggle off } // Initiate stepper feed hold - if (sys_state & BIT_FEED_HOLD) { - st_feed_hold(); - sys_state ^= BIT_FEED_HOLD; // Toggle off + if (rt_exec & EXEC_FEED_HOLD) { + st_feed_hold(); // Initiate feed hold. + bit_false(sys.execute,EXEC_FEED_HOLD); } - // Re-plans the buffer after a feed hold completes - // NOTE: BIT_REPLAN_CYCLE is set by the stepper subsystem when the feed hold is complete. - if (sys_state & BIT_REPLAN_CYCLE) { + // Reinitializes the stepper module running flags and re-plans the buffer after a feed hold. + // NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes. + if (rt_exec & EXEC_CYCLE_STOP) { st_cycle_reinitialize(); - sys_state ^= BIT_REPLAN_CYCLE; // Toggle off + bit_false(sys.execute,EXEC_CYCLE_STOP); } - if (sys_state & BIT_CYCLE_START) { + if (rt_exec & EXEC_CYCLE_START) { st_cycle_start(); // Issue cycle start command to stepper subsystem - sys_state ^= BIT_CYCLE_START; // Toggle off + #ifdef CYCLE_AUTO_START + sys.auto_start = true; // Re-enable auto start after feed hold. + #endif + bit_false(sys.execute,EXEC_CYCLE_START); } } } @@ -130,6 +160,15 @@ void protocol_execute_runtime() uint8_t protocol_execute_line(char *line) { if(line[0] == '$') { + + // TODO: Re-write this '$' as a way to change runtime settings without having to reset, i.e. + // auto-starting, status query output formatting and type, jog mode (axes, direction, and + // nominal feedrate), toggle block delete, etc. This differs from the EEPROM settings, as they + // are considered defaults and loaded upon startup/reset. + // This use is envisioned where '$' itself dumps settings and help. Defined characters + // proceeding the '$' may be used to setup modes, such as jog mode with a '$J=X100' for X-axis + // motion with a nominal feedrate of 100mm/min. Writing EEPROM settings will likely stay the + // same or similar. Should be worked out in upcoming releases. return(settings_execute_line(line)); // Delegate lines starting with '$' to the settings module // } else if { @@ -165,7 +204,7 @@ void protocol_process() // NOTE: If there is no line, this function should quickly return to the main program when // the buffer empties of non-executable data. protocol_execute_runtime(); - if (sys_abort) { return; } // Bail to main program upon system abort + if (sys.abort) { return; } // Bail to main program upon system abort if (char_counter > 0) {// Line is complete. Then execute! line[char_counter] = 0; // Terminate string @@ -176,6 +215,7 @@ void protocol_process() } char_counter = 0; // Reset line buffer index iscomment = false; // Reset comment flag + } else { if (iscomment) { // Throw away all comment characters @@ -187,9 +227,10 @@ void protocol_process() if (c <= ' ') { // Throw away whitepace and control characters } else if (c == '/') { - // Disable block delete and throw away character - // To enable block delete, uncomment following line. Will ignore until EOL. - // iscomment = true; + // Disable block delete and throw away characters. Will ignore until EOL. + #if BLOCK_DELETE_ENABLE + iscomment = true; + #endif } else if (c == '(') { // Enable comments flag and ignore all characters until ')' or EOL. iscomment = true; diff --git a/script/console b/script/Obsolete/console similarity index 100% rename from script/console rename to script/Obsolete/console diff --git a/script/proxy b/script/Obsolete/proxy similarity index 100% rename from script/proxy rename to script/Obsolete/proxy diff --git a/script/stream b/script/Obsolete/stream similarity index 100% rename from script/stream rename to script/Obsolete/stream diff --git a/script/stream.rb b/script/Obsolete/stream.rb similarity index 100% rename from script/stream.rb rename to script/Obsolete/stream.rb diff --git a/script/trapezoid_simulator.rb b/script/Obsolete/trapezoid_simulator.rb similarity index 100% rename from script/trapezoid_simulator.rb rename to script/Obsolete/trapezoid_simulator.rb diff --git a/script/simple_stream.py b/script/simple_stream.py index 8bd4b3b..5119db4 100755 --- a/script/simple_stream.py +++ b/script/simple_stream.py @@ -1,6 +1,15 @@ #!/usr/bin/env python """\ Simple g-code streaming script for grbl + +Provided as an illustration of the basic communication interface +for grbl. When grbl has finished parsing the g-code block, it will +return an 'ok' or 'error' response. When the planner buffer is full, +grbl will not send a response until the planner buffer clears space. + +G02/03 arcs are special exceptions, where they inject short line +segments directly into the planner. So there may not be a response +from grbl for the duration of the arc. """ import serial diff --git a/script/stream.py b/script/stream.py new file mode 100755 index 0000000..32971ff --- /dev/null +++ b/script/stream.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +"""\ +Stream g-code to grbl controller + +This script differs from the simple_stream.py script by +tracking the number of characters in grbl's serial read +buffer. This allows grbl to fetch the next line directly +from the serial buffer and does not have to wait for a +response from the computer. This effectively adds another +buffer layer to prevent buffer starvation. + +TODO: - Add runtime command capabilities + +Version: SKJ.20120104 +""" + +import serial +import re +import time +import sys +import argparse + +RX_BUFFER_SIZE = 128 + +# Define command line argument interface +parser = argparse.ArgumentParser(description='Stream g-code file to grbl. (pySerial library required)') +parser.add_argument('gcode', type=argparse.FileType('r'), + help='g-code filename to be streamed') +parser.add_argument('device', + help='serial device path') +parser.add_argument('-q','--quiet',action='store_true', default=False, + help='suppress output text') +args = parser.parse_args() + +# Initialize +s = serial.Serial(args.device_file,9600) +f = args.gcode_file +verbose = True +if args.quiet : verbose = False + +# Wake up grbl +print "Initializing grbl..." +s.write("\r\n\r\n") + +# Wait for grbl to initialize and flush startup text in serial input +time.sleep(2) +s.flushInput() + +# Stream g-code to grbl +print "Streaming ", args.gcode_file.name, " to ", args.device_file +l_count = 0 +g_count = 0 +c_line = [] +for line in f: + l_count += 1 # Iterate line counter +# l_block = re.sub('\s|\(.*?\)','',line).upper() # Strip comments/spaces/new line and capitalize + l_block = line.strip() + c_line.append(len(l_block)) # Track number of characters in grbl serial read buffer + grbl_out = '' + while sum(c_line) >= RX_BUFFER_SIZE-1 | s.inWaiting() : + out_temp = s.readline().strip() # Wait for grbl response + if out_temp not in ['ok','error'] : + print " Debug: ",out_temp # Debug response + else : + grbl_out += out_temp; + g_count += 1 # Iterate g-code counter + grbl_out += str(g_count); # Add line finished indicator + del c_line[0] + if verbose: print "SND: " + str(l_count) + " : " + l_block, + s.write(l_block + '\n') # Send block to grbl + if verbose : print "BUF:",str(sum(c_line)),"REC:",grbl_out + +# Wait for user input after streaming is completed +print "G-code streaming finished!\n" +print "WARNING: Wait until grbl completes buffered g-code blocks before exiting." +raw_input(" Press to exit and disable grbl.") + +# Close file and serial port +f.close() +s.close() \ No newline at end of file diff --git a/serial.c b/serial.c index 8aa9419..87aab50 100644 --- a/serial.c +++ b/serial.c @@ -31,7 +31,7 @@ #include "protocol.h" #define RX_BUFFER_SIZE 128 -#define TX_BUFFER_SIZE 32 +#define TX_BUFFER_SIZE 64 uint8_t rx_buffer[RX_BUFFER_SIZE]; uint8_t rx_buffer_head = 0; @@ -72,7 +72,7 @@ void serial_write(uint8_t data) { // Wait until there is space in the buffer while (next_head == tx_buffer_tail) { protocol_execute_runtime(); // Check for any run-time commands - if (sys_abort) { return; } // Bail, if system abort. + if (sys.abort) { return; } // Bail, if system abort. } // Store data and advance head @@ -124,15 +124,15 @@ ISR(USART_RX_vect) // Pick off runtime command characters directly from the serial stream. These characters are // not passed into the buffer, but these set system state flag bits for runtime execution. switch (data) { - case CMD_STATUS_REPORT: sys_state |= BIT_STATUS_REPORT; break; // Set as true - case CMD_CYCLE_START: sys_state |= BIT_CYCLE_START; break; // Set as true - case CMD_FEED_HOLD: sys_state |= BIT_FEED_HOLD; break; // Set as true + case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true + case CMD_CYCLE_START: sys.execute |= EXEC_CYCLE_START; break; // Set as true + case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true case CMD_RESET: // Immediately force stepper subsystem idle at an interrupt level. - if (!(sys_state & BIT_RESET)) { // Force stop only first time. + if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. st_go_idle(); } - sys_state |= BIT_RESET; // Set as true + sys.execute |= EXEC_RESET; // Set as true break; default : // Write character to buffer rx_buffer[rx_buffer_head] = data; diff --git a/settings.c b/settings.c index 9d2c173..5cc3b16 100644 --- a/settings.c +++ b/settings.c @@ -54,6 +54,7 @@ typedef struct { #define DEFAULT_ACCELERATION (DEFAULT_FEEDRATE*60*60/10.0) // mm/min^2 #define DEFAULT_JUNCTION_DEVIATION 0.05 // mm #define DEFAULT_STEPPING_INVERT_MASK ((1< #include diff --git a/stepper.c b/stepper.c index f48cf77..3278c89 100644 --- a/stepper.c +++ b/stepper.c @@ -4,7 +4,6 @@ Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2011 Sungeun K. Jeon - Copyright (c) 2011 Jens Geisler Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,8 +33,6 @@ #include "planner.h" #include "limits.h" -#include "print.h" - // Some useful constants #define STEP_MASK ((1<initial_rate; set_step_events_per_minute(st.trapezoid_adjusted_rate); // Initialize cycles_per_step_event st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule. @@ -177,9 +172,9 @@ ISR(TIMER1_COMPA_vect,ISR_NOBLOCK) st.event_count = current_block->step_event_count; st.step_events_completed = 0; } else { - st.cycle_start = false; - st.feed_hold = false; st_go_idle(); + sys.cycle_start = false; + bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end } } @@ -190,29 +185,29 @@ ISR(TIMER1_COMPA_vect,ISR_NOBLOCK) 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<step_event_count) { - if (st.feed_hold) { + if (sys.feed_hold) { // Check for and execute feed hold by enforcing a steady deceleration from the moment of // execution. The rate of deceleration is limited by rate_delta and will never decelerate // faster or slower than in normal operation. If the distance required for the feed hold @@ -225,10 +220,11 @@ ISR(TIMER1_COMPA_vect,ISR_NOBLOCK) // If deceleration complete, set system flags and shutdown steppers. if (st.trapezoid_adjusted_rate <= current_block->rate_delta) { // Just go idle. Do not NULL current block. The bresenham algorithm variables must - // remain intact to ensure the stepper path is exactly the same. - st.cycle_start = false; + // remain intact to ensure the stepper path is exactly the same. Feed hold is still + // active and is released after the buffer has been reinitialized. st_go_idle(); - sys_state |= BIT_REPLAN_CYCLE; // Flag main program that feed hold is complete. + sys.cycle_start = false; + bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program that feed hold is complete. } else { st.trapezoid_adjusted_rate -= current_block->rate_delta; set_step_events_per_minute(st.trapezoid_adjusted_rate); @@ -339,9 +335,6 @@ void st_init() TCCR2A = 0; // Normal operation TCCR2B = (1<step_event_count - st.step_events_completed); - // Update initial rate and timers after feed hold. - st.trapezoid_adjusted_rate = 0; // Resumes from rest - set_step_events_per_minute(st.trapezoid_adjusted_rate); - st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule. - st.step_events_completed = 0; - st.feed_hold = false; // Release feed hold. Cycle is ready to re-start. + if (current_block != NULL) { + // Replan buffer from the feed hold stop location. + plan_cycle_reinitialize(current_block->step_event_count - st.step_events_completed); + // Update initial rate and timers after feed hold. + st.trapezoid_adjusted_rate = 0; // Resumes from rest + set_step_events_per_minute(st.trapezoid_adjusted_rate); + st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule. + st.step_events_completed = 0; + } + sys.feed_hold = false; // Release feed hold. Cycle is ready to re-start. } + +