From 03e2ca7cd5ef6653de74c242e73be978dfa2fec4 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Thu, 8 Dec 2011 18:47:48 -0700 Subject: [PATCH 01/55] Initial v0.8 ALPHA commit. Features multi-tasking run-time command execution (feed hold, cycle start, reset, status query). Extensive re-structuring of code for future features. - ALPHA status. - Multitasking ability with run-time command executions for real-time control and feedback. - Decelerating feed hold and resume during operation. - System abort/reset, which immediately kills all movement and re-initializes grbl. - Re-structured grbl to easily allow for new features: Status reporting, jogging, backlash compensation. (To be completed in the following releases.) - Resized TX/RX serial buffers (32/128 bytes) - Increased planner buffer size to 20 blocks. - Updated documentation. --- config.h | 50 ++---- doc/commands.txt | 21 +++ doc/planner-maths.txt | 30 ---- doc/structure.txt | 14 +- gcode.c | 4 - limits.c | 6 +- main.c | 64 +++++-- motion_control.c | 84 +++++++-- motion_control.h | 9 +- nuts_bolts.c | 20 +++ nuts_bolts.h | 19 +- planner.c | 287 +++++++++++++++++------------- planner.h | 33 ++-- protocol.c | 116 ++++++++++-- protocol.h | 4 + readme.textile | 27 ++- serial.c | 92 ++++++---- serial.h | 4 + settings.c | 4 +- settings.h | 2 +- spindle_control.c | 7 +- stepper.c | 404 +++++++++++++++++++++++++----------------- stepper.h | 17 +- 23 files changed, 841 insertions(+), 477 deletions(-) create mode 100644 doc/commands.txt delete mode 100644 doc/planner-maths.txt diff --git a/config.h b/config.h index db4d361..56f4b9d 100644 --- a/config.h +++ b/config.h @@ -26,9 +26,7 @@ #define BAUD_RATE 9600 -// Updated default pin-assignments from 0.6 onwards -// (see bottom of file for a copy of the old config) - +// Define pin-assignments #define STEPPERS_DISABLE_DDR DDRB #define STEPPERS_DISABLE_PORT PORTB #define STEPPERS_DISABLE_BIT 0 @@ -56,6 +54,16 @@ #define SPINDLE_DIRECTION_PORT PORTB #define SPINDLE_DIRECTION_BIT 5 +// 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. +// TODO: Solidify these default characters. Temporary for now. +#define CMD_STATUS_REPORT '?' +#define CMD_FEED_HOLD '!' +#define CMD_CYCLE_START '~' +#define CMD_RESET 0x18 // ctrl-x + // This parameter sets the delay time before disabling the steppers after the final block of movement. // A short delay ensures the steppers come to a complete stop and the residual inertial force in the // CNC axes don't cause the axes to drift off position. This is particularly important when manually @@ -94,33 +102,11 @@ // computational efficiency of generating arcs. #define N_ARC_CORRECTION 25 // Integer (1-255) +// Time delay increments performed during a dwell. The default value is set at 50ms, which provides +// a maximum time delay of roughly 55 minutes, more than enough for most any application. Increasing +// this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of +// run-time command executions, like status reports, since these are performed between each dwell +// 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) + #endif - -// Pin-assignments from Grbl 0.5 - -// #define STEPPERS_DISABLE_DDR DDRD -// #define STEPPERS_DISABLE_PORT PORTD -// #define STEPPERS_DISABLE_BIT 2 -// -// #define STEPPING_DDR DDRC -// #define STEPPING_PORT PORTC -// #define X_STEP_BIT 0 -// #define Y_STEP_BIT 1 -// #define Z_STEP_BIT 2 -// #define X_DIRECTION_BIT 3 -// #define Y_DIRECTION_BIT 4 -// #define Z_DIRECTION_BIT 5 -// -// #define LIMIT_DDR DDRD -// #define LIMIT_PORT PORTD -// #define X_LIMIT_BIT 3 -// #define Y_LIMIT_BIT 4 -// #define Z_LIMIT_BIT 5 -// -// #define SPINDLE_ENABLE_DDR DDRD -// #define SPINDLE_ENABLE_PORT PORTD -// #define SPINDLE_ENABLE_BIT 6 -// -// #define SPINDLE_DIRECTION_DDR DDRD -// #define SPINDLE_DIRECTION_PORT PORTD -// #define SPINDLE_DIRECTION_BIT 7 diff --git a/doc/commands.txt b/doc/commands.txt new file mode 100644 index 0000000..aacf76a --- /dev/null +++ b/doc/commands.txt @@ -0,0 +1,21 @@ +Runtime commands for Grbl +========================= + +In normal operation, grbl accepts g-code blocks followed by a carriage return. Each block is then parsed, processed, and placed into a ring buffer with computed acceleration profiles. Grbl will respond with an 'ok' or 'error:XXX' for each block received. + +As of v0.8, grbl features multi-tasking events, which allow for immediate execution of run-time commands regardless of what grbl is doing. With this functionality, direct control of grbl may be possible, such as a controlled decelerating feed hold, resume, and system abort/reset. In addition, this provides the ability to report the real-time status of the CNC machine's current location and feed rates. + +How it works: The run-time commands are defined as special characters, which are picked off the serial read buffer at an interrupt level. The serial interrupt then sets a run-time command system flag for the main program to execute when ready. The main program consists of run-time command check points placed strategically in various points in the program, where grbl maybe idle waiting for room in a buffer or the execution time from the last check point may exceed a fraction of a second. + +How to interface: From a terminal connection, run-time commands may be sent at anytime via keystrokes. When streaming g-code, the user interface should be able to send these special characters independently of the stream. Grbl will not write these run-time command special characters to the buffer, so they may be placed anywhere in the stream at anytime, as long as there is room in the buffer. Also, ensure that the g-code program being streamed to grbl does not contain any of these special characters in the program. These characters may be defined per user requirements in the 'config.h' file. + +Run-time commands: + +- Feed Hold: This initiates an immediate controlled deceleration of the streaming g-code program to a stop. The deceleration, limited by the machine acceleration settings, ensures no steps are lost and positioning is maintained. Grbl may still receive and buffer g-code blocks as the feed hold is being executed. Once the feed hold completes, grbl will replan the buffer and resume upon a 'cycle start' command. + +- Cycle Start: (a.k.a. Resume) For now, cycle start only resumes the g-code program after a feed hold. In later releases, this may also function as a way to initiate the steppers manually when a user would like to fill the planner buffer completely before starting the cycle. + +- Reset: This issues an immediate shutdown of the stepper motors and a system abort. The main program will exit back to the main loop and re-initialize grbl. + +- Status Report: (TODO) In future releases, this will provide real-time positioning, feed rate, and block processed data, as well as other important data to the user. This also may be considered a 'poor-man's' DRO (digital read-out), where grbl thinks it is, rather than a direct and absolute measurement. + diff --git a/doc/planner-maths.txt b/doc/planner-maths.txt deleted file mode 100644 index 49f371f..0000000 --- a/doc/planner-maths.txt +++ /dev/null @@ -1,30 +0,0 @@ -Reasoning behind the mathematics in 'planner' module (in the key of 'Mathematica') -================================================================================== - - -s == speed, a == acceleration, t == time, d == distance - -Basic definitions: - - Speed[s_, a_, t_] := s + (a*t) - Travel[s_, a_, t_] := Integrate[Speed[s, a, t], t] - -Distance to reach a specific speed with a constant acceleration: - - Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, d, t] - d -> (m^2 - s^2)/(2 a) --> estimate_acceleration_distance() - -Speed after a given distance of travel with constant acceleration: - - Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, m, t] - m -> Sqrt[2 a d + s^2] - - DestinationSpeed[s_, a_, d_] := Sqrt[2 a d + s^2] - -When to start braking (di) to reach a specified destionation speed (s2) after accelerating -from initial speed s1 without ever stopping at a plateau: - - Solve[{DestinationSpeed[s1, a, di] == DestinationSpeed[s2, a, d - di]}, di] - di -> (2 a d - s1^2 + s2^2)/(4 a) --> intersection_distance() - - IntersectionDistance[s1_, s2_, a_, d_] := (2 a d - s1^2 + s2^2)/(4 a) diff --git a/doc/structure.txt b/doc/structure.txt index 4992f80..cf176a6 100644 --- a/doc/structure.txt +++ b/doc/structure.txt @@ -4,7 +4,8 @@ The general structure of Grbl The main processing stack: 'protocol' : Accepts command lines from the serial port and passes them to 'gcode' for execution. - Provides status responses for each command. + Provides status responses for each command. Also manages run-time commands set by + the serial interrupt. 'gcode' : Recieves gcode from 'protocol', parses it according to the current state of the parser and issues commands via '..._control' modules @@ -14,8 +15,8 @@ The main processing stack: 'motion_control' : Accepts motion commands from 'gcode' and passes them to the 'planner'. This module represents the public interface of the planner/stepper duo. -'planner' : Recieves linear motion commands from 'motion_control' and adds them to the plan of - prepared motions. It takes care of continously optimizing the acceleration profile +'planner' : Receives linear motion commands from 'motion_control' and adds them to the plan of + prepared motions. It takes care of continuously optimizing the acceleration profile as motions are added. 'stepper' : Executes the motions by stepping the steppers according to the plan. @@ -27,15 +28,16 @@ Supporting files: 'config.h' : Compile time user settings -'settings' : Maintains the run time settings record in eeprom and makes it availible +'settings' : Maintains the run time settings record in eeprom and makes it available to all modules. 'eeprom' : A library from Atmel that provides methods for reading and writing the eeprom with a small addition from us that read and write binary streams with check sums used to verify validity of the settings record. -'nuts_bolts.h' : A tiny collection of useful constants and macros used everywhere +'nuts_bolts.h' : A collection of global variable definitions, useful constants, and macros used everywhere -'serial' : Low level serial communications +'serial' : Low level serial communications and picks off run-time commands real-time for asynchronous + control. 'print' : Functions to print strings of different formats (using serial) \ No newline at end of file diff --git a/gcode.c b/gcode.c index 7fb5b30..1936015 100644 --- a/gcode.c +++ b/gcode.c @@ -127,10 +127,8 @@ uint8_t gc_execute_line(char *line) { switch(int_value) { case 0: gc.motion_mode = MOTION_MODE_SEEK; break; case 1: gc.motion_mode = MOTION_MODE_LINEAR; break; -#ifdef __AVR_ATmega328P__ case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break; case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break; -#endif case 4: next_action = NEXT_ACTION_DWELL; break; case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break; case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break; @@ -226,7 +224,6 @@ uint8_t gc_execute_line(char *line) { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); break; -#ifdef __AVR_ATmega328P__ case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: if (radius_mode) { /* @@ -336,7 +333,6 @@ uint8_t gc_execute_line(char *line) { r, isclockwise); break; -#endif } } diff --git a/limits.c b/limits.c index 6444c8d..ec0b432 100644 --- a/limits.c +++ b/limits.c @@ -24,6 +24,10 @@ #include "settings.h" #include "nuts_bolts.h" #include "config.h" +#include "motion_control.h" +#include "planner.h" + +// TODO: Deprecated. Need to update for new version. void limits_init() { LIMIT_DDR &= ~(LIMIT_MASK); @@ -87,7 +91,7 @@ static void leave_limit_switch(bool x, bool y, bool z) { } void limits_go_home() { - st_synchronize(); + plan_synchronize(); // Store the current limit switch state uint8_t original_limit_state = LIMIT_PIN; approach_limit_switch(false, false, true); // First home the z axis diff --git a/main.c b/main.c index b56bf6f..a1b0b1d 100644 --- a/main.c +++ b/main.c @@ -3,7 +3,9 @@ Part of Grbl 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 @@ -21,9 +23,11 @@ #include #include #include +#include #include #include "config.h" #include "planner.h" +#include "nuts_bolts.h" #include "stepper.h" #include "spindle_control.h" #include "motion_control.h" @@ -34,22 +38,56 @@ #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 + int main(void) { - sei(); - - serial_init(BAUD_RATE); - protocol_init(); - settings_init(); - plan_init(); - st_init(); - spindle_init(); - gc_init(); - limits_init(); + // Initialize system + 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 - for(;;){ - sleep_mode(); // Wait for it ... + 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 + + // Reset system. + serial_reset_read_buffer(); // Clear serial read buffer + settings_init(); // Load grbl settings from EEPROM + protocol_init(); // Clear incoming line data + plan_init(); // Clear block buffer and planner variables + 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")); + } + + protocol_execute_runtime(); protocol_process(); // ... process the serial protocol + } return 0; /* never reached */ } diff --git a/motion_control.c b/motion_control.c index c7e5670..a5985c4 100644 --- a/motion_control.c +++ b/motion_control.c @@ -4,6 +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 @@ -29,28 +30,55 @@ #include "nuts_bolts.h" #include "stepper.h" #include "planner.h" +#include "limits.h" +#include "protocol.h" -// Execute dwell in seconds. Maximum time delay is > 18 hours, more than enough for any application. -void mc_dwell(double seconds) +#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. +// NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line +// segments, must pass through this routine before being passed to the planner. The seperation of +// mc_line and plan_buffer_line is done primarily to make backlash compensation integration simple +// and direct. +// TODO: Check for a better way to avoid having to push the arguments twice for non-backlash cases. +// However, this keeps the memory requirements lower since it doesn't have to call and hold two +// plan_buffer_lines in memory. Grbl only has to retain the original line input variables during a +// backlash segment(s). +void mc_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate) { - uint16_t i = floor(seconds); - st_synchronize(); - _delay_ms(floor(1000*(seconds-i))); // Delay millisecond remainder - while (i > 0) { - _delay_ms(1000); // Delay one second - i--; - } + // TODO: Backlash compensation may be installed here. Only need direction info to track when + // to insert a backlash line motion(s) before the intended line motion. Requires its own + // plan_check_full_buffer() and check for system abort loop. + + // If the buffer is full: good! That means we are well ahead of the robot. + // 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. + } 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(); } -#ifdef __AVR_ATmega328P__ + +// Execute an arc in offset mode format. position == current xyz, target == target xyz, +// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is +// the direction of helical travel, radius == circle radius, isclockwise boolean. Used +// for vector transformation direction. // The arc is approximated by generating a huge number of tiny, linear segments. The length of each // segment is configured in settings.mm_per_arc_segment. void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, double feed_rate, uint8_t invert_feed_rate, double radius, uint8_t isclockwise) { -// int acceleration_manager_was_enabled = plan_is_acceleration_manager_enabled(); -// plan_set_acceleration_manager_enabled(false); // disable acceleration management for the duration of the arc - double center_axis0 = position[axis_0] + offset[axis_0]; double center_axis1 = position[axis_1] + offset[axis_1]; double linear_travel = target[axis_linear] - position[axis_linear]; @@ -136,17 +164,35 @@ void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, ui arc_target[axis_0] = center_axis0 + r_axis0; arc_target[axis_1] = center_axis1 + r_axis1; arc_target[axis_linear] += linear_per_segment; - plan_buffer_line(arc_target[X_AXIS], arc_target[Y_AXIS], arc_target[Z_AXIS], feed_rate, invert_feed_rate); + 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; } } // Ensure last segment arrives at target location. - plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, invert_feed_rate); - -// plan_set_acceleration_manager_enabled(acceleration_manager_was_enabled); + mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, invert_feed_rate); } -#endif + +// Execute dwell in seconds. +void mc_dwell(double seconds) +{ + uint16_t i = floor(1000/DWELL_TIME_STEP*seconds); + plan_synchronize(); + _delay_ms(floor(1000*seconds-i*DWELL_TIME_STEP)); // Delay millisecond remainder + while (i > 0) { + // NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds. + protocol_execute_runtime(); + if (sys_abort) { return; } + _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment + i--; + } +} + + +// TODO: Update limits and homing cycle subprograms for better integration with new features. void mc_go_home() { - st_go_home(); + limits_go_home(); + plan_set_current_position(0,0,0); } diff --git a/motion_control.h b/motion_control.h index b91650d..003893a 100644 --- a/motion_control.h +++ b/motion_control.h @@ -25,24 +25,21 @@ #include #include "planner.h" -// NOTE: Although the following functions structurally belongs in this module, there is nothing to do but +// NOTE: Although the following function structurally belongs in this module, there is nothing to do but // to forward the request to the planner. +#define mc_set_current_position(x, y, z) plan_set_current_position(x, y, z) // 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. -#define mc_line(x, y, z, feed_rate, invert_feed_rate) plan_buffer_line(x, y, z, feed_rate, invert_feed_rate) +void mc_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate); -#define mc_set_current_position(x, y, z) plan_set_current_position(x, y, z) - -#ifdef __AVR_ATmega328P__ // Execute an arc in offset mode format. position == current xyz, target == target xyz, // offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is // the direction of helical travel, radius == circle radius, isclockwise boolean. Used // for vector transformation direction. void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, double feed_rate, uint8_t invert_feed_rate, double radius, uint8_t isclockwise); -#endif // Dwell for a specific number of seconds void mc_dwell(double seconds); diff --git a/nuts_bolts.c b/nuts_bolts.c index 0a8bf5c..8a6960c 100644 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -1,3 +1,23 @@ +/* + nuts_bolts.c - Shared functions + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + #include "nuts_bolts.h" #include #include diff --git a/nuts_bolts.h b/nuts_bolts.h index 37f3aa6..665de25 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -1,5 +1,5 @@ /* - motion_control.h - cartesian robot controller. + nuts_bolts.h - Header file for shared definitions, variables, and functions Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -37,6 +37,23 @@ #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 +// 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 global system variables +extern uint8_t sys_abort; // Global system abort flag +extern volatile uint8_t sys_state; // Global system state variable + // 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 // a pointer to the result variable. Returns true when it succeeds diff --git a/planner.c b/planner.c index 6dea6b2..aeba0cd 100644 --- a/planner.c +++ b/planner.c @@ -4,6 +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 @@ -30,28 +31,26 @@ #include "stepper.h" #include "settings.h" #include "config.h" +#include "protocol.h" // The number of linear motions that can be in the plan at any give time -#ifdef __AVR_ATmega328P__ -#define BLOCK_BUFFER_SIZE 18 -#else -#define BLOCK_BUFFER_SIZE 5 -#endif +#define BLOCK_BUFFER_SIZE 20 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 static volatile uint8_t block_buffer_tail; // Index of the block to process now +static uint8_t next_buffer_head; // Index of the next buffer head -static int32_t position[3]; // The current position of the tool in absolute steps +static int32_t position[3]; // The planner position of the tool in absolute steps +// static int32_t coord_offset[3]; // Current coordinate offset from machine zero in absolute steps static double previous_unit_vec[3]; // Unit vector of previous path line segment static double previous_nominal_speed; // Nominal speed of previous path line segment -static uint8_t acceleration_manager_enabled; // Acceleration management active? - // Returns the index of the next block in the ring buffer // NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication. -static int8_t next_block_index(int8_t block_index) { +static uint8_t next_block_index(uint8_t block_index) +{ block_index++; if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; } return(block_index); @@ -59,7 +58,8 @@ static int8_t next_block_index(int8_t block_index) { // Returns the index of the previous block in the ring buffer -static int8_t prev_block_index(int8_t block_index) { +static uint8_t prev_block_index(uint8_t block_index) +{ if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; } block_index--; return(block_index); @@ -68,7 +68,8 @@ static int8_t prev_block_index(int8_t block_index) { // Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the // given acceleration: -static double estimate_acceleration_distance(double initial_rate, double target_rate, double acceleration) { +static double estimate_acceleration_distance(double initial_rate, double target_rate, double acceleration) +{ return( (target_rate*target_rate-initial_rate*initial_rate)/(2*acceleration) ); } @@ -86,7 +87,8 @@ static double estimate_acceleration_distance(double initial_rate, double target_ // you started at speed initial_rate and accelerated until this point and want to end at the final_rate after // a total travel of distance. This can be used to compute the intersection point between acceleration and // deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) -static double intersection_distance(double initial_rate, double final_rate, double acceleration, double distance) { +static double intersection_distance(double initial_rate, double final_rate, double acceleration, double distance) +{ return( (2*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/(4*acceleration) ); } @@ -96,13 +98,15 @@ static double intersection_distance(double initial_rate, double final_rate, doub // NOTE: sqrt() reimplimented here from prior version due to improved planner logic. Increases speed // in time critical computations, i.e. arcs or rapid short lines from curves. Guaranteed to not exceed // BLOCK_BUFFER_SIZE calls per planner cycle. -static double max_allowable_speed(double acceleration, double target_velocity, double distance) { +static double max_allowable_speed(double acceleration, double target_velocity, double distance) +{ return( sqrt(target_velocity*target_velocity-2*acceleration*distance) ); } // The kernel called by planner_recalculate() when scanning the plan from last to first entry. -static void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *next) { +static void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *next) +{ if (!current) { return; } // Cannot operate on nothing. if (next) { @@ -128,8 +132,9 @@ static void planner_reverse_pass_kernel(block_t *previous, block_t *current, blo // planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This // implements the reverse pass. -static void planner_reverse_pass() { - auto int8_t block_index = block_buffer_head; +static void planner_reverse_pass() +{ + uint8_t block_index = block_buffer_head; block_t *block[3] = {NULL, NULL, NULL}; while(block_index != block_buffer_tail) { block_index = prev_block_index( block_index ); @@ -143,7 +148,8 @@ static void planner_reverse_pass() { // The kernel called by planner_recalculate() when scanning the plan from first to last entry. -static void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *next) { +static void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *next) +{ if(!previous) { return; } // Begin planning after buffer_tail // If the previous block is an acceleration block, but it is not long enough to complete the @@ -167,8 +173,9 @@ static void planner_forward_pass_kernel(block_t *previous, block_t *current, blo // planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This // implements the forward pass. -static void planner_forward_pass() { - int8_t block_index = block_buffer_tail; +static void planner_forward_pass() +{ + uint8_t block_index = block_buffer_tail; block_t *block[3] = {NULL, NULL, NULL}; while(block_index != block_buffer_head) { @@ -194,8 +201,8 @@ static void planner_forward_pass() { // The factors represent a factor of braking and must be in the range 0.0-1.0. // This converts the planner parameters to the data required by the stepper controller. // NOTE: Final rates must be computed in terms of their respective blocks. -static void calculate_trapezoid_for_block(block_t *block, double entry_factor, double exit_factor) { - +static void calculate_trapezoid_for_block(block_t *block, double entry_factor, double exit_factor) +{ block->initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min) block->final_rate = ceil(block->nominal_rate*exit_factor); // (step/min) int32_t acceleration_per_minute = block->rate_delta*ACCELERATION_TICKS_PER_SECOND*60.0; // (step/min^2) @@ -235,8 +242,9 @@ static void calculate_trapezoid_for_block(block_t *block, double entry_factor, d // planner_recalculate() after updating the blocks. Any recalulate flagged junction will // compute the two adjacent trapezoids to the junction, since the junction speed corresponds // to exit speed and entry speed of one another. -static void planner_recalculate_trapezoids() { - int8_t block_index = block_buffer_tail; +static void planner_recalculate_trapezoids() +{ + uint8_t block_index = block_buffer_tail; block_t *current; block_t *next = NULL; @@ -281,63 +289,71 @@ static void planner_recalculate_trapezoids() { // All planner computations are performed with doubles (float on Arduinos) to minimize numerical round- // off errors. Only when planned values are converted to stepper rate parameters, these are integers. -static void planner_recalculate() { +static void planner_recalculate() +{ planner_reverse_pass(); planner_forward_pass(); planner_recalculate_trapezoids(); } -void plan_init() { - block_buffer_head = 0; - block_buffer_tail = 0; - plan_set_acceleration_manager_enabled(true); +void plan_reset_buffer() +{ + block_buffer_tail = block_buffer_head; + next_buffer_head = next_block_index(block_buffer_head); +} + +void plan_init() +{ + plan_reset_buffer(); clear_vector(position); +// clear_vector(coord_offset); clear_vector_double(previous_unit_vec); previous_nominal_speed = 0.0; } -void plan_set_acceleration_manager_enabled(uint8_t enabled) { - if ((!!acceleration_manager_enabled) != (!!enabled)) { - st_synchronize(); - acceleration_manager_enabled = !!enabled; - } -} - -int plan_is_acceleration_manager_enabled() { - return(acceleration_manager_enabled); -} - -void plan_discard_current_block() { +void plan_discard_current_block() +{ if (block_buffer_head != block_buffer_tail) { block_buffer_tail = next_block_index( block_buffer_tail ); } } -block_t *plan_get_current_block() { +block_t *plan_get_current_block() +{ if (block_buffer_head == block_buffer_tail) { return(NULL); } return(&block_buffer[block_buffer_tail]); } +// Returns the availability status of the block ring buffer. True, if full. +uint8_t plan_check_full_buffer() +{ + if (block_buffer_tail == next_buffer_head) { return(true); } + return(false); +} + +// Block until all buffered steps are executed. +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 + } +} // Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in -// millimaters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed +// millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed // rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. -void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate) { - +// NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control. +void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate) +{ + // Prepare to set up new block + block_t *block = &block_buffer[block_buffer_head]; + // Calculate target position in absolute steps int32_t target[3]; target[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); target[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); target[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); - - // Calculate the buffer head after we push this byte - int next_buffer_head = next_block_index( block_buffer_head ); - // If the buffer is full: good! That means we are well ahead of the robot. - // Rest here until there is room in the buffer. - while(block_buffer_tail == next_buffer_head) { sleep_mode(); } - - // Prepare to set up new block - block_t *block = &block_buffer[block_buffer_head]; // Compute direction bits for this block block->direction_bits = 0; @@ -384,92 +400,113 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in block->rate_delta = ceil( block->step_event_count*inverse_millimeters * settings.acceleration / (60 * ACCELERATION_TICKS_PER_SECOND )); // (step/min/acceleration_tick) - // Perform planner-enabled calculations - if (acceleration_manager_enabled) { - - // Compute path unit vector - double unit_vec[3]; + // Compute path unit vector + double unit_vec[3]; - unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters; - unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters; - unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters; - - // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. - // Let a circle be tangent to both previous and current path line segments, where the junction - // deviation is defined as the distance from the junction to the closest edge of the circle, - // colinear with the circle center. The circular segment joining the two paths represents the - // path of centripetal acceleration. Solve for max velocity based on max acceleration about the - // radius of the circle, defined indirectly by junction deviation. This may be also viewed as - // path width or max_jerk in the previous grbl version. This approach does not actually deviate - // from path, but used as a robust way to compute cornering speeds, as it takes into account the - // nonlinearities of both the junction angle and junction velocity. - double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed + unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters; + unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters; + unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters; - // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. - if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { - // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) - // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. - double cos_theta = - previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] - - previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] - - previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; - - // Skip and use default max junction speed for 0 degree acute junction. - if (cos_theta < 0.95) { - vmax_junction = min(previous_nominal_speed,block->nominal_speed); - // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. - if (cos_theta > -0.95) { - // Compute maximum junction velocity based on maximum acceleration and junction deviation - double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. - vmax_junction = min(vmax_junction, - sqrt(settings.acceleration * settings.junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) ); - } + // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. + // Let a circle be tangent to both previous and current path line segments, where the junction + // deviation is defined as the distance from the junction to the closest edge of the circle, + // colinear with the circle center. The circular segment joining the two paths represents the + // path of centripetal acceleration. Solve for max velocity based on max acceleration about the + // radius of the circle, defined indirectly by junction deviation. This may be also viewed as + // path width or max_jerk in the previous grbl version. This approach does not actually deviate + // from path, but used as a robust way to compute cornering speeds, as it takes into account the + // nonlinearities of both the junction angle and junction velocity. + double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed + + // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. + if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { + // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) + // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. + double cos_theta = - previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] + - previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] + - previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; + + // Skip and use default max junction speed for 0 degree acute junction. + if (cos_theta < 0.95) { + vmax_junction = min(previous_nominal_speed,block->nominal_speed); + // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. + if (cos_theta > -0.95) { + // Compute maximum junction velocity based on maximum acceleration and junction deviation + double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. + vmax_junction = min(vmax_junction, + sqrt(settings.acceleration * settings.junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) ); } } - block->max_entry_speed = vmax_junction; - - // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. - double v_allowable = max_allowable_speed(-settings.acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); - block->entry_speed = min(vmax_junction, v_allowable); - - // Initialize planner efficiency flags - // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. - // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then - // the current block and next block junction speeds are guaranteed to always be at their maximum - // junction speeds in deceleration and acceleration, respectively. This is due to how the current - // block nominal speed limits both the current and next maximum junction speeds. Hence, in both - // the reverse and forward planners, the corresponding block junction speed will always be at the - // the maximum junction speed and may always be ignored for any speed reduction checks. - if (block->nominal_speed <= v_allowable) { block->nominal_length_flag = true; } - else { block->nominal_length_flag = false; } - block->recalculate_flag = true; // Always calculate trapezoid for new block - - // Update previous path unit_vector and nominal speed - memcpy(previous_unit_vec, unit_vec, sizeof(unit_vec)); // previous_unit_vec[] = unit_vec[] - previous_nominal_speed = block->nominal_speed; - - } else { - // Acceleration planner disabled. Set minimum that is required. - block->initial_rate = block->nominal_rate; - block->final_rate = block->nominal_rate; - block->accelerate_until = 0; - block->decelerate_after = block->step_event_count; - block->rate_delta = 0; } + block->max_entry_speed = vmax_junction; + + // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. + double v_allowable = max_allowable_speed(-settings.acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); + block->entry_speed = min(vmax_junction, v_allowable); + + // Initialize planner efficiency flags + // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. + // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then + // the current block and next block junction speeds are guaranteed to always be at their maximum + // junction speeds in deceleration and acceleration, respectively. This is due to how the current + // block nominal speed limits both the current and next maximum junction speeds. Hence, in both + // the reverse and forward planners, the corresponding block junction speed will always be at the + // the maximum junction speed and may always be ignored for any speed reduction checks. + if (block->nominal_speed <= v_allowable) { block->nominal_length_flag = true; } + else { block->nominal_length_flag = false; } + block->recalculate_flag = true; // Always calculate trapezoid for new block + + // Update previous path unit_vector and nominal speed + memcpy(previous_unit_vec, unit_vec, sizeof(unit_vec)); // previous_unit_vec[] = unit_vec[] + previous_nominal_speed = block->nominal_speed; + + // Update buffer head and next buffer head indices + block_buffer_head = next_buffer_head; + next_buffer_head = next_block_index(block_buffer_head); - // Move buffer head - block_buffer_head = next_buffer_head; // Update position memcpy(position, target, sizeof(target)); // position[] = target[] - if (acceleration_manager_enabled) { planner_recalculate(); } - st_cycle_start(); + planner_recalculate(); } // Reset the planner position vector and planner speed -void plan_set_current_position(double x, double y, double z) { +void plan_set_current_position(double x, double y, double z) +{ + // Track the position offset from the initial position + // TODO: Need to make sure coord_offset is robust and/or needed. Can be used for a soft reset, + // where the machine position is retained after a system abort/reset. However, this is not + // correlated to the actual machine position after a soft reset and may not be needed. This could + // be left to a user interface to maintain. +// coord_offset[X_AXIS] += position[X_AXIS]; +// coord_offset[Y_AXIS] += position[Y_AXIS]; +// coord_offset[Z_AXIS] += position[Z_AXIS]; position[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); position[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); - position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); + position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); +// coord_offset[X_AXIS] -= position[X_AXIS]; +// coord_offset[Y_AXIS] -= position[Y_AXIS]; +// coord_offset[Z_AXIS] -= position[Z_AXIS]; previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. clear_vector_double(previous_unit_vec); } + +// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail. +// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped. +void plan_cycle_reinitialize(int32_t step_events_remaining) +{ + block_t *block = &block_buffer[block_buffer_tail]; // Point to partially completed block + + // Only remaining millimeters and step_event_count need to be updated for planner recalculate. + // Other variables (step_x, step_y, step_z, rate_delta, etc.) all need to remain the same to + // ensure the original planned motion is resumed exactly. + block->millimeters = (block->millimeters*step_events_remaining)/block->step_event_count; + block->step_event_count = step_events_remaining; + + // Re-plan from a complete stop. Reset planner entry speeds and flags. + block->entry_speed = 0.0; + block->max_entry_speed = 0.0; + block->nominal_length_flag = false; + block->recalculate_flag = true; + planner_recalculate(); +} diff --git a/planner.h b/planner.h index f91b7c2..fc692b5 100644 --- a/planner.h +++ b/planner.h @@ -27,12 +27,12 @@ // This struct is used when buffering the setup for each linear movement "nominal" values are as specified in // the source g-code and may never actually be reached if acceleration management is active. typedef struct { + // Fields used by the bresenham algorithm for tracing the line - uint32_t steps_x, steps_y, steps_z; // Step count along each axis uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) + uint32_t steps_x, steps_y, steps_z; // Step count along each axis int32_t step_event_count; // The number of step events required to complete this block - uint32_t nominal_rate; // The nominal step rate for this block in step_events/minute - + // 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 @@ -42,12 +42,13 @@ typedef struct { uint8_t nominal_length_flag; // Planner flag for nominal speed always reached // Settings for the trapezoid generator - uint32_t initial_rate; // The jerk-adjusted step rate at start of block - uint32_t final_rate; // The minimal rate at exit + uint32_t initial_rate; // The step rate at start of block + uint32_t final_rate; // The step rate at end of block int32_t rate_delta; // The steps/minute to add or subtract when changing speed (must be positive) uint32_t accelerate_until; // The index of the step event on which to stop acceleration uint32_t decelerate_after; // The index of the step event on which to start decelerating - + uint32_t nominal_rate; // The nominal step rate for this block in step_events/minute + } block_t; // Initialize the motion plan subsystem @@ -65,13 +66,19 @@ void plan_discard_current_block(); // Gets the current block. Returns NULL if buffer empty block_t *plan_get_current_block(); -// Enables or disables acceleration-management for upcoming blocks -void plan_set_acceleration_manager_enabled(uint8_t enabled); - -// Is acceleration-management currently enabled? -int plan_is_acceleration_manager_enabled(); - // Reset the position vector -void plan_set_current_position(double x, double y, double z); +void plan_set_current_position(double x, double y, double z); + +// Reinitialize plan with a partially completed block +void plan_cycle_reinitialize(int32_t step_events_remaining); + +// Reset buffer +void plan_reset_buffer(); + +// Returns the status of the block ring buffer. True, if buffer is full. +uint8_t plan_check_full_buffer(); + +// Block until all buffered steps are executed +void plan_synchronize(); #endif diff --git a/protocol.c b/protocol.c index 4835c90..da31da8 100644 --- a/protocol.c +++ b/protocol.c @@ -29,12 +29,17 @@ #include #include "nuts_bolts.h" #include +#include "stepper.h" +#include "planner.h" + #define LINE_BUFFER_SIZE 50 -static char line[LINE_BUFFER_SIZE]; -static uint8_t char_counter; +static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. +static uint8_t char_counter; // Last character counter in line variable. +static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters. -static void status_message(int status_code) { +static void status_message(int status_code) +{ if (status_code == 0) { printPgmString(PSTR("ok\r\n")); } else { @@ -55,30 +60,115 @@ static void status_message(int status_code) { } } -void protocol_init() + +void protocol_status_report() { - printPgmString(PSTR("\r\nGrbl " GRBL_VERSION)); - printPgmString(PSTR("\r\n")); + // TODO: Status report data is written to the user here. This function should be able to grab a + // real-time snapshot of the stepper subprogram and the actual location of the CNC machine. At a + // minimum, status report should return real-time location information. Other important information + // 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 + // 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. } + +void protocol_init() +{ + char_counter = 0; // Reset line input + iscomment = false; +} + + +// Executes run-time commands, when required. This is called from various check points in the main +// 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. +void protocol_execute_runtime() +{ + if (sys_state) { // Enter only if any bit flag is enabled + + // System abort. Steppers have already been force stopped. + if (sys_state & BIT_RESET) { + sys_abort = true; + return; // Nothing else to do but exit. + } + + // Execute and serial print status + if (sys_state & BIT_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 + } + + // 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) { + st_cycle_reinitialize(); + sys_state ^= BIT_REPLAN_CYCLE; // Toggle off + } + + if (sys_state & BIT_CYCLE_START) { + st_cycle_start(); // Issue cycle start command to stepper subsystem + sys_state ^= BIT_CYCLE_START; // Toggle off + } + } +} + + // Executes one line of input according to protocol -uint8_t protocol_execute_line(char *line) { +uint8_t protocol_execute_line(char *line) +{ if(line[0] == '$') { return(settings_execute_line(line)); // Delegate lines starting with '$' to the settings module + + // } else if { + // + // JOG MODE + // + // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be + // susceptible to other runtime commands except for e-stop. The jogging function is intended to + // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped + // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would + // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the + // motion by repeatedly toggling to slow the motion to the desired location. Location data would + // need to be updated real-time and supplied to the user through status queries. + // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are + // handled by the planner. It would be possible for the jog subprogram to insert blocks into the + // block buffer without having the planner plan them. It would need to manage de/ac-celerations + // on its own carefully. This approach could be effective and possibly size/memory efficient. + } else { return(gc_execute_line(line)); // Everything else is gcode } } + +// Process one line of incoming serial data. Remove unneeded characters and capitalize. void protocol_process() { - char c; - uint8_t iscomment = false; - while((c = serial_read()) != SERIAL_NO_DATA) - { - if ((c == '\n') || (c == '\r')) { // End of block reached + uint8_t c; + while((c = serial_read()) != SERIAL_NO_DATA) { + if ((c == '\n') || (c == '\r')) { // End of line reached + + // Runtime command check point before executing line. Prevent any furthur line executions. + // 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 (char_counter > 0) {// Line is complete. Then execute! - line[char_counter] = 0; // terminate string + line[char_counter] = 0; // Terminate string status_message(protocol_execute_line(line)); } else { // Empty or comment line. Skip block. diff --git a/protocol.h b/protocol.h index 3ad6597..1ac66ae 100644 --- a/protocol.h +++ b/protocol.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 @@ -36,4 +37,7 @@ void protocol_process(); // Executes one line of input according to protocol uint8_t protocol_execute_line(char *line); +// Checks and executes a runtime command at various stop points in main program +void protocol_execute_runtime(); + #endif diff --git a/readme.textile b/readme.textile index fab1580..4f0bb98 100644 --- a/readme.textile +++ b/readme.textile @@ -4,12 +4,33 @@ Grbl is a no-compromise, high performance, low cost alternative to parallel-port The controller is written in highly optimized C utilizing every clever feature of the AVR-chips to achieve precise timing and asynchronous operation. It is able to maintain more than 30kHz of stable, jitter free control pulses. -It accepts standards-compliant G-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported - but no support for tool offsets, functions or variables as these are apocryphal and fell into disuse after humans left G-code authoring to machines some time in the 80s. +It accepts standards-compliant G-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as, other basic functional g-code commands. Functions and variables are not currently supported, but may be included in future releases in a form of a pre-processor. Grbl includes full acceleration management with look ahead. That means the controller will look up to 20 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering. -*Important note for Atmega 168 users:* Grbl used to be compatible with both the older Ardunios running atmega 168 and the newer with 328p. The full version of Grbl now compiles without support for circles/arcs if you target 168. If you need arcs, but not acceleration-management I am still maintaining Grbl 0.51 "in the branch called 'v0_51'":https://github.com/simen/grbl/tree/v0_51. +*Changelog for v0.8 from v0.7:* + - *ALPHA STATUS*: Major structural overhaul to allow for multi-tasking events and new feature sets + - New run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status report (TBD) + - Controlled feed hold with deceleration to ensure no skipped steps and loss of location. + - After feed hold, cycle accelerations are re-planned and may be resumed. + - System reset re-initializes grbl without resetting the Arduino. + - Updated dwell function. + - Restructured planner and stepper modules to become independent and ready for future features. + - Planned features: Jog mode, status reporting, backlash compensation, improved flow control, planner optimizations + - Reduce serial read buffer to 127 characters and increased write buffer to 31 characters (-1 ring buffer). + - Increased planner buffer size to 20 blocks. + - Misc bug fixes and removed deprecated acceleration enabled code. -*Note for users upgrading from 0.51 to 0.6:* The new version has new and improved default pin-out. If nothing works when you upgrade, that is because the pulse trains are coming from the wrong pins. This is a simple matter of editing config.h – the whole legacy pin assignment is there for you to uncomment. +*Changelog for v0.7 from v0.6:* + - Significantly improved and optimized planner re-factoring. + - New robust cornering algorithm, enabling smoother and faster motions. + - Arc acceleration planning enabled by efficient vector transformation implementation. + - Stepper subsystem re-factoring to help remove some motion issues from pre-v0.7 builds. + - Increased dwell times. + - G92 Coordinate offset support. + - (Beta) Limit switch and homing cycle support. + - Many other bug fixes and efficiency improvements. + +*Important note for Atmega 168 users:* Going forward, support for Atmega 168 will be dropped due to its limited memory and speed. However, legacy Grbl v0.51 "in the branch called 'v0_51' is still available for use. _The project was initially inspired by the Arduino GCode Interpreter by Mike Ellery_ diff --git a/serial.c b/serial.c index 7310e3f..8aa9419 100644 --- a/serial.c +++ b/serial.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,15 +25,13 @@ #include #include #include "serial.h" +#include "config.h" +#include "stepper.h" +#include "nuts_bolts.h" +#include "protocol.h" - -#ifdef __AVR_ATmega328P__ -#define RX_BUFFER_SIZE 256 -#else -#define RX_BUFFER_SIZE 64 -#endif - -#define TX_BUFFER_SIZE 16 +#define RX_BUFFER_SIZE 128 +#define TX_BUFFER_SIZE 32 uint8_t rx_buffer[RX_BUFFER_SIZE]; uint8_t rx_buffer_head = 0; @@ -44,25 +43,25 @@ volatile uint8_t tx_buffer_tail = 0; static void set_baud_rate(long baud) { uint16_t UBRR0_value = ((F_CPU / 16 + baud / 2) / baud - 1); - UBRR0H = UBRR0_value >> 8; - UBRR0L = UBRR0_value; + UBRR0H = UBRR0_value >> 8; + UBRR0L = UBRR0_value; } void serial_init(long baud) { set_baud_rate(baud); - /* baud doubler off - Only needed on Uno XXX */ + /* baud doubler off - Only needed on Uno XXX */ UCSR0A &= ~(1 << U2X0); - // enable rx and tx + // enable rx and tx UCSR0B |= 1< #include -#define GRBL_VERSION "0.7d" +#define GRBL_VERSION "0.8a" // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom diff --git a/spindle_control.c b/spindle_control.c index 0321c89..cd99c9a 100644 --- a/spindle_control.c +++ b/spindle_control.c @@ -20,11 +20,14 @@ #include "spindle_control.h" #include "settings.h" +#include "motion_control.h" #include "config.h" -#include "stepper.h" +#include "planner.h" #include +// TODO: Deprecated. Need to update for new version. + static int current_direction; void spindle_init() @@ -35,7 +38,7 @@ void spindle_init() void spindle_run(int direction, uint32_t rpm) { if (direction != current_direction) { - st_synchronize(); + plan_synchronize(); if(direction) { if(direction > 0) { SPINDLE_DIRECTION_PORT &= ~(1<> 3); // Enable steppers by resetting the stepper disable port STEPPERS_DISABLE_PORT &= ~(1<initial_rate; - min_safe_rate = current_block->rate_delta + (current_block->rate_delta >> 1); // 1.5 x rate_delta - trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule. - set_step_events_per_minute(trapezoid_adjusted_rate); // Initialize cycles_per_step_event -} - // This function determines an acceleration velocity change every CYCLES_PER_ACCELERATION_TICK by // keeping track of the number of elapsed cycles during a de/ac-celeration. The code assumes that // step_events occur significantly more often than the acceleration velocity iterations. -static uint8_t iterate_trapezoid_cycle_counter() { - trapezoid_tick_cycle_counter += cycles_per_step_event; - if(trapezoid_tick_cycle_counter > CYCLES_PER_ACCELERATION_TICK) { - trapezoid_tick_cycle_counter -= CYCLES_PER_ACCELERATION_TICK; +static uint8_t iterate_trapezoid_cycle_counter() +{ + st.trapezoid_tick_cycle_counter += st.cycles_per_step_event; + if(st.trapezoid_tick_cycle_counter > CYCLES_PER_ACCELERATION_TICK) { + st.trapezoid_tick_cycle_counter -= CYCLES_PER_ACCELERATION_TICK; return(true); } else { return(false); @@ -129,36 +137,48 @@ static uint8_t iterate_trapezoid_cycle_counter() { // config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. // It is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port after each pulse. // The bresenham line tracer algorithm controls all three stepper outputs simultaneously with these two interrupts. -SIGNAL(TIMER1_COMPA_vect) +// NOTE: ISR_NOBLOCK allows SIG_OVERFLOW2 to trigger on-time regardless of time in this handler. + +// TODO: ISR_NOBLOCK is the same as the old SIGNAL with sei() method, but is optimizable by the compiler. On +// an oscilloscope there is a weird hitch in the step pulse during high load operation. Very infrequent, but +// when this does happen most of the time the pulse falling edge is randomly delayed by 20%-50% of the total +// intended pulse time, but sometimes it pulses less than 3usec. The former likely caused by the serial +// interrupt doing its thing, not that big of a deal, but the latter cause is unknown and worrisome. Need +// to track down what is causing this problem. Functionally, this shouldn't cause any noticeable issues +// as long as stepper drivers have a pulse minimum of 1usec or so (Pololu and any Allegro IC are ok). + +ISR(TIMER1_COMPA_vect,ISR_NOBLOCK) { - // TODO: Check if the busy-flag can be eliminated by just disabling this interrupt while we are in it + if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt + busy = true; - if(busy){ return; } // The busy-flag is used to avoid reentering this interrupt // Set the direction pins a couple of nanoseconds before we step the steppers STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK); // Then pulse the stepping pins STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits; // Reset step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after // exactly settings.pulse_microseconds microseconds. -// TCNT2 = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND)/8); - TCNT2 = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3); // Bit shift divide by 8. - - busy = true; - sei(); // Re enable interrupts (normally disabled while inside an interrupt handler) - // ((We re-enable interrupts in order for SIG_OVERFLOW2 to be able to be triggered - // at exactly the right time even if we occasionally spend a lot of time inside this handler.)) + TCNT2 = step_pulse_time; // If there is no current block, attempt to pop one from the buffer if (current_block == NULL) { - // Anything in the buffer? + // Anything in the buffer? If so, initialize next motion. current_block = plan_get_current_block(); if (current_block != NULL) { - trapezoid_generator_reset(); - counter_x = -(current_block->step_event_count >> 1); - counter_y = counter_x; - counter_z = counter_x; - step_events_completed = 0; + if (!st.feed_hold) { // During feed hold, do not update rate and trap counter. Keep decelerating. + st.trapezoid_adjusted_rate = current_block->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. + } + st.min_safe_rate = current_block->rate_delta + (current_block->rate_delta >> 1); // 1.5 x rate_delta + 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_completed = 0; } else { + st.cycle_start = false; + st.feed_hold = false; st_go_idle(); } } @@ -166,139 +186,167 @@ SIGNAL(TIMER1_COMPA_vect) if (current_block != NULL) { // Execute step displacement profile by bresenham line algorithm out_bits = current_block->direction_bits; - counter_x += current_block->steps_x; - if (counter_x > 0) { + st.counter_x += current_block->steps_x; + if (st.counter_x > 0) { out_bits |= (1<step_event_count; + st.counter_x -= st.event_count; + if (out_bits & (1<steps_y; - if (counter_y > 0) { + st.counter_y += current_block->steps_y; + if (st.counter_y > 0) { out_bits |= (1<step_event_count; + st.counter_y -= st.event_count; + if (out_bits & (1<steps_z; - if (counter_z > 0) { + st.counter_z += current_block->steps_z; + if (st.counter_z > 0) { out_bits |= (1<step_event_count; + st.counter_z -= st.event_count; + if (out_bits & (1<step_event_count) { - - // The trapezoid generator always checks step event location to ensure de/ac-celerations are - // executed and terminated at exactly the right time. This helps prevent over/under-shooting - // the target position and speed. - - // NOTE: By increasing the ACCELERATION_TICKS_PER_SECOND in config.h, the resolution of the - // discrete velocity changes increase and accuracy can increase as well to a point. Numerical - // round-off errors can effect this, if set too high. This is important to note if a user has - // very high acceleration and/or feedrate requirements for their machine. - - if (step_events_completed < current_block->accelerate_until) { - // Iterate cycle counter and check if speeds need to be increased. - if ( iterate_trapezoid_cycle_counter() ) { - trapezoid_adjusted_rate += current_block->rate_delta; - if (trapezoid_adjusted_rate >= current_block->nominal_rate) { - // Reached nominal rate a little early. Cruise at nominal rate until decelerate_after. - trapezoid_adjusted_rate = current_block->nominal_rate; - } - set_step_events_per_minute(trapezoid_adjusted_rate); - } - } else if (step_events_completed >= current_block->decelerate_after) { - // Reset trapezoid tick cycle counter to make sure that the deceleration is performed the - // same every time. Reset to CYCLES_PER_ACCELERATION_TICK/2 to follow the midpoint rule for - // an accurate approximation of the deceleration curve. - if (step_events_completed == current_block-> decelerate_after) { - trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; - } else { - // Iterate cycle counter and check if speeds need to be reduced. - if ( iterate_trapezoid_cycle_counter() ) { - // NOTE: We will only do a full speed reduction if the result is more than the minimum safe - // rate, initialized in trapezoid reset as 1.5 x rate_delta. Otherwise, reduce the speed by - // half increments until finished. The half increments are guaranteed not to exceed the - // CNC acceleration limits, because they will never be greater than rate_delta. This catches - // small errors that might leave steps hanging after the last trapezoid tick or a very slow - // step rate at the end of a full stop deceleration in certain situations. The half rate - // reductions should only be called once or twice per block and create a nice smooth - // end deceleration. - if (trapezoid_adjusted_rate > min_safe_rate) { - trapezoid_adjusted_rate -= current_block->rate_delta; - } else { - trapezoid_adjusted_rate >>= 1; // Bit shift divide by 2 - } - if (trapezoid_adjusted_rate < current_block->final_rate) { - // Reached final rate a little early. Cruise to end of block at final rate. - trapezoid_adjusted_rate = current_block->final_rate; - } - set_step_events_per_minute(trapezoid_adjusted_rate); - } + if (st.step_events_completed < current_block->step_event_count) { + if (st.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 + // deceleration spans more than one block, the initial rate of the following blocks are not + // updated and deceleration is continued according to their corresponding rate_delta. + // NOTE: The trapezoid tick cycle counter is not updated intentionally. This ensures that + // the deceleration is smooth regardless of where the feed hold is initiated and if the + // deceleration distance spans multiple blocks. + if ( iterate_trapezoid_cycle_counter() ) { + // 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; + st_go_idle(); + sys_state |= BIT_REPLAN_CYCLE; // 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); + } } + } else { - // No accelerations. Make sure we cruise exactly at the nominal rate. - if (trapezoid_adjusted_rate != current_block->nominal_rate) { - trapezoid_adjusted_rate = current_block->nominal_rate; - set_step_events_per_minute(trapezoid_adjusted_rate); + // The trapezoid generator always checks step event location to ensure de/ac-celerations are + // executed and terminated at exactly the right time. This helps prevent over/under-shooting + // the target position and speed. + // NOTE: By increasing the ACCELERATION_TICKS_PER_SECOND in config.h, the resolution of the + // discrete velocity changes increase and accuracy can increase as well to a point. Numerical + // round-off errors can effect this, if set too high. This is important to note if a user has + // very high acceleration and/or feedrate requirements for their machine. + if (st.step_events_completed < current_block->accelerate_until) { + // Iterate cycle counter and check if speeds need to be increased. + if ( iterate_trapezoid_cycle_counter() ) { + st.trapezoid_adjusted_rate += current_block->rate_delta; + if (st.trapezoid_adjusted_rate >= current_block->nominal_rate) { + // Reached nominal rate a little early. Cruise at nominal rate until decelerate_after. + st.trapezoid_adjusted_rate = current_block->nominal_rate; + } + set_step_events_per_minute(st.trapezoid_adjusted_rate); + } + } else if (st.step_events_completed >= current_block->decelerate_after) { + // Reset trapezoid tick cycle counter to make sure that the deceleration is performed the + // same every time. Reset to CYCLES_PER_ACCELERATION_TICK/2 to follow the midpoint rule for + // an accurate approximation of the deceleration curve. + if (st.step_events_completed == current_block-> decelerate_after) { + st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; + } else { + // Iterate cycle counter and check if speeds need to be reduced. + if ( iterate_trapezoid_cycle_counter() ) { + // NOTE: We will only do a full speed reduction if the result is more than the minimum safe + // rate, initialized in trapezoid reset as 1.5 x rate_delta. Otherwise, reduce the speed by + // half increments until finished. The half increments are guaranteed not to exceed the + // CNC acceleration limits, because they will never be greater than rate_delta. This catches + // small errors that might leave steps hanging after the last trapezoid tick or a very slow + // step rate at the end of a full stop deceleration in certain situations. The half rate + // reductions should only be called once or twice per block and create a nice smooth + // end deceleration. + if (st.trapezoid_adjusted_rate > st.min_safe_rate) { + st.trapezoid_adjusted_rate -= current_block->rate_delta; + } else { + st.trapezoid_adjusted_rate >>= 1; // Bit shift divide by 2 + } + if (st.trapezoid_adjusted_rate < current_block->final_rate) { + // Reached final rate a little early. Cruise to end of block at final rate. + st.trapezoid_adjusted_rate = current_block->final_rate; + } + set_step_events_per_minute(st.trapezoid_adjusted_rate); + } + } + } else { + // No accelerations. Make sure we cruise exactly at the nominal rate. + if (st.trapezoid_adjusted_rate != current_block->nominal_rate) { + st.trapezoid_adjusted_rate = current_block->nominal_rate; + set_step_events_per_minute(st.trapezoid_adjusted_rate); + } } - } - + } } else { // If current block is finished, reset pointer current_block = NULL; plan_discard_current_block(); } - } - out_bits ^= settings.invert_mask; // Apply stepper invert mask - busy=false; + busy = false; } // This interrupt is set up by SIG_OUTPUT_COMPARE1A when it sets the motor port bits. It resets // the motor port after a short period (settings.pulse_microseconds) completing one step cycle. -SIGNAL(TIMER2_OVF_vect) +ISR(TIMER2_OVF_vect) { - // reset stepping pins (leave the direction pins) + // Reset stepping pins (leave the direction pins) STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK); } +// Reset and clear stepper subsystem variables +void st_reset() +{ + memset(&st, 0, sizeof(st)); + set_step_events_per_minute(MINIMUM_STEPS_PER_MINUTE); + current_block = NULL; + busy = false; +} + // Initialize and start the stepper motor subsystem void st_init() { - // Configure directions of interface pins - STEPPING_DDR |= STEPPING_MASK; + // Configure directions of interface pins + STEPPING_DDR |= STEPPING_MASK; STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask; STEPPERS_DISABLE_DDR |= 1<> 3; prescaler = 1; // prescaler: 8 actual_cycles = ceiling * 8L; - } else if (cycles <= 0x3fffffL) { - ceiling = cycles >> 6; + } else if (cycles <= 0x3fffffL) { + ceiling = cycles >> 6; prescaler = 2; // prescaler: 64 actual_cycles = ceiling * 64L; - } else if (cycles <= 0xffffffL) { - ceiling = (cycles >> 8); + } else if (cycles <= 0xffffffL) { + ceiling = (cycles >> 8); prescaler = 3; // prescaler: 256 actual_cycles = ceiling * 256L; - } else if (cycles <= 0x3ffffffL) { - ceiling = (cycles >> 10); + } else if (cycles <= 0x3ffffffL) { + ceiling = (cycles >> 10); prescaler = 4; // prescaler: 1024 actual_cycles = ceiling * 1024L; - } else { - // Okay, that was slower than we actually go. Just set the slowest speed - ceiling = 0xffff; + } else { + // Okay, that was slower than we actually go. Just set the slowest speed + ceiling = 0xffff; prescaler = 4; actual_cycles = 0xffff * 1024; - } - // Set prescaler + } + // Set prescaler TCCR1B = (TCCR1B & ~(0x07<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. +} diff --git a/stepper.h b/stepper.h index 73d3e7c..1bf8e6b 100644 --- a/stepper.h +++ b/stepper.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 @@ -29,16 +30,22 @@ #define DIRECTION_MASK ((1< Date: Fri, 6 Jan 2012 10:10:41 -0700 Subject: [PATCH 02/55] Position reporting, refactored system variables, serial print fixes, updated streaming scripts. - Added machine position reporting to status queries. This will be further developed with part positioning/offsets and maintaining location upon reset. - System variables refactored into a global struct for better readability. - Removed old obsolete Ruby streaming scripts. These were no longer compatible. Updated Python streaming scripts. - Fixed printFloat() and other printing functions. - Decreased planner buffer back to 18 blocks and increased TX serial buffer to 64 bytes. Need the memory space for future developments. - Begun adding run-time modes to grbl, where block delete toggle, mm/in reporting modes, jog modes, etc can be set during runtime. Will be fleshed out and placed into EEPROM when everything is added. --- config.h | 55 +++++---- gcode.c | 2 - main.c | 52 ++++----- motion_control.c | 24 ++-- nuts_bolts.h | 49 +++++--- planner.c | 4 +- planner.h | 2 +- print.c | 112 ++++++++++++------- print.h | 3 +- protocol.c | 87 ++++++++++---- script/{ => Obsolete}/console | 0 script/{ => Obsolete}/proxy | 0 script/{ => Obsolete}/stream | 0 script/{ => Obsolete}/stream.rb | 0 script/{ => Obsolete}/trapezoid_simulator.rb | 0 script/simple_stream.py | 9 ++ script/stream.py | 80 +++++++++++++ serial.c | 14 +-- settings.c | 16 ++- settings.h | 1 - stepper.c | 80 +++++++------ 21 files changed, 396 insertions(+), 194 deletions(-) rename script/{ => Obsolete}/console (100%) rename script/{ => Obsolete}/proxy (100%) rename script/{ => Obsolete}/stream (100%) rename script/{ => Obsolete}/stream.rb (100%) rename script/{ => Obsolete}/trapezoid_simulator.rb (100%) create mode 100755 script/stream.py 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. } + + From f40078110eda6b8939fdda8d38d2941deec7dd71 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Mon, 9 Jan 2012 18:51:53 -0700 Subject: [PATCH 03/55] Updated line in streaming script. --- script/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/stream.py b/script/stream.py index 32971ff..b614549 100755 --- a/script/stream.py +++ b/script/stream.py @@ -59,7 +59,7 @@ for line in f: 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'] : + if out_temp.find('ok') < 0 and out_temp.find('error') < 0 : print " Debug: ",out_temp # Debug response else : grbl_out += out_temp; From 6f27e2cdb1625657ccb8a96c3fbc9bd637f29170 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Mon, 9 Jan 2012 21:41:02 -0700 Subject: [PATCH 04/55] Corrected a minor streaming script character counting bug. --- script/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/stream.py b/script/stream.py index b614549..0c91a90 100755 --- a/script/stream.py +++ b/script/stream.py @@ -55,7 +55,7 @@ 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 + c_line.append(len(l_block)+1) # 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 From 89a3b37e020aa4e657973baadb0ff0936aa25656 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Tue, 10 Jan 2012 08:34:48 -0700 Subject: [PATCH 05/55] Extended position reporting with both home and work coordinates. Home position now retained after reset. Other minor changes/fixes. - Grbl now tracks both home and work (G92) coordinate systems and does live updates when G92 is called. - Rudimentary home and work position status reporting. Works but still under major construction. - Updated the main streaming script. Has a disabled periodic timer for querying status reports, disabled only because the Python timer doesn't consistently restart after the script exits. Add here only for user testing. - Fixed a bug to prevent an endless serial_write loop during status reports. - Refactored the planner variables to make it more clear what they are and make it easier for clear them. --- limits.c | 3 +- main.c | 16 +++++++-- motion_control.c | 5 ++- nuts_bolts.h | 7 ++-- planner.c | 91 +++++++++++++++++++++++++----------------------- protocol.c | 40 ++++++++++++--------- script/stream.py | 10 +++++- serial.c | 3 +- stepper.c | 2 +- 9 files changed, 106 insertions(+), 71 deletions(-) diff --git a/limits.c b/limits.c index ec0b432..a5c4a3e 100644 --- a/limits.c +++ b/limits.c @@ -27,7 +27,8 @@ #include "motion_control.h" #include "planner.h" -// TODO: Deprecated. Need to update for new version. +// TODO: Deprecated. Need to update for new version. Sys.position now tracks position relative +// to the home position. Limits should update this vector directly. void limits_init() { LIMIT_DDR &= ~(LIMIT_MASK); diff --git a/main.c b/main.c index 17732ac..0ddd410 100644 --- a/main.c +++ b/main.c @@ -43,6 +43,7 @@ int main(void) serial_init(BAUD_RATE); // Setup serial baud rate and interrupts st_init(); // Setup stepper pins and interrupt timers + memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization while(1) { @@ -51,10 +52,21 @@ int main(void) // 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) { - + + // Retain last known machine position. If the system abort occurred while in motion, machine + // position is not guaranteed, since a hard stop can cause the steppers to lose steps. Always + // perform a feedhold before an abort, if maintaining accurate machine position is required. + int32_t last_position[3]; + memcpy(last_position, sys.position, sizeof(sys.position)); // last_position[] = sys.position[] + // Clear all system variables memset(&sys, 0, sizeof(sys)); - + + // Update last known machine position. Set the post-abort work position as the origin [0,0,0], + // which corresponds to the g-code parser and planner positions after re-initialization. + memcpy(sys.position, last_position, sizeof(last_position)); // sys.position[] = last_position[] + memcpy(sys.coord_offset, last_position, sizeof(last_position)); // sys.coord_offset[] = last_position[] + // Reset system. serial_reset_read_buffer(); // Clear serial read buffer settings_init(); // Load grbl settings from EEPROM diff --git a/motion_control.c b/motion_control.c index 12c929e..176accc 100644 --- a/motion_control.c +++ b/motion_control.c @@ -48,7 +48,10 @@ void mc_line(double x, double y, double z, double feed_rate, uint8_t invert_feed { // TODO: Backlash compensation may be installed here. Only need direction info to track when // to insert a backlash line motion(s) before the intended line motion. Requires its own - // plan_check_full_buffer() and check for system abort loop. + // plan_check_full_buffer() and check for system abort loop. Also for position reporting + // backlash steps will need to be also tracked. Not sure what the best strategy is for this, + // i.e. keep the planner independent and do the computations in the status reporting, or let + // the planner handle the position corrections. The latter may get complicated. // If the buffer is full: good! That means we are well ahead of the robot. // Remain in this loop until there is room in the buffer. diff --git a/nuts_bolts.h b/nuts_bolts.h index edbb96c..f1b867f 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -68,9 +68,10 @@ typedef struct { 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. + int32_t position[3]; // Real-time machine (aka home) position vector in steps. + // NOTE: This may need to be a volatile variable, if problems arise. + int32_t coord_offset[3]; // Retains the G92 coordinate offset (work coordinates) relative to + // machine zero in steps. 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. diff --git a/planner.c b/planner.c index 772e168..6e09945 100644 --- a/planner.c +++ b/planner.c @@ -41,11 +41,15 @@ static volatile uint8_t block_buffer_head; // Index of the next block to b static volatile uint8_t block_buffer_tail; // Index of the block to process now static uint8_t next_buffer_head; // Index of the next buffer head -static int32_t position[3]; // The planner position of the tool in absolute steps -// static int32_t coord_offset[3]; // Current coordinate offset from machine zero in absolute steps -static double previous_unit_vec[3]; // Unit vector of previous path line segment -static double previous_nominal_speed; // Nominal speed of previous path line segment - +// Define planner variables +typedef struct { + int32_t position[3]; // The planner position of the tool in absolute steps. Kept separate + // from g-code position for movements requiring multiple line motions, + // i.e. arcs, canned cycles, and backlash compensation. + double previous_unit_vec[3]; // Unit vector of previous path line segment + double previous_nominal_speed; // Nominal speed of previous path line segment +} planner_t; +static planner_t pl; // Returns the index of the next block in the ring buffer // NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication. @@ -305,10 +309,7 @@ void plan_reset_buffer() void plan_init() { plan_reset_buffer(); - clear_vector(position); -// clear_vector(coord_offset); - clear_vector_double(previous_unit_vec); - previous_nominal_speed = 0.0; + memset(&pl, 0, sizeof(pl)); // Clear planner struct } void plan_discard_current_block() @@ -357,14 +358,14 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in // Compute direction bits for this block block->direction_bits = 0; - if (target[X_AXIS] < position[X_AXIS]) { block->direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<steps_x = labs(target[X_AXIS]-position[X_AXIS]); - block->steps_y = labs(target[Y_AXIS]-position[Y_AXIS]); - block->steps_z = labs(target[Z_AXIS]-position[Z_AXIS]); + block->steps_x = labs(target[X_AXIS]-pl.position[X_AXIS]); + block->steps_y = labs(target[Y_AXIS]-pl.position[Y_AXIS]); + block->steps_z = labs(target[Z_AXIS]-pl.position[Z_AXIS]); block->step_event_count = max(block->steps_x, max(block->steps_y, block->steps_z)); // Bail if this is a zero-length block @@ -372,9 +373,9 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in // Compute path vector in terms of absolute step target and current positions double delta_mm[3]; - delta_mm[X_AXIS] = (target[X_AXIS]-position[X_AXIS])/settings.steps_per_mm[X_AXIS]; - delta_mm[Y_AXIS] = (target[Y_AXIS]-position[Y_AXIS])/settings.steps_per_mm[Y_AXIS]; - delta_mm[Z_AXIS] = (target[Z_AXIS]-position[Z_AXIS])/settings.steps_per_mm[Z_AXIS]; + delta_mm[X_AXIS] = (target[X_AXIS]-pl.position[X_AXIS])/settings.steps_per_mm[X_AXIS]; + delta_mm[Y_AXIS] = (target[Y_AXIS]-pl.position[Y_AXIS])/settings.steps_per_mm[Y_AXIS]; + delta_mm[Z_AXIS] = (target[Z_AXIS]-pl.position[Z_AXIS])/settings.steps_per_mm[Z_AXIS]; block->millimeters = sqrt(square(delta_mm[X_AXIS]) + square(delta_mm[Y_AXIS]) + square(delta_mm[Z_AXIS])); double inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides @@ -419,16 +420,16 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. - if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { + if ((block_buffer_head != block_buffer_tail) && (pl.previous_nominal_speed > 0.0)) { // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. - double cos_theta = - previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] - - previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] - - previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; + double cos_theta = - pl.previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] + - pl.previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] + - pl.previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; // Skip and use default max junction speed for 0 degree acute junction. if (cos_theta < 0.95) { - vmax_junction = min(previous_nominal_speed,block->nominal_speed); + vmax_junction = min(pl.previous_nominal_speed,block->nominal_speed); // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. if (cos_theta > -0.95) { // Compute maximum junction velocity based on maximum acceleration and junction deviation @@ -457,15 +458,15 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in block->recalculate_flag = true; // Always calculate trapezoid for new block // Update previous path unit_vector and nominal speed - memcpy(previous_unit_vec, unit_vec, sizeof(unit_vec)); // previous_unit_vec[] = unit_vec[] - previous_nominal_speed = block->nominal_speed; + memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[] + pl.previous_nominal_speed = block->nominal_speed; // Update buffer head and next buffer head indices block_buffer_head = next_buffer_head; next_buffer_head = next_block_index(block_buffer_head); - // Update position - memcpy(position, target, sizeof(target)); // position[] = target[] + // Update planner position + memcpy(pl.position, target, sizeof(target)); // pl.position[] = target[] planner_recalculate(); } @@ -473,22 +474,26 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in // Reset the planner position vector and planner speed void plan_set_current_position(double x, double y, double z) { - // Track the position offset from the initial position - // TODO: Need to make sure coord_offset is robust and/or needed. Can be used for a soft reset, - // where the machine position is retained after a system abort/reset. However, this is not - // correlated to the actual machine position after a soft reset and may not be needed. This could - // be left to a user interface to maintain. -// coord_offset[X_AXIS] += position[X_AXIS]; -// coord_offset[Y_AXIS] += position[Y_AXIS]; -// coord_offset[Z_AXIS] += position[Z_AXIS]; - position[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); - position[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); - position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); -// coord_offset[X_AXIS] -= position[X_AXIS]; -// coord_offset[Y_AXIS] -= position[Y_AXIS]; -// coord_offset[Z_AXIS] -= position[Z_AXIS]; - previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. - clear_vector_double(previous_unit_vec); + // To correlate status reporting work position correctly, the planner must force the steppers to + // empty the block buffer and synchronize with the planner, as the real-time machine position and + // the planner position at the end of the buffer are different. This will only be called with a + // G92 is executed, which typically is used only at the beginning of a g-code program. + // TODO: Find a robust way to avoid a planner synchronize, but may require a bit of ingenuity. + plan_synchronize(); + + // Update the system coordinate offsets from machine zero + sys.coord_offset[X_AXIS] += pl.position[X_AXIS]; + sys.coord_offset[Y_AXIS] += pl.position[Y_AXIS]; + sys.coord_offset[Z_AXIS] += pl.position[Z_AXIS]; + + memset(&pl, 0, sizeof(pl)); // Clear planner variables. Assume start from rest. + + pl.position[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); // Update planner position + pl.position[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); + pl.position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); + sys.coord_offset[X_AXIS] -= pl.position[X_AXIS]; + sys.coord_offset[Y_AXIS] -= pl.position[Y_AXIS]; + sys.coord_offset[Z_AXIS] -= pl.position[Z_AXIS]; } // Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail. diff --git a/protocol.c b/protocol.c index 7b812f3..ca80439 100644 --- a/protocol.c +++ b/protocol.c @@ -72,25 +72,31 @@ void protocol_status_report() // 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 (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? + // **Under construction** Bare-bones status report. Provides real-time machine position relative to + // the system power on location (0,0,0) and work coordinate position, updatable by the G92 command. + // The following are still needed: user setting of output units (mm|inch), compressed (non-human + // readable) data for interfaces?, save last known position in EEPROM?, code optimizations, solidify + // the reporting schemes, move to a separate .c file for easy user accessibility, and setting the + // home position by the user (likely through '$' setting interface). + // Successfully tested at a query rate of 10-20Hz while running a gauntlet of programs at various + // speeds. + int32_t print_position[3]; + memcpy(print_position,sys.position,sizeof(sys.position)); #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)); + printString("MPos: x"); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); + printString(",y"); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); + printString(",z"); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); + printString(" WPos: x"); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); + printString(",y"); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); + printString(",z"); printFloat((print_position[Z_AXIS]-sys.coord_offset[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])); + printString("MPos: x"); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS])); + printString(",y"); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS])); + printString(",z"); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS])); + printString(" WPos: x"); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS])); + printString(",y"); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS])); + printString(",z"); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS])); #endif printString("\r\n"); } @@ -128,8 +134,8 @@ void protocol_execute_runtime() // Execute and serial print status if (rt_exec & EXEC_STATUS_REPORT) { - bit_false(sys.execute,EXEC_STATUS_REPORT); protocol_status_report(); + bit_false(sys.execute,EXEC_STATUS_REPORT); } // Initiate stepper feed hold diff --git a/script/stream.py b/script/stream.py index 0c91a90..10189fd 100755 --- a/script/stream.py +++ b/script/stream.py @@ -11,7 +11,7 @@ buffer layer to prevent buffer starvation. TODO: - Add runtime command capabilities -Version: SKJ.20120104 +Version: SKJ.20120110 """ import serial @@ -32,6 +32,13 @@ parser.add_argument('-q','--quiet',action='store_true', default=False, help='suppress output text') args = parser.parse_args() +# Periodic timer to query for status reports +# TODO: Need to track down why this doesn't restart consistently before a release. +# def periodic(): +# s.write('?') +# t = threading.Timer(0.1, periodic) # In seconds +# t.start() + # Initialize s = serial.Serial(args.device_file,9600) f = args.gcode_file @@ -51,6 +58,7 @@ print "Streaming ", args.gcode_file.name, " to ", args.device_file l_count = 0 g_count = 0 c_line = [] +# periodic() # Start status report periodic timer for line in f: l_count += 1 # Iterate line counter # l_block = re.sub('\s|\(.*?\)','',line).upper() # Strip comments/spaces/new line and capitalize diff --git a/serial.c b/serial.c index 87aab50..8a20223 100644 --- a/serial.c +++ b/serial.c @@ -71,8 +71,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.execute & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop. } // Store data and advance head diff --git a/stepper.c b/stepper.c index 3278c89..248c7e6 100644 --- a/stepper.c +++ b/stepper.c @@ -139,7 +139,7 @@ static uint8_t iterate_trapezoid_cycle_counter() // interrupt doing its thing, not that big of a deal, but the latter cause is unknown and worrisome. Need // to track down what is causing this problem. Functionally, this shouldn't cause any noticeable issues // as long as stepper drivers have a pulse minimum of 1usec or so (Pololu and any Allegro IC are ok). -// This seems to be an inherent issue that dates all the way back to Simen's v0.6b. +// ** This seems to be an inherent issue that dates all the way back to Simen's v0.6b or earlier. ** ISR(TIMER1_COMPA_vect,ISR_NOBLOCK) { From d27dd13a54badd0f8ecb21b3c06dbde7a2ea8cd3 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sun, 15 Jan 2012 18:25:12 -0700 Subject: [PATCH 06/55] Fix bug with premature step end. Refactored _delay_ms() and square() for better portability. - Fixed a premature step end bug dating back to Simen's 0.7b edge version is fixed, from which this code is forked from. Caused by Timer2 constantly overflowing calling the Step Reset Interrupt every 128usec. Now Timer2 is always disabled after a step end and should free up some cycles for the main program. Could be more than one way to fix this problem. I'm open to suggestions. - _delay_ms() refactored to accept only constants to comply with current compilers. square() removed since not available with some compilers. --- config.h | 2 +- main.c | 2 +- motion_control.c | 2 +- nuts_bolts.c | 8 ++++++++ nuts_bolts.h | 3 +++ planner.c | 11 ++++++----- script/stream.py | 1 + stepper.c | 38 ++++++++++++++++++-------------------- 8 files changed, 39 insertions(+), 28 deletions(-) diff --git a/config.h b/config.h index ac68acb..9f44602 100644 --- a/config.h +++ b/config.h @@ -108,7 +108,7 @@ // this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of // run-time command executions, like status reports, since these are performed between each dwell // 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) +#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) // ----------------------------------------------- diff --git a/main.c b/main.c index 0ddd410..06b03b8 100644 --- a/main.c +++ b/main.c @@ -39,9 +39,9 @@ system_t sys; int main(void) { // Initialize system - sei(); // Enable interrupts serial_init(BAUD_RATE); // Setup serial baud rate and interrupts st_init(); // Setup stepper pins and interrupt timers + sei(); // Enable interrupts memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization diff --git a/motion_control.c b/motion_control.c index 176accc..eba3346 100644 --- a/motion_control.c +++ b/motion_control.c @@ -182,7 +182,7 @@ void mc_dwell(double seconds) { uint16_t i = floor(1000/DWELL_TIME_STEP*seconds); plan_synchronize(); - _delay_ms(floor(1000*seconds-i*DWELL_TIME_STEP)); // Delay millisecond remainder + delay_ms(floor(1000*seconds-i*DWELL_TIME_STEP)); // Delay millisecond remainder while (i > 0) { // NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds. protocol_execute_runtime(); diff --git a/nuts_bolts.c b/nuts_bolts.c index 8a6960c..5e018f7 100644 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -21,6 +21,7 @@ #include "nuts_bolts.h" #include #include +#include int read_double(char *line, uint8_t *char_counter, double *double_ptr) { @@ -36,3 +37,10 @@ int read_double(char *line, uint8_t *char_counter, double *double_ptr) return(true); } + +// Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(), +// which only accepts constants in future compiler releases. +void delay_ms(uint16_t ms) +{ + while ( ms-- ) { _delay_ms(1); } +} \ No newline at end of file diff --git a/nuts_bolts.h b/nuts_bolts.h index f1b867f..945129c 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -83,4 +83,7 @@ extern system_t sys; // a pointer to the result variable. Returns true when it succeeds int read_double(char *line, uint8_t *char_counter, double *double_ptr); +// Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms(). +void delay_ms(uint16_t ms); + #endif diff --git a/planner.c b/planner.c index 6e09945..579116f 100644 --- a/planner.c +++ b/planner.c @@ -376,8 +376,8 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in delta_mm[X_AXIS] = (target[X_AXIS]-pl.position[X_AXIS])/settings.steps_per_mm[X_AXIS]; delta_mm[Y_AXIS] = (target[Y_AXIS]-pl.position[Y_AXIS])/settings.steps_per_mm[Y_AXIS]; delta_mm[Z_AXIS] = (target[Z_AXIS]-pl.position[Z_AXIS])/settings.steps_per_mm[Z_AXIS]; - block->millimeters = sqrt(square(delta_mm[X_AXIS]) + square(delta_mm[Y_AXIS]) + - square(delta_mm[Z_AXIS])); + block->millimeters = sqrt(delta_mm[X_AXIS]*delta_mm[X_AXIS] + delta_mm[Y_AXIS]*delta_mm[Y_AXIS] + + delta_mm[Z_AXIS]*delta_mm[Z_AXIS]); double inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides // Calculate speed in mm/minute for each axis. No divide by zero due to previous checks. @@ -476,9 +476,10 @@ void plan_set_current_position(double x, double y, double z) { // To correlate status reporting work position correctly, the planner must force the steppers to // empty the block buffer and synchronize with the planner, as the real-time machine position and - // the planner position at the end of the buffer are different. This will only be called with a - // G92 is executed, which typically is used only at the beginning of a g-code program. - // TODO: Find a robust way to avoid a planner synchronize, but may require a bit of ingenuity. + // the planner position at the end of the buffer can be and are usually different. This function is + // only called with a G92, which typically is used only at the beginning of a g-code program or + // between different operations. + // TODO: Find a robust way to avoid a planner synchronize, but this may require a bit of ingenuity. plan_synchronize(); // Update the system coordinate offsets from machine zero diff --git a/script/stream.py b/script/stream.py index 10189fd..17cded3 100755 --- a/script/stream.py +++ b/script/stream.py @@ -19,6 +19,7 @@ import re import time import sys import argparse +# import threading RX_BUFFER_SIZE = 128 diff --git a/stepper.c b/stepper.c index 248c7e6..4ebda7b 100644 --- a/stepper.c +++ b/stepper.c @@ -130,30 +130,25 @@ static uint8_t iterate_trapezoid_cycle_counter() // config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. // It is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port after each pulse. // The bresenham line tracer algorithm controls all three stepper outputs simultaneously with these two interrupts. -// NOTE: ISR_NOBLOCK allows SIG_OVERFLOW2 to trigger on-time regardless of time in this handler. - -// TODO: ISR_NOBLOCK is the same as the old SIGNAL with sei() method, but is optimizable by the compiler. On -// an oscilloscope there is a weird hitch in the step pulse during high load operation. Very infrequent, but -// when this does happen most of the time the pulse falling edge is randomly delayed by 20%-50% of the total -// intended pulse time, but sometimes it pulses less than 3usec. The former likely caused by the serial -// interrupt doing its thing, not that big of a deal, but the latter cause is unknown and worrisome. Need -// to track down what is causing this problem. Functionally, this shouldn't cause any noticeable issues -// as long as stepper drivers have a pulse minimum of 1usec or so (Pololu and any Allegro IC are ok). -// ** This seems to be an inherent issue that dates all the way back to Simen's v0.6b or earlier. ** - -ISR(TIMER1_COMPA_vect,ISR_NOBLOCK) +ISR(TIMER1_COMPA_vect) { if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt - busy = true; // Set the direction pins a couple of nanoseconds before we step the steppers STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK); // Then pulse the stepping pins STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits; - // Reset step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after - // exactly settings.pulse_microseconds microseconds. - TCNT2 = step_pulse_time; - + // 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 = step_pulse_time; // Reload timer counter + TCCR2B = (1< Date: Tue, 17 Jan 2012 18:05:46 -0700 Subject: [PATCH 07/55] Update readme.textile --- readme.textile | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/readme.textile b/readme.textile index 4f0bb98..029acb3 100644 --- a/readme.textile +++ b/readme.textile @@ -10,27 +10,16 @@ Grbl includes full acceleration management with look ahead. That means the contr *Changelog for v0.8 from v0.7:* - *ALPHA STATUS*: Major structural overhaul to allow for multi-tasking events and new feature sets - - New run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status report (TBD) + - New run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status reporting - Controlled feed hold with deceleration to ensure no skipped steps and loss of location. - After feed hold, cycle accelerations are re-planned and may be resumed. - - System reset re-initializes grbl without resetting the Arduino. - - Updated dwell function. + - Work(G92) and machine coordinate system support. + - System reset re-initializes grbl without resetting the Arduino and retains machine/home position. - Restructured planner and stepper modules to become independent and ready for future features. - - Planned features: Jog mode, status reporting, backlash compensation, improved flow control, planner optimizations - - Reduce serial read buffer to 127 characters and increased write buffer to 31 characters (-1 ring buffer). - - Increased planner buffer size to 20 blocks. + - Planned features: Jog mode, status reporting, runtime settings such as toggling block delete. + - Reduce serial read buffer to 128 characters and increased write buffer to 64 characters. - Misc bug fixes and removed deprecated acceleration enabled code. -*Changelog for v0.7 from v0.6:* - - Significantly improved and optimized planner re-factoring. - - New robust cornering algorithm, enabling smoother and faster motions. - - Arc acceleration planning enabled by efficient vector transformation implementation. - - Stepper subsystem re-factoring to help remove some motion issues from pre-v0.7 builds. - - Increased dwell times. - - G92 Coordinate offset support. - - (Beta) Limit switch and homing cycle support. - - Many other bug fixes and efficiency improvements. - *Important note for Atmega 168 users:* Going forward, support for Atmega 168 will be dropped due to its limited memory and speed. However, legacy Grbl v0.51 "in the branch called 'v0_51' is still available for use. _The project was initially inspired by the Arduino GCode Interpreter by Mike Ellery_ From 0f0d5a6138b6a3edac20b150da318d5f637e4152 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Fri, 27 Jan 2012 19:51:16 -0700 Subject: [PATCH 08/55] Streaming script argparse bugfix. --- script/stream.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/stream.py b/script/stream.py index 17cded3..5b82c94 100755 --- a/script/stream.py +++ b/script/stream.py @@ -24,10 +24,10 @@ 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'), +parser = argparse.ArgumentParser(description='Stream g-code file to grbl. (pySerial and argparse libraries required)') +parser.add_argument('gcode_file', type=argparse.FileType('r'), help='g-code filename to be streamed') -parser.add_argument('device', +parser.add_argument('device_file', help='serial device path') parser.add_argument('-q','--quiet',action='store_true', default=False, help='suppress output text') From b51e90253011e8628643e3fd37103f5192e726a5 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sat, 28 Jan 2012 20:41:08 -0700 Subject: [PATCH 09/55] Program stop support (M0,M1*,M2,M30*), proper position retainment upon reset, misc minor updates. - Program stop support (M0,M1*,M2,M30*). *Optional stop to be done. *Pallet shuttle not supported. - Work position is set equal to machine position upon reset, as according to NIST RS274-NGC guidelines. G92 is disabled. - Renamed mc_set_current_position() to mc_set_coordinate_offset(). - Fixed bug in plan_synchronize(). Would exit right before last step is finished and caused issues with program stops. Now fixed. - Spindle now stops upon a run-time abort command. - Updated readme and misc upkeeping. --- config.h | 2 +- gcode.c | 376 +++++++++++++++++++++++++---------------------- gcode.h | 3 + main.c | 26 ++-- motion_control.c | 5 +- motion_control.h | 4 +- nuts_bolts.c | 4 +- nuts_bolts.h | 2 +- planner.c | 29 ++-- planner.h | 9 +- print.c | 2 +- print.h | 2 +- protocol.c | 28 ++-- protocol.h | 2 +- readme.textile | 10 +- serial.c | 6 +- stepper.c | 4 +- 17 files changed, 282 insertions(+), 232 deletions(-) diff --git a/config.h b/config.h index 9f44602..1e01df7 100644 --- a/config.h +++ b/config.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 diff --git a/gcode.c b/gcode.c index b9ca1f4..6a9372f 100644 --- a/gcode.c +++ b/gcode.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -49,7 +49,8 @@ #define PROGRAM_FLOW_RUNNING 0 #define PROGRAM_FLOW_PAUSED 1 -#define PROGRAM_FLOW_COMPLETED 2 +#define PROGRAM_FLOW_OPT_PAUSED 2 +#define PROGRAM_FLOW_COMPLETED 3 #define SPINDLE_DIRECTION_CW 0 #define SPINDLE_DIRECTION_CCW 1 @@ -84,7 +85,8 @@ static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2) gc.plane_axis_2 = axis_2; } -void gc_init() { +void gc_init() +{ memset(&gc, 0, sizeof(gc)); gc.feed_rate = settings.default_feed_rate; gc.seek_rate = settings.default_seek_rate; @@ -92,6 +94,14 @@ void gc_init() { gc.absolute_mode = true; } +// Set g-code parser position. Input in steps. +void gc_set_current_position(int32_t x, int32_t y, int32_t z) +{ + gc.position[X_AXIS] = x/settings.steps_per_mm[X_AXIS]; + gc.position[Y_AXIS] = y/settings.steps_per_mm[Y_AXIS]; + gc.position[Z_AXIS] = z/settings.steps_per_mm[Z_AXIS]; +} + static float to_millimeters(double value) { return(gc.inches_mode ? (value * MM_PER_INCH) : value); } @@ -99,7 +109,8 @@ static float to_millimeters(double value) { // Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase // characters and signed floating point values (no whitespace). Comments and block delete // characters have been removed. -uint8_t gc_execute_line(char *line) { +uint8_t gc_execute_line(char *line) +{ uint8_t char_counter = 0; char letter; double value; @@ -122,39 +133,39 @@ uint8_t gc_execute_line(char *line) { int_value = trunc(value); switch(letter) { case 'G': - switch(int_value) { - case 0: gc.motion_mode = MOTION_MODE_SEEK; break; - case 1: gc.motion_mode = MOTION_MODE_LINEAR; break; - case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break; - case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break; - case 4: next_action = NEXT_ACTION_DWELL; break; - case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break; - case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break; - case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; - case 20: gc.inches_mode = true; break; - case 21: gc.inches_mode = false; break; - case 28: case 30: next_action = NEXT_ACTION_GO_HOME; break; - case 53: absolute_override = true; break; - case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; - case 90: gc.absolute_mode = true; break; - case 91: gc.absolute_mode = false; break; - case 92: next_action = NEXT_ACTION_SET_COORDINATE_OFFSET; break; - case 93: gc.inverse_feed_rate_mode = true; break; - case 94: gc.inverse_feed_rate_mode = false; break; - default: FAIL(STATUS_UNSUPPORTED_STATEMENT); - } - break; - + switch(int_value) { + case 0: gc.motion_mode = MOTION_MODE_SEEK; break; + case 1: gc.motion_mode = MOTION_MODE_LINEAR; break; + case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break; + case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break; + case 4: next_action = NEXT_ACTION_DWELL; break; + case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break; + case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break; + case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; + case 20: gc.inches_mode = true; break; + case 21: gc.inches_mode = false; break; + case 28: case 30: next_action = NEXT_ACTION_GO_HOME; break; + case 53: absolute_override = true; break; + case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; + case 90: gc.absolute_mode = true; break; + case 91: gc.absolute_mode = false; break; + case 92: next_action = NEXT_ACTION_SET_COORDINATE_OFFSET; break; + case 93: gc.inverse_feed_rate_mode = true; break; + case 94: gc.inverse_feed_rate_mode = false; break; + default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + } + break; case 'M': - switch(int_value) { - case 0: case 1: gc.program_flow = PROGRAM_FLOW_PAUSED; break; - case 2: case 30: case 60: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; - case 3: gc.spindle_direction = 1; break; - case 4: gc.spindle_direction = -1; break; - case 5: gc.spindle_direction = 0; break; - default: FAIL(STATUS_UNSUPPORTED_STATEMENT); - } - break; + switch(int_value) { + case 0: case 60: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause + case 1: gc.program_flow = PROGRAM_FLOW_OPT_PAUSED; break; // Program pause with optional stop on + case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset + case 3: gc.spindle_direction = 1; break; + case 4: gc.spindle_direction = -1; break; + case 5: gc.spindle_direction = 0; break; + default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + } + break; case 'T': gc.tool = trunc(value); break; } if(gc.status_code) { break; } @@ -174,28 +185,28 @@ uint8_t gc_execute_line(char *line) { unit_converted_value = to_millimeters(value); switch(letter) { case 'F': - if (unit_converted_value <= 0) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // Must be greater than zero - if (gc.inverse_feed_rate_mode) { - inverse_feed_rate = unit_converted_value; // seconds per motion for this motion only - } else { - if (gc.motion_mode == MOTION_MODE_SEEK) { - gc.seek_rate = unit_converted_value; - } else { - gc.feed_rate = unit_converted_value; // millimeters per minute + if (unit_converted_value <= 0) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // Must be greater than zero + if (gc.inverse_feed_rate_mode) { + inverse_feed_rate = unit_converted_value; // seconds per motion for this motion only + } else { + if (gc.motion_mode == MOTION_MODE_SEEK) { + gc.seek_rate = unit_converted_value; + } else { + gc.feed_rate = unit_converted_value; // millimeters per minute + } } - } - break; + break; case 'I': case 'J': case 'K': offset[letter-'I'] = unit_converted_value; break; case 'P': p = value; break; case 'R': r = unit_converted_value; radius_mode = true; break; case 'S': gc.spindle_speed = value; break; case 'X': case 'Y': case 'Z': - if (gc.absolute_mode || absolute_override) { - target[letter - 'X'] = unit_converted_value; - } else { - target[letter - 'X'] += unit_converted_value; - } - break; + if (gc.absolute_mode || absolute_override) { + target[letter - 'X'] = unit_converted_value; + } else { + target[letter - 'X'] += unit_converted_value; + } + break; } } @@ -207,144 +218,161 @@ uint8_t gc_execute_line(char *line) { // Perform any physical actions switch (next_action) { - case NEXT_ACTION_GO_HOME: mc_go_home(); clear_vector(target); break; + case NEXT_ACTION_GO_HOME: mc_go_home(); clear_vector_double(target); break; case NEXT_ACTION_DWELL: mc_dwell(p); break; case NEXT_ACTION_SET_COORDINATE_OFFSET: - mc_set_current_position(target[X_AXIS], target[Y_AXIS], target[Z_AXIS]); - break; + mc_set_coordinate_offset(target[X_AXIS],target[Y_AXIS],target[Z_AXIS]); + break; case NEXT_ACTION_DEFAULT: - switch (gc.motion_mode) { - case MOTION_MODE_CANCEL: break; - case MOTION_MODE_SEEK: - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); - break; - case MOTION_MODE_LINEAR: - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], - (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); - break; - case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: - if (radius_mode) { - /* - We need to calculate the center of the circle that has the designated radius and passes - through both the current position and the target position. This method calculates the following - set of equations where [x,y] is the vector from current to target position, d == magnitude of - that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to - the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the - length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point - [i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc. - - d^2 == x^2 + y^2 - h^2 == r^2 - (d/2)^2 - i == x/2 - y/d*h - j == y/2 + x/d*h - - O <- [i,j] - - | - r - | - - | - - | h - - | - [0,0] -> C -----------------+--------------- T <- [x,y] - | <------ d/2 ---->| + switch (gc.motion_mode) { + case MOTION_MODE_CANCEL: break; + case MOTION_MODE_SEEK: + mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); + break; + case MOTION_MODE_LINEAR: + mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], + (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); + break; + case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: + if (radius_mode) { + /* + We need to calculate the center of the circle that has the designated radius and passes + through both the current position and the target position. This method calculates the following + set of equations where [x,y] is the vector from current to target position, d == magnitude of + that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to + the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the + length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point + [i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc. + + d^2 == x^2 + y^2 + h^2 == r^2 - (d/2)^2 + i == x/2 - y/d*h + j == y/2 + x/d*h + + O <- [i,j] + - | + r - | + - | + - | h + - | + [0,0] -> C -----------------+--------------- T <- [x,y] + | <------ d/2 ---->| + + C - Current position + T - Target position + O - center of circle that pass through both C and T + d - distance from C to T + r - designated radius + h - distance from center of CT to O + + Expanding the equations: + + d -> sqrt(x^2 + y^2) + h -> sqrt(4 * r^2 - x^2 - y^2)/2 + i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 + j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 + + Which can be written: + + i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 + j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 + + Which we for size and speed reasons optimize to: + + h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2) + i = (x - (y * h_x2_div_d))/2 + j = (y + (x * h_x2_div_d))/2 + + */ + + // Calculate the change in position along each selected axis + double x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0]; + double y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1]; + + clear_vector(offset); + double h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y); // == -(h * 2 / d) + // If r is smaller than d, the arc is now traversing the complex plane beyond the reach of any + // real CNC, and thus - for practical reasons - we will terminate promptly: + if(isnan(h_x2_div_d)) { FAIL(STATUS_FLOATING_POINT_ERROR); return(gc.status_code); } + // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below) + if (gc.motion_mode == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; } + + /* The counter clockwise circle lies to the left of the target direction. When offset is positive, + the left hand circle will be generated - when it is negative the right hand circle is generated. + + + T <-- Target position + + ^ + Clockwise circles with this center | Clockwise circles with this center will have + will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing! + \ | / + center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative + | + | + + C <-- Current position */ - C - Current position - T - Target position - O - center of circle that pass through both C and T - d - distance from C to T - r - designated radius - h - distance from center of CT to O + + // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!), + // even though it is advised against ever generating such circles in a single line of g-code. By + // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of + // travel and thus we get the unadvisably long arcs as prescribed. + if (r < 0) { + h_x2_div_d = -h_x2_div_d; + r = -r; // Finished with r. Set to positive for mc_arc + } + // Complete the operation by calculating the actual center of the arc + offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d)); + offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d)); + + } else { // Offset mode specific computations + + r = hypot(offset[gc.plane_axis_0], offset[gc.plane_axis_1]); // Compute arc radius for mc_arc + + } - Expanding the equations: - - d -> sqrt(x^2 + y^2) - h -> sqrt(4 * r^2 - x^2 - y^2)/2 - i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 - j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 - - Which can be written: - - i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 - j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 - - Which we for size and speed reasons optimize to: - - h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2) - i = (x - (y * h_x2_div_d))/2 - j = (y + (x * h_x2_div_d))/2 - - */ - - // Calculate the change in position along each selected axis - double x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0]; - double y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1]; - - clear_vector(offset); - double h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y); // == -(h * 2 / d) - // If r is smaller than d, the arc is now traversing the complex plane beyond the reach of any - // real CNC, and thus - for practical reasons - we will terminate promptly: - if(isnan(h_x2_div_d)) { FAIL(STATUS_FLOATING_POINT_ERROR); return(gc.status_code); } - // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below) - if (gc.motion_mode == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; } - - /* The counter clockwise circle lies to the left of the target direction. When offset is positive, - the left hand circle will be generated - when it is negative the right hand circle is generated. - - - T <-- Target position - - ^ - Clockwise circles with this center | Clockwise circles with this center will have - will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing! - \ | / - center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative - | - | - - C <-- Current position */ - - - // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!), - // even though it is advised against ever generating such circles in a single line of g-code. By - // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of - // travel and thus we get the unadvisably long arcs as prescribed. - if (r < 0) { - h_x2_div_d = -h_x2_div_d; - r = -r; // Finished with r. Set to positive for mc_arc - } - // Complete the operation by calculating the actual center of the arc - offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d)); - offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d)); - - } else { // Offset mode specific computations - - r = hypot(offset[gc.plane_axis_0], offset[gc.plane_axis_1]); // Compute arc radius for mc_arc - - } - - // Set clockwise/counter-clockwise sign for mc_arc computations - uint8_t isclockwise = false; - if (gc.motion_mode == MOTION_MODE_CW_ARC) { isclockwise = true; } - - // Trace the arc - mc_arc(gc.position, target, offset, gc.plane_axis_0, gc.plane_axis_1, gc.plane_axis_2, - (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode, - r, isclockwise); - - break; - } + // Set clockwise/counter-clockwise sign for mc_arc computations + uint8_t isclockwise = false; + if (gc.motion_mode == MOTION_MODE_CW_ARC) { isclockwise = true; } + + // Trace the arc + mc_arc(gc.position, target, offset, gc.plane_axis_0, gc.plane_axis_1, gc.plane_axis_2, + (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode, + r, isclockwise); + + break; + } } // As far as the parser is concerned, the position is now == target. In reality the // motion control system might still be processing the action and the real tool position // in any intermediate location. memcpy(gc.position, target, sizeof(double)*3); // gc.position[] = target[]; + + // Perform non-running program flow actions. During a program pause, the buffer may refill and can + // only be resumed by the cycle start run-time command. + // TODO: Install optional stop setting and re-factor program flow actions. + if (gc.program_flow) { + plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete. + sys.auto_start = false; // Disable auto cycle start. + switch (gc.program_flow) { + case PROGRAM_FLOW_PAUSED: case PROGRAM_FLOW_OPT_PAUSED: + gc.program_flow = PROGRAM_FLOW_RUNNING; // Re-enable program flow after pause complete. + break; + // Reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9) + case PROGRAM_FLOW_COMPLETED: sys.abort = true; break; + } + } + return(gc.status_code); } // Parses the next statement and leaves the counter on the first character following // the statement. Returns 1 if there was a statements, 0 if end of string was reached // or there was an error (check state.status_code). -static int next_statement(char *letter, double *double_ptr, char *line, uint8_t *char_counter) { +static int next_statement(char *letter, double *double_ptr, char *line, uint8_t *char_counter) +{ if (line[*char_counter] == 0) { return(0); // No more statements } @@ -375,7 +403,7 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t - Probing - Override control - group 0 = {G10, G28, G30, G92, G92.1, G92.2, G92.3} (Non modal G-codes) + group 0 = {G10, G28, G30, G92.1, G92.2, G92.3} (Non modal G-codes) group 8 = {M7, M8, M9} coolant (special case: M7 and M8 may be active at the same time) group 9 = {M48, M49} enable/disable feed and speed override switches group 12 = {G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection diff --git a/gcode.h b/gcode.h index 7ad4e0e..af9751a 100644 --- a/gcode.h +++ b/gcode.h @@ -29,4 +29,7 @@ void gc_init(); // Execute one block of rs275/ngc/g-code uint8_t gc_execute_line(char *line); +// Set g-code parser position. Input in steps. +void gc_set_current_position(int32_t x, int32_t y, int32_t z); + #endif diff --git a/main.c b/main.c index 06b03b8..40441b8 100644 --- a/main.c +++ b/main.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -46,7 +46,7 @@ int main(void) memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization - while(1) { + for(;;) { // 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 @@ -56,18 +56,15 @@ int main(void) // Retain last known machine position. If the system abort occurred while in motion, machine // position is not guaranteed, since a hard stop can cause the steppers to lose steps. Always // perform a feedhold before an abort, if maintaining accurate machine position is required. - int32_t last_position[3]; + // TODO: Report last position and coordinate offset to users to help relocate origins. Future + // releases will auto-reset the machine position back to [0,0,0] if an abort is used while + // grbl is moving the machine. + int32_t last_position[3]; // last_coord_offset[3]; memcpy(last_position, sys.position, sizeof(sys.position)); // last_position[] = sys.position[] - - // Clear all system variables - memset(&sys, 0, sizeof(sys)); - - // Update last known machine position. Set the post-abort work position as the origin [0,0,0], - // which corresponds to the g-code parser and planner positions after re-initialization. - memcpy(sys.position, last_position, sizeof(last_position)); // sys.position[] = last_position[] - memcpy(sys.coord_offset, last_position, sizeof(last_position)); // sys.coord_offset[] = last_position[] - + // memcpy(last_coord_offset, sys.coord_offset, sizeof(sys.coord_offset)); // last_coord_offset[] = sys.coord_offset[] + // Reset system. + memset(&sys, 0, sizeof(sys)); // Clear all system variables serial_reset_read_buffer(); // Clear serial read buffer settings_init(); // Load grbl settings from EEPROM protocol_init(); // Clear incoming line data @@ -77,6 +74,11 @@ int main(void) limits_init(); st_reset(); // Clear stepper subsystem variables. + // Reload last known machine position. Coordinate offsets are reset per NIST RS274-NGC protocol. + memcpy(sys.position, last_position, sizeof(last_position)); // sys.position[] = last_position[] + gc_set_current_position(last_position[X_AXIS],last_position[Y_AXIS],last_position[Z_AXIS]); + plan_set_current_position(last_position[X_AXIS],last_position[Y_AXIS],last_position[Z_AXIS]); + // 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. diff --git a/motion_control.c b/motion_control.c index eba3346..8149689 100644 --- a/motion_control.c +++ b/motion_control.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 Sungeun K. Jeon Copyright (c) 2011 Jens Geisler Grbl is free software: you can redistribute it and/or modify @@ -183,12 +183,11 @@ void mc_dwell(double seconds) uint16_t i = floor(1000/DWELL_TIME_STEP*seconds); plan_synchronize(); delay_ms(floor(1000*seconds-i*DWELL_TIME_STEP)); // Delay millisecond remainder - while (i > 0) { + while (i-- > 0) { // NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds. protocol_execute_runtime(); if (sys.abort) { return; } _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment - i--; } } diff --git a/motion_control.h b/motion_control.h index 003893a..b5061e9 100644 --- a/motion_control.h +++ b/motion_control.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -27,7 +27,7 @@ // NOTE: Although the following function structurally belongs in this module, there is nothing to do but // to forward the request to the planner. -#define mc_set_current_position(x, y, z) plan_set_current_position(x, y, z) +#define mc_set_coordinate_offset(x, y, z) plan_set_coordinate_offset(x, y, z) // 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 diff --git a/nuts_bolts.c b/nuts_bolts.c index 5e018f7..83b4e40 100644 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -3,6 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011-2012 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 @@ -37,10 +38,9 @@ int read_double(char *line, uint8_t *char_counter, double *double_ptr) return(true); } - // Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(), // which only accepts constants in future compiler releases. void delay_ms(uint16_t ms) { while ( ms-- ) { _delay_ms(1); } -} \ No newline at end of file +} diff --git a/nuts_bolts.h b/nuts_bolts.h index 945129c..3c4112b 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 diff --git a/planner.c b/planner.c index 579116f..ff32b5e 100644 --- a/planner.c +++ b/planner.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 Sungeun K. Jeon Copyright (c) 2011 Jens Geisler Grbl is free software: you can redistribute it and/or modify @@ -312,14 +312,14 @@ void plan_init() memset(&pl, 0, sizeof(pl)); // Clear planner struct } -void plan_discard_current_block() +inline void plan_discard_current_block() { if (block_buffer_head != block_buffer_tail) { block_buffer_tail = next_block_index( block_buffer_tail ); } } -block_t *plan_get_current_block() +inline block_t *plan_get_current_block() { if (block_buffer_head == block_buffer_tail) { return(NULL); } return(&block_buffer[block_buffer_tail]); @@ -335,7 +335,7 @@ uint8_t plan_check_full_buffer() // Block until all buffered steps are executed. void plan_synchronize() { - while(plan_get_current_block()) { + while (plan_get_current_block() || sys.cycle_start) { protocol_execute_runtime(); // Check and execute run-time commands if (sys.abort) { return; } // Check for system abort } @@ -471,8 +471,8 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in planner_recalculate(); } -// Reset the planner position vector and planner speed -void plan_set_current_position(double x, double y, double z) +// Apply G92 coordinate offsets and update planner position vector. +void plan_set_coordinate_offset(double x, double y, double z) { // To correlate status reporting work position correctly, the planner must force the steppers to // empty the block buffer and synchronize with the planner, as the real-time machine position and @@ -489,14 +489,25 @@ void plan_set_current_position(double x, double y, double z) memset(&pl, 0, sizeof(pl)); // Clear planner variables. Assume start from rest. - pl.position[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); // Update planner position - pl.position[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); - pl.position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); + // Update planner position and coordinate offset vectors + int32_t new_position[3]; + new_position[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); + new_position[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); + new_position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); + plan_set_current_position(new_position[X_AXIS],new_position[Y_AXIS],new_position[Z_AXIS]); sys.coord_offset[X_AXIS] -= pl.position[X_AXIS]; sys.coord_offset[Y_AXIS] -= pl.position[Y_AXIS]; sys.coord_offset[Z_AXIS] -= pl.position[Z_AXIS]; } +// Reset the planner position vector (in steps) +void plan_set_current_position(int32_t x, int32_t y, int32_t z) +{ + pl.position[X_AXIS] = x; + pl.position[Y_AXIS] = y; + pl.position[Z_AXIS] = z; +} + // Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail. // Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped. void plan_cycle_reinitialize(int32_t step_events_remaining) diff --git a/planner.h b/planner.h index 451fdf9..95dbd78 100644 --- a/planner.h +++ b/planner.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -66,8 +66,11 @@ void plan_discard_current_block(); // Gets the current block. Returns NULL if buffer empty block_t *plan_get_current_block(); -// Reset the position vector -void plan_set_current_position(double x, double y, double z); +// Reset the planner position vector (in steps) +void plan_set_current_position(int32_t x, int32_t y, int32_t z); + +// Apply G92 coordinate offsets and update planner position vector. Called by g-code parser. +void plan_set_coordinate_offset(double x, double y, double z); // Reinitialize plan with a partially completed block void plan_cycle_reinitialize(int32_t step_events_remaining); diff --git a/print.c b/print.c index f509e88..83a8db5 100644 --- a/print.c +++ b/print.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 diff --git a/print.h b/print.h index ff4ea99..f0dff4c 100644 --- a/print.h +++ b/print.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 diff --git a/protocol.c b/protocol.c index ca80439..3f92d1e 100644 --- a/protocol.c +++ b/protocol.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -84,21 +84,21 @@ void protocol_status_report() int32_t print_position[3]; memcpy(print_position,sys.position,sizeof(sys.position)); #if REPORT_INCH_MODE - printString("MPos: x"); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); - printString(",y"); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); - printString(",z"); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); - printString(" WPos: x"); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); - printString(",y"); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); - printString(",z"); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); + printString("MPos:["); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); + printString(","); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); + printString(","); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); + printString("],WPos:["); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); + printString(","); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); + printString(","); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); #else - printString("MPos: x"); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS])); - printString(",y"); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS])); - printString(",z"); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS])); - printString(" WPos: x"); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS])); - printString(",y"); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS])); - printString(",z"); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS])); + printString("MPos:["); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS])); + printString(","); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS])); + printString(","); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS])); + printString("],WPos:["); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS])); + printString(","); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS])); + printString(","); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS])); #endif - printString("\r\n"); + printString("]\r\n"); } diff --git a/protocol.h b/protocol.h index 1ac66ae..ece1f1f 100644 --- a/protocol.h +++ b/protocol.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 diff --git a/readme.textile b/readme.textile index 029acb3..43b89f9 100644 --- a/readme.textile +++ b/readme.textile @@ -6,17 +6,19 @@ The controller is written in highly optimized C utilizing every clever feature o It accepts standards-compliant G-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as, other basic functional g-code commands. Functions and variables are not currently supported, but may be included in future releases in a form of a pre-processor. -Grbl includes full acceleration management with look ahead. That means the controller will look up to 20 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering. +Grbl includes full acceleration management with look ahead. That means the controller will look up to 18 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering. *Changelog for v0.8 from v0.7:* - - *ALPHA STATUS*: Major structural overhaul to allow for multi-tasking events and new feature sets + - *ALPHA status: _Under heavy development. Code state may significantly change with each push as new features are integrated._* + - Major structural overhaul to allow for multi-tasking events and new feature sets - New run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status reporting - Controlled feed hold with deceleration to ensure no skipped steps and loss of location. - After feed hold, cycle accelerations are re-planned and may be resumed. - - Work(G92) and machine coordinate system support. + - Work offset(G92) and machine coordinate system support. + - Program stop(M0,M1*,M2,M30) initial support. Pallet shuttle not supported. - System reset re-initializes grbl without resetting the Arduino and retains machine/home position. - Restructured planner and stepper modules to become independent and ready for future features. - - Planned features: Jog mode, status reporting, runtime settings such as toggling block delete. + - Planned features: Jog mode, status reporting, runtime settings such as toggling block delete, XON/XOFF flow control. - Reduce serial read buffer to 128 characters and increased write buffer to 64 characters. - Misc bug fixes and removed deprecated acceleration enabled code. diff --git a/serial.c b/serial.c index 8a20223..8d385bf 100644 --- a/serial.c +++ b/serial.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -27,6 +27,7 @@ #include "serial.h" #include "config.h" #include "stepper.h" +#include "spindle_control.h" #include "nuts_bolts.h" #include "protocol.h" @@ -127,9 +128,10 @@ ISR(USART_RX_vect) 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. + // Immediately force stepper and spindle subsystem idle at an interrupt level. if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. st_go_idle(); + spindle_stop(); } sys.execute |= EXEC_RESET; // Set as true break; diff --git a/stepper.c b/stepper.c index 4ebda7b..9da4120 100644 --- a/stepper.c +++ b/stepper.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -115,7 +115,7 @@ void st_go_idle() // This function determines an acceleration velocity change every CYCLES_PER_ACCELERATION_TICK by // keeping track of the number of elapsed cycles during a de/ac-celeration. The code assumes that // step_events occur significantly more often than the acceleration velocity iterations. -static uint8_t iterate_trapezoid_cycle_counter() +inline static uint8_t iterate_trapezoid_cycle_counter() { st.trapezoid_tick_cycle_counter += st.cycles_per_step_event; if(st.trapezoid_tick_cycle_counter > CYCLES_PER_ACCELERATION_TICK) { From 567fbf93ed6b8fb4e305c420e1ae5387547ff047 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sat, 11 Feb 2012 11:59:35 -0700 Subject: [PATCH 10/55] G54 work coordinate support (w/ G10,G92.1). Re-factored g-code parser with error checking. Minor compiler compatibility changes. - G54 work coordinate system support. Up to 6 work coordinate systems (G54-G59) available as a compile-time option. - G10 command added to set work coordinate offsets from machine position. - G92/G92.1 position offsets and cancellation support. Properly follows NIST standard rules with other systems. - G53 absolute override now works correctly with new coordinate systems. - Revamped g-code parser with robust error checking. Providing user feedback with bad commands. Follows NIST standards. - Planner module slightly changed to only expected position movements in terms of machine coordinates only. This was to simplify coordinate system handling, which is done solely by the g-code parser. - Upon grbl system abort, machine position and work positions are retained, while G92 offsets are reset per NIST standards. - Compiler compatibility update for _delay_us(). - Updated README. --- config.h | 4 + gcode.c | 413 ++++++++++++++++++++++++++++++++-------------- gcode.h | 3 +- limits.c | 4 +- main.c | 16 +- motion_control.h | 4 - nuts_bolts.c | 7 + nuts_bolts.h | 13 +- planner.c | 33 +--- planner.h | 3 - protocol.c | 18 +- protocol.h | 2 + readme.textile | 7 +- serial.h | 2 +- spindle_control.c | 2 +- 15 files changed, 351 insertions(+), 180 deletions(-) diff --git a/config.h b/config.h index 1e01df7..935a483 100644 --- a/config.h +++ b/config.h @@ -65,6 +65,10 @@ #define CMD_CYCLE_START '~' #define CMD_RESET 0x18 // ctrl-x +// Specifies the number of work coordinate systems grbl will support (G54 - G59). +// This parameter must be one or greater, currently supporting up to a value of 6. +#define N_COORDINATE_SYSTEM 1 + // This parameter sets the delay time before disabling the steppers after the final block of movement. // A short delay ensures the steppers come to a complete stop and the residual inertial force in the // CNC axes don't cause the axes to drift off position. This is particularly important when manually diff --git a/gcode.c b/gcode.c index 6a9372f..4a054e7 100644 --- a/gcode.c +++ b/gcode.c @@ -32,42 +32,53 @@ #include "errno.h" #include "protocol.h" -#define NEXT_ACTION_DEFAULT 0 -#define NEXT_ACTION_DWELL 1 -#define NEXT_ACTION_GO_HOME 2 -#define NEXT_ACTION_SET_COORDINATE_OFFSET 3 +// Define modal group internal numbers for checking multiple command violations and tracking the +// type of command that is called in the block. A modal group is a group of g-code commands that are +// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute +// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online, +// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc). +#define MODAL_GROUP_NONE 0 +#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal +#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G80] Motion +#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection +#define MODAL_GROUP_3 4 // [G90,G91] Distance mode +#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping +#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode +#define MODAL_GROUP_6 7 // [G20,G21] Units +#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning +#define MODAL_GROUP_12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection +// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used +// internally by the parser to know which command to execute. #define MOTION_MODE_SEEK 0 // G0 #define MOTION_MODE_LINEAR 1 // G1 #define MOTION_MODE_CW_ARC 2 // G2 #define MOTION_MODE_CCW_ARC 3 // G3 #define MOTION_MODE_CANCEL 4 // G80 -#define PATH_CONTROL_MODE_EXACT_PATH 0 -#define PATH_CONTROL_MODE_EXACT_STOP 1 -#define PATH_CONTROL_MODE_CONTINOUS 2 - #define PROGRAM_FLOW_RUNNING 0 -#define PROGRAM_FLOW_PAUSED 1 -#define PROGRAM_FLOW_OPT_PAUSED 2 -#define PROGRAM_FLOW_COMPLETED 3 +#define PROGRAM_FLOW_PAUSED 1 // M0, M1 +#define PROGRAM_FLOW_COMPLETED 2 // M2, M30 -#define SPINDLE_DIRECTION_CW 0 -#define SPINDLE_DIRECTION_CCW 1 +#define NON_MODAL_NONE 0 +#define NON_MODAL_DWELL 1 // G4 +#define NON_MODAL_SET_COORDINATE_DATA 2 // G10 +#define NON_MODAL_GO_HOME 3 // G28,G30 +#define NON_MODAL_SET_COORDINATE_OFFSET 4 // G92 +#define NON_MODAL_RESET_COORDINATE_OFFSET 5 //G92.1 typedef struct { - uint8_t status_code; - - uint8_t motion_mode; /* {G0, G1, G2, G3, G80} */ - uint8_t inverse_feed_rate_mode; /* G93, G94 */ - uint8_t inches_mode; /* 0 = millimeter mode, 1 = inches mode {G20, G21} */ - uint8_t absolute_mode; /* 0 = relative motion, 1 = absolute motion {G90, G91} */ - uint8_t program_flow; - int8_t spindle_direction; - double feed_rate, seek_rate; /* Millimeters/second */ - double position[3]; /* Where the interpreter considers the tool to be at this point in the code */ + uint8_t status_code; // Parser status for current block + uint8_t motion_mode; // {G0, G1, G2, G3, G80} + uint8_t inverse_feed_rate_mode; // {G93, G94} + uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} + uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91} + uint8_t program_flow; // {M0, M1, M2, M30} + int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} + double feed_rate, seek_rate; // Millimeters/second + double position[3]; // Where the interpreter considers the tool to be at this point in the code uint8_t tool; - int16_t spindle_speed; /* RPM/100 */ + int16_t spindle_speed; // RPM/100 uint8_t plane_axis_0, plane_axis_1, plane_axis_2; // The axes of the selected plane @@ -89,12 +100,11 @@ void gc_init() { memset(&gc, 0, sizeof(gc)); gc.feed_rate = settings.default_feed_rate; - gc.seek_rate = settings.default_seek_rate; select_plane(X_AXIS, Y_AXIS, Z_AXIS); gc.absolute_mode = true; } -// Set g-code parser position. Input in steps. +// Sets g-code parser position in mm. Input in steps. Called by the system abort routine. void gc_set_current_position(int32_t x, int32_t y, int32_t z) { gc.position[X_AXIS] = x/settings.steps_per_mm[X_AXIS]; @@ -102,63 +112,104 @@ void gc_set_current_position(int32_t x, int32_t y, int32_t z) gc.position[Z_AXIS] = z/settings.steps_per_mm[Z_AXIS]; } -static float to_millimeters(double value) { +static float to_millimeters(double value) +{ return(gc.inches_mode ? (value * MM_PER_INCH) : value); } // Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase // characters and signed floating point values (no whitespace). Comments and block delete -// characters have been removed. +// characters have been removed. All units and positions are converted and exported to grbl's +// internal functions in terms of (mm, mm/min) and absolute machine coordinates, respectively. uint8_t gc_execute_line(char *line) { uint8_t char_counter = 0; char letter; double value; - double unit_converted_value; - double inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified - uint8_t radius_mode = false; + int int_value; - uint8_t absolute_override = false; /* 1 = absolute motion for this block only {G53} */ - uint8_t next_action = NEXT_ACTION_DEFAULT; /* The action that will be taken by the parsed line */ + uint16_t modal_group_words = 0; // Bitflag variable to track and check modal group words in block + uint8_t axis_words = 0; // Bitflag to track which XYZ(ABC) parameters exist in block + + double inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified + uint8_t absolute_override = false; // true(1) = absolute motion for this block only {G53} + uint8_t non_modal_action = NON_MODAL_NONE; // Tracks the actions of modal group 0 (non-modal) double target[3], offset[3]; - - double p = 0, r = 0; - int int_value; - + clear_vector(target); // XYZ(ABC) axes parameters. + clear_vector(offset); // IJK Arc offsets are incremental. Value of zero indicates no change. + gc.status_code = STATUS_OK; - // Pass 1: Commands + /* Pass 1: Commands and set all modes. Check for modal group violations. + NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */ + uint8_t group_number = MODAL_GROUP_NONE; while(next_statement(&letter, &value, line, &char_counter)) { int_value = trunc(value); switch(letter) { case 'G': + // Set modal group values + switch(int_value) { + case 4: case 10: case 28: case 30: case 53: case 92: group_number = MODAL_GROUP_0; break; + case 0: case 1: case 2: case 3: case 80: group_number = MODAL_GROUP_1; break; + case 17: case 18: case 19: group_number = MODAL_GROUP_2; break; + case 90: case 91: group_number = MODAL_GROUP_3; break; + case 93: case 94: group_number = MODAL_GROUP_5; break; + case 20: case 21: group_number = MODAL_GROUP_6; break; + case 54: case 55: case 56: case 57: case 58: case 59: group_number = MODAL_GROUP_12; break; + } + // Set 'G' commands switch(int_value) { case 0: gc.motion_mode = MOTION_MODE_SEEK; break; case 1: gc.motion_mode = MOTION_MODE_LINEAR; break; case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break; case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break; - case 4: next_action = NEXT_ACTION_DWELL; break; + case 4: non_modal_action = NON_MODAL_DWELL; break; + case 10: non_modal_action = NON_MODAL_SET_COORDINATE_DATA; break; case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break; case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break; case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; case 20: gc.inches_mode = true; break; case 21: gc.inches_mode = false; break; - case 28: case 30: next_action = NEXT_ACTION_GO_HOME; break; + case 28: case 30: non_modal_action = NON_MODAL_GO_HOME; break; case 53: absolute_override = true; break; - case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; + case 54: case 55: case 56: case 57: case 58: case 59: + int_value -= 54; // Compute coordinate system row index (0=G54,1=G55,...) + if (int_value < N_COORDINATE_SYSTEM) { + sys.coord_select = int_value; + } else { + FAIL(STATUS_UNSUPPORTED_STATEMENT); + } + break; + case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; case 90: gc.absolute_mode = true; break; case 91: gc.absolute_mode = false; break; - case 92: next_action = NEXT_ACTION_SET_COORDINATE_OFFSET; break; + case 92: + int_value = trunc(10*value); // Multiply by 10 to pick up G92.1 + switch(int_value) { + case 920: non_modal_action = NON_MODAL_SET_COORDINATE_OFFSET; break; + case 921: non_modal_action = NON_MODAL_RESET_COORDINATE_OFFSET; break; + default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + } + break; case 93: gc.inverse_feed_rate_mode = true; break; case 94: gc.inverse_feed_rate_mode = false; break; default: FAIL(STATUS_UNSUPPORTED_STATEMENT); } - break; + break; case 'M': + // Set modal group values switch(int_value) { - case 0: case 60: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause - case 1: gc.program_flow = PROGRAM_FLOW_OPT_PAUSED; break; // Program pause with optional stop on + case 0: case 1: case 2: case 30: group_number = MODAL_GROUP_4; break; + case 3: case 4: case 5: group_number = MODAL_GROUP_7; break; + } + // Set 'M' commands + switch(int_value) { + case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause + case 1: // Program pause with optional stop on + // if (sys.opt_stop) { // TODO: Add system variable for optional stop. + gc.program_flow = PROGRAM_FLOW_PAUSED; break; + // } case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset case 3: gc.spindle_direction = 1; break; case 4: gc.spindle_direction = -1; break; @@ -166,75 +217,197 @@ uint8_t gc_execute_line(char *line) default: FAIL(STATUS_UNSUPPORTED_STATEMENT); } break; - case 'T': gc.tool = trunc(value); break; + } + // Check for modal group multiple command violations in the current block + if (group_number) { + if ( bit_istrue(modal_group_words,bit(group_number)) ) { + FAIL(STATUS_MODAL_GROUP_VIOLATION); + } else { + bit_true(modal_group_words,bit(group_number)); + } + group_number = MODAL_GROUP_NONE; // Reset for next command. } - if(gc.status_code) { break; } - } - + } + // 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); } - + + /* Pass 2: Parameters. All units converted according to current block commands. Position + parameters are converted and flagged to indicate a change. These can have multiple connotations + for different commands. Each will be converted to their proper value upon execution. */ + double p = 0, r = 0; + uint8_t l = 0; char_counter = 0; - clear_vector(target); - clear_vector(offset); - memcpy(target, gc.position, sizeof(target)); // i.e. target = gc.position - - // Pass 2: Parameters while(next_statement(&letter, &value, line, &char_counter)) { - int_value = trunc(value); - unit_converted_value = to_millimeters(value); switch(letter) { case 'F': - if (unit_converted_value <= 0) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // Must be greater than zero + if (value <= 0) { FAIL(STATUS_INVALID_COMMAND); } // Must be greater than zero if (gc.inverse_feed_rate_mode) { - inverse_feed_rate = unit_converted_value; // seconds per motion for this motion only + inverse_feed_rate = to_millimeters(value); // seconds per motion for this motion only } else { - if (gc.motion_mode == MOTION_MODE_SEEK) { - gc.seek_rate = unit_converted_value; - } else { - gc.feed_rate = unit_converted_value; // millimeters per minute - } + gc.feed_rate = to_millimeters(value); // millimeters per minute } break; - case 'I': case 'J': case 'K': offset[letter-'I'] = unit_converted_value; break; - case 'P': p = value; break; - case 'R': r = unit_converted_value; radius_mode = true; break; - case 'S': gc.spindle_speed = value; break; - case 'X': case 'Y': case 'Z': - if (gc.absolute_mode || absolute_override) { - target[letter - 'X'] = unit_converted_value; - } else { - target[letter - 'X'] += unit_converted_value; - } + case 'I': case 'J': case 'K': offset[letter-'I'] = to_millimeters(value); break; + case 'L': l = trunc(value); break; + case 'P': p = value; break; + case 'R': r = to_millimeters(value); break; + case 'S': + if (value < 0) { FAIL(STATUS_INVALID_COMMAND); } // Cannot be negative + gc.spindle_speed = value; break; + case 'T': + if (value < 0) { FAIL(STATUS_INVALID_COMMAND); } // Cannot be negative + gc.tool = trunc(value); + break; + case 'X': target[X_AXIS] = to_millimeters(value); bit_true(axis_words,bit(X_AXIS)); break; + case 'Y': target[Y_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Y_AXIS)); break; + case 'Z': target[Z_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Z_AXIS)); break; } } // 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); } - - // Update spindle state + + + /* Execute Commands: Perform by order of execution defined in NIST RS274-NGC.v3, Table 8, pg.41. + NOTE: Independent non-motion/settings parameters are set out of this order for code efficiency + and simplicity purposes, but this should not affect proper g-code execution. */ + + // ([M6]: Tool change execution should be executed here.) + + // [M3,M4,M5]: Update spindle state spindle_run(gc.spindle_direction, gc.spindle_speed); - // Perform any physical actions - switch (next_action) { - case NEXT_ACTION_GO_HOME: mc_go_home(); clear_vector_double(target); break; - case NEXT_ACTION_DWELL: mc_dwell(p); break; - case NEXT_ACTION_SET_COORDINATE_OFFSET: - mc_set_coordinate_offset(target[X_AXIS],target[Y_AXIS],target[Z_AXIS]); + // ([M7,M8,M9]: Coolant state should be executed here.) + + // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets. + // NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this + // modal group and do not effect these actions. + switch (non_modal_action) { + case NON_MODAL_DWELL: + if (p < 0) { // Time cannot be negative. + FAIL(STATUS_INVALID_COMMAND); + } else { + mc_dwell(p); + } break; - case NEXT_ACTION_DEFAULT: - switch (gc.motion_mode) { - case MOTION_MODE_CANCEL: break; - case MOTION_MODE_SEEK: - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); - break; - case MOTION_MODE_LINEAR: - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], - (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); - break; - case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: - if (radius_mode) { + case NON_MODAL_SET_COORDINATE_DATA: + int_value = trunc(p); // Convert p value to int. + if (l != 2 || (int_value < 1 || int_value > N_COORDINATE_SYSTEM)) { // L2 only. P1=G54, P2=G55, ... + FAIL(STATUS_UNSUPPORTED_STATEMENT); + } else if (!axis_words) { // No axis words. + FAIL(STATUS_INVALID_COMMAND); + } else { + int_value--; // Adjust p to be inline with row array index. + // Update axes defined only in block. Always in machine coordinates. Can change non-active system. + uint8_t i; + for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used. + if ( bit_istrue(axis_words,bit(i)) ) { sys.coord_system[int_value][i] = target[i]; } + } + } + axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. + break; + case NON_MODAL_GO_HOME: + // Move to intermediate position before going home. Obeys current coordinate system and offsets + // and absolute and incremental modes. + if (axis_words) { + // Apply absolute mode coordinate offsets or incremental mode offsets. + uint8_t i; + for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used. + if ( bit_istrue(axis_words,bit(i)) ) { + if (gc.absolute_mode) { + target[i] += sys.coord_system[sys.coord_select][i] + sys.coord_offset[i]; + } else { + target[i] += gc.position[i]; + } + } else { + target[i] = gc.position[i]; + } + } + mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); + } + mc_go_home(); + clear_vector(gc.position); // Assumes home is at [0,0,0] + axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. + break; + case NON_MODAL_SET_COORDINATE_OFFSET: + if (!axis_words) { // No axis words + FAIL(STATUS_INVALID_COMMAND); + } else { + // Update axes defined only in block. Offsets current system to defined value. Does not update when + // active coordinate system is selected, but is still active unless G92.1 disables it. + uint8_t i; + for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used. + if (bit_istrue(axis_words,bit(i)) ) { + sys.coord_offset[i] = gc.position[i]-sys.coord_system[sys.coord_select][i]-target[i]; + } + } + } + axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. + break; + case NON_MODAL_RESET_COORDINATE_OFFSET: + clear_vector(sys.coord_offset); // Disable G92 offsets by zeroing offset vector. + break; + } + + // [G0,G1,G2,G3,G80]: Perform motion modes. + // NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes. + // Enter motion modes only if there are axis words or a motion mode command word in the block. + if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_1)) || axis_words ) { + + // G1,G2,G3 require F word in inverse time mode. + if ( gc.inverse_feed_rate_mode ) { + if (inverse_feed_rate < 0 && gc.motion_mode != MOTION_MODE_CANCEL) { + FAIL(STATUS_INVALID_COMMAND); + } + } + // Absolute override G53 only valid with G0 and G1 active. + if ( absolute_override && !(gc.motion_mode == MOTION_MODE_SEEK || gc.motion_mode == MOTION_MODE_LINEAR)) { + FAIL(STATUS_INVALID_COMMAND); + } + // Report any errors. + if (gc.status_code) { return(gc.status_code); } + + // Convert all target position data to machine coordinates for executing motion. Apply + // absolute mode coordinate offsets or incremental mode offsets. + // NOTE: Tool offsets may be appended to these conversions when/if this feature is added. + uint8_t i; + for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used to save flash space. + if ( bit_istrue(axis_words,bit(i)) ) { + if (!absolute_override) { // Do not update target in absolute override mode + if (gc.absolute_mode) { + target[i] += sys.coord_system[sys.coord_select][i] + sys.coord_offset[i]; // Absolute mode + } else { + target[i] += gc.position[i]; // Incremental mode + } + } + } else { + target[i] = gc.position[i]; // No axis word in block. Keep same axis position. + } + } + + switch (gc.motion_mode) { + case MOTION_MODE_CANCEL: + if (axis_words) { FAIL(STATUS_INVALID_COMMAND); } // No axis words allowed while active. + break; + case MOTION_MODE_SEEK: + if (!axis_words) { FAIL(STATUS_INVALID_COMMAND);} + else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); } + break; + case MOTION_MODE_LINEAR: + if (!axis_words) { FAIL(STATUS_INVALID_COMMAND);} + else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], + (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); } + break; + case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: + // Check if at least one of the axes of the selected plane has been specified. If in center + // format arc mode, also check for at least one of the IJK axes of the selected plane was sent. + if ( !( bit_false(axis_words,bit(gc.plane_axis_2)) ) || + ( !r && !offset[gc.plane_axis_0] && !offset[gc.plane_axis_1] ) ) { + FAIL(STATUS_INVALID_COMMAND); + } else { + if (r != 0) { // Arc Radius Mode /* We need to calculate the center of the circle that has the designated radius and passes through both the current position and the target position. This method calculates the following @@ -325,11 +498,9 @@ uint8_t gc_execute_line(char *line) // Complete the operation by calculating the actual center of the arc offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d)); offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d)); - - } else { // Offset mode specific computations - + + } else { // Arc Center Format Offset Mode r = hypot(offset[gc.plane_axis_0], offset[gc.plane_axis_1]); // Compute arc radius for mc_arc - } // Set clockwise/counter-clockwise sign for mc_arc computations @@ -340,29 +511,28 @@ uint8_t gc_execute_line(char *line) mc_arc(gc.position, target, offset, gc.plane_axis_0, gc.plane_axis_1, gc.plane_axis_2, (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode, r, isclockwise); - - break; - } + } + break; + } + + // Report any errors. + if (gc.status_code) { return(gc.status_code); } + + // As far as the parser is concerned, the position is now == target. In reality the + // motion control system might still be processing the action and the real tool position + // in any intermediate location. + memcpy(gc.position, target, sizeof(double)*3); // gc.position[] = target[]; } - // As far as the parser is concerned, the position is now == target. In reality the - // motion control system might still be processing the action and the real tool position - // in any intermediate location. - memcpy(gc.position, target, sizeof(double)*3); // gc.position[] = target[]; - - // Perform non-running program flow actions. During a program pause, the buffer may refill and can - // only be resumed by the cycle start run-time command. - // TODO: Install optional stop setting and re-factor program flow actions. + // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may + // refill and can only be resumed by the cycle start run-time command. if (gc.program_flow) { plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete. sys.auto_start = false; // Disable auto cycle start. - switch (gc.program_flow) { - case PROGRAM_FLOW_PAUSED: case PROGRAM_FLOW_OPT_PAUSED: - gc.program_flow = PROGRAM_FLOW_RUNNING; // Re-enable program flow after pause complete. - break; - // Reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9) - case PROGRAM_FLOW_COMPLETED: sys.abort = true; break; - } + gc.program_flow = PROGRAM_FLOW_RUNNING; // Re-enable program flow after pause complete. + + // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9) + if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { sys.abort = true; } } return(gc.status_code); @@ -391,21 +561,24 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t } /* - Intentionally not supported: + Not supported: - Canned cycles - Tool radius compensation - A,B,C-axes - - Multiple coordinate systems - Evaluation of expressions - Variables - Multiple home locations + - Multiple coordinate systems (May be added in the future) - Probing - Override control + - Tool changes - group 0 = {G10, G28, G30, G92.1, G92.2, G92.3} (Non modal G-codes) + group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets) + group 1 = {G38.2, G81 - G89} (Motion modes: straight probe, canned cycles) + group 6 = {M6} (Tool change) group 8 = {M7, M8, M9} coolant (special case: M7 and M8 may be active at the same time) group 9 = {M48, M49} enable/disable feed and speed override switches - group 12 = {G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection + group 12 = {G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection group 13 = {G61, G61.1, G64} path control mode */ diff --git a/gcode.h b/gcode.h index af9751a..f3d86f1 100644 --- a/gcode.h +++ b/gcode.h @@ -3,7 +3,8 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - + Copyright (c) 2011-2012 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 the Free Software Foundation, either version 3 of the License, or diff --git a/limits.c b/limits.c index a5c4a3e..be93b78 100644 --- a/limits.c +++ b/limits.c @@ -76,9 +76,9 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, bool reverse_dir // Check if we are done if(!(x_axis || y_axis || z_axis)) { return; } STEPPING_PORT |= out_bits & STEP_MASK; - _delay_us(settings.pulse_microseconds); + delay_us(settings.pulse_microseconds); STEPPING_PORT ^= out_bits & STEP_MASK; - _delay_us(step_delay); + delay_us(step_delay); } return; } diff --git a/main.c b/main.c index 40441b8..fd75f3b 100644 --- a/main.c +++ b/main.c @@ -53,15 +53,17 @@ int main(void) // reset to finish the initialization process. if (sys.abort) { - // Retain last known machine position. If the system abort occurred while in motion, machine - // position is not guaranteed, since a hard stop can cause the steppers to lose steps. Always - // perform a feedhold before an abort, if maintaining accurate machine position is required. + // Retain last known machine position and work coordinate offset(s). If the system abort + // occurred while in motion, machine position is not guaranteed, since a hard stop can cause + // the steppers to lose steps. Always perform a feedhold before an abort, if maintaining + // accurate machine position is required. // TODO: Report last position and coordinate offset to users to help relocate origins. Future // releases will auto-reset the machine position back to [0,0,0] if an abort is used while // grbl is moving the machine. - int32_t last_position[3]; // last_coord_offset[3]; + int32_t last_position[3]; + double last_coord_system[N_COORDINATE_SYSTEM][3]; memcpy(last_position, sys.position, sizeof(sys.position)); // last_position[] = sys.position[] - // memcpy(last_coord_offset, sys.coord_offset, sizeof(sys.coord_offset)); // last_coord_offset[] = sys.coord_offset[] + memcpy(last_coord_system, sys.coord_system, sizeof(sys.coord_system)); // last_coord_system[] = sys.coord_system[] // Reset system. memset(&sys, 0, sizeof(sys)); // Clear all system variables @@ -74,8 +76,9 @@ int main(void) limits_init(); st_reset(); // Clear stepper subsystem variables. - // Reload last known machine position. Coordinate offsets are reset per NIST RS274-NGC protocol. + // Reload last known machine position and work systems. G92 coordinate offsets are reset. memcpy(sys.position, last_position, sizeof(last_position)); // sys.position[] = last_position[] + memcpy(sys.coord_system, last_coord_system, sizeof(last_coord_system)); // sys.coord_system[] = last_coord_system[] gc_set_current_position(last_position[X_AXIS],last_position[Y_AXIS],last_position[Z_AXIS]); plan_set_current_position(last_position[X_AXIS],last_position[Y_AXIS],last_position[Z_AXIS]); @@ -85,6 +88,7 @@ int main(void) #ifdef CYCLE_AUTO_START sys.auto_start = true; #endif + // TODO: Install G20/G21 unit default into settings and load appropriate settings. } protocol_execute_runtime(); diff --git a/motion_control.h b/motion_control.h index b5061e9..5f9fc35 100644 --- a/motion_control.h +++ b/motion_control.h @@ -25,10 +25,6 @@ #include #include "planner.h" -// NOTE: Although the following function structurally belongs in this module, there is nothing to do but -// to forward the request to the planner. -#define mc_set_coordinate_offset(x, y, z) plan_set_coordinate_offset(x, y, z) - // 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. diff --git a/nuts_bolts.c b/nuts_bolts.c index 83b4e40..79cc41d 100644 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -44,3 +44,10 @@ void delay_ms(uint16_t ms) { while ( ms-- ) { _delay_ms(1); } } + +// Delays variable defined microseconds. Compiler compatibility fix for _delay_us(), +// which only accepts constants in future compiler releases. +void delay_us(uint16_t us) +{ + while ( us-- ) { _delay_us(1); } +} diff --git a/nuts_bolts.h b/nuts_bolts.h index 3c4112b..35fc7c4 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -21,6 +21,7 @@ #ifndef nuts_bolts_h #define nuts_bolts_h +#include #include #include #include @@ -70,8 +71,13 @@ typedef struct { int32_t position[3]; // Real-time machine (aka home) position vector in steps. // NOTE: This may need to be a volatile variable, if problems arise. - int32_t coord_offset[3]; // Retains the G92 coordinate offset (work coordinates) relative to - // machine zero in steps. + + uint8_t coord_select; // Active work coordinate system number. Default: 0=G54. + double coord_system[N_COORDINATE_SYSTEM][3]; // Work coordinate systems (G54+). Stores offset from + // absolute machine position in mm. + // Rows: Work system number (0=G54,1=G55,...5=G59), Columns: XYZ Offsets + double coord_offset[3]; // Retains the G92 coordinate offset (work coordinates) relative to + // machine zero in mm. 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. @@ -86,4 +92,7 @@ int read_double(char *line, uint8_t *char_counter, double *double_ptr); // Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms(). void delay_ms(uint16_t ms); +// Delays variable-defined microseconds. Compiler compatibility fix for _delay_us(). +void delay_us(uint16_t us); + #endif diff --git a/planner.c b/planner.c index ff32b5e..dac2d62 100644 --- a/planner.c +++ b/planner.c @@ -344,6 +344,8 @@ void plan_synchronize() // Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in // millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed // rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. +// All position data passed to the planner must be in terms of machine position to keep the planner +// independent of any coordinate system changes and offsets, which are handled by the g-code parser. // NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control. void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate) { @@ -471,36 +473,7 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in planner_recalculate(); } -// Apply G92 coordinate offsets and update planner position vector. -void plan_set_coordinate_offset(double x, double y, double z) -{ - // To correlate status reporting work position correctly, the planner must force the steppers to - // empty the block buffer and synchronize with the planner, as the real-time machine position and - // the planner position at the end of the buffer can be and are usually different. This function is - // only called with a G92, which typically is used only at the beginning of a g-code program or - // between different operations. - // TODO: Find a robust way to avoid a planner synchronize, but this may require a bit of ingenuity. - plan_synchronize(); - - // Update the system coordinate offsets from machine zero - sys.coord_offset[X_AXIS] += pl.position[X_AXIS]; - sys.coord_offset[Y_AXIS] += pl.position[Y_AXIS]; - sys.coord_offset[Z_AXIS] += pl.position[Z_AXIS]; - - memset(&pl, 0, sizeof(pl)); // Clear planner variables. Assume start from rest. - - // Update planner position and coordinate offset vectors - int32_t new_position[3]; - new_position[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); - new_position[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); - new_position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); - plan_set_current_position(new_position[X_AXIS],new_position[Y_AXIS],new_position[Z_AXIS]); - sys.coord_offset[X_AXIS] -= pl.position[X_AXIS]; - sys.coord_offset[Y_AXIS] -= pl.position[Y_AXIS]; - sys.coord_offset[Z_AXIS] -= pl.position[Z_AXIS]; -} - -// Reset the planner position vector (in steps) +// Reset the planner position vector (in steps). Called by the system abort routine. void plan_set_current_position(int32_t x, int32_t y, int32_t z) { pl.position[X_AXIS] = x; diff --git a/planner.h b/planner.h index 95dbd78..3f1ba01 100644 --- a/planner.h +++ b/planner.h @@ -69,9 +69,6 @@ block_t *plan_get_current_block(); // Reset the planner position vector (in steps) void plan_set_current_position(int32_t x, int32_t y, int32_t z); -// Apply G92 coordinate offsets and update planner position vector. Called by g-code parser. -void plan_set_coordinate_offset(double x, double y, double z); - // Reinitialize plan with a partially completed block void plan_cycle_reinitialize(int32_t step_events_remaining); diff --git a/protocol.c b/protocol.c index 3f92d1e..2646f49 100644 --- a/protocol.c +++ b/protocol.c @@ -53,6 +53,10 @@ static void status_message(int status_code) printPgmString(PSTR("Unsupported statement\r\n")); break; case STATUS_FLOATING_POINT_ERROR: printPgmString(PSTR("Floating point error\r\n")); break; + case STATUS_MODAL_GROUP_VIOLATION: + printPgmString(PSTR("Modal group violation\r\n")); break; + case STATUS_INVALID_COMMAND: + printPgmString(PSTR("Invalid command\r\n")); break; default: printInteger(status_code); printPgmString(PSTR("\r\n")); @@ -74,7 +78,7 @@ void protocol_status_report() // short line segments and interface setups that require real-time status reports (5-20Hz). // **Under construction** Bare-bones status report. Provides real-time machine position relative to - // the system power on location (0,0,0) and work coordinate position, updatable by the G92 command. + // the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). // The following are still needed: user setting of output units (mm|inch), compressed (non-human // readable) data for interfaces?, save last known position in EEPROM?, code optimizations, solidify // the reporting schemes, move to a separate .c file for easy user accessibility, and setting the @@ -87,16 +91,16 @@ void protocol_status_report() printString("MPos:["); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); printString(","); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); printString(","); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); - printString("],WPos:["); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); - printString(","); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); - printString(","); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); + printString("],WPos:["); printFloat((print_position[X_AXIS]/settings.steps_per_mm[X_AXIS]-sys.coord_system[sys.coord_select][X_AXIS]-sys.coord_offset[X_AXIS])/MM_PER_INCH); + printString(","); printFloat((print_position[Y_AXIS]/settings.steps_per_mm[Y_AXIS]-sys.coord_system[sys.coord_select][Y_AXIS]-sys.coord_offset[Y_AXIS])/MM_PER_INCH); + printString(","); printFloat((print_position[Z_AXIS]/settings.steps_per_mm[Z_AXIS]-sys.coord_system[sys.coord_select][Z_AXIS]-sys.coord_offset[Z_AXIS])/MM_PER_INCH); #else printString("MPos:["); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS])); printString(","); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS])); printString(","); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS])); - printString("],WPos:["); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS])); - printString(","); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS])); - printString(","); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS])); + printString("],WPos:["); printFloat(print_position[X_AXIS]/settings.steps_per_mm[X_AXIS]-sys.coord_system[sys.coord_select][X_AXIS]-sys.coord_offset[X_AXIS]); + printString(","); printFloat(print_position[Y_AXIS]/settings.steps_per_mm[Y_AXIS]-sys.coord_system[sys.coord_select][Y_AXIS]-sys.coord_offset[Y_AXIS]); + printString(","); printFloat(print_position[Z_AXIS]/settings.steps_per_mm[Z_AXIS]-sys.coord_system[sys.coord_select][Z_AXIS]-sys.coord_offset[Z_AXIS]); #endif printString("]\r\n"); } diff --git a/protocol.h b/protocol.h index ece1f1f..8c341fe 100644 --- a/protocol.h +++ b/protocol.h @@ -26,6 +26,8 @@ #define STATUS_EXPECTED_COMMAND_LETTER 2 #define STATUS_UNSUPPORTED_STATEMENT 3 #define STATUS_FLOATING_POINT_ERROR 4 +#define STATUS_MODAL_GROUP_VIOLATION 5 +#define STATUS_INVALID_COMMAND 6 // Initialize the serial protocol void protocol_init(); diff --git a/readme.textile b/readme.textile index 43b89f9..ad06c0f 100644 --- a/readme.textile +++ b/readme.textile @@ -14,9 +14,10 @@ Grbl includes full acceleration management with look ahead. That means the contr - New run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status reporting - Controlled feed hold with deceleration to ensure no skipped steps and loss of location. - After feed hold, cycle accelerations are re-planned and may be resumed. - - Work offset(G92) and machine coordinate system support. - - Program stop(M0,M1*,M2,M30) initial support. Pallet shuttle not supported. - - System reset re-initializes grbl without resetting the Arduino and retains machine/home position. + - Re-factored g-code parser with robust error-checking. + - Work coordinate system (G54), offsets(G92), and machine coordinate system support. G10 work coordinate settings support. (Up to 6 work coordinate systems(G54-G59) available as a compile-time option.) + - Program stop(M0,M1*,M2,M30) initial support. Optional stop to do. + - System reset re-initializes grbl without resetting the Arduino and retains machine/home position and work coordinates. - Restructured planner and stepper modules to become independent and ready for future features. - Planned features: Jog mode, status reporting, runtime settings such as toggling block delete, XON/XOFF flow control. - Reduce serial read buffer to 128 characters and increased write buffer to 64 characters. diff --git a/serial.h b/serial.h index 904c6c0..3e1cc77 100644 --- a/serial.h +++ b/serial.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 diff --git a/spindle_control.c b/spindle_control.c index cd99c9a..3b7f8ca 100644 --- a/spindle_control.c +++ b/spindle_control.c @@ -24,7 +24,7 @@ #include "config.h" #include "planner.h" -#include +#include // TODO: Deprecated. Need to update for new version. From e9b28279db89421c3588ee80a0d6c95d37067916 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sun, 12 Feb 2012 11:02:23 -0700 Subject: [PATCH 11/55] Spindle DDR pins init minor fix. --- spindle_control.c | 3 +++ stepper.c | 4 ---- stepper.h | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spindle_control.c b/spindle_control.c index 3b7f8ca..1809a3b 100644 --- a/spindle_control.c +++ b/spindle_control.c @@ -23,6 +23,7 @@ #include "motion_control.h" #include "config.h" #include "planner.h" +#include "stepper.h" #include @@ -32,6 +33,8 @@ static int current_direction; void spindle_init() { + SPINDLE_ENABLE_DDR |= (1< #include +// Some useful constants #define LIMIT_MASK ((1< Date: Sat, 25 Feb 2012 09:06:42 -0700 Subject: [PATCH 12/55] Minor include related compile fix. Added experimental XON/XOFF flow control. Not officially supported! - A latency issue related to USB-to-serial converters on the Arduino does not allow for XON/XOFF flow control to work correctly on standard terminal programs. It seems that only specialized UI's or avoiding the USB port all together solves this problem. However, XON/XOFF flow control is added for advanced users only as a compile-time option. This feature is officially *NOT* supported by grbl, but let us know of any successes with it! --- config.h | 8 ++++ nuts_bolts.h | 3 +- serial.c | 109 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 92 insertions(+), 28 deletions(-) diff --git a/config.h b/config.h index 935a483..591b620 100644 --- a/config.h +++ b/config.h @@ -114,6 +114,14 @@ // time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays. #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) +// FOR ADVANCED USERS ONLY: Toggles XON/XOFF software flow control for serial communications. +// Officially not supported due to problems involving USB-to-serial chip latency (Atmega8U2/FTDI) +// when connecting to an Arduino through the USB port. This problem has to do with having no control +// of the USB packets and causing standard terminal programs not being able to honor the XON/XOFF +// control characters on time. However, with specially programmed UI's or avoiding the USB interface +// completely, XON/XOFF flow control should work. In any case, please report any successes to grbl +// administrators! +#define ENABLE_XONXOFF 0 // Boolean. Default disabled. // ----------------------------------------------- diff --git a/nuts_bolts.h b/nuts_bolts.h index 35fc7c4..5909876 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -21,10 +21,11 @@ #ifndef nuts_bolts_h #define nuts_bolts_h -#include + #include #include #include +#include "config.h" #define false 0 #define true 1 diff --git a/serial.c b/serial.c index 8d385bf..4202c3a 100644 --- a/serial.c +++ b/serial.c @@ -42,6 +42,27 @@ uint8_t tx_buffer[TX_BUFFER_SIZE]; uint8_t tx_buffer_head = 0; volatile uint8_t tx_buffer_tail = 0; +#if ENABLE_XONXOFF + #define RX_BUFFER_FULL 96 // XOFF high watermark + #define RX_BUFFER_LOW 64 // XON low watermark + #define SEND_XOFF 1 + #define SEND_XON 2 + #define XOFF_SENT 3 + #define XON_SENT 4 + #define XOFF_CHAR 0x13 + #define XON_CHAR 0x11 + volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable + + // Returns the number of bytes in the RX buffer. This replaces a typical byte counter to prevent + // the interrupt and main programs from writing to the counter at the same time. + static uint8_t get_rx_buffer_count() + { + if (rx_buffer_head == rx_buffer_tail) { return(0); } + if (rx_buffer_head < rx_buffer_tail) { return(rx_buffer_tail-rx_buffer_head); } + return (RX_BUFFER_SIZE - (rx_buffer_head-rx_buffer_tail)); + } +#endif + static void set_baud_rate(long baud) { uint16_t UBRR0_value = ((F_CPU / 16 + baud / 2) / baud - 1); UBRR0H = UBRR0_value >> 8; @@ -84,21 +105,33 @@ void serial_write(uint8_t data) { } // Data Register Empty Interrupt handler -ISR(USART_UDRE_vect) { +ISR(USART_UDRE_vect) +{ // Temporary tx_buffer_tail (to optimize for volatile) uint8_t tail = tx_buffer_tail; - + + #if ENABLE_XONXOFF + switch (flow_ctrl) { + case SEND_XOFF: UDR0 = XOFF_CHAR; flow_ctrl = XOFF_SENT; break; + case SEND_XON: UDR0 = XON_CHAR; flow_ctrl = XON_SENT; break; + default: + #endif + // Send a byte from the buffer UDR0 = tx_buffer[tail]; // Update tail position - tail ++; + tail++; if (tail == TX_BUFFER_SIZE) { tail = 0; } + tx_buffer_tail = tail; + + #if ENABLE_XONXOFF + } + #endif + // Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); } - - tx_buffer_tail = tail; } uint8_t serial_read() @@ -109,6 +142,14 @@ uint8_t serial_read() uint8_t data = rx_buffer[rx_buffer_tail]; rx_buffer_tail++; if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; } + + #if ENABLE_XONXOFF + if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl != XON_SENT) { + flow_ctrl = SEND_XON; + UCSR0B |= (1 << UDRIE0); // Force TX + } + #endif + return data; } } @@ -116,33 +157,47 @@ uint8_t serial_read() ISR(USART_RX_vect) { uint8_t data = UDR0; - uint8_t next_head = rx_buffer_head + 1; - if (next_head == RX_BUFFER_SIZE) { next_head = 0; } - - // Write data to buffer unless it is full. - if (next_head != rx_buffer_tail) { - // 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.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 and spindle subsystem idle at an interrupt level. - if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. - st_go_idle(); - spindle_stop(); - } - sys.execute |= EXEC_RESET; // Set as true - break; - default : // Write character to buffer + uint8_t next_head; + + // 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.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 and spindle subsystem idle at an interrupt level. + if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. + st_go_idle(); + spindle_stop(); + } + sys.execute |= EXEC_RESET; // Set as true + break; + default: // Write character to buffer + next_head = rx_buffer_head + 1; + if (next_head == RX_BUFFER_SIZE) { next_head = 0; } + + // Write data to buffer unless it is full. + if (next_head != rx_buffer_tail) { rx_buffer[rx_buffer_head] = data; - rx_buffer_head = next_head; - } + rx_buffer_head = next_head; + + #if ENABLE_XONXOFF + if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl != XOFF_SENT) { + flow_ctrl = SEND_XOFF; + UCSR0B |= (1 << UDRIE0); // Force TX + } + #endif + + } } } void serial_reset_read_buffer() { rx_buffer_tail = rx_buffer_head; + + #if ENABLE_XONXOFF + flow_ctrl = XON_SENT; + #endif } From 86cdae0060443e0878dcf5e07bcf7adc97dc563a Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Mon, 5 Mar 2012 12:01:02 -0700 Subject: [PATCH 13/55] Minor updates. - Updated makefile to be more universally compatible by not requiring grep or ruby. - Edited XON/XOFF flow control usage, noting that FTDI-based Arduinos are known to work, but not Atmega8U2-based Arduino. Still officially not supported, but added for advanced users. - Minor edits. --- Makefile | 5 ++--- config.h | 15 +++++++++------ gcode.c | 2 +- limits.c | 2 +- serial.c | 36 ++++++++++++++++++------------------ 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index 6011ae0..922f2b6 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m # Tune the lines below only if you know what you are doing: AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F -COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections +COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections # symbolic targets: all: grbl.hex @@ -81,8 +81,7 @@ main.elf: $(OBJECTS) grbl.hex: main.elf rm -f grbl.hex avr-objcopy -j .text -j .data -O ihex main.elf grbl.hex - avr-objdump -h main.elf | grep .bss | ruby -e 'puts "\n\n--- Requires %s bytes of SRAM" % STDIN.read.match(/0[0-9a-f]+\s/)[0].to_i(16)' - avr-size *.hex *.elf *.o + avr-size -C --mcu=$(DEVICE) main.elf # If you have an EEPROM section, you must also create a hex file for the # EEPROM and add it to the "flash" target. diff --git a/config.h b/config.h index 591b620..9c2305a 100644 --- a/config.h +++ b/config.h @@ -115,12 +115,15 @@ #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) // FOR ADVANCED USERS ONLY: Toggles XON/XOFF software flow control for serial communications. -// Officially not supported due to problems involving USB-to-serial chip latency (Atmega8U2/FTDI) -// when connecting to an Arduino through the USB port. This problem has to do with having no control -// of the USB packets and causing standard terminal programs not being able to honor the XON/XOFF -// control characters on time. However, with specially programmed UI's or avoiding the USB interface -// completely, XON/XOFF flow control should work. In any case, please report any successes to grbl -// administrators! +// Officially not supported due to problems involving the Atmega8U2 USB-to-serial chips on current +// future Arduinos boards. The firmware on these chips do not support XON/XOFF flow control +// characters and the intermediate buffer in the chips cause latency and overflow problems with +// standard terminal programs. However, using specifically-programmed UI's to manage this latency +// problem has been confirmed to work, as well as, using older FTDI FT232RL-based Arduinos +// (Duemilanove) since their firmaware correctly manage the XON/XOFF characters. Other unconfirmed +// methods include using an FTDI board/cable or directly communicate on the RX/TX pins on the +// Arduino, both of which circumvent the Atmega8U2 chip altogether. In any case, please report any +// successes to grbl administrators! #define ENABLE_XONXOFF 0 // Boolean. Default disabled. // ----------------------------------------------- diff --git a/gcode.c b/gcode.c index 4a054e7..7e20f89 100644 --- a/gcode.c +++ b/gcode.c @@ -569,7 +569,7 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t - Evaluation of expressions - Variables - Multiple home locations - - Multiple coordinate systems (May be added in the future) + - Multiple coordinate systems (Additional ones maybe added in config.h) - Probing - Override control - Tool changes diff --git a/limits.c b/limits.c index be93b78..64ff53f 100644 --- a/limits.c +++ b/limits.c @@ -1,5 +1,5 @@ /* - limits.h - code pertaining to limit-switches and performing the homing cycle + limits.c - code pertaining to limit-switches and performing the homing cycle Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud diff --git a/serial.c b/serial.c index 4202c3a..6036d3a 100644 --- a/serial.c +++ b/serial.c @@ -111,24 +111,24 @@ ISR(USART_UDRE_vect) uint8_t tail = tx_buffer_tail; #if ENABLE_XONXOFF - switch (flow_ctrl) { - case SEND_XOFF: UDR0 = XOFF_CHAR; flow_ctrl = XOFF_SENT; break; - case SEND_XON: UDR0 = XON_CHAR; flow_ctrl = XON_SENT; break; - default: + if (flow_ctrl == SEND_XOFF) { + UDR0 = XOFF_CHAR; + flow_ctrl = XOFF_SENT; + } else if (flow_ctrl == SEND_XON) { + UDR0 = XON_CHAR; + flow_ctrl = XON_SENT; + } else #endif + { + // Send a byte from the buffer + UDR0 = tx_buffer[tail]; - // Send a byte from the buffer - UDR0 = tx_buffer[tail]; - - // Update tail position - tail++; - if (tail == TX_BUFFER_SIZE) { tail = 0; } - - tx_buffer_tail = tail; - - #if ENABLE_XONXOFF - } - #endif + // Update tail position + tail++; + if (tail == TX_BUFFER_SIZE) { tail = 0; } + + tx_buffer_tail = tail; + } // Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); } @@ -144,7 +144,7 @@ uint8_t serial_read() if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; } #if ENABLE_XONXOFF - if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl != XON_SENT) { + if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) { flow_ctrl = SEND_XON; UCSR0B |= (1 << UDRIE0); // Force TX } @@ -183,7 +183,7 @@ ISR(USART_RX_vect) rx_buffer_head = next_head; #if ENABLE_XONXOFF - if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl != XOFF_SENT) { + if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) { flow_ctrl = SEND_XOFF; UCSR0B |= (1 << UDRIE0); // Force TX } From d3be21693171ed597dd212176c6fb33d35cff622 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sat, 10 Mar 2012 12:34:09 -0700 Subject: [PATCH 14/55] Minor fix related to spindle_stop() crashing abort in certain cases. - Updated spindle_stop() to fix abort bug and to be more in line with v0.8. --- config.h | 12 ++++++++++-- gcode.c | 2 +- main.c | 2 +- serial.c | 2 +- spindle_control.c | 22 +++++++++++----------- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/config.h b/config.h index 9c2305a..0128be8 100644 --- a/config.h +++ b/config.h @@ -115,8 +115,8 @@ #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) // FOR ADVANCED USERS ONLY: Toggles XON/XOFF software flow control for serial communications. -// Officially not supported due to problems involving the Atmega8U2 USB-to-serial chips on current -// future Arduinos boards. The firmware on these chips do not support XON/XOFF flow control +// Officially not supported due to problems involving the Atmega8U2 USB-to-serial chips on all +// new and future Arduinos. The firmware on these chips do not support XON/XOFF flow control // characters and the intermediate buffer in the chips cause latency and overflow problems with // standard terminal programs. However, using specifically-programmed UI's to manage this latency // problem has been confirmed to work, as well as, using older FTDI FT232RL-based Arduinos @@ -141,4 +141,12 @@ #define DECIMAL_MULTIPLIER 100 #endif +// Limit step rate for homing +#define LIMIT_STEP_RATE 1 // (mm/min) + +// Debounce delay is the time delay the controller waits for a "good" signal from the limit switch. +// A delay of 3ms to 5ms is a good starting value. +#define LIMIT_DEBOUNCE_DELAY 5 // (milliseconds) + + #endif diff --git a/gcode.c b/gcode.c index 7e20f89..9d74b9d 100644 --- a/gcode.c +++ b/gcode.c @@ -569,7 +569,7 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t - Evaluation of expressions - Variables - Multiple home locations - - Multiple coordinate systems (Additional ones maybe added in config.h) + - Multiple coordinate systems (Up to 6 may be added via config.h) - Probing - Override control - Tool changes diff --git a/main.c b/main.c index fd75f3b..a074140 100644 --- a/main.c +++ b/main.c @@ -72,7 +72,7 @@ int main(void) protocol_init(); // Clear incoming line data plan_init(); // Clear block buffer and planner variables gc_init(); // Set g-code parser to default state - spindle_init(); + spindle_init(); limits_init(); st_reset(); // Clear stepper subsystem variables. diff --git a/serial.c b/serial.c index 6036d3a..44907c5 100644 --- a/serial.c +++ b/serial.c @@ -200,4 +200,4 @@ void serial_reset_read_buffer() #if ENABLE_XONXOFF flow_ctrl = XON_SENT; #endif -} +} diff --git a/spindle_control.c b/spindle_control.c index 1809a3b..4a2ed2e 100644 --- a/spindle_control.c +++ b/spindle_control.c @@ -3,6 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2012 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 @@ -27,22 +28,26 @@ #include -// TODO: Deprecated. Need to update for new version. - -static int current_direction; +static uint8_t current_direction; void spindle_init() { + current_direction = 0; SPINDLE_ENABLE_DDR |= (1< 0) { SPINDLE_DIRECTION_PORT &= ~(1< Date: Tue, 26 Jun 2012 21:48:42 -0600 Subject: [PATCH 15/55] Added step pulse delay after direction set (Compile-time option only). Updated read me. Added a compile-time only experimental feature that creates a user-specified time delay between a step pulse and a direction pin set (in config.h). This is for users with hardware-specific issues (opto-couplers) that need more than a few microseconds between events, which can lead to slowly progressing step drift after many many direction changes. We suggest to try the hack/fix posted in the Wiki before using this, as this experimental feature may cause Grbl to take a performance hit at high step rates and about complex curves. --- config.h | 41 +++++++++++++++++++++++++++++------------ readme.textile | 8 +++++--- stepper.c | 44 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 72 insertions(+), 21 deletions(-) mode change 100644 => 100755 config.h mode change 100644 => 100755 readme.textile mode change 100644 => 100755 stepper.c diff --git a/config.h b/config.h old mode 100644 new mode 100755 index 0128be8..69290f7 --- a/config.h +++ b/config.h @@ -75,7 +75,7 @@ // 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 commented out, the delay will not be compiled. +// NOTE: If set to zero, 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 @@ -114,19 +114,36 @@ // time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays. #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) -// FOR ADVANCED USERS ONLY: Toggles XON/XOFF software flow control for serial communications. -// Officially not supported due to problems involving the Atmega8U2 USB-to-serial chips on all -// new and future Arduinos. The firmware on these chips do not support XON/XOFF flow control -// characters and the intermediate buffer in the chips cause latency and overflow problems with -// standard terminal programs. However, using specifically-programmed UI's to manage this latency -// problem has been confirmed to work, as well as, using older FTDI FT232RL-based Arduinos -// (Duemilanove) since their firmaware correctly manage the XON/XOFF characters. Other unconfirmed -// methods include using an FTDI board/cable or directly communicate on the RX/TX pins on the -// Arduino, both of which circumvent the Atmega8U2 chip altogether. In any case, please report any -// successes to grbl administrators! +// --------------------------------------------------------------------------------------- +// FOR ADVANCED USERS ONLY: + +// Toggles XON/XOFF software flow control for serial communications. Not officially supported +// due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos. The firmware +// on these chips do not support XON/XOFF flow control characters and the intermediate buffer +// in the chips cause latency and overflow problems with standard terminal programs. However, +// using specifically-programmed UI's to manage this latency problem has been confirmed to work. +// As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard +// terminal programs since their firmware correctly manage these XON/XOFF characters. In any +// case, please report any successes to grbl administrators! #define ENABLE_XONXOFF 0 // Boolean. Default disabled. -// ----------------------------------------------- +// Creates a delay between the direction pin setting and corresponding step pulse by creating +// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare) +// sets the direction pins, and does not immediately set the stepper pins, as it would in +// normal operation. The Timer2 compare fires next to set the stepper pins after the step +// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed +// by the step pulse time plus the step pulse delay. (Thanks langwadt for the idea!) +// This is an experimental feature that should only be used if your setup requires a longer +// delay between direction and step pin settings (some opto coupler based drivers), as it may +// adversely effect Grbl's high-end performance (>10kHz). Please notify Grbl administrators +// of your successes or difficulties, as we will monitor this and possibly integrate this as a +// standard feature for future releases. However, we suggest to first try our direction delay +// hack/solution posted in the Wiki involving inverting the stepper pin mask. +// NOTE: If set greater than zero, step pulse delay will be compiled and enabled. Also, the +// total delay added with the Grbl settings pulse microseconds must not exceed 127 ms. +#define STEP_PULSE_DELAY 0 // Step pulse delay in microseconds. Default disabled. + +// --------------------------------------------------------------------------------------- // TODO: The following options are set as compile-time options for now, until the next EEPROM // settings version has solidified. diff --git a/readme.textile b/readme.textile old mode 100644 new mode 100755 index ad06c0f..9bcc8ea --- a/readme.textile +++ b/readme.textile @@ -19,9 +19,11 @@ Grbl includes full acceleration management with look ahead. That means the contr - Program stop(M0,M1*,M2,M30) initial support. Optional stop to do. - System reset re-initializes grbl without resetting the Arduino and retains machine/home position and work coordinates. - Restructured planner and stepper modules to become independent and ready for future features. - - Planned features: Jog mode, status reporting, runtime settings such as toggling block delete, XON/XOFF flow control. - - Reduce serial read buffer to 128 characters and increased write buffer to 64 characters. - - Misc bug fixes and removed deprecated acceleration enabled code. + - Reduced serial read buffer to 128 characters and increased write buffer to 64 characters. + - Misc bug fixes and removed deprecated acceleration enabled code. + - Planned features: Jog mode, full-featured status reporting, runtime settings such as toggling block delete. + - Advanced compile-time options: Up to 6 work coordinate systems(G54-G59), XON/XOFF flow control (limited support), direction and step pulse time delay. + *Important note for Atmega 168 users:* Going forward, support for Atmega 168 will be dropped due to its limited memory and speed. However, legacy Grbl v0.51 "in the branch called 'v0_51' is still available for use. diff --git a/stepper.c b/stepper.c old mode 100644 new mode 100755 index c340b61..5ba9eda --- a/stepper.c +++ b/stepper.c @@ -62,6 +62,10 @@ 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 static volatile uint8_t busy; // True when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler. +#if STEP_PULSE_DELAY > 0 + static uint8_t step_bits; // Stores out_bits output to complete the step pulse delay +#endif + // __________________________ // /| |\ _________________ ^ // / | | \ /| |\ | @@ -86,8 +90,16 @@ static void st_wake_up() { // Initialize stepper output bits out_bits = (0) ^ (settings.invert_mask); - // Set step pulse time. Ad hoc computation from oscilloscope. - step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3); + // Initialize step pulse timing from settings. Here to ensure updating after re-writing. + #if STEP_PULSE_DELAY > 0 + // Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope. + 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. + step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3); + #endif // Enable steppers by resetting the stepper disable port STEPPERS_DISABLE_PORT &= ~(1< 0 _delay_ms(STEPPER_IDLE_LOCK_TIME); #endif // Disable steppers by setting stepper disable @@ -133,7 +145,11 @@ ISR(TIMER1_COMPA_vect) // Set the direction pins a couple of nanoseconds before we step the steppers STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK); // Then pulse the stepping pins - STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits; + #if STEP_PULSE_DELAY > 0 + step_bits = (STEPPING_PORT & ~STEP_MASK) | out_bits; // Store out_bits to prevent overwriting. + #else // Normal operation + STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | 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 = step_pulse_time; // Reload timer counter @@ -298,6 +314,18 @@ ISR(TIMER2_OVF_vect) TCCR2B = 0; // Disable Timer2 to prevent re-entering this interrupt when it's not needed. } +#if STEP_PULSE_DELAY > 0 + // 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 = step_bits; // Begin step pulse. + } +#endif + // Reset and clear stepper subsystem variables void st_reset() { @@ -314,7 +342,7 @@ void st_init() STEPPING_DDR |= STEPPING_MASK; STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask; STEPPERS_DISABLE_DDR |= 1< 0 + TIMSK2 |= (1< Date: Wed, 27 Jun 2012 07:06:24 -0600 Subject: [PATCH 16/55] No changes. Github commit bug. --- COPYING | 0 Makefile | 0 doc/commands.txt | 0 doc/resources.txt | 0 doc/structure.txt | 0 eeprom.h | 0 gcode.c | 0 gcode.h | 0 limits.c | 0 limits.h | 0 main.c | 0 motion_control.c | 0 motion_control.h | 0 nuts_bolts.c | 0 nuts_bolts.h | 0 planner.c | 0 planner.h | 0 print.c | 0 print.h | 0 protocol.c | 0 protocol.h | 0 script/Obsolete/stream.rb | 0 script/Obsolete/trapezoid_simulator.rb | 0 serial.c | 0 serial.h | 0 settings.c | 0 settings.h | 0 spindle_control.c | 0 spindle_control.h | 0 stepper.h | 0 30 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 COPYING mode change 100644 => 100755 Makefile mode change 100644 => 100755 doc/commands.txt mode change 100644 => 100755 doc/resources.txt mode change 100644 => 100755 doc/structure.txt mode change 100644 => 100755 eeprom.h mode change 100644 => 100755 gcode.c mode change 100644 => 100755 gcode.h mode change 100644 => 100755 limits.c mode change 100644 => 100755 limits.h mode change 100644 => 100755 main.c mode change 100644 => 100755 motion_control.c mode change 100644 => 100755 motion_control.h mode change 100644 => 100755 nuts_bolts.c mode change 100644 => 100755 nuts_bolts.h mode change 100644 => 100755 planner.c mode change 100644 => 100755 planner.h mode change 100644 => 100755 print.c mode change 100644 => 100755 print.h mode change 100644 => 100755 protocol.c mode change 100644 => 100755 protocol.h mode change 100644 => 100755 script/Obsolete/stream.rb mode change 100644 => 100755 script/Obsolete/trapezoid_simulator.rb mode change 100644 => 100755 serial.c mode change 100644 => 100755 serial.h mode change 100644 => 100755 settings.c mode change 100644 => 100755 settings.h mode change 100644 => 100755 spindle_control.c mode change 100644 => 100755 spindle_control.h mode change 100644 => 100755 stepper.h diff --git a/COPYING b/COPYING old mode 100644 new mode 100755 diff --git a/Makefile b/Makefile old mode 100644 new mode 100755 diff --git a/doc/commands.txt b/doc/commands.txt old mode 100644 new mode 100755 diff --git a/doc/resources.txt b/doc/resources.txt old mode 100644 new mode 100755 diff --git a/doc/structure.txt b/doc/structure.txt old mode 100644 new mode 100755 diff --git a/eeprom.h b/eeprom.h old mode 100644 new mode 100755 diff --git a/gcode.c b/gcode.c old mode 100644 new mode 100755 diff --git a/gcode.h b/gcode.h old mode 100644 new mode 100755 diff --git a/limits.c b/limits.c old mode 100644 new mode 100755 diff --git a/limits.h b/limits.h old mode 100644 new mode 100755 diff --git a/main.c b/main.c old mode 100644 new mode 100755 diff --git a/motion_control.c b/motion_control.c old mode 100644 new mode 100755 diff --git a/motion_control.h b/motion_control.h old mode 100644 new mode 100755 diff --git a/nuts_bolts.c b/nuts_bolts.c old mode 100644 new mode 100755 diff --git a/nuts_bolts.h b/nuts_bolts.h old mode 100644 new mode 100755 diff --git a/planner.c b/planner.c old mode 100644 new mode 100755 diff --git a/planner.h b/planner.h old mode 100644 new mode 100755 diff --git a/print.c b/print.c old mode 100644 new mode 100755 diff --git a/print.h b/print.h old mode 100644 new mode 100755 diff --git a/protocol.c b/protocol.c old mode 100644 new mode 100755 diff --git a/protocol.h b/protocol.h old mode 100644 new mode 100755 diff --git a/script/Obsolete/stream.rb b/script/Obsolete/stream.rb old mode 100644 new mode 100755 diff --git a/script/Obsolete/trapezoid_simulator.rb b/script/Obsolete/trapezoid_simulator.rb old mode 100644 new mode 100755 diff --git a/serial.c b/serial.c old mode 100644 new mode 100755 diff --git a/serial.h b/serial.h old mode 100644 new mode 100755 diff --git a/settings.c b/settings.c old mode 100644 new mode 100755 diff --git a/settings.h b/settings.h old mode 100644 new mode 100755 diff --git a/spindle_control.c b/spindle_control.c old mode 100644 new mode 100755 diff --git a/spindle_control.h b/spindle_control.h old mode 100644 new mode 100755 diff --git a/stepper.h b/stepper.h old mode 100644 new mode 100755 From 4d3c720bcc0cec06e9f108913fd5a5fc655ce5ec Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Wed, 19 Sep 2012 20:50:24 -0600 Subject: [PATCH 17/55] M30 minor bug fix. Order of operations was off. Now works as intended, --- gcode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gcode.c b/gcode.c index 9d74b9d..300eceb 100755 --- a/gcode.c +++ b/gcode.c @@ -529,10 +529,11 @@ uint8_t gc_execute_line(char *line) if (gc.program_flow) { plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete. sys.auto_start = false; // Disable auto cycle start. - gc.program_flow = PROGRAM_FLOW_RUNNING; // Re-enable program flow after pause complete. - // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9) + // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9). Otherwise, + // re-enable program flow after pause complete, where cycle start will resume the program. if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { sys.abort = true; } + else { gc.program_flow = PROGRAM_FLOW_RUNNING; } } return(gc.status_code); From f5b1761406c48a8188e070b4d824ce93a19e320b Mon Sep 17 00:00:00 2001 From: Elijah Insua Date: Fri, 21 Sep 2012 00:41:31 -0700 Subject: [PATCH 18/55] Add support for overriding DEVICE and PROGRAMMER By setting environment variables. example: PROGRAMMER=-c arduino -P /dev/tty.usbmodemfa131 make flash --- Makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 922f2b6..1659460 100755 --- a/Makefile +++ b/Makefile @@ -27,26 +27,26 @@ # is connected. # FUSES ........ Parameters for avrdude to flash the fuses appropriately. -DEVICE = atmega328p +DEVICE ?= atmega328p CLOCK = 16000000 -PROGRAMMER = -c avrisp2 -P usb +PROGRAMMER ?= -c avrisp2 -P usb OBJECTS = main.o motion_control.o gcode.o spindle_control.o serial.o protocol.o stepper.o \ eeprom.o settings.o planner.o nuts_bolts.o limits.o print.o # FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m -# update that line with this when programmer is back up: -# FUSES = -U hfuse:w:0xd7:m -U lfuse:w:0xff:m +# update that line with this when programmer is back up: +# FUSES = -U hfuse:w:0xd7:m -U lfuse:w:0xff:m # Tune the lines below only if you know what you are doing: -AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F -COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections +AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F +COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections # symbolic targets: all: grbl.hex .c.o: - $(COMPILE) -c $< -o $@ + $(COMPILE) -c $< -o $@ .S.o: $(COMPILE) -x assembler-with-cpp -c $< -o $@ @@ -90,4 +90,4 @@ disasm: main.elf avr-objdump -d main.elf cpp: - $(COMPILE) -E main.c + $(COMPILE) -E main.c From 420c7c25842f49fc780c7ce3d8f7f00e6f3a4a62 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Fri, 21 Sep 2012 11:14:13 -0600 Subject: [PATCH 19/55] Added coolant control (M7*, M8, M9). Mist control can be enabled via config.h. - Added coolant control! Flood control (M8) functions on analog pin 0. Mist control (M7) is compile-time optional and is on analog pin 1. (Use only if you have multiple coolants on your system). Based on work by @openpnp. - Fixed some variable assignments in spindle control. --- Makefile | 4 +-- config.h | 12 +++++++++ coolant_control.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ coolant_control.h | 34 +++++++++++++++++++++++++ gcode.c | 14 +++++++--- main.c | 2 ++ spindle_control.c | 2 +- spindle_control.h | 2 +- 8 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 coolant_control.c create mode 100644 coolant_control.h diff --git a/Makefile b/Makefile index 1659460..cfc0186 100755 --- a/Makefile +++ b/Makefile @@ -30,8 +30,8 @@ DEVICE ?= atmega328p CLOCK = 16000000 PROGRAMMER ?= -c avrisp2 -P usb -OBJECTS = main.o motion_control.o gcode.o spindle_control.o serial.o protocol.o stepper.o \ - eeprom.o settings.o planner.o nuts_bolts.o limits.o print.o +OBJECTS = main.o motion_control.o gcode.o spindle_control.o coolant_control.o serial.o \ + protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o print.o # FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m # update that line with this when programmer is back up: diff --git a/config.h b/config.h index 69290f7..e6ab7ac 100755 --- a/config.h +++ b/config.h @@ -54,6 +54,18 @@ #define SPINDLE_DIRECTION_PORT PORTB #define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 +#define COOLANT_FLOOD_DDR DDRC +#define COOLANT_FLOOD_PORT PORTC +#define COOLANT_FLOOD_BIT 0 // Uno Analog Pin 0 + +#define ENABLE_M7 0 // DISABLED BY DEFAULT: To enable, change to '1' and recompile. +#if ENABLE_M7 + #define COOLANT_MIST_DDR DDRC + #define COOLANT_MIST_PORT PORTC + #define COOLANT_MIST_BIT 1 // Uno Analog Pin 1 +#endif + + // 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 diff --git a/coolant_control.c b/coolant_control.c new file mode 100644 index 0000000..b3e9984 --- /dev/null +++ b/coolant_control.c @@ -0,0 +1,65 @@ +/* + coolant_control.c - coolant control methods + Part of Grbl + + Copyright (c) 2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include "coolant_control.h" +#include "settings.h" +#include "config.h" +#include "planner.h" + +#include + +static uint8_t current_coolant_mode; + +void coolant_init() +{ + current_coolant_mode = COOLANT_DISABLE; + #if ENABLE_M7 + COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT); + #endif + COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); + coolant_stop(); +} + +void coolant_stop() +{ + #if ENABLE_M7 + COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); + #endif + COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); +} + + +void coolant_run(uint8_t mode) +{ + if (mode != current_coolant_mode) + { + plan_synchronize(); // Ensure coolant turns on when specified in program. + if (mode == COOLANT_FLOOD_ENABLE) { + COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); + #if ENABLE_M7 + } else if (mode == COOLANT_MIST_ENABLE) { + COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); + #endif + } else { + coolant_stop(); + } + current_coolant_mode = mode; + } +} diff --git a/coolant_control.h b/coolant_control.h new file mode 100644 index 0000000..fd2d549 --- /dev/null +++ b/coolant_control.h @@ -0,0 +1,34 @@ +/* + coolant_control.h - spindle control methods + Part of Grbl + + Copyright (c) 2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#ifndef coolant_control_h +#define coolant_control_h + +#include + +#define COOLANT_MIST_ENABLE 2 +#define COOLANT_FLOOD_ENABLE 1 +#define COOLANT_DISABLE 0 // Must be zero. + +void coolant_init(); +void coolant_stop(); +void coolant_run(uint8_t mode); + +#endif \ No newline at end of file diff --git a/gcode.c b/gcode.c index 300eceb..0bcab95 100755 --- a/gcode.c +++ b/gcode.c @@ -29,6 +29,7 @@ #include "settings.h" #include "motion_control.h" #include "spindle_control.h" +#include "coolant_control.h" #include "errno.h" #include "protocol.h" @@ -75,10 +76,11 @@ typedef struct { uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91} uint8_t program_flow; // {M0, M1, M2, M30} int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} + uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9} double feed_rate, seek_rate; // Millimeters/second double position[3]; // Where the interpreter considers the tool to be at this point in the code uint8_t tool; - int16_t spindle_speed; // RPM/100 + uint16_t spindle_speed; // RPM/100 uint8_t plane_axis_0, plane_axis_1, plane_axis_2; // The axes of the selected plane @@ -214,6 +216,11 @@ uint8_t gc_execute_line(char *line) case 3: gc.spindle_direction = 1; break; case 4: gc.spindle_direction = -1; break; case 5: gc.spindle_direction = 0; break; + #if ENABLE_M7 + case 7: gc.coolant_mode = COOLANT_MIST_ENABLE; break; + #endif + case 8: gc.coolant_mode = COOLANT_FLOOD_ENABLE; break; + case 9: gc.coolant_mode = COOLANT_DISABLE; break; default: FAIL(STATUS_UNSUPPORTED_STATEMENT); } break; @@ -279,7 +286,8 @@ uint8_t gc_execute_line(char *line) // [M3,M4,M5]: Update spindle state spindle_run(gc.spindle_direction, gc.spindle_speed); - // ([M7,M8,M9]: Coolant state should be executed here.) + // [*M7,M8,M9]: Update coolant state + coolant_run(gc.coolant_mode); // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets. // NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this @@ -578,7 +586,7 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets) group 1 = {G38.2, G81 - G89} (Motion modes: straight probe, canned cycles) group 6 = {M6} (Tool change) - group 8 = {M7, M8, M9} coolant (special case: M7 and M8 may be active at the same time) + group 8 = {M7} coolant (M7 mist may be enabled via config.h) group 9 = {M48, M49} enable/disable feed and speed override switches group 12 = {G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection group 13 = {G61, G61.1, G64} path control mode diff --git a/main.c b/main.c index a074140..6613f71 100755 --- a/main.c +++ b/main.c @@ -26,6 +26,7 @@ #include "nuts_bolts.h" #include "stepper.h" #include "spindle_control.h" +#include "coolant_control.h" #include "motion_control.h" #include "gcode.h" #include "protocol.h" @@ -73,6 +74,7 @@ int main(void) plan_init(); // Clear block buffer and planner variables gc_init(); // Set g-code parser to default state spindle_init(); + coolant_init(); limits_init(); st_reset(); // Clear stepper subsystem variables. diff --git a/spindle_control.c b/spindle_control.c index 4a2ed2e..abc67e2 100755 --- a/spindle_control.c +++ b/spindle_control.c @@ -43,7 +43,7 @@ void spindle_stop() SPINDLE_ENABLE_PORT &= ~(1< void spindle_init(); -void spindle_run(int direction, uint32_t rpm); +void spindle_run(int8_t direction, uint16_t rpm); void spindle_stop(); #endif From 4224ab4999bdcad9cff026ab584e17c4a2fe93ff Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Fri, 21 Sep 2012 17:55:02 -0600 Subject: [PATCH 20/55] Minor prescalar optimization. Changed up some defines. --- config.h | 15 +++++++-------- coolant_control.c | 4 ++-- gcode.c | 10 +++++----- stepper.c | 26 +++++++++++++------------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/config.h b/config.h index e6ab7ac..910b490 100755 --- a/config.h +++ b/config.h @@ -58,14 +58,13 @@ #define COOLANT_FLOOD_PORT PORTC #define COOLANT_FLOOD_BIT 0 // Uno Analog Pin 0 -#define ENABLE_M7 0 // DISABLED BY DEFAULT: To enable, change to '1' and recompile. -#if ENABLE_M7 +// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable. +#ifdef ENABLE_M7 #define COOLANT_MIST_DDR DDRC #define COOLANT_MIST_PORT PORTC #define COOLANT_MIST_BIT 1 // Uno Analog Pin 1 #endif - // 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 @@ -87,7 +86,7 @@ // 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 set to zero, the delay will not be compiled. +// NOTE: If the define commented, 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 @@ -137,7 +136,7 @@ // As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard // terminal programs since their firmware correctly manage these XON/XOFF characters. In any // case, please report any successes to grbl administrators! -#define ENABLE_XONXOFF 0 // Boolean. Default disabled. +// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable. // Creates a delay between the direction pin setting and corresponding step pulse by creating // another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare) @@ -151,9 +150,9 @@ // of your successes or difficulties, as we will monitor this and possibly integrate this as a // standard feature for future releases. However, we suggest to first try our direction delay // hack/solution posted in the Wiki involving inverting the stepper pin mask. -// NOTE: If set greater than zero, step pulse delay will be compiled and enabled. Also, the -// total delay added with the Grbl settings pulse microseconds must not exceed 127 ms. -#define STEP_PULSE_DELAY 0 // Step pulse delay in microseconds. Default disabled. +// NOTE: Uncomment to enable. The recommended delay should be > 3us but not exceed a total +// time of 127us when added with the Grbl settings pulse microsecond. +// #define STEP_PULSE_DELAY 5 // Step pulse delay in microseconds. Default disabled. // --------------------------------------------------------------------------------------- diff --git a/coolant_control.c b/coolant_control.c index b3e9984..8abd674 100644 --- a/coolant_control.c +++ b/coolant_control.c @@ -39,7 +39,7 @@ void coolant_init() void coolant_stop() { - #if ENABLE_M7 + #ifdef ENABLE_M7 COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); #endif COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); @@ -53,7 +53,7 @@ void coolant_run(uint8_t mode) plan_synchronize(); // Ensure coolant turns on when specified in program. if (mode == COOLANT_FLOOD_ENABLE) { COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); - #if ENABLE_M7 + #ifdef ENABLE_M7 } else if (mode == COOLANT_MIST_ENABLE) { COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); #endif diff --git a/gcode.c b/gcode.c index 0bcab95..f9d46fc 100755 --- a/gcode.c +++ b/gcode.c @@ -216,7 +216,7 @@ uint8_t gc_execute_line(char *line) case 3: gc.spindle_direction = 1; break; case 4: gc.spindle_direction = -1; break; case 5: gc.spindle_direction = 0; break; - #if ENABLE_M7 + #ifdef ENABLE_M7 case 7: gc.coolant_mode = COOLANT_MIST_ENABLE; break; #endif case 8: gc.coolant_mode = COOLANT_FLOOD_ENABLE; break; @@ -578,16 +578,16 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t - Evaluation of expressions - Variables - Multiple home locations - - Multiple coordinate systems (Up to 6 may be added via config.h) - Probing - Override control - Tool changes - + + (*) Indicates optional parameter, enabled through config.h and re-compile group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets) group 1 = {G38.2, G81 - G89} (Motion modes: straight probe, canned cycles) group 6 = {M6} (Tool change) - group 8 = {M7} coolant (M7 mist may be enabled via config.h) + group 8 = {*M7} enable mist coolant group 9 = {M48, M49} enable/disable feed and speed override switches - group 12 = {G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection + group 12 = {*G55, *G56, *G57, *G58, *G59, G59.1, G59.2, G59.3} coordinate system selection group 13 = {G61, G61.1, G64} path control mode */ diff --git a/stepper.c b/stepper.c index 5ba9eda..e4534ed 100755 --- a/stepper.c +++ b/stepper.c @@ -91,7 +91,7 @@ static void st_wake_up() // Initialize stepper output bits out_bits = (0) ^ (settings.invert_mask); // Initialize step pulse timing from settings. Here to ensure updating after re-writing. - #if STEP_PULSE_DELAY > 0 + #ifdef STEP_PULSE_DELAY // Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope. step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3); // Set delay between direction pin write and step command. @@ -113,7 +113,7 @@ void st_go_idle() TIMSK1 &= ~(1< 0 + #ifdef STEPPER_IDLE_LOCK_TIME _delay_ms(STEPPER_IDLE_LOCK_TIME); #endif // Disable steppers by setting stepper disable @@ -145,7 +145,7 @@ ISR(TIMER1_COMPA_vect) // Set the direction pins a couple of nanoseconds before we step the steppers STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK); // Then pulse the stepping pins - #if STEP_PULSE_DELAY > 0 + #ifdef STEP_PULSE_DELAY step_bits = (STEPPING_PORT & ~STEP_MASK) | out_bits; // Store out_bits to prevent overwriting. #else // Normal operation STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits; @@ -314,7 +314,7 @@ ISR(TIMER2_OVF_vect) TCCR2B = 0; // Disable Timer2 to prevent re-entering this interrupt when it's not needed. } -#if STEP_PULSE_DELAY > 0 +#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. @@ -358,7 +358,7 @@ void st_init() TCCR2B = 0; // Disable timer until needed. TIMSK2 |= (1< 0 + #ifdef STEP_PULSE_DELAY TIMSK2 |= (1<> 3; - prescaler = 1; // prescaler: 8 + prescaler = 2; // prescaler: 8 actual_cycles = ceiling * 8L; } else if (cycles <= 0x3fffffL) { ceiling = cycles >> 6; - prescaler = 2; // prescaler: 64 + prescaler = 3; // prescaler: 64 actual_cycles = ceiling * 64L; } else if (cycles <= 0xffffffL) { ceiling = (cycles >> 8); - prescaler = 3; // prescaler: 256 + prescaler = 4; // prescaler: 256 actual_cycles = ceiling * 256L; } else if (cycles <= 0x3ffffffL) { ceiling = (cycles >> 10); - prescaler = 4; // prescaler: 1024 + prescaler = 5; // prescaler: 1024 actual_cycles = ceiling * 1024L; } else { // Okay, that was slower than we actually go. Just set the slowest speed ceiling = 0xffff; - prescaler = 4; + prescaler = 6; actual_cycles = 0xffff * 1024; } // Set prescaler - TCCR1B = (TCCR1B & ~(0x07< Date: Sun, 30 Sep 2012 19:57:10 -0600 Subject: [PATCH 21/55] Updated limit/homing routine. Works, but needs more TLC. - Added acceleration to the homing routine. - Homing now accounts for different step rates when moving multiple axes without exceeding acceleration limits. - Homing now updates all internal positioning variables to machine zero after completion. - "Poor-man's" debounce delay added. - Updated the delay_us() function to perform faster and more accurate microsecond delays. Previously, the single increments would add noticeable time drift for larger delays. - Fix a bug in the stepper.c prescalar calculations that was changed in the last commit. - Other minor fixes. --- config.h | 51 +++++++------- gcode.c | 8 ++- gcode.h | 5 +- limits.c | 172 ++++++++++++++++++++++++++++++++++------------- motion_control.c | 10 ++- nuts_bolts.c | 19 +++++- planner.c | 6 ++ planner.h | 3 + serial.c | 10 +-- stepper.c | 5 +- 10 files changed, 201 insertions(+), 88 deletions(-) diff --git a/config.h b/config.h index 910b490..2ac197f 100755 --- a/config.h +++ b/config.h @@ -40,29 +40,32 @@ #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 // Uno Digital Pin 9 -#define Y_LIMIT_BIT 2 // Uno Digital Pin 10 -#define Z_LIMIT_BIT 3 // Uno Digital Pin 11 +#define LIMIT_DDR DDRB +#define LIMIT_PIN PINB +#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 LIMIT_INT PCIE0 // Pin change interrupt settings +// #define LIMIT_INT_vect PCINT0_vect +// #define LIMIT_PCMSK PCMSK0 -#define SPINDLE_ENABLE_DDR DDRB -#define SPINDLE_ENABLE_PORT PORTB -#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12 +#define SPINDLE_ENABLE_DDR DDRB +#define SPINDLE_ENABLE_PORT PORTB +#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12 -#define SPINDLE_DIRECTION_DDR DDRB -#define SPINDLE_DIRECTION_PORT PORTB -#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 +#define SPINDLE_DIRECTION_DDR DDRB +#define SPINDLE_DIRECTION_PORT PORTB +#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 #define COOLANT_FLOOD_DDR DDRC -#define COOLANT_FLOOD_PORT PORTC -#define COOLANT_FLOOD_BIT 0 // Uno Analog Pin 0 +#define COOLANT_FLOOD_PORT PORTC +#define COOLANT_FLOOD_BIT 0 // Uno Analog Pin 0 // #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable. #ifdef ENABLE_M7 #define COOLANT_MIST_DDR DDRC - #define COOLANT_MIST_PORT PORTC - #define COOLANT_MIST_BIT 1 // Uno Analog Pin 1 + #define COOLANT_MIST_PORT PORTC + #define COOLANT_MIST_BIT 1 // Uno Analog Pin 1 #endif // Define runtime command special characters. These characters are 'picked-off' directly from the @@ -86,7 +89,7 @@ // 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 the define commented, the delay will not be compiled. +// NOTE: If the define commented, the stepper lock will be disabled upon compiling. #define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer > 0 // The temporal resolution of the acceleration management subsystem. Higher number give smoother @@ -150,14 +153,15 @@ // of your successes or difficulties, as we will monitor this and possibly integrate this as a // standard feature for future releases. However, we suggest to first try our direction delay // hack/solution posted in the Wiki involving inverting the stepper pin mask. -// NOTE: Uncomment to enable. The recommended delay should be > 3us but not exceed a total -// time of 127us when added with the Grbl settings pulse microsecond. +// NOTE: Uncomment to enable. The recommended delay should be > 3us and the total step pulse +// time, which includes the Grbl settings pulse microseconds, should not exceed 127us. // #define STEP_PULSE_DELAY 5 // Step pulse delay in microseconds. Default disabled. // --------------------------------------------------------------------------------------- // TODO: The following options are set as compile-time options for now, until the next EEPROM -// settings version has solidified. +// settings version has solidified. This is to prevent having to support dozens of different +// incremental settings versions. #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) @@ -170,11 +174,8 @@ #endif // Limit step rate for homing -#define LIMIT_STEP_RATE 1 // (mm/min) - -// Debounce delay is the time delay the controller waits for a "good" signal from the limit switch. -// A delay of 3ms to 5ms is a good starting value. -#define LIMIT_DEBOUNCE_DELAY 5 // (milliseconds) - +#define LIMIT_DEBOUNCE 50 // Limit switch debounce delay (in ms) +// #define LIMIT_INVERT_MASK 0 // +// #define LIMIT_NORMAL_HIGH 1 // Normal low 0 or normal high 1 #endif diff --git a/gcode.c b/gcode.c index f9d46fc..171eef8 100755 --- a/gcode.c +++ b/gcode.c @@ -114,6 +114,12 @@ void gc_set_current_position(int32_t x, int32_t y, int32_t z) gc.position[Z_AXIS] = z/settings.steps_per_mm[Z_AXIS]; } +// Clears and zeros g-code parser position. Called by homing routine. +void gc_clear_position() +{ + clear_vector(gc.position); +} + static float to_millimeters(double value) { return(gc.inches_mode ? (value * MM_PER_INCH) : value); @@ -336,7 +342,7 @@ uint8_t gc_execute_line(char *line) mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); } mc_go_home(); - clear_vector(gc.position); // Assumes home is at [0,0,0] +// clear_vector(gc.position); // Assumes home is at [0,0,0] axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. break; case NON_MODAL_SET_COORDINATE_OFFSET: diff --git a/gcode.h b/gcode.h index f3d86f1..c4325d1 100755 --- a/gcode.h +++ b/gcode.h @@ -1,5 +1,5 @@ /* - gcode.c - rs274/ngc parser. + gcode.h - rs274/ngc parser. Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -33,4 +33,7 @@ uint8_t gc_execute_line(char *line); // Set g-code parser position. Input in steps. void gc_set_current_position(int32_t x, int32_t y, int32_t z); +// Clear g-code parser position +void gc_clear_position(); + #endif diff --git a/limits.c b/limits.c index 64ff53f..5e8bfa1 100755 --- a/limits.c +++ b/limits.c @@ -3,6 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2012 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 @@ -20,89 +21,164 @@ #include #include +#include #include "stepper.h" #include "settings.h" #include "nuts_bolts.h" #include "config.h" +#include "spindle_control.h" #include "motion_control.h" #include "planner.h" +#include "protocol.h" -// TODO: Deprecated. Need to update for new version. Sys.position now tracks position relative -// to the home position. Limits should update this vector directly. +#define MICROSECONDS_PER_ACCELERATION_TICK (1000000/ACCELERATION_TICKS_PER_SECOND) -void limits_init() { +void limits_init() +{ LIMIT_DDR &= ~(LIMIT_MASK); } -static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, bool reverse_direction, uint32_t microseconds_per_pulse) { - // First home the Z axis - uint32_t step_delay = microseconds_per_pulse - settings.pulse_microseconds; - uint8_t out_bits = DIRECTION_MASK; - uint8_t limit_bits; +// Moves all specified axes in same specified direction (positive=true, negative=false) +// and at the homing rate. Homing is a special motion case, where there is only an +// acceleration followed by abrupt asynchronous stops by each axes reaching their limit +// switch independently. Instead of showhorning homing cycles into the main stepper +// algorithm and overcomplicate things, a stripped-down, lite version of the stepper +// 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. +static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, double homing_rate) +{ + // Determine governing axes with finest step resolution per distance for the Bresenham + // algorithm. This solves the issue when homing multiple axes that have different + // resolutions without exceeding system acceleration setting. It doesn't have to be + // perfect since homing locates machine zero, but should create for a more consistent + // and speedy homing routine. + // NOTE: For each axes enabled, the following calculations assume they physically move + // an equal distance over each time step until they hit a limit switch, aka dogleg. + uint32_t steps[3]; + clear_vector(steps); + if (x_axis) { steps[X_AXIS] = lround(settings.steps_per_mm[X_AXIS]); } + if (y_axis) { steps[Y_AXIS] = lround(settings.steps_per_mm[Y_AXIS]); } + if (z_axis) { steps[Z_AXIS] = lround(settings.steps_per_mm[Z_AXIS]); } + uint32_t step_event_count = max(steps[X_AXIS], max(steps[Y_AXIS], steps[Z_AXIS])); - if (x_axis) { out_bits |= (1<> 1); // Bresenham counters + int32_t counter_y = counter_x; + int32_t counter_z = counter_x; + uint32_t step_delay = dt-settings.pulse_microseconds; // Step delay after pulse + uint32_t step_rate = 0; // Tracks step rate. Initialized from 0 rate. (in step/min) + uint32_t trap_counter = MICROSECONDS_PER_ACCELERATION_TICK/2; // Acceleration trapezoid counter + uint8_t out_bits; for(;;) { - limit_bits = LIMIT_PIN; - if (reverse_direction) { - // Invert limit_bits if this is a reverse homing_cycle - limit_bits ^= LIMIT_MASK; + + // Reset out bits. Both direction and step pins appropriately inverted and set. + out_bits = out_bits0; + + // Set step pins by Bresenham line algorithm. If limit switch reached, disable and + // flag for completion. + if (x_axis) { + counter_x += steps[X_AXIS]; + if (counter_x > 0) { + if (LIMIT_PIN & (1< 0) { + if (LIMIT_PIN & (1< 0) { + if (LIMIT_PIN & (1< dt_min) { // Unless cruising, check for time update. + trap_counter += dt; // Track time passed since last update. + if (trap_counter > MICROSECONDS_PER_ACCELERATION_TICK) { + trap_counter -= MICROSECONDS_PER_ACCELERATION_TICK; + step_rate += delta_rate; // Increment velocity + dt = (1000000*60)/step_rate; // Compute new time increment + if (dt < dt_min) {dt = dt_min;} // If target rate reached, cruise. + step_delay = dt-settings.pulse_microseconds; + } + } } - return; } -static void approach_limit_switch(bool x, bool y, bool z) { - homing_cycle(x, y, z, false, 100000); +static void approach_limit_switch(bool x, bool y, bool z) +{ + homing_cycle(x, y, z, true, settings.default_seek_rate); } + static void leave_limit_switch(bool x, bool y, bool z) { - homing_cycle(x, y, z, true, 500000); + homing_cycle(x, y, z, false, settings.default_feed_rate); } -void limits_go_home() { - plan_synchronize(); - // Store the current limit switch state - uint8_t original_limit_state = LIMIT_PIN; +void limits_go_home() +{ + plan_synchronize(); // Empty all motions in buffer. + + // TODO: Need to come up a better way to manage and set limit switches. + uint8_t original_limit_state = LIMIT_PIN; // Store the current limit switch state + + // Jog all axes toward home to engage their limit switches. approach_limit_switch(false, false, true); // First home the z axis approach_limit_switch(true, true, false); // Then home the x and y axis + delay_ms(LIMIT_DEBOUNCE); // Delay to debounce signal before leaving limit switches + // Xor previous and current limit switch state to determine which were high then but have become // low now. These are the actual installed limit switches. uint8_t limit_switches_present = (original_limit_state ^ LIMIT_PIN) & LIMIT_MASK; + // Now carefully leave the limit switches leave_limit_switch( limit_switches_present & (1< #include "settings.h" #include "config.h" +#include "gcode.h" #include "motion_control.h" #include #include @@ -192,9 +193,12 @@ void mc_dwell(double seconds) } -// TODO: Update limits and homing cycle subprograms for better integration with new features. +// Execute homing cycle to locate and set machine zero. void mc_go_home() { - limits_go_home(); - plan_set_current_position(0,0,0); + limits_go_home(); + // Upon completion, reset all internal position vectors (g-code parser, planner, system) + gc_clear_position(); + plan_clear_position(); + clear_vector_double(sys.position); } diff --git a/nuts_bolts.c b/nuts_bolts.c index 79cc41d..2e2bfe4 100755 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -46,8 +46,23 @@ void delay_ms(uint16_t ms) } // Delays variable defined microseconds. Compiler compatibility fix for _delay_us(), -// which only accepts constants in future compiler releases. +// which only accepts constants in future compiler releases. Written to perform more +// efficiently with larger delays, as the counter adds parasitic time in each iteration. void delay_us(uint16_t us) { - while ( us-- ) { _delay_us(1); } + while (us) { + if (us < 10) { + _delay_us(1); + us--; + } else if (us < 100) { + _delay_us(10); + us -= 10; + } else if (us < 1000) { + _delay_us(100); + us -= 100; + } else { + _delay_ms(1); + us -= 1000; + } + } } diff --git a/planner.c b/planner.c index dac2d62..750af75 100755 --- a/planner.c +++ b/planner.c @@ -481,6 +481,12 @@ void plan_set_current_position(int32_t x, int32_t y, int32_t z) pl.position[Z_AXIS] = z; } +// Clear planner position vector. Called by homing routine. +void plan_clear_position() +{ + clear_vector(pl.position); +} + // Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail. // Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped. void plan_cycle_reinitialize(int32_t step_events_remaining) diff --git a/planner.h b/planner.h index 3f1ba01..a1adc26 100755 --- a/planner.h +++ b/planner.h @@ -69,6 +69,9 @@ block_t *plan_get_current_block(); // Reset the planner position vector (in steps) void plan_set_current_position(int32_t x, int32_t y, int32_t z); +// Clear the planner position vector +void plan_clear_position(); + // Reinitialize plan with a partially completed block void plan_cycle_reinitialize(int32_t step_events_remaining); diff --git a/serial.c b/serial.c index 44907c5..aa35237 100755 --- a/serial.c +++ b/serial.c @@ -42,7 +42,7 @@ uint8_t tx_buffer[TX_BUFFER_SIZE]; uint8_t tx_buffer_head = 0; volatile uint8_t tx_buffer_tail = 0; -#if ENABLE_XONXOFF +#ifdef ENABLE_XONXOFF #define RX_BUFFER_FULL 96 // XOFF high watermark #define RX_BUFFER_LOW 64 // XON low watermark #define SEND_XOFF 1 @@ -110,7 +110,7 @@ ISR(USART_UDRE_vect) // Temporary tx_buffer_tail (to optimize for volatile) uint8_t tail = tx_buffer_tail; - #if ENABLE_XONXOFF + #ifdef ENABLE_XONXOFF if (flow_ctrl == SEND_XOFF) { UDR0 = XOFF_CHAR; flow_ctrl = XOFF_SENT; @@ -143,7 +143,7 @@ uint8_t serial_read() rx_buffer_tail++; if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; } - #if ENABLE_XONXOFF + #ifdef ENABLE_XONXOFF if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) { flow_ctrl = SEND_XON; UCSR0B |= (1 << UDRIE0); // Force TX @@ -182,7 +182,7 @@ ISR(USART_RX_vect) rx_buffer[rx_buffer_head] = data; rx_buffer_head = next_head; - #if ENABLE_XONXOFF + #ifdef ENABLE_XONXOFF if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) { flow_ctrl = SEND_XOFF; UCSR0B |= (1 << UDRIE0); // Force TX @@ -197,7 +197,7 @@ void serial_reset_read_buffer() { rx_buffer_tail = rx_buffer_head; - #if ENABLE_XONXOFF + #ifdef ENABLE_XONXOFF flow_ctrl = XON_SENT; #endif } diff --git a/stepper.c b/stepper.c index e4534ed..912419b 100755 --- a/stepper.c +++ b/stepper.c @@ -31,7 +31,6 @@ #include "nuts_bolts.h" #include #include "planner.h" -#include "limits.h" // Some useful constants #define TICKS_PER_MICROSECOND (F_CPU/1000000) @@ -299,7 +298,7 @@ ISR(TIMER1_COMPA_vect) plan_discard_current_block(); } } - out_bits ^= settings.invert_mask; // Apply stepper invert mask + out_bits ^= settings.invert_mask; // Apply step and direction invert mask busy = false; } @@ -396,7 +395,7 @@ static uint32_t config_step_timer(uint32_t cycles) } else { // Okay, that was slower than we actually go. Just set the slowest speed ceiling = 0xffff; - prescaler = 6; + prescaler = 5; actual_cycles = 0xffff * 1024; } // Set prescaler From ff82489da7773dd9a90e9a0e5bebcd3361411df2 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Mon, 8 Oct 2012 15:57:58 -0600 Subject: [PATCH 22/55] Limit pin internal pull-resistors enabled. Re-wrote read_double() function. Correctly changed all 'double's to 'float's. - Limit pin internal pull-resistors now enabled. Normal high operation. This will be the standard going forward. - Updated all of the 'double' variable types to 'float' to reflect what happens when compiled for the Arduino. Also done for compatibility reasons to @jgeisler0303 's Grbl simulator code. - G-code parser will now ignore 'E' exponent values, since they are reserved g-code characters for some machines. Thanks @csdexter! - The read_double() function was re-written and optimized for use in Grbl. The strtod() avr lib was removed. --- config.h | 3 +- gcode.c | 31 ++++++++-------- limits.c | 11 +++--- main.c | 2 +- motion_control.c | 44 +++++++++++------------ motion_control.h | 8 ++--- nuts_bolts.c | 93 ++++++++++++++++++++++++++++++++++++++++++------ nuts_bolts.h | 10 +++--- planner.c | 32 ++++++++--------- planner.h | 10 +++--- print.c | 2 +- print.h | 2 +- protocol.c | 2 +- settings.c | 16 ++++----- settings.h | 14 ++++---- stepper.c | 13 ++++--- 16 files changed, 183 insertions(+), 110 deletions(-) diff --git a/config.h b/config.h index 2ac197f..dd0a9dc 100755 --- a/config.h +++ b/config.h @@ -42,6 +42,7 @@ #define LIMIT_DDR DDRB #define LIMIT_PIN PINB +#define LIMIT_PORT PORTB #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 @@ -175,7 +176,5 @@ // Limit step rate for homing #define LIMIT_DEBOUNCE 50 // Limit switch debounce delay (in ms) -// #define LIMIT_INVERT_MASK 0 // -// #define LIMIT_NORMAL_HIGH 1 // Normal low 0 or normal high 1 #endif diff --git a/gcode.c b/gcode.c index 171eef8..e4e22d6 100755 --- a/gcode.c +++ b/gcode.c @@ -77,8 +77,8 @@ typedef struct { uint8_t program_flow; // {M0, M1, M2, M30} int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9} - double feed_rate, seek_rate; // Millimeters/second - double position[3]; // Where the interpreter considers the tool to be at this point in the code + float feed_rate, seek_rate; // Millimeters/second + float position[3]; // Where the interpreter considers the tool to be at this point in the code uint8_t tool; uint16_t spindle_speed; // RPM/100 uint8_t plane_axis_0, @@ -89,7 +89,7 @@ static parser_state_t gc; #define FAIL(status) gc.status_code = status; -static int next_statement(char *letter, double *double_ptr, char *line, uint8_t *char_counter); +static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter); static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2) { @@ -120,7 +120,7 @@ void gc_clear_position() clear_vector(gc.position); } -static float to_millimeters(double value) +static float to_millimeters(float value) { return(gc.inches_mode ? (value * MM_PER_INCH) : value); } @@ -133,17 +133,17 @@ uint8_t gc_execute_line(char *line) { uint8_t char_counter = 0; char letter; - double value; + float value; int int_value; uint16_t modal_group_words = 0; // Bitflag variable to track and check modal group words in block uint8_t axis_words = 0; // Bitflag to track which XYZ(ABC) parameters exist in block - double inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified + float inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified uint8_t absolute_override = false; // true(1) = absolute motion for this block only {G53} uint8_t non_modal_action = NON_MODAL_NONE; // Tracks the actions of modal group 0 (non-modal) - double target[3], offset[3]; + float target[3], offset[3]; clear_vector(target); // XYZ(ABC) axes parameters. clear_vector(offset); // IJK Arc offsets are incremental. Value of zero indicates no change. @@ -248,11 +248,12 @@ uint8_t gc_execute_line(char *line) /* Pass 2: Parameters. All units converted according to current block commands. Position parameters are converted and flagged to indicate a change. These can have multiple connotations for different commands. Each will be converted to their proper value upon execution. */ - double p = 0, r = 0; + float p = 0, r = 0; uint8_t l = 0; char_counter = 0; while(next_statement(&letter, &value, line, &char_counter)) { switch(letter) { + case 'G': case 'M': break; // Ignore command statements case 'F': if (value <= 0) { FAIL(STATUS_INVALID_COMMAND); } // Must be greater than zero if (gc.inverse_feed_rate_mode) { @@ -276,6 +277,7 @@ uint8_t gc_execute_line(char *line) case 'X': target[X_AXIS] = to_millimeters(value); bit_true(axis_words,bit(X_AXIS)); break; case 'Y': target[Y_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Y_AXIS)); break; case 'Z': target[Z_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Z_AXIS)); break; + default: FAIL(STATUS_UNSUPPORTED_STATEMENT); } } @@ -342,7 +344,6 @@ uint8_t gc_execute_line(char *line) mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); } mc_go_home(); -// clear_vector(gc.position); // Assumes home is at [0,0,0] axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. break; case NON_MODAL_SET_COORDINATE_OFFSET: @@ -473,11 +474,11 @@ uint8_t gc_execute_line(char *line) */ // Calculate the change in position along each selected axis - double x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0]; - double y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1]; + float x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0]; + float y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1]; clear_vector(offset); - double h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y); // == -(h * 2 / d) + float h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y); // == -(h * 2 / d) // If r is smaller than d, the arc is now traversing the complex plane beyond the reach of any // real CNC, and thus - for practical reasons - we will terminate promptly: if(isnan(h_x2_div_d)) { FAIL(STATUS_FLOATING_POINT_ERROR); return(gc.status_code); } @@ -535,7 +536,7 @@ uint8_t gc_execute_line(char *line) // As far as the parser is concerned, the position is now == target. In reality the // motion control system might still be processing the action and the real tool position // in any intermediate location. - memcpy(gc.position, target, sizeof(double)*3); // gc.position[] = target[]; + memcpy(gc.position, target, sizeof(float)*3); // gc.position[] = target[]; } // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may @@ -556,7 +557,7 @@ uint8_t gc_execute_line(char *line) // Parses the next statement and leaves the counter on the first character following // the statement. Returns 1 if there was a statements, 0 if end of string was reached // or there was an error (check state.status_code). -static int next_statement(char *letter, double *double_ptr, char *line, uint8_t *char_counter) +static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter) { if (line[*char_counter] == 0) { return(0); // No more statements @@ -568,7 +569,7 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t return(0); } (*char_counter)++; - if (!read_double(line, char_counter, double_ptr)) { + if (!read_float(line, char_counter, float_ptr)) { FAIL(STATUS_BAD_NUMBER_FORMAT); return(0); }; diff --git a/limits.c b/limits.c index 5e8bfa1..535c681 100755 --- a/limits.c +++ b/limits.c @@ -35,18 +35,19 @@ void limits_init() { - LIMIT_DDR &= ~(LIMIT_MASK); + LIMIT_DDR &= ~(LIMIT_MASK); // Input pin + LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors for normal high } // Moves all specified axes in same specified direction (positive=true, negative=false) // and at the homing rate. Homing is a special motion case, where there is only an // acceleration followed by abrupt asynchronous stops by each axes reaching their limit -// switch independently. Instead of showhorning homing cycles into the main stepper +// switch independently. Instead of shoehorning homing cycles into the main stepper // algorithm and overcomplicate things, a stripped-down, lite version of the stepper // 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. -static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, double homing_rate) +static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, float homing_rate) { // Determine governing axes with finest step resolution per distance for the Bresenham // algorithm. This solves the issue when homing multiple axes that have different @@ -67,7 +68,7 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, // used in the main planner to account for distance traveled when moving multiple axes. // NOTE: When axis acceleration independence is installed, this will be updated to move // all axes at their maximum acceleration and rate. - double ds = step_event_count/sqrt(x_axis+y_axis+z_axis); + float ds = step_event_count/sqrt(x_axis+y_axis+z_axis); // Compute the adjusted step rate change with each acceleration tick. (in step/min/acceleration_tick) uint32_t delta_rate = ceil( ds*settings.acceleration/(60*ACCELERATION_TICKS_PER_SECOND)); @@ -180,5 +181,5 @@ void limits_go_home() limit_switches_present & (1< #include -int read_double(char *line, uint8_t *char_counter, double *double_ptr) -{ - char *start = line + *char_counter; - char *end; - - *double_ptr = strtod(start, &end); - if(end == start) { - return(false); - }; +#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float) +extern float __floatunsisf (unsigned long); - *char_counter = end - line; +// Extracts a floating point value from a string. The following code is based loosely on +// the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely +// available conversion method examples, but has been highly optimized for Grbl. For known +// CNC applications, the typical decimal value is expected to be in the range of E0 to E-4. +// Scientific notation is officially not supported by g-code, and the 'E' character may +// be a g-code word on some CNC systems. So, 'E' notation will not be recognized. +// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod(). +int read_float(char *line, uint8_t *char_counter, float *float_ptr) +{ + char *ptr = line + *char_counter; + unsigned char c; + + // Grab first character and increment pointer. No spaces assumed in line. + c = *ptr++; + + // Capture initial positive/minus character + bool isnegative = false; + if (c == '-') { + isnegative = true; + c = *ptr++; + } else if (c == '+') { + c = *ptr++; + } + + // Extract number into fast integer. Track decimal in terms of exponent value. + uint32_t intval = 0; + int8_t exp = 0; + uint8_t ndigit = 0; + bool isdecimal = false; + while(1) { + c -= '0'; + if (c <= 9) { + ndigit++; + if (ndigit <= MAX_INT_DIGITS) { + if (isdecimal) { exp--; } + intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c + } else { + if (!(isdecimal)) { exp++; } // Drop overflow digits + } + } else if (c == (('.'-'0') & 0xff) && !(isdecimal)) { + isdecimal = true; + } else { + break; + } + c = *ptr++; + } + + // Return if no digits have been read. + if (!ndigit) { return(false); }; + + // Convert integer into floating point. + float fval; + fval = __floatunsisf(intval); + + // Apply decimal. Should perform no more than two floating point multiplications for the + // expected range of E0 to E-4. + if (fval != 0) { + while (exp <= -2) { + fval *= 0.01; + exp += 2; + } + if (exp < 0) { + fval *= 0.1; + } else if (exp > 0) { + do { + fval *= 10.0; + } while (--exp > 0); + } + } + + // Assign floating point value with correct sign. + if (isnegative) { + *float_ptr = -fval; + } else { + *float_ptr = fval; + } + + *char_counter = ptr - line - 1; // Set char_counter to next statement + return(true); } + // Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(), // which only accepts constants in future compiler releases. void delay_ms(uint16_t ms) @@ -45,6 +117,7 @@ void delay_ms(uint16_t ms) while ( ms-- ) { _delay_ms(1); } } + // Delays variable defined microseconds. Compiler compatibility fix for _delay_us(), // which only accepts constants in future compiler releases. Written to perform more // efficiently with larger delays, as the counter adds parasitic time in each iteration. diff --git a/nuts_bolts.h b/nuts_bolts.h index 5909876..e262d38 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -38,7 +38,7 @@ // Useful macros #define clear_vector(a) memset(a, 0, sizeof(a)) -#define clear_vector_double(a) memset(a, 0.0, sizeof(a)) +#define clear_vector_float(a) memset(a, 0.0, sizeof(a)) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) @@ -74,10 +74,10 @@ typedef struct { // NOTE: This may need to be a volatile variable, if problems arise. uint8_t coord_select; // Active work coordinate system number. Default: 0=G54. - double coord_system[N_COORDINATE_SYSTEM][3]; // Work coordinate systems (G54+). Stores offset from + float coord_system[N_COORDINATE_SYSTEM][3]; // Work coordinate systems (G54+). Stores offset from // absolute machine position in mm. // Rows: Work system number (0=G54,1=G55,...5=G59), Columns: XYZ Offsets - double coord_offset[3]; // Retains the G92 coordinate offset (work coordinates) relative to + float coord_offset[3]; // Retains the G92 coordinate offset (work coordinates) relative to // machine zero in mm. volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program. @@ -86,9 +86,9 @@ typedef struct { 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 +// is the indexer pointing to the current character of the line, while float_ptr is // a pointer to the result variable. Returns true when it succeeds -int read_double(char *line, uint8_t *char_counter, double *double_ptr); +int read_float(char *line, uint8_t *char_counter, float *float_ptr); // Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms(). void delay_ms(uint16_t ms); diff --git a/planner.c b/planner.c index 750af75..0d09cc4 100755 --- a/planner.c +++ b/planner.c @@ -46,8 +46,8 @@ typedef struct { int32_t position[3]; // The planner position of the tool in absolute steps. Kept separate // from g-code position for movements requiring multiple line motions, // i.e. arcs, canned cycles, and backlash compensation. - double previous_unit_vec[3]; // Unit vector of previous path line segment - double previous_nominal_speed; // Nominal speed of previous path line segment + float previous_unit_vec[3]; // Unit vector of previous path line segment + float previous_nominal_speed; // Nominal speed of previous path line segment } planner_t; static planner_t pl; @@ -72,7 +72,7 @@ static uint8_t prev_block_index(uint8_t block_index) // Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the // given acceleration: -static double estimate_acceleration_distance(double initial_rate, double target_rate, double acceleration) +static float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration) { return( (target_rate*target_rate-initial_rate*initial_rate)/(2*acceleration) ); } @@ -91,7 +91,7 @@ static double estimate_acceleration_distance(double initial_rate, double target_ // you started at speed initial_rate and accelerated until this point and want to end at the final_rate after // a total travel of distance. This can be used to compute the intersection point between acceleration and // deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) -static double intersection_distance(double initial_rate, double final_rate, double acceleration, double distance) +static float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) { return( (2*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/(4*acceleration) ); } @@ -102,7 +102,7 @@ static double intersection_distance(double initial_rate, double final_rate, doub // NOTE: sqrt() reimplimented here from prior version due to improved planner logic. Increases speed // in time critical computations, i.e. arcs or rapid short lines from curves. Guaranteed to not exceed // BLOCK_BUFFER_SIZE calls per planner cycle. -static double max_allowable_speed(double acceleration, double target_velocity, double distance) +static float max_allowable_speed(float acceleration, float target_velocity, float distance) { return( sqrt(target_velocity*target_velocity-2*acceleration*distance) ); } @@ -162,7 +162,7 @@ static void planner_forward_pass_kernel(block_t *previous, block_t *current, blo // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. if (!previous->nominal_length_flag) { if (previous->entry_speed < current->entry_speed) { - double entry_speed = min( current->entry_speed, + float entry_speed = min( current->entry_speed, max_allowable_speed(-settings.acceleration,previous->entry_speed,previous->millimeters) ); // Check for junction speed change @@ -205,7 +205,7 @@ static void planner_forward_pass() // The factors represent a factor of braking and must be in the range 0.0-1.0. // This converts the planner parameters to the data required by the stepper controller. // NOTE: Final rates must be computed in terms of their respective blocks. -static void calculate_trapezoid_for_block(block_t *block, double entry_factor, double exit_factor) +static void calculate_trapezoid_for_block(block_t *block, float entry_factor, float exit_factor) { block->initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min) block->final_rate = ceil(block->nominal_rate*exit_factor); // (step/min) @@ -347,7 +347,7 @@ void plan_synchronize() // All position data passed to the planner must be in terms of machine position to keep the planner // independent of any coordinate system changes and offsets, which are handled by the g-code parser. // NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control. -void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate) +void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate) { // Prepare to set up new block block_t *block = &block_buffer[block_buffer_head]; @@ -374,17 +374,17 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in if (block->step_event_count == 0) { return; }; // Compute path vector in terms of absolute step target and current positions - double delta_mm[3]; + float delta_mm[3]; delta_mm[X_AXIS] = (target[X_AXIS]-pl.position[X_AXIS])/settings.steps_per_mm[X_AXIS]; delta_mm[Y_AXIS] = (target[Y_AXIS]-pl.position[Y_AXIS])/settings.steps_per_mm[Y_AXIS]; delta_mm[Z_AXIS] = (target[Z_AXIS]-pl.position[Z_AXIS])/settings.steps_per_mm[Z_AXIS]; block->millimeters = sqrt(delta_mm[X_AXIS]*delta_mm[X_AXIS] + delta_mm[Y_AXIS]*delta_mm[Y_AXIS] + delta_mm[Z_AXIS]*delta_mm[Z_AXIS]); - double inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides + float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides // Calculate speed in mm/minute for each axis. No divide by zero due to previous checks. // NOTE: Minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c - double inverse_minute; + float inverse_minute; if (!invert_feed_rate) { inverse_minute = feed_rate * inverse_millimeters; } else { @@ -404,7 +404,7 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in settings.acceleration / (60 * ACCELERATION_TICKS_PER_SECOND )); // (step/min/acceleration_tick) // Compute path unit vector - double unit_vec[3]; + float unit_vec[3]; unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters; unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters; @@ -419,13 +419,13 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in // path width or max_jerk in the previous grbl version. This approach does not actually deviate // from path, but used as a robust way to compute cornering speeds, as it takes into account the // nonlinearities of both the junction angle and junction velocity. - double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed + float vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. if ((block_buffer_head != block_buffer_tail) && (pl.previous_nominal_speed > 0.0)) { // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. - double cos_theta = - pl.previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] + float cos_theta = - pl.previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] - pl.previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] - pl.previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; @@ -435,7 +435,7 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. if (cos_theta > -0.95) { // Compute maximum junction velocity based on maximum acceleration and junction deviation - double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. + float sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. vmax_junction = min(vmax_junction, sqrt(settings.acceleration * settings.junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) ); } @@ -444,7 +444,7 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in block->max_entry_speed = vmax_junction; // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. - double v_allowable = max_allowable_speed(-settings.acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); + float v_allowable = max_allowable_speed(-settings.acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); block->entry_speed = min(vmax_junction, v_allowable); // Initialize planner efficiency flags diff --git a/planner.h b/planner.h index a1adc26..9c77f90 100755 --- a/planner.h +++ b/planner.h @@ -34,10 +34,10 @@ typedef struct { int32_t step_event_count; // The number of step events required to complete this block // 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 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 + float nominal_speed; // The nominal speed for this block in mm/min + float entry_speed; // Entry speed at previous-current block junction in mm/min + float max_entry_speed; // Maximum allowable junction entry speed in mm/min + float millimeters; // The total travel of this block in mm uint8_t recalculate_flag; // Planner flag to recalculate trapezoids on entry junction uint8_t nominal_length_flag; // Planner flag for nominal speed always reached @@ -57,7 +57,7 @@ void plan_init(); // Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in // millimaters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed // rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. -void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate); +void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate); // Called when the current block is no longer needed. Discards the block and makes the memory // availible for new blocks. diff --git a/print.c b/print.c index 83a8db5..35f8d96 100755 --- a/print.c +++ b/print.c @@ -105,7 +105,7 @@ void printInteger(long n) print_uint32_base10(n); } -void printFloat(double n) +void printFloat(float n) { if (n < 0) { serial_write('-'); diff --git a/print.h b/print.h index f0dff4c..9983aee 100755 --- a/print.h +++ b/print.h @@ -33,6 +33,6 @@ void printInteger(long n); void print_uint8_base2(uint8_t n); -void printFloat(double n); +void printFloat(float n); #endif \ No newline at end of file diff --git a/protocol.c b/protocol.c index 2646f49..a5c9e87 100755 --- a/protocol.c +++ b/protocol.c @@ -245,7 +245,7 @@ void protocol_process() // Enable comments flag and ignore all characters until ')' or EOL. iscomment = true; } else if (char_counter >= LINE_BUFFER_SIZE-1) { - // Throw away any characters beyond the end of the line buffer + // Throw away any characters beyond the end of the line buffer } else if (c >= 'a' && c <= 'z') { // Upcase lowercase line[char_counter++] = c-'a'+'A'; } else { diff --git a/settings.c b/settings.c index 5cc3b16..b44b5ae 100755 --- a/settings.c +++ b/settings.c @@ -33,13 +33,13 @@ settings_t settings; // Version 1 outdated settings record typedef struct { - double steps_per_mm[3]; + float steps_per_mm[3]; uint8_t microsteps; uint8_t pulse_microseconds; - double default_feed_rate; - double default_seek_rate; + float default_feed_rate; + float default_seek_rate; uint8_t invert_mask; - double mm_per_arc_segment; + float mm_per_arc_segment; } settings_v1_t; // Default settings (used when resetting eeprom-settings) @@ -89,20 +89,20 @@ void settings_dump() { // Parameter lines are on the form '$4=374.3' or '$' to dump current settings uint8_t settings_execute_line(char *line) { uint8_t char_counter = 1; - double parameter, value; + float parameter, value; if(line[0] != '$') { return(STATUS_UNSUPPORTED_STATEMENT); } if(line[char_counter] == 0) { settings_dump(); return(STATUS_OK); } - if(!read_double(line, &char_counter, ¶meter)) { + if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }; if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } - if(!read_double(line, &char_counter, &value)) { + if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } if(line[char_counter] != 0) { @@ -158,7 +158,7 @@ int read_settings() { } // A helper method to set settings from command line -void settings_store_setting(int parameter, double value) { +void settings_store_setting(int parameter, float value) { switch(parameter) { case 0: case 1: case 2: if (value <= 0.0) { diff --git a/settings.h b/settings.h index 290c6fb..33dc5b8 100755 --- a/settings.h +++ b/settings.h @@ -33,15 +33,15 @@ // Current global settings (persisted in EEPROM from byte 1 onwards) typedef struct { - double steps_per_mm[3]; + float steps_per_mm[3]; uint8_t microsteps; uint8_t pulse_microseconds; - double default_feed_rate; - double default_seek_rate; + float default_feed_rate; + float default_seek_rate; uint8_t invert_mask; - double mm_per_arc_segment; - double acceleration; - double junction_deviation; + float mm_per_arc_segment; + float acceleration; + float junction_deviation; } settings_t; extern settings_t settings; @@ -55,6 +55,6 @@ void settings_dump(); uint8_t settings_execute_line(char *line); // A helper method to set new settings from command line -void settings_store_setting(int parameter, double value); +void settings_store_setting(int parameter, float value); #endif diff --git a/stepper.c b/stepper.c index 912419b..f03f8fc 100755 --- a/stepper.c +++ b/stepper.c @@ -304,8 +304,10 @@ ISR(TIMER1_COMPA_vect) // 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. -// TODO: It is possible for the serial interrupts to delay this interrupt by a few microseconds, if -// they execute right before this interrupt. Not a big deal, but could use some TLC at some point. +// 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) @@ -355,12 +357,11 @@ void st_init() // Configure Timer 2 TCCR2A = 0; // Normal operation TCCR2B = 0; // Disable timer until needed. - TIMSK2 |= (1< Date: Mon, 8 Oct 2012 16:06:57 -0600 Subject: [PATCH 23/55] Updated version number to v0.8b to reflect changes. --- settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.h b/settings.h index 33dc5b8..cf732db 100755 --- a/settings.h +++ b/settings.h @@ -25,7 +25,7 @@ #include #include -#define GRBL_VERSION "0.8a" +#define GRBL_VERSION "0.8b" // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom From 6506b7a338abf4302eab8d24a061e7ff80431567 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Mon, 8 Oct 2012 17:39:53 -0600 Subject: [PATCH 24/55] Fixed an issue with leaving the limit switches during a homing cycle. --- limits.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/limits.c b/limits.c index 535c681..269f243 100755 --- a/limits.c +++ b/limits.c @@ -35,8 +35,8 @@ void limits_init() { - LIMIT_DDR &= ~(LIMIT_MASK); // Input pin - LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors for normal high + LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins + LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. } // Moves all specified axes in same specified direction (positive=true, negative=false) @@ -47,7 +47,8 @@ void limits_init() // 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. -static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, float homing_rate) +static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, + bool invert_pin, float homing_rate) { // Determine governing axes with finest step resolution per distance for the Bresenham // algorithm. This solves the issue when homing multiple axes that have different @@ -92,17 +93,22 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, uint32_t step_rate = 0; // Tracks step rate. Initialized from 0 rate. (in step/min) uint32_t trap_counter = MICROSECONDS_PER_ACCELERATION_TICK/2; // Acceleration trapezoid counter uint8_t out_bits; + uint8_t limit_state; for(;;) { // Reset out bits. Both direction and step pins appropriately inverted and set. out_bits = out_bits0; + // Get limit pin state. + limit_state = LIMIT_PIN; + if (invert_pin) { limit_state ^= LIMIT_MASK; } // If leaving switch, invert to move. + // Set step pins by Bresenham line algorithm. If limit switch reached, disable and // flag for completion. if (x_axis) { counter_x += steps[X_AXIS]; if (counter_x > 0) { - if (LIMIT_PIN & (1< 0) { - if (LIMIT_PIN & (1< 0) { - if (LIMIT_PIN & (1< Date: Tue, 9 Oct 2012 22:01:10 -0600 Subject: [PATCH 25/55] Improved homing cycle. New settings: homing enable/rates, debounce and step idle lock time. - Homing cycle will now cycle twice (spec more/less in config) to improve repeatability and accuracy by decreasing overshoot. - New Grbl settings added: Enable/disable homing cycles, homing seek and feed rates, switch debounce delay, and stepper idle lock time. - Please note that these settings may change upon the next push, since there will be more added soon. Grbl *should* not re-write your old settings, just re-write the new ones. So, make sure you keep these written down somewhere in case they get lost from a code bug. - Refactored settings migration to be a little smaller and managable going forward. --- config.h | 27 ++++++------ gcode.c | 8 +++- limits.c | 34 ++++++++------- protocol.c | 2 + protocol.h | 1 + settings.c | 121 ++++++++++++++++++++++++++++++++++++++++------------- settings.h | 13 +++++- stepper.c | 4 +- 8 files changed, 146 insertions(+), 64 deletions(-) diff --git a/config.h b/config.h index dd0a9dc..7da9082 100755 --- a/config.h +++ b/config.h @@ -84,15 +84,6 @@ // This parameter must be one or greater, currently supporting up to a value of 6. #define N_COORDINATE_SYSTEM 1 -// This parameter sets the delay time before disabling the steppers after the final block of movement. -// A short delay ensures the steppers come to a complete stop and the residual inertial force in the -// CNC axes don't cause the axes to drift off position. This is particularly important when manually -// 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 the define commented, the stepper lock will be disabled upon compiling. -#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. // NOTE: Increasing this parameter will help any resolution related issues, especially with machines @@ -102,7 +93,7 @@ // round-off can be great enough to cause problems and/or it's too fast for the Arduino. The correct // value for this parameter is machine dependent, so it's advised to set this only as high as needed. // Approximate successful values can range from 30L to 100L or more. -#define ACCELERATION_TICKS_PER_SECOND 50L +#define ACCELERATION_TICKS_PER_SECOND 60L // Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end // of the buffer and all stops. This should not be much greater than zero and should only be changed @@ -129,6 +120,11 @@ // time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays. #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) +// Number of homing cycles performed after when the machine initially jogs to limit switches. +// This help in preventing overshoot and should improve repeatability. This value should be one or +// greater. +#define N_HOMING_CYCLE 2 // Integer (1-128) + // --------------------------------------------------------------------------------------- // FOR ADVANCED USERS ONLY: @@ -174,7 +170,14 @@ #define DECIMAL_MULTIPLIER 100 #endif -// Limit step rate for homing -#define LIMIT_DEBOUNCE 50 // Limit switch debounce delay (in ms) +// This parameter sets the delay time before disabling the steppers after the final block of movement. +// A short delay ensures the steppers come to a complete stop and the residual inertial force in the +// CNC axes don't cause the axes to drift off position. This is particularly important when manually +// 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 the define commented, the stepper lock will be disabled upon compiling. +// -> NOW INSTALLED IN SETTINGS #define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer > 0 + #endif diff --git a/gcode.c b/gcode.c index e4e22d6..d83fc5a 100755 --- a/gcode.c +++ b/gcode.c @@ -179,7 +179,13 @@ uint8_t gc_execute_line(char *line) case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; case 20: gc.inches_mode = true; break; case 21: gc.inches_mode = false; break; - case 28: case 30: non_modal_action = NON_MODAL_GO_HOME; break; + case 28: case 30: + if (bit_istrue(settings.flags,FLAG_BIT_HOMING_ENABLE)) { + non_modal_action = NON_MODAL_GO_HOME; + } else { + FAIL(STATUS_SETTING_DISABLED); + } + break; case 53: absolute_override = true; break; case 54: case 55: case 56: case 57: case 58: case 59: int_value -= 54; // Compute coordinate system row index (0=G54,1=G55,...) diff --git a/limits.c b/limits.c index 269f243..05c7f1f 100755 --- a/limits.c +++ b/limits.c @@ -156,26 +156,28 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, } } -static void approach_limit_switch(bool x, bool y, bool z) -{ - homing_cycle(x, y, z, true, false, settings.default_seek_rate); -} - - -static void leave_limit_switch(bool x, bool y, bool z) { - homing_cycle(x, y, z, false, true, settings.default_feed_rate); -} void limits_go_home() { plan_synchronize(); // Empty all motions in buffer. - // Jog all axes toward home to engage their limit switches. - approach_limit_switch(false, false, true); // First home the z axis - approach_limit_switch(true, true, false); // Then home the x and y axis - delay_ms(LIMIT_DEBOUNCE); // Delay to debounce signal before leaving limit switches + // Jog all axes toward home to engage their limit switches at faster homing seek rate. + homing_cycle(false, false, true, true, false, settings.homing_seek_rate); // First jog the z axis + homing_cycle(true, true, false, true, false, settings.homing_seek_rate); // Then jog the x and y axis + delay_ms(settings.homing_debounce_delay); // Delay to debounce signal - // Now carefully leave the limit switches - leave_limit_switch(true,true,true); - delay_ms(LIMIT_DEBOUNCE); // Delay to debounce signal before exiting routine + // 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_CYCLE; + while (n_cycle--) { + // Leave all switches to release them. After cycles complete, this is machine zero. + homing_cycle(true, true, true, false, true, settings.homing_feed_rate); + delay_ms(settings.homing_debounce_delay); + + if (n_cycle > 0) { + // Re-approach all switches to re-engage them. + homing_cycle(true, true, true, true, false, settings.homing_feed_rate); + delay_ms(settings.homing_debounce_delay); + } + } } diff --git a/protocol.c b/protocol.c index a5c9e87..dbfa39f 100755 --- a/protocol.c +++ b/protocol.c @@ -57,6 +57,8 @@ static void status_message(int status_code) printPgmString(PSTR("Modal group violation\r\n")); break; case STATUS_INVALID_COMMAND: printPgmString(PSTR("Invalid command\r\n")); break; + case STATUS_SETTING_DISABLED: + printPgmString(PSTR("Grbl setting disabled\r\n")); break; default: printInteger(status_code); printPgmString(PSTR("\r\n")); diff --git a/protocol.h b/protocol.h index 8c341fe..2a43c95 100755 --- a/protocol.h +++ b/protocol.h @@ -28,6 +28,7 @@ #define STATUS_FLOATING_POINT_ERROR 4 #define STATUS_MODAL_GROUP_VIOLATION 5 #define STATUS_INVALID_COMMAND 6 +#define STATUS_SETTING_DISABLED 7 // Initialize the serial protocol void protocol_init(); diff --git a/settings.c b/settings.c index b44b5ae..aa71f83 100755 --- a/settings.c +++ b/settings.c @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -42,6 +42,19 @@ typedef struct { float mm_per_arc_segment; } settings_v1_t; +// Version 2,3,4 outdated settings record +typedef struct { + float steps_per_mm[3]; + uint8_t microsteps; + uint8_t pulse_microseconds; + float default_feed_rate; + float default_seek_rate; + uint8_t invert_mask; + float mm_per_arc_segment; + float acceleration; + float junction_deviation; +} settings_v2_v4_t; + // Default settings (used when resetting eeprom-settings) #define MICROSTEPS 8 #define DEFAULT_X_STEPS_PER_MM (94.488188976378*MICROSTEPS) @@ -54,7 +67,15 @@ 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<= 50) { + // Developmental settings. Version numbers greater than or equal to 50 are temporary. + // Currently, this will update the user settings to v4 and the remainder of the settings + // should be re-written to the default value, if the developmental version number changed. + + // Grab settings regardless of error. + memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)); + + settings.flags = 0; + // if (DEFAULT_AUTO_START) { settings.flags |= FLAG_BIT_AUTO_START; } + if (DEFAULT_HOMING_ENABLE) { settings.flags |= FLAG_BIT_HOMING_ENABLE; } + settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; + settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; + settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; + settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; + + write_settings(); + + } else { return(false); } - settings.acceleration = DEFAULT_ACCELERATION; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; -// settings.auto_start = DEFAULT_AUTO_START; - write_settings(); - } else if ((version == 2) || (version == 3)) { - // Migrate from settings version 2 and 3 - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)))) { - return(false); - } - if (version == 2) { settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; } - settings.acceleration *= 3600; // Convert to mm/min^2 from mm/sec^2 -// settings.auto_start = DEFAULT_AUTO_START; - write_settings(); -// } else if (version == 4) { -// // Migrate from settings version 4 -// if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)))) { -// return(false); -// } -// settings.auto_start = DEFAULT_AUTO_START; -// write_settings(); - } else { - return(false); } return(true); } @@ -178,7 +230,16 @@ void settings_store_setting(int parameter, float value) { case 7: settings.invert_mask = trunc(value); break; case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. case 9: settings.junction_deviation = fabs(value); break; -// case 10: settings.auto_start = value; break; + case 10: + if (value) { + settings.flags |= FLAG_BIT_HOMING_ENABLE; + printPgmString(PSTR("Install all axes limit switches before use\r\n")); + } else { settings.flags &= ~FLAG_BIT_HOMING_ENABLE; } + break; + case 11: settings.homing_feed_rate = value; break; + case 12: settings.homing_seek_rate = value; break; + case 13: settings.homing_debounce_delay = round(value); break; + case 14: settings.stepper_idle_lock_time = round(value); break; default: printPgmString(PSTR("Unknown parameter\r\n")); return; diff --git a/settings.h b/settings.h index cf732db..3341223 100755 --- a/settings.h +++ b/settings.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 @@ -29,7 +29,11 @@ // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom -#define SETTINGS_VERSION 4 +#define SETTINGS_VERSION 50 + +// Define bit flag masks in settings.flag. +#define FLAG_BIT_HOMING_ENABLE bit(0) +//#define FLAG_BIT_AUTO_START bit(1) // Current global settings (persisted in EEPROM from byte 1 onwards) typedef struct { @@ -42,6 +46,11 @@ typedef struct { float mm_per_arc_segment; float acceleration; float junction_deviation; + uint8_t flags; // Contains default toggles + float homing_feed_rate; + float homing_seek_rate; + uint16_t homing_debounce_delay; + uint8_t stepper_idle_lock_time; } settings_t; extern settings_t settings; diff --git a/stepper.c b/stepper.c index f03f8fc..148d29f 100755 --- a/stepper.c +++ b/stepper.c @@ -112,9 +112,7 @@ void st_go_idle() TIMSK1 &= ~(1< Date: Wed, 10 Oct 2012 18:01:43 -0600 Subject: [PATCH 26/55] Homing direction pin bits fixed. Lite refactoring of settings. --- limits.c | 5 ++--- settings.c | 63 +++++++++++++++++++++++++----------------------------- settings.h | 3 ++- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/limits.c b/limits.c index 05c7f1f..ed2fc7d 100755 --- a/limits.c +++ b/limits.c @@ -80,9 +80,8 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, uint32_t dt_min = lround(1000000*60/(ds*homing_rate)); // Cruising (usec/step) uint32_t dt = 1000000*60/MINIMUM_STEPS_PER_MINUTE; // Initial (usec/step) - // Determine default out_bits set. Direction fixed and step pin inverted - uint8_t out_bits0 = DIRECTION_MASK; - out_bits0 ^= settings.invert_mask; // Apply the global step and direction invert mask + // Set default out_bits. + uint8_t out_bits0 = settings.invert_mask; if (!pos_dir) { out_bits0 ^= DIRECTION_MASK; } // Invert bits, if negative dir. // Initialize stepping variables diff --git a/settings.c b/settings.c index aa71f83..86e4439 100755 --- a/settings.c +++ b/settings.c @@ -75,19 +75,32 @@ typedef struct { #define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k) #define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-255) // #define DEFAULT_AUTO_START 1 // true +// #define DEFAULT_INCHES_MODE 1 // true // #define DEFAULT_BLOCK_DELETE 0 // false -void settings_reset() { - settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; - settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; - settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; - settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; - settings.default_feed_rate = DEFAULT_FEEDRATE; - settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE; - settings.acceleration = DEFAULT_ACCELERATION; - settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT; - settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; +void settings_reset(bool reset_all) { + // Reset all settings or only the migration settings to the new version. + if (reset_all) { + settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; + settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; + settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; + settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; + settings.default_feed_rate = DEFAULT_FEEDRATE; + settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE; + settings.acceleration = DEFAULT_ACCELERATION; + settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT; + settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; + settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; + } + // New settings since last version + settings.flags = 0; + // if (DEFAULT_AUTO_START) { settings.flags |= FLAG_BIT_AUTO_START; } + // if (DEFAULT_INCHES_MODE) { settings.flags |= FLAG_BIT_INCHES_MODE; } + if (DEFAULT_HOMING_ENABLE) { settings.flags |= FLAG_BIT_HOMING_ENABLE; } + settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; + settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; + settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; + settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; } void settings_dump() { @@ -107,7 +120,7 @@ void settings_dump() { printPgmString(PSTR(" (mm/min homing feed rate)\r\n$12 = ")); printFloat(settings.homing_seek_rate); printPgmString(PSTR(" (mm/min homing seek rate)\r\n$13 = ")); printInteger(settings.homing_debounce_delay); printPgmString(PSTR(" (milliseconds homing debounce delay)\r\n$14 = ")); printInteger(settings.stepper_idle_lock_time); - printPgmString(PSTR(" (milliseconds stepper idle lock time)\r\n")); + printPgmString(PSTR(" (milliseconds stepper idle lock time)")); printPgmString(PSTR("\r\n'$x=value' to set parameter or just '$' to dump current settings\r\n")); } @@ -172,18 +185,9 @@ int read_settings() { // Migrate from settings version 4 to current version. if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v2_v4_t)))) { return(false); - } - - settings.flags = 0; - // if (DEFAULT_AUTO_START) { settings.flags |= FLAG_BIT_AUTO_START; } - if (DEFAULT_HOMING_ENABLE) { settings.flags |= FLAG_BIT_HOMING_ENABLE; } - settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; - settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; - settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; - settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; - + } + settings_reset(false); write_settings(); - } else if (version >= 50) { // Developmental settings. Version numbers greater than or equal to 50 are temporary. // Currently, this will update the user settings to v4 and the remainder of the settings @@ -191,17 +195,8 @@ int read_settings() { // Grab settings regardless of error. memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)); - - settings.flags = 0; - // if (DEFAULT_AUTO_START) { settings.flags |= FLAG_BIT_AUTO_START; } - if (DEFAULT_HOMING_ENABLE) { settings.flags |= FLAG_BIT_HOMING_ENABLE; } - settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; - settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; - settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; - settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; - + settings_reset(false); write_settings(); - } else { return(false); } @@ -252,7 +247,7 @@ void settings_store_setting(int parameter, float value) { void settings_init() { if(!read_settings()) { printPgmString(PSTR("Warning: Failed to read EEPROM settings. Using defaults.\r\n")); - settings_reset(); + settings_reset(true); write_settings(); settings_dump(); } diff --git a/settings.h b/settings.h index 3341223..f20f015 100755 --- a/settings.h +++ b/settings.h @@ -29,11 +29,12 @@ // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom -#define SETTINGS_VERSION 50 +#define SETTINGS_VERSION 51 // Define bit flag masks in settings.flag. #define FLAG_BIT_HOMING_ENABLE bit(0) //#define FLAG_BIT_AUTO_START bit(1) +//#define FLAG_BIT_INCHES_MODE bit(2) // Current global settings (persisted in EEPROM from byte 1 onwards) typedef struct { From d8ca4176bf7f5e410105d26200390903a6f5fe68 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Thu, 11 Oct 2012 00:06:52 -0600 Subject: [PATCH 27/55] Homing stepper enable bit fix. --- limits.c | 6 ++++++ print.c | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/limits.c b/limits.c index ed2fc7d..d1cd7a9 100755 --- a/limits.c +++ b/limits.c @@ -160,6 +160,9 @@ void limits_go_home() { plan_synchronize(); // Empty all motions in buffer. + // Enable steppers by resetting the stepper disable port + STEPPERS_DISABLE_PORT &= ~(1< Date: Thu, 11 Oct 2012 22:43:54 -0600 Subject: [PATCH 28/55] (2x) speed increase in printFloat() function. Decimal places setting added. - printFloat() function execution doubled in speed. This is a precursor to status reporting, since GUIs may query real-time position rapidly. - Decimal places added to settings (for now). This may disappear in future pushes, but here for testing purposes. --- print.c | 57 +++++++++++++++++++++++++++++++++++------------------- settings.c | 6 +++++- settings.h | 3 ++- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/print.c b/print.c index f9da5ab..7aa710c 100755 --- a/print.c +++ b/print.c @@ -27,6 +27,7 @@ #include #include "config.h" #include "serial.h" +#include "settings.h" void printString(const char *s) { @@ -88,12 +89,12 @@ static void print_uint32_base10(unsigned long n) } while (n > 0) { - buf[i++] = n % 10; + buf[i++] = n % 10 + '0'; n /= 10; } - + for (; i > 0; i--) - serial_write('0' + buf[i - 1]); + serial_write(buf[i-1]); } void printInteger(long n) @@ -105,27 +106,43 @@ void printInteger(long n) print_uint32_base10(n); } -void printFloat(double n) +// Convert float to string by immediately converting to a long integer, which contains +// more digits than a float. Number of decimal places, which are tracked by a counter, +// may be set by the user. The integer is then efficiently converted to a string. +void printFloat(float n) { 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('.'); - - n -= integer_part; - int decimals = DECIMAL_PLACES; - uint8_t decimal_part; - while(decimals-- > 0) { - n *= 10; - decimal_part = (int) n; - serial_write('0'+decimal_part); - n -= decimal_part; + + uint8_t decimals = settings.decimal_places; + while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4. + n *= 100; + decimals -= 2; } + if (decimals) { n *= 10; } + n += 0.5; // Add rounding factor. Ensures carryover through entire value. + + // Generate digits backwards and store in string. + unsigned char buf[10]; + uint8_t i = 0; + uint32_t a = (long)n; + buf[settings.decimal_places] = '.'; // Place decimal point, even if decimal places are zero. + while(a > 0) { + if (i == settings.decimal_places) { i++; } // Skip decimal point location + buf[i++] = (a % 10) + '0'; // Get digit + a /= 10; + } + while (i < settings.decimal_places) { + buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1) + } + if (i == settings.decimal_places) { // Fill in leading zero, if needed. + i++; + buf[i++] = '0'; + } + + // Print the generated string. + for (; i > 0; i--) + serial_write(buf[i-1]); } diff --git a/settings.c b/settings.c index 86e4439..92c8226 100755 --- a/settings.c +++ b/settings.c @@ -77,6 +77,7 @@ typedef struct { // #define DEFAULT_AUTO_START 1 // true // #define DEFAULT_INCHES_MODE 1 // true // #define DEFAULT_BLOCK_DELETE 0 // false +#define DEFAULT_DECIMAL_PLACES 3 void settings_reset(bool reset_all) { // Reset all settings or only the migration settings to the new version. @@ -101,6 +102,7 @@ void settings_reset(bool reset_all) { settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; + settings.decimal_places = DEFAULT_DECIMAL_PLACES; } void settings_dump() { @@ -120,7 +122,8 @@ void settings_dump() { printPgmString(PSTR(" (mm/min homing feed rate)\r\n$12 = ")); printFloat(settings.homing_seek_rate); printPgmString(PSTR(" (mm/min homing seek rate)\r\n$13 = ")); printInteger(settings.homing_debounce_delay); printPgmString(PSTR(" (milliseconds homing debounce delay)\r\n$14 = ")); printInteger(settings.stepper_idle_lock_time); - printPgmString(PSTR(" (milliseconds stepper idle lock time)")); + printPgmString(PSTR(" (milliseconds stepper idle lock time)\r\n$15 = ")); printInteger(settings.decimal_places); + printPgmString(PSTR(" (float decimal places)")); printPgmString(PSTR("\r\n'$x=value' to set parameter or just '$' to dump current settings\r\n")); } @@ -235,6 +238,7 @@ void settings_store_setting(int parameter, float value) { case 12: settings.homing_seek_rate = value; break; case 13: settings.homing_debounce_delay = round(value); break; case 14: settings.stepper_idle_lock_time = round(value); break; + case 15: settings.decimal_places = round(value); break; default: printPgmString(PSTR("Unknown parameter\r\n")); return; diff --git a/settings.h b/settings.h index f20f015..cb15bd6 100755 --- a/settings.h +++ b/settings.h @@ -29,7 +29,7 @@ // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom -#define SETTINGS_VERSION 51 +#define SETTINGS_VERSION 52 // Define bit flag masks in settings.flag. #define FLAG_BIT_HOMING_ENABLE bit(0) @@ -52,6 +52,7 @@ typedef struct { float homing_seek_rate; uint16_t homing_debounce_delay; uint8_t stepper_idle_lock_time; + uint8_t decimal_places; } settings_t; extern settings_t settings; From 00701ff24e8213d41d591b6e28d8b5f2c69d5a37 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Fri, 12 Oct 2012 08:27:14 -0600 Subject: [PATCH 29/55] Updated delay_us() function to accept long integers --- config.h | 7 ------- nuts_bolts.c | 2 +- nuts_bolts.h | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/config.h b/config.h index 7da9082..fc5605e 100755 --- a/config.h +++ b/config.h @@ -162,13 +162,6 @@ #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 // This parameter sets the delay time before disabling the steppers after the final block of movement. // A short delay ensures the steppers come to a complete stop and the residual inertial force in the diff --git a/nuts_bolts.c b/nuts_bolts.c index f25c7a6..324b043 100755 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -121,7 +121,7 @@ void delay_ms(uint16_t ms) // Delays variable defined microseconds. Compiler compatibility fix for _delay_us(), // which only accepts constants in future compiler releases. Written to perform more // efficiently with larger delays, as the counter adds parasitic time in each iteration. -void delay_us(uint16_t us) +void delay_us(uint32_t us) { while (us) { if (us < 10) { diff --git a/nuts_bolts.h b/nuts_bolts.h index e262d38..8e9b58b 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -94,6 +94,6 @@ int read_float(char *line, uint8_t *char_counter, float *float_ptr); void delay_ms(uint16_t ms); // Delays variable-defined microseconds. Compiler compatibility fix for _delay_us(). -void delay_us(uint16_t us); +void delay_us(uint32_t us); #endif From 34f6d2eb4b3e25aec008889451e6fd4276ee055b Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sat, 13 Oct 2012 13:11:43 -0600 Subject: [PATCH 30/55] Minor updates, improvements, and bug fixes. - Allowed status_message function to be called by others. This is to centralize all feedback into protocol.c. - Fixed a bug where line number words 'N' were causing the parser to error out. - Allowed homing routine feed rates to move slower than the MINIMUM_STEP_RATE parameter in config.h. - Homing performs idle lock at the end of the routine. - Stepper idle lock time will now not disable the steppers when the value is set at 255. This is accomodate users who prefer to keep their axes enabled at all times. - Moved some defines around to where they need to be. --- gcode.c | 5 ++++- limits.c | 14 ++++++++++---- limits.h | 2 ++ planner.c | 6 ++++++ protocol.c | 8 +++----- protocol.h | 5 +++++ settings.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- settings.h | 4 +++- stepper.c | 6 ++++-- stepper.h | 1 - 10 files changed, 81 insertions(+), 15 deletions(-) diff --git a/gcode.c b/gcode.c index d83fc5a..c59bac7 100755 --- a/gcode.c +++ b/gcode.c @@ -104,6 +104,8 @@ void gc_init() gc.feed_rate = settings.default_feed_rate; select_plane(X_AXIS, Y_AXIS, Z_AXIS); gc.absolute_mode = true; + +// protocol_status_message(settings_execute_startup()); } // Sets g-code parser position in mm. Input in steps. Called by the system abort routine. @@ -180,6 +182,7 @@ uint8_t gc_execute_line(char *line) case 20: gc.inches_mode = true; break; case 21: gc.inches_mode = false; break; case 28: case 30: + // NOTE: G28.1, G30.1 sets home position parameters. Not currently supported. if (bit_istrue(settings.flags,FLAG_BIT_HOMING_ENABLE)) { non_modal_action = NON_MODAL_GO_HOME; } else { @@ -259,7 +262,7 @@ uint8_t gc_execute_line(char *line) char_counter = 0; while(next_statement(&letter, &value, line, &char_counter)) { switch(letter) { - case 'G': case 'M': break; // Ignore command statements + case 'G': case 'M': case 'N': break; // Ignore command statements and line numbers case 'F': if (value <= 0) { FAIL(STATUS_INVALID_COMMAND); } // Must be greater than zero if (gc.inverse_feed_rate_mode) { diff --git a/limits.c b/limits.c index d1cd7a9..da36409 100755 --- a/limits.c +++ b/limits.c @@ -30,6 +30,7 @@ #include "motion_control.h" #include "planner.h" #include "protocol.h" +#include "limits.h" #define MICROSECONDS_PER_ACCELERATION_TICK (1000000/ACCELERATION_TICKS_PER_SECOND) @@ -76,9 +77,11 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, // Nominal and initial time increment per step. Nominal should always be greater then 3 // usec, since they are based on the same parameters as the main stepper routine. Initial - // is based on the MINIMUM_STEPS_PER_MINUTE config. + // is based on the MINIMUM_STEPS_PER_MINUTE config. Since homing feed can be very slow, + // disable acceleration when rates are below MINIMUM_STEPS_PER_MINUTE. uint32_t dt_min = lround(1000000*60/(ds*homing_rate)); // Cruising (usec/step) uint32_t dt = 1000000*60/MINIMUM_STEPS_PER_MINUTE; // Initial (usec/step) + if (dt > dt_min) { dt = dt_min; } // Disable acceleration for very slow rates. // Set default out_bits. uint8_t out_bits0 = settings.invert_mask; @@ -164,8 +167,8 @@ void limits_go_home() STEPPERS_DISABLE_PORT &= ~(1< 0) {// Line is complete. Then execute! line[char_counter] = 0; // Terminate string - status_message(protocol_execute_line(line)); + protocol_status_message(protocol_execute_line(line)); } else { // Empty or comment line. Skip block. - status_message(STATUS_OK); // Send status message for syncing purposes. + protocol_status_message(STATUS_OK); // Send status message for syncing purposes. } char_counter = 0; // Reset line buffer index iscomment = false; // Reset comment flag diff --git a/protocol.h b/protocol.h index 2a43c95..559560c 100755 --- a/protocol.h +++ b/protocol.h @@ -30,6 +30,8 @@ #define STATUS_INVALID_COMMAND 6 #define STATUS_SETTING_DISABLED 7 +#define LINE_BUFFER_SIZE 50 + // Initialize the serial protocol void protocol_init(); @@ -43,4 +45,7 @@ uint8_t protocol_execute_line(char *line); // Checks and executes a runtime command at various stop points in main program void protocol_execute_runtime(); +// Prints g-code parser status message. +void protocol_status_message(int8_t status_code); + #endif diff --git a/settings.c b/settings.c index 92c8226..e012c5c 100755 --- a/settings.c +++ b/settings.c @@ -105,6 +105,10 @@ void settings_reset(bool reset_all) { settings.decimal_places = DEFAULT_DECIMAL_PLACES; } +// static void settings_startup_string(char *buf) { +// memcpy_from_eeprom_with_checksum((char*)buf,512, 4); +// } + void settings_dump() { printPgmString(PSTR("$0 = ")); printFloat(settings.steps_per_mm[X_AXIS]); printPgmString(PSTR(" (steps/mm x)\r\n$1 = ")); printFloat(settings.steps_per_mm[Y_AXIS]); @@ -124,12 +128,18 @@ void settings_dump() { printPgmString(PSTR(" (milliseconds homing debounce delay)\r\n$14 = ")); printInteger(settings.stepper_idle_lock_time); printPgmString(PSTR(" (milliseconds stepper idle lock time)\r\n$15 = ")); printInteger(settings.decimal_places); printPgmString(PSTR(" (float decimal places)")); + +// char buf[4]; +// settings_startup_string((char *)buf); +// printPgmString(PSTR("\r\n Startup: ")); printString(buf); + printPgmString(PSTR("\r\n'$x=value' to set parameter or just '$' to dump current settings\r\n")); } // Parameter lines are on the form '$4=374.3' or '$' to dump current settings uint8_t settings_execute_line(char *line) { uint8_t char_counter = 1; +// unsigned char letter; float parameter, value; if(line[0] != '$') { return(STATUS_UNSUPPORTED_STATEMENT); @@ -137,6 +147,23 @@ uint8_t settings_execute_line(char *line) { if(line[char_counter] == 0) { settings_dump(); return(STATUS_OK); } +// if(line[char_counter] >= 'A' || line[char_counter] <= 'Z') { +// letter = line[char_counter++]; +// if(line[char_counter++] != '=') { +// return(STATUS_UNSUPPORTED_STATEMENT); +// } +// for (char_counter = 0; char_counter < LINE_BUFFER_SIZE-3; char_counter++) { +// line[char_counter] = line[char_counter+3]; +// } +// uint8_t status = gc_execute_line(line); +// if (status) { return(status); } +// else { settings_store_startup_line(line); } +// +// +// // Opt stop and block delete are referred to as switches. +// // How to store home position and work offsets real-time?? +// +// } else { if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }; @@ -151,11 +178,16 @@ uint8_t settings_execute_line(char *line) { } settings_store_setting(parameter, value); return(STATUS_OK); +// } } void write_settings() { eeprom_put_char(0, SETTINGS_VERSION); memcpy_to_eeprom_with_checksum(1, (char*)&settings, sizeof(settings_t)); +// +// char buf[4]; buf[0] = 'G'; buf[1] = '2'; buf[2] = '0'; buf[3] = 0; +// memcpy_to_eeprom_with_checksum(512, (char*)buf, 4); +// } int read_settings() { @@ -254,5 +286,16 @@ void settings_init() { settings_reset(true); write_settings(); settings_dump(); - } + } } + +// int8_t settings_execute_startup() { +// +// char buf[4]; +// settings_startup_string((char *)buf); +// uint8_t i = 0; +// while (i < 4) { +// serial_write(buf[i++]); +// } +// return(gc_execute_line(buf)); +// } diff --git a/settings.h b/settings.h index cb15bd6..64aba96 100755 --- a/settings.h +++ b/settings.h @@ -29,7 +29,7 @@ // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom -#define SETTINGS_VERSION 52 +#define SETTINGS_VERSION 53 // Define bit flag masks in settings.flag. #define FLAG_BIT_HOMING_ENABLE bit(0) @@ -68,4 +68,6 @@ uint8_t settings_execute_line(char *line); // A helper method to set new settings from command line void settings_store_setting(int parameter, float value); +// int8_t settings_execute_startup(); + #endif diff --git a/stepper.c b/stepper.c index 148d29f..54f3fd9 100755 --- a/stepper.c +++ b/stepper.c @@ -112,9 +112,11 @@ void st_go_idle() TIMSK1 &= ~(1< // Some useful constants -#define LIMIT_MASK ((1< Date: Tue, 16 Oct 2012 21:29:45 -0600 Subject: [PATCH 31/55] Hard limits, homing direction, pull-off limits after homing, status reports in mm or inches, system alarm, and more. - Thank you statement added for Alden Hart of Synthetos. - Hard limits option added, which also works with homing by pulling off the switches to help prevent unintended triggering. Hard limits use a interrupt to sense a falling edge pin change and immediately go into alarm mode, which stops everything and forces the user to issue a reset (Ctrl-x) or reboot. - Auto cycle start now a configuration option. - Alarm mode: A new method to kill all Grbl processes in the event of something catastrophic or potentially catastropic. Just works with hard limits for now, but will be expanded to include g-code errors (most likely) and other events. - Updated status reports to be configurable in inches or mm mode. Much more to do here, but this is the first step. - New settings: auto cycle start, hard limit enable, homing direction mask (which works the same as the stepper mask), homing pulloff distance (or distance traveled from homed machine zero to prevent accidental limit trip). - Minor memory liberation and calculation speed ups. --- config.h | 8 ++--- gcode.c | 5 +-- limits.c | 37 ++++++++++++++------ main.c | 12 +++++-- motion_control.c | 23 ++++++++++++- nuts_bolts.h | 19 ++++++++-- protocol.c | 61 +++++++++++++++++++------------- serial.c | 6 +++- settings.c | 90 ++++++++++++++++++++++++++++++++---------------- settings.h | 11 +++--- stepper.c | 4 +-- 11 files changed, 193 insertions(+), 83 deletions(-) diff --git a/config.h b/config.h index fc5605e..73c4533 100755 --- a/config.h +++ b/config.h @@ -46,9 +46,9 @@ #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 LIMIT_INT PCIE0 // Pin change interrupt settings -// #define LIMIT_INT_vect PCINT0_vect -// #define LIMIT_PCMSK PCMSK0 +#define LIMIT_INT PCIE0 // Pin change interrupt settings +#define LIMIT_INT_vect PCINT0_vect +#define LIMIT_PCMSK PCMSK0 #define SPINDLE_ENABLE_DDR DDRB #define SPINDLE_ENABLE_PORT PORTB @@ -159,9 +159,7 @@ // TODO: The following options are set as compile-time options for now, until the next EEPROM // settings version has solidified. This is to prevent having to support dozens of different // incremental settings versions. -#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) // This parameter sets the delay time before disabling the steppers after the final block of movement. // A short delay ensures the steppers come to a complete stop and the residual inertial force in the diff --git a/gcode.c b/gcode.c index c59bac7..8437405 100755 --- a/gcode.c +++ b/gcode.c @@ -108,7 +108,8 @@ void gc_init() // protocol_status_message(settings_execute_startup()); } -// Sets g-code parser position in mm. Input in steps. Called by the system abort routine. +// Sets g-code parser position in mm. Input in steps. Called by the system abort and hard +// limit pull-off routines. void gc_set_current_position(int32_t x, int32_t y, int32_t z) { gc.position[X_AXIS] = x/settings.steps_per_mm[X_AXIS]; @@ -183,7 +184,7 @@ uint8_t gc_execute_line(char *line) case 21: gc.inches_mode = false; break; case 28: case 30: // NOTE: G28.1, G30.1 sets home position parameters. Not currently supported. - if (bit_istrue(settings.flags,FLAG_BIT_HOMING_ENABLE)) { + if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { non_modal_action = NON_MODAL_GO_HOME; } else { FAIL(STATUS_SETTING_DISABLED); diff --git a/limits.c b/limits.c index da36409..0785fa5 100755 --- a/limits.c +++ b/limits.c @@ -38,8 +38,31 @@ void limits_init() { LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. + + if bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE) { + MCUCR = (1<. */ +/* A big thanks to Alden Hart of Synthetos, supplier of grblshield and TinyG, who has + been integral throughout the development of the higher level details of Grbl, as well + as being a consistent sounding board for the future of accessible and free CNC. */ + #include #include #include "config.h" @@ -46,7 +50,9 @@ int main(void) memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization - + + // TODO: When Grbl system status is installed, need to set position lost state upon startup. + for(;;) { // Execute system reset upon a system abort, where the main program will return to this loop. @@ -87,9 +93,9 @@ int main(void) // 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 + if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; - #endif + } // TODO: Install G20/G21 unit default into settings and load appropriate settings. } diff --git a/motion_control.c b/motion_control.c index 26f16bc..76d8092 100755 --- a/motion_control.c +++ b/motion_control.c @@ -196,9 +196,30 @@ void mc_dwell(float seconds) // Execute homing cycle to locate and set machine zero. void mc_go_home() { - limits_go_home(); + plan_synchronize(); // Empty all motions in buffer before homing. + PCICR &= ~(1 << LIMIT_INT); // Disable hard limits pin change interrupt + + limits_go_home(); // Perform homing routine. + // Upon completion, reset all internal position vectors (g-code parser, planner, system) gc_clear_position(); plan_clear_position(); clear_vector_float(sys.position); + + // If hard limits enabled, move all axes off limit switches before enabling the hard limit + // pin change interrupt. This should help prevent the switches from falsely tripping. + // NOTE: G-code parser was circumvented so its position needs to be updated explicitly. + if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { + int8_t x_dir, y_dir, z_dir; + x_dir = y_dir = z_dir = 1; + if (bit_istrue(settings.homing_dir_mask,bit(X_DIRECTION_BIT))) { x_dir = -1; } + if (bit_istrue(settings.homing_dir_mask,bit(Y_DIRECTION_BIT))) { y_dir = -1; } + if (bit_istrue(settings.homing_dir_mask,bit(Z_DIRECTION_BIT))) { z_dir = -1; } + mc_line(x_dir*settings.homing_pulloff, y_dir*settings.homing_pulloff, + z_dir*settings.homing_pulloff, settings.homing_feed_rate, false); + st_cycle_start(); // Nothing should be in the buffer except this motion. + plan_synchronize(); // Make sure the motion completes. + gc_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); + PCICR |= (1 << LIMIT_INT); // Re-enable hard limits. + } } diff --git a/nuts_bolts.h b/nuts_bolts.h index 8e9b58b..d4ecb2d 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -35,6 +35,7 @@ #define Z_AXIS 2 #define MM_PER_INCH (25.4) +#define INCH_PER_MM (0.03937) // Useful macros #define clear_vector(a) memset(a, 0, sizeof(a)) @@ -60,16 +61,29 @@ #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 EXEC_ALARM bit(5) // bitmask 00100000 // #define bit(6) // bitmask 01000000 // #define bit(7) // bitmask 10000000 +// Define bit flag masks for sys.switches. (8 flag limit) +#define BITFLAG_BLOCK_DELETE bit(0) +#define BITFLAG_SINGLE_BLOCK bit(1) +#define BITFLAG_OPT_STOP bit(2) +// #define bit(3) +// #define bit(4) +// #define bit(5) +// #define bit(6) +// #define bit(7) + // Define global system variables 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. - + uint8_t alarm; // Alarm mode. Causes all functions to immediately cease until a system abort + // is issued by the user. +// uint8_t switches; // Switches state bitflag variable. For settings not governed by g-code. + int32_t position[3]; // Real-time machine (aka home) position vector in steps. // NOTE: This may need to be a volatile variable, if problems arise. @@ -82,6 +96,7 @@ typedef struct { 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; diff --git a/protocol.c b/protocol.c index 78e42bf..234ad45 100755 --- a/protocol.c +++ b/protocol.c @@ -73,7 +73,7 @@ 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. '[0.23,120.4,2.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 (5-20Hz). @@ -85,24 +85,33 @@ void protocol_status_report() // home position by the user (likely through '$' setting interface). // Successfully tested at a query rate of 10-20Hz while running a gauntlet of programs at various // speeds. - int32_t print_position[3]; - memcpy(print_position,sys.position,sizeof(sys.position)); - #if REPORT_INCH_MODE - printString("MPos:["); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); - printString(","); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); - printString(","); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); - printString("],WPos:["); printFloat((print_position[X_AXIS]/settings.steps_per_mm[X_AXIS]-sys.coord_system[sys.coord_select][X_AXIS]-sys.coord_offset[X_AXIS])/MM_PER_INCH); - printString(","); printFloat((print_position[Y_AXIS]/settings.steps_per_mm[Y_AXIS]-sys.coord_system[sys.coord_select][Y_AXIS]-sys.coord_offset[Y_AXIS])/MM_PER_INCH); - printString(","); printFloat((print_position[Z_AXIS]/settings.steps_per_mm[Z_AXIS]-sys.coord_system[sys.coord_select][Z_AXIS]-sys.coord_offset[Z_AXIS])/MM_PER_INCH); - #else - printString("MPos:["); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS])); - printString(","); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS])); - printString(","); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS])); - printString("],WPos:["); printFloat(print_position[X_AXIS]/settings.steps_per_mm[X_AXIS]-sys.coord_system[sys.coord_select][X_AXIS]-sys.coord_offset[X_AXIS]); - printString(","); printFloat(print_position[Y_AXIS]/settings.steps_per_mm[Y_AXIS]-sys.coord_system[sys.coord_select][Y_AXIS]-sys.coord_offset[Y_AXIS]); - printString(","); printFloat(print_position[Z_AXIS]/settings.steps_per_mm[Z_AXIS]-sys.coord_system[sys.coord_select][Z_AXIS]-sys.coord_offset[Z_AXIS]); - #endif - printString("]\r\n"); + uint8_t i; + int32_t current_position[3]; // Copy current state of the system position variable + memcpy(current_position,sys.position,sizeof(sys.position)); + float print_position[3]; + + // Report machine position + printPgmString(PSTR("MPos:[")); + for (i=0; i<= 2; i++) { + print_position[i] = current_position[i]/settings.steps_per_mm[i]; + if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; } + printFloat(print_position[i]); + if (i < 2) { printPgmString(PSTR(",")); } + } + + // Report work position + printPgmString(PSTR("],WPos:[")); + for (i=0; i<= 2; i++) { + if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { + print_position[i] -= (sys.coord_system[sys.coord_select][i]+sys.coord_offset[i])*INCH_PER_MM; + } else { + print_position[i] -= sys.coord_system[sys.coord_select][i]+sys.coord_offset[i]; + } + printFloat(print_position[i]); + if (i < 2) { printPgmString(PSTR(",")); } + } + + printPgmString(PSTR("]\r\n")); } @@ -129,6 +138,12 @@ void protocol_execute_runtime() { if (sys.execute) { // Enter only if any bit flag is true uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times + + // System alarm. Something has gone wrong. Disable everything until system reset. + if (rt_exec & EXEC_ALARM) { + while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } + bit_false(sys.execute,EXEC_ALARM); + } // System abort. Steppers have already been force stopped. if (rt_exec & EXEC_RESET) { @@ -157,12 +172,12 @@ void protocol_execute_runtime() if (rt_exec & EXEC_CYCLE_START) { st_cycle_start(); // Issue cycle start command to stepper subsystem - #ifdef CYCLE_AUTO_START + if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; // Re-enable auto start after feed hold. - #endif + } bit_false(sys.execute,EXEC_CYCLE_START); - } - } + } + } } diff --git a/serial.c b/serial.c index aa35237..a346cd3 100755 --- a/serial.c +++ b/serial.c @@ -165,7 +165,11 @@ ISR(USART_RX_vect) 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: + case CMD_RESET: + sys.alarm |= EXEC_ALARM; // Set alarm to allow subsystem disable for certain settings. + + // TODO: When Grbl system status is installed, set position lost state if the cycle is active. + // Immediately force stepper and spindle subsystem idle at an interrupt level. if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. st_go_idle(); diff --git a/settings.c b/settings.c index e012c5c..d4e723c 100755 --- a/settings.c +++ b/settings.c @@ -69,14 +69,16 @@ typedef struct { #define DEFAULT_STEPPING_INVERT_MASK ((1< Date: Thu, 18 Oct 2012 21:29:07 -0600 Subject: [PATCH 32/55] Hard limits code minor updates. - Fixed a bug that would not disable the steppers if a user issues a system abort during a homing cycle. - Updated the hard limit interrupt to be more correct and to issue a shutdown for the right situations when the switch has been triggered. - Added a status message to indicate to the user what happened and what to do upon a hard limit trigger. --- config.h | 6 +++--- limits.c | 27 +++++++++++++++++---------- motion_control.c | 2 +- nuts_bolts.h | 2 -- protocol.c | 3 +++ protocol.h | 1 + serial.c | 2 +- 7 files changed, 26 insertions(+), 17 deletions(-) diff --git a/config.h b/config.h index 73c4533..e5cc760 100755 --- a/config.h +++ b/config.h @@ -46,9 +46,9 @@ #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 LIMIT_INT PCIE0 // Pin change interrupt settings -#define LIMIT_INT_vect PCINT0_vect -#define LIMIT_PCMSK PCMSK0 +#define LIMIT_INT PCIE0 // Pin change interrupt enable pin +#define LIMIT_INT_vect PCINT0_vect +#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register #define SPINDLE_ENABLE_DDR DDRB #define SPINDLE_ENABLE_PORT PORTB diff --git a/limits.c b/limits.c index 0785fa5..8e0fa56 100755 --- a/limits.c +++ b/limits.c @@ -39,27 +39,34 @@ void limits_init() LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. - if bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE) { - MCUCR = (1<\r\n")); break; default: printInteger(status_code); printPgmString(PSTR("\r\n")); @@ -141,6 +143,7 @@ void protocol_execute_runtime() // System alarm. Something has gone wrong. Disable everything until system reset. if (rt_exec & EXEC_ALARM) { + protocol_status_message(STATUS_HARD_LIMIT); while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } bit_false(sys.execute,EXEC_ALARM); } diff --git a/protocol.h b/protocol.h index 559560c..82dc74c 100755 --- a/protocol.h +++ b/protocol.h @@ -29,6 +29,7 @@ #define STATUS_MODAL_GROUP_VIOLATION 5 #define STATUS_INVALID_COMMAND 6 #define STATUS_SETTING_DISABLED 7 +#define STATUS_HARD_LIMIT 8 #define LINE_BUFFER_SIZE 50 diff --git a/serial.c b/serial.c index a346cd3..a9a214e 100755 --- a/serial.c +++ b/serial.c @@ -166,7 +166,7 @@ ISR(USART_RX_vect) 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: - sys.alarm |= EXEC_ALARM; // Set alarm to allow subsystem disable for certain settings. + sys.execute |= EXEC_ALARM; // Set alarm to allow subsystem disable for certain settings. // TODO: When Grbl system status is installed, set position lost state if the cycle is active. From 909feb7f79b67dc70563a546f339f132b4cf014f Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sun, 21 Oct 2012 16:55:59 -0600 Subject: [PATCH 33/55] Re-organized status messages to be more coherent and centralized. - Reorganized all of the status message feedback from both the g-code parser and settings modules to be centralized into two message modules: status feedback from executing a line and warnings for misc feedback. - Pulled out the printPgmString() messages in settings.c and placed it into the new module. (settings_dump() not moved). - Some other minor edits. Renaming defines, comment updates, etc. --- config.h | 10 ++++--- gcode.c | 26 ++++++++-------- protocol.c | 88 ++++++++++++++++++++++++++++++++++++++---------------- protocol.h | 21 +++++++++---- serial.c | 1 + settings.c | 46 +++++++++++----------------- settings.h | 12 ++++---- 7 files changed, 123 insertions(+), 81 deletions(-) diff --git a/config.h b/config.h index e5cc760..6542913 100755 --- a/config.h +++ b/config.h @@ -150,9 +150,10 @@ // of your successes or difficulties, as we will monitor this and possibly integrate this as a // standard feature for future releases. However, we suggest to first try our direction delay // hack/solution posted in the Wiki involving inverting the stepper pin mask. -// NOTE: Uncomment to enable. The recommended delay should be > 3us and the total step pulse -// time, which includes the Grbl settings pulse microseconds, should not exceed 127us. -// #define STEP_PULSE_DELAY 5 // Step pulse delay in microseconds. Default disabled. +// NOTE: Uncomment to enable. The recommended delay must be > 3us and the total step pulse +// time, which includes the Grbl settings pulse microseconds, must not exceed 127us. Reported +// successful values for certain setups have ranged from 10 to 20us. +// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled. // --------------------------------------------------------------------------------------- @@ -167,7 +168,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 the define commented, the stepper lock will be disabled upon compiling. +// NOTE: If set to zero, no lock will occur. If set to max 255, the lock will never release, in other +// words, the steppers never disable for users that require this. // -> NOW INSTALLED IN SETTINGS #define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer > 0 diff --git a/gcode.c b/gcode.c index 8437405..58bc042 100755 --- a/gcode.c +++ b/gcode.c @@ -265,7 +265,7 @@ uint8_t gc_execute_line(char *line) switch(letter) { case 'G': case 'M': case 'N': break; // Ignore command statements and line numbers case 'F': - if (value <= 0) { FAIL(STATUS_INVALID_COMMAND); } // Must be greater than zero + if (value <= 0) { FAIL(STATUS_INVALID_STATEMENT); } // Must be greater than zero if (gc.inverse_feed_rate_mode) { inverse_feed_rate = to_millimeters(value); // seconds per motion for this motion only } else { @@ -277,11 +277,11 @@ uint8_t gc_execute_line(char *line) case 'P': p = value; break; case 'R': r = to_millimeters(value); break; case 'S': - if (value < 0) { FAIL(STATUS_INVALID_COMMAND); } // Cannot be negative + if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative gc.spindle_speed = value; break; case 'T': - if (value < 0) { FAIL(STATUS_INVALID_COMMAND); } // Cannot be negative + if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative gc.tool = trunc(value); break; case 'X': target[X_AXIS] = to_millimeters(value); bit_true(axis_words,bit(X_AXIS)); break; @@ -299,7 +299,7 @@ uint8_t gc_execute_line(char *line) NOTE: Independent non-motion/settings parameters are set out of this order for code efficiency and simplicity purposes, but this should not affect proper g-code execution. */ - // ([M6]: Tool change execution should be executed here.) + // ([M6]: Tool change should be executed here.) // [M3,M4,M5]: Update spindle state spindle_run(gc.spindle_direction, gc.spindle_speed); @@ -313,7 +313,7 @@ uint8_t gc_execute_line(char *line) switch (non_modal_action) { case NON_MODAL_DWELL: if (p < 0) { // Time cannot be negative. - FAIL(STATUS_INVALID_COMMAND); + FAIL(STATUS_INVALID_STATEMENT); } else { mc_dwell(p); } @@ -323,7 +323,7 @@ uint8_t gc_execute_line(char *line) if (l != 2 || (int_value < 1 || int_value > N_COORDINATE_SYSTEM)) { // L2 only. P1=G54, P2=G55, ... FAIL(STATUS_UNSUPPORTED_STATEMENT); } else if (!axis_words) { // No axis words. - FAIL(STATUS_INVALID_COMMAND); + FAIL(STATUS_INVALID_STATEMENT); } else { int_value--; // Adjust p to be inline with row array index. // Update axes defined only in block. Always in machine coordinates. Can change non-active system. @@ -358,7 +358,7 @@ uint8_t gc_execute_line(char *line) break; case NON_MODAL_SET_COORDINATE_OFFSET: if (!axis_words) { // No axis words - FAIL(STATUS_INVALID_COMMAND); + FAIL(STATUS_INVALID_STATEMENT); } else { // Update axes defined only in block. Offsets current system to defined value. Does not update when // active coordinate system is selected, but is still active unless G92.1 disables it. @@ -384,12 +384,12 @@ uint8_t gc_execute_line(char *line) // G1,G2,G3 require F word in inverse time mode. if ( gc.inverse_feed_rate_mode ) { if (inverse_feed_rate < 0 && gc.motion_mode != MOTION_MODE_CANCEL) { - FAIL(STATUS_INVALID_COMMAND); + FAIL(STATUS_INVALID_STATEMENT); } } // Absolute override G53 only valid with G0 and G1 active. if ( absolute_override && !(gc.motion_mode == MOTION_MODE_SEEK || gc.motion_mode == MOTION_MODE_LINEAR)) { - FAIL(STATUS_INVALID_COMMAND); + FAIL(STATUS_INVALID_STATEMENT); } // Report any errors. if (gc.status_code) { return(gc.status_code); } @@ -414,14 +414,14 @@ uint8_t gc_execute_line(char *line) switch (gc.motion_mode) { case MOTION_MODE_CANCEL: - if (axis_words) { FAIL(STATUS_INVALID_COMMAND); } // No axis words allowed while active. + if (axis_words) { FAIL(STATUS_INVALID_STATEMENT); } // No axis words allowed while active. break; case MOTION_MODE_SEEK: - if (!axis_words) { FAIL(STATUS_INVALID_COMMAND);} + if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);} else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); } break; case MOTION_MODE_LINEAR: - if (!axis_words) { FAIL(STATUS_INVALID_COMMAND);} + if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);} else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); } break; @@ -430,7 +430,7 @@ uint8_t gc_execute_line(char *line) // format arc mode, also check for at least one of the IJK axes of the selected plane was sent. if ( !( bit_false(axis_words,bit(gc.plane_axis_2)) ) || ( !r && !offset[gc.plane_axis_0] && !offset[gc.plane_axis_1] ) ) { - FAIL(STATUS_INVALID_COMMAND); + FAIL(STATUS_INVALID_STATEMENT); } else { if (r != 0) { // Arc Radius Mode /* diff --git a/protocol.c b/protocol.c index 412001a..a61f254 100755 --- a/protocol.c +++ b/protocol.c @@ -36,37 +36,73 @@ static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. static uint8_t char_counter; // Last character counter in line variable. static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters. +// Prints all status messages, an 'ok' or 'error', after Grbl has processed a line of incoming +// serial data, whether this was a g-code block or grbl setting command. void protocol_status_message(int8_t status_code) { - if (status_code == 0) { + // TODO: Compile time option to only return numeric codes for GUIs. + if (status_code == 0) { // STATUS_OK printPgmString(PSTR("ok\r\n")); } else { printPgmString(PSTR("error: ")); - switch(status_code) { - case STATUS_BAD_NUMBER_FORMAT: - printPgmString(PSTR("Bad number format\r\n")); break; - case STATUS_EXPECTED_COMMAND_LETTER: - printPgmString(PSTR("Expected command letter\r\n")); break; - case STATUS_UNSUPPORTED_STATEMENT: - printPgmString(PSTR("Unsupported statement\r\n")); break; - case STATUS_FLOATING_POINT_ERROR: - printPgmString(PSTR("Floating point error\r\n")); break; - case STATUS_MODAL_GROUP_VIOLATION: - printPgmString(PSTR("Modal group violation\r\n")); break; - case STATUS_INVALID_COMMAND: - printPgmString(PSTR("Invalid command\r\n")); break; - case STATUS_SETTING_DISABLED: - printPgmString(PSTR("Grbl setting disabled\r\n")); break; - case STATUS_HARD_LIMIT: - printPgmString(PSTR("Limit triggered \r\n")); break; - default: - printInteger(status_code); - printPgmString(PSTR("\r\n")); + // All critical error codes are greater than zero. These are defined to be any error + // that may cause damage by crashing or improper g-code inputs and that are susceptible + // to Grbl's alarm mode which will stop all processes, if the user enables this option. + if (status_code > 0) { + // TODO: Install option to enter alarm mode upon any critical error. + switch(status_code) { + case STATUS_BAD_NUMBER_FORMAT: + printPgmString(PSTR("Bad number format")); break; + case STATUS_EXPECTED_COMMAND_LETTER: + printPgmString(PSTR("Expected command letter")); break; + case STATUS_UNSUPPORTED_STATEMENT: + printPgmString(PSTR("Unsupported statement")); break; + case STATUS_FLOATING_POINT_ERROR: + printPgmString(PSTR("Floating point error")); break; + case STATUS_MODAL_GROUP_VIOLATION: + printPgmString(PSTR("Modal group violation")); break; + case STATUS_INVALID_STATEMENT: + printPgmString(PSTR("Invalid gcode statement")); break; + case STATUS_SETTING_DISABLED: + printPgmString(PSTR("Grbl setting disabled")); break; + case STATUS_HARD_LIMIT: + printPgmString(PSTR("Limit triggered ")); break; + } + // All other non-critical error codes are less than zero. These are defined to be any + // error that is not susceptible to the alarm mode. Typically settings responses. + } else { + switch(status_code) { + case STATUS_SETTING_INVALID: + printPgmString(PSTR("Invalid setting statement")); break; + case STATUS_SETTING_STEPS_NEG: + printPgmString(PSTR("Steps/mm must be > 0.0")); break; + case STATUS_SETTING_STEP_PULSE_MIN: + printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break; + } } + printPgmString(PSTR("\r\n")); } } +// Prints Grbl warning messages. This serves as a centralized method to provide additional +// user feedback for things that do not pass through the protocol_execute_line() function. +// This includes things like initialization checks or setup warnings when features are +// enabled. This function maybe called from anywhere in Grbl at the point of concern. +void protocol_warning_message(int8_t warning_code) +{ + // TODO: Install silence warning messages option in settings + printPgmString(PSTR("warning: ")); + switch(warning_code) { + case WARNING_HOMING_ENABLE: + printPgmString(PSTR("Install all axes limit switches before use")); break; + case WARNING_SETTING_READ_FAIL: + printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; + } + printPgmString(PSTR("\r\n")); +} + + void protocol_status_report() { // TODO: Status report data is written to the user here. This function should be able to grab a @@ -99,10 +135,11 @@ void protocol_status_report() if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; } printFloat(print_position[i]); if (i < 2) { printPgmString(PSTR(",")); } + else { printPgmString(PSTR("]")); } } // Report work position - printPgmString(PSTR("],WPos:[")); + printPgmString(PSTR(",WPos:[")); for (i=0; i<= 2; i++) { if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] -= (sys.coord_system[sys.coord_select][i]+sys.coord_offset[i])*INCH_PER_MM; @@ -111,9 +148,10 @@ void protocol_status_report() } printFloat(print_position[i]); if (i < 2) { printPgmString(PSTR(",")); } + else { printPgmString(PSTR("]")); } } - printPgmString(PSTR("]\r\n")); + printPgmString(PSTR("\r\n")); } @@ -133,7 +171,7 @@ void protocol_init() // 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. 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 +// set the system runtime flags, where only the main program 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() @@ -185,7 +223,7 @@ void protocol_execute_runtime() // Executes one line of input according to protocol -uint8_t protocol_execute_line(char *line) +int8_t protocol_execute_line(char *line) { if(line[0] == '$') { diff --git a/protocol.h b/protocol.h index 82dc74c..24a2bdd 100755 --- a/protocol.h +++ b/protocol.h @@ -21,17 +21,28 @@ #ifndef protocol_h #define protocol_h + +#define LINE_BUFFER_SIZE 50 + +// Define Grbl status codes. #define STATUS_OK 0 +// Critical error codes. Greater than zero. #define STATUS_BAD_NUMBER_FORMAT 1 #define STATUS_EXPECTED_COMMAND_LETTER 2 #define STATUS_UNSUPPORTED_STATEMENT 3 #define STATUS_FLOATING_POINT_ERROR 4 #define STATUS_MODAL_GROUP_VIOLATION 5 -#define STATUS_INVALID_COMMAND 6 +#define STATUS_INVALID_STATEMENT 6 #define STATUS_SETTING_DISABLED 7 #define STATUS_HARD_LIMIT 8 +// Non-critical error codes. Less than zero. +#define STATUS_SETTING_INVALID -1 +#define STATUS_SETTING_STEPS_NEG -2 +#define STATUS_SETTING_STEP_PULSE_MIN -3 -#define LINE_BUFFER_SIZE 50 +// Define Grbl warning message codes +#define WARNING_HOMING_ENABLE 1 +#define WARNING_SETTING_READ_FAIL 2 // Initialize the serial protocol void protocol_init(); @@ -41,12 +52,12 @@ void protocol_init(); void protocol_process(); // Executes one line of input according to protocol -uint8_t protocol_execute_line(char *line); +int8_t protocol_execute_line(char *line); // Checks and executes a runtime command at various stop points in main program void protocol_execute_runtime(); -// Prints g-code parser status message. -void protocol_status_message(int8_t status_code); +// Prints any warning messages. +void protocol_warning_message(int8_t warning_code); #endif diff --git a/serial.c b/serial.c index a9a214e..4b431f9 100755 --- a/serial.c +++ b/serial.c @@ -169,6 +169,7 @@ ISR(USART_RX_vect) sys.execute |= EXEC_ALARM; // Set alarm to allow subsystem disable for certain settings. // TODO: When Grbl system status is installed, set position lost state if the cycle is active. + // if (sys.cycle_start) { POSITION LOST } // Immediately force stepper and spindle subsystem idle at an interrupt level. if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. diff --git a/settings.c b/settings.c index d4e723c..7085480 100755 --- a/settings.c +++ b/settings.c @@ -147,13 +147,11 @@ void settings_dump() { } // Parameter lines are on the form '$4=374.3' or '$' to dump current settings -uint8_t settings_execute_line(char *line) { - uint8_t char_counter = 1; +// NOTE: Assumes '$' already exists in line[0], which is checked by protocol.c. +int8_t settings_execute_line(char *line) { + uint8_t char_counter = 1; // unsigned char letter; float parameter, value; - if(line[0] != '$') { - return(STATUS_UNSUPPORTED_STATEMENT); - } if(line[char_counter] == 0) { settings_dump(); return(STATUS_OK); } @@ -175,19 +173,18 @@ uint8_t settings_execute_line(char *line) { // // } else { if(!read_float(line, &char_counter, ¶meter)) { - return(STATUS_BAD_NUMBER_FORMAT); - }; + return(STATUS_SETTING_INVALID); + } if(line[char_counter++] != '=') { - return(STATUS_UNSUPPORTED_STATEMENT); + return(STATUS_SETTING_INVALID); } if(!read_float(line, &char_counter, &value)) { - return(STATUS_BAD_NUMBER_FORMAT); + return(STATUS_SETTING_INVALID); } if(line[char_counter] != 0) { - return(STATUS_UNSUPPORTED_STATEMENT); + return(STATUS_SETTING_INVALID); } - settings_store_setting(parameter, value); - return(STATUS_OK); + return(settings_store_setting(parameter, value)); // } } @@ -250,20 +247,14 @@ int read_settings() { } // A helper method to set settings from command line -void settings_store_setting(int parameter, float value) { +int8_t settings_store_setting(int parameter, float value) { switch(parameter) { case 0: case 1: case 2: - if (value <= 0.0) { - printPgmString(PSTR("Steps/mm must be > 0.0\r\n")); - return; - } - settings.steps_per_mm[parameter] = value; break; + if (value <= 0.0) { return(STATUS_SETTING_STEPS_NEG); } + settings.steps_per_mm[parameter] = value; break; case 3: - if (value < 3) { - printPgmString(PSTR("Step pulse must be >= 3 microseconds\r\n")); - return; - } - settings.pulse_microseconds = round(value); break; + if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } + settings.pulse_microseconds = round(value); break; case 4: settings.default_feed_rate = value; break; case 5: settings.default_seek_rate = value; break; case 6: settings.mm_per_arc_segment = value; break; @@ -288,7 +279,7 @@ void settings_store_setting(int parameter, float value) { case 13: if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; - printPgmString(PSTR("Install all axes limit switches before use\r\n")); + protocol_warning_message(WARNING_HOMING_ENABLE); } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } break; case 14: settings.homing_dir_mask = trunc(value); break; @@ -302,17 +293,16 @@ void settings_store_setting(int parameter, float value) { break; case 20: settings.decimal_places = round(value); break; default: - printPgmString(PSTR("Unknown parameter\r\n")); - return; + return(STATUS_SETTING_INVALID); } write_settings(); - printPgmString(PSTR("Stored new setting\r\n")); + return(STATUS_OK); } // Initialize the config subsystem void settings_init() { if(!read_settings()) { - printPgmString(PSTR("Warning: Failed to read EEPROM settings. Using defaults.\r\n")); + protocol_warning_message(WARNING_SETTING_READ_FAIL); settings_reset(true); write_settings(); settings_dump(); diff --git a/settings.h b/settings.h index 6d3c7b6..3d2086a 100755 --- a/settings.h +++ b/settings.h @@ -31,7 +31,7 @@ // when firmware is upgraded. Always stored in byte 0 of eeprom #define SETTINGS_VERSION 55 -// Define bit flag masks in settings.flag. +// Define bit flag masks for the boolean settings in settings.flag. #define BITFLAG_REPORT_INCHES bit(0) #define BITFLAG_AUTO_START bit(1) #define BITFLAG_HARD_LIMIT_ENABLE bit(2) @@ -48,14 +48,14 @@ typedef struct { float mm_per_arc_segment; float acceleration; float junction_deviation; - uint8_t flags; // Contains default toggles + uint8_t flags; // Contains default boolean settings uint8_t homing_dir_mask; float homing_feed_rate; float homing_seek_rate; uint16_t homing_debounce_delay; float homing_pulloff; - uint8_t stepper_idle_lock_time; - uint8_t decimal_places; + uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable. + uint8_t decimal_places; } settings_t; extern settings_t settings; @@ -66,10 +66,10 @@ void settings_init(); void settings_dump(); // Handle settings command -uint8_t settings_execute_line(char *line); +int8_t settings_execute_line(char *line); // A helper method to set new settings from command line -void settings_store_setting(int parameter, float value); +int8_t settings_store_setting(int parameter, float value); // int8_t settings_execute_startup(); From 065ceceb342f1366fc61cdc900af292767a3728d Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sun, 21 Oct 2012 19:18:24 -0600 Subject: [PATCH 34/55] New alarm method. Re(re)organized status messages. - Installed a new 'alarm' method to centralize motion kills across alarm or reset events. Right now, this is controlled by system abort and hard limits. But, in the future, a g-code parser error may call this too as a safety feature. - Re(re)organized status messages to just print all errors, regardless from where it was called. This centralizes them into one place. - Misc messages method installed for any user feedback that is not a confirmation or error. Mainly so that there is a place to perform warnings and such. - New stuff installed and still made the flash size smaller by saving flash space from clearing out repeated '\r\n' pgmstrings. - Fixed a bug where hard limits message would print everytime a system abort was sent. --- limits.c | 10 +++--- motion_control.c | 23 +++++++++++++ motion_control.h | 3 ++ protocol.c | 90 +++++++++++++++++++++--------------------------- protocol.h | 34 ++++++++++-------- serial.c | 13 ++----- settings.c | 18 +++++----- settings.h | 4 +-- stepper.c | 2 +- 9 files changed, 103 insertions(+), 94 deletions(-) diff --git a/limits.c b/limits.c index 8e0fa56..52b88df 100755 --- a/limits.c +++ b/limits.c @@ -56,16 +56,14 @@ ISR(LIMIT_INT_vect) if (bit_isfalse(sys.execute,EXEC_ALARM)) { // Kill all processes upon hard limit event. if ((LIMIT_PIN & LIMIT_MASK) ^ LIMIT_MASK) { - st_go_idle(); // Immediately stop stepper motion - spindle_stop(); // Stop spindle - sys.auto_start = false; // Disable auto cycle start. - sys.execute |= EXEC_ALARM; - // TODO: When Grbl system status is installed, update here to indicate loss of position. + mc_alarm(); // Initiate system kill. + protocol_status_message(STATUS_HARD_LIMIT); // Print ok in interrupt since system killed. } // else { // TODO: When leaving a switch, this interrupt can be activated upon detecting a pin // change to high. If so, need to start a countdown timer to check the pin again after - // a debounce period to not falsely re-engage the alarm. + // a debounce period to not falsely re-engage the alarm. Not essential, but *could* be + // a minor annoyance. } } diff --git a/motion_control.c b/motion_control.c index 2cffcc1..bb89275 100755 --- a/motion_control.c +++ b/motion_control.c @@ -25,6 +25,8 @@ #include "config.h" #include "gcode.h" #include "motion_control.h" +#include "spindle_control.h" +#include "coolant_control.h" #include #include #include @@ -223,3 +225,24 @@ void mc_go_home() PCICR |= (1 << LIMIT_INT); // Re-enable hard limits. } } + + +// Method to immediately kill all motion and set system alarm. Used by system abort, hard limits, +// and upon g-code parser error (when installed). +void mc_alarm() +{ + // Only this function can set the system alarm. This is done to prevent multiple kill calls + // by different processes. + if (bit_isfalse(sys.execute, EXEC_ALARM)) { + sys.execute |= EXEC_ALARM; // Set alarm to allow subsystem disable for certain settings. + sys.auto_start = false; // Disable auto cycle start. + + // TODO: When Grbl system status is installed, set position lost state if the cycle is active. + // if (sys.cycle_start) { POSITION LOST } + + // Immediately force stepper, spindle, and coolant to stop. + st_go_idle(); + spindle_stop(); + coolant_stop(); + } +} diff --git a/motion_control.h b/motion_control.h index 4f988ed..4d71848 100755 --- a/motion_control.h +++ b/motion_control.h @@ -43,4 +43,7 @@ void mc_dwell(float seconds); // Send the tool home (not implemented) void mc_go_home(); +// Kills all motion and sets system alarm +void mc_alarm(); + #endif diff --git a/protocol.c b/protocol.c index a61f254..942b22c 100755 --- a/protocol.c +++ b/protocol.c @@ -36,68 +36,54 @@ static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. static uint8_t char_counter; // Last character counter in line variable. static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters. -// Prints all status messages, an 'ok' or 'error', after Grbl has processed a line of incoming -// serial data, whether this was a g-code block or grbl setting command. -void protocol_status_message(int8_t status_code) +// Method to handle status messages, from an 'ok' to report any 'error' that has occurred. +// Errors can originate from the g-code parser, settings module, or a critical error, such +// as a triggered hard limit. +void protocol_status_message(uint8_t status_code) { // TODO: Compile time option to only return numeric codes for GUIs. if (status_code == 0) { // STATUS_OK printPgmString(PSTR("ok\r\n")); } else { printPgmString(PSTR("error: ")); - // All critical error codes are greater than zero. These are defined to be any error - // that may cause damage by crashing or improper g-code inputs and that are susceptible - // to Grbl's alarm mode which will stop all processes, if the user enables this option. - if (status_code > 0) { - // TODO: Install option to enter alarm mode upon any critical error. - switch(status_code) { - case STATUS_BAD_NUMBER_FORMAT: - printPgmString(PSTR("Bad number format")); break; - case STATUS_EXPECTED_COMMAND_LETTER: - printPgmString(PSTR("Expected command letter")); break; - case STATUS_UNSUPPORTED_STATEMENT: - printPgmString(PSTR("Unsupported statement")); break; - case STATUS_FLOATING_POINT_ERROR: - printPgmString(PSTR("Floating point error")); break; - case STATUS_MODAL_GROUP_VIOLATION: - printPgmString(PSTR("Modal group violation")); break; - case STATUS_INVALID_STATEMENT: - printPgmString(PSTR("Invalid gcode statement")); break; - case STATUS_SETTING_DISABLED: - printPgmString(PSTR("Grbl setting disabled")); break; - case STATUS_HARD_LIMIT: - printPgmString(PSTR("Limit triggered ")); break; - } - // All other non-critical error codes are less than zero. These are defined to be any - // error that is not susceptible to the alarm mode. Typically settings responses. - } else { - switch(status_code) { - case STATUS_SETTING_INVALID: - printPgmString(PSTR("Invalid setting statement")); break; - case STATUS_SETTING_STEPS_NEG: - printPgmString(PSTR("Steps/mm must be > 0.0")); break; - case STATUS_SETTING_STEP_PULSE_MIN: - printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break; - } + switch(status_code) { + case STATUS_BAD_NUMBER_FORMAT: + printPgmString(PSTR("Bad number format")); break; + case STATUS_EXPECTED_COMMAND_LETTER: + printPgmString(PSTR("Expected command letter")); break; + case STATUS_UNSUPPORTED_STATEMENT: + printPgmString(PSTR("Unsupported statement")); break; + case STATUS_FLOATING_POINT_ERROR: + printPgmString(PSTR("Floating point error")); break; + case STATUS_MODAL_GROUP_VIOLATION: + printPgmString(PSTR("Modal group violation")); break; + case STATUS_INVALID_STATEMENT: + printPgmString(PSTR("Invalid statement")); break; + case STATUS_HARD_LIMIT: + printPgmString(PSTR(" Limit triggered")); break; + case STATUS_SETTING_DISABLED: + printPgmString(PSTR("Grbl setting disabled")); break; + case STATUS_SETTING_STEPS_NEG: + printPgmString(PSTR("Steps/mm must be > 0.0")); break; + case STATUS_SETTING_STEP_PULSE_MIN: + printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break; + case STATUS_SETTING_READ_FAIL: + printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; } printPgmString(PSTR("\r\n")); } } -// Prints Grbl warning messages. This serves as a centralized method to provide additional -// user feedback for things that do not pass through the protocol_execute_line() function. -// This includes things like initialization checks or setup warnings when features are -// enabled. This function maybe called from anywhere in Grbl at the point of concern. -void protocol_warning_message(int8_t warning_code) +// Prints miscellaneous messages. This serves as a centralized method to provide additional +// user feedback for things that do not pass through the protocol_execute_line() function +// and are not errors or confirmations, such as setup warnings. +void protocol_misc_message(uint8_t message_code) { - // TODO: Install silence warning messages option in settings - printPgmString(PSTR("warning: ")); - switch(warning_code) { - case WARNING_HOMING_ENABLE: - printPgmString(PSTR("Install all axes limit switches before use")); break; - case WARNING_SETTING_READ_FAIL: - printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; + // TODO: Install silence misc messages option in settings + switch(message_code) { + case MESSAGE_HOMING_ENABLE: + printPgmString(PSTR("warning: Install all axes limit switches before use")); break; } printPgmString(PSTR("\r\n")); } @@ -181,7 +167,6 @@ void protocol_execute_runtime() // System alarm. Something has gone wrong. Disable everything until system reset. if (rt_exec & EXEC_ALARM) { - protocol_status_message(STATUS_HARD_LIMIT); while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } bit_false(sys.execute,EXEC_ALARM); } @@ -223,7 +208,7 @@ void protocol_execute_runtime() // Executes one line of input according to protocol -int8_t protocol_execute_line(char *line) +uint8_t protocol_execute_line(char *line) { if(line[0] == '$') { @@ -255,6 +240,9 @@ int8_t protocol_execute_line(char *line) } else { return(gc_execute_line(line)); // Everything else is gcode + // TODO: Install option to set system alarm upon any error code received back from the + // the g-code parser. This is a common safety feature on CNCs to help prevent crashes + // if the g-code doesn't perform as intended. } } diff --git a/protocol.h b/protocol.h index 24a2bdd..bed6092 100755 --- a/protocol.h +++ b/protocol.h @@ -21,28 +21,31 @@ #ifndef protocol_h #define protocol_h - +// Line buffer size from the serial input stream to be executed. +// NOTE: Not a problem except for extreme cases, but the line buffer size 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 will be increased, when we know how much extra +// memory space we can invest into here or we re-write the g-code parser not to have his +// buffer. #define LINE_BUFFER_SIZE 50 // Define Grbl status codes. #define STATUS_OK 0 -// Critical error codes. Greater than zero. #define STATUS_BAD_NUMBER_FORMAT 1 #define STATUS_EXPECTED_COMMAND_LETTER 2 #define STATUS_UNSUPPORTED_STATEMENT 3 #define STATUS_FLOATING_POINT_ERROR 4 #define STATUS_MODAL_GROUP_VIOLATION 5 #define STATUS_INVALID_STATEMENT 6 -#define STATUS_SETTING_DISABLED 7 -#define STATUS_HARD_LIMIT 8 -// Non-critical error codes. Less than zero. -#define STATUS_SETTING_INVALID -1 -#define STATUS_SETTING_STEPS_NEG -2 -#define STATUS_SETTING_STEP_PULSE_MIN -3 +#define STATUS_HARD_LIMIT 7 +#define STATUS_SETTING_DISABLED 8 +#define STATUS_SETTING_STEPS_NEG 9 +#define STATUS_SETTING_STEP_PULSE_MIN 10 +#define STATUS_SETTING_READ_FAIL 11 + +// Define Grbl misc message codes +#define MESSAGE_HOMING_ENABLE 1 -// Define Grbl warning message codes -#define WARNING_HOMING_ENABLE 1 -#define WARNING_SETTING_READ_FAIL 2 // Initialize the serial protocol void protocol_init(); @@ -52,12 +55,15 @@ void protocol_init(); void protocol_process(); // Executes one line of input according to protocol -int8_t protocol_execute_line(char *line); +uint8_t protocol_execute_line(char *line); // Checks and executes a runtime command at various stop points in main program void protocol_execute_runtime(); -// Prints any warning messages. -void protocol_warning_message(int8_t warning_code); +// Prints Grbl's status messages. +void protocol_status_message(uint8_t status_code); + +// Prints any misc messages. +void protocol_misc_message(uint8_t message_code); #endif diff --git a/serial.c b/serial.c index 4b431f9..a0e7916 100755 --- a/serial.c +++ b/serial.c @@ -26,8 +26,7 @@ #include #include "serial.h" #include "config.h" -#include "stepper.h" -#include "spindle_control.h" +#include "motion_control.h" #include "nuts_bolts.h" #include "protocol.h" @@ -166,16 +165,8 @@ ISR(USART_RX_vect) 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: - sys.execute |= EXEC_ALARM; // Set alarm to allow subsystem disable for certain settings. - - // TODO: When Grbl system status is installed, set position lost state if the cycle is active. - // if (sys.cycle_start) { POSITION LOST } - // Immediately force stepper and spindle subsystem idle at an interrupt level. - if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. - st_go_idle(); - spindle_stop(); - } + if (!(sys.execute & EXEC_RESET)) { mc_alarm(); } // Stop only first time. sys.execute |= EXEC_RESET; // Set as true break; default: // Write character to buffer diff --git a/settings.c b/settings.c index 7085480..175cf8d 100755 --- a/settings.c +++ b/settings.c @@ -148,7 +148,7 @@ void settings_dump() { // Parameter lines are on the form '$4=374.3' or '$' to dump current settings // NOTE: Assumes '$' already exists in line[0], which is checked by protocol.c. -int8_t settings_execute_line(char *line) { +uint8_t settings_execute_line(char *line) { uint8_t char_counter = 1; // unsigned char letter; float parameter, value; @@ -173,16 +173,16 @@ int8_t settings_execute_line(char *line) { // // } else { if(!read_float(line, &char_counter, ¶meter)) { - return(STATUS_SETTING_INVALID); + return(STATUS_BAD_NUMBER_FORMAT); } if(line[char_counter++] != '=') { - return(STATUS_SETTING_INVALID); + return(STATUS_UNSUPPORTED_STATEMENT); } if(!read_float(line, &char_counter, &value)) { - return(STATUS_SETTING_INVALID); + return(STATUS_BAD_NUMBER_FORMAT); } if(line[char_counter] != 0) { - return(STATUS_SETTING_INVALID); + return(STATUS_UNSUPPORTED_STATEMENT); } return(settings_store_setting(parameter, value)); // } @@ -247,7 +247,7 @@ int read_settings() { } // A helper method to set settings from command line -int8_t settings_store_setting(int parameter, float value) { +uint8_t settings_store_setting(int parameter, float value) { switch(parameter) { case 0: case 1: case 2: if (value <= 0.0) { return(STATUS_SETTING_STEPS_NEG); } @@ -279,7 +279,7 @@ int8_t settings_store_setting(int parameter, float value) { case 13: if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; - protocol_warning_message(WARNING_HOMING_ENABLE); + protocol_misc_message(MESSAGE_HOMING_ENABLE); } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } break; case 14: settings.homing_dir_mask = trunc(value); break; @@ -293,7 +293,7 @@ int8_t settings_store_setting(int parameter, float value) { break; case 20: settings.decimal_places = round(value); break; default: - return(STATUS_SETTING_INVALID); + return(STATUS_INVALID_STATEMENT); } write_settings(); return(STATUS_OK); @@ -302,7 +302,7 @@ int8_t settings_store_setting(int parameter, float value) { // Initialize the config subsystem void settings_init() { if(!read_settings()) { - protocol_warning_message(WARNING_SETTING_READ_FAIL); + protocol_status_message(STATUS_SETTING_READ_FAIL); settings_reset(true); write_settings(); settings_dump(); diff --git a/settings.h b/settings.h index 3d2086a..635911b 100755 --- a/settings.h +++ b/settings.h @@ -66,10 +66,10 @@ void settings_init(); void settings_dump(); // Handle settings command -int8_t settings_execute_line(char *line); +uint8_t settings_execute_line(char *line); // A helper method to set new settings from command line -int8_t settings_store_setting(int parameter, float value); +uint8_t settings_store_setting(int parameter, float value); // int8_t settings_execute_startup(); diff --git a/stepper.c b/stepper.c index f96c3f4..117c7a2 100755 --- a/stepper.c +++ b/stepper.c @@ -113,7 +113,7 @@ void st_go_idle() // Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete // stop and not drift from residual inertial forces at the end of the last movement. delay_ms(settings.stepper_idle_lock_time); - // Disable steppers only upon system alarm activated or by user setting to keep 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)) { STEPPERS_DISABLE_PORT |= (1< Date: Sun, 21 Oct 2012 19:29:18 -0600 Subject: [PATCH 35/55] Added misc message to indicate how to exit ALARM mode. --- protocol.c | 9 ++++++--- protocol.h | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/protocol.c b/protocol.c index 942b22c..e78f4af 100755 --- a/protocol.c +++ b/protocol.c @@ -60,7 +60,7 @@ void protocol_status_message(uint8_t status_code) case STATUS_INVALID_STATEMENT: printPgmString(PSTR("Invalid statement")); break; case STATUS_HARD_LIMIT: - printPgmString(PSTR(" Limit triggered")); break; + printPgmString(PSTR("Limit triggered")); break; case STATUS_SETTING_DISABLED: printPgmString(PSTR("Grbl setting disabled")); break; case STATUS_SETTING_STEPS_NEG: @@ -77,13 +77,15 @@ void protocol_status_message(uint8_t status_code) // Prints miscellaneous messages. This serves as a centralized method to provide additional // user feedback for things that do not pass through the protocol_execute_line() function -// and are not errors or confirmations, such as setup warnings. +// and are not errors or confirmations, such as setup warnings and how to exit alarms. void protocol_misc_message(uint8_t message_code) { // TODO: Install silence misc messages option in settings switch(message_code) { + case MESSAGE_SYSTEM_ALARM: + printPgmString(PSTR("")); break; case MESSAGE_HOMING_ENABLE: - printPgmString(PSTR("warning: Install all axes limit switches before use")); break; + printPgmString(PSTR("warning: Install all axes limit switches before use")); break; } printPgmString(PSTR("\r\n")); } @@ -167,6 +169,7 @@ void protocol_execute_runtime() // System alarm. Something has gone wrong. Disable everything until system reset. if (rt_exec & EXEC_ALARM) { + if (bit_isfalse(sys.execute,EXEC_RESET)) { protocol_misc_message(MESSAGE_SYSTEM_ALARM); } while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } bit_false(sys.execute,EXEC_ALARM); } diff --git a/protocol.h b/protocol.h index bed6092..95fc285 100755 --- a/protocol.h +++ b/protocol.h @@ -44,6 +44,7 @@ #define STATUS_SETTING_READ_FAIL 11 // Define Grbl misc message codes +#define MESSAGE_SYSTEM_ALARM 0 #define MESSAGE_HOMING_ENABLE 1 From e0a9054e32f6e00cf6931552b11b9f201e240bf0 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Thu, 1 Nov 2012 09:37:27 -0600 Subject: [PATCH 36/55] New report module. 6 persistent work coordinates. New G-codes and settings. README and minor bug updates (NOTE: This push is likely buggy so proceed with caution. Just uploading to let people know where we're going.) - New report.c module. Moved all feedback functions into this module to centralize these processes. Includes realtime status reports, status messages, feedback messages. - Official support 6 work coordinate systems (G54-G59), which are persistently held in EEPROM memory. - New g-code support: G28.1, G30.1 stores current machine position as a home position into EEPROM. G10 L20 Px stores current machine position into work coordinates without needing to explicitly send XYZ words. - Homing performed with '$H' command. G28/G30 no longer start the homing cycle. This is how it's supposed to be. - New settings: Stepper enable invert and n_arc correction installed. - Updated and changed up some limits and homing functionality. Pull-off travel will now move after the homing cycle regardless of hard limits enabled. Fixed direction of pull-off travel (went wrong way). - Started on designing an internal Grbl command protocol based on the '$' settings letter. Commands with non numeric characters after '$' will perform switch commands, homing cycle, jogging, printing paramters, etc. Much more to do here. - Updated README to reflect all of the new features. --- Makefile | 4 +- config.h | 23 +- gcode.c | 92 +++++--- limits.c | 16 +- main.c | 9 +- motion_control.c | 53 +++-- nuts_bolts.h | 17 +- protocol.c | 259 +++++++++------------ protocol.h | 25 -- readme.textile | 13 +- report.c | 211 +++++++++++++++++ report.h | 59 +++++ serial.c | 2 +- settings.c | 577 +++++++++++++++++++++-------------------------- settings.h | 40 +++- stepper.c | 64 ++++-- stepper.h | 4 + 17 files changed, 847 insertions(+), 621 deletions(-) create mode 100644 report.c create mode 100644 report.h mode change 100755 => 100644 settings.c diff --git a/Makefile b/Makefile index cfc0186..58a4d9d 100755 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ # Part of Grbl # # Copyright (c) 2009-2011 Simen Svale Skogsrud +# Copyright (c) 2012 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 @@ -31,7 +32,8 @@ DEVICE ?= atmega328p CLOCK = 16000000 PROGRAMMER ?= -c avrisp2 -P usb OBJECTS = main.o motion_control.o gcode.o spindle_control.o coolant_control.o serial.o \ - protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o print.o + protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o \ + print.o report.o # FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m # update that line with this when programmer is back up: diff --git a/config.h b/config.h index 6542913..a598b5c 100755 --- a/config.h +++ b/config.h @@ -80,10 +80,6 @@ #define CMD_CYCLE_START '~' #define CMD_RESET 0x18 // ctrl-x -// Specifies the number of work coordinate systems grbl will support (G54 - G59). -// This parameter must be one or greater, currently supporting up to a value of 6. -#define N_COORDINATE_SYSTEM 1 - // The temporal resolution of the acceleration management subsystem. Higher number give smoother // acceleration but may impact performance. // NOTE: Increasing this parameter will help any resolution related issues, especially with machines @@ -106,13 +102,6 @@ // never reach its target. This parameter should always be greater than zero. #define MINIMUM_STEPS_PER_MINUTE 800 // (steps/min) - Integer value only -// Number of arc generation iterations by small angle approximation before exact arc trajectory -// correction. This parameter maybe decreased if there are issues with the accuracy of the arc -// generations. In general, the default value is more than enough for the intended CNC applications -// of grbl, and should be on the order or greater than the size of the buffer to help with the -// computational efficiency of generating arcs. -#define N_ARC_CORRECTION 25 // Integer (1-255) - // Time delay increments performed during a dwell. The default value is set at 50ms, which provides // a maximum time delay of roughly 55 minutes, more than enough for most any application. Increasing // this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of @@ -172,5 +161,17 @@ // words, the steppers never disable for users that require this. // -> NOW INSTALLED IN SETTINGS #define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer > 0 +// Number of arc generation iterations by small angle approximation before exact arc trajectory +// correction. This parameter maybe decreased if there are issues with the accuracy of the arc +// generations. In general, the default value is more than enough for the intended CNC applications +// of grbl, and should be on the order or greater than the size of the buffer to help with the +// computational efficiency of generating arcs. +// -> NOW INSTALLED IN SETTINGS #define N_ARC_CORRECTION 25 // Integer (1-255) + +// Specifies the number of work coordinate systems grbl will support (G54 - G59). +// This parameter must be one or greater, currently supporting up to a value of 6. +// -> NOW CODED INTO SETTINGS.C #define N_COORDINATE_SYSTEM 6 + +// TODO: Install compile-time option to send numeric status codes rather than strings. #endif diff --git a/gcode.c b/gcode.c index 58bc042..cbda3df 100755 --- a/gcode.c +++ b/gcode.c @@ -32,6 +32,7 @@ #include "coolant_control.h" #include "errno.h" #include "protocol.h" +#include "report.h" // Define modal group internal numbers for checking multiple command violations and tracking the // type of command that is called in the block. A modal group is a group of g-code commands that are @@ -64,9 +65,12 @@ #define NON_MODAL_NONE 0 #define NON_MODAL_DWELL 1 // G4 #define NON_MODAL_SET_COORDINATE_DATA 2 // G10 -#define NON_MODAL_GO_HOME 3 // G28,G30 -#define NON_MODAL_SET_COORDINATE_OFFSET 4 // G92 -#define NON_MODAL_RESET_COORDINATE_OFFSET 5 //G92.1 +#define NON_MODAL_GO_HOME_0 3 // G28 +#define NON_MODAL_SET_HOME_0 4 // G28.1 +#define NON_MODAL_GO_HOME_1 5 // G30 +#define NON_MODAL_SET_HOME_1 6 // G30.1 +#define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92 +#define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1 typedef struct { uint8_t status_code; // Parser status for current block @@ -182,22 +186,20 @@ uint8_t gc_execute_line(char *line) case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; case 20: gc.inches_mode = true; break; case 21: gc.inches_mode = false; break; + // NOTE: G28.1, G30.1 sets home position parameters. Not currently supported. case 28: case 30: - // NOTE: G28.1, G30.1 sets home position parameters. Not currently supported. - if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { - non_modal_action = NON_MODAL_GO_HOME; - } else { - FAIL(STATUS_SETTING_DISABLED); + int_value = trunc(10*value); // Multiply by 10 to pick up G92.1 + switch(int_value) { + case 280: non_modal_action = NON_MODAL_GO_HOME_0; break; + case 281: non_modal_action = NON_MODAL_SET_HOME_0; break; + case 300: non_modal_action = NON_MODAL_GO_HOME_1; break; + case 301: non_modal_action = NON_MODAL_SET_HOME_1; break; + default: FAIL(STATUS_UNSUPPORTED_STATEMENT); } break; case 53: absolute_override = true; break; case 54: case 55: case 56: case 57: case 58: case 59: - int_value -= 54; // Compute coordinate system row index (0=G54,1=G55,...) - if (int_value < N_COORDINATE_SYSTEM) { - sys.coord_select = int_value; - } else { - FAIL(STATUS_UNSUPPORTED_STATEMENT); - } + sys.coord_select = int_value-54; break; case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; case 90: gc.absolute_mode = true; break; @@ -307,6 +309,13 @@ uint8_t gc_execute_line(char *line) // [*M7,M8,M9]: Update coolant state coolant_run(gc.coolant_mode); + // [G54,G55,...,G59]: Coordinate system selection + if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_12)) ) { // Check if called in block + float coord_data[N_AXIS]; + if (!(settings_read_coord_data(sys.coord_select,coord_data))) { return(STATUS_SETTING_READ_FAIL); } + memcpy(sys.coord_system,coord_data,sizeof(coord_data)); + } + // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets. // NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this // modal group and do not effect these actions. @@ -320,30 +329,41 @@ uint8_t gc_execute_line(char *line) break; case NON_MODAL_SET_COORDINATE_DATA: int_value = trunc(p); // Convert p value to int. - if (l != 2 || (int_value < 1 || int_value > N_COORDINATE_SYSTEM)) { // L2 only. P1=G54, P2=G55, ... + if ((l != 2 && l != 20) || (int_value < 1 || int_value > N_COORDINATE_SYSTEM)) { // L2 and L20. P1=G54, P2=G55, ... FAIL(STATUS_UNSUPPORTED_STATEMENT); - } else if (!axis_words) { // No axis words. + } else if (!axis_words && l==2) { // No axis words. FAIL(STATUS_INVALID_STATEMENT); } else { - int_value--; // Adjust p to be inline with row array index. - // Update axes defined only in block. Always in machine coordinates. Can change non-active system. - uint8_t i; - for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used. - if ( bit_istrue(axis_words,bit(i)) ) { sys.coord_system[int_value][i] = target[i]; } + int_value--; // Adjust P index to EEPROM coordinate data indexing. + if (l == 20) { + settings_write_coord_data(int_value,gc.position); + // Update system coordinate system if currently active. + if (sys.coord_select == int_value) { memcpy(sys.coord_system,gc.position,sizeof(gc.position)); } + } else { + float coord_data[N_AXIS]; + if (!settings_read_coord_data(int_value,coord_data)) { return(STATUS_SETTING_READ_FAIL); } + // Update axes defined only in block. Always in machine coordinates. Can change non-active system. + uint8_t i; + for (i=0; i (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) @@ -75,6 +76,11 @@ // #define bit(6) // #define bit(7) +// Define Grbl system states for sys.state + +// Define position lost in states? + + // Define global system variables typedef struct { uint8_t abort; // System abort flag. Forces exit back to main loop for reset. @@ -86,10 +92,9 @@ typedef struct { // NOTE: This may need to be a volatile variable, if problems arise. uint8_t coord_select; // Active work coordinate system number. Default: 0=G54. - float coord_system[N_COORDINATE_SYSTEM][3]; // Work coordinate systems (G54+). Stores offset from - // absolute machine position in mm. - // Rows: Work system number (0=G54,1=G55,...5=G59), Columns: XYZ Offsets - float coord_offset[3]; // Retains the G92 coordinate offset (work coordinates) relative to + float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine + // position in mm. Loaded from EEPROM when called. + float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to // machine zero in mm. volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program. diff --git a/protocol.c b/protocol.c index e78f4af..439b148 100755 --- a/protocol.c +++ b/protocol.c @@ -31,124 +31,16 @@ #include #include "stepper.h" #include "planner.h" +#include "report.h" +#include "motion_control.h" static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. static uint8_t char_counter; // Last character counter in line variable. static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters. -// Method to handle status messages, from an 'ok' to report any 'error' that has occurred. -// Errors can originate from the g-code parser, settings module, or a critical error, such -// as a triggered hard limit. -void protocol_status_message(uint8_t status_code) -{ - // TODO: Compile time option to only return numeric codes for GUIs. - if (status_code == 0) { // STATUS_OK - printPgmString(PSTR("ok\r\n")); - } else { - printPgmString(PSTR("error: ")); - switch(status_code) { - case STATUS_BAD_NUMBER_FORMAT: - printPgmString(PSTR("Bad number format")); break; - case STATUS_EXPECTED_COMMAND_LETTER: - printPgmString(PSTR("Expected command letter")); break; - case STATUS_UNSUPPORTED_STATEMENT: - printPgmString(PSTR("Unsupported statement")); break; - case STATUS_FLOATING_POINT_ERROR: - printPgmString(PSTR("Floating point error")); break; - case STATUS_MODAL_GROUP_VIOLATION: - printPgmString(PSTR("Modal group violation")); break; - case STATUS_INVALID_STATEMENT: - printPgmString(PSTR("Invalid statement")); break; - case STATUS_HARD_LIMIT: - printPgmString(PSTR("Limit triggered")); break; - case STATUS_SETTING_DISABLED: - printPgmString(PSTR("Grbl setting disabled")); break; - case STATUS_SETTING_STEPS_NEG: - printPgmString(PSTR("Steps/mm must be > 0.0")); break; - case STATUS_SETTING_STEP_PULSE_MIN: - printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break; - case STATUS_SETTING_READ_FAIL: - printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; - } - printPgmString(PSTR("\r\n")); - } -} - - -// Prints miscellaneous messages. This serves as a centralized method to provide additional -// user feedback for things that do not pass through the protocol_execute_line() function -// and are not errors or confirmations, such as setup warnings and how to exit alarms. -void protocol_misc_message(uint8_t message_code) -{ - // TODO: Install silence misc messages option in settings - switch(message_code) { - case MESSAGE_SYSTEM_ALARM: - printPgmString(PSTR("")); break; - case MESSAGE_HOMING_ENABLE: - printPgmString(PSTR("warning: Install all axes limit switches before use")); break; - } - printPgmString(PSTR("\r\n")); -} - - -void protocol_status_report() -{ - // TODO: Status report data is written to the user here. This function should be able to grab a - // real-time snapshot of the stepper subprogram and the actual location of the CNC machine. At a - // minimum, status report should return real-time location information. Other important information - // 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. '[0.23,120.4,2.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 (5-20Hz). - - // **Under construction** Bare-bones status report. Provides real-time machine position relative to - // the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). - // The following are still needed: user setting of output units (mm|inch), compressed (non-human - // readable) data for interfaces?, save last known position in EEPROM?, code optimizations, solidify - // the reporting schemes, move to a separate .c file for easy user accessibility, and setting the - // home position by the user (likely through '$' setting interface). - // Successfully tested at a query rate of 10-20Hz while running a gauntlet of programs at various - // speeds. - uint8_t i; - int32_t current_position[3]; // Copy current state of the system position variable - memcpy(current_position,sys.position,sizeof(sys.position)); - float print_position[3]; - - // Report machine position - printPgmString(PSTR("MPos:[")); - for (i=0; i<= 2; i++) { - print_position[i] = current_position[i]/settings.steps_per_mm[i]; - if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; } - printFloat(print_position[i]); - if (i < 2) { printPgmString(PSTR(",")); } - else { printPgmString(PSTR("]")); } - } - - // Report work position - printPgmString(PSTR(",WPos:[")); - for (i=0; i<= 2; i++) { - if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { - print_position[i] -= (sys.coord_system[sys.coord_select][i]+sys.coord_offset[i])*INCH_PER_MM; - } else { - print_position[i] -= sys.coord_system[sys.coord_select][i]+sys.coord_offset[i]; - } - printFloat(print_position[i]); - if (i < 2) { printPgmString(PSTR(",")); } - else { printPgmString(PSTR("]")); } - } - - printPgmString(PSTR("\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; } @@ -160,29 +52,43 @@ void protocol_init() // This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code // 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 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. +// define more computationally-expensive volatile variables. This also provides a controlled way to +// execute certain tasks without having two or more instances of the same task, such as the planner +// recalculating the buffer upon a feedhold or override. +// NOTE: The sys.execute variable flags are set by the serial read subprogram, except where noted, +// but may be set by any process, such as a switch pin change interrupt when pinouts are installed. void protocol_execute_runtime() { if (sys.execute) { // Enter only if any bit flag is true uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times - // System alarm. Something has gone wrong. Disable everything until system reset. + // System alarm. Something has gone wrong. Disable everything by entering an infinite + // loop until system reset/abort. if (rt_exec & EXEC_ALARM) { - if (bit_isfalse(sys.execute,EXEC_RESET)) { protocol_misc_message(MESSAGE_SYSTEM_ALARM); } - while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } + if (bit_isfalse(rt_exec,EXEC_RESET)) { // Ignore loop if reset is already issued + report_feedback_message(MESSAGE_SYSTEM_ALARM); + while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } + } bit_false(sys.execute,EXEC_ALARM); } // System abort. Steppers have already been force stopped. if (rt_exec & EXEC_RESET) { sys.abort = true; + + // If the cycle is active before killing the motion, the event will likely caused a loss + // of position since there is no controlled deceleration(feed hold) to a stop. + // TODO: Add force home option upon position lost. Need to verify that cycle start isn't + // set false by anything but the stepper module. Also, need to look at a better place for + // this. Main.c? + // if (sys.cycle_start) { protocol_feedback_message(MESSAGE_POSITION_LOST); } + return; // Nothing else to do but exit. } // Execute and serial print status if (rt_exec & EXEC_STATUS_REPORT) { - protocol_status_report(); + report_realtime_status(); bit_false(sys.execute,EXEC_STATUS_REPORT); } @@ -206,41 +112,95 @@ void protocol_execute_runtime() } bit_false(sys.execute,EXEC_CYCLE_START); } - } + } + + // Overrides flag byte (sys.override) and execution should be installed here, since they + // are runtime and require a direct and controlled interface to the main stepper program. } -// Executes one line of input according to protocol +// Directs and executes one line of formatted input from protocol_process. While mostly +// incoming streaming g-code blocks, this also executes Grbl internal commands, such as +// settings, initiating the homing cycle, and toggling switch states. This differs from +// the runtime command module by being susceptible to when Grbl is ready to execute the +// next line during a cycle, so for switches like block delete, the switch only effects +// the lines that are processed afterward, not necessarily real-time during a cycle, +// since there are motions already stored in the buffer. However, this 'lag' should not +// be an issue, since these commands are not typically used during a cycle. uint8_t protocol_execute_line(char *line) -{ +{ + // Grbl internal command and parameter lines are of the form '$4=374.3' or '$' for help 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 { - // - // JOG MODE - // - // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be - // susceptible to other runtime commands except for e-stop. The jogging function is intended to - // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped - // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would - // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the - // motion by repeatedly toggling to slow the motion to the desired location. Location data would - // need to be updated real-time and supplied to the user through status queries. - // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are - // handled by the planner. It would be possible for the jog subprogram to insert blocks into the - // block buffer without having the planner plan them. It would need to manage de/ac-celerations - // on its own carefully. This approach could be effective and possibly size/memory efficient. - + + uint8_t char_counter = 1; + float parameter, value; + switch( line[char_counter] ) { + case 0 : + report_grbl_help(); + return(STATUS_OK); + break; +// case '#' : +// if ( line[++char_counter] == 0 ) { +// // Print all parameters +// return(STATUS_OK); +// } else { +// return(STATUS_UNSUPPORTED_STATEMENT); +// } +// case 'G' : // Start up blocks +// if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } +// if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } +// // Extract startup block, execute, and store. +// for (char_counter = 0; char_counter < LINE_BUFFER_SIZE-3; char_counter++) { +// line[char_counter] = line[char_counter+3]; +// } +// uint8_t status = gc_execute_line(line); +// if (status) { return(status); } +// else { settings_store_startup_block(line); } +// break; + case 'H' : // Perform homing cycle + if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { + mc_go_home(); + return(STATUS_OK); + } else { + return(STATUS_SETTING_DISABLED); + } + break; +// // case 'J' : break; // Jogging methods +// // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be +// // susceptible to other runtime commands except for e-stop. The jogging function is intended to +// // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped +// // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would +// // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the +// // motion by repeatedly toggling to slow the motion to the desired location. Location data would +// // need to be updated real-time and supplied to the user through status queries. +// // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are +// // handled by the planner. It would be possible for the jog subprogram to insert blocks into the +// // block buffer without having the planner plan them. It would need to manage de/ac-celerations +// // on its own carefully. This approach could be effective and possibly size/memory efficient. +// case 'P' : // Print g-code parameters and parser state +// if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } +// +// break; +// case 'S' : // Switch methods +// // Opt stop and block delete are referred to as switches. +// // How to store home position and work offsets real-time?? +// break; +// // Parse $parameter=value settings + default : + if(!read_float(line, &char_counter, ¶meter)) { + return(STATUS_BAD_NUMBER_FORMAT); + } + if(line[char_counter++] != '=') { + return(STATUS_UNSUPPORTED_STATEMENT); + } + if(!read_float(line, &char_counter, &value)) { + return(STATUS_BAD_NUMBER_FORMAT); + } + if(line[char_counter] != 0) { + return(STATUS_UNSUPPORTED_STATEMENT); + } + return(settings_store_global_setting(parameter, value)); + } } else { return(gc_execute_line(line)); // Everything else is gcode // TODO: Install option to set system alarm upon any error code received back from the @@ -250,7 +210,8 @@ uint8_t protocol_execute_line(char *line) } -// Process one line of incoming serial data. Remove unneeded characters and capitalize. +// Process and report status one line of incoming serial data. Performs an initial filtering +// by removing spaces and comments and capitalizing all letters. void protocol_process() { uint8_t c; @@ -265,10 +226,10 @@ void protocol_process() if (char_counter > 0) {// Line is complete. Then execute! line[char_counter] = 0; // Terminate string - protocol_status_message(protocol_execute_line(line)); + report_status_message(protocol_execute_line(line)); } else { // Empty or comment line. Skip block. - protocol_status_message(STATUS_OK); // Send status message for syncing purposes. + report_status_message(STATUS_OK); // Send status message for syncing purposes. } char_counter = 0; // Reset line buffer index iscomment = false; // Reset comment flag diff --git a/protocol.h b/protocol.h index 95fc285..f176fd5 100755 --- a/protocol.h +++ b/protocol.h @@ -29,25 +29,6 @@ // buffer. #define LINE_BUFFER_SIZE 50 -// Define Grbl status codes. -#define STATUS_OK 0 -#define STATUS_BAD_NUMBER_FORMAT 1 -#define STATUS_EXPECTED_COMMAND_LETTER 2 -#define STATUS_UNSUPPORTED_STATEMENT 3 -#define STATUS_FLOATING_POINT_ERROR 4 -#define STATUS_MODAL_GROUP_VIOLATION 5 -#define STATUS_INVALID_STATEMENT 6 -#define STATUS_HARD_LIMIT 7 -#define STATUS_SETTING_DISABLED 8 -#define STATUS_SETTING_STEPS_NEG 9 -#define STATUS_SETTING_STEP_PULSE_MIN 10 -#define STATUS_SETTING_READ_FAIL 11 - -// Define Grbl misc message codes -#define MESSAGE_SYSTEM_ALARM 0 -#define MESSAGE_HOMING_ENABLE 1 - - // Initialize the serial protocol void protocol_init(); @@ -61,10 +42,4 @@ uint8_t protocol_execute_line(char *line); // Checks and executes a runtime command at various stop points in main program void protocol_execute_runtime(); -// Prints Grbl's status messages. -void protocol_status_message(uint8_t status_code); - -// Prints any misc messages. -void protocol_misc_message(uint8_t message_code); - #endif diff --git a/readme.textile b/readme.textile index 9bcc8ea..3c5fde6 100755 --- a/readme.textile +++ b/readme.textile @@ -9,19 +9,24 @@ It accepts standards-compliant G-code and has been tested with the output of sev Grbl includes full acceleration management with look ahead. That means the controller will look up to 18 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering. *Changelog for v0.8 from v0.7:* - - *ALPHA status: _Under heavy development. Code state may significantly change with each push as new features are integrated._* + - *BETA status: _Under development. Code state may significantly change with each push as new features are integrated._* - Major structural overhaul to allow for multi-tasking events and new feature sets - New run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status reporting - Controlled feed hold with deceleration to ensure no skipped steps and loss of location. - After feed hold, cycle accelerations are re-planned and may be resumed. + - Advanced homing cycle with direction and speed configuration options. (Requires limit switches.) + - Limit pins are held normal high with an internal pull-up resister. Wiring only requires a normally-open switch connected to ground. (For both ends of an axis, simply wire two in parallel into the same pin.) + - Hard limits option and plays nice with homing cycle, so switches can be used for both homing and hard limits. - Re-factored g-code parser with robust error-checking. - - Work coordinate system (G54), offsets(G92), and machine coordinate system support. G10 work coordinate settings support. (Up to 6 work coordinate systems(G54-G59) available as a compile-time option.) + - 6 work coordinate systems (G54-G59), offsets(G92), and machine coordinate system support. Work systems are stored in EEPROM and persistent. + - G10 L2 and L20 work coordinate settings support. L2 sets one or more axes values. L20 sets the current machine position to the specified work origin. - Program stop(M0,M1*,M2,M30) initial support. Optional stop to do. + - Coolant control(M7*,M8,M9) support. (M7 is a compile-time option). - System reset re-initializes grbl without resetting the Arduino and retains machine/home position and work coordinates. - Restructured planner and stepper modules to become independent and ready for future features. - - Reduced serial read buffer to 128 characters and increased write buffer to 64 characters. + - Settings re-factoring to allow configuration without compiling of most features. (Still in progress). - Misc bug fixes and removed deprecated acceleration enabled code. - - Planned features: Jog mode, full-featured status reporting, runtime settings such as toggling block delete. + - Planned features: Axis acceleration and max speed individual settings, full-featured status reporting, runtime settings such as toggling block delete. - Advanced compile-time options: Up to 6 work coordinate systems(G54-G59), XON/XOFF flow control (limited support), direction and step pulse time delay. diff --git a/report.c b/report.c new file mode 100644 index 0000000..9bb3522 --- /dev/null +++ b/report.c @@ -0,0 +1,211 @@ +/* + report.c - reporting and messaging methods + Part of Grbl + + Copyright (c) 2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +/* + This file functions as the primary feedback interface for Grbl. Any outgoing data, such + as the protocol status messages, feedback messages, and status reports, are stored here. + For the most part, these functions primarily are called from protocol.c methods. If a + different style feedback is desired (i.e. JSON), then a user can change these following + methods to accomodate their needs. +*/ + +#include +#include "print.h" +#include "settings.h" +#include +#include "nuts_bolts.h" +#include +#include "report.h" +#include "protocol.h" + + +// Handles the primary confirmation protocol response for streaming interfaces and human-feedback. +// For every incoming line, this method responds with an 'ok' for a successful command or an +// 'error:' to indicate some error event with the line or some critical system error during +// operation. Errors events can originate from the g-code parser, settings module, or asynchronously +// from a critical error, such as a triggered hard limit. Interface should always monitor for these +// responses. +// NOTE: In silent mode, all error codes are greater than zero. +// TODO: Install silent mode to return only numeric values, primarily for GUIs. +void report_status_message(uint8_t status_code) +{ + if (status_code == 0) { // STATUS_OK + printPgmString(PSTR("ok\r\n")); + } else { + printPgmString(PSTR("error: ")); + switch(status_code) { + case STATUS_BAD_NUMBER_FORMAT: + printPgmString(PSTR("Bad number format")); break; + case STATUS_EXPECTED_COMMAND_LETTER: + printPgmString(PSTR("Expected command letter")); break; + case STATUS_UNSUPPORTED_STATEMENT: + printPgmString(PSTR("Unsupported statement")); break; + case STATUS_FLOATING_POINT_ERROR: + printPgmString(PSTR("Floating point error")); break; + case STATUS_MODAL_GROUP_VIOLATION: + printPgmString(PSTR("Modal group violation")); break; + case STATUS_INVALID_STATEMENT: + printPgmString(PSTR("Invalid statement")); break; + case STATUS_HARD_LIMIT: + printPgmString(PSTR("Limit triggered")); break; + case STATUS_SETTING_DISABLED: + printPgmString(PSTR("Grbl setting disabled")); break; + case STATUS_SETTING_VALUE_NEG: + printPgmString(PSTR("Set value must be > 0.0")); break; + case STATUS_SETTING_STEP_PULSE_MIN: + printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break; + case STATUS_SETTING_READ_FAIL: + printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; + } + printPgmString(PSTR("\r\n")); + } +} + + +// Prints feedback messages. This serves as a centralized method to provide additional +// user feedback for things that are not of the status message response protocol. These +// are messages such as setup warnings and how to exit alarms. +// NOTE: For interfaces, messages are always placed within parentheses. And if silent mode +// is installed, the message codes are less than zero. +// TODO: Install silence feedback messages option in settings +void report_feedback_message(int8_t message_code) +{ + printPgmString(PSTR("(")); + switch(message_code) { + case MESSAGE_SYSTEM_ALARM: + printPgmString(PSTR("ALARM: Check and reset Grbl")); break; + case MESSAGE_POSITION_LOST: + printPgmString(PSTR("warning: Position may be lost")); break; + case MESSAGE_HOMING_ENABLE: + printPgmString(PSTR("'$H' to home. Ensure all limit switches are installed")); break; + } + printPgmString(PSTR(")\r\n")); +} + +// Welcome message +void report_init_message() +{ + // Print grbl initialization message + printPgmString(PSTR("\r\nGrbl " GRBL_VERSION)); + printPgmString(PSTR("\r\n'$' for help and to view settings\r\n")); +} + + +void report_grbl_help() { + printPgmString(PSTR("$0 = ")); printFloat(settings.steps_per_mm[X_AXIS]); + printPgmString(PSTR(" (x axis, steps/mm)\r\n$1 = ")); printFloat(settings.steps_per_mm[Y_AXIS]); + printPgmString(PSTR(" (y axis, steps/mm)\r\n$2 = ")); printFloat(settings.steps_per_mm[Z_AXIS]); + printPgmString(PSTR(" (z axis, steps/mm)\r\n$3 = ")); printInteger(settings.pulse_microseconds); + printPgmString(PSTR(" (step pulse, usec)\r\n$4 = ")); printFloat(settings.default_feed_rate); + printPgmString(PSTR(" (default feed rate, mm/min)\r\n$5 = ")); printFloat(settings.default_seek_rate); + printPgmString(PSTR(" (default seek rate, mm/min)\r\n$6 = ")); printFloat(settings.mm_per_arc_segment); + printPgmString(PSTR(" (arc resolution, mm/segment)\r\n$7 = ")); printInteger(settings.invert_mask); + printPgmString(PSTR(" (step port invert mask, int:binary = ")); print_uint8_base2(settings.invert_mask); + printPgmString(PSTR(")\r\n$8 = ")); printFloat(settings.acceleration/(60*60)); // Convert from mm/min^2 for human readability + printPgmString(PSTR(" (acceleration, mm/sec^2)\r\n$9 = ")); printFloat(settings.junction_deviation); + printPgmString(PSTR(" (cornering junction deviation, mm)\r\n$10 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); + printPgmString(PSTR(" (status report inches, bool)\r\n$11 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START)); + printPgmString(PSTR(" (auto start enable, bool)\r\n$12 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); + printPgmString(PSTR(" (invert stepper enable, bool)\r\n$13 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); + printPgmString(PSTR(" (hard limit enable, bool)\r\n$14 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); + printPgmString(PSTR(" (homing enable, bool)\r\n$15 = ")); printInteger(settings.homing_dir_mask); + printPgmString(PSTR(" (homing direction mask, int:binary = ")); print_uint8_base2(settings.homing_dir_mask); + printPgmString(PSTR(")\r\n$16 = ")); printFloat(settings.homing_feed_rate); + printPgmString(PSTR(" (homing feed rate, mm/min)\r\n$17 = ")); printFloat(settings.homing_seek_rate); + printPgmString(PSTR(" (homing seek rate, mm/min)\r\n$18 = ")); printInteger(settings.homing_debounce_delay); + printPgmString(PSTR(" (homing debounce delay, msec)\r\n$19 = ")); printFloat(settings.homing_pulloff); + printPgmString(PSTR(" (homing pull-off travel, mm)\r\n$20 = ")); printInteger(settings.stepper_idle_lock_time); + printPgmString(PSTR(" (stepper idle lock time, msec)\r\n$21 = ")); printInteger(settings.decimal_places); + printPgmString(PSTR(" (decimal places, int)\r\n$22 = ")); printInteger(settings.n_arc_correction); +// char st_line[LINE_BUFFER_SIZE]; +// printPgmString(PSTR("\r\n\r\n Startup\r\n$100 = ")); printString(settings_read_startup(st_line,0)); +// printPgmString(PSTR("\r\n$101 = ")); printString(settings_read_startup(st_line,1)); + +// char buf[4]; +// settings_startup_string((char *)buf); +// printPgmString(PSTR("\r\n Startup: ")); printString(buf); + + printPgmString(PSTR("\r\n'$x=value' to store setting")); +// printPgmString(PSTR("\r\n'$Sx' to toggle switch\r\n")); +} + + + +// void report_gcode_status() +// { +// // Print gcode parser state +// } +// +// void report_gcode_parameters() +// { +// // Print gcode work coordinate offsets? +// } + + +void report_realtime_status() +{ + // TODO: Status report data is written to the user here. This function should be able to grab a + // real-time snapshot of the stepper subprogram and the actual location of the CNC machine. At a + // minimum, status report should return real-time location information. Other important information + // 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. '[0.23,120.4,2.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 (5-20Hz). + + // **Under construction** Bare-bones status report. Provides real-time machine position relative to + // the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). + // The following are still needed: user setting of output units (mm|inch), compressed (non-human + // readable) data for interfaces?, save last known position in EEPROM?, code optimizations, solidify + // the reporting schemes, move to a separate .c file for easy user accessibility, and setting the + // home position by the user (likely through '$' setting interface). + // Successfully tested at a query rate of 10-20Hz while running a gauntlet of programs at various + // speeds. + uint8_t i; + int32_t current_position[3]; // Copy current state of the system position variable + memcpy(current_position,sys.position,sizeof(sys.position)); + float print_position[3]; + + // Report machine position + printPgmString(PSTR("MPos:[")); + for (i=0; i<= 2; i++) { + print_position[i] = current_position[i]/settings.steps_per_mm[i]; + if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; } + printFloat(print_position[i]); + if (i < 2) { printPgmString(PSTR(",")); } + else { printPgmString(PSTR("]")); } + } + + // Report work position + printPgmString(PSTR(",WPos:[")); + for (i=0; i<= 2; i++) { + if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { + print_position[i] -= (sys.coord_system[i]+sys.coord_offset[i])*INCH_PER_MM; + } else { + print_position[i] -= sys.coord_system[i]+sys.coord_offset[i]; + } + printFloat(print_position[i]); + if (i < 2) { printPgmString(PSTR(",")); } + else { printPgmString(PSTR("]")); } + } + + printPgmString(PSTR("\r\n")); +} diff --git a/report.h b/report.h new file mode 100644 index 0000000..a1b9a4a --- /dev/null +++ b/report.h @@ -0,0 +1,59 @@ +/* + report.h - reporting and messaging methods + Part of Grbl + + Copyright (c) 2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ +#ifndef report_h +#define report_h + + +// Define Grbl status codes. +#define STATUS_OK 0 +#define STATUS_BAD_NUMBER_FORMAT 1 +#define STATUS_EXPECTED_COMMAND_LETTER 2 +#define STATUS_UNSUPPORTED_STATEMENT 3 +#define STATUS_FLOATING_POINT_ERROR 4 +#define STATUS_MODAL_GROUP_VIOLATION 5 +#define STATUS_INVALID_STATEMENT 6 +#define STATUS_HARD_LIMIT 7 +#define STATUS_SETTING_DISABLED 8 +#define STATUS_SETTING_VALUE_NEG 9 +#define STATUS_SETTING_STEP_PULSE_MIN 10 +#define STATUS_SETTING_READ_FAIL 11 + +// Define Grbl feedback message codes. Less than zero to distinguish message from error. +#define MESSAGE_SYSTEM_ALARM -1 +#define MESSAGE_POSITION_LOST -2 +#define MESSAGE_HOMING_ENABLE -3 + + +// Prints system status messages. +void report_status_message(uint8_t status_code); + +// Prints miscellaneous feedback messages. +void report_feedback_message(int8_t message_code); + +// Prints welcome message +void report_init_message(); + +// Prints Grbl help and current global settings +void report_grbl_help(); + +// Prints realtime status report +void report_realtime_status(); + +#endif diff --git a/serial.c b/serial.c index a0e7916..89c562b 100755 --- a/serial.c +++ b/serial.c @@ -166,7 +166,7 @@ ISR(USART_RX_vect) case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true case CMD_RESET: // Immediately force stepper and spindle subsystem idle at an interrupt level. - if (!(sys.execute & EXEC_RESET)) { mc_alarm(); } // Stop only first time. + mc_alarm(); sys.execute |= EXEC_RESET; // Set as true break; default: // Write character to buffer diff --git a/settings.c b/settings.c old mode 100755 new mode 100644 index 175cf8d..098fc7e --- a/settings.c +++ b/settings.c @@ -1,321 +1,256 @@ -/* - settings.c - eeprom configuration handling - Part of Grbl - - Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 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 - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Grbl is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Grbl. If not, see . -*/ - -#include -#include -#include "nuts_bolts.h" -#include "settings.h" -#include "eeprom.h" -#include "print.h" -#include -#include "protocol.h" -#include "config.h" - -settings_t settings; - -// Version 1 outdated settings record -typedef struct { - float steps_per_mm[3]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - float default_seek_rate; - uint8_t invert_mask; - float mm_per_arc_segment; -} settings_v1_t; - -// Version 2,3,4 outdated settings record -typedef struct { - float steps_per_mm[3]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - float default_seek_rate; - uint8_t invert_mask; - float mm_per_arc_segment; - float acceleration; - float junction_deviation; -} settings_v2_v4_t; - -// Default settings (used when resetting eeprom-settings) -#define MICROSTEPS 8 -#define DEFAULT_X_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_Y_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_Z_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_STEP_PULSE_MICROSECONDS 30 -#define DEFAULT_MM_PER_ARC_SEGMENT 0.1 -#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min -#define DEFAULT_FEEDRATE 500.0 -#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<= 'A' || line[char_counter] <= 'Z') { -// letter = line[char_counter++]; -// if(line[char_counter++] != '=') { -// return(STATUS_UNSUPPORTED_STATEMENT); -// } -// for (char_counter = 0; char_counter < LINE_BUFFER_SIZE-3; char_counter++) { -// line[char_counter] = line[char_counter+3]; -// } -// uint8_t status = gc_execute_line(line); -// if (status) { return(status); } -// else { settings_store_startup_line(line); } -// -// -// // Opt stop and block delete are referred to as switches. -// // How to store home position and work offsets real-time?? -// -// } else { - if(!read_float(line, &char_counter, ¶meter)) { - return(STATUS_BAD_NUMBER_FORMAT); - } - if(line[char_counter++] != '=') { - return(STATUS_UNSUPPORTED_STATEMENT); - } - if(!read_float(line, &char_counter, &value)) { - return(STATUS_BAD_NUMBER_FORMAT); - } - if(line[char_counter] != 0) { - return(STATUS_UNSUPPORTED_STATEMENT); - } - return(settings_store_setting(parameter, value)); -// } -} - -void write_settings() { - eeprom_put_char(0, SETTINGS_VERSION); - memcpy_to_eeprom_with_checksum(1, (char*)&settings, sizeof(settings_t)); -// -// char buf[4]; buf[0] = 'G'; buf[1] = '2'; buf[2] = '0'; buf[3] = 0; -// memcpy_to_eeprom_with_checksum(512, (char*)buf, 4); -// -} - -int read_settings() { - // Check version-byte of eeprom - uint8_t version = eeprom_get_char(0); - - if (version == SETTINGS_VERSION) { - // Read settings-record and check checksum - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)))) { - return(false); - } - } else { - // Incrementally update the old versions until up-to-date. - if (version == 1) { - // Migrate from settings version 1 to version 4. - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v1_t)))) { - return(false); - } - settings.acceleration = DEFAULT_ACCELERATION; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; - } else if ((version == 2) || (version == 3)) { - // Migrate from settings version 2 and 3 to version 4. - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v2_v4_t)))) { - return(false); - } - if (version == 2) { settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; } - settings.acceleration *= 3600; // Convert to mm/min^2 from mm/sec^2 - } - if (version <= 4) { - // Migrate from settings version 4 to current version. - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v2_v4_t)))) { - return(false); - } - settings_reset(false); - write_settings(); - } else if (version >= 50) { - // Developmental settings. Version numbers greater than or equal to 50 are temporary. - // Currently, this will update the user settings to v4 and the remainder of the settings - // should be re-written to the default value, if the developmental version number changed. - - // Grab settings regardless of error. - memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)); - settings_reset(false); - write_settings(); - } else { - return(false); - } - } - return(true); -} - -// A helper method to set settings from command line -uint8_t settings_store_setting(int parameter, float value) { - switch(parameter) { - case 0: case 1: case 2: - if (value <= 0.0) { return(STATUS_SETTING_STEPS_NEG); } - settings.steps_per_mm[parameter] = value; break; - case 3: - if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } - settings.pulse_microseconds = round(value); break; - case 4: settings.default_feed_rate = value; break; - case 5: settings.default_seek_rate = value; break; - case 6: settings.mm_per_arc_segment = value; break; - case 7: settings.invert_mask = trunc(value); break; - case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. - case 9: settings.junction_deviation = fabs(value); break; - case 10: - if (value) { - settings.flags |= BITFLAG_REPORT_INCHES; - } else { settings.flags &= ~BITFLAG_REPORT_INCHES; } - break; - case 11: - if (value) { - settings.flags |= BITFLAG_AUTO_START; - } else { settings.flags &= ~BITFLAG_AUTO_START; } - break; - case 12: - if (value) { - settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; - } else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } - break; - case 13: - if (value) { - settings.flags |= BITFLAG_HOMING_ENABLE; - protocol_misc_message(MESSAGE_HOMING_ENABLE); - } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } - break; - case 14: settings.homing_dir_mask = trunc(value); break; - case 15: settings.homing_feed_rate = value; break; - case 16: settings.homing_seek_rate = value; break; - case 17: settings.homing_debounce_delay = round(value); break; - case 18: settings.homing_pulloff = value; break; - case 19: - settings.stepper_idle_lock_time = round(value); - // TODO: Immediately check and toggle steppers from always enable or disable? - break; - case 20: settings.decimal_places = round(value); break; - default: - return(STATUS_INVALID_STATEMENT); - } - write_settings(); - return(STATUS_OK); -} - -// Initialize the config subsystem -void settings_init() { - if(!read_settings()) { - protocol_status_message(STATUS_SETTING_READ_FAIL); - settings_reset(true); - write_settings(); - settings_dump(); - } -} - -// int8_t settings_execute_startup() { -// -// char buf[4]; -// settings_startup_string((char *)buf); -// uint8_t i = 0; -// while (i < 4) { -// serial_write(buf[i++]); -// } -// return(gc_execute_line(buf)); -// } +/* + settings.c - eeprom configuration handling + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011-2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include +#include +#include "nuts_bolts.h" +#include "settings.h" +#include "eeprom.h" +#include "print.h" +#include +#include "protocol.h" +#include "config.h" +#include "report.h" + +settings_t settings; + +// Version 4 outdated settings record +typedef struct { + float steps_per_mm[3]; + uint8_t microsteps; + uint8_t pulse_microseconds; + float default_feed_rate; + float default_seek_rate; + uint8_t invert_mask; + float mm_per_arc_segment; + float acceleration; + float junction_deviation; +} settings_v4_t; + +// Default settings (used when resetting eeprom-settings) +#define MICROSTEPS 8 +#define DEFAULT_X_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_Y_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_Z_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_STEP_PULSE_MICROSECONDS 30 +#define DEFAULT_MM_PER_ARC_SEGMENT 0.1 +#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min +#define DEFAULT_FEEDRATE 500.0 +#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<= 50) { + // Developmental settings. Version numbers greater than or equal to 50 are temporary. + // Currently, this will update the user settings to v4 and the remainder of the settings + // should be re-written to the default value, if the developmental version number changed. + + // Grab settings regardless of error. + memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)); + settings_reset(false); + } else { + return(false); + } + } + return(true); +} + + +// A helper method to set settings from command line +uint8_t settings_store_global_setting(int parameter, float value) { + switch(parameter) { + case 0: case 1: case 2: + if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } + settings.steps_per_mm[parameter] = value; break; + case 3: + if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } + settings.pulse_microseconds = round(value); break; + case 4: settings.default_feed_rate = value; break; + case 5: settings.default_seek_rate = value; break; + case 6: settings.mm_per_arc_segment = value; break; + case 7: settings.invert_mask = trunc(value); break; + case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. + case 9: settings.junction_deviation = fabs(value); break; + case 10: + if (value) { + settings.flags |= BITFLAG_REPORT_INCHES; + } else { settings.flags &= ~BITFLAG_REPORT_INCHES; } + break; + case 11: + if (value) { + settings.flags |= BITFLAG_AUTO_START; + } else { settings.flags &= ~BITFLAG_AUTO_START; } + break; + case 12: + if (value) { + settings.flags |= BITFLAG_INVERT_ST_ENABLE; + } else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } + break; + case 13: + if (value) { + settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; + } else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } + break; + case 14: + if (value) { + settings.flags |= BITFLAG_HOMING_ENABLE; + report_feedback_message(MESSAGE_HOMING_ENABLE); + } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } + break; + case 15: settings.homing_dir_mask = trunc(value); break; + case 16: settings.homing_feed_rate = value; break; + case 17: settings.homing_seek_rate = value; break; + case 18: settings.homing_debounce_delay = round(value); break; + case 19: + if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } + settings.homing_pulloff = value; break; + case 20: + settings.stepper_idle_lock_time = round(value); + // TODO: Immediately check and toggle steppers from always enable or disable? + break; + case 21: settings.decimal_places = round(value); break; + case 22: settings.n_arc_correction = round(value); break; + default: + return(STATUS_INVALID_STATEMENT); + } + write_global_settings(); + return(STATUS_OK); +} + +// Initialize the config subsystem +void settings_init() { + if(!read_global_settings()) { + report_status_message(STATUS_SETTING_READ_FAIL); + settings_reset(true); + report_grbl_help(); + } +} + +// int8_t settings_execute_startup() { +// +// char buf[4]; +// settings_startup_string((char *)buf); +// uint8_t i = 0; +// while (i < 4) { +// serial_write(buf[i++]); +// } +// return(gc_execute_line(buf)); +// } diff --git a/settings.h b/settings.h index 635911b..3af5ba5 100755 --- a/settings.h +++ b/settings.h @@ -24,20 +24,35 @@ #include #include +#include "nuts_bolts.h" #define GRBL_VERSION "0.8b" // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom -#define SETTINGS_VERSION 55 +#define SETTINGS_VERSION 56 // Define bit flag masks for the boolean settings in settings.flag. #define BITFLAG_REPORT_INCHES bit(0) #define BITFLAG_AUTO_START bit(1) -#define BITFLAG_HARD_LIMIT_ENABLE bit(2) -#define BITFLAG_HOMING_ENABLE bit(3) +#define BITFLAG_INVERT_ST_ENABLE bit(2) +#define BITFLAG_HARD_LIMIT_ENABLE bit(3) +#define BITFLAG_HOMING_ENABLE bit(4) -// Current global settings (persisted in EEPROM from byte 1 onwards) +// Define EEPROM memory address location values for on-demand settings and parameters +#define EEPROM_ADDR_GLOBAL 1 +#define EEPROM_ADDR_PARAMETERS 512 +#define EEPROM_ADDR_STARTUP_SCRIPT 768 + +// Define EEPROM address indexing for coordinate parameters +#define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1) +#define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+2 // Total number of system stored (from index 0) +// NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59) +#define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1 +#define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2 +#define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset + +// Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards) typedef struct { float steps_per_mm[3]; uint8_t microsteps; @@ -55,21 +70,22 @@ typedef struct { uint16_t homing_debounce_delay; float homing_pulloff; uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable. - uint8_t decimal_places; + uint8_t decimal_places; + uint8_t n_arc_correction; } settings_t; extern settings_t settings; // Initialize the configuration subsystem (load settings from EEPROM) void settings_init(); -// Print current settings -void settings_dump(); - -// Handle settings command -uint8_t settings_execute_line(char *line); - // A helper method to set new settings from command line -uint8_t settings_store_setting(int parameter, float value); +uint8_t settings_store_global_setting(int parameter, float value); + +// Writes selected coordinate data to EEPROM +void settings_write_coord_data(uint8_t coord_select, float *coord_data); + +// Reads selected coordinate data from EEPROM +uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data); // int8_t settings_execute_startup(); diff --git a/stepper.c b/stepper.c index 117c7a2..97e6e4b 100755 --- a/stepper.c +++ b/stepper.c @@ -84,25 +84,32 @@ static volatile uint8_t busy; // True when SIG_OUTPUT_COMPARE1A is being servi static void set_step_events_per_minute(uint32_t steps_per_minute); -// Stepper state initialization -static void st_wake_up() +// 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() { - // Initialize stepper output bits - out_bits = (0) ^ (settings.invert_mask); - // 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. - 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. - step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3); - #endif // Enable steppers by resetting the stepper disable port - STEPPERS_DISABLE_PORT &= ~(1<> 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. + step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3); + #endif + // Enable stepper driver interrupt + TIMSK1 |= (1< Date: Thu, 1 Nov 2012 19:48:55 -0600 Subject: [PATCH 37/55] Added block delete, opt stop, single block mode. New parser state and parameter feedback. Overhauled '$' command. NOTE: Another incremental update. Likely buggy, still a ways to go before everything is installed, such as startup blocks. - Changed the '$' command to print help. '$$' now prints Grbl settings. The help now instructs the user of runtime commands, switch toggling, homing, etc. Jogging will be added to these in v0.9. - Added switches: block delete, opt stop, and single block mode. - Now can print the g-code parser state and persistent parameters (coord sys) to view what Grbl has internally. - Made the gc struct in the g-code parser global to be able to print the states. Also moved coordinate system tracking from sys to gc struct. - Changed up the welcome flag and updated version to v0.8c. - Removed spindle speed from gcode parser. Not used. --- config.h | 2 +- gcode.c | 106 +++------- gcode.h | 64 +++++- nuts_bolts.h | 24 +-- protocol.c | 129 ++++++------ report.c | 154 +++++++++++--- report.h | 10 +- settings.c | 512 +++++++++++++++++++++++----------------------- settings.h | 12 +- spindle_control.c | 2 +- spindle_control.h | 3 +- 11 files changed, 570 insertions(+), 448 deletions(-) diff --git a/config.h b/config.h index a598b5c..6e96453 100755 --- a/config.h +++ b/config.h @@ -149,7 +149,7 @@ // TODO: The following options are set as compile-time options for now, until the next EEPROM // settings version has solidified. This is to prevent having to support dozens of different // incremental settings versions. -#define BLOCK_DELETE_ENABLE 0 // Block delete enable/disable flag during g-code parsing +// -> NOW CODED INTO SETTINGS #define BLOCK_DELETE_ENABLE 0 // Block delete enable/disable flag during g-code parsing // This parameter sets the delay time before disabling the steppers after the final block of movement. // A short delay ensures the steppers come to a complete stop and the residual inertial force in the diff --git a/gcode.c b/gcode.c index cbda3df..2ba47c0 100755 --- a/gcode.c +++ b/gcode.c @@ -34,62 +34,8 @@ #include "protocol.h" #include "report.h" -// Define modal group internal numbers for checking multiple command violations and tracking the -// type of command that is called in the block. A modal group is a group of g-code commands that are -// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute -// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online, -// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc). -#define MODAL_GROUP_NONE 0 -#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal -#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G80] Motion -#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection -#define MODAL_GROUP_3 4 // [G90,G91] Distance mode -#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping -#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode -#define MODAL_GROUP_6 7 // [G20,G21] Units -#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning -#define MODAL_GROUP_12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection - -// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used -// internally by the parser to know which command to execute. -#define MOTION_MODE_SEEK 0 // G0 -#define MOTION_MODE_LINEAR 1 // G1 -#define MOTION_MODE_CW_ARC 2 // G2 -#define MOTION_MODE_CCW_ARC 3 // G3 -#define MOTION_MODE_CANCEL 4 // G80 - -#define PROGRAM_FLOW_RUNNING 0 -#define PROGRAM_FLOW_PAUSED 1 // M0, M1 -#define PROGRAM_FLOW_COMPLETED 2 // M2, M30 - -#define NON_MODAL_NONE 0 -#define NON_MODAL_DWELL 1 // G4 -#define NON_MODAL_SET_COORDINATE_DATA 2 // G10 -#define NON_MODAL_GO_HOME_0 3 // G28 -#define NON_MODAL_SET_HOME_0 4 // G28.1 -#define NON_MODAL_GO_HOME_1 5 // G30 -#define NON_MODAL_SET_HOME_1 6 // G30.1 -#define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92 -#define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1 - -typedef struct { - uint8_t status_code; // Parser status for current block - uint8_t motion_mode; // {G0, G1, G2, G3, G80} - uint8_t inverse_feed_rate_mode; // {G93, G94} - uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} - uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91} - uint8_t program_flow; // {M0, M1, M2, M30} - int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} - uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9} - float feed_rate, seek_rate; // Millimeters/second - float position[3]; // Where the interpreter considers the tool to be at this point in the code - uint8_t tool; - uint16_t spindle_speed; // RPM/100 - uint8_t plane_axis_0, - plane_axis_1, - plane_axis_2; // The axes of the selected plane -} parser_state_t; -static parser_state_t gc; +// Declare gc extern struct +parser_state_t gc; #define FAIL(status) gc.status_code = status; @@ -105,10 +51,15 @@ static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2) void gc_init() { memset(&gc, 0, sizeof(gc)); - gc.feed_rate = settings.default_feed_rate; + gc.feed_rate = settings.default_feed_rate; // Should be zero at initialization. select_plane(X_AXIS, Y_AXIS, Z_AXIS); gc.absolute_mode = true; + // Load default G54 coordinate system. + if (!(settings_read_coord_data(gc.coord_select,gc.coord_system))) { + report_status_message(STATUS_SETTING_READ_FAIL); + } + // protocol_status_message(settings_execute_startup()); } @@ -186,9 +137,8 @@ uint8_t gc_execute_line(char *line) case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; case 20: gc.inches_mode = true; break; case 21: gc.inches_mode = false; break; - // NOTE: G28.1, G30.1 sets home position parameters. Not currently supported. case 28: case 30: - int_value = trunc(10*value); // Multiply by 10 to pick up G92.1 + int_value = trunc(10*value); // Multiply by 10 to pick up Gxx.1 switch(int_value) { case 280: non_modal_action = NON_MODAL_GO_HOME_0; break; case 281: non_modal_action = NON_MODAL_SET_HOME_0; break; @@ -199,7 +149,7 @@ uint8_t gc_execute_line(char *line) break; case 53: absolute_override = true; break; case 54: case 55: case 56: case 57: case 58: case 59: - sys.coord_select = int_value-54; + gc.coord_select = int_value-54; break; case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; case 90: gc.absolute_mode = true; break; @@ -226,10 +176,11 @@ uint8_t gc_execute_line(char *line) // Set 'M' commands switch(int_value) { case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause - case 1: // Program pause with optional stop on - // if (sys.opt_stop) { // TODO: Add system variable for optional stop. - gc.program_flow = PROGRAM_FLOW_PAUSED; break; - // } + case 1: // Program pause with optional stop on, otherwise do nothing. + if (bit_istrue(sys.switches,BITFLAG_OPT_STOP)) { + gc.program_flow = PROGRAM_FLOW_PAUSED; + } + break; case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset case 3: gc.spindle_direction = 1; break; case 4: gc.spindle_direction = -1; break; @@ -280,7 +231,8 @@ uint8_t gc_execute_line(char *line) case 'R': r = to_millimeters(value); break; case 'S': if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative - gc.spindle_speed = value; + // TBD: Spindle speed not supported due to PWM issues, but may come back once resolved. + // gc.spindle_speed = value; break; case 'T': if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative @@ -304,7 +256,7 @@ uint8_t gc_execute_line(char *line) // ([M6]: Tool change should be executed here.) // [M3,M4,M5]: Update spindle state - spindle_run(gc.spindle_direction, gc.spindle_speed); + spindle_run(gc.spindle_direction); //, gc.spindle_speed); // [*M7,M8,M9]: Update coolant state coolant_run(gc.coolant_mode); @@ -312,8 +264,8 @@ uint8_t gc_execute_line(char *line) // [G54,G55,...,G59]: Coordinate system selection if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_12)) ) { // Check if called in block float coord_data[N_AXIS]; - if (!(settings_read_coord_data(sys.coord_select,coord_data))) { return(STATUS_SETTING_READ_FAIL); } - memcpy(sys.coord_system,coord_data,sizeof(coord_data)); + if (!(settings_read_coord_data(gc.coord_select,coord_data))) { return(STATUS_SETTING_READ_FAIL); } + memcpy(gc.coord_system,coord_data,sizeof(coord_data)); } // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets. @@ -338,7 +290,7 @@ uint8_t gc_execute_line(char *line) if (l == 20) { settings_write_coord_data(int_value,gc.position); // Update system coordinate system if currently active. - if (sys.coord_select == int_value) { memcpy(sys.coord_system,gc.position,sizeof(gc.position)); } + if (gc.coord_select == int_value) { memcpy(gc.coord_system,gc.position,sizeof(gc.position)); } } else { float coord_data[N_AXIS]; if (!settings_read_coord_data(int_value,coord_data)) { return(STATUS_SETTING_READ_FAIL); } @@ -349,7 +301,7 @@ uint8_t gc_execute_line(char *line) } settings_write_coord_data(int_value,coord_data); // Update system coordinate system if currently active. - if (sys.coord_select == int_value) { memcpy(sys.coord_system,coord_data,sizeof(coord_data)); } + if (gc.coord_select == int_value) { memcpy(gc.coord_system,coord_data,sizeof(coord_data)); } } } axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. @@ -363,7 +315,7 @@ uint8_t gc_execute_line(char *line) for (i=0; i. */ - #ifndef gcode_h #define gcode_h #include +#include "nuts_bolts.h" + +// Define modal group internal numbers for checking multiple command violations and tracking the +// type of command that is called in the block. A modal group is a group of g-code commands that are +// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute +// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online, +// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc). +#define MODAL_GROUP_NONE 0 +#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal +#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G80] Motion +#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection +#define MODAL_GROUP_3 4 // [G90,G91] Distance mode +#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping +#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode +#define MODAL_GROUP_6 7 // [G20,G21] Units +#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning +#define MODAL_GROUP_12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection + +// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used +// internally by the parser to know which command to execute. +#define MOTION_MODE_SEEK 0 // G0 +#define MOTION_MODE_LINEAR 1 // G1 +#define MOTION_MODE_CW_ARC 2 // G2 +#define MOTION_MODE_CCW_ARC 3 // G3 +#define MOTION_MODE_CANCEL 4 // G80 + +#define PROGRAM_FLOW_RUNNING 0 +#define PROGRAM_FLOW_PAUSED 1 // M0, M1 +#define PROGRAM_FLOW_COMPLETED 2 // M2, M30 + +#define NON_MODAL_NONE 0 +#define NON_MODAL_DWELL 1 // G4 +#define NON_MODAL_SET_COORDINATE_DATA 2 // G10 +#define NON_MODAL_GO_HOME_0 3 // G28 +#define NON_MODAL_SET_HOME_0 4 // G28.1 +#define NON_MODAL_GO_HOME_1 5 // G30 +#define NON_MODAL_SET_HOME_1 6 // G30.1 +#define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92 +#define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1 + +typedef struct { + uint8_t status_code; // Parser status for current block + uint8_t motion_mode; // {G0, G1, G2, G3, G80} + uint8_t inverse_feed_rate_mode; // {G93, G94} + uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} + uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91} + uint8_t program_flow; // {M0, M1, M2, M30} + int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} + uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9} + float feed_rate; // Millimeters/min + float position[3]; // Where the interpreter considers the tool to be at this point in the code + uint8_t tool; +// uint16_t spindle_speed; // RPM/100 + uint8_t plane_axis_0, + plane_axis_1, + plane_axis_2; // The axes of the selected plane + uint8_t coord_select; // Active work coordinate system number. Default: 0=G54. + float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine + // position in mm. Loaded from EEPROM when called. + float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to + // machine zero in mm. Non-persistent. Cleared upon reset and boot. +} parser_state_t; +extern parser_state_t gc; // Initialize the parser void gc_init(); diff --git a/nuts_bolts.h b/nuts_bolts.h index 981bb50..2eb0525 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -35,12 +35,12 @@ #define Y_AXIS 1 #define Z_AXIS 2 -#define MM_PER_INCH (25.4) -#define INCH_PER_MM (0.03937) +#define MM_PER_INCH (25.40) +#define INCH_PER_MM (0.0393701) // Useful macros #define clear_vector(a) memset(a, 0, sizeof(a)) -#define clear_vector_float(a) memset(a, 0.0, sizeof(float)*3) +#define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) @@ -86,20 +86,16 @@ 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. -// uint8_t switches; // Switches state bitflag variable. For settings not governed by g-code. - - int32_t position[3]; // Real-time machine (aka home) position vector in steps. - // NOTE: This may need to be a volatile variable, if problems arise. + uint8_t switches; // Switches state bitflag variable. For features not governed by g-code. + +// uint8_t state; // Tracks the current state of Grbl. +// TODO: This may replace the functionality of the feed_hold and cycle_start variables. Need to +// make sure that overall processes don't change. - uint8_t coord_select; // Active work coordinate system number. Default: 0=G54. - float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine - // position in mm. Loaded from EEPROM when called. - float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to - // machine zero in mm. - 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. - + int32_t position[3]; // Real-time machine (aka home) position vector in steps. + // NOTE: This may need to be a volatile variable, if problems arise. } system_t; extern system_t sys; diff --git a/protocol.c b/protocol.c index 439b148..864351e 100755 --- a/protocol.c +++ b/protocol.c @@ -135,77 +135,80 @@ uint8_t protocol_execute_line(char *line) uint8_t char_counter = 1; float parameter, value; switch( line[char_counter] ) { - case 0 : - report_grbl_help(); - return(STATUS_OK); + case 0 : report_grbl_help(); break; + case '$' : // Prints Grbl settings + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + else { report_grbl_settings(); } + break; + case '#' : // Print gcode parameters + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + else { report_gcode_parameters(); } + break; + case 'G' : // Prints gcode parser state + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + else { report_gcode_modes(); } break; -// case '#' : -// if ( line[++char_counter] == 0 ) { -// // Print all parameters -// return(STATUS_OK); -// } else { -// return(STATUS_UNSUPPORTED_STATEMENT); -// } -// case 'G' : // Start up blocks -// if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } -// if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } -// // Extract startup block, execute, and store. -// for (char_counter = 0; char_counter < LINE_BUFFER_SIZE-3; char_counter++) { -// line[char_counter] = line[char_counter+3]; -// } -// uint8_t status = gc_execute_line(line); -// if (status) { return(status); } -// else { settings_store_startup_block(line); } -// break; case 'H' : // Perform homing cycle - if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { - mc_go_home(); - return(STATUS_OK); - } else { - return(STATUS_SETTING_DISABLED); - } + if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { mc_go_home(); } + else { return(STATUS_SETTING_DISABLED); } break; -// // case 'J' : break; // Jogging methods -// // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be -// // susceptible to other runtime commands except for e-stop. The jogging function is intended to -// // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped -// // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would -// // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the -// // motion by repeatedly toggling to slow the motion to the desired location. Location data would -// // need to be updated real-time and supplied to the user through status queries. -// // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are -// // handled by the planner. It would be possible for the jog subprogram to insert blocks into the -// // block buffer without having the planner plan them. It would need to manage de/ac-celerations -// // on its own carefully. This approach could be effective and possibly size/memory efficient. -// case 'P' : // Print g-code parameters and parser state -// if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } -// -// break; -// case 'S' : // Switch methods -// // Opt stop and block delete are referred to as switches. -// // How to store home position and work offsets real-time?? -// break; -// // Parse $parameter=value settings - default : - if(!read_float(line, &char_counter, ¶meter)) { - return(STATUS_BAD_NUMBER_FORMAT); - } - if(line[char_counter++] != '=') { - return(STATUS_UNSUPPORTED_STATEMENT); - } - if(!read_float(line, &char_counter, &value)) { - return(STATUS_BAD_NUMBER_FORMAT); - } - if(line[char_counter] != 0) { - return(STATUS_UNSUPPORTED_STATEMENT); - } +// case 'J' : break; // Jogging methods + // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be + // susceptible to other runtime commands except for e-stop. The jogging function is intended to + // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped + // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would + // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the + // motion by repeatedly toggling to slow the motion to the desired location. Location data would + // need to be updated real-time and supplied to the user through status queries. + // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are + // handled by the planner. It would be possible for the jog subprogram to insert blocks into the + // block buffer without having the planner plan them. It would need to manage de/ac-celerations + // on its own carefully. This approach could be effective and possibly size/memory efficient. +// case 'N' : // Start up blocks +// if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } +// if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } +// // Extract startup block, execute, and store. +// for (char_counter = 0; char_counter < LINE_BUFFER_SIZE-3; char_counter++) { +// line[char_counter] = line[char_counter+3]; +// } +// uint8_t status = gc_execute_line(line); +// if (status) { return(status); } +// else { settings_store_startup_block(line); } +// break; + case 'B' : // Toggle block delete + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + sys.switches ^= BITFLAG_BLOCK_DELETE; + if (bit_istrue(sys.switches,BITFLAG_BLOCK_DELETE)) { report_feedback_message(MESSAGE_SWITCH_ON); } + else { report_feedback_message(MESSAGE_SWITCH_OFF); } + break; + case 'S' : // Toggle single block mode + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + sys.switches ^= BITFLAG_SINGLE_BLOCK; + if (bit_istrue(sys.switches,BITFLAG_SINGLE_BLOCK)) { report_feedback_message(MESSAGE_SWITCH_ON); } + else { report_feedback_message(MESSAGE_SWITCH_OFF); } + break; + case 'O' : // Toggle optional stop + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + sys.switches ^= BITFLAG_OPT_STOP; + if (bit_istrue(sys.switches,BITFLAG_OPT_STOP)) { report_feedback_message(MESSAGE_SWITCH_ON); } + else { report_feedback_message(MESSAGE_SWITCH_OFF); } + break; + default : // Store global setting + if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } + if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } + if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } + if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } return(settings_store_global_setting(parameter, value)); } + return(STATUS_OK); // If '$' command makes it to here, then everything's ok. + } else { + return(gc_execute_line(line)); // Everything else is gcode // TODO: Install option to set system alarm upon any error code received back from the // the g-code parser. This is a common safety feature on CNCs to help prevent crashes // if the g-code doesn't perform as intended. + } } @@ -246,9 +249,9 @@ void protocol_process() // Throw away whitepace and control characters } else if (c == '/') { // Disable block delete and throw away characters. Will ignore until EOL. - #if BLOCK_DELETE_ENABLE + if (bit_istrue(sys.switches,BITFLAG_BLOCK_DELETE)) { iscomment = true; - #endif + } } else if (c == '(') { // Enable comments flag and ignore all characters until ')' or EOL. iscomment = true; diff --git a/report.c b/report.c index 9bb3522..2383695 100644 --- a/report.c +++ b/report.c @@ -34,6 +34,8 @@ #include #include "report.h" #include "protocol.h" +#include "gcode.h" +#include "coolant_control.h" // Handles the primary confirmation protocol response for streaming interfaces and human-feedback. @@ -82,12 +84,12 @@ void report_status_message(uint8_t status_code) // Prints feedback messages. This serves as a centralized method to provide additional // user feedback for things that are not of the status message response protocol. These // are messages such as setup warnings and how to exit alarms. -// NOTE: For interfaces, messages are always placed within parentheses. And if silent mode -// is installed, the message codes are less than zero. +// NOTE: For interfaces, messages are always placed within chevrons. And if silent mode +// is installed, the message number codes are less than zero. // TODO: Install silence feedback messages option in settings void report_feedback_message(int8_t message_code) { - printPgmString(PSTR("(")); + printPgmString(PSTR("<")); switch(message_code) { case MESSAGE_SYSTEM_ALARM: printPgmString(PSTR("ALARM: Check and reset Grbl")); break; @@ -95,20 +97,37 @@ void report_feedback_message(int8_t message_code) printPgmString(PSTR("warning: Position may be lost")); break; case MESSAGE_HOMING_ENABLE: printPgmString(PSTR("'$H' to home. Ensure all limit switches are installed")); break; + case MESSAGE_SWITCH_ON: + printPgmString(PSTR("Switch enabled")); break; + case MESSAGE_SWITCH_OFF: + printPgmString(PSTR("Switch disabled")); break; } - printPgmString(PSTR(")\r\n")); + printPgmString(PSTR(">\r\n")); } // Welcome message void report_init_message() { - // Print grbl initialization message - printPgmString(PSTR("\r\nGrbl " GRBL_VERSION)); - printPgmString(PSTR("\r\n'$' for help and to view settings\r\n")); + printPgmString(PSTR("\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n")); } void report_grbl_help() { +// char st_line[LINE_BUFFER_SIZE]; +// printPgmString(PSTR("\r\n\r\n Startup\r\n$100 = ")); printString(settings_read_startup(st_line,0)); +// printPgmString(PSTR("\r\n$101 = ")); printString(settings_read_startup(st_line,1)); + +// char buf[4]; +// settings_startup_string((char *)buf); +// printPgmString(PSTR("\r\n Startup: ")); printString(buf); + + printPgmString(PSTR("$ (help)\r\n$$ (print Grbl settings)\r\n$# (print gcode parameters)\r\n$G (print gcode parser state)")); + printPgmString(PSTR("\r\n$x=value (store Grbl setting)\r\n$Nx=line (store startup block)\r\n$H (run homing cycle, if enabled)")); + printPgmString(PSTR("\r\n$B (toggle block delete)\r\n$S (toggle single block mode)\r\n$O (toggle opt stop)")); + printPgmString(PSTR("\r\n~ (cycle start)\r\n! (feed hold)\r\n? (current position)\r\n^x (reset Grbl)\r\n")); +} + +void report_grbl_settings() { printPgmString(PSTR("$0 = ")); printFloat(settings.steps_per_mm[X_AXIS]); printPgmString(PSTR(" (x axis, steps/mm)\r\n$1 = ")); printFloat(settings.steps_per_mm[Y_AXIS]); printPgmString(PSTR(" (y axis, steps/mm)\r\n$2 = ")); printFloat(settings.steps_per_mm[Z_AXIS]); @@ -134,29 +153,108 @@ void report_grbl_help() { printPgmString(PSTR(" (homing pull-off travel, mm)\r\n$20 = ")); printInteger(settings.stepper_idle_lock_time); printPgmString(PSTR(" (stepper idle lock time, msec)\r\n$21 = ")); printInteger(settings.decimal_places); printPgmString(PSTR(" (decimal places, int)\r\n$22 = ")); printInteger(settings.n_arc_correction); -// char st_line[LINE_BUFFER_SIZE]; -// printPgmString(PSTR("\r\n\r\n Startup\r\n$100 = ")); printString(settings_read_startup(st_line,0)); -// printPgmString(PSTR("\r\n$101 = ")); printString(settings_read_startup(st_line,1)); - -// char buf[4]; -// settings_startup_string((char *)buf); -// printPgmString(PSTR("\r\n Startup: ")); printString(buf); - - printPgmString(PSTR("\r\n'$x=value' to store setting")); -// printPgmString(PSTR("\r\n'$Sx' to toggle switch\r\n")); + printPgmString(PSTR(" (n arc correction, int)\r\n")); } +void report_gcode_parameters() +{ + float coord_data[N_AXIS]; + uint8_t coord_select, i; + for (coord_select = 0; coord_select <= SETTING_INDEX_NCOORD; coord_select++) { + if (!(settings_read_coord_data(coord_select,coord_data))) { + report_status_message(STATUS_SETTING_READ_FAIL); + return; + } + switch (coord_select) { + case 0: printPgmString(PSTR("G54")); break; + case 1: printPgmString(PSTR("G55")); break; + case 2: printPgmString(PSTR("G56")); break; + case 3: printPgmString(PSTR("G57")); break; + case 4: printPgmString(PSTR("G58")); break; + case 5: printPgmString(PSTR("G59")); break; + case 6: printPgmString(PSTR("G28")); break; + case 7: printPgmString(PSTR("G30")); break; + // case 8: printPgmString(PSTR("G92")); break; // G92.2, G92.3 currently not supported. + } + printPgmString(PSTR(":[")); + for (i=0; i. -*/ - -#include -#include -#include "nuts_bolts.h" -#include "settings.h" -#include "eeprom.h" -#include "print.h" -#include -#include "protocol.h" -#include "config.h" -#include "report.h" - -settings_t settings; - -// Version 4 outdated settings record -typedef struct { - float steps_per_mm[3]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - float default_seek_rate; - uint8_t invert_mask; - float mm_per_arc_segment; - float acceleration; - float junction_deviation; -} settings_v4_t; - -// Default settings (used when resetting eeprom-settings) -#define MICROSTEPS 8 -#define DEFAULT_X_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_Y_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_Z_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_STEP_PULSE_MICROSECONDS 30 -#define DEFAULT_MM_PER_ARC_SEGMENT 0.1 -#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min -#define DEFAULT_FEEDRATE 500.0 -#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<= 50) { - // Developmental settings. Version numbers greater than or equal to 50 are temporary. - // Currently, this will update the user settings to v4 and the remainder of the settings - // should be re-written to the default value, if the developmental version number changed. - - // Grab settings regardless of error. - memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)); - settings_reset(false); - } else { - return(false); - } - } - return(true); -} - - -// A helper method to set settings from command line -uint8_t settings_store_global_setting(int parameter, float value) { - switch(parameter) { - case 0: case 1: case 2: - if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } - settings.steps_per_mm[parameter] = value; break; - case 3: - if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } - settings.pulse_microseconds = round(value); break; - case 4: settings.default_feed_rate = value; break; - case 5: settings.default_seek_rate = value; break; - case 6: settings.mm_per_arc_segment = value; break; - case 7: settings.invert_mask = trunc(value); break; - case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. - case 9: settings.junction_deviation = fabs(value); break; - case 10: - if (value) { - settings.flags |= BITFLAG_REPORT_INCHES; - } else { settings.flags &= ~BITFLAG_REPORT_INCHES; } - break; - case 11: - if (value) { - settings.flags |= BITFLAG_AUTO_START; - } else { settings.flags &= ~BITFLAG_AUTO_START; } - break; - case 12: - if (value) { - settings.flags |= BITFLAG_INVERT_ST_ENABLE; - } else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } - break; - case 13: - if (value) { - settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; - } else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } - break; - case 14: - if (value) { - settings.flags |= BITFLAG_HOMING_ENABLE; - report_feedback_message(MESSAGE_HOMING_ENABLE); - } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } - break; - case 15: settings.homing_dir_mask = trunc(value); break; - case 16: settings.homing_feed_rate = value; break; - case 17: settings.homing_seek_rate = value; break; - case 18: settings.homing_debounce_delay = round(value); break; - case 19: - if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } - settings.homing_pulloff = value; break; - case 20: - settings.stepper_idle_lock_time = round(value); - // TODO: Immediately check and toggle steppers from always enable or disable? - break; - case 21: settings.decimal_places = round(value); break; - case 22: settings.n_arc_correction = round(value); break; - default: - return(STATUS_INVALID_STATEMENT); - } - write_global_settings(); - return(STATUS_OK); -} - -// Initialize the config subsystem -void settings_init() { - if(!read_global_settings()) { - report_status_message(STATUS_SETTING_READ_FAIL); - settings_reset(true); - report_grbl_help(); - } -} - -// int8_t settings_execute_startup() { -// -// char buf[4]; -// settings_startup_string((char *)buf); -// uint8_t i = 0; -// while (i < 4) { -// serial_write(buf[i++]); -// } -// return(gc_execute_line(buf)); -// } +/* + settings.c - eeprom configuration handling + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011-2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include +#include +#include "nuts_bolts.h" +#include "settings.h" +#include "eeprom.h" +#include "print.h" +#include +#include "protocol.h" +#include "config.h" +#include "report.h" + +settings_t settings; + +// Version 4 outdated settings record +typedef struct { + float steps_per_mm[3]; + uint8_t microsteps; + uint8_t pulse_microseconds; + float default_feed_rate; + float default_seek_rate; + uint8_t invert_mask; + float mm_per_arc_segment; + float acceleration; + float junction_deviation; +} settings_v4_t; + +// Default settings (used when resetting eeprom-settings) +#define MICROSTEPS 8 +#define DEFAULT_X_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_Y_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_Z_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_STEP_PULSE_MICROSECONDS 30 +#define DEFAULT_MM_PER_ARC_SEGMENT 0.1 +#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min +#define DEFAULT_FEEDRATE 500.0 +#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<= 50) { + // Developmental settings. Version numbers greater than or equal to 50 are temporary. + // Currently, this will update the user settings to v4 and the remainder of the settings + // should be re-written to the default value, if the developmental version number changed. + + // Grab settings regardless of error. + memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)); + settings_reset(false); + } else { + return(false); + } + } + return(true); +} + + +// A helper method to set settings from command line +uint8_t settings_store_global_setting(int parameter, float value) { + switch(parameter) { + case 0: case 1: case 2: + if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } + settings.steps_per_mm[parameter] = value; break; + case 3: + if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } + settings.pulse_microseconds = round(value); break; + case 4: settings.default_feed_rate = value; break; + case 5: settings.default_seek_rate = value; break; + case 6: settings.mm_per_arc_segment = value; break; + case 7: settings.invert_mask = trunc(value); break; + case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. + case 9: settings.junction_deviation = fabs(value); break; + case 10: + if (value) { + settings.flags |= BITFLAG_REPORT_INCHES; + } else { settings.flags &= ~BITFLAG_REPORT_INCHES; } + break; + case 11: + if (value) { + settings.flags |= BITFLAG_AUTO_START; + } else { settings.flags &= ~BITFLAG_AUTO_START; } + break; + case 12: + if (value) { + settings.flags |= BITFLAG_INVERT_ST_ENABLE; + } else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } + break; + case 13: + if (value) { + settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; + } else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } + break; + case 14: + if (value) { + settings.flags |= BITFLAG_HOMING_ENABLE; + report_feedback_message(MESSAGE_HOMING_ENABLE); + } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } + break; + case 15: settings.homing_dir_mask = trunc(value); break; + case 16: settings.homing_feed_rate = value; break; + case 17: settings.homing_seek_rate = value; break; + case 18: settings.homing_debounce_delay = round(value); break; + case 19: + if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } + settings.homing_pulloff = value; break; + case 20: + settings.stepper_idle_lock_time = round(value); + // TODO: Immediately check and toggle steppers from always enable or disable? + break; + case 21: settings.decimal_places = round(value); break; + case 22: settings.n_arc_correction = round(value); break; + default: + return(STATUS_INVALID_STATEMENT); + } + write_global_settings(); + return(STATUS_OK); +} + +// Initialize the config subsystem +void settings_init() { + if(!read_global_settings()) { + report_status_message(STATUS_SETTING_READ_FAIL); + settings_reset(true); + report_grbl_help(); + } +} + +// int8_t settings_execute_startup() { +// +// char buf[4]; +// settings_startup_string((char *)buf); +// uint8_t i = 0; +// while (i < 4) { +// serial_write(buf[i++]); +// } +// return(gc_execute_line(buf)); +// } diff --git a/settings.h b/settings.h index 3af5ba5..3abf350 100755 --- a/settings.h +++ b/settings.h @@ -26,7 +26,7 @@ #include #include "nuts_bolts.h" -#define GRBL_VERSION "0.8b" +#define GRBL_VERSION "0.8c" // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom @@ -39,18 +39,21 @@ #define BITFLAG_HARD_LIMIT_ENABLE bit(3) #define BITFLAG_HOMING_ENABLE bit(4) -// Define EEPROM memory address location values for on-demand settings and parameters +// Define EEPROM memory address location values for Grbl settings and parameters +// NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and +// the startup script. The lower half contains the global settings and space for future +// developments. #define EEPROM_ADDR_GLOBAL 1 #define EEPROM_ADDR_PARAMETERS 512 #define EEPROM_ADDR_STARTUP_SCRIPT 768 // Define EEPROM address indexing for coordinate parameters #define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1) -#define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+2 // Total number of system stored (from index 0) +#define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+1 // Total number of system stored (from index 0) // NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59) #define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1 #define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2 -#define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset +// #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported) // Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards) typedef struct { @@ -72,6 +75,7 @@ typedef struct { uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable. uint8_t decimal_places; uint8_t n_arc_correction; +// uint8_t status_report_mask; // Mask to indicate desired report data. } settings_t; extern settings_t settings; diff --git a/spindle_control.c b/spindle_control.c index abc67e2..1d65f41 100755 --- a/spindle_control.c +++ b/spindle_control.c @@ -43,7 +43,7 @@ void spindle_stop() SPINDLE_ENABLE_PORT &= ~(1< void spindle_init(); -void spindle_run(int8_t direction, uint16_t rpm); +void spindle_run(int8_t direction); //, uint16_t rpm); void spindle_stop(); #endif From 4c711a4af7a21b1005bd2ac59a423b021ae831fb Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sat, 3 Nov 2012 11:32:23 -0600 Subject: [PATCH 38/55] New startup script setting. New dry run, check gcode switches. New system state variable. Lots of reorganizing. (All v0.8 features installed. Still likely buggy, but now thourough testing will need to start to squash them all. As soon as we're done, this will be pushed to master and v0.9 development will be started. Please report ANY issues to us so we can get this rolled out ASAP.) - User startup script! A user can now save one (up to 5 as compile-time option) block of g-code in EEPROM memory. This will be run everytime Grbl resets. Mainly to be used as a way to set your preferences, like G21, G54, etc. - New dry run and check g-code switches. Dry run moves ALL motions at rapids rate ignoring spindle, coolant, and dwell commands. For rapid physical proofing of your code. The check g-code switch ignores all motion and provides the user a way to check if there are any errors in their program that Grbl may not like. - Program restart! (sort of). Program restart is typically an advanced feature that allows users to restart a program mid-stream. The check g-code switch can perform this feature by enabling the switch at the start of the program, and disabling it at the desired point with some minimal changes. - New system state variable. This state variable tracks all of the different state processes that Grbl performs, i.e. cycle start, feed hold, homing, etc. This is mainly for making managing of these task easier and more clear. - Position lost state variable. Only when homing is enabled, Grbl will refuse to move until homing is completed and position is known. This is mainly for safety. Otherwise, it will let users fend for themselves. - Moved the default settings defines into config.h. The plan is to eventually create a set of config.h's for particular as-built machines to help users from doing it themselves. - Moved around misc defines into .h files. And lots of other little things. --- config.h | 61 +++++++++-------- gcode.c | 22 ++++-- gcode.h | 11 +++ limits.c | 3 +- limits.h | 1 + main.c | 58 ++++++++-------- motion_control.c | 54 +++++++++------ motion_control.h | 2 +- nuts_bolts.c | 2 - nuts_bolts.h | 39 +++++------ planner.c | 9 +-- planner.h | 3 +- print.c | 3 +- protocol.c | 137 +++++++++++++++++++++++-------------- protocol.h | 5 ++ readme.textile | 23 ++++--- report.c | 167 +++++++++++++++++++++++++--------------------- report.h | 8 +++ serial.c | 13 ---- serial.h | 15 +++++ settings.c | 125 +++++++++++++++++----------------- settings.h | 11 +-- spindle_control.c | 7 +- stepper.c | 39 ++++------- stepper.h | 5 +- 25 files changed, 453 insertions(+), 370 deletions(-) diff --git a/config.h b/config.h index 6e96453..f27a4b7 100755 --- a/config.h +++ b/config.h @@ -62,13 +62,40 @@ #define COOLANT_FLOOD_PORT PORTC #define COOLANT_FLOOD_BIT 0 // Uno Analog Pin 0 -// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable. +// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable. #ifdef ENABLE_M7 #define COOLANT_MIST_DDR DDRC #define COOLANT_MIST_PORT PORTC #define COOLANT_MIST_BIT 1 // Uno Analog Pin 1 #endif +// Default settings (used when resetting eeprom-settings) +// TODO: Begin to fill these out for various as-built machines, i.e. config_sherline5400.h +#define MICROSTEPS 4 +#define DEFAULT_X_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_Y_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_Z_STEPS_PER_MM (94.488188976378*MICROSTEPS) +#define DEFAULT_STEP_PULSE_MICROSECONDS 10 +#define DEFAULT_MM_PER_ARC_SEGMENT 0.1 +#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min +#define DEFAULT_FEEDRATE 250.0 +#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< NOW CODED INTO SETTINGS #define BLOCK_DELETE_ENABLE 0 // Block delete enable/disable flag during g-code parsing - -// This parameter sets the delay time before disabling the steppers after the final block of movement. -// A short delay ensures the steppers come to a complete stop and the residual inertial force in the -// CNC axes don't cause the axes to drift off position. This is particularly important when manually -// 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 set to zero, no lock will occur. If set to max 255, the lock will never release, in other -// words, the steppers never disable for users that require this. -// -> NOW INSTALLED IN SETTINGS #define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer > 0 - -// Number of arc generation iterations by small angle approximation before exact arc trajectory -// correction. This parameter maybe decreased if there are issues with the accuracy of the arc -// generations. In general, the default value is more than enough for the intended CNC applications -// of grbl, and should be on the order or greater than the size of the buffer to help with the -// computational efficiency of generating arcs. -// -> NOW INSTALLED IN SETTINGS #define N_ARC_CORRECTION 25 // Integer (1-255) - -// Specifies the number of work coordinate systems grbl will support (G54 - G59). -// This parameter must be one or greater, currently supporting up to a value of 6. -// -> NOW CODED INTO SETTINGS.C #define N_COORDINATE_SYSTEM 6 - // TODO: Install compile-time option to send numeric status codes rather than strings. #endif diff --git a/gcode.c b/gcode.c index 2ba47c0..235371c 100755 --- a/gcode.c +++ b/gcode.c @@ -177,7 +177,7 @@ uint8_t gc_execute_line(char *line) switch(int_value) { case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause case 1: // Program pause with optional stop on, otherwise do nothing. - if (bit_istrue(sys.switches,BITFLAG_OPT_STOP)) { + if (bit_istrue(gc.switches,BITFLAG_OPT_STOP)) { gc.program_flow = PROGRAM_FLOW_PAUSED; } break; @@ -253,13 +253,20 @@ uint8_t gc_execute_line(char *line) NOTE: Independent non-motion/settings parameters are set out of this order for code efficiency and simplicity purposes, but this should not affect proper g-code execution. */ + // ([F]: Set feed rate. Already performed, but enforce rapids for dry runs.) + if (bit_istrue(gc.switches,BITFLAG_DRY_RUN)) { gc.feed_rate = settings.default_seek_rate; } + // ([M6]: Tool change should be executed here.) // [M3,M4,M5]: Update spindle state - spindle_run(gc.spindle_direction); //, gc.spindle_speed); + if (!(gc.switches & (BITFLAG_DRY_RUN | BITFLAG_CHECK_GCODE))) { + spindle_run(gc.spindle_direction); //, gc.spindle_speed); + } // [*M7,M8,M9]: Update coolant state - coolant_run(gc.coolant_mode); + if (!(gc.switches & (BITFLAG_DRY_RUN | BITFLAG_CHECK_GCODE))) { + coolant_run(gc.coolant_mode); + } // [G54,G55,...,G59]: Coordinate system selection if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_12)) ) { // Check if called in block @@ -275,8 +282,11 @@ uint8_t gc_execute_line(char *line) case NON_MODAL_DWELL: if (p < 0) { // Time cannot be negative. FAIL(STATUS_INVALID_STATEMENT); - } else { - mc_dwell(p); + } else { + // Ignore dwell in dry run and check gcode modes + if (!(gc.switches & (BITFLAG_DRY_RUN | BITFLAG_CHECK_GCODE))) { + mc_dwell(p); + } } break; case NON_MODAL_SET_COORDINATE_DATA: @@ -537,7 +547,7 @@ uint8_t gc_execute_line(char *line) // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may // refill and can only be resumed by the cycle start run-time command. - if (gc.program_flow || bit_istrue(sys.switches,BITFLAG_SINGLE_BLOCK)) { + if (gc.program_flow || bit_istrue(gc.switches,BITFLAG_SINGLE_BLOCK)) { plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete. sys.auto_start = false; // Disable auto cycle start. Forces pause until cycle start issued. diff --git a/gcode.h b/gcode.h index f993d43..c780c41 100755 --- a/gcode.h +++ b/gcode.h @@ -62,8 +62,19 @@ #define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92 #define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1 +// Define bit flag masks for gc.switches. (8 flag limit) +#define BITFLAG_CHECK_GCODE bit(0) +#define BITFLAG_DRY_RUN bit(1) +#define BITFLAG_BLOCK_DELETE bit(2) +#define BITFLAG_SINGLE_BLOCK bit(3) +#define BITFLAG_OPT_STOP bit(4) +// #define bit(5) +// #define bit(6) +// #define bit(7) + typedef struct { uint8_t status_code; // Parser status for current block + uint8_t switches; // Handles non-gcode switches modes. Set externally by protocol. Default off uint8_t motion_mode; // {G0, G1, G2, G3, G80} uint8_t inverse_feed_rate_mode; // {G93, G94} uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} diff --git a/limits.c b/limits.c index 6dea453..f34ffbd 100755 --- a/limits.c +++ b/limits.c @@ -33,7 +33,6 @@ #include "limits.h" #include "report.h" -#define MICROSECONDS_PER_ACCELERATION_TICK (1000000/ACCELERATION_TICKS_PER_SECOND) void limits_init() { @@ -63,7 +62,7 @@ ISR(LIMIT_INT_vect) // Kill all processes upon hard limit event. if ((LIMIT_PIN & LIMIT_MASK) ^ LIMIT_MASK) { mc_alarm(); // Initiate system kill. - report_status_message(STATUS_HARD_LIMIT); // Print ok in interrupt since system killed. + sys.state = STATE_LIMIT; // Set system state to indicate event. } } } diff --git a/limits.h b/limits.h index 0281ef4..59f9008 100755 --- a/limits.h +++ b/limits.h @@ -22,6 +22,7 @@ #define limits_h #define LIMIT_MASK ((1< +#include +#include +#include #include "settings.h" #include "config.h" #include "gcode.h" #include "motion_control.h" #include "spindle_control.h" #include "coolant_control.h" -#include -#include -#include #include "nuts_bolts.h" #include "stepper.h" #include "planner.h" @@ -61,18 +61,28 @@ void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rat do { protocol_execute_runtime(); // Check for any run-time commands 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 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(); } + } while ( plan_check_full_buffer() ); + + // If in check gcode mode, prevent motion by blocking planner. + if (bit_isfalse(gc.switches,BITFLAG_CHECK_GCODE)) { + plan_buffer_line(x, y, z, feed_rate, invert_feed_rate); + + // Indicate to the system there is now a planned block in the buffer ready to cycle start. + // NOTE: If homing cycles are enabled, a position lost state will lock out all motions, + // until a homing cycle has been completed. This is a safety feature to help prevent + // the machine from crashing. + if (!sys.state) { sys.state = STATE_QUEUED; } + + // 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(); } + } } @@ -195,14 +205,19 @@ void mc_dwell(float seconds) } -// Execute homing cycle to locate and set machine zero. +// 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 +// executing the homing cycle. This prevents incorrect buffered plans after homing. void mc_go_home() { - plan_synchronize(); // Empty all motions in buffer before homing. + sys.state = STATE_HOMING; // Set system state variable PCICR &= ~(1 << LIMIT_INT); // Disable hard limits pin change interrupt - plan_clear_position(); - + limits_go_home(); // Perform homing routine. + if (sys.abort) { + sys.state = STATE_LOST; // Homing routine did not complete. + return; + } // The machine should now be homed and machine zero has been located. Upon completion, // reset planner and system internal position vectors, but not gcode parser position yet. @@ -227,6 +242,7 @@ void mc_go_home() // If hard limits feature enabled, re-enable hard limits interrupt after homing cycle. if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { PCICR |= (1 << LIMIT_INT); } + sys.state = STATE_IDLE; // Finished! } diff --git a/motion_control.h b/motion_control.h index 4d71848..cd35f70 100755 --- a/motion_control.h +++ b/motion_control.h @@ -40,7 +40,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8 // Dwell for a specific number of seconds void mc_dwell(float seconds); -// Send the tool home (not implemented) +// Perform homing cycle to locate machine zero. Requires limit switches. void mc_go_home(); // Kills all motion and sets system alarm diff --git a/nuts_bolts.c b/nuts_bolts.c index 324b043..dbd1788 100755 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -20,8 +20,6 @@ */ #include "nuts_bolts.h" -#include -#include #include #define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float) diff --git a/nuts_bolts.h b/nuts_bolts.h index 2eb0525..b894441 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -66,36 +66,29 @@ // #define bit(6) // bitmask 01000000 // #define bit(7) // bitmask 10000000 -// Define bit flag masks for sys.switches. (8 flag limit) -#define BITFLAG_BLOCK_DELETE bit(0) -#define BITFLAG_SINGLE_BLOCK bit(1) -#define BITFLAG_OPT_STOP bit(2) -// #define bit(3) -// #define bit(4) -// #define bit(5) -// #define bit(6) -// #define bit(7) - -// Define Grbl system states for sys.state - -// Define position lost in states? - +// 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 +// critical events. +#define STATE_IDLE 0 // Must be zero. +#define STATE_QUEUED 1 // Indicates buffered blocks, awaiting cycle start. +#define STATE_CYCLE 2 // Cycle is running +#define STATE_HOLD 3 // Executing feed hold +#define STATE_HOMING 4 // Performing homing cycle +#define STATE_JOG 5 // Jogging mode is unique like homing. +#define STATE_ALARM 6 // In alarm state. Locks out all but reset +#define STATE_LOST 7 // Used to message position may be lost upon startup or event +#define STATE_LIMIT 8 // Used to message hard limit triggered. // Define global system variables 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. - uint8_t switches; // Switches state bitflag variable. For features not governed by g-code. - -// uint8_t state; // Tracks the current state of Grbl. -// TODO: This may replace the functionality of the feed_hold and cycle_start variables. Need to -// make sure that overall processes don't change. - - volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program. + uint8_t state; // Tracks the current state of Grbl. volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. int32_t position[3]; // Real-time machine (aka home) position vector in steps. // NOTE: This may need to be a volatile variable, if problems arise. + uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings. +// uint8_t feed_hold; // Feed hold flag. Held true during feed hold. Released when ready to resume. +// volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program. } system_t; extern system_t sys; diff --git a/planner.c b/planner.c index ffdf22e..fac89bf 100755 --- a/planner.c +++ b/planner.c @@ -22,10 +22,8 @@ /* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */ -#include -#include +#include #include - #include "planner.h" #include "nuts_bolts.h" #include "stepper.h" @@ -33,9 +31,6 @@ #include "config.h" #include "protocol.h" -// The number of linear motions that can be in the plan at any give time -#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 static volatile uint8_t block_buffer_tail; // Index of the block to process now @@ -335,7 +330,7 @@ uint8_t plan_check_full_buffer() // Block until all buffered steps are executed. void plan_synchronize() { - while (plan_get_current_block() || sys.cycle_start) { + while (plan_get_current_block()) { protocol_execute_runtime(); // Check and execute run-time commands if (sys.abort) { return; } // Check for system abort } diff --git a/planner.h b/planner.h index 9c77f90..a326084 100755 --- a/planner.h +++ b/planner.h @@ -22,7 +22,8 @@ #ifndef planner_h #define planner_h -#include +// The number of linear motions that can be in the plan at any give time +#define BLOCK_BUFFER_SIZE 18 // This struct is used when buffering the setup for each linear movement "nominal" values are as specified in // the source g-code and may never actually be reached if acceleration management is active. diff --git a/print.c b/print.c index 7aa710c..2f820d5 100755 --- a/print.c +++ b/print.c @@ -23,7 +23,6 @@ used to be a part of the Arduino project. */ -#include #include #include "config.h" #include "serial.h" @@ -109,6 +108,8 @@ void printInteger(long n) // Convert float to string by immediately converting to a long integer, which contains // more digits than a float. Number of decimal places, which are tracked by a counter, // may be set by the user. The integer is then efficiently converted to a string. +// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up +// techniques are actually just slightly slower. Found this out the hard way. void printFloat(float n) { if (n < 0) { diff --git a/protocol.c b/protocol.c index 864351e..5c21d73 100755 --- a/protocol.c +++ b/protocol.c @@ -26,11 +26,8 @@ #include "print.h" #include "settings.h" #include "config.h" -#include #include "nuts_bolts.h" -#include #include "stepper.h" -#include "planner.h" #include "report.h" #include "motion_control.h" @@ -43,8 +40,24 @@ void protocol_init() { char_counter = 0; // Reset line input iscomment = false; + report_init_message(); // Welcome message } +// Executes user startup script, if stored. +void protocol_execute_startup() +{ + uint8_t n; + for (n=0; n < N_STARTUP_LINE; n++) { + if (!(settings_read_startup_line(n, line))) { + report_status_message(STATUS_SETTING_READ_FAIL); + } else { + if (line[0] != 0) { + printString(line); // Echo startup line to indicate execution. + report_status_message(gc_execute_line(line)); + } + } + } +} // Executes run-time commands, when required. This is called from various check points in the main // program, primarily where there may be a while loop waiting for a buffer to clear space or any @@ -65,7 +78,16 @@ void protocol_execute_runtime() // System alarm. Something has gone wrong. Disable everything by entering an infinite // loop until system reset/abort. if (rt_exec & EXEC_ALARM) { - if (bit_isfalse(rt_exec,EXEC_RESET)) { // Ignore loop if reset is already issued + // Report the cause of the alarm here in the main program. + if (sys.state == STATE_LIMIT) { report_status_message(STATUS_HARD_LIMIT); } + if (sys.state == STATE_CYCLE) { // Pick up abort during active cycle. + report_status_message(STATUS_ABORT_CYCLE); + sys.state = STATE_LOST; + } + // Ignore loop if reset is already issued. In other words, a normal system abort/reset + // will not enter this loop, only a critical event not controlled by the user will. + if (bit_isfalse(rt_exec,EXEC_RESET)) { + sys.state = STATE_ALARM; report_feedback_message(MESSAGE_SYSTEM_ALARM); while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } } @@ -74,15 +96,7 @@ void protocol_execute_runtime() // System abort. Steppers have already been force stopped. if (rt_exec & EXEC_RESET) { - sys.abort = true; - - // If the cycle is active before killing the motion, the event will likely caused a loss - // of position since there is no controlled deceleration(feed hold) to a stop. - // TODO: Add force home option upon position lost. Need to verify that cycle start isn't - // set false by anything but the stepper module. Also, need to look at a better place for - // this. Main.c? - // if (sys.cycle_start) { protocol_feedback_message(MESSAGE_POSITION_LOST); } - + sys.abort = true; return; // Nothing else to do but exit. } @@ -98,7 +112,7 @@ void protocol_execute_runtime() bit_false(sys.execute,EXEC_FEED_HOLD); } - // Reinitializes the stepper module running flags and re-plans the buffer after a feed hold. + // Reinitializes the stepper module running state and, if a feed hold, re-plans the buffer. // 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(); @@ -133,6 +147,7 @@ uint8_t protocol_execute_line(char *line) if(line[0] == '$') { uint8_t char_counter = 1; + uint8_t helper_var = 0; // Helper variable float parameter, value; switch( line[char_counter] ) { case 0 : report_grbl_help(); break; @@ -149,8 +164,11 @@ uint8_t protocol_execute_line(char *line) else { report_gcode_modes(); } break; case 'H' : // Perform homing cycle - if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { mc_go_home(); } - else { return(STATUS_SETTING_DISABLED); } + if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { + // Only perform homing if Grbl is idle or lost. + if ( sys.state==STATE_IDLE || sys.state==STATE_LOST ) { mc_go_home(); } + else { return(STATUS_HOMING_ERROR); } + } else { return(STATUS_SETTING_DISABLED); } break; // case 'J' : break; // Jogging methods // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be @@ -164,41 +182,62 @@ uint8_t protocol_execute_line(char *line) // handled by the planner. It would be possible for the jog subprogram to insert blocks into the // block buffer without having the planner plan them. It would need to manage de/ac-celerations // on its own carefully. This approach could be effective and possibly size/memory efficient. -// case 'N' : // Start up blocks -// if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } -// if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } -// // Extract startup block, execute, and store. -// for (char_counter = 0; char_counter < LINE_BUFFER_SIZE-3; char_counter++) { -// line[char_counter] = line[char_counter+3]; -// } -// uint8_t status = gc_execute_line(line); -// if (status) { return(status); } -// else { settings_store_startup_block(line); } -// break; - case 'B' : // Toggle block delete - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - sys.switches ^= BITFLAG_BLOCK_DELETE; - if (bit_istrue(sys.switches,BITFLAG_BLOCK_DELETE)) { report_feedback_message(MESSAGE_SWITCH_ON); } - else { report_feedback_message(MESSAGE_SWITCH_OFF); } + case 'S' : // Switch modes + // Set helper_var as switch bitmask or clearing flag + switch (line[++char_counter]) { + case 0 : helper_var = 0; break; + case '0' : helper_var = BITFLAG_CHECK_GCODE; break; + case '1' : helper_var = BITFLAG_DRY_RUN; break; + case '2' : helper_var = BITFLAG_BLOCK_DELETE; break; + case '3' : helper_var = BITFLAG_SINGLE_BLOCK; break; + case '4' : helper_var = BITFLAG_OPT_STOP; break; + default : return(STATUS_INVALID_STATEMENT); + } + if (helper_var) { + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + gc.switches ^= helper_var; + if (bit_istrue(gc.switches,helper_var)) { report_feedback_message(MESSAGE_SWITCH_ON); } + else { report_feedback_message(MESSAGE_SWITCH_OFF); } + } else { + gc.switches = helper_var; // Clear all switches + report_feedback_message(MESSAGE_SWITCHES_CLEARED); + } break; - case 'S' : // Toggle single block mode - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - sys.switches ^= BITFLAG_SINGLE_BLOCK; - if (bit_istrue(sys.switches,BITFLAG_SINGLE_BLOCK)) { report_feedback_message(MESSAGE_SWITCH_ON); } - else { report_feedback_message(MESSAGE_SWITCH_OFF); } - break; - case 'O' : // Toggle optional stop - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - sys.switches ^= BITFLAG_OPT_STOP; - if (bit_istrue(sys.switches,BITFLAG_OPT_STOP)) { report_feedback_message(MESSAGE_SWITCH_ON); } - else { report_feedback_message(MESSAGE_SWITCH_OFF); } - break; - default : // Store global setting + case 'N' : // Startup lines. + if ( line[++char_counter] == 0 ) { // Print startup lines + for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { + if (!(settings_read_startup_line(helper_var, line))) { + report_status_message(STATUS_SETTING_READ_FAIL); + } else { + report_startup_line(helper_var,line); + } + } + break; + } else { // Store startup line + helper_var = true; // Set helper_var to flag storing method. + // No break. Continues into default: to read remaining command characters. + } + default : // Storing setting methods if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } - if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } - if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } - return(settings_store_global_setting(parameter, value)); + if (helper_var) { // Store startup line + // Prepare sending gcode block to gcode parser by shifting all characters + helper_var = char_counter; // Set helper variable as counter to start of gcode block + do { + line[char_counter-helper_var] = line[char_counter]; + } while (line[char_counter++] != 0); + // Execute gcode block to ensure block is valid. + helper_var = gc_execute_line(line); // Set helper_var to returned status code. + if (helper_var) { return(helper_var); } + else { + helper_var = trunc(parameter); // Set helper_var to int value of parameter + settings_store_startup_line(helper_var,line); + } + } else { // Store global setting. + if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } + if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } + return(settings_store_global_setting(parameter, value)); + } } return(STATUS_OK); // If '$' command makes it to here, then everything's ok. @@ -249,7 +288,7 @@ void protocol_process() // Throw away whitepace and control characters } else if (c == '/') { // Disable block delete and throw away characters. Will ignore until EOL. - if (bit_istrue(sys.switches,BITFLAG_BLOCK_DELETE)) { + if (bit_istrue(gc.switches,BITFLAG_BLOCK_DELETE)) { iscomment = true; } } else if (c == '(') { diff --git a/protocol.h b/protocol.h index f176fd5..83c4c0d 100755 --- a/protocol.h +++ b/protocol.h @@ -21,6 +21,8 @@ #ifndef protocol_h #define protocol_h +#include + // Line buffer size from the serial input stream to be executed. // NOTE: Not a problem except for extreme cases, but the line buffer size can be too small // and g-code blocks can get truncated. Officially, the g-code standards support up to 256 @@ -42,4 +44,7 @@ uint8_t protocol_execute_line(char *line); // Checks and executes a runtime command at various stop points in main program void protocol_execute_runtime(); +// Execute the startup script lines stored in EEPROM upon initialization +void protocol_execute_startup(); + #endif diff --git a/readme.textile b/readme.textile index 3c5fde6..2acf3e8 100755 --- a/readme.textile +++ b/readme.textile @@ -9,25 +9,26 @@ It accepts standards-compliant G-code and has been tested with the output of sev Grbl includes full acceleration management with look ahead. That means the controller will look up to 18 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering. *Changelog for v0.8 from v0.7:* - - *BETA status: _Under development. Code state may significantly change with each push as new features are integrated._* - - Major structural overhaul to allow for multi-tasking events and new feature sets - - New run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status reporting + - *BETA status: _Under development but nearing completion. Code state may change with each push as new features are integrated or when bugs are squashed._* + - Major structural overhaul to allow for multi-tasking events and new feature sets. + - Run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status reporting - Controlled feed hold with deceleration to ensure no skipped steps and loss of location. - After feed hold, cycle accelerations are re-planned and may be resumed. - - Advanced homing cycle with direction and speed configuration options. (Requires limit switches.) - - Limit pins are held normal high with an internal pull-up resister. Wiring only requires a normally-open switch connected to ground. (For both ends of an axis, simply wire two in parallel into the same pin.) + - Advanced homing cycle with direction and speed configuration options. (Requires limit switches.) When enabled, homing is required before use to ensure safety. + - Limit pins are held normal high with internal pull-up resistors. Wiring only requires a normally-open switch connected to ground. (For both ends of an axis, simply wire two in parallel into the same pin.) - Hard limits option and plays nice with homing cycle, so switches can be used for both homing and hard limits. + - New switch commands: Dry-run, block delete, single block mode, optional stop. A check g-code switch has also been added to allow users to error check their programs. This feature can also be used as a "program restart" by enabling the check g-code switch, begin streaming, and disable the check g-code switch at the desired point with minimal additional g-code. - Re-factored g-code parser with robust error-checking. - - 6 work coordinate systems (G54-G59), offsets(G92), and machine coordinate system support. Work systems are stored in EEPROM and persistent. + - 6 work coordinate systems (G54-G59), offsets(G92), and machine coordinate system support. Work coordinate systems are stored in EEPROM and persistent. - G10 L2 and L20 work coordinate settings support. L2 sets one or more axes values. L20 sets the current machine position to the specified work origin. - - Program stop(M0,M1*,M2,M30) initial support. Optional stop to do. + - Program stop(M0,M1,M2,M30) initial support. - Coolant control(M7*,M8,M9) support. (M7 is a compile-time option). - System reset re-initializes grbl without resetting the Arduino and retains machine/home position and work coordinates. - - Restructured planner and stepper modules to become independent and ready for future features. - - Settings re-factoring to allow configuration without compiling of most features. (Still in progress). + - Settings overhauled and dozens of new settings and internal commands are now available, when most were compile-time only. + - New startup line setting. Allows users to store a custom g-code block into Grbl's startup routine. Executes immediately upon startup or reset. May be used to set g-code defaults like G20. - Misc bug fixes and removed deprecated acceleration enabled code. - - Planned features: Axis acceleration and max speed individual settings, full-featured status reporting, runtime settings such as toggling block delete. - - Advanced compile-time options: Up to 6 work coordinate systems(G54-G59), XON/XOFF flow control (limited support), direction and step pulse time delay. + - Planned features: Axis acceleration and max speed individual settings, full-featured status reporting. + - Advanced compile-time options: XON/XOFF flow control (limited support), direction and step pulse time delay, and up to 5 startup lines. *Important note for Atmega 168 users:* Going forward, support for Atmega 168 will be dropped due to its limited memory and speed. However, legacy Grbl v0.51 "in the branch called 'v0_51' is still available for use. diff --git a/report.c b/report.c index 2383695..a036820 100644 --- a/report.c +++ b/report.c @@ -26,14 +26,11 @@ methods to accomodate their needs. */ -#include -#include "print.h" -#include "settings.h" -#include -#include "nuts_bolts.h" #include #include "report.h" -#include "protocol.h" +#include "print.h" +#include "settings.h" +#include "nuts_bolts.h" #include "gcode.h" #include "coolant_control.h" @@ -75,6 +72,10 @@ void report_status_message(uint8_t status_code) printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break; case STATUS_SETTING_READ_FAIL: printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; + case STATUS_HOMING_ERROR: + printPgmString(PSTR("Grbl must be idle to home")); break; + case STATUS_ABORT_CYCLE: + printPgmString(PSTR("Abort during cycle")); break; } printPgmString(PSTR("\r\n")); } @@ -84,78 +85,91 @@ void report_status_message(uint8_t status_code) // Prints feedback messages. This serves as a centralized method to provide additional // user feedback for things that are not of the status message response protocol. These // are messages such as setup warnings and how to exit alarms. -// NOTE: For interfaces, messages are always placed within chevrons. And if silent mode +// NOTE: For interfaces, messages are always placed within brackets. And if silent mode // is installed, the message number codes are less than zero. // TODO: Install silence feedback messages option in settings void report_feedback_message(int8_t message_code) { - printPgmString(PSTR("<")); + printPgmString(PSTR("[")); switch(message_code) { case MESSAGE_SYSTEM_ALARM: printPgmString(PSTR("ALARM: Check and reset Grbl")); break; case MESSAGE_POSITION_LOST: - printPgmString(PSTR("warning: Position may be lost")); break; + printPgmString(PSTR("Position unknown. '$H' to home")); break; case MESSAGE_HOMING_ENABLE: - printPgmString(PSTR("'$H' to home. Ensure all limit switches are installed")); break; + printPgmString(PSTR("WARNING: All limit switches must be installed before homing")); break; case MESSAGE_SWITCH_ON: printPgmString(PSTR("Switch enabled")); break; case MESSAGE_SWITCH_OFF: printPgmString(PSTR("Switch disabled")); break; + case MESSAGE_SWITCHES_CLEARED: + printPgmString(PSTR("Switches cleared")); break; } - printPgmString(PSTR(">\r\n")); + printPgmString(PSTR("]\r\n")); } + // Welcome message void report_init_message() { printPgmString(PSTR("\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n")); } - +// Grbl help message void report_grbl_help() { -// char st_line[LINE_BUFFER_SIZE]; -// printPgmString(PSTR("\r\n\r\n Startup\r\n$100 = ")); printString(settings_read_startup(st_line,0)); -// printPgmString(PSTR("\r\n$101 = ")); printString(settings_read_startup(st_line,1)); - -// char buf[4]; -// settings_startup_string((char *)buf); -// printPgmString(PSTR("\r\n Startup: ")); printString(buf); - - printPgmString(PSTR("$ (help)\r\n$$ (print Grbl settings)\r\n$# (print gcode parameters)\r\n$G (print gcode parser state)")); - printPgmString(PSTR("\r\n$x=value (store Grbl setting)\r\n$Nx=line (store startup block)\r\n$H (run homing cycle, if enabled)")); - printPgmString(PSTR("\r\n$B (toggle block delete)\r\n$S (toggle single block mode)\r\n$O (toggle opt stop)")); - printPgmString(PSTR("\r\n~ (cycle start)\r\n! (feed hold)\r\n? (current position)\r\n^x (reset Grbl)\r\n")); + printPgmString(PSTR("$ (help)\r\n" + "$$ (print Grbl settings)\r\n" + "$# (print gcode parameters)\r\n" + "$G (print gcode parser state)\r\n" + "$N (print startup blocks)\r\n" + "$x=value (store Grbl setting)\r\n" + "$Nx=line (store startup block)\r\n" + "$H (perform homing cycle)\r\n" + "$S (clear all switches)\r\n" + "$S0 (toggle check gcode)\r\n" + "$S1 (toggle dry run)\r\n" + "$S2 (toggle block delete)\r\n" + "$S3 (toggle single block)\r\n" + "$S4 (toggle optional stop)\r\n" + "~ (cycle start)\r\n" + "! (feed hold)\r\n" + "? (current position)\r\n" + "^x (reset Grbl)\r\n")); } +// Grbl global settings print out. +// NOTE: The numbering scheme here must correlate to storing in settings.c void report_grbl_settings() { - printPgmString(PSTR("$0 = ")); printFloat(settings.steps_per_mm[X_AXIS]); - printPgmString(PSTR(" (x axis, steps/mm)\r\n$1 = ")); printFloat(settings.steps_per_mm[Y_AXIS]); - printPgmString(PSTR(" (y axis, steps/mm)\r\n$2 = ")); printFloat(settings.steps_per_mm[Z_AXIS]); - printPgmString(PSTR(" (z axis, steps/mm)\r\n$3 = ")); printInteger(settings.pulse_microseconds); - printPgmString(PSTR(" (step pulse, usec)\r\n$4 = ")); printFloat(settings.default_feed_rate); - printPgmString(PSTR(" (default feed rate, mm/min)\r\n$5 = ")); printFloat(settings.default_seek_rate); - printPgmString(PSTR(" (default seek rate, mm/min)\r\n$6 = ")); printFloat(settings.mm_per_arc_segment); - printPgmString(PSTR(" (arc resolution, mm/segment)\r\n$7 = ")); printInteger(settings.invert_mask); - printPgmString(PSTR(" (step port invert mask, int:binary = ")); print_uint8_base2(settings.invert_mask); - printPgmString(PSTR(")\r\n$8 = ")); printFloat(settings.acceleration/(60*60)); // Convert from mm/min^2 for human readability - printPgmString(PSTR(" (acceleration, mm/sec^2)\r\n$9 = ")); printFloat(settings.junction_deviation); - printPgmString(PSTR(" (cornering junction deviation, mm)\r\n$10 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); - printPgmString(PSTR(" (status report inches, bool)\r\n$11 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START)); - printPgmString(PSTR(" (auto start enable, bool)\r\n$12 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); - printPgmString(PSTR(" (invert stepper enable, bool)\r\n$13 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); - printPgmString(PSTR(" (hard limit enable, bool)\r\n$14 = ")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); - printPgmString(PSTR(" (homing enable, bool)\r\n$15 = ")); printInteger(settings.homing_dir_mask); - printPgmString(PSTR(" (homing direction mask, int:binary = ")); print_uint8_base2(settings.homing_dir_mask); - printPgmString(PSTR(")\r\n$16 = ")); printFloat(settings.homing_feed_rate); - printPgmString(PSTR(" (homing feed rate, mm/min)\r\n$17 = ")); printFloat(settings.homing_seek_rate); - printPgmString(PSTR(" (homing seek rate, mm/min)\r\n$18 = ")); printInteger(settings.homing_debounce_delay); - printPgmString(PSTR(" (homing debounce delay, msec)\r\n$19 = ")); printFloat(settings.homing_pulloff); - printPgmString(PSTR(" (homing pull-off travel, mm)\r\n$20 = ")); printInteger(settings.stepper_idle_lock_time); - printPgmString(PSTR(" (stepper idle lock time, msec)\r\n$21 = ")); printInteger(settings.decimal_places); - printPgmString(PSTR(" (decimal places, int)\r\n$22 = ")); printInteger(settings.n_arc_correction); + printPgmString(PSTR("$0=")); printFloat(settings.steps_per_mm[X_AXIS]); + printPgmString(PSTR(" (x axis, steps/mm)\r\n$1=")); printFloat(settings.steps_per_mm[Y_AXIS]); + printPgmString(PSTR(" (y axis, steps/mm)\r\n$2=")); printFloat(settings.steps_per_mm[Z_AXIS]); + printPgmString(PSTR(" (z axis, steps/mm)\r\n$3=")); printInteger(settings.pulse_microseconds); + printPgmString(PSTR(" (step pulse, usec)\r\n$4=")); printFloat(settings.default_feed_rate); + printPgmString(PSTR(" (default feed rate, mm/min)\r\n$5=")); printFloat(settings.default_seek_rate); + printPgmString(PSTR(" (default seek rate, mm/min)\r\n$6=")); printFloat(settings.mm_per_arc_segment); + printPgmString(PSTR(" (arc resolution, mm/segment)\r\n$7=")); printInteger(settings.invert_mask); + printPgmString(PSTR(" (step port invert mask, int:binary=")); print_uint8_base2(settings.invert_mask); + printPgmString(PSTR(")\r\n$8=")); printFloat(settings.acceleration/(60*60)); // Convert from mm/min^2 for human readability + printPgmString(PSTR(" (acceleration, mm/sec^2)\r\n$9=")); printFloat(settings.junction_deviation); + printPgmString(PSTR(" (cornering junction deviation, mm)\r\n$10=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); + printPgmString(PSTR(" (report inches, bool)\r\n$11=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START)); + printPgmString(PSTR(" (auto start enable, bool)\r\n$12=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); + printPgmString(PSTR(" (invert stepper enable, bool)\r\n$13=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); + printPgmString(PSTR(" (hard limit enable, bool)\r\n$14=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); + printPgmString(PSTR(" (homing enable, bool)\r\n$15=")); printInteger(settings.homing_dir_mask); + printPgmString(PSTR(" (homing dir invert mask, int:binary=")); print_uint8_base2(settings.homing_dir_mask); + printPgmString(PSTR(")\r\n$16=")); printFloat(settings.homing_feed_rate); + printPgmString(PSTR(" (homing feed rate, mm/min)\r\n$17=")); printFloat(settings.homing_seek_rate); + printPgmString(PSTR(" (homing seek rate, mm/min)\r\n$18=")); printInteger(settings.homing_debounce_delay); + printPgmString(PSTR(" (homing debounce delay, msec)\r\n$19=")); printFloat(settings.homing_pulloff); + printPgmString(PSTR(" (homing pull-off travel, mm)\r\n$20=")); printInteger(settings.stepper_idle_lock_time); + printPgmString(PSTR(" (stepper idle lock time, msec)\r\n$21=")); printInteger(settings.decimal_places); + printPgmString(PSTR(" (decimal places, int)\r\n$22=")); printInteger(settings.n_arc_correction); printPgmString(PSTR(" (n arc correction, int)\r\n")); } + +// Prints gcode coordinate offset parameters void report_gcode_parameters() { float coord_data[N_AXIS]; @@ -174,7 +188,7 @@ void report_gcode_parameters() case 5: printPgmString(PSTR("G59")); break; case 6: printPgmString(PSTR("G28")); break; case 7: printPgmString(PSTR("G30")); break; - // case 8: printPgmString(PSTR("G92")); break; // G92.2, G92.3 currently not supported. + // case 8: printPgmString(PSTR("G92")); break; // G92.2, G92.3 not supported. Hence not stored. } printPgmString(PSTR(":[")); for (i=0; i -#include #include "serial.h" #include "config.h" #include "motion_control.h" -#include "nuts_bolts.h" #include "protocol.h" -#define RX_BUFFER_SIZE 128 -#define TX_BUFFER_SIZE 64 - uint8_t rx_buffer[RX_BUFFER_SIZE]; uint8_t rx_buffer_head = 0; uint8_t rx_buffer_tail = 0; @@ -42,14 +37,6 @@ uint8_t tx_buffer_head = 0; volatile uint8_t tx_buffer_tail = 0; #ifdef ENABLE_XONXOFF - #define RX_BUFFER_FULL 96 // XOFF high watermark - #define RX_BUFFER_LOW 64 // XON low watermark - #define SEND_XOFF 1 - #define SEND_XON 2 - #define XOFF_SENT 3 - #define XON_SENT 4 - #define XOFF_CHAR 0x13 - #define XON_CHAR 0x11 volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable // Returns the number of bytes in the RX buffer. This replaces a typical byte counter to prevent diff --git a/serial.h b/serial.h index 3e1cc77..3ea56c0 100755 --- a/serial.h +++ b/serial.h @@ -25,8 +25,23 @@ #ifndef serial_h #define serial_h +#include "nuts_bolts.h" + +#define RX_BUFFER_SIZE 128 +#define TX_BUFFER_SIZE 64 #define SERIAL_NO_DATA 0xff +#ifdef ENABLE_XONXOFF + #define RX_BUFFER_FULL 96 // XOFF high watermark + #define RX_BUFFER_LOW 64 // XON low watermark + #define SEND_XOFF 1 + #define SEND_XON 2 + #define XOFF_SENT 3 + #define XON_SENT 4 + #define XOFF_CHAR 0x13 + #define XON_CHAR 0x11 +#endif + void serial_init(long baud); void serial_write(uint8_t data); diff --git a/settings.c b/settings.c index a2e02d1..1deb732 100644 --- a/settings.c +++ b/settings.c @@ -20,15 +20,12 @@ */ #include -#include +#include "protocol.h" +#include "report.h" +#include "stepper.h" #include "nuts_bolts.h" #include "settings.h" #include "eeprom.h" -#include "print.h" -#include -#include "protocol.h" -#include "config.h" -#include "report.h" settings_t settings; @@ -45,49 +42,29 @@ typedef struct { float junction_deviation; } settings_v4_t; -// Default settings (used when resetting eeprom-settings) -#define MICROSTEPS 8 -#define DEFAULT_X_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_Y_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_Z_STEPS_PER_MM (94.488188976378*MICROSTEPS) -#define DEFAULT_STEP_PULSE_MICROSECONDS 30 -#define DEFAULT_MM_PER_ARC_SEGMENT 0.1 -#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min -#define DEFAULT_FEEDRATE 500.0 -#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 #include "nuts_bolts.h" #define GRBL_VERSION "0.8c" @@ -45,7 +44,7 @@ // developments. #define EEPROM_ADDR_GLOBAL 1 #define EEPROM_ADDR_PARAMETERS 512 -#define EEPROM_ADDR_STARTUP_SCRIPT 768 +#define EEPROM_ADDR_STARTUP_BLOCK 768 // Define EEPROM address indexing for coordinate parameters #define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1) @@ -85,12 +84,16 @@ void settings_init(); // A helper method to set new settings from command line uint8_t settings_store_global_setting(int parameter, float value); +// Stores the protocol line variable as a startup line in EEPROM +void settings_store_startup_line(uint8_t n, char *line); + +// Reads an EEPROM startup line to the protocol line variable +uint8_t settings_read_startup_line(uint8_t n, char *line); + // Writes selected coordinate data to EEPROM void settings_write_coord_data(uint8_t coord_select, float *coord_data); // Reads selected coordinate data from EEPROM uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data); -// int8_t settings_execute_startup(); - #endif diff --git a/spindle_control.c b/spindle_control.c index 1d65f41..5bfe621 100755 --- a/spindle_control.c +++ b/spindle_control.c @@ -19,14 +19,9 @@ along with Grbl. If not, see . */ -#include "spindle_control.h" #include "settings.h" -#include "motion_control.h" -#include "config.h" +#include "spindle_control.h" #include "planner.h" -#include "stepper.h" - -#include static uint8_t current_direction; diff --git a/stepper.c b/stepper.c index 97e6e4b..56350cc 100755 --- a/stepper.c +++ b/stepper.c @@ -22,19 +22,12 @@ /* The timer calculations of this module informed by the 'RepRap cartesian firmware' by Zack Smith and Philipp Tiefenbacher. */ +#include #include "stepper.h" #include "config.h" #include "settings.h" -#include -#include -#include -#include "nuts_bolts.h" -#include #include "planner.h" -// Some useful constants -#define TICKS_PER_MICROSECOND (F_CPU/1000000) -#define CYCLES_PER_ACCELERATION_TICK ((TICKS_PER_MICROSECOND*1000000)/ACCELERATION_TICKS_PER_SECOND) // Stepper state variable. Contains running data and trapezoid variables. typedef struct { @@ -94,7 +87,7 @@ void st_wake_up() } else { STEPPERS_DISABLE_PORT &= ~(1<initial_rate; set_step_events_per_minute(st.trapezoid_adjusted_rate); // Initialize cycles_per_step_event @@ -190,7 +183,6 @@ ISR(TIMER1_COMPA_vect) st.step_events_completed = 0; } else { st_go_idle(); - sys.cycle_start = false; bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end } } @@ -224,7 +216,7 @@ ISR(TIMER1_COMPA_vect) // While in block steps, check for de/ac-celeration events and execute them accordingly. if (st.step_events_completed < current_block->step_event_count) { - if (sys.feed_hold) { + if (sys.state == STATE_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 @@ -240,7 +232,6 @@ ISR(TIMER1_COMPA_vect) // 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.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; @@ -430,24 +421,18 @@ static void set_step_events_per_minute(uint32_t steps_per_minute) // variable will manage all of Grbl's processes and keep them separate. void st_cycle_start() { - if (!sys.cycle_start) { - if (!sys.feed_hold) { - if (bit_isfalse(sys.execute,EXEC_ALARM)) { - sys.cycle_start = true; // Only place this variable is set true. - st_wake_up(); - } - } + if (sys.state == STATE_QUEUED) { + sys.state = STATE_CYCLE; + st_wake_up(); } } // Execute a feed hold with deceleration, only during cycle. Called by main program. void st_feed_hold() { - if (!sys.feed_hold) { - if (sys.cycle_start) { - sys.auto_start = false; // Disable planner auto start upon feed hold. - sys.feed_hold = true; - } + if (sys.state == STATE_CYCLE) { + sys.state = STATE_HOLD; + sys.auto_start = false; // Disable planner auto start upon feed hold. } } @@ -466,6 +451,8 @@ void st_cycle_reinitialize() 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.state = STATE_QUEUED; + } else { + sys.state = STATE_IDLE; } - sys.feed_hold = false; // Release feed hold. Cycle is ready to re-start. } diff --git a/stepper.h b/stepper.h index 1c7977a..a72546f 100755 --- a/stepper.h +++ b/stepper.h @@ -23,7 +23,6 @@ #define stepper_h #include -#include // Some useful constants #define STEP_MASK ((1< Date: Sun, 4 Nov 2012 08:44:54 -0700 Subject: [PATCH 39/55] Tweaks and minor bug fixes. Added purge buffer command. - Added a purge buffer (and lock) command. This is an advanced option to clear any queued blocks in the buffer in the event of system position being lost or homed. These queued blocks will likely not move correctly if not purged. In typical use, the purging command releases the homing axes lock in case a user need to move the axes off their hard limit switches, but position is not guaranteed. Homing is advised immediately after. - Created a system-wide sync current position function. Cleans up some of the repetitive tasks in various places in the code that do the same thing. - Removed the clear all switches command '$S'. Not really needed and helped clean up a sync call. - Other minor tweaks. Readme updated slightly.. --- gcode.c | 6 ------ gcode.h | 3 --- main.c | 16 +++++++++------- motion_control.c | 12 ++++++------ nuts_bolts.c | 11 ++++++++++- nuts_bolts.h | 3 +++ planner.c | 6 ------ planner.h | 3 --- protocol.c | 28 ++++++++++++++++++---------- readme.textile | 4 ++-- report.c | 16 +++++++++------- report.h | 3 ++- 12 files changed, 59 insertions(+), 52 deletions(-) diff --git a/gcode.c b/gcode.c index 235371c..8e09c53 100755 --- a/gcode.c +++ b/gcode.c @@ -72,12 +72,6 @@ void gc_set_current_position(int32_t x, int32_t y, int32_t z) gc.position[Z_AXIS] = z/settings.steps_per_mm[Z_AXIS]; } -// Clears and zeros g-code parser position. Called by homing routine. -void gc_clear_position() -{ - clear_vector(gc.position); -} - static float to_millimeters(float value) { return(gc.inches_mode ? (value * MM_PER_INCH) : value); diff --git a/gcode.h b/gcode.h index c780c41..6369dfe 100755 --- a/gcode.h +++ b/gcode.h @@ -106,7 +106,4 @@ uint8_t gc_execute_line(char *line); // Set g-code parser position. Input in steps. void gc_set_current_position(int32_t x, int32_t y, int32_t z); -// Clear g-code parser position -void gc_clear_position(); - #endif diff --git a/main.c b/main.c index 32bda81..43abb64 100755 --- a/main.c +++ b/main.c @@ -76,12 +76,14 @@ int main(void) limits_init(); st_reset(); // Clear stepper subsystem variables. - // Set cleared gcode and planner positions to current system position, which is only - // cleared upon startup, not a reset/abort. If Grbl does not know or ensure its position, - // a feedback message will be sent back to the user to let them know. - gc_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); - plan_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); - + // Sync cleared gcode and planner positions to current system position, which is only + // cleared upon startup, not a reset/abort. If Grbl does not know or can ensure its + // position, a feedback message will be sent back to the user to let them know. Also, + // if position is lost and homing is enabled, the axes motions will be locked, and + // user must either perform the homing cycle '$H' or purge the system locks '$P' to + // resume. + sys_sync_current_position(); + // Reset system variables sys.abort = false; sys.execute = 0; @@ -91,7 +93,7 @@ int main(void) sys.state = STATE_IDLE; } if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } - + // Execute user startup script protocol_execute_startup(); } diff --git a/motion_control.c b/motion_control.c index 7f006ab..a7d4295 100755 --- a/motion_control.c +++ b/motion_control.c @@ -220,10 +220,10 @@ void mc_go_home() } // The machine should now be homed and machine zero has been located. Upon completion, - // reset planner and system internal position vectors, but not gcode parser position yet. - plan_clear_position(); - clear_vector_float(sys.position); - + // reset system position and sync internal position vectors. + clear_vector_float(sys.position); // Set machine zero + sys_sync_current_position(); + // Pull-off all axes from limit switches before continuing motion. This provides some initial // clearance off the switches and should also help prevent them from falsely tripping when // hard limits are enabled. @@ -237,8 +237,8 @@ void mc_go_home() st_cycle_start(); // Move it. Nothing should be in the buffer except this motion. plan_synchronize(); // Make sure the motion completes. - // Explicitly update the gcode parser position since it was circumvented by the pull-off maneuver. - gc_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); + // The gcode parser position was circumvented by the pull-off maneuver, so sync position vectors. + sys_sync_current_position(); // If hard limits feature enabled, re-enable hard limits interrupt after homing cycle. if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { PCICR |= (1 << LIMIT_INT); } diff --git a/nuts_bolts.c b/nuts_bolts.c index dbd1788..921ac9a 100755 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -19,8 +19,10 @@ along with Grbl. If not, see . */ -#include "nuts_bolts.h" #include +#include "nuts_bolts.h" +#include "gcode.h" +#include "planner.h" #define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float) extern float __floatunsisf (unsigned long); @@ -137,3 +139,10 @@ void delay_us(uint32_t us) } } } + + +void sys_sync_current_position() +{ + plan_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); + gc_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); +} diff --git a/nuts_bolts.h b/nuts_bolts.h index b894441..7438660 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -103,4 +103,7 @@ void delay_ms(uint16_t ms); // Delays variable-defined microseconds. Compiler compatibility fix for _delay_us(). void delay_us(uint32_t us); +// Syncs Grbl's gcode and planner position variables with the system position. +void sys_sync_current_position(); + #endif diff --git a/planner.c b/planner.c index fac89bf..92f2ac1 100755 --- a/planner.c +++ b/planner.c @@ -482,12 +482,6 @@ void plan_set_current_position(int32_t x, int32_t y, int32_t z) pl.position[Z_AXIS] = z; } -// Clear planner position vector. Called by homing routine. -void plan_clear_position() -{ - clear_vector(pl.position); -} - // Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail. // Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped. void plan_cycle_reinitialize(int32_t step_events_remaining) diff --git a/planner.h b/planner.h index a326084..37bd188 100755 --- a/planner.h +++ b/planner.h @@ -70,9 +70,6 @@ block_t *plan_get_current_block(); // Reset the planner position vector (in steps) void plan_set_current_position(int32_t x, int32_t y, int32_t z); -// Clear the planner position vector -void plan_clear_position(); - // Reinitialize plan with a partially completed block void plan_cycle_reinitialize(int32_t step_events_remaining); diff --git a/protocol.c b/protocol.c index 5c21d73..ee0926c 100755 --- a/protocol.c +++ b/protocol.c @@ -183,25 +183,33 @@ uint8_t protocol_execute_line(char *line) // block buffer without having the planner plan them. It would need to manage de/ac-celerations // on its own carefully. This approach could be effective and possibly size/memory efficient. case 'S' : // Switch modes + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } // Set helper_var as switch bitmask or clearing flag switch (line[++char_counter]) { - case 0 : helper_var = 0; break; - case '0' : helper_var = BITFLAG_CHECK_GCODE; break; + case '0' : + helper_var = BITFLAG_CHECK_GCODE; + // Sync position vectors if check mode is being disabled. May be different after checking. + if (bit_istrue(gc.switches,helper_var)) { sys_sync_current_position(); } + break; case '1' : helper_var = BITFLAG_DRY_RUN; break; case '2' : helper_var = BITFLAG_BLOCK_DELETE; break; case '3' : helper_var = BITFLAG_SINGLE_BLOCK; break; case '4' : helper_var = BITFLAG_OPT_STOP; break; default : return(STATUS_INVALID_STATEMENT); } - if (helper_var) { - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - gc.switches ^= helper_var; - if (bit_istrue(gc.switches,helper_var)) { report_feedback_message(MESSAGE_SWITCH_ON); } - else { report_feedback_message(MESSAGE_SWITCH_OFF); } - } else { - gc.switches = helper_var; // Clear all switches - report_feedback_message(MESSAGE_SWITCHES_CLEARED); + gc.switches ^= helper_var; + if (bit_istrue(gc.switches,helper_var)) { report_feedback_message(MESSAGE_SWITCH_ON); } + else { report_feedback_message(MESSAGE_SWITCH_OFF); } + break; + case 'P' : // Purge system + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + if (sys.state == STATE_CYCLE) { return(STATUS_PURGE_CYCLE); } // Also prevents position error + plan_reset_buffer(); + if (sys.state == STATE_LOST) { + report_feedback_message(MESSAGE_PURGE_AXES_LOCK); + sys_sync_current_position(); // Any motion commands during a lock can unsync position vectors. } + sys.state = STATE_IDLE; break; case 'N' : // Startup lines. if ( line[++char_counter] == 0 ) { // Print startup lines diff --git a/readme.textile b/readme.textile index 2acf3e8..e647f92 100755 --- a/readme.textile +++ b/readme.textile @@ -21,13 +21,13 @@ Grbl includes full acceleration management with look ahead. That means the contr - Re-factored g-code parser with robust error-checking. - 6 work coordinate systems (G54-G59), offsets(G92), and machine coordinate system support. Work coordinate systems are stored in EEPROM and persistent. - G10 L2 and L20 work coordinate settings support. L2 sets one or more axes values. L20 sets the current machine position to the specified work origin. - - Program stop(M0,M1,M2,M30) initial support. + - G28.1 and G30.1 set home position support. These set the internal EEPROM parameter values to the current machine position. (G28 and G30 no longer perform homing cycle, '$H' does. They move to these stored positions.) + - Program stop(M0,M1,M2,M30) support. - Coolant control(M7*,M8,M9) support. (M7 is a compile-time option). - System reset re-initializes grbl without resetting the Arduino and retains machine/home position and work coordinates. - Settings overhauled and dozens of new settings and internal commands are now available, when most were compile-time only. - New startup line setting. Allows users to store a custom g-code block into Grbl's startup routine. Executes immediately upon startup or reset. May be used to set g-code defaults like G20. - Misc bug fixes and removed deprecated acceleration enabled code. - - Planned features: Axis acceleration and max speed individual settings, full-featured status reporting. - Advanced compile-time options: XON/XOFF flow control (limited support), direction and step pulse time delay, and up to 5 startup lines. diff --git a/report.c b/report.c index a036820..42456bf 100644 --- a/report.c +++ b/report.c @@ -73,9 +73,11 @@ void report_status_message(uint8_t status_code) case STATUS_SETTING_READ_FAIL: printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; case STATUS_HOMING_ERROR: - printPgmString(PSTR("Grbl must be idle to home")); break; + printPgmString(PSTR("Must be idle to home")); break; case STATUS_ABORT_CYCLE: - printPgmString(PSTR("Abort during cycle")); break; + printPgmString(PSTR("Abort during cycle. Position may be lost")); break; + case STATUS_PURGE_CYCLE: + printPgmString(PSTR("Can't purge buffer during cycle")); break; } printPgmString(PSTR("\r\n")); } @@ -95,15 +97,15 @@ void report_feedback_message(int8_t message_code) case MESSAGE_SYSTEM_ALARM: printPgmString(PSTR("ALARM: Check and reset Grbl")); break; case MESSAGE_POSITION_LOST: - printPgmString(PSTR("Position unknown. '$H' to home")); break; + printPgmString(PSTR("'$H' to home and enable axes")); break; case MESSAGE_HOMING_ENABLE: printPgmString(PSTR("WARNING: All limit switches must be installed before homing")); break; case MESSAGE_SWITCH_ON: printPgmString(PSTR("Switch enabled")); break; case MESSAGE_SWITCH_OFF: printPgmString(PSTR("Switch disabled")); break; - case MESSAGE_SWITCHES_CLEARED: - printPgmString(PSTR("Switches cleared")); break; + case MESSAGE_PURGE_AXES_LOCK: + printPgmString(PSTR("WARNING: Motion lock disabled. Position unknown.")); break; } printPgmString(PSTR("]\r\n")); } @@ -124,13 +126,13 @@ void report_grbl_help() { "$N (print startup blocks)\r\n" "$x=value (store Grbl setting)\r\n" "$Nx=line (store startup block)\r\n" - "$H (perform homing cycle)\r\n" - "$S (clear all switches)\r\n" "$S0 (toggle check gcode)\r\n" "$S1 (toggle dry run)\r\n" "$S2 (toggle block delete)\r\n" "$S3 (toggle single block)\r\n" "$S4 (toggle optional stop)\r\n" + "$P (purge buffer and locks)\r\n" + "$H (perform homing cycle)\r\n" "~ (cycle start)\r\n" "! (feed hold)\r\n" "? (current position)\r\n" diff --git a/report.h b/report.h index f0b5604..810d199 100644 --- a/report.h +++ b/report.h @@ -36,6 +36,7 @@ #define STATUS_SETTING_READ_FAIL 11 #define STATUS_HOMING_ERROR 12 #define STATUS_ABORT_CYCLE 13 +#define STATUS_PURGE_CYCLE 14 // Define Grbl feedback message codes. Less than zero to distinguish message from error. #define MESSAGE_SYSTEM_ALARM -1 @@ -43,7 +44,7 @@ #define MESSAGE_HOMING_ENABLE -3 #define MESSAGE_SWITCH_ON -4 #define MESSAGE_SWITCH_OFF -5 -#define MESSAGE_SWITCHES_CLEARED -6 +#define MESSAGE_PURGE_AXES_LOCK -6 // Prints system status messages. void report_status_message(uint8_t status_code); From 9cabc915ef9096b470e236679db70834f6fc9e7a Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sun, 4 Nov 2012 10:48:57 -0700 Subject: [PATCH 40/55] Runtime command pinned out! Re-organized coolant pins. - Pinned out cycle start(A2), feed hold(A1), and reset(A0) runtime commands. These pins are held high with the internal pull-up resistor enabled. All you have to do is connect a normally-open switch to the pin and ground. That's it. - Moved the coolant control pins to A3 (and the optional mist control to A4). - Moved all of the MASK defines into the config.h file to centralize them. --- config.h | 23 +++++++++++++++++++++-- limits.h | 1 - protocol.c | 27 +++++++++++++++++++++++++++ report.c | 4 ++-- stepper.h | 6 ------ 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/config.h b/config.h index f27a4b7..ae48023 100755 --- a/config.h +++ b/config.h @@ -35,10 +35,14 @@ #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 STEP_MASK ((1< +#include #include "protocol.h" #include "gcode.h" #include "serial.h" @@ -41,6 +42,11 @@ void protocol_init() char_counter = 0; // Reset line input iscomment = false; report_init_message(); // Welcome message + + PINOUT_DDR &= ~(PINOUT_MASK); // Set as input pins + PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation. + PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt + PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt } // Executes user startup script, if stored. @@ -59,6 +65,27 @@ void protocol_execute_startup() } } +// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets +// only the runtime command execute variable to have the main program execute these when +// its ready. This works exactly like the character-based runtime commands when picked off +// directly from the incoming serial data stream. +ISR(PINOUT_INT_vect) +{ + // Enter only if any pinout pin is actively low. + if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) { + if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) { + mc_alarm(); + sys.execute |= EXEC_RESET; // Set as true + } + if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) { + sys.execute |= EXEC_FEED_HOLD; + } + if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) { + sys.execute |= EXEC_CYCLE_START; + } + } +} + // Executes run-time commands, when required. This is called from various check points in the main // 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. diff --git a/report.c b/report.c index 42456bf..d336677 100644 --- a/report.c +++ b/report.c @@ -65,7 +65,7 @@ void report_status_message(uint8_t status_code) case STATUS_HARD_LIMIT: printPgmString(PSTR("Limit triggered")); break; case STATUS_SETTING_DISABLED: - printPgmString(PSTR("Grbl setting disabled")); break; + printPgmString(PSTR("Setting disabled")); break; case STATUS_SETTING_VALUE_NEG: printPgmString(PSTR("Set value must be > 0.0")); break; case STATUS_SETTING_STEP_PULSE_MIN: @@ -75,7 +75,7 @@ void report_status_message(uint8_t status_code) case STATUS_HOMING_ERROR: printPgmString(PSTR("Must be idle to home")); break; case STATUS_ABORT_CYCLE: - printPgmString(PSTR("Abort during cycle. Position may be lost")); break; + printPgmString(PSTR("Abort during cycle. Position maybe lost")); break; case STATUS_PURGE_CYCLE: printPgmString(PSTR("Can't purge buffer during cycle")); break; } diff --git a/stepper.h b/stepper.h index a72546f..84fe8c0 100755 --- a/stepper.h +++ b/stepper.h @@ -25,15 +25,9 @@ #include // Some useful constants -#define STEP_MASK ((1< Date: Mon, 5 Nov 2012 13:32:29 -0700 Subject: [PATCH 41/55] Tweaked dry run and check g-code switches. Now resets when toggled off. - To make managing the code easier and to help ensure a user starts with a fresh reset, the functionality of check g-code and dry run has been changed to automatically perform a soft reset when toggled off. Position will not be lost, unless there is a cycle active. Feed hold before toggling off it needed. This is mainly a safety issue. If a user dry runs a program and kills it mid-program, and then restarts it thinking to run it as normal, the g-code modes that we're set may not be what they expect, and very bad things can happen. - NOTE: Grbl is at 83.5% of flash capacity. Not a lot of room left, but I think we can squeeze in some more! --- gcode.c | 32 ++++++++++++++++++++------------ gcode.h | 1 + protocol.c | 18 ++++++++++++++---- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/gcode.c b/gcode.c index 8e09c53..b56e241 100755 --- a/gcode.c +++ b/gcode.c @@ -52,6 +52,7 @@ void gc_init() { memset(&gc, 0, sizeof(gc)); gc.feed_rate = settings.default_feed_rate; // Should be zero at initialization. + gc.seek_rate = settings.default_seek_rate; select_plane(X_AXIS, Y_AXIS, Z_AXIS); gc.absolute_mode = true; @@ -247,20 +248,25 @@ uint8_t gc_execute_line(char *line) NOTE: Independent non-motion/settings parameters are set out of this order for code efficiency and simplicity purposes, but this should not affect proper g-code execution. */ - // ([F]: Set feed rate. Already performed, but enforce rapids for dry runs.) - if (bit_istrue(gc.switches,BITFLAG_DRY_RUN)) { gc.feed_rate = settings.default_seek_rate; } + // ([F]: Set feed and seek rates. Used to enforce user feed rate for dry runs.) + // TODO: Dry runs move at whatever feed rate the user specifies. Need to update this to allow + // this feature. Users can also change the rates realtime like a feedrate override. Until that + // is installed, it will have to wait, but users could control it by using the default feed rate. + // TODO: Seek rates can change depending on the direction and maximum speeds of each axes. When + // max axis speed is installed, the calculation can be performed here, or maybe in the planner. + if (bit_istrue(gc.switches,BITFLAG_DRY_RUN)) { + // NOTE: Since dry run resets after disabled, the defaults rates should come back. + gc.feed_rate = settings.default_feed_rate; + gc.seek_rate = settings.default_feed_rate; + } // ([M6]: Tool change should be executed here.) // [M3,M4,M5]: Update spindle state - if (!(gc.switches & (BITFLAG_DRY_RUN | BITFLAG_CHECK_GCODE))) { - spindle_run(gc.spindle_direction); //, gc.spindle_speed); - } + if (!(gc.switches & BITFLAG_CHECK_GCODE)) { spindle_run(gc.spindle_direction); } // [*M7,M8,M9]: Update coolant state - if (!(gc.switches & (BITFLAG_DRY_RUN | BITFLAG_CHECK_GCODE))) { - coolant_run(gc.coolant_mode); - } + if (!(gc.switches & BITFLAG_CHECK_GCODE)) { coolant_run(gc.coolant_mode); } // [G54,G55,...,G59]: Coordinate system selection if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_12)) ) { // Check if called in block @@ -327,14 +333,14 @@ uint8_t gc_execute_line(char *line) target[i] = gc.position[i]; } } - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); + mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); } // Retreive G28/30 go-home position data (in machine coordinates) from EEPROM float coord_data[N_AXIS]; uint8_t home_select = SETTING_INDEX_G28; if (non_modal_action == NON_MODAL_GO_HOME_1) { home_select = SETTING_INDEX_G30; } if (!settings_read_coord_data(home_select,coord_data)) { return(STATUS_SETTING_READ_FAIL); } - mc_line(coord_data[X_AXIS], coord_data[Y_AXIS], coord_data[Z_AXIS], settings.default_seek_rate, false); + mc_line(coord_data[X_AXIS], coord_data[Y_AXIS], coord_data[Z_AXIS], gc.seek_rate, false); axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. break; case NON_MODAL_SET_HOME_0: case NON_MODAL_SET_HOME_1: @@ -404,7 +410,9 @@ uint8_t gc_execute_line(char *line) break; case MOTION_MODE_SEEK: if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);} - else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); } + else { + mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); + } break; case MOTION_MODE_LINEAR: // TODO: Inverse time requires F-word with each statement. Need to do a check. Also need @@ -547,7 +555,7 @@ uint8_t gc_execute_line(char *line) // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9). Otherwise, // re-enable program flow after pause complete, where cycle start will resume the program. - if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { sys.abort = true; } + if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { sys.execute |= EXEC_RESET; } else { gc.program_flow = PROGRAM_FLOW_RUNNING; } } diff --git a/gcode.h b/gcode.h index 6369dfe..11980ad 100755 --- a/gcode.h +++ b/gcode.h @@ -83,6 +83,7 @@ typedef struct { int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9} float feed_rate; // Millimeters/min + float seek_rate; // Millimeters/min - Can change depending on switches and such. float position[3]; // Where the interpreter considers the tool to be at this point in the code uint8_t tool; // uint16_t spindle_speed; // RPM/100 diff --git a/protocol.c b/protocol.c index 068c7a2..cae64cb 100755 --- a/protocol.c +++ b/protocol.c @@ -123,7 +123,7 @@ void protocol_execute_runtime() // System abort. Steppers have already been force stopped. if (rt_exec & EXEC_RESET) { - sys.abort = true; + sys.abort = true; // Only place this is set true. return; // Nothing else to do but exit. } @@ -215,10 +215,20 @@ uint8_t protocol_execute_line(char *line) switch (line[++char_counter]) { case '0' : helper_var = BITFLAG_CHECK_GCODE; - // Sync position vectors if check mode is being disabled. May be different after checking. - if (bit_istrue(gc.switches,helper_var)) { sys_sync_current_position(); } + // If check mode is being disabled, automatically soft reset Grbl to ensure the user starts + // fresh with the g-code modes in their default, known state. + if (bit_istrue(gc.switches,helper_var)) { sys.execute |= EXEC_RESET; } + break; + case '1' : + helper_var = BITFLAG_DRY_RUN; + // If dry run is being disabled, automatically soft reset Grbl as with check g-code mode + if (bit_istrue(gc.switches,helper_var)) { + // If disabled while in cycle, immediately stop everything and notify user that stopping + // mid-cycle likely lost position. + if (bit_istrue(sys.state,STATE_CYCLE)) { mc_alarm(); } + sys.execute |= EXEC_RESET; // Soft-reset Grbl. + } break; - case '1' : helper_var = BITFLAG_DRY_RUN; break; case '2' : helper_var = BITFLAG_BLOCK_DELETE; break; case '3' : helper_var = BITFLAG_SINGLE_BLOCK; break; case '4' : helper_var = BITFLAG_OPT_STOP; break; From b3b454ee770d708cc0035c2768387cc21aedc1db Mon Sep 17 00:00:00 2001 From: Hans Insulander Date: Mon, 5 Nov 2012 22:48:09 +0100 Subject: [PATCH 42/55] Generate header dependencies and use them in Makefile --- Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 58a4d9d..f0e7f41 100755 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ all: grbl.hex .c.o: $(COMPILE) -c $< -o $@ + @$(COMPILE) -MM $< > $*.d .S.o: $(COMPILE) -x assembler-with-cpp -c $< -o $@ @@ -74,7 +75,7 @@ load: all bootloadHID grbl.hex clean: - rm -f grbl.hex main.elf $(OBJECTS) + rm -f grbl.hex main.elf $(OBJECTS) $(OBJECTS:.o=.d) # file targets: main.elf: $(OBJECTS) @@ -93,3 +94,7 @@ disasm: main.elf cpp: $(COMPILE) -E main.c + +# include generated header dependencies +-include $(OBJECTS:.o=.d) + From 8a09e2c9c4245a6b6cf0531faed2f831dd070f58 Mon Sep 17 00:00:00 2001 From: Hans Insulander Date: Mon, 5 Nov 2012 22:55:49 +0100 Subject: [PATCH 43/55] Ignore dependency files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7ad1a0e..844cd7a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.o *.elf *.DS_Store +*.d From f41dd6927378c7a2600b2b177aadd59a2fc83ad3 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Mon, 5 Nov 2012 21:40:52 -0700 Subject: [PATCH 44/55] Tweaks and bug fixes. Increase to 3 startup blocks. Remove purge/added unlock command - Increased the number of startup blocks to 3 for no good reason other than it doesn't increase the flash size. - Removed the purge buffer command and replaced with an disable homing lock command. - Homing now blocks all g-code commands (not system commands) until the homing cycle has been performed or the disable homing lock is sent. Homing is required upon startup or if Grbl loses it position. This is for safety reasons. - Cleaned up some of the Grbl states and re-organized it to be little more cohesive. - Cleaned up the feedback and status messages to not use so much flash space, as it's a premium now. - Check g-code and dry run switches how are mutually exclusive and can't be enabled when the other is. And automatically resets Grbl when disabled. - Some bug fixes and other minor tweaks. --- config.h | 2 +- gcode.c | 4 +- main.c | 17 ++++---- motion_control.c | 5 ++- nuts_bolts.h | 5 +-- protocol.c | 104 +++++++++++++++++++++++++---------------------- report.c | 36 ++++++++-------- report.h | 15 ++++--- settings.c | 5 +-- 9 files changed, 96 insertions(+), 97 deletions(-) diff --git a/config.h b/config.h index ae48023..dca6c86 100755 --- a/config.h +++ b/config.h @@ -164,7 +164,7 @@ // and addresses are defined in settings.h. With the current settings, up to 5 startup blocks may // be stored and executed in order. These startup blocks would typically be used to set the g-code // parser state depending on user preferences. -#define N_STARTUP_LINE 1 // Integer (1-5) +#define N_STARTUP_LINE 3 // Integer (1-5) // --------------------------------------------------------------------------------------- // FOR ADVANCED USERS ONLY: diff --git a/gcode.c b/gcode.c index b56e241..56e29fb 100755 --- a/gcode.c +++ b/gcode.c @@ -410,9 +410,7 @@ uint8_t gc_execute_line(char *line) break; case MOTION_MODE_SEEK: if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);} - else { - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); - } + else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); } break; case MOTION_MODE_LINEAR: // TODO: Inverse time requires F-word with each statement. Need to do a check. Also need diff --git a/main.c b/main.c index 43abb64..5e9ecb3 100755 --- a/main.c +++ b/main.c @@ -51,7 +51,7 @@ int main(void) memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization - sys.state = STATE_LOST; // Set state to indicate unknown initial position + sys.state = STATE_ALARM; // Set alarm state to indicate unknown initial position for(;;) { @@ -59,11 +59,6 @@ int main(void) // 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) { - // If a critical event has occurred, set the position lost system state. For example, a - // hard limit event can cause the stepper to lose steps and position due to an immediate - // stop, not with a controlled deceleration. Or, if an abort was issued while a cycle - // was active, the immediate stop can also cause lost steps. - if (sys.state == STATE_ALARM) { sys.state = STATE_LOST; } // Reset system. serial_reset_read_buffer(); // Clear serial read buffer @@ -84,11 +79,15 @@ int main(void) // resume. sys_sync_current_position(); - // Reset system variables + // Reset system variables. sys.abort = false; sys.execute = 0; - if (sys.state == STATE_LOST && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { - report_feedback_message(MESSAGE_POSITION_LOST); + if (sys.state == STATE_ALARM && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { + // If a critical event has occurred, set the position lost system state. For example, a + // hard limit event can cause the stepper to lose steps and position due to an immediate + // stop, not with a controlled deceleration. Or, if an abort was issued while a cycle + // was active, the immediate stop can also cause lost steps. + report_feedback_message(MESSAGE_HOMING_ALARM); } else { sys.state = STATE_IDLE; } diff --git a/motion_control.c b/motion_control.c index a7d4295..995d27c 100755 --- a/motion_control.c +++ b/motion_control.c @@ -215,7 +215,7 @@ void mc_go_home() limits_go_home(); // Perform homing routine. if (sys.abort) { - sys.state = STATE_LOST; // Homing routine did not complete. + sys.state = STATE_ALARM; // Homing routine did not complete. return; } @@ -253,7 +253,8 @@ void mc_alarm() // Only this function can set the system alarm. This is done to prevent multiple kill calls // by different processes. if (bit_isfalse(sys.execute, EXEC_ALARM)) { - // Set system alarm flag. + // Set system alarm flag to have the main program check for anything wrong with shutting + // down the system. sys.execute |= EXEC_ALARM; // Immediately force stepper, spindle, and coolant to stop. st_go_idle(); diff --git a/nuts_bolts.h b/nuts_bolts.h index 7438660..7b00210 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -75,9 +75,8 @@ #define STATE_HOLD 3 // Executing feed hold #define STATE_HOMING 4 // Performing homing cycle #define STATE_JOG 5 // Jogging mode is unique like homing. -#define STATE_ALARM 6 // In alarm state. Locks out all but reset -#define STATE_LOST 7 // Used to message position may be lost upon startup or event -#define STATE_LIMIT 8 // Used to message hard limit triggered. +#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes and messages position lost +#define STATE_LIMIT 7 // Used to message hard limit triggered. // Define global system variables typedef struct { diff --git a/protocol.c b/protocol.c index cae64cb..aa84bfc 100755 --- a/protocol.c +++ b/protocol.c @@ -102,22 +102,25 @@ void protocol_execute_runtime() if (sys.execute) { // Enter only if any bit flag is true uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times - // System alarm. Something has gone wrong. Disable everything by entering an infinite - // loop until system reset/abort. + // System alarm. Everything has shutdown by either something that has gone wrong or issuing + // the Grbl soft-reset/abort. Check the system states to report any critical error found. + // If critical, disable Grbl by entering an infinite loop until system reset/abort. + // NOTE: The system alarm state is also used to set if (rt_exec & EXEC_ALARM) { - // Report the cause of the alarm here in the main program. - if (sys.state == STATE_LIMIT) { report_status_message(STATUS_HARD_LIMIT); } - if (sys.state == STATE_CYCLE) { // Pick up abort during active cycle. - report_status_message(STATUS_ABORT_CYCLE); - sys.state = STATE_LOST; - } - // Ignore loop if reset is already issued. In other words, a normal system abort/reset - // will not enter this loop, only a critical event not controlled by the user will. - if (bit_isfalse(rt_exec,EXEC_RESET)) { + // Limit switch critical event. Lock out Grbl until reset. + if (sys.state == STATE_LIMIT) { + report_status_message(STATUS_HARD_LIMIT); sys.state = STATE_ALARM; - report_feedback_message(MESSAGE_SYSTEM_ALARM); + report_feedback_message(MESSAGE_CRITICAL_EVENT); while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } } + + // Check if a runtime abort command was issued during a cycle. If so, message the user + // that position may have been lost and set alarm state to force re-homing, if enabled. + if (sys.state == STATE_CYCLE) { + report_status_message(STATUS_ABORT_CYCLE); + sys.state = STATE_ALARM; + } bit_false(sys.execute,EXEC_ALARM); } @@ -193,8 +196,8 @@ uint8_t protocol_execute_line(char *line) case 'H' : // Perform homing cycle if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { // Only perform homing if Grbl is idle or lost. - if ( sys.state==STATE_IDLE || sys.state==STATE_LOST ) { mc_go_home(); } - else { return(STATUS_HOMING_ERROR); } + if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) { mc_go_home(); } + else { return(STATUS_IDLE_ERROR); } } else { return(STATUS_SETTING_DISABLED); } break; // case 'J' : break; // Jogging methods @@ -210,49 +213,47 @@ uint8_t protocol_execute_line(char *line) // block buffer without having the planner plan them. It would need to manage de/ac-celerations // on its own carefully. This approach could be effective and possibly size/memory efficient. case 'S' : // Switch modes - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } // Set helper_var as switch bitmask or clearing flag switch (line[++char_counter]) { - case '0' : - helper_var = BITFLAG_CHECK_GCODE; - // If check mode is being disabled, automatically soft reset Grbl to ensure the user starts - // fresh with the g-code modes in their default, known state. - if (bit_istrue(gc.switches,helper_var)) { sys.execute |= EXEC_RESET; } - break; - case '1' : - helper_var = BITFLAG_DRY_RUN; - // If dry run is being disabled, automatically soft reset Grbl as with check g-code mode - if (bit_istrue(gc.switches,helper_var)) { - // If disabled while in cycle, immediately stop everything and notify user that stopping - // mid-cycle likely lost position. - if (bit_istrue(sys.state,STATE_CYCLE)) { mc_alarm(); } - sys.execute |= EXEC_RESET; // Soft-reset Grbl. - } - break; + case '0' : helper_var = BITFLAG_CHECK_GCODE; break; + case '1' : helper_var = BITFLAG_DRY_RUN; break; case '2' : helper_var = BITFLAG_BLOCK_DELETE; break; case '3' : helper_var = BITFLAG_SINGLE_BLOCK; break; case '4' : helper_var = BITFLAG_OPT_STOP; break; default : return(STATUS_INVALID_STATEMENT); } - gc.switches ^= helper_var; - if (bit_istrue(gc.switches,helper_var)) { report_feedback_message(MESSAGE_SWITCH_ON); } - else { report_feedback_message(MESSAGE_SWITCH_OFF); } - break; - case 'P' : // Purge system if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - if (sys.state == STATE_CYCLE) { return(STATUS_PURGE_CYCLE); } // Also prevents position error - plan_reset_buffer(); - if (sys.state == STATE_LOST) { - report_feedback_message(MESSAGE_PURGE_AXES_LOCK); - sys_sync_current_position(); // Any motion commands during a lock can unsync position vectors. + if ( helper_var & (BITFLAG_CHECK_GCODE | BITFLAG_DRY_RUN) ) { + if ( bit_istrue(gc.switches,helper_var) ) { + // Perform reset and check for cycle when toggling off. If disabled while in cycle, + // immediately stop everything and notify user that stopping mid-cycle and likely + // lost position. In check g-code mode, there is never a cycle. + if (sys.state == STATE_CYCLE) { mc_alarm(); } + sys.execute |= EXEC_RESET; // Soft-reset Grbl. + } else { + // Check if Grbl is idle and ready or if the other mode is enabled. + if (sys.state) { return(STATUS_IDLE_ERROR); } + if ((gc.switches & (BITFLAG_CHECK_GCODE | BITFLAG_DRY_RUN)) & ~(helper_var)) { return(STATUS_INVALID_STATEMENT); } + } + } + gc.switches ^= helper_var; + if (bit_istrue(gc.switches,helper_var)) { report_feedback_message(MESSAGE_ENABLED); } + else { report_feedback_message(MESSAGE_DISABLED); } + break; + case 'U' : // Disable homing lock + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + if (sys.state == STATE_ALARM) { + report_feedback_message(MESSAGE_HOMING_UNLOCK); + sys.state = STATE_IDLE; + } else { + return(STATUS_SETTING_DISABLED); } - sys.state = STATE_IDLE; break; case 'N' : // Startup lines. if ( line[++char_counter] == 0 ) { // Print startup lines for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { if (!(settings_read_startup_line(helper_var, line))) { - report_status_message(STATUS_SETTING_READ_FAIL); + report_status_message(STATUS_SETTING_READ_FAIL); } else { report_startup_line(helper_var,line); } @@ -287,12 +288,17 @@ uint8_t protocol_execute_line(char *line) return(STATUS_OK); // If '$' command makes it to here, then everything's ok. } else { - - return(gc_execute_line(line)); // Everything else is gcode - // TODO: Install option to set system alarm upon any error code received back from the - // the g-code parser. This is a common safety feature on CNCs to help prevent crashes - // if the g-code doesn't perform as intended. - + + // If homing is enabled and position is lost, lock out all g-code commands. + if (sys.state != STATE_ALARM) { + return(gc_execute_line(line)); // Everything else is gcode + // TODO: Install option to set system alarm upon any error code received back from the + // the g-code parser. This is a common safety feature on CNCs to help prevent crashes + // if the g-code doesn't perform as intended. + } else { + return(STATUS_HOMING_LOCK); + } + } } diff --git a/report.c b/report.c index d336677..1407a7f 100644 --- a/report.c +++ b/report.c @@ -63,7 +63,7 @@ void report_status_message(uint8_t status_code) case STATUS_INVALID_STATEMENT: printPgmString(PSTR("Invalid statement")); break; case STATUS_HARD_LIMIT: - printPgmString(PSTR("Limit triggered")); break; + printPgmString(PSTR("Limit triggered. MPos lost?")); break; case STATUS_SETTING_DISABLED: printPgmString(PSTR("Setting disabled")); break; case STATUS_SETTING_VALUE_NEG: @@ -72,12 +72,12 @@ void report_status_message(uint8_t status_code) printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break; case STATUS_SETTING_READ_FAIL: printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; - case STATUS_HOMING_ERROR: - printPgmString(PSTR("Must be idle to home")); break; + case STATUS_IDLE_ERROR: + printPgmString(PSTR("Must be idle to execute")); break; case STATUS_ABORT_CYCLE: - printPgmString(PSTR("Abort during cycle. Position maybe lost")); break; - case STATUS_PURGE_CYCLE: - printPgmString(PSTR("Can't purge buffer during cycle")); break; + printPgmString(PSTR("Abort during cycle. MPos lost?")); break; + case STATUS_HOMING_LOCK: + printPgmString(PSTR("Locked until homed")); break; } printPgmString(PSTR("\r\n")); } @@ -94,18 +94,16 @@ void report_feedback_message(int8_t message_code) { printPgmString(PSTR("[")); switch(message_code) { - case MESSAGE_SYSTEM_ALARM: - printPgmString(PSTR("ALARM: Check and reset Grbl")); break; - case MESSAGE_POSITION_LOST: - printPgmString(PSTR("'$H' to home and enable axes")); break; - case MESSAGE_HOMING_ENABLE: - printPgmString(PSTR("WARNING: All limit switches must be installed before homing")); break; - case MESSAGE_SWITCH_ON: - printPgmString(PSTR("Switch enabled")); break; - case MESSAGE_SWITCH_OFF: - printPgmString(PSTR("Switch disabled")); break; - case MESSAGE_PURGE_AXES_LOCK: - printPgmString(PSTR("WARNING: Motion lock disabled. Position unknown.")); break; + case MESSAGE_CRITICAL_EVENT: + printPgmString(PSTR("ALARM: Check and reset")); break; + case MESSAGE_HOMING_ALARM: + printPgmString(PSTR("'$H' to home and unlock")); break; + case MESSAGE_ENABLED: + printPgmString(PSTR("Enabled")); break; + case MESSAGE_DISABLED: + printPgmString(PSTR("Disabled")); break; + case MESSAGE_HOMING_UNLOCK: + printPgmString(PSTR("Unlocked. MPos lost?")); break; } printPgmString(PSTR("]\r\n")); } @@ -131,7 +129,7 @@ void report_grbl_help() { "$S2 (toggle block delete)\r\n" "$S3 (toggle single block)\r\n" "$S4 (toggle optional stop)\r\n" - "$P (purge buffer and locks)\r\n" + "$U (disable homing lock)\r\n" "$H (perform homing cycle)\r\n" "~ (cycle start)\r\n" "! (feed hold)\r\n" diff --git a/report.h b/report.h index 810d199..4d8c471 100644 --- a/report.h +++ b/report.h @@ -34,17 +34,16 @@ #define STATUS_SETTING_VALUE_NEG 9 #define STATUS_SETTING_STEP_PULSE_MIN 10 #define STATUS_SETTING_READ_FAIL 11 -#define STATUS_HOMING_ERROR 12 +#define STATUS_IDLE_ERROR 12 #define STATUS_ABORT_CYCLE 13 -#define STATUS_PURGE_CYCLE 14 +#define STATUS_HOMING_LOCK 14 // Define Grbl feedback message codes. Less than zero to distinguish message from error. -#define MESSAGE_SYSTEM_ALARM -1 -#define MESSAGE_POSITION_LOST -2 -#define MESSAGE_HOMING_ENABLE -3 -#define MESSAGE_SWITCH_ON -4 -#define MESSAGE_SWITCH_OFF -5 -#define MESSAGE_PURGE_AXES_LOCK -6 +#define MESSAGE_CRITICAL_EVENT -1 +#define MESSAGE_HOMING_ALARM -2 +#define MESSAGE_ENABLED -3 +#define MESSAGE_DISABLED -4 +#define MESSAGE_HOMING_UNLOCK -5 // Prints system status messages. void report_status_message(uint8_t status_code); diff --git a/settings.c b/settings.c index 1deb732..2e22ea6 100644 --- a/settings.c +++ b/settings.c @@ -202,9 +202,8 @@ uint8_t settings_store_global_setting(int parameter, float value) { case 14: if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; - sys.state = STATE_LOST; - report_feedback_message(MESSAGE_POSITION_LOST); - report_feedback_message(MESSAGE_HOMING_ENABLE); + sys.state = STATE_ALARM; + report_feedback_message(MESSAGE_HOMING_ALARM); } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } break; case 15: settings.homing_dir_mask = trunc(value); break; From 5e7a4b3ba8a032aa6880d7eb8224a37819f0286c Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Wed, 7 Nov 2012 20:53:03 -0700 Subject: [PATCH 45/55] More tweaks. Removed dry run. Trimmed all messages to save flash space. - Removed the dry run switch. It was getting overly complicated for what it needed to do. In practice, single block mode and feed rate overrides (coming in next release) does a much better job with dry runs than 'dry run'. - Trimmed all of Grbl's messages from help, status, feedback to settings. Saved 0.6KB+ of flash space that could be used for v0.9 features. - Removed some settings inits when set. Will depend on user to power cycle to get some of these to reload. - Fixed a bug with settings version not re-writing old settings, when it should. Thanks Alden! --- config.h | 4 +-- gcode.c | 16 ++-------- gcode.h | 8 ++--- main.c | 9 +++--- protocol.c | 33 +++++++------------ report.c | 93 ++++++++++++++++++++++++++---------------------------- settings.c | 79 +++++++++++++++------------------------------- settings.h | 2 +- 8 files changed, 97 insertions(+), 147 deletions(-) diff --git a/config.h b/config.h index dca6c86..c0f9ad6 100755 --- a/config.h +++ b/config.h @@ -100,7 +100,7 @@ #define DEFAULT_FEEDRATE 250.0 #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< 0.0")); break; + printPgmString(PSTR("Value < 0.0")); break; case STATUS_SETTING_STEP_PULSE_MIN: - printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break; + printPgmString(PSTR("Value < 3 usec")); break; case STATUS_SETTING_READ_FAIL: - printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break; + printPgmString(PSTR("EEPROM read fail. Using defaults")); break; case STATUS_IDLE_ERROR: - printPgmString(PSTR("Must be idle to execute")); break; + printPgmString(PSTR("Busy or locked")); break; case STATUS_ABORT_CYCLE: printPgmString(PSTR("Abort during cycle. MPos lost?")); break; case STATUS_HOMING_LOCK: @@ -117,55 +117,53 @@ void report_init_message() // Grbl help message void report_grbl_help() { - printPgmString(PSTR("$ (help)\r\n" - "$$ (print Grbl settings)\r\n" - "$# (print gcode parameters)\r\n" - "$G (print gcode parser state)\r\n" - "$N (print startup blocks)\r\n" - "$x=value (store Grbl setting)\r\n" - "$Nx=line (store startup block)\r\n" + printPgmString(PSTR("$$ (view Grbl settings)\r\n" + "$# (view # parameters)\r\n" + "$G (view parser state)\r\n" + "$N (view startup blocks)\r\n" + "$x=value (save Grbl setting)\r\n" + "$Nx=line (save startup block)\r\n" "$S0 (toggle check gcode)\r\n" - "$S1 (toggle dry run)\r\n" - "$S2 (toggle block delete)\r\n" - "$S3 (toggle single block)\r\n" - "$S4 (toggle optional stop)\r\n" - "$U (disable homing lock)\r\n" - "$H (perform homing cycle)\r\n" + "$S1 (toggle blk del)\r\n" + "$S2 (toggle single blk)\r\n" + "$S3 (toggle opt stop)\r\n" + "$X (kill homing lock)\r\n" + "$H (run homing cycle)\r\n" "~ (cycle start)\r\n" "! (feed hold)\r\n" - "? (current position)\r\n" - "^x (reset Grbl)\r\n")); + "? (position)\r\n" + "ctrl-x (reset Grbl)\r\n")); } // Grbl global settings print out. // NOTE: The numbering scheme here must correlate to storing in settings.c void report_grbl_settings() { printPgmString(PSTR("$0=")); printFloat(settings.steps_per_mm[X_AXIS]); - printPgmString(PSTR(" (x axis, steps/mm)\r\n$1=")); printFloat(settings.steps_per_mm[Y_AXIS]); - printPgmString(PSTR(" (y axis, steps/mm)\r\n$2=")); printFloat(settings.steps_per_mm[Z_AXIS]); - printPgmString(PSTR(" (z axis, steps/mm)\r\n$3=")); printInteger(settings.pulse_microseconds); + printPgmString(PSTR(" (x, step/mm)\r\n$1=")); printFloat(settings.steps_per_mm[Y_AXIS]); + printPgmString(PSTR(" (y, step/mm)\r\n$2=")); printFloat(settings.steps_per_mm[Z_AXIS]); + printPgmString(PSTR(" (z, step/mm)\r\n$3=")); printInteger(settings.pulse_microseconds); printPgmString(PSTR(" (step pulse, usec)\r\n$4=")); printFloat(settings.default_feed_rate); - printPgmString(PSTR(" (default feed rate, mm/min)\r\n$5=")); printFloat(settings.default_seek_rate); - printPgmString(PSTR(" (default seek rate, mm/min)\r\n$6=")); printFloat(settings.mm_per_arc_segment); - printPgmString(PSTR(" (arc resolution, mm/segment)\r\n$7=")); printInteger(settings.invert_mask); - printPgmString(PSTR(" (step port invert mask, int:binary=")); print_uint8_base2(settings.invert_mask); - printPgmString(PSTR(")\r\n$8=")); printFloat(settings.acceleration/(60*60)); // Convert from mm/min^2 for human readability + printPgmString(PSTR(" (default feed, mm/min)\r\n$5=")); printFloat(settings.default_seek_rate); + printPgmString(PSTR(" (default seek, mm/min)\r\n$6=")); printInteger(settings.invert_mask); + printPgmString(PSTR(" (step port invert mask, int:")); print_uint8_base2(settings.invert_mask); + printPgmString(PSTR(")\r\n$7=")); printInteger(settings.stepper_idle_lock_time); + printPgmString(PSTR(" (step idle delay, msec)\r\n$8=")); printFloat(settings.acceleration/(60*60)); // Convert from mm/min^2 for human readability printPgmString(PSTR(" (acceleration, mm/sec^2)\r\n$9=")); printFloat(settings.junction_deviation); - printPgmString(PSTR(" (cornering junction deviation, mm)\r\n$10=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); - printPgmString(PSTR(" (report inches, bool)\r\n$11=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START)); - printPgmString(PSTR(" (auto start enable, bool)\r\n$12=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); - printPgmString(PSTR(" (invert stepper enable, bool)\r\n$13=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); - printPgmString(PSTR(" (hard limit enable, bool)\r\n$14=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); - printPgmString(PSTR(" (homing enable, bool)\r\n$15=")); printInteger(settings.homing_dir_mask); - printPgmString(PSTR(" (homing dir invert mask, int:binary=")); print_uint8_base2(settings.homing_dir_mask); - printPgmString(PSTR(")\r\n$16=")); printFloat(settings.homing_feed_rate); - printPgmString(PSTR(" (homing feed rate, mm/min)\r\n$17=")); printFloat(settings.homing_seek_rate); - printPgmString(PSTR(" (homing seek rate, mm/min)\r\n$18=")); printInteger(settings.homing_debounce_delay); - printPgmString(PSTR(" (homing debounce delay, msec)\r\n$19=")); printFloat(settings.homing_pulloff); - printPgmString(PSTR(" (homing pull-off travel, mm)\r\n$20=")); printInteger(settings.stepper_idle_lock_time); - printPgmString(PSTR(" (stepper idle lock time, msec)\r\n$21=")); printInteger(settings.decimal_places); - printPgmString(PSTR(" (decimal places, int)\r\n$22=")); printInteger(settings.n_arc_correction); - printPgmString(PSTR(" (n arc correction, int)\r\n")); + printPgmString(PSTR(" (junction deviation, mm)\r\n$10=")); printFloat(settings.mm_per_arc_segment); + printPgmString(PSTR(" (arc, mm/segment)\r\n$11=")); printInteger(settings.n_arc_correction); + printPgmString(PSTR(" (n-arc correction, int)\r\n$12=")); printInteger(settings.decimal_places); + printPgmString(PSTR(" (n-decimals, int)\r\n$13=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); + printPgmString(PSTR(" (report inches, bool)\r\n$14=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START)); + printPgmString(PSTR(" (auto start, bool)\r\n$15=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); + printPgmString(PSTR(" (invert step enable, bool)\r\n$16=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); + printPgmString(PSTR(" (hard limits, bool)\r\n$17=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); + printPgmString(PSTR(" (homing cycle, bool)\r\n$18=")); printInteger(settings.homing_dir_mask); + printPgmString(PSTR(" (homing dir invert mask, int:")); print_uint8_base2(settings.homing_dir_mask); + printPgmString(PSTR(")\r\n$19=")); printFloat(settings.homing_feed_rate); + printPgmString(PSTR(" (homing feed, mm/min)\r\n$20=")); printFloat(settings.homing_seek_rate); + printPgmString(PSTR(" (homing seek, mm/min)\r\n$21=")); printInteger(settings.homing_debounce_delay); + printPgmString(PSTR(" (homing debounce, msec)\r\n$22=")); printFloat(settings.homing_pulloff); + printPgmString(PSTR(" (homing pull-off, mm)\r\n")); } @@ -266,10 +264,9 @@ void report_gcode_modes() // Print active switches if (gc.switches) { if (bit_istrue(gc.switches,BITFLAG_CHECK_GCODE)) { printPgmString(PSTR(" $S0")); } - if (bit_istrue(gc.switches,BITFLAG_DRY_RUN)) { printPgmString(PSTR(" $S1")); } - if (bit_istrue(gc.switches,BITFLAG_BLOCK_DELETE)) { printPgmString(PSTR(" $S2")); } - if (bit_istrue(gc.switches,BITFLAG_SINGLE_BLOCK)) { printPgmString(PSTR(" $S3")); } - if (bit_istrue(gc.switches,BITFLAG_OPT_STOP)) { printPgmString(PSTR(" $S4")); } + if (bit_istrue(gc.switches,BITFLAG_BLOCK_DELETE)) { printPgmString(PSTR(" $S1")); } + if (bit_istrue(gc.switches,BITFLAG_SINGLE_BLOCK)) { printPgmString(PSTR(" $S2")); } + if (bit_istrue(gc.switches,BITFLAG_OPT_STOP)) { printPgmString(PSTR(" $S3")); } } printPgmString(PSTR("\r\n")); diff --git a/settings.c b/settings.c index 2e22ea6..d1d42e2 100644 --- a/settings.c +++ b/settings.c @@ -142,15 +142,7 @@ uint8_t read_global_settings() { if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { return(false); } - settings_reset(false); - } else if (version >= 50) { - // Developmental settings. Version numbers greater than or equal to 50 are temporary. - // Currently, this will update the user settings to v4 and the remainder of the settings - // should be re-written to the default value, if the developmental version number changed. - - // Grab settings regardless of error. - memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)); - settings_reset(false); + settings_reset(false); // Old settings ok. Write new settings only. } else { return(false); } @@ -170,60 +162,41 @@ uint8_t settings_store_global_setting(int parameter, float value) { settings.pulse_microseconds = round(value); break; case 4: settings.default_feed_rate = value; break; case 5: settings.default_seek_rate = value; break; - case 6: settings.mm_per_arc_segment = value; break; - case 7: settings.invert_mask = trunc(value); break; + case 6: settings.invert_mask = trunc(value); break; + case 7: settings.stepper_idle_lock_time = round(value); break; case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. case 9: settings.junction_deviation = fabs(value); break; - case 10: - if (value) { - settings.flags |= BITFLAG_REPORT_INCHES; - } else { settings.flags &= ~BITFLAG_REPORT_INCHES; } - break; - case 11: - // Immediately apply auto_start to sys struct. - if (value) { - settings.flags |= BITFLAG_AUTO_START; - sys.auto_start = true; - } else { - settings.flags &= ~BITFLAG_AUTO_START; - sys.auto_start = false; - } - break; - case 12: - if (value) { - settings.flags |= BITFLAG_INVERT_ST_ENABLE; - } else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } - break; + case 10: settings.mm_per_arc_segment = value; break; + case 11: settings.n_arc_correction = round(value); break; + case 12: settings.decimal_places = round(value); break; case 13: - if (value) { - settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; - } else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } + if (value) { settings.flags |= BITFLAG_REPORT_INCHES; } + else { settings.flags &= ~BITFLAG_REPORT_INCHES; } break; - case 14: + case 14: // Reboot to ensure change + if (value) { settings.flags |= BITFLAG_AUTO_START; } + else { settings.flags &= ~BITFLAG_AUTO_START; } + break; + case 15: // Reboot to ensure change + if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } + else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } + break; + case 16: // Reboot to ensure change + if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } + else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } + break; + case 17: if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; sys.state = STATE_ALARM; report_feedback_message(MESSAGE_HOMING_ALARM); } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } break; - case 15: settings.homing_dir_mask = trunc(value); break; - case 16: settings.homing_feed_rate = value; break; - case 17: settings.homing_seek_rate = value; break; - case 18: settings.homing_debounce_delay = round(value); break; - case 19: - if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } - settings.homing_pulloff = value; break; - case 20: - settings.stepper_idle_lock_time = round(value); - // Immediately toggle stepper enable/disable functions to ensure change from always - // enable or disable. Will not start or stop a cycle. - if (!sys.state) { // Only if idle. - st_wake_up(); - st_go_idle(); - } - break; - case 21: settings.decimal_places = round(value); break; - case 22: settings.n_arc_correction = round(value); break; + case 18: settings.homing_dir_mask = trunc(value); break; + case 19: settings.homing_feed_rate = value; break; + case 20: settings.homing_seek_rate = value; break; + case 21: settings.homing_debounce_delay = round(value); break; + case 22: settings.homing_pulloff = value; break; default: return(STATUS_INVALID_STATEMENT); } diff --git a/settings.h b/settings.h index c363605..bf3bb16 100755 --- a/settings.h +++ b/settings.h @@ -29,7 +29,7 @@ // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom -#define SETTINGS_VERSION 56 +#define SETTINGS_VERSION 5 // Define bit flag masks for the boolean settings in settings.flag. #define BITFLAG_REPORT_INCHES bit(0) From baf137882b653b0728502ddd489d73e4f9ac8e08 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Thu, 8 Nov 2012 17:24:56 -0700 Subject: [PATCH 46/55] Sanguino compile patch @daapp : Sanguino compile serial USART path. Thanks! --- serial.c | 8 + settings.c | 445 ++++++++++++++++++++++++++--------------------------- 2 files changed, 230 insertions(+), 223 deletions(-) diff --git a/serial.c b/serial.c index 57cacc8..ee22333 100755 --- a/serial.c +++ b/serial.c @@ -91,7 +91,11 @@ void serial_write(uint8_t data) { } // Data Register Empty Interrupt handler +#ifdef __AVR_ATmega644P__ +ISR(USART0_UDRE_vect) +#else ISR(USART_UDRE_vect) +#endif { // Temporary tx_buffer_tail (to optimize for volatile) uint8_t tail = tx_buffer_tail; @@ -140,7 +144,11 @@ uint8_t serial_read() } } +#ifdef __AVR_ATmega644P__ +ISR(USART0_RX_vect) +#else ISR(USART_RX_vect) +#endif { uint8_t data = UDR0; uint8_t next_head; diff --git a/settings.c b/settings.c index d1d42e2..5b1ef2e 100644 --- a/settings.c +++ b/settings.c @@ -1,223 +1,222 @@ -/* - settings.c - eeprom configuration handling - Part of Grbl - - Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011-2012 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 - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Grbl is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Grbl. If not, see . -*/ - -#include -#include "protocol.h" -#include "report.h" -#include "stepper.h" -#include "nuts_bolts.h" -#include "settings.h" -#include "eeprom.h" - -settings_t settings; - -// Version 4 outdated settings record -typedef struct { - float steps_per_mm[3]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - float default_seek_rate; - uint8_t invert_mask; - float mm_per_arc_segment; - float acceleration; - float junction_deviation; -} settings_v4_t; - - -// Method to store startup lines into EEPROM -void settings_store_startup_line(uint8_t n, char *line) -{ - uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; - memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); -} - -// Method to store coord data parameters into EEPROM -void settings_write_coord_data(uint8_t coord_select, float *coord_data) -{ - uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; - memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); -} - -// Method to store Grbl global settings struct and version number into EEPROM -void write_global_settings() -{ - eeprom_put_char(0, SETTINGS_VERSION); - memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); -} - -// Method to reset Grbl global settings back to defaults. -void settings_reset(bool reset_all) { - // Reset all settings or only the migration settings to the new version. - if (reset_all) { - settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; - settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; - settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; - settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; - settings.default_feed_rate = DEFAULT_FEEDRATE; - settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE; - settings.acceleration = DEFAULT_ACCELERATION; - settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT; - settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; - } - // New settings since last version - settings.flags = 0; - if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } - if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } - if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } - if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } - if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } - settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; - settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; - settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; - settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; - settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; - settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; - settings.decimal_places = DEFAULT_DECIMAL_PLACES; - settings.n_arc_correction = DEFAULT_N_ARC_CORRECTION; - write_global_settings(); -} - -// Reads startup line from EEPROM. Updated pointed line string data. -uint8_t settings_read_startup_line(uint8_t n, char *line) -{ - uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; - if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { - // Reset line with default value - // TODO: Need to come up with a method to do this. - line[0] = 0; - settings_store_startup_line(n, line); - return(false); - } else { - return(true); - } -} - -// Read selected coordinate data from EEPROM. Updates pointed coord_data value. -uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) -{ - uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; - if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { - // Reset with default zero vector - clear_vector_float(coord_data); - settings_write_coord_data(coord_select,coord_data); - return(false); - } else { - return(true); - } -} - -// Reads Grbl global settings struct from EEPROM. -uint8_t read_global_settings() { - // Check version-byte of eeprom - uint8_t version = eeprom_get_char(0); - - if (version == SETTINGS_VERSION) { - // Read settings-record and check checksum - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { - return(false); - } - } else { - if (version <= 4) { - // Migrate from settings version 4 to current version. - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { - return(false); - } - settings_reset(false); // Old settings ok. Write new settings only. - } else { - return(false); - } - } - return(true); -} - - -// A helper method to set settings from command line -uint8_t settings_store_global_setting(int parameter, float value) { - switch(parameter) { - case 0: case 1: case 2: - if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } - settings.steps_per_mm[parameter] = value; break; - case 3: - if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } - settings.pulse_microseconds = round(value); break; - case 4: settings.default_feed_rate = value; break; - case 5: settings.default_seek_rate = value; break; - case 6: settings.invert_mask = trunc(value); break; - case 7: settings.stepper_idle_lock_time = round(value); break; - case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. - case 9: settings.junction_deviation = fabs(value); break; - case 10: settings.mm_per_arc_segment = value; break; - case 11: settings.n_arc_correction = round(value); break; - case 12: settings.decimal_places = round(value); break; - case 13: - if (value) { settings.flags |= BITFLAG_REPORT_INCHES; } - else { settings.flags &= ~BITFLAG_REPORT_INCHES; } - break; - case 14: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_AUTO_START; } - else { settings.flags &= ~BITFLAG_AUTO_START; } - break; - case 15: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } - else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } - break; - case 16: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } - else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } - break; - case 17: - if (value) { - settings.flags |= BITFLAG_HOMING_ENABLE; - sys.state = STATE_ALARM; - report_feedback_message(MESSAGE_HOMING_ALARM); - } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } - break; - case 18: settings.homing_dir_mask = trunc(value); break; - case 19: settings.homing_feed_rate = value; break; - case 20: settings.homing_seek_rate = value; break; - case 21: settings.homing_debounce_delay = round(value); break; - case 22: settings.homing_pulloff = value; break; - default: - return(STATUS_INVALID_STATEMENT); - } - write_global_settings(); - return(STATUS_OK); -} - -// Initialize the config subsystem -void settings_init() { - if(!read_global_settings()) { - report_status_message(STATUS_SETTING_READ_FAIL); - settings_reset(true); - report_grbl_settings(); - } - // Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. - float coord_data[N_AXIS]; - uint8_t i; - for (i=0; i<=SETTING_INDEX_NCOORD; i++) { - if (!settings_read_coord_data(i, coord_data)) { - report_status_message(STATUS_SETTING_READ_FAIL); - } - } - // NOTE: Startup lines are handled and called by protocol_init(). -} +/* + settings.c - eeprom configuration handling + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011-2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include +#include "protocol.h" +#include "report.h" +#include "stepper.h" +#include "nuts_bolts.h" +#include "settings.h" +#include "eeprom.h" + +settings_t settings; + +// Version 4 outdated settings record +typedef struct { + float steps_per_mm[3]; + uint8_t microsteps; + uint8_t pulse_microseconds; + float default_feed_rate; + float default_seek_rate; + uint8_t invert_mask; + float mm_per_arc_segment; + float acceleration; + float junction_deviation; +} settings_v4_t; + + +// Method to store startup lines into EEPROM +void settings_store_startup_line(uint8_t n, char *line) +{ + uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; + memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); +} + +// Method to store coord data parameters into EEPROM +void settings_write_coord_data(uint8_t coord_select, float *coord_data) +{ + uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; + memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); +} + +// Method to store Grbl global settings struct and version number into EEPROM +void write_global_settings() +{ + eeprom_put_char(0, SETTINGS_VERSION); + memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); +} + +// Method to reset Grbl global settings back to defaults. +void settings_reset(bool reset_all) { + // Reset all settings or only the migration settings to the new version. + if (reset_all) { + settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; + settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; + settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; + settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; + settings.default_feed_rate = DEFAULT_FEEDRATE; + settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE; + settings.acceleration = DEFAULT_ACCELERATION; + settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT; + settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; + settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; + } + // New settings since last version + settings.flags = 0; + if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } + if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } + if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } + if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } + if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } + settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; + settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; + settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; + settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; + settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; + settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; + settings.decimal_places = DEFAULT_DECIMAL_PLACES; + settings.n_arc_correction = DEFAULT_N_ARC_CORRECTION; + write_global_settings(); +} + +// Reads startup line from EEPROM. Updated pointed line string data. +uint8_t settings_read_startup_line(uint8_t n, char *line) +{ + uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; + if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { + // Reset line with default value + line[0] = 0; + settings_store_startup_line(n, line); + return(false); + } else { + return(true); + } +} + +// Read selected coordinate data from EEPROM. Updates pointed coord_data value. +uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) +{ + uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; + if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { + // Reset with default zero vector + clear_vector_float(coord_data); + settings_write_coord_data(coord_select,coord_data); + return(false); + } else { + return(true); + } +} + +// Reads Grbl global settings struct from EEPROM. +uint8_t read_global_settings() { + // Check version-byte of eeprom + uint8_t version = eeprom_get_char(0); + + if (version == SETTINGS_VERSION) { + // Read settings-record and check checksum + if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { + return(false); + } + } else { + if (version <= 4) { + // Migrate from settings version 4 to current version. + if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { + return(false); + } + settings_reset(false); // Old settings ok. Write new settings only. + } else { + return(false); + } + } + return(true); +} + + +// A helper method to set settings from command line +uint8_t settings_store_global_setting(int parameter, float value) { + switch(parameter) { + case 0: case 1: case 2: + if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } + settings.steps_per_mm[parameter] = value; break; + case 3: + if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } + settings.pulse_microseconds = round(value); break; + case 4: settings.default_feed_rate = value; break; + case 5: settings.default_seek_rate = value; break; + case 6: settings.invert_mask = trunc(value); break; + case 7: settings.stepper_idle_lock_time = round(value); break; + case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. + case 9: settings.junction_deviation = fabs(value); break; + case 10: settings.mm_per_arc_segment = value; break; + case 11: settings.n_arc_correction = round(value); break; + case 12: settings.decimal_places = round(value); break; + case 13: + if (value) { settings.flags |= BITFLAG_REPORT_INCHES; } + else { settings.flags &= ~BITFLAG_REPORT_INCHES; } + break; + case 14: // Reboot to ensure change + if (value) { settings.flags |= BITFLAG_AUTO_START; } + else { settings.flags &= ~BITFLAG_AUTO_START; } + break; + case 15: // Reboot to ensure change + if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } + else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } + break; + case 16: // Reboot to ensure change + if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } + else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } + break; + case 17: + if (value) { + settings.flags |= BITFLAG_HOMING_ENABLE; + sys.state = STATE_ALARM; + report_feedback_message(MESSAGE_HOMING_ALARM); + } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } + break; + case 18: settings.homing_dir_mask = trunc(value); break; + case 19: settings.homing_feed_rate = value; break; + case 20: settings.homing_seek_rate = value; break; + case 21: settings.homing_debounce_delay = round(value); break; + case 22: settings.homing_pulloff = value; break; + default: + return(STATUS_INVALID_STATEMENT); + } + write_global_settings(); + return(STATUS_OK); +} + +// Initialize the config subsystem +void settings_init() { + if(!read_global_settings()) { + report_status_message(STATUS_SETTING_READ_FAIL); + settings_reset(true); + report_grbl_settings(); + } + // Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. + float coord_data[N_AXIS]; + uint8_t i; + for (i=0; i<=SETTING_INDEX_NCOORD; i++) { + if (!settings_read_coord_data(i, coord_data)) { + report_status_message(STATUS_SETTING_READ_FAIL); + } + } + // NOTE: Startup lines are handled and called by main.c at the end of initialization. +} From 00fd09189daf30be6bb2bc21233d0f7c2b0339f3 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Thu, 8 Nov 2012 19:23:47 -0700 Subject: [PATCH 47/55] Housekeeping. - Added some more notes to config.h. - Added the ability to override some of the #defines around Grbl in config.h, like planner buffer size, line buffer size, serial send/receive buffers. Mainly to centralize the configurations to be able to port to different microcontrollers later. --- config.h | 31 ++++++++++++++++++++++++++++++- limits.c | 1 + limits.h | 2 -- planner.h | 4 +++- protocol.h | 4 +++- serial.h | 9 +++++++-- stepper.c | 3 +++ stepper.h | 4 ---- 8 files changed, 47 insertions(+), 11 deletions(-) diff --git a/config.h b/config.h index c0f9ad6..72aad6f 100755 --- a/config.h +++ b/config.h @@ -27,6 +27,7 @@ #define BAUD_RATE 9600 // Define pin-assignments +// NOTE: All step bit and direction pins must be on the same port. #define STEPPING_DDR DDRD #define STEPPING_PORT PORTD #define X_STEP_BIT 2 // Uno Digital Pin 2 @@ -44,6 +45,7 @@ #define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8 #define STEPPERS_DISABLE_MASK (1< -// Some useful constants -#define TICKS_PER_MICROSECOND (F_CPU/1000000) -#define CYCLES_PER_ACCELERATION_TICK ((TICKS_PER_MICROSECOND*1000000)/ACCELERATION_TICKS_PER_SECOND) - // Initialize and setup the stepper motor subsystem void st_init(); From 5844172af13e11b03c8b80f6d6ebf2b6b64ff181 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Fri, 9 Nov 2012 16:25:42 -0700 Subject: [PATCH 48/55] Fixed homing cycle hanging after locating switches. --- motion_control.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/motion_control.c b/motion_control.c index 995d27c..431aac3 100755 --- a/motion_control.c +++ b/motion_control.c @@ -217,6 +217,8 @@ void mc_go_home() if (sys.abort) { sys.state = STATE_ALARM; // Homing routine did not complete. return; + } else { + sys.state = STATE_IDLE; // Otherwise, homed and zero out machine position. } // The machine should now be homed and machine zero has been located. Upon completion, @@ -242,7 +244,7 @@ void mc_go_home() // If hard limits feature enabled, re-enable hard limits interrupt after homing cycle. if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { PCICR |= (1 << LIMIT_INT); } - sys.state = STATE_IDLE; // Finished! + // Finished! } From 978de77c2f6a4ca15625d95946650875d3cef53e Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Fri, 9 Nov 2012 18:33:38 -0700 Subject: [PATCH 49/55] Added note that D13 can't be used as input, pulled-high. --- config.h | 2 +- motion_control.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/config.h b/config.h index 72aad6f..211c34b 100755 --- a/config.h +++ b/config.h @@ -63,7 +63,7 @@ #define SPINDLE_DIRECTION_DDR DDRB #define SPINDLE_DIRECTION_PORT PORTB -#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 +#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.) #define COOLANT_FLOOD_DDR DDRC #define COOLANT_FLOOD_PORT PORTC diff --git a/motion_control.c b/motion_control.c index 431aac3..27c2cc5 100755 --- a/motion_control.c +++ b/motion_control.c @@ -217,14 +217,13 @@ void mc_go_home() if (sys.abort) { sys.state = STATE_ALARM; // Homing routine did not complete. return; - } else { - sys.state = STATE_IDLE; // Otherwise, homed and zero out machine position. } // The machine should now be homed and machine zero has been located. Upon completion, // reset system position and sync internal position vectors. clear_vector_float(sys.position); // Set machine zero sys_sync_current_position(); + sys.state = STATE_IDLE; // Set system state to IDLE to complete motion and indicate homed. // Pull-off all axes from limit switches before continuing motion. This provides some initial // clearance off the switches and should also help prevent them from falsely tripping when From e6ad15b548c65bf64c5fc733329e0ed9ebfc0e27 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sat, 10 Nov 2012 12:49:33 -0700 Subject: [PATCH 50/55] Tweaks. Seek rate updates when set. CCW arc full circle fix. - Fixed a minor issue where the seek rates would not immediately be used and only would after a reset. Should update live now. - A full circle IJ offset CCW arc would not do anything. Fixed bug via a simple if-then statement. - Radius mode tweaks to check for negative value in sqrt() rather than isnan() it. Error report updated to indicate what actually happened. --- gcode.c | 18 ++++++++++-------- gcode.h | 2 +- motion_control.c | 7 +++++-- report.c | 4 ++-- report.h | 2 +- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/gcode.c b/gcode.c index 691118f..029e022 100755 --- a/gcode.c +++ b/gcode.c @@ -52,7 +52,7 @@ void gc_init() { memset(&gc, 0, sizeof(gc)); gc.feed_rate = settings.default_feed_rate; // Should be zero at initialization. - gc.seek_rate = settings.default_seek_rate; +// gc.seek_rate = settings.default_seek_rate; select_plane(X_AXIS, Y_AXIS, Z_AXIS); gc.absolute_mode = true; @@ -323,14 +323,14 @@ uint8_t gc_execute_line(char *line) target[i] = gc.position[i]; } } - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); + mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); } // Retreive G28/30 go-home position data (in machine coordinates) from EEPROM float coord_data[N_AXIS]; uint8_t home_select = SETTING_INDEX_G28; if (non_modal_action == NON_MODAL_GO_HOME_1) { home_select = SETTING_INDEX_G30; } if (!settings_read_coord_data(home_select,coord_data)) { return(STATUS_SETTING_READ_FAIL); } - mc_line(coord_data[X_AXIS], coord_data[Y_AXIS], coord_data[Z_AXIS], gc.seek_rate, false); + mc_line(coord_data[X_AXIS], coord_data[Y_AXIS], coord_data[Z_AXIS], settings.default_seek_rate, false); axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. break; case NON_MODAL_SET_HOME_0: case NON_MODAL_SET_HOME_1: @@ -400,7 +400,7 @@ uint8_t gc_execute_line(char *line) break; case MOTION_MODE_SEEK: if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);} - else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); } + else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); } break; case MOTION_MODE_LINEAR: // TODO: Inverse time requires F-word with each statement. Need to do a check. Also need @@ -474,10 +474,12 @@ uint8_t gc_execute_line(char *line) float y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1]; clear_vector(offset); - float h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y); // == -(h * 2 / d) - // If r is smaller than d, the arc is now traversing the complex plane beyond the reach of any - // real CNC, and thus - for practical reasons - we will terminate promptly: - if(isnan(h_x2_div_d)) { FAIL(STATUS_FLOATING_POINT_ERROR); return(gc.status_code); } + // First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller + // than d. If so, the sqrt of a negative number is complex and error out. + float h_x2_div_d = 4 * r*r - x*x - y*y; + if (h_x2_div_d < 0) { FAIL(STATUS_ARC_RADIUS_ERROR); return(gc.status_code); } + // Finish computing h_x2_div_d. + h_x2_div_d = -sqrt(h_x2_div_d)/hypot(x,y); // == -(h * 2 / d) // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below) if (gc.motion_mode == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; } diff --git a/gcode.h b/gcode.h index 4618744..8315ed2 100755 --- a/gcode.h +++ b/gcode.h @@ -83,7 +83,7 @@ typedef struct { int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9} float feed_rate; // Millimeters/min - float seek_rate; // Millimeters/min - Can change depending on switches and such. +// float seek_rate; // Millimeters/min. Will be used in v0.9 when axis independence is installed float position[3]; // Where the interpreter considers the tool to be at this point in the code uint8_t tool; // uint16_t spindle_speed; // RPM/100 diff --git a/motion_control.c b/motion_control.c index 27c2cc5..c80e557 100755 --- a/motion_control.c +++ b/motion_control.c @@ -105,8 +105,11 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8 // CCW angle between position and target from circle center. Only one atan2() trig computation required. float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1); - if (angular_travel < 0) { angular_travel += 2*M_PI; } - if (isclockwise) { angular_travel -= 2*M_PI; } + if (isclockwise) { // Correct atan2 output per direction + if (angular_travel >= 0) { angular_travel -= 2*M_PI; } + } else { + if (angular_travel <= 0) { angular_travel += 2*M_PI; } + } float millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel)); if (millimeters_of_travel == 0.0) { return; } diff --git a/report.c b/report.c index 16b906d..f5dd4f6 100644 --- a/report.c +++ b/report.c @@ -56,8 +56,8 @@ void report_status_message(uint8_t status_code) printPgmString(PSTR("Expected command letter")); break; case STATUS_UNSUPPORTED_STATEMENT: printPgmString(PSTR("Unsupported statement")); break; - case STATUS_FLOATING_POINT_ERROR: - printPgmString(PSTR("Float error")); break; + case STATUS_ARC_RADIUS_ERROR: + printPgmString(PSTR("Invalid radius")); break; case STATUS_MODAL_GROUP_VIOLATION: printPgmString(PSTR("Modal group violation")); break; case STATUS_INVALID_STATEMENT: diff --git a/report.h b/report.h index 4d8c471..b79e1af 100644 --- a/report.h +++ b/report.h @@ -26,7 +26,7 @@ #define STATUS_BAD_NUMBER_FORMAT 1 #define STATUS_EXPECTED_COMMAND_LETTER 2 #define STATUS_UNSUPPORTED_STATEMENT 3 -#define STATUS_FLOATING_POINT_ERROR 4 +#define STATUS_ARC_RADIUS_ERROR 4 #define STATUS_MODAL_GROUP_VIOLATION 5 #define STATUS_INVALID_STATEMENT 6 #define STATUS_HARD_LIMIT 7 From 559feb97e23b783883c8aef549d3dc330f9171b9 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Wed, 14 Nov 2012 17:36:29 -0700 Subject: [PATCH 51/55] Re-factored system states and alarm management. Serial baud support greater than 57600. - Refactored system states to be more clear and concise. Alarm locks processes when position is unknown to indicate to user something has gone wrong. - Changed mc_alarm to mc_reset, which now manages the system reset function. Centralizes it. - Renamed '$X' kill homing lock to kill alarm lock. - Created an alarm error reporting method to clear up what is an alarm: message vs a status error: message. For GUIs mainly. Alarm codes are negative. Status codes are positive. - Serial baud support upto 115200. Previous baudrate calc was unstable for 57600 and above. - Alarm state locks out all g-code blocks, including startup scripts, but allows user to access settings and internal commands. For example, to disable hard limits, if they are problematic. - Hard limits do not respond in an alarm state. - Fixed a problem with the hard limit interrupt during the homing cycle. The interrupt register is still active during the homing cycle and still signal the interrupt to trigger when re-enabled. Instead, just disabled the register. - Homing rate adjusted. All axes move at homing seek rate, regardless of how many axes move at the same time. This is unlike how the stepper module does it as a point to point rate. - New config.h settings to disable the homing rate adjustment and the force homing upon powerup. - Reduced the number of startup lines back down to 2 from 3. This discourages users from placing motion block in there, which can be very dangerous. - Startup blocks now run only after an alarm-free reset or after a homing cycle. Does not run when $X kill is called. For satefy reasons --- config.h | 16 +- gcode.c | 7 +- limits.c | 53 ++++-- main.c | 42 +++-- motion_control.c | 51 +++--- motion_control.h | 4 +- nuts_bolts.c | 2 +- nuts_bolts.h | 20 +-- protocol.c | 77 ++++---- report.c | 106 +++++++----- report.h | 35 ++-- serial.c | 28 ++- serial.h | 2 +- settings.c | 443 +++++++++++++++++++++++------------------------ 14 files changed, 466 insertions(+), 420 deletions(-) diff --git a/config.h b/config.h index 211c34b..6fe5620 100755 --- a/config.h +++ b/config.h @@ -69,9 +69,9 @@ #define COOLANT_FLOOD_PORT PORTC #define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3 -// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable. // NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at // a later date if flash and memory space allows. +// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable. #ifdef ENABLE_M7 #define COOLANT_MIST_DDR DDRC #define COOLANT_MIST_PORT PORTC @@ -158,6 +158,18 @@ // time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays. #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) +// 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 +// mainly a safety feature to remind the user to home, since position is unknown to Grbl. +#define HOMING_INIT_LOCK // Comment to disable + +// The homing cycle seek and feed rates will adjust so all axes independently move at the homing +// seek and feed rates regardless of how many axes are in motion simultaneously. If disabled, rates +// are point-to-point rates, as done in normal operation. For example in an XY diagonal motion, the +// diagonal motion moves at the intended rate, but the individual axes move at 70% speed. This option +// just moves them all at 100% speed. +#define HOMING_RATE_ADJUST // Comment to disable + // Number of homing cycles performed after when the machine initially jogs to limit switches. // This help in preventing overshoot and should improve repeatability. This value should be one or // greater. @@ -167,7 +179,7 @@ // and addresses are defined in settings.h. With the current settings, up to 5 startup blocks may // be stored and executed in order. These startup blocks would typically be used to set the g-code // parser state depending on user preferences. -#define N_STARTUP_LINE 3 // Integer (1-5) +#define N_STARTUP_LINE 2 // Integer (1-5) // --------------------------------------------------------------------------------------- // FOR ADVANCED USERS ONLY: diff --git a/gcode.c b/gcode.c index 029e022..9f145a2 100755 --- a/gcode.c +++ b/gcode.c @@ -84,6 +84,11 @@ static float to_millimeters(float value) // internal functions in terms of (mm, mm/min) and absolute machine coordinates, respectively. uint8_t gc_execute_line(char *line) { + + // If in alarm state, don't process. Immediately return with error. + // NOTE: Might not be right place for this, but also prevents $N storing during alarm. + if (sys.state == STATE_ALARM) { return(STATUS_ALARM_LOCK); } + uint8_t char_counter = 0; char letter; float value; @@ -545,7 +550,7 @@ uint8_t gc_execute_line(char *line) // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9). Otherwise, // re-enable program flow after pause complete, where cycle start will resume the program. - if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { sys.execute |= EXEC_RESET; } + if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { mc_reset(); } else { gc.program_flow = PROGRAM_FLOW_RUNNING; } } diff --git a/limits.c b/limits.c index bc0cf40..2548cd4 100755 --- a/limits.c +++ b/limits.c @@ -41,30 +41,41 @@ void limits_init() LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { - LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt - PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt + LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt + PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt + } else { + LIMIT_PCMSK &= ~LIMIT_MASK; // Disable + PCICR &= ~(1 << LIMIT_INT); } } -// This is the Limit Pin Change Interrupt, which handles the hard limit feature. +// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing +// limit switch can cause a lot of problems, like false readings and multiple interrupt calls. +// If a switch is triggered at all, something bad has happened and treat it as such, regardless +// if a limit switch is being disengaged. It's impossible to reliably tell the state of a +// bouncing pin without a debouncing method. // NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during // 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 // your e-stop switch to the Arduino reset pin, since it is the most correct way to do this. -// TODO: This interrupt may be used to manage the homing cycle directly with the main stepper -// interrupt without adding too much to it. All it would need is some way to stop one axis -// when its limit is triggered and continue the others. This may reduce some of the code, but -// would make Grbl a little harder to read and understand down road. Holding off on this until -// we move on to new hardware or flash space becomes an issue. If it ain't broke, don't fix it. ISR(LIMIT_INT_vect) { - // Only enter if the system alarm is not active. - if (bit_isfalse(sys.execute,EXEC_ALARM)) { - // Kill all processes upon hard limit event. - if ((LIMIT_PIN & LIMIT_MASK) ^ LIMIT_MASK) { - mc_alarm(); // Initiate system kill. - sys.state = STATE_LIMIT; // Set system state to indicate event. - } + // TODO: This interrupt may be used to manage the homing cycle directly with the main stepper + // interrupt without adding too much to it. All it would need is some way to stop one axis + // when its limit is triggered and continue the others. This may reduce some of the code, but + // would make Grbl a little harder to read and understand down road. Holding off on this until + // we move on to new hardware or flash space becomes an issue. If it ain't broke, don't fix it. + + // 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 + } } } @@ -104,6 +115,11 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, // Compute the adjusted step rate change with each acceleration tick. (in step/min/acceleration_tick) uint32_t delta_rate = ceil( ds*settings.acceleration/(60*ACCELERATION_TICKS_PER_SECOND)); + #ifdef HOMING_RATE_ADJUST + // Adjust homing rate so a multiple axes moves all at the homing rate independently. + homing_rate *= sqrt(x_axis+y_axis+z_axis); + #endif + // Nominal and initial time increment per step. Nominal should always be greater then 3 // usec, since they are based on the same parameters as the main stepper routine. Initial // is based on the MINIMUM_STEPS_PER_MINUTE config. Since homing feed can be very slow, @@ -191,12 +207,13 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, void limits_go_home() { - // Enable only the steppers, not the cycle. Cycle should be complete. + // Enable only the steppers, not the cycle. Cycle should be inactive/complete. st_wake_up(); // Jog all axes toward home to engage their limit switches at faster homing seek rate. - homing_cycle(false, false, true, true, false, settings.homing_seek_rate); // First jog the z axis - homing_cycle(true, true, false, true, false, settings.homing_seek_rate); // Then jog the x and y axis + // First jog z-axis to clear workspace, then jog the x and y axis. + homing_cycle(false, false, true, true, false, settings.homing_seek_rate); // z-axis + homing_cycle(true, true, false, true, false, settings.homing_seek_rate); // xy-axes delay_ms(settings.homing_debounce_delay); // Delay to debounce signal // Now in proximity of all limits. Carefully leave and approach switches in multiple cycles diff --git a/main.c b/main.c index 9174d52..c03735f 100755 --- a/main.c +++ b/main.c @@ -45,14 +45,14 @@ system_t sys; int main(void) { // Initialize system - serial_init(BAUD_RATE); // Setup serial baud rate and interrupts + serial_init(); // Setup serial baud rate and interrupts settings_init(); // Load grbl settings from EEPROM st_init(); // Setup stepper pins and interrupt timers sei(); // Enable interrupts memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization - sys.state = STATE_ALARM; // Set alarm state to indicate unknown initial position + sys.state = STATE_INIT; // Set alarm state to indicate unknown initial position for(;;) { @@ -60,7 +60,6 @@ int main(void) // 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) { - // Reset system. serial_reset_read_buffer(); // Clear serial read buffer plan_init(); // Clear block buffer and planner variables @@ -72,28 +71,33 @@ int main(void) st_reset(); // Clear stepper subsystem variables. // Sync cleared gcode and planner positions to current system position, which is only - // cleared upon startup, not a reset/abort. If Grbl does not know or can ensure its - // position, a feedback message will be sent back to the user to let them know. Also, - // if position is lost and homing is enabled, the axes motions will be locked, and - // user must either perform the homing cycle '$H' or purge the system locks '$P' to - // resume. + // cleared upon startup, not a reset/abort. sys_sync_current_position(); // Reset system variables. sys.abort = false; sys.execute = 0; - if (sys.state == STATE_ALARM && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { - // Position has either been lost from a critical event or a power cycle reboot. If - // homing is enabled, force user to home to get machine position. Otherwise, let the - // user manage position on their own. - report_feedback_message(MESSAGE_HOMING_ALARM); - } else { - sys.state = STATE_IDLE; - } if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } - - // Execute user startup script - protocol_execute_startup(); + + // 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. + if (sys.state == STATE_ALARM) { + report_feedback_message(MESSAGE_ALARM_LOCK); + } else { + // All systems go. Set system to ready and execute startup script. + sys.state = STATE_IDLE; + protocol_execute_startup(); + } } protocol_execute_runtime(); diff --git a/motion_control.c b/motion_control.c index c80e557..6cc000f 100755 --- a/motion_control.c +++ b/motion_control.c @@ -67,10 +67,8 @@ void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rat if (bit_isfalse(gc.switches,BITFLAG_CHECK_GCODE)) { plan_buffer_line(x, y, z, feed_rate, invert_feed_rate); - // Indicate to the system there is now a planned block in the buffer ready to cycle start. - // NOTE: If homing cycles are enabled, a position lost state will lock out all motions, - // until a homing cycle has been completed. This is a safety feature to help prevent - // the machine from crashing. + // If idle, indicate to the system there is now a planned block in the buffer ready to cycle + // start. Otherwise ignore and continue on. if (!sys.state) { sys.state = STATE_QUEUED; } // Auto-cycle start immediately after planner finishes. Enabled/disabled by grbl settings. During @@ -214,13 +212,10 @@ void mc_dwell(float seconds) void mc_go_home() { sys.state = STATE_HOMING; // Set system state variable - PCICR &= ~(1 << LIMIT_INT); // Disable hard limits pin change interrupt + LIMIT_PCMSK &= ~LIMIT_MASK; // Disable hard limits pin change register for cycle duration limits_go_home(); // Perform homing routine. - if (sys.abort) { - sys.state = STATE_ALARM; // Homing routine did not complete. - return; - } + if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. // The machine should now be homed and machine zero has been located. Upon completion, // reset system position and sync internal position vectors. @@ -237,32 +232,42 @@ void mc_go_home() if (bit_istrue(settings.homing_dir_mask,bit(Y_DIRECTION_BIT))) { y_dir = 1; } if (bit_istrue(settings.homing_dir_mask,bit(Z_DIRECTION_BIT))) { z_dir = 1; } mc_line(x_dir*settings.homing_pulloff, y_dir*settings.homing_pulloff, - z_dir*settings.homing_pulloff, settings.homing_feed_rate, false); + z_dir*settings.homing_pulloff, settings.homing_seek_rate, false); st_cycle_start(); // Move it. Nothing should be in the buffer except this motion. plan_synchronize(); // Make sure the motion completes. // The gcode parser position was circumvented by the pull-off maneuver, so sync position vectors. sys_sync_current_position(); - // If hard limits feature enabled, re-enable hard limits interrupt after homing cycle. - if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { PCICR |= (1 << LIMIT_INT); } + // 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; } // Finished! } -// Method to immediately kill all motion and set system alarm. Used by system abort, hard limits, -// and upon g-code parser error (when installed). -void mc_alarm() +// Method to ready the system to reset by setting the runtime reset command and killing any +// active processes in the system. This also checks if a system reset is issued while Grbl +// is in a motion state. If so, kills the steppers and sets the system alarm to flag position +// lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by +// runtime abort command and hard limits. So, keep to a minimum. +void mc_reset() { - // Only this function can set the system alarm. This is done to prevent multiple kill calls - // by different processes. - if (bit_isfalse(sys.execute, EXEC_ALARM)) { - // Set system alarm flag to have the main program check for anything wrong with shutting - // down the system. - sys.execute |= EXEC_ALARM; - // Immediately force stepper, spindle, and coolant to stop. - st_go_idle(); + // Only this function can set the system reset. Helps prevent multiple kill calls. + if (bit_isfalse(sys.execute, EXEC_RESET)) { + sys.execute |= EXEC_RESET; + + // Kill spindle and coolant. spindle_stop(); coolant_stop(); + + // Kill steppers only if in any motion state, i.e. cycle, feed hold, homing, or jogging + // 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 + // violated, by which, all bets are off. + switch (sys.state) { + case STATE_CYCLE: case STATE_HOLD: case STATE_HOMING: // case STATE_JOG: + sys.execute |= EXEC_ALARM; // Execute alarm state. + st_go_idle(); // Execute alarm force kills steppers. Position likely lost. + } } } diff --git a/motion_control.h b/motion_control.h index cd35f70..20ecf08 100755 --- a/motion_control.h +++ b/motion_control.h @@ -43,7 +43,7 @@ void mc_dwell(float seconds); // Perform homing cycle to locate machine zero. Requires limit switches. void mc_go_home(); -// Kills all motion and sets system alarm -void mc_alarm(); +// Performs system reset. If in motion state, kills all motion and sets system alarm. +void mc_reset(); #endif diff --git a/nuts_bolts.c b/nuts_bolts.c index 921ac9a..a244bfb 100755 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -140,7 +140,7 @@ void delay_us(uint32_t us) } } - +// Syncs all internal position vectors to the current system position. void sys_sync_current_position() { plan_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); diff --git a/nuts_bolts.h b/nuts_bolts.h index 7b00210..1f588ca 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -63,31 +63,29 @@ #define EXEC_FEED_HOLD bit(3) // bitmask 00001000 #define EXEC_RESET bit(4) // bitmask 00010000 #define EXEC_ALARM bit(5) // bitmask 00100000 -// #define bit(6) // bitmask 01000000 +#define EXEC_CRIT_EVENT bit(6) // bitmask 01000000 // #define bit(7) // bitmask 10000000 // 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 // critical events. #define STATE_IDLE 0 // Must be zero. -#define STATE_QUEUED 1 // Indicates buffered blocks, awaiting cycle start. -#define STATE_CYCLE 2 // Cycle is running -#define STATE_HOLD 3 // Executing feed hold -#define STATE_HOMING 4 // Performing homing cycle -#define STATE_JOG 5 // Jogging mode is unique like homing. -#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes and messages position lost -#define STATE_LIMIT 7 // Used to message hard limit triggered. +#define STATE_INIT 1 // Initial power up state. +#define STATE_QUEUED 2 // Indicates buffered blocks, awaiting cycle start. +#define STATE_CYCLE 3 // Cycle is running +#define STATE_HOLD 4 // Executing feed hold +#define STATE_HOMING 5 // Performing homing cycle +#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes. Allows settings access. +// #define STATE_JOG 7 // Jogging mode is unique like homing. // Define global system variables typedef struct { uint8_t abort; // System abort flag. Forces exit back to main loop for reset. uint8_t state; // Tracks the current state of Grbl. volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. - int32_t position[3]; // Real-time machine (aka home) position vector in steps. + int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps. // NOTE: This may need to be a volatile variable, if problems arise. uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings. -// uint8_t feed_hold; // Feed hold flag. Held true during feed hold. Released when ready to resume. -// volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program. } system_t; extern system_t sys; diff --git a/protocol.c b/protocol.c index 8152aa9..9ece210 100755 --- a/protocol.c +++ b/protocol.c @@ -74,8 +74,7 @@ ISR(PINOUT_INT_vect) // Enter only if any pinout pin is actively low. if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) { if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) { - mc_alarm(); - sys.execute |= EXEC_RESET; // Set as true + mc_reset(); } else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) { sys.execute |= EXEC_FEED_HOLD; } else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) { @@ -93,36 +92,41 @@ ISR(PINOUT_INT_vect) // define more computationally-expensive volatile variables. This also provides a controlled way to // execute certain tasks without having two or more instances of the same task, such as the planner // recalculating the buffer upon a feedhold or override. -// NOTE: The sys.execute variable flags are set by the serial read subprogram, except where noted, -// but may be set by any process, such as a switch pin change interrupt when pinouts are installed. +// NOTE: The sys.execute variable flags are set by any process, step or serial interrupts, pinouts, +// limit switches, or the main program. void protocol_execute_runtime() { if (sys.execute) { // Enter only if any bit flag is true uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times - // System alarm. Everything has shutdown by either something that has gone wrong or issuing - // the Grbl soft-reset/abort. Check the system states to report any critical error found. - // If critical, disable Grbl by entering an infinite loop until system reset/abort. - // NOTE: The system alarm state is also used to set - if (rt_exec & EXEC_ALARM) { - // Limit switch critical event. Lock out Grbl until reset. - if (sys.state == STATE_LIMIT) { - report_status_message(STATUS_HARD_LIMIT); - sys.state = STATE_ALARM; + // System alarm. Everything has shutdown by something that has gone severely wrong. Report + // the source of the error to the user. If critical, Grbl disables by entering an infinite + // loop until system reset/abort. + if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) { + sys.state = STATE_ALARM; // Set system alarm state + + // Critical event. Only hard limit qualifies. Update this as new critical events surface. + if (rt_exec & EXEC_CRIT_EVENT) { + report_alarm_message(ALARM_HARD_LIMIT); report_feedback_message(MESSAGE_CRITICAL_EVENT); - while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } + bit_false(sys.execute,EXEC_RESET); // Disable any existing reset + do { + // Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits + // typically occur while unattended or not paying attention. Gives the user time + // to do what is needed before resetting, like killing the incoming stream. + } while (bit_isfalse(sys.execute,EXEC_RESET)); + + // Standard alarm event. Only abort during motion qualifies. + } else { + // Runtime abort command issued during a cycle, feed hold, or homing cycle. Message the + // user that position may have been lost and set alarm state to enable the alarm lockout + // to indicate the possible severity of the problem. + report_alarm_message(ALARM_ABORT_CYCLE); } - - // Check if a runtime abort command was issued during a cycle. If so, message the user - // that position may have been lost and set alarm state to force re-homing, if enabled. - if (sys.state == STATE_CYCLE) { - report_status_message(STATUS_ABORT_CYCLE); - sys.state = STATE_ALARM; - } - bit_false(sys.execute,EXEC_ALARM); + bit_false(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT)); } - // System abort. Steppers have already been force stopped. + // Execute system abort. if (rt_exec & EXEC_RESET) { sys.abort = true; // Only place this is set true. return; // Nothing else to do but exit. @@ -194,8 +198,10 @@ uint8_t protocol_execute_line(char *line) case 'H' : // Perform homing cycle if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { // Only perform homing if Grbl is idle or lost. - if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) { mc_go_home(); } - else { return(STATUS_IDLE_ERROR); } + if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) { + mc_go_home(); + if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing. + } else { return(STATUS_IDLE_ERROR); } } else { return(STATUS_SETTING_DISABLED); } break; // case 'J' : break; // Jogging methods @@ -224,20 +230,19 @@ uint8_t protocol_execute_line(char *line) // Perform reset when toggling off. Check g-code mode should only work if Grbl // is idle and ready, regardless of homing locks. This is mainly to keep things // simple and consistent. - if ( bit_istrue(gc.switches,helper_var) ) { sys.execute |= EXEC_RESET; } + if ( bit_istrue(gc.switches,helper_var) ) { mc_reset(); } else if (sys.state) { return(STATUS_IDLE_ERROR); } } gc.switches ^= helper_var; if (bit_istrue(gc.switches,helper_var)) { report_feedback_message(MESSAGE_ENABLED); } else { report_feedback_message(MESSAGE_DISABLED); } break; - case 'X' : // Disable homing lock + case 'X' : // Disable alarm lock if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } if (sys.state == STATE_ALARM) { - report_feedback_message(MESSAGE_HOMING_UNLOCK); + report_feedback_message(MESSAGE_ALARM_UNLOCK); sys.state = STATE_IDLE; - } else { - return(STATUS_SETTING_DISABLED); + // Don't run startup script. Prevents stored moves in startup from causing accidents. } break; case 'N' : // Startup lines. @@ -279,17 +284,7 @@ uint8_t protocol_execute_line(char *line) return(STATUS_OK); // If '$' command makes it to here, then everything's ok. } else { - - // If homing is enabled and position is lost, lock out all g-code commands. - if (sys.state != STATE_ALARM) { - return(gc_execute_line(line)); // Everything else is gcode - // TODO: Install option to set system alarm upon any error code received back from the - // the g-code parser. This is a common safety feature on CNCs to help prevent crashes - // if the g-code doesn't perform as intended. - } else { - return(STATUS_HOMING_LOCK); - } - + return(gc_execute_line(line)); // Everything else is gcode } } diff --git a/report.c b/report.c index f5dd4f6..485d240 100644 --- a/report.c +++ b/report.c @@ -62,8 +62,6 @@ void report_status_message(uint8_t status_code) printPgmString(PSTR("Modal group violation")); break; case STATUS_INVALID_STATEMENT: printPgmString(PSTR("Invalid statement")); break; - case STATUS_HARD_LIMIT: - printPgmString(PSTR("Hard limit. MPos lost?")); break; case STATUS_SETTING_DISABLED: printPgmString(PSTR("Setting disabled")); break; case STATUS_SETTING_VALUE_NEG: @@ -73,37 +71,47 @@ void report_status_message(uint8_t status_code) case STATUS_SETTING_READ_FAIL: printPgmString(PSTR("EEPROM read fail. Using defaults")); break; case STATUS_IDLE_ERROR: - printPgmString(PSTR("Busy or locked")); break; - case STATUS_ABORT_CYCLE: - printPgmString(PSTR("Abort during cycle. MPos lost?")); break; - case STATUS_HOMING_LOCK: - printPgmString(PSTR("Locked until homed")); break; + printPgmString(PSTR("Busy or queued")); break; + case STATUS_ALARM_LOCK: + printPgmString(PSTR("Alarm lock")); break; } printPgmString(PSTR("\r\n")); } } +// Prints alarm messages. +void report_alarm_message(int8_t alarm_code) +{ + printPgmString(PSTR("ALARM: ")); + switch (alarm_code) { + case ALARM_HARD_LIMIT: + printPgmString(PSTR("Hard limit")); break; + case ALARM_ABORT_CYCLE: + printPgmString(PSTR("Abort during cycle")); break; + } + printPgmString(PSTR(". MPos?\r\n")); +} // Prints feedback messages. This serves as a centralized method to provide additional -// user feedback for things that are not of the status message response protocol. These -// are messages such as setup warnings and how to exit alarms. +// user feedback for things that are not of the status/alarm message protocol. These are +// messages such as setup warnings, switch toggling, and how to exit alarms. // NOTE: For interfaces, messages are always placed within brackets. And if silent mode // is installed, the message number codes are less than zero. // TODO: Install silence feedback messages option in settings -void report_feedback_message(int8_t message_code) +void report_feedback_message(uint8_t message_code) { printPgmString(PSTR("[")); switch(message_code) { case MESSAGE_CRITICAL_EVENT: - printPgmString(PSTR("ALARM: Check and reset")); break; - case MESSAGE_HOMING_ALARM: - printPgmString(PSTR("'$H' to home and unlock")); break; + printPgmString(PSTR("Reset to continue")); break; + case MESSAGE_ALARM_LOCK: + printPgmString(PSTR("'$H'|'$X' to unlock")); break; + case MESSAGE_ALARM_UNLOCK: + printPgmString(PSTR("Caution: Unlocked")); break; case MESSAGE_ENABLED: printPgmString(PSTR("Enabled")); break; case MESSAGE_DISABLED: printPgmString(PSTR("Disabled")); break; - case MESSAGE_HOMING_UNLOCK: - printPgmString(PSTR("Unlocked. MPos lost?")); break; } printPgmString(PSTR("]\r\n")); } @@ -127,7 +135,7 @@ void report_grbl_help() { "$S1 (toggle blk del)\r\n" "$S2 (toggle single blk)\r\n" "$S3 (toggle opt stop)\r\n" - "$X (kill homing lock)\r\n" + "$X (kill alarm lock)\r\n" "$H (run homing cycle)\r\n" "~ (cycle start)\r\n" "! (feed hold)\r\n" @@ -287,37 +295,39 @@ void report_startup_line(uint8_t n, char *line) // especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz). void report_realtime_status() { - // **Under construction** Bare-bones status report. Provides real-time machine position relative to - // the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). Eventually - // to be added are distance to go on block, processed block id, and feed rate. Also a settings bitmask - // for a user to select the desired real-time data. - uint8_t i; - int32_t current_position[3]; // Copy current state of the system position variable - memcpy(current_position,sys.position,sizeof(sys.position)); - float print_position[3]; - - // Report machine position - printPgmString(PSTR("MPos:[")); - for (i=0; i<= 2; i++) { - print_position[i] = current_position[i]/settings.steps_per_mm[i]; - if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; } - printFloat(print_position[i]); - if (i < 2) { printPgmString(PSTR(",")); } - else { printPgmString(PSTR("]")); } - } + // **Under construction** Bare-bones status report. Provides real-time machine position relative to + // the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). Eventually + // to be added are distance to go on block, processed block id, and feed rate. Also a settings bitmask + // for a user to select the desired real-time data. + uint8_t i; + int32_t current_position[3]; // Copy current state of the system position variable + memcpy(current_position,sys.position,sizeof(sys.position)); + float print_position[3]; - // Report work position - printPgmString(PSTR(",WPos:[")); - for (i=0; i<= 2; i++) { - if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { - print_position[i] -= (gc.coord_system[i]+gc.coord_offset[i])*INCH_PER_MM; - } else { - print_position[i] -= gc.coord_system[i]+gc.coord_offset[i]; - } - printFloat(print_position[i]); - if (i < 2) { printPgmString(PSTR(",")); } - else { printPgmString(PSTR("]")); } - } - - printPgmString(PSTR("\r\n")); + // TODO: Add Grbl state feedback, i.e. IDLE, RUN, HOLD, HOME, etc. + + // Report machine position + printPgmString(PSTR("MPos:[")); + for (i=0; i<= 2; i++) { + print_position[i] = current_position[i]/settings.steps_per_mm[i]; + if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; } + printFloat(print_position[i]); + if (i < 2) { printPgmString(PSTR(",")); } + else { printPgmString(PSTR("]")); } + } + + // Report work position + printPgmString(PSTR(",WPos:[")); + for (i=0; i<= 2; i++) { + if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { + print_position[i] -= (gc.coord_system[i]+gc.coord_offset[i])*INCH_PER_MM; + } else { + print_position[i] -= gc.coord_system[i]+gc.coord_offset[i]; + } + printFloat(print_position[i]); + if (i < 2) { printPgmString(PSTR(",")); } + else { printPgmString(PSTR("]")); } + } + + printPgmString(PSTR("\r\n")); } diff --git a/report.h b/report.h index b79e1af..885114f 100644 --- a/report.h +++ b/report.h @@ -29,27 +29,32 @@ #define STATUS_ARC_RADIUS_ERROR 4 #define STATUS_MODAL_GROUP_VIOLATION 5 #define STATUS_INVALID_STATEMENT 6 -#define STATUS_HARD_LIMIT 7 -#define STATUS_SETTING_DISABLED 8 -#define STATUS_SETTING_VALUE_NEG 9 -#define STATUS_SETTING_STEP_PULSE_MIN 10 -#define STATUS_SETTING_READ_FAIL 11 -#define STATUS_IDLE_ERROR 12 -#define STATUS_ABORT_CYCLE 13 -#define STATUS_HOMING_LOCK 14 +#define STATUS_SETTING_DISABLED 7 +#define STATUS_SETTING_VALUE_NEG 8 +#define STATUS_SETTING_STEP_PULSE_MIN 9 +#define STATUS_SETTING_READ_FAIL 10 +#define STATUS_IDLE_ERROR 11 +#define STATUS_ALARM_LOCK 12 -// Define Grbl feedback message codes. Less than zero to distinguish message from error. -#define MESSAGE_CRITICAL_EVENT -1 -#define MESSAGE_HOMING_ALARM -2 -#define MESSAGE_ENABLED -3 -#define MESSAGE_DISABLED -4 -#define MESSAGE_HOMING_UNLOCK -5 +// Define Grbl alarm codes. Less than zero to distinguish alarm error from status error. +#define ALARM_HARD_LIMIT -1 +#define ALARM_ABORT_CYCLE -2 + +// Define Grbl feedback message codes. +#define MESSAGE_CRITICAL_EVENT 1 +#define MESSAGE_ALARM_LOCK 2 +#define MESSAGE_ALARM_UNLOCK 3 +#define MESSAGE_ENABLED 4 +#define MESSAGE_DISABLED 5 // Prints system status messages. void report_status_message(uint8_t status_code); +// Prints system alarm messages. +void report_alarm_message(int8_t alarm_code); + // Prints miscellaneous feedback messages. -void report_feedback_message(int8_t message_code); +void report_feedback_message(uint8_t message_code); // Prints welcome message void report_init_message(); diff --git a/serial.c b/serial.c index ee22333..69fa717 100755 --- a/serial.c +++ b/serial.c @@ -49,19 +49,19 @@ volatile uint8_t tx_buffer_tail = 0; } #endif -static void set_baud_rate(long baud) { - uint16_t UBRR0_value = ((F_CPU / 16 + baud / 2) / baud - 1); +void serial_init() +{ + // Set baud rate + #if BAUD_RATE < 57600 + uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1)/2 ; + UCSR0A &= ~(1 << U2X0); // baud doubler off - Only needed on Uno XXX + #else + uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2; + UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200 + #endif UBRR0H = UBRR0_value >> 8; UBRR0L = UBRR0_value; -} - -void serial_init(long baud) -{ - set_baud_rate(baud); - - /* baud doubler off - Only needed on Uno XXX */ - UCSR0A &= ~(1 << U2X0); - + // enable rx and tx UCSR0B |= 1<. -*/ - -#include -#include "protocol.h" -#include "report.h" -#include "stepper.h" -#include "nuts_bolts.h" -#include "settings.h" -#include "eeprom.h" - -settings_t settings; - -// Version 4 outdated settings record -typedef struct { - float steps_per_mm[3]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - float default_seek_rate; - uint8_t invert_mask; - float mm_per_arc_segment; - float acceleration; - float junction_deviation; -} settings_v4_t; - - -// Method to store startup lines into EEPROM -void settings_store_startup_line(uint8_t n, char *line) -{ - uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; - memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); -} - -// Method to store coord data parameters into EEPROM -void settings_write_coord_data(uint8_t coord_select, float *coord_data) -{ - uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; - memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); -} - -// Method to store Grbl global settings struct and version number into EEPROM -void write_global_settings() -{ - eeprom_put_char(0, SETTINGS_VERSION); - memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); -} - -// Method to reset Grbl global settings back to defaults. -void settings_reset(bool reset_all) { - // Reset all settings or only the migration settings to the new version. - if (reset_all) { - settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; - settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; - settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; - settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; - settings.default_feed_rate = DEFAULT_FEEDRATE; - settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE; - settings.acceleration = DEFAULT_ACCELERATION; - settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT; - settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; - } - // New settings since last version - settings.flags = 0; - if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } - if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } - if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } - if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } - if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } - settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; - settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; - settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; - settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; - settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; - settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; - settings.decimal_places = DEFAULT_DECIMAL_PLACES; - settings.n_arc_correction = DEFAULT_N_ARC_CORRECTION; - write_global_settings(); -} - -// Reads startup line from EEPROM. Updated pointed line string data. -uint8_t settings_read_startup_line(uint8_t n, char *line) -{ - uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; - if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { - // Reset line with default value - line[0] = 0; - settings_store_startup_line(n, line); - return(false); - } else { - return(true); - } -} - -// Read selected coordinate data from EEPROM. Updates pointed coord_data value. -uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) -{ - uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; - if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { - // Reset with default zero vector - clear_vector_float(coord_data); - settings_write_coord_data(coord_select,coord_data); - return(false); - } else { - return(true); - } -} - -// Reads Grbl global settings struct from EEPROM. -uint8_t read_global_settings() { - // Check version-byte of eeprom - uint8_t version = eeprom_get_char(0); - - if (version == SETTINGS_VERSION) { - // Read settings-record and check checksum - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { - return(false); - } - } else { - if (version <= 4) { - // Migrate from settings version 4 to current version. - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { - return(false); - } - settings_reset(false); // Old settings ok. Write new settings only. - } else { - return(false); - } - } - return(true); -} - - -// A helper method to set settings from command line -uint8_t settings_store_global_setting(int parameter, float value) { - switch(parameter) { - case 0: case 1: case 2: - if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } - settings.steps_per_mm[parameter] = value; break; - case 3: - if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } - settings.pulse_microseconds = round(value); break; - case 4: settings.default_feed_rate = value; break; - case 5: settings.default_seek_rate = value; break; - case 6: settings.invert_mask = trunc(value); break; - case 7: settings.stepper_idle_lock_time = round(value); break; - case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. - case 9: settings.junction_deviation = fabs(value); break; - case 10: settings.mm_per_arc_segment = value; break; - case 11: settings.n_arc_correction = round(value); break; - case 12: settings.decimal_places = round(value); break; - case 13: - if (value) { settings.flags |= BITFLAG_REPORT_INCHES; } - else { settings.flags &= ~BITFLAG_REPORT_INCHES; } - break; - case 14: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_AUTO_START; } - else { settings.flags &= ~BITFLAG_AUTO_START; } - break; - case 15: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } - else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } - break; - case 16: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } - else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } - break; - case 17: - if (value) { - settings.flags |= BITFLAG_HOMING_ENABLE; - sys.state = STATE_ALARM; - report_feedback_message(MESSAGE_HOMING_ALARM); - } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } - break; - case 18: settings.homing_dir_mask = trunc(value); break; - case 19: settings.homing_feed_rate = value; break; - case 20: settings.homing_seek_rate = value; break; - case 21: settings.homing_debounce_delay = round(value); break; - case 22: settings.homing_pulloff = value; break; - default: - return(STATUS_INVALID_STATEMENT); - } - write_global_settings(); - return(STATUS_OK); -} - -// Initialize the config subsystem -void settings_init() { - if(!read_global_settings()) { - report_status_message(STATUS_SETTING_READ_FAIL); - settings_reset(true); - report_grbl_settings(); - } - // Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. - float coord_data[N_AXIS]; - uint8_t i; - for (i=0; i<=SETTING_INDEX_NCOORD; i++) { - if (!settings_read_coord_data(i, coord_data)) { - report_status_message(STATUS_SETTING_READ_FAIL); - } - } - // NOTE: Startup lines are handled and called by main.c at the end of initialization. -} +/* + settings.c - eeprom configuration handling + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011-2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include +#include "protocol.h" +#include "report.h" +#include "stepper.h" +#include "nuts_bolts.h" +#include "settings.h" +#include "eeprom.h" +#include "limits.h" + +settings_t settings; + +// Version 4 outdated settings record +typedef struct { + float steps_per_mm[3]; + uint8_t microsteps; + uint8_t pulse_microseconds; + float default_feed_rate; + float default_seek_rate; + uint8_t invert_mask; + float mm_per_arc_segment; + float acceleration; + float junction_deviation; +} settings_v4_t; + + +// Method to store startup lines into EEPROM +void settings_store_startup_line(uint8_t n, char *line) +{ + uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; + memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); +} + +// Method to store coord data parameters into EEPROM +void settings_write_coord_data(uint8_t coord_select, float *coord_data) +{ + uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; + memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); +} + +// Method to store Grbl global settings struct and version number into EEPROM +void write_global_settings() +{ + eeprom_put_char(0, SETTINGS_VERSION); + memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); +} + +// Method to reset Grbl global settings back to defaults. +void settings_reset(bool reset_all) { + // Reset all settings or only the migration settings to the new version. + if (reset_all) { + settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; + settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; + settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; + settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; + settings.default_feed_rate = DEFAULT_FEEDRATE; + settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE; + settings.acceleration = DEFAULT_ACCELERATION; + settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT; + settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; + settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; + } + // New settings since last version + settings.flags = 0; + if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } + if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } + if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } + if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } + if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } + settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; + settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; + settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; + settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; + settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; + settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; + settings.decimal_places = DEFAULT_DECIMAL_PLACES; + settings.n_arc_correction = DEFAULT_N_ARC_CORRECTION; + write_global_settings(); +} + +// Reads startup line from EEPROM. Updated pointed line string data. +uint8_t settings_read_startup_line(uint8_t n, char *line) +{ + uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; + if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { + // Reset line with default value + line[0] = 0; + settings_store_startup_line(n, line); + return(false); + } else { + return(true); + } +} + +// Read selected coordinate data from EEPROM. Updates pointed coord_data value. +uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) +{ + uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; + if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { + // Reset with default zero vector + clear_vector_float(coord_data); + settings_write_coord_data(coord_select,coord_data); + return(false); + } else { + return(true); + } +} + +// Reads Grbl global settings struct from EEPROM. +uint8_t read_global_settings() { + // Check version-byte of eeprom + uint8_t version = eeprom_get_char(0); + + if (version == SETTINGS_VERSION) { + // Read settings-record and check checksum + if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { + return(false); + } + } else { + if (version <= 4) { + // Migrate from settings version 4 to current version. + if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { + return(false); + } + settings_reset(false); // Old settings ok. Write new settings only. + } else { + return(false); + } + } + return(true); +} + + +// A helper method to set settings from command line +uint8_t settings_store_global_setting(int parameter, float value) { + switch(parameter) { + case 0: case 1: case 2: + if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } + settings.steps_per_mm[parameter] = value; break; + case 3: + if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } + settings.pulse_microseconds = round(value); break; + case 4: settings.default_feed_rate = value; break; + case 5: settings.default_seek_rate = value; break; + case 6: settings.invert_mask = trunc(value); break; + case 7: settings.stepper_idle_lock_time = round(value); break; + case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use. + case 9: settings.junction_deviation = fabs(value); break; + case 10: settings.mm_per_arc_segment = value; break; + case 11: settings.n_arc_correction = round(value); break; + case 12: settings.decimal_places = round(value); break; + case 13: + if (value) { settings.flags |= BITFLAG_REPORT_INCHES; } + else { settings.flags &= ~BITFLAG_REPORT_INCHES; } + break; + case 14: // Reset to ensure change. Immediate re-init may cause problems. + if (value) { settings.flags |= BITFLAG_AUTO_START; } + else { settings.flags &= ~BITFLAG_AUTO_START; } + break; + case 15: // Reset to ensure change. Immediate re-init may cause problems. + if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } + else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } + break; + case 16: + if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } + else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } + limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later. + break; + case 17: + if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; } + else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } + break; + case 18: settings.homing_dir_mask = trunc(value); break; + case 19: settings.homing_feed_rate = value; break; + case 20: settings.homing_seek_rate = value; break; + case 21: settings.homing_debounce_delay = round(value); break; + case 22: settings.homing_pulloff = value; break; + default: + return(STATUS_INVALID_STATEMENT); + } + write_global_settings(); + return(STATUS_OK); +} + +// Initialize the config subsystem +void settings_init() { + if(!read_global_settings()) { + report_status_message(STATUS_SETTING_READ_FAIL); + settings_reset(true); + report_grbl_settings(); + } + // Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. + float coord_data[N_AXIS]; + uint8_t i; + for (i=0; i<=SETTING_INDEX_NCOORD; i++) { + if (!settings_read_coord_data(i, coord_data)) { + report_status_message(STATUS_SETTING_READ_FAIL); + } + } + // NOTE: Startup lines are handled and called by main.c at the end of initialization. +} From 5dd6d90122dce991a99eab5aa3a5c991dd5c938a Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Thu, 15 Nov 2012 21:53:11 -0700 Subject: [PATCH 52/55] Added Grbl state in status report. Removed switch support. - Added Grbl state (Idle, Running, Queued, Hold, etc) to the real-time status reporting feature as feedback to the user of what Grbl is doing. Updated the help message to reflect this change. - Removed switches (dry run, block delete, single block mode). To keep Grbl simple and not muddled up from things that can easily be taken care of by an external interface, these were removed. - Check g-code mode was retained, but the command was moved to '$C' from '$S0'. --- config.h | 2 +- gcode.c | 20 +++++++---------- gcode.h | 11 --------- motion_control.c | 2 +- nuts_bolts.h | 17 +++++++------- protocol.c | 58 ++++++++++++++++++++---------------------------- report.c | 41 ++++++++++++++++------------------ report.h | 2 +- 8 files changed, 63 insertions(+), 90 deletions(-) diff --git a/config.h b/config.h index 6fe5620..b193850 100755 --- a/config.h +++ b/config.h @@ -123,7 +123,7 @@ // 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. 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. +// NOTE: If changed, manually update help message in report.c. #define CMD_STATUS_REPORT '?' #define CMD_FEED_HOLD '!' #define CMD_CYCLE_START '~' diff --git a/gcode.c b/gcode.c index 9f145a2..d9de80e 100755 --- a/gcode.c +++ b/gcode.c @@ -60,8 +60,6 @@ void gc_init() if (!(settings_read_coord_data(gc.coord_select,gc.coord_system))) { report_status_message(STATUS_SETTING_READ_FAIL); } - -// protocol_status_message(settings_execute_startup()); } // Sets g-code parser position in mm. Input in steps. Called by the system abort and hard @@ -176,11 +174,7 @@ uint8_t gc_execute_line(char *line) // Set 'M' commands switch(int_value) { case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause - case 1: // Program pause with optional stop on, otherwise do nothing. - if (bit_istrue(gc.switches,BITFLAG_OPT_STOP)) { - gc.program_flow = PROGRAM_FLOW_PAUSED; - } - break; + case 1: break; // Optional stop not supported. Ignore. case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset case 3: gc.spindle_direction = 1; break; case 4: gc.spindle_direction = -1; break; @@ -260,10 +254,10 @@ uint8_t gc_execute_line(char *line) // ([M6]: Tool change should be executed here.) // [M3,M4,M5]: Update spindle state - if (!(gc.switches & BITFLAG_CHECK_GCODE)) { spindle_run(gc.spindle_direction); } + if (sys.state != STATE_CHECK_MODE) { spindle_run(gc.spindle_direction); } // [*M7,M8,M9]: Update coolant state - if (!(gc.switches & BITFLAG_CHECK_GCODE)) { coolant_run(gc.coolant_mode); } + if (sys.state != STATE_CHECK_MODE) { coolant_run(gc.coolant_mode); } // [G54,G55,...,G59]: Coordinate system selection if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_12)) ) { // Check if called in block @@ -281,7 +275,7 @@ uint8_t gc_execute_line(char *line) FAIL(STATUS_INVALID_STATEMENT); } else { // Ignore dwell in check gcode modes - if (!(gc.switches & BITFLAG_CHECK_GCODE)) { mc_dwell(p); } + if (sys.state != STATE_CHECK_MODE) { mc_dwell(p); } } break; case NON_MODAL_SET_COORDINATE_DATA: @@ -544,7 +538,7 @@ uint8_t gc_execute_line(char *line) // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may // refill and can only be resumed by the cycle start run-time command. - if (gc.program_flow || bit_istrue(gc.switches,BITFLAG_SINGLE_BLOCK)) { + if (gc.program_flow) { plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete. sys.auto_start = false; // Disable auto cycle start. Forces pause until cycle start issued. @@ -588,12 +582,14 @@ static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *c - Evaluation of expressions - Variables - Probing - - Override control + - Override control (TBD) - Tool changes + - Switches (*) Indicates optional parameter, enabled through config.h and re-compile group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets) group 1 = {G38.2, G81 - G89} (Motion modes: straight probe, canned cycles) + group 4 = {M1} (Optional stop, ignored) group 6 = {M6} (Tool change) group 8 = {*M7} enable mist coolant group 9 = {M48, M49} enable/disable feed and speed override switches diff --git a/gcode.h b/gcode.h index 8315ed2..f3671c2 100755 --- a/gcode.h +++ b/gcode.h @@ -62,19 +62,8 @@ #define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92 #define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1 -// Define bit flag masks for gc.switches. (8 flag limit) -#define BITFLAG_CHECK_GCODE bit(0) -#define BITFLAG_BLOCK_DELETE bit(1) -#define BITFLAG_SINGLE_BLOCK bit(2) -#define BITFLAG_OPT_STOP bit(3) -// #define bit(4) -// #define bit(5) -// #define bit(6) -// #define bit(7) - typedef struct { uint8_t status_code; // Parser status for current block - uint8_t switches; // Handles non-gcode switches modes. Set externally by protocol. Default off uint8_t motion_mode; // {G0, G1, G2, G3, G80} uint8_t inverse_feed_rate_mode; // {G93, G94} uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} diff --git a/motion_control.c b/motion_control.c index 6cc000f..d5ba8c9 100755 --- a/motion_control.c +++ b/motion_control.c @@ -64,7 +64,7 @@ void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rat } while ( plan_check_full_buffer() ); // If in check gcode mode, prevent motion by blocking planner. - if (bit_isfalse(gc.switches,BITFLAG_CHECK_GCODE)) { + if (sys.state != STATE_CHECK_MODE) { plan_buffer_line(x, y, z, feed_rate, invert_feed_rate); // If idle, indicate to the system there is now a planned block in the buffer ready to cycle diff --git a/nuts_bolts.h b/nuts_bolts.h index 1f588ca..316c9ce 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -69,14 +69,15 @@ // 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 // critical events. -#define STATE_IDLE 0 // Must be zero. -#define STATE_INIT 1 // Initial power up state. -#define STATE_QUEUED 2 // Indicates buffered blocks, awaiting cycle start. -#define STATE_CYCLE 3 // Cycle is running -#define STATE_HOLD 4 // Executing feed hold -#define STATE_HOMING 5 // Performing homing cycle -#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes. Allows settings access. -// #define STATE_JOG 7 // Jogging mode is unique like homing. +#define STATE_IDLE 0 // Must be zero. +#define STATE_INIT 1 // Initial power up state. +#define STATE_QUEUED 2 // Indicates buffered blocks, awaiting cycle start. +#define STATE_CYCLE 3 // Cycle is running +#define STATE_HOLD 4 // Executing feed hold +#define STATE_HOMING 5 // Performing homing cycle +#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes. Allows settings access. +#define STATE_CHECK_MODE 7 // G-code check mode. Locks out planner and motion only. +// #define STATE_JOG 8 // Jogging mode is unique like homing. // Define global system variables typedef struct { diff --git a/protocol.c b/protocol.c index 9ece210..c97770a 100755 --- a/protocol.c +++ b/protocol.c @@ -195,6 +195,28 @@ uint8_t protocol_execute_line(char *line) if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } else { report_gcode_modes(); } break; + case 'C' : // Set check g-code mode + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + // Perform reset when toggling off. Check g-code mode should only work if Grbl + // is idle and ready, regardless of alarm locks. This is mainly to keep things + // simple and consistent. + if ( sys.state == STATE_CHECK_MODE ) { + mc_reset(); + report_feedback_message(MESSAGE_DISABLED); + } else { + if (sys.state) { return(STATUS_IDLE_ERROR); } + sys.state = STATE_CHECK_MODE; + report_feedback_message(MESSAGE_ENABLED); + } + break; + case 'X' : // Disable alarm lock + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + if (sys.state == STATE_ALARM) { + report_feedback_message(MESSAGE_ALARM_UNLOCK); + sys.state = STATE_IDLE; + // Don't run startup script. Prevents stored moves in startup from causing accidents. + } + break; case 'H' : // Perform homing cycle if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { // Only perform homing if Grbl is idle or lost. @@ -216,35 +238,6 @@ uint8_t protocol_execute_line(char *line) // handled by the planner. It would be possible for the jog subprogram to insert blocks into the // block buffer without having the planner plan them. It would need to manage de/ac-celerations // on its own carefully. This approach could be effective and possibly size/memory efficient. - case 'S' : // Switch modes - // Set helper_var as switch bitmask or clearing flag - switch (line[++char_counter]) { - case '0' : helper_var = BITFLAG_CHECK_GCODE; break; - case '1' : helper_var = BITFLAG_BLOCK_DELETE; break; - case '2' : helper_var = BITFLAG_SINGLE_BLOCK; break; - case '3' : helper_var = BITFLAG_OPT_STOP; break; - default : return(STATUS_INVALID_STATEMENT); - } - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - if ( helper_var & BITFLAG_CHECK_GCODE ) { - // Perform reset when toggling off. Check g-code mode should only work if Grbl - // is idle and ready, regardless of homing locks. This is mainly to keep things - // simple and consistent. - if ( bit_istrue(gc.switches,helper_var) ) { mc_reset(); } - else if (sys.state) { return(STATUS_IDLE_ERROR); } - } - gc.switches ^= helper_var; - if (bit_istrue(gc.switches,helper_var)) { report_feedback_message(MESSAGE_ENABLED); } - else { report_feedback_message(MESSAGE_DISABLED); } - break; - case 'X' : // Disable alarm lock - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - if (sys.state == STATE_ALARM) { - report_feedback_message(MESSAGE_ALARM_UNLOCK); - sys.state = STATE_IDLE; - // Don't run startup script. Prevents stored moves in startup from causing accidents. - } - break; case 'N' : // Startup lines. if ( line[++char_counter] == 0 ) { // Print startup lines for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { @@ -323,11 +316,8 @@ void protocol_process() } else { if (c <= ' ') { // Throw away whitepace and control characters - } else if (c == '/') { - // Disable block delete and throw away characters. Will ignore until EOL. - if (bit_istrue(gc.switches,BITFLAG_BLOCK_DELETE)) { - iscomment = true; - } + } else if (c == '/') { + // Block delete not supported. Ignore character. } else if (c == '(') { // Enable comments flag and ignore all characters until ')' or EOL. iscomment = true; diff --git a/report.c b/report.c index 485d240..e1bf30d 100644 --- a/report.c +++ b/report.c @@ -131,15 +131,12 @@ void report_grbl_help() { "$N (view startup blocks)\r\n" "$x=value (save Grbl setting)\r\n" "$Nx=line (save startup block)\r\n" - "$S0 (toggle check gcode)\r\n" - "$S1 (toggle blk del)\r\n" - "$S2 (toggle single blk)\r\n" - "$S3 (toggle opt stop)\r\n" + "$C (check gcode mode)\r\n" "$X (kill alarm lock)\r\n" "$H (run homing cycle)\r\n" "~ (cycle start)\r\n" "! (feed hold)\r\n" - "? (position)\r\n" + "? (current status)\r\n" "ctrl-x (reset Grbl)\r\n")); } @@ -214,7 +211,7 @@ void report_gcode_parameters() } -// Print current gcode parser mode state and active switches +// Print current gcode parser mode state void report_gcode_modes() { switch (gc.motion_mode) { @@ -268,15 +265,7 @@ void report_gcode_modes() printPgmString(PSTR(" F")); if (gc.inches_mode) { printFloat(gc.feed_rate*INCH_PER_MM); } else { printFloat(gc.feed_rate); } - - // Print active switches - if (gc.switches) { - if (bit_istrue(gc.switches,BITFLAG_CHECK_GCODE)) { printPgmString(PSTR(" $S0")); } - if (bit_istrue(gc.switches,BITFLAG_BLOCK_DELETE)) { printPgmString(PSTR(" $S1")); } - if (bit_istrue(gc.switches,BITFLAG_SINGLE_BLOCK)) { printPgmString(PSTR(" $S2")); } - if (bit_istrue(gc.switches,BITFLAG_OPT_STOP)) { printPgmString(PSTR(" $S3")); } - } - + printPgmString(PSTR("\r\n")); } @@ -304,20 +293,29 @@ void report_realtime_status() memcpy(current_position,sys.position,sizeof(sys.position)); float print_position[3]; - // TODO: Add Grbl state feedback, i.e. IDLE, RUN, HOLD, HOME, etc. + // Report current machine state + switch (sys.state) { + 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_CYCLE: printPgmString(PSTR("[Run")); break; + case STATE_HOLD: printPgmString(PSTR("[Hold")); break; + case STATE_HOMING: printPgmString(PSTR("[Home")); break; + case STATE_ALARM: printPgmString(PSTR("[Alarm")); break; + case STATE_CHECK_MODE: printPgmString(PSTR("[Check")); break; + } // Report machine position - printPgmString(PSTR("MPos:[")); + printPgmString(PSTR(",MPos:")); for (i=0; i<= 2; i++) { print_position[i] = current_position[i]/settings.steps_per_mm[i]; if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; } printFloat(print_position[i]); - if (i < 2) { printPgmString(PSTR(",")); } - else { printPgmString(PSTR("]")); } + printPgmString(PSTR(",")); } // Report work position - printPgmString(PSTR(",WPos:[")); + printPgmString(PSTR("WPos:")); for (i=0; i<= 2; i++) { if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] -= (gc.coord_system[i]+gc.coord_offset[i])*INCH_PER_MM; @@ -326,8 +324,7 @@ void report_realtime_status() } printFloat(print_position[i]); if (i < 2) { printPgmString(PSTR(",")); } - else { printPgmString(PSTR("]")); } } - printPgmString(PSTR("\r\n")); + printPgmString(PSTR("]\r\n")); } diff --git a/report.h b/report.h index 885114f..8f1555c 100644 --- a/report.h +++ b/report.h @@ -71,7 +71,7 @@ void report_realtime_status(); // Prints Grbl persistent coordinate parameters void report_gcode_parameters(); -// Prints current g-code parser mode state and active switches +// Prints current g-code parser mode state void report_gcode_modes(); // Prints startup line From d85238cc9b70abc94fd4f07e720f8090468d383c Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sun, 18 Nov 2012 19:52:16 -0700 Subject: [PATCH 53/55] Homing search sequence now compile-time option. New defaults.h file. Tidying up. - The homing sequence is now a compile-time option, where a user can choose which axes(s) move in sequence during the search phase. Up to 3 sequences. Works with the locating phase and the pull-off maneuver. - New defaults.h file to store user generated default settings for different machines. Mainly to be used as a central repo, but each set may be select to be compiled in as a config.h define. --- config.h | 48 ++++++++----------- defaults.h | 122 +++++++++++++++++++++++++++++++++++++++++++++++ gcode.c | 16 ++++--- limits.c | 56 +++++++++++++--------- motion_control.c | 66 ++++++++++++++----------- nuts_bolts.h | 1 + 6 files changed, 224 insertions(+), 85 deletions(-) create mode 100644 defaults.h diff --git a/config.h b/config.h index b193850..c0bffa4 100755 --- a/config.h +++ b/config.h @@ -24,6 +24,10 @@ // IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them. +// Default settings. Used when resetting EEPROM. Change to desired name in defaults.h +#define DEFAULTS_GENERIC + +// Serial baud rate #define BAUD_RATE 9600 // Define pin-assignments @@ -90,34 +94,6 @@ #define PINOUT_PCMSK PCMSK1 // Pin change interrupt register #define PINOUT_MASK ((1<. +*/ + +/* The defaults.h file serves as a central default settings file for different machine + types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings + here are supplied by users, so your results may vary. However, this should give you + a good starting point as you get to know your machine and tweak the settings for your + our nefarious needs. */ + +#ifndef defaults_h +#define defaults_h + +#ifdef DEFAULTS_GENERIC + // Grbl generic default settings. Should work across different machines. + #define DEFAULT_X_STEPS_PER_MM 250 + #define DEFAULT_Y_STEPS_PER_MM 250 + #define DEFAULT_Z_STEPS_PER_MM 250 + #define DEFAULT_STEP_PULSE_MICROSECONDS 10 + #define DEFAULT_MM_PER_ARC_SEGMENT 0.1 + #define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min + #define DEFAULT_FEEDRATE 250.0 + #define DEFAULT_ACCELERATION 10*60*60 // 10 mm/sec^2 + #define DEFAULT_JUNCTION_DEVIATION 0.05 // mm + #define DEFAULT_STEPPING_INVERT_MASK ((1< 0) { if (limit_state & (1< 0) { if (limit_state & (1< 0) { if (limit_state & (1< 0) { // Re-approach all switches to re-engage them. - homing_cycle(true, true, true, true, false, settings.homing_feed_rate); + homing_cycle(HOMING_LOCATE_CYCLE, true, false, settings.homing_feed_rate); delay_ms(settings.homing_debounce_delay); } } diff --git a/motion_control.c b/motion_control.c index d5ba8c9..2d595c8 100755 --- a/motion_control.c +++ b/motion_control.c @@ -49,6 +49,13 @@ // backlash segment(s). void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate) { + // TODO: Perform soft limit check here. Just check if the target x,y,z values are outside the + // work envelope. Should be straightforward and efficient. By placing it here, rather than in + // the g-code parser, it directly picks up motions from everywhere in Grbl. + + // If in check gcode mode, prevent motion by blocking planner. + if (sys.state == STATE_CHECK_MODE) { return; } + // TODO: Backlash compensation may be installed here. Only need direction info to track when // to insert a backlash line motion(s) before the intended line motion. Requires its own // plan_check_full_buffer() and check for system abort loop. Also for position reporting @@ -62,25 +69,21 @@ void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rat protocol_execute_runtime(); // Check for any run-time commands if (sys.abort) { return; } // Bail, if system abort. } while ( plan_check_full_buffer() ); - - // If in check gcode mode, prevent motion by blocking planner. - if (sys.state != STATE_CHECK_MODE) { - plan_buffer_line(x, y, z, feed_rate, invert_feed_rate); - - // If idle, indicate to the system there is now a planned block in the buffer ready to cycle - // start. Otherwise ignore and continue on. - if (!sys.state) { sys.state = STATE_QUEUED; } - - // 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(); } - } + plan_buffer_line(x, y, z, feed_rate, invert_feed_rate); + + // If idle, indicate to the system there is now a planned block in the buffer ready to cycle + // start. Otherwise ignore and continue on. + if (!sys.state) { sys.state = STATE_QUEUED; } + + // 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(); } } @@ -223,20 +226,29 @@ void mc_go_home() sys_sync_current_position(); sys.state = STATE_IDLE; // Set system state to IDLE to complete motion and indicate homed. - // Pull-off all axes from limit switches before continuing motion. This provides some initial - // clearance off the switches and should also help prevent them from falsely tripping when - // hard limits are enabled. + // Pull-off axes (that have been homed) from limit switches before continuing motion. + // This provides some initial clearance off the switches and should also help prevent them + // from falsely tripping when hard limits are enabled. int8_t x_dir, y_dir, z_dir; - x_dir = y_dir = z_dir = -1; - if (bit_istrue(settings.homing_dir_mask,bit(X_DIRECTION_BIT))) { x_dir = 1; } - if (bit_istrue(settings.homing_dir_mask,bit(Y_DIRECTION_BIT))) { y_dir = 1; } - if (bit_istrue(settings.homing_dir_mask,bit(Z_DIRECTION_BIT))) { z_dir = 1; } + x_dir = y_dir = z_dir = 0; + if (HOMING_LOCATE_CYCLE & (1< #include #include "config.h" +#include "defaults.h" #define false 0 #define true 1 From b3f553653078234f0e5f22ebeccd73d9bf5f8575 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Sun, 18 Nov 2012 21:03:58 -0700 Subject: [PATCH 54/55] Updated readme --- defaults.h | 6 +++--- readme.textile | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/defaults.h b/defaults.h index 4fbd580..3e9e0b4 100644 --- a/defaults.h +++ b/defaults.h @@ -36,7 +36,7 @@ #define DEFAULT_MM_PER_ARC_SEGMENT 0.1 #define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min #define DEFAULT_FEEDRATE 250.0 - #define DEFAULT_ACCELERATION 10*60*60 // 10 mm/sec^2 + #define DEFAULT_ACCELERATION 10*60*60 // 10 mm/min^2 #define DEFAULT_JUNCTION_DEVIATION 0.05 // mm #define DEFAULT_STEPPING_INVERT_MASK ((1< Date: Mon, 19 Nov 2012 17:39:40 -0700 Subject: [PATCH 55/55] Updated interface protocol. Fixed M2 bug. - Updated interface protocol to play nicer with interface programs. All Grbl responses beginning with '$' signifies a setting. Bracketed '[]' responses are feedback messages containing either state, parameter, or general messages. Chevron '<>' response are from the real-time status messages, i.e. position. - M2 Program end command was causing a system alarm. Fixed. Thanks @blinkenlight ! --- defaults.h | 24 ++++++++++++------------ planner.c | 5 +++-- report.c | 52 ++++++++++++++++++++++++++-------------------------- stepper.c | 2 -- 4 files changed, 41 insertions(+), 42 deletions(-) diff --git a/defaults.h b/defaults.h index 3e9e0b4..279e4b9 100644 --- a/defaults.h +++ b/defaults.h @@ -29,14 +29,14 @@ #ifdef DEFAULTS_GENERIC // Grbl generic default settings. Should work across different machines. - #define DEFAULT_X_STEPS_PER_MM 250 - #define DEFAULT_Y_STEPS_PER_MM 250 - #define DEFAULT_Z_STEPS_PER_MM 250 + #define DEFAULT_X_STEPS_PER_MM 250.0 + #define DEFAULT_Y_STEPS_PER_MM 250.0 + #define DEFAULT_Z_STEPS_PER_MM 250.0 #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_MM_PER_ARC_SEGMENT 0.1 #define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min #define DEFAULT_FEEDRATE 250.0 - #define DEFAULT_ACCELERATION 10*60*60 // 10 mm/min^2 + #define DEFAULT_ACCELERATION (10.0*60*60) // 10 mm/min^2 #define DEFAULT_JUNCTION_DEVIATION 0.05 // mm #define DEFAULT_STEPPING_INVERT_MASK ((1<\r\n")); } diff --git a/stepper.c b/stepper.c index 129ccd8..28e6f50 100755 --- a/stepper.c +++ b/stepper.c @@ -420,8 +420,6 @@ static void set_step_events_per_minute(uint32_t steps_per_minute) // Planner external interface to start stepper interrupt and execute the blocks in queue. Called // by the main program functions: planner auto-start and run-time command execution. -// TODO: Update sys.cycle_start and feed_hold variables to a sys.state variable. This state -// variable will manage all of Grbl's processes and keep them separate. void st_cycle_start() { if (sys.state == STATE_QUEUED) {