Settings refactoring. Bug fixes. Misc new features.

This is likely the last major change to the v0.9 code base before push
to master. Only two minor things remain on the agenda (CoreXY support,
force clear EEPROM, and an extremely low federate bug).

- NEW! Grbl is now compile-able and may be flashed directly through the
Arduino IDE. Only minor changes were required for this compatibility.
See the Wiki to learn how to do it.

- New status reporting mask to turn on and off what Grbl sends back.
This includes machine coordinates, work coordinates, serial RX buffer
usage, and planner buffer usage. Expandable to more information on user
request, but that’s it for now.

- Settings have been completely renumbered to allow for future new
settings to be installed without having to constantly reshuffle and
renumber all of the settings every time.

- All settings masks have been standardized to mean bit 0 = X, bit 1 =
Y, and bit 2 = Z, to reduce confusion on how they work. The invert
masks used by the internal Grbl system were updated to accommodate this
change as well.

- New invert probe pin setting, which does what it sounds like.

- Fixed a probing cycle bug, where it would freeze intermittently, and
removed some redundant code.

- Homing may now be set to the origin wherever the limit switches are.
Traditionally machine coordinates should always be in negative space,
but when limit switches on are on the opposite side, the machine
coordinate would be set to -max_travel for the axis. Now you can always
make it [0,0,0] via a compile-time option in config.h. (Soft limits
routine was updated to account for this as well.)

 - Probe coordinate message immediately after a probing cycle may now
be turned off via a compile-time option in config.h. By default the
probing location is always reported.

- Reduced the N_ARC_CORRECTION default value to reflect the changes in
how circles are generated by an arc tolerance, rather than a fixed arc
segment setting.

- Increased the incoming line buffer limit from 70 to 80 characters.
Had some extra memory space to invest into this.

- Fixed a bug where tool number T was not being tracked and reported
correctly.

- Added a print free memory function for debugging purposes. Not used
otherwise.

- Realtime rate report should now work during feed holds, but it hasn’t
been tested yet.

- Updated the streaming scripts with MIT-license and added the simple
streaming to the main stream.py script to allow for settings to be sent.

- Some minor code refactoring to improve flash efficiency. Reduced the
flash by several hundred KB, which was re-invested in some of these new
features.
This commit is contained in:
Sonny Jeon 2014-07-26 15:01:34 -06:00
parent 1c74be0625
commit 71f333ddca
34 changed files with 5686 additions and 562 deletions

View File

@ -13,7 +13,7 @@ Grbl includes full acceleration management with look ahead. That means the contr
* For more information and help, check out our **[Wiki pages!](https://github.com/grbl/grbl/wiki)** If you find that the information is out-dated, please to help us keep it updated by editing it or notifying our community! Thanks!
* Lead Developers: Sungeun "Sonny" Jeon, Ph.D. (2011-2014) and Simen Svale Skogsrud, a.k.a the O.G. (2009-2011)
* Lead Developers: Sonny Jeon, Ph.D. (2011-2014) and Simen Svale Skogsrud, a.k.a the O.G. (2009-2011)
------------
@ -23,6 +23,7 @@ Grbl includes full acceleration management with look ahead. That means the contr
- **_NEW_ Super Smooth Stepper Algorithm:** Complete overhaul of the handling of the stepper driver to simplify and reduce task time per ISR tick. Much smoother operation with the new Adaptive Multi-Axis Step Smoothing (AMASS) algorithm which does what its name implies (see stepper.c source for details). Users should immediately see significant improvements in how their machines move and overall performance!
- **Stability and Robustness Updates:** Grbl's overall stability has been focused on for this version. The planner and step-execution interface has been completely re-written for robustness and incorruptibility by the introduction of an intermediate step segment buffer that "checks-out" steps from the planner buffer in real-time. This means we can now fearlessly drive Grbl to it's highest limits. Combined with the new stepper algorithm and planner optimizations, this translated to **5x to 10x** overall performance increases in our testing! Also, stability and robustness tests have been reported to easily take 1.4 million (yes, **million**) line g-code programs like a champ!
- **(x4)+ Faster Planner:** Planning computations improved four-fold or more by optimizing end-to-end operations, which included streamlining the computations and introducing a planner pointer to locate un-improvable portions of the buffer and not waste cycles recomputing them.
- **Compile-able via Arduino IDE!:** Grbl's source code may be now download and altered, and then be compiled and flashed directly through the Arduino IDE, which should work on all platforms. See the Wiki for details on how to do it.
- **G-Code Parser Overhaul:** Completely re-written from the ground-up for 100%-compliance* to the g-code standard. (* Parts of the NIST standard are a bit out-dated and arbitrary, so we altered some minor things to make more sense. Differences are outlined in the source code.) We also took steps to allow us to break up the g-code parser into distinct separate tasks, which is key for some future development ideas and improvements.
- **Independent Acceleration and Velocity Settings:** Each axes may be defined with unique acceleration and velocity parameters and Grbl will automagically calculate the maximum acceleration and velocity through a path depending on the direction traveled. This is very useful for machines that have very different axes properties, like the ShapeOko's z-axis.
- **Soft Limits:** Checks if any motion command exceeds workspace limits before executing it, and alarms out, if detected. Another safety feature, but, unlike hard limits, position does not get lost, as it forces a feed hold before erroring out. NOTE: This still requires limit switches for homing so Grbl knows where the machine origin is, and the new max axis travel settings configured correctly for the machine.
@ -31,12 +32,28 @@ Grbl includes full acceleration management with look ahead. That means the contr
- **Improved Arc Performance:** The larger the arc radius, the faster Grbl will trace it! We are now defining arcs in terms of arc chordal tolerance, rather than a fixed segment length. This automatically scales the arc segment length such that maximum radial error of the segment from the true arc is never more than the chordal tolerance value of a super-accurate default of 0.002 mm.
- **CPU Pin Mapping:** In an effort for Grbl to be compatible with other AVR architectures, such as the 1280 or 2560, a new cpu_map.h pin configuration file has been created to allow Grbl to be compiled for them. This is currently user supported, so your mileage may vary. If you run across a bug, please let us know or better send us a fix! Thanks in advance!
- **New Grbl SIMULATOR! (by @jgeisler and @ashelly):** A completely independent wrapper of the Grbl main source code that may be compiled as an executable on a computer. No Arduino required. Simply simulates the responses of Grbl as if it was on an Arduino. May be used for many things: checking out how Grbl works, pre-process moves for GUI graphics, debugging of new features, etc. Much left to do, but potentially very powerful, as the dummy AVR variables can be written to output anything you need.
- **Updated Homing Routine:** Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. Now tied directly into the main planner and stepper modules to reduce flash space and allow maximum speeds during seeking.
- **Configurable Real-time Status Reporting:** Users can now customize the type of real-time data Grbl reports back when they issue a '?' status report. This includes data such as: machine position, work position, planner buffer usage, serial RX buffer usage.
- **Updated Homing Routine:** Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. But, the behavior may be changed by a compile-time option though. Now tied directly into the main planner and stepper modules to reduce flash space and allow maximum speeds during seeking.
- **Optional Limit Pin Sharing:** Limit switches can be combined to share the same pins to free up precious I/O pins for other purposes. When combined, users must adjust the homing cycle mask in config.h to not home the axes on a shared pin at the same time. Don't worry; hard limits and the homing cycle still work just like they did before.
- **Optional Variable Spindle Speed Output:** Available only as a compile-time option through the config.h file. Enables PWM output for 'S' g-code commands. Enabling this feature will swap the Z-limit D11 pin and spindle enable D12 pin to access the hardware PWM on pin D12. The Z-limit pin, now on D12, should work just as it did before.
- **Additional Compile-Time Feature Options:** Line number tracking, real-time feed rate reporting.
- **SLATED FOR v1.0 DEVELOPMENT** Jogging controls and feedrate/spindle/coolant overrides. (In v0.9, the framework for feedrate overrides are in-place, only the minor details to complete it have yet to be installed.)
-
```
List of Supported G-Codes in Grbl v0.9
- Non-Modal Commands: G4, G10 L2, G10 L20, G28, G30, G28.1, G30.1, G53, G92, G92.1
- Motion Modes: G0, G1, G2, G3, G38.1, G80
- Feed Rate Modes: G93, G94
- Unit Modes: G20, G21
- Distance Modes: G90, G91
- Plane Select Modes: G17, G18, G19
- Tool Length Offset Modes: G43.1, G49
- Coordinate System Modes: G54, G55, G56, G57, G58, G59
- Program Flow: M0, M1, M2, M30*
- Coolant Control: M7*, M8, M9
- Spindle Control: M3, M4, M5
```
-------------
Grbl is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you!

View File

@ -27,6 +27,8 @@
#ifndef config_h
#define config_h
#include <system.h>
// Default settings. Used when resetting EEPROM. Change to desired name in defaults.h
#define DEFAULTS_GENERIC
@ -78,6 +80,11 @@
// greater.
#define N_HOMING_LOCATE_CYCLE 2 // Integer (1-128)
// After homing, Grbl will set by default the entire machine space into negative space, as is typical
// for professional CNC machines, regardless of where the limit switches are located. Uncomment this
// define to force Grbl to always set the machine origin at the homed location despite switch orientation.
// #define HOMING_FORCE_SET_ORIGIN // Uncomment to enable.
// Number of blocks Grbl executes upon startup. These blocks are stored in EEPROM, where the size
// and addresses are defined in settings.h. With the current settings, up to 3 startup blocks may
// be stored and executed in order. These startup blocks would typically be used to set the g-code
@ -104,6 +111,11 @@
// NOTE: This is experimental and doesn't quite work 100%. Maybe fixed or refactored later.
// #define REPORT_REALTIME_RATE // Disabled by default. Uncomment to enable.
// Upon a successful probe cycle, this option provides immediately feedback of the probe coordinates
// through an automatically generated message. If disabled, users can still access the last probe
// coordinates through Grbl '$#' print parameters.
#define MESSAGE_PROBE_COORDINATES // Enabled by default. Comment to disable.
// Enables a second coolant control pin via the mist coolant g-code command M7 on the Arduino Uno
// analog pin 5. Only use this option if you require a second coolant control pin.
// NOTE: The M8 flood coolant control pin on analog pin 4 will still be functional regardless.
@ -156,12 +168,10 @@
#define MINIMUM_JUNCTION_SPEED 0.0 // (mm/min)
// 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.
// NOTE: Arcs are now generated by a chordal tolerance
#define N_ARC_CORRECTION 20 // Integer (1-255)
// correction with expensive sin() and cos() calcualtions. This parameter maybe decreased if there
// are issues with the accuracy of the arc generations, or increased if arc execution is getting
// bogged down by too many trig calculations.
#define N_ARC_CORRECTION 12 // 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
@ -200,11 +210,11 @@
// each of the startup blocks, as they are each stored as a string of this size. Make sure
// to account for the available EEPROM at the defined memory address in settings.h and for
// the number of desired startup blocks.
// NOTE: 70 characters is not a problem except for extreme cases, but the line buffer size
// NOTE: 80 characters is 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 default will be increased, when
// we know how much extra memory space we can re-invest into this.
// #define LINE_BUFFER_SIZE 70 // Uncomment to override default in protocol.h
// #define LINE_BUFFER_SIZE 80 // Uncomment to override default in protocol.h
// Serial send and receive buffer size. The receive buffer is often used as another streaming
// buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming
@ -212,6 +222,7 @@
// increase the receive buffer if a deeper receive buffer is needed for streaming and avaiable
// memory allows. The send buffer primarily handles messages in Grbl. Only increase if large
// messages are sent and Grbl begins to stall, waiting to send the rest of the message.
// NOTE: Buffer size values must be greater than zero and less than 256.
// #define RX_BUFFER_SIZE 128 // Uncomment to override defaults in serial.h
// #define TX_BUFFER_SIZE 64
@ -246,4 +257,6 @@
// #endif
// ---------------------------------------------------------------------------------------
#endif

View File

@ -43,8 +43,9 @@
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
@ -82,8 +83,9 @@
#define DEFAULT_Z_MAX_TRAVEL 170.0 // mm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
@ -124,8 +126,9 @@
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
@ -166,8 +169,9 @@
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
@ -206,8 +210,9 @@
#define DEFAULT_Z_MAX_TRAVEL 150.0 // mm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT))
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3

62
gcode.c
View File

@ -95,6 +95,7 @@ uint8_t gc_execute_line(char *line)
memcpy(&gc_block.modal,&gc_state.modal,sizeof(gc_modal_t)); // Copy current modes
uint8_t axis_command = AXIS_COMMAND_NONE;
uint8_t axis_0, axis_1, axis_linear;
uint8_t coord_select = 0; // Tracks G10 P coordinate selection for execution
float coordinate_data[N_AXIS]; // Multi-use variable to store coordinate data for execution
float parameter_data[N_AXIS]; // Multi-use variable to store parameter data for execution
@ -526,8 +527,8 @@ uint8_t gc_execute_line(char *line)
// [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing.
if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS) }; // [No axis words]
if (bit_isfalse(value_words,((1<<WORD_P)|(1<<WORD_L)))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P/L word missing]
int_value = trunc(gc_block.values.p); // Convert p value to int.
if (int_value > N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys]
coord_select = trunc(gc_block.values.p); // Convert p value to int.
if (coord_select > N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys]
if (gc_block.values.l != 20) {
if (gc_block.values.l == 2) {
if (bit_istrue(value_words,bit(WORD_R))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G10 L2 R not supported]
@ -535,10 +536,12 @@ uint8_t gc_execute_line(char *line)
}
bit_false(value_words,(bit(WORD_L)|bit(WORD_P)));
// Load EEPROM coordinate data and pre-calculate the new coordinate data.
if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
else { int_value = gc_block.modal.coord_select; } // Index P0 as the active coordinate system
if (!settings_read_coord_data(int_value,parameter_data)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail]
// Determine coordinate system to change and try to load from EEPROM.
if (coord_select > 0) { coord_select--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
else { coord_select = gc_block.modal.coord_select; } // Index P0 as the active coordinate system
if (!settings_read_coord_data(coord_select,parameter_data)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail]
// Pre-calculate the coordinate data changes. NOTE: Uses parameter_data since coordinate_data may be in use by G54-59.
for (idx=0; idx<N_AXIS; idx++) { // Axes indices are consistent, so loop may be used.
// Update axes defined only in block. Always in machine coordinates. Can change non-active system.
if (bit_istrue(axis_words,bit(idx)) ) {
@ -801,7 +804,7 @@ uint8_t gc_execute_line(char *line)
}
}
// [21. Program flow ]: No error check required.
// [21. Program flow ]: No error checks required.
// [0. Non-specific error-checks]: Complete unused value words check, i.e. IJK used when in arc
// radius mode, or axis words that aren't used in the block.
@ -832,7 +835,8 @@ uint8_t gc_execute_line(char *line)
if (gc_state.modal.spindle != SPINDLE_DISABLE) { spindle_run(gc_state.modal.spindle, gc_state.spindle_speed); }
}
// [5. Select tool ]: NOT SUPPORTED
// [5. Select tool ]: NOT SUPPORTED. Only tracks tool value.
gc_state.tool = gc_block.values.t;
// [6. Change tool ]: NOT SUPPORTED
@ -891,30 +895,24 @@ uint8_t gc_execute_line(char *line)
// [19. Go to predefined position, Set G10, or Set axis offsets ]:
switch(gc_block.non_modal_command) {
case NON_MODAL_SET_COORDINATE_DATA:
// TODO: See if I can clean up this int_value.
int_value = trunc(gc_block.values.p); // Convert p value to int.
if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
else { int_value = gc_state.modal.coord_select; } // Index P0 as the active coordinate system
settings_write_coord_data(int_value,parameter_data);
settings_write_coord_data(coord_select,parameter_data);
// Update system coordinate system if currently active.
if (gc_state.modal.coord_select == int_value) { memcpy(gc_state.coord_system,parameter_data,sizeof(parameter_data)); }
if (gc_state.modal.coord_select == coord_select) { memcpy(gc_state.coord_system,parameter_data,sizeof(parameter_data)); }
break;
case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1:
// Move to intermediate position before going home. Obeys current coordinate system and offsets
// and absolute and incremental modes.
if (axis_command) {
#ifdef USE_LINE_NUMBERS
mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n);
mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n);
#else
mc_line(gc_block.values.xyz, -1.0, false);
mc_line(gc_block.values.xyz, -1.0, false);
#endif
}
#ifdef USE_LINE_NUMBERS
mc_line(parameter_data, -1.0, false, gc_block.values.n);
mc_line(parameter_data, -1.0, false, gc_block.values.n);
#else
mc_line(parameter_data, -1.0, false);
mc_line(parameter_data, -1.0, false);
#endif
memcpy(gc_state.position, parameter_data, sizeof(parameter_data));
break;
@ -942,32 +940,34 @@ uint8_t gc_execute_line(char *line)
switch (gc_state.modal.motion) {
case MOTION_MODE_SEEK:
#ifdef USE_LINE_NUMBERS
mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n);
mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n);
#else
mc_line(gc_block.values.xyz, -1.0, false);
mc_line(gc_block.values.xyz, -1.0, false);
#endif
break;
case MOTION_MODE_LINEAR:
#ifdef USE_LINE_NUMBERS
mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_block.values.n);
mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_block.values.n);
#else
mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate);
mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate);
#endif
break;
case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC:
#ifdef USE_LINE_NUMBERS
mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r,
gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear, gc_block.values.n);
mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r,
gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear, gc_block.values.n);
#else
mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r,
gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear);
mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r,
gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear);
#endif
break;
case MOTION_MODE_PROBE:
// NOTE: gc_block.values.xyz is returned from mc_probe_cycle with the updated position value. So
// upon a successful probing cycle, the machine position and the returned value should be the same.
#ifdef USE_LINE_NUMBERS
mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_block.values.n);
mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_block.values.n);
#else
mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate);
mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate);
#endif
}
@ -992,7 +992,7 @@ uint8_t gc_execute_line(char *line)
else { gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; }
}
// TBD: % to denote start of program. Sets auto cycle start?
// TODO: % to denote start of program. Sets auto cycle start?
return(STATUS_OK);
}

View File

@ -22,7 +22,6 @@
#ifndef gcode_h
#define gcode_h
#include "system.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
@ -128,7 +127,7 @@
// NOTE: When this struct is zeroed, the above defines set the defaults for the system.
typedef struct {
uint8_t motion; // {G0,G1,G2,G3,G80}
uint8_t motion; // {G0,G1,G2,G3,G38.2,G80}
uint8_t feed_rate; // {G93,G94}
uint8_t units; // {G20,G21}
uint8_t distance; // {G90,G91}
@ -149,7 +148,7 @@ typedef struct {
// float q; // G82 peck drilling
float r; // Arc radius
float s; // Spindle speed
// uint8_t t; // Tool selection
uint8_t t; // Tool selection
float xyz[3]; // X,Y,Z Translational axes
} gc_values_t;

163
limits.c
View File

@ -28,8 +28,8 @@
#include "limits.h"
#include "report.h"
#define HOMING_AXIS_SEARCH_SCALAR 1.5 // Axis search distance multiplier. Must be > 1.
// Homing axis search distance multiplier. Computed by this value times the axis max travel.
#define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged.
void limits_init()
@ -50,9 +50,9 @@ void limits_init()
}
#ifdef ENABLE_SOFTWARE_DEBOUNCE
MCUSR &= ~(1<<WDRF);
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = (1<<WDP0); // Set time-out at ~32msec.
MCUSR &= ~(1<<WDRF);
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = (1<<WDP0); // Set time-out at ~32msec.
#endif
}
@ -76,38 +76,38 @@ void limits_disable()
// 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.
#ifndef ENABLE_SOFTWARE_DEBOUNCE
ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process.
{
// 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.
bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate hard limit critical event
}
}
}
#else // OPTIONAL: Software debounce limit pin routine.
// Upon limit pin change, enable watchdog timer to create a short delay.
ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
ISR(WDT_vect) // Watchdog timer ISR
{
WDTCSR &= ~(1<<WDIE); // Disable watchdog timer.
if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
if (bit_isfalse(sys.execute,EXEC_ALARM)) {
uint8_t bits = LIMIT_PIN;
// Check limit pin state.
if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { bits ^= LIMIT_MASK; }
if (bits & LIMIT_MASK) {
ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process.
{
// 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.
bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate hard limit critical event
}
}
}
}
#else // OPTIONAL: Software debounce limit pin routine.
// Upon limit pin change, enable watchdog timer to create a short delay.
ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
ISR(WDT_vect) // Watchdog timer ISR
{
WDTCSR &= ~(1<<WDIE); // Disable watchdog timer.
if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
if (bit_isfalse(sys.execute,EXEC_ALARM)) {
uint8_t bits = LIMIT_PIN;
// Check limit pin state.
if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { bits ^= LIMIT_MASK; }
if (bits & LIMIT_MASK) {
mc_reset(); // Initiate system kill.
bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate hard limit critical event
}
}
}
}
#endif
@ -128,11 +128,17 @@ void limits_go_home(uint8_t cycle_mask)
uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1);
float target[N_AXIS];
// Determine travel distance to the furthest homing switch based on user max travel settings.
// NOTE: settings.max_travel[] is stored as a negative value.
float max_travel = settings.max_travel[X_AXIS];
if (max_travel > settings.max_travel[Y_AXIS]) { max_travel = settings.max_travel[Y_AXIS]; }
if (max_travel > settings.max_travel[Z_AXIS]) { max_travel = settings.max_travel[Z_AXIS]; }
uint8_t limit_pin[N_AXIS], step_pin[N_AXIS];
float max_travel = 0.0;
for (idx=0; idx<N_AXIS; idx++) {
// Initialize limit and step pin masks
limit_pin[idx] = get_limit_pin_mask(idx);
step_pin[idx] = get_step_pin_mask(idx);
// Determine travel distance to the furthest homing switch based on user max travel settings.
// NOTE: settings.max_travel[] is stored as a negative value.
if (max_travel > settings.max_travel[idx]) { max_travel = settings.max_travel[idx]; }
}
max_travel *= -HOMING_AXIS_SEARCH_SCALAR; // Ensure homing switches engaged by over-estimating max travel.
plan_reset(); // Reset planner buffer to zero planner current position and to clear previous motions.
@ -142,9 +148,12 @@ void limits_go_home(uint8_t cycle_mask)
if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { invert_pin = approach; }
else { invert_pin = !approach; }
// Set target location and rate for active axes.
// Initialize and declare variables needed for homing routine.
uint8_t n_active_axis = 0;
uint8_t axislock = 0;
for (idx=0; idx<N_AXIS; idx++) {
// Set target location for active axes and setup computation for homing rate.
if (bit_istrue(cycle_mask,bit(idx))) {
n_active_axis++;
if (!approach) { target[idx] = -max_travel; }
@ -152,41 +161,35 @@ void limits_go_home(uint8_t cycle_mask)
} else {
target[idx] = 0.0;
}
// Set target direction based on cycle mask
if (bit_istrue(settings.homing_dir_mask,bit(idx))) { target[idx] = -target[idx]; }
// Apply axislock to the step port pins active in this cycle.
if (bit_istrue(cycle_mask,bit(idx))) { axislock |= step_pin[idx]; }
}
if (bit_istrue(settings.homing_dir_mask,(1<<X_DIRECTION_BIT))) { target[X_AXIS] = -target[X_AXIS]; }
if (bit_istrue(settings.homing_dir_mask,(1<<Y_DIRECTION_BIT))) { target[Y_AXIS] = -target[Y_AXIS]; }
if (bit_istrue(settings.homing_dir_mask,(1<<Z_DIRECTION_BIT))) { target[Z_AXIS] = -target[Z_AXIS]; }
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
// Reset homing axis locks based on cycle mask.
uint8_t axislock = 0;
if (bit_istrue(cycle_mask,bit(X_AXIS))) { axislock |= (1<<X_STEP_BIT); }
if (bit_istrue(cycle_mask,bit(Y_AXIS))) { axislock |= (1<<Y_STEP_BIT); }
if (bit_istrue(cycle_mask,bit(Z_AXIS))) { axislock |= (1<<Z_STEP_BIT); }
sys.homing_axis_lock = axislock;
// Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
uint8_t limit_state;
#ifdef USE_LINE_NUMBERS
plan_buffer_line(target, homing_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan homing motion.
plan_buffer_line(target, homing_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan homing motion.
#else
plan_buffer_line(target, homing_rate, false); // Bypass mc_line(). Directly plan homing motion.
plan_buffer_line(target, homing_rate, false); // Bypass mc_line(). Directly plan homing motion.
#endif
st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
st_wake_up(); // Initiate motion
do {
// Check limit state. Lock out cycle axes when they change.
limit_state = LIMIT_PIN;
if (invert_pin) { limit_state ^= LIMIT_MASK; }
if (axislock & (1<<X_STEP_BIT)) {
if (limit_state & (1<<X_LIMIT_BIT)) { axislock &= ~(1<<X_STEP_BIT); }
}
if (axislock & (1<<Y_STEP_BIT)) {
if (limit_state & (1<<Y_LIMIT_BIT)) { axislock &= ~(1<<Y_STEP_BIT); }
}
if (axislock & (1<<Z_STEP_BIT)) {
if (limit_state & (1<<Z_LIMIT_BIT)) { axislock &= ~(1<<Z_STEP_BIT); }
for (idx=0; idx<N_AXIS; idx++) {
if (axislock & step_pin[idx]) {
if (limit_state & limit_pin[idx]) { axislock &= ~(step_pin[idx]); }
}
}
sys.homing_axis_lock = axislock;
st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
@ -217,22 +220,31 @@ void limits_go_home(uint8_t cycle_mask)
// do not move them.
// NOTE: settings.max_travel[] is stored as a negative value.
if (cycle_mask & bit(idx)) {
if ( settings.homing_dir_mask & get_direction_mask(idx) ) {
target[idx] = settings.homing_pulloff+settings.max_travel[idx];
sys.position[idx] = lround(settings.max_travel[idx]*settings.steps_per_mm[idx]);
} else {
target[idx] = -settings.homing_pulloff;
sys.position[idx] = 0;
}
#ifdef HOMING_FORCE_SET_ORIGIN
sys.position[idx] = 0; // Set axis homed location as axis origin
target[idx] = settings.homing_pulloff;
if ( bit_isfalse(settings.homing_dir_mask,bit(idx)) ) { target[idx] = -target[idx]; }
#else
if ( bit_istrue(settings.homing_dir_mask,bit(idx)) ) {
target[idx] = settings.homing_pulloff+settings.max_travel[idx];
sys.position[idx] = lround(settings.max_travel[idx]*settings.steps_per_mm[idx]);
} else {
target[idx] = -settings.homing_pulloff;
sys.position[idx] = 0;
}
#endif
} else { // Non-active cycle axis. Set target to not move during pull-off.
target[idx] = (float)sys.position[idx]/settings.steps_per_mm[idx];
}
}
plan_sync_position(); // Sync planner position to current machine position for pull-off move.
#ifdef USE_LINE_NUMBERS
plan_buffer_line(target, settings.homing_seek_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan motion.
plan_buffer_line(target, settings.homing_seek_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan motion.
#else
plan_buffer_line(target, settings.homing_seek_rate, false); // Bypass mc_line(). Directly plan motion.
plan_buffer_line(target, settings.homing_seek_rate, false); // Bypass mc_line(). Directly plan motion.
#endif
// Initiate pull-off using main motion control routines.
@ -252,9 +264,23 @@ void limits_go_home(uint8_t cycle_mask)
void limits_soft_check(float *target)
{
uint8_t idx;
uint8_t soft_limit_error = false;
for (idx=0; idx<N_AXIS; idx++) {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { // NOTE: max_travel is stored as negative
#ifdef HOMING_FORCE_SET_ORIGIN
// When homing forced set origin is enabled, soft limits checks need to account for directionality.
// NOTE: max_travel is stored as negative
if (bit_istrue(settings.homing_dir_mask,bit(idx))) {
if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) { soft_limit_error = true; }
} else {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; }
}
#else
// NOTE: max_travel is stored as negative
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; }
#endif
if (soft_limit_error) {
// Force feed hold if cycle is active. All buffered blocks are guaranteed to be within
// workspace volume so just come to a controlled stop so position is not lost. When complete
// enter alarm mode.
@ -270,7 +296,6 @@ void limits_soft_check(float *target)
bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate soft limit critical event
protocol_execute_runtime(); // Execute to enter critical event loop and system abort
return;
}
}
}

View File

@ -42,9 +42,9 @@
// mc_line and plan_buffer_line is done primarily to place non-planner-type functions from being
// in the planner and to let backlash compensation or canned cycle integration simple and direct.
#ifdef USE_LINE_NUMBERS
void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number)
void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number)
#else
void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate)
void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate)
#endif
{
// If enabled, check for soft limit violations. Placed here all line motions are picked up
@ -78,9 +78,9 @@ void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate)
} while (1);
#ifdef USE_LINE_NUMBERS
plan_buffer_line(target, feed_rate, invert_feed_rate, line_number);
plan_buffer_line(target, feed_rate, invert_feed_rate, line_number);
#else
plan_buffer_line(target, feed_rate, invert_feed_rate);
plan_buffer_line(target, feed_rate, invert_feed_rate);
#endif
// If idle, indicate to the system there is now a planned block in the buffer ready to cycle
@ -97,11 +97,11 @@ void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate)
// of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal
// distance from segment to the circle when the end points both lie on the circle.
#ifdef USE_LINE_NUMBERS
void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, int32_t line_number)
void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, int32_t line_number)
#else
void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear)
void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear)
#endif
{
float center_axis0 = position[axis_0] + offset[axis_0];
@ -142,16 +142,18 @@ void mc_arc(float *position, float *target, float *offset, float radius, float f
For arc generation, the center of the circle is the axis of rotation and the radius vector is
defined from the circle center to the initial position. Each line segment is formed by successive
vector rotations. Single precision values can accumulate error greater than tool precision in some
vector rotations. Single precision values can accumulate error greater than tool precision in rare
cases. So, exact arc path correction is implemented. This approach avoids the problem of too many very
expensive trig operations [sin(),cos(),tan()] which can take 100-200 usec each to compute.
Small angle approximation may be used to reduce computation overhead further. A third-order approximation
(second order sin() has too much error) holds for nearly all CNC applications, except for possibly very
small radii (~0.5mm). In other words, theta_per_segment would need to be greater than 0.25 rad(14 deg)
and N_ARC_CORRECTION would need to be large to cause an appreciable drift error (>5% of radius, for very
small radii, 5% of 0.5mm is very, very small). N_ARC_CORRECTION~=20 should be more than small enough to
correct for numerical drift error. Also decreasing the tolerance will improve the approximation too.
(second order sin() has too much error) holds for most, if not, all CNC applications. Note that this
approximation will begin to accumulate a numerical drift error when theta_per_segment is greater than
~0.25 rad(14 deg) AND the approximation is successively used without correction several dozen times. This
scenario is extremely unlikely, since segment lengths and theta_per_segment are automatically generated
and scaled by the arc tolerance setting. Only a very large arc tolerance setting, unrealistic for CNC
applications, would cause this numerical drift error. However, it is best to set N_ARC_CORRECTION from a
low of ~4 to a high of ~20 or so to avoid trig operations while keeping arc generation accurate.
This approximation also allows mc_arc to immediately insert a line segment into the planner
without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied
@ -193,9 +195,9 @@ void mc_arc(float *position, float *target, float *offset, float radius, float f
position[axis_linear] += linear_per_segment;
#ifdef USE_LINE_NUMBERS
mc_line(position, feed_rate, invert_feed_rate, line_number);
mc_line(position, feed_rate, invert_feed_rate, line_number);
#else
mc_line(position, feed_rate, invert_feed_rate);
mc_line(position, feed_rate, invert_feed_rate);
#endif
// Bail mid-circle on system abort. Runtime command check already performed by mc_line.
@ -204,9 +206,9 @@ void mc_arc(float *position, float *target, float *offset, float radius, float f
}
// Ensure last segment arrives at target location.
#ifdef USE_LINE_NUMBERS
mc_line(target, feed_rate, invert_feed_rate, line_number);
mc_line(target, feed_rate, invert_feed_rate, line_number);
#else
mc_line(target, feed_rate, invert_feed_rate);
mc_line(target, feed_rate, invert_feed_rate);
#endif
}
@ -269,62 +271,64 @@ void mc_homing_cycle()
// Perform tool length probe cycle. Requires probe switch.
// NOTE: Upon probe failure, the program will be stopped and placed into ALARM state.
#ifdef USE_LINE_NUMBERS
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number)
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number)
#else
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate)
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate)
#endif
{
if (sys.state != STATE_CYCLE) protocol_auto_cycle_start();
protocol_buffer_synchronize(); // Finish all queued commands
protocol_buffer_synchronize(); // Finish all queued commands and empty planner buffer.
if (sys.abort) { return; } // Return if system reset has been issued.
// Perform probing cycle. Planner buffer should be empty at this point.
// Setup and queue probing motion. Auto cycle-start should not start the cycle.
#ifdef USE_LINE_NUMBERS
mc_line(target, feed_rate, invert_feed_rate, line_number);
mc_line(target, feed_rate, invert_feed_rate, line_number);
#else
mc_line(target, feed_rate, invert_feed_rate);
mc_line(target, feed_rate, invert_feed_rate);
#endif
// Activate the probing monitor in the stepper module.
// NOTE: Parser error-checking ensures the probe isn't already closed/triggered.
sys.probe_state = PROBE_ACTIVE;
// Perform probing cycle. Wait here until probe is triggered or motion completes.
bit_true_atomic(sys.execute, EXEC_CYCLE_START);
do {
protocol_execute_runtime();
if (sys.abort) { return; } // Check for system abort
} while ((sys.state != STATE_IDLE) && (sys.state != STATE_QUEUED));
// If motion completes without probe triggering, error out.
if (sys.probe_state == PROBE_ACTIVE) { bit_true_atomic(sys.execute, EXEC_CRIT_EVENT); }
protocol_execute_runtime(); // Check and execute run-time commands
if (sys.abort) { return; } // Check for system abort
//Prep the new target based on the position that the probe triggered
uint8_t i;
for(i=0; i<N_AXIS; ++i){
target[i] = (float)sys.probe_position[i]/settings.steps_per_mm[i];
// Reset the stepper and planner buffers to remove the remainder of the probe motion.
st_reset(); // Reest step segment buffer.
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared.
plan_sync_position(); // Sync planner position to current machine position.
// Probing motion is complete, but we'll pull-off triggered probe to the trigger location since
// we had to decelerate a little beyond it to stop the machine in a controlled manner.
uint8_t idx;
for(idx=0; idx<N_AXIS; idx++){
// NOTE: The target[] variable updated here will be sent back and synced with the g-code parser.
target[idx] = (float)sys.probe_position[idx]/settings.steps_per_mm[idx];
}
protocol_execute_runtime();
st_reset(); // Immediately force kill steppers and reset step segment buffer.
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure homing motion is cleared.
plan_sync_position(); // Sync planner position to current machine position for pull-off move.
#ifdef USE_LINE_NUMBERS
mc_line(target, feed_rate, invert_feed_rate, line_number); // Bypass mc_line(). Directly plan homing motion.
mc_line(target, feed_rate, invert_feed_rate, line_number);
#else
mc_line(target, feed_rate, invert_feed_rate); // Bypass mc_line(). Directly plan homing motion.
mc_line(target, feed_rate, invert_feed_rate);
#endif
// Execute pull-off motion and wait until it completes.
bit_true_atomic(sys.execute, EXEC_CYCLE_START);
protocol_buffer_synchronize(); // Complete pull-off motion.
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
protocol_buffer_synchronize();
if (sys.abort) { return; } // Return if system reset has been issued.
// Gcode parser position was circumvented by the this routine, so sync position now.
gc_sync_position();
// Output the probe position as message.
report_probe_parameters();
#ifdef MESSAGE_PROBE_COORDINATES
// All done! Output the probe position as message.
report_probe_parameters();
#endif
}

View File

@ -20,6 +20,7 @@
*/
#include "system.h"
#include "print.h"
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
@ -139,21 +140,5 @@ void delay_us(uint32_t us)
}
// Returns direction mask according to Grbl internal axis indexing.
uint8_t get_direction_mask(uint8_t axis_idx)
{
uint8_t axis_mask = 0;
switch( axis_idx ) {
case X_AXIS: axis_mask = (1<<X_DIRECTION_BIT); break;
case Y_AXIS: axis_mask = (1<<Y_DIRECTION_BIT); break;
case Z_AXIS: axis_mask = (1<<Z_DIRECTION_BIT); break;
}
return(axis_mask);
}
float hypot_f(float x, float y)
{
return(sqrt(x*x + y*y));
}
// Simple hypotenuse computation function.
float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); }

View File

@ -63,8 +63,7 @@ void delay_ms(uint16_t ms);
// Delays variable-defined microseconds. Compiler compatibility fix for _delay_us().
void delay_us(uint32_t us);
uint8_t get_direction_mask(uint8_t i);
// Computes hypotenuse, avoiding avr-gcc's bloated version and the extra error checking.
float hypot_f(float x, float y);
#endif

View File

@ -260,9 +260,9 @@ uint8_t plan_check_full_buffer()
invert_feed_rate is true, or as seek/rapids rate if the feed_rate value is negative (and
invert_feed_rate always false). */
#ifdef USE_LINE_NUMBERS
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number)
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number)
#else
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate)
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate)
#endif
{
// Prepare and initialize new block
@ -295,7 +295,7 @@ void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate)
unit_vec[idx] = delta_mm; // Store unit vector numerator. Denominator computed later.
// Set direction bits. Bit enabled always means direction is negative.
if (delta_mm < 0 ) { block->direction_bits |= get_direction_mask(idx); }
if (delta_mm < 0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
// Incrementally compute total move distance by Euclidean norm. First add square of each term.
block->millimeters += delta_mm*delta_mm;
@ -406,6 +406,14 @@ void plan_sync_position()
}
// Returns the number of active blocks are in the planner buffer.
uint8_t plan_get_block_buffer_count()
{
if (block_buffer_head >= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); }
return(BLOCK_BUFFER_SIZE - (block_buffer_tail-block_buffer_head));
}
// 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()

View File

@ -22,7 +22,6 @@
#ifndef planner_h
#define planner_h
#include "system.h"
// The number of linear motions that can be in the plan at any give time
#ifndef BLOCK_BUFFER_SIZE
@ -65,9 +64,9 @@ void plan_reset();
// 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.
#ifdef USE_LINE_NUMBERS
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number);
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number);
#else
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate);
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate);
#endif
// Called when the current block is no longer needed. Discards the block and makes the memory
@ -89,6 +88,9 @@ void plan_sync_position();
// Reinitialize plan with a partially completed block
void plan_cycle_reinitialize();
// Returns the number of active blocks are in the planner buffer.
uint8_t plan_get_block_buffer_count();
// Returns the status of the block ring buffer. True, if buffer is full.
uint8_t plan_check_full_buffer();

21
print.c
View File

@ -19,9 +19,6 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
/* This code was initially inspired by the wiring_serial module by David A. Mellis which
used to be a part of the Arduino project. */
#include "system.h"
#include "serial.h"
#include "settings.h"
@ -33,6 +30,7 @@ void printString(const char *s)
serial_write(*s++);
}
// Print a string stored in PGM-memory
void printPgmString(const char *s)
{
@ -41,6 +39,7 @@ void printPgmString(const char *s)
serial_write(c);
}
// void printIntegerInBase(unsigned long n, unsigned long base)
// {
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
@ -62,6 +61,7 @@ void printPgmString(const char *s)
// 'A' + buf[i - 1] - 10);
// }
void print_uint8_base2(uint8_t n)
{
unsigned char buf[8];
@ -76,6 +76,7 @@ void print_uint8_base2(uint8_t n)
serial_write('0' + buf[i - 1]);
}
void print_uint8_base10(uint8_t n)
{
if (n == 0) {
@ -95,6 +96,7 @@ void print_uint8_base10(uint8_t n)
serial_write(buf[i - 1]);
}
void print_uint32_base10(unsigned long n)
{
if (n == 0) {
@ -114,6 +116,7 @@ void print_uint32_base10(unsigned long n)
serial_write('0' + buf[i-1]);
}
void printInteger(long n)
{
if (n < 0) {
@ -124,6 +127,7 @@ 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.
@ -190,3 +194,14 @@ void printFloat_RateValue(float n) {
}
void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); }
// Debug tool to print free memory in bytes at the called point. Not used otherwise.
void printFreeMemory()
{
extern int __heap_start, *__brkval;
uint16_t free; // Up to 64k values.
free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
printInteger((int32_t)free);
printString(" ");
}

View File

@ -25,6 +25,7 @@
#ifndef print_h
#define print_h
void printString(const char *s);
void printPgmString(const char *s);
@ -49,4 +50,7 @@ void printFloat_RateValue(float n);
void printFloat_SettingValue(float n);
// Debug tool to print free memory in bytes at the called point. Not used otherwise.
void printFreeMemory();
#endif

18
probe.c
View File

@ -19,22 +19,30 @@
*/
#include "system.h"
#include "settings.h"
#include "probe.h"
// Inverts the probe pin state depending on user settings.
uint8_t probe_invert_mask;
// Probe pin initialization routine.
void probe_init()
{
PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins
PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation.
if (bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN)) {
PROBE_PORT &= ~(PROBE_MASK); // Normal low operation. Requires external pull-down.
probe_invert_mask = 0;
} else {
PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation.
probe_invert_mask = PROBE_MASK;
}
}
// Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor.
uint8_t probe_get_state()
{
return(!(PROBE_PIN & PROBE_MASK));
}
uint8_t probe_get_state() { return((PROBE_PIN & PROBE_MASK) ^ probe_invert_mask); }
// Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick.

View File

@ -136,7 +136,7 @@ void protocol_main_loop()
// everything until the next '%' sign. This will help fix resuming issues with certain
// functions that empty the planner buffer to execute its task on-time.
} else if (char_counter >= LINE_BUFFER_SIZE-1) {
} else if (char_counter >= (LINE_BUFFER_SIZE-1)) {
// Detect line buffer overflow. Report error and reset line buffer.
report_status_message(STATUS_OVERFLOW);
iscomment = false;

View File

@ -28,7 +28,7 @@
// memory space we can invest into here or we re-write the g-code parser not to have this
// buffer.
#ifndef LINE_BUFFER_SIZE
#define LINE_BUFFER_SIZE 70
#define LINE_BUFFER_SIZE 80
#endif
// Starts Grbl main loop. It handles all incoming characters from the serial port and executes

145
report.c
View File

@ -35,6 +35,7 @@
#include "planner.h"
#include "spindle_control.h"
#include "stepper.h"
#include "serial.h"
// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
@ -155,43 +156,67 @@ void report_grbl_help() {
"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_SettingValue(settings.steps_per_mm[X_AXIS]);
printPgmString(PSTR(" (x, step/mm)\r\n$1=")); printFloat_SettingValue(settings.steps_per_mm[Y_AXIS]);
printPgmString(PSTR(" (y, step/mm)\r\n$2=")); printFloat_SettingValue(settings.steps_per_mm[Z_AXIS]);
printPgmString(PSTR(" (z, step/mm)\r\n$3=")); printFloat_SettingValue(settings.max_rate[X_AXIS]);
printPgmString(PSTR(" (x max rate, mm/min)\r\n$4=")); printFloat_SettingValue(settings.max_rate[Y_AXIS]);
printPgmString(PSTR(" (y max rate, mm/min)\r\n$5=")); printFloat_SettingValue(settings.max_rate[Z_AXIS]);
printPgmString(PSTR(" (z max rate, mm/min)\r\n$6=")); printFloat_SettingValue(settings.acceleration[X_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (x accel, mm/sec^2)\r\n$7=")); printFloat_SettingValue(settings.acceleration[Y_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (y accel, mm/sec^2)\r\n$8=")); printFloat_SettingValue(settings.acceleration[Z_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (z accel, mm/sec^2)\r\n$9=")); printFloat_SettingValue(-settings.max_travel[X_AXIS]); // Grbl internally store this as negative.
printPgmString(PSTR(" (x max travel, mm)\r\n$10=")); printFloat_SettingValue(-settings.max_travel[Y_AXIS]); // Grbl internally store this as negative.
printPgmString(PSTR(" (y max travel, mm)\r\n$11=")); printFloat_SettingValue(-settings.max_travel[Z_AXIS]); // Grbl internally store this as negative.
printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); print_uint8_base10(settings.pulse_microseconds);
printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); print_uint8_base10(settings.step_invert_mask);
// Print Grbl settings.
printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds);
printPgmString(PSTR(" (step pulse, usec)\r\n$1=")); print_uint8_base10(settings.stepper_idle_lock_time);
printPgmString(PSTR(" (step idle delay, msec)\r\n$2=")); print_uint8_base10(settings.step_invert_mask);
printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask);
printPgmString(PSTR(")\r\n$14=")); print_uint8_base10(settings.dir_invert_mask);
printPgmString(PSTR(")\r\n$3=")); print_uint8_base10(settings.dir_invert_mask);
printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask);
printPgmString(PSTR(")\r\n$15=")); print_uint8_base10(settings.stepper_idle_lock_time);
printPgmString(PSTR(" (step idle delay, msec)\r\n$16=")); printFloat_SettingValue(settings.junction_deviation);
printPgmString(PSTR(" (junction deviation, mm)\r\n$17=")); printFloat_SettingValue(settings.arc_tolerance);
printPgmString(PSTR(" (arc tolerance, mm)\r\n$19=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
printPgmString(PSTR(" (report inches, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START));
printPgmString(PSTR(" (auto start, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
printPgmString(PSTR(" (invert step enable, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
printPgmString(PSTR(" (invert limit pins, bool)\r\n$23=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
printPgmString(PSTR(" (soft limits, bool)\r\n$24=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
printPgmString(PSTR(" (hard limits, bool)\r\n$25=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
printPgmString(PSTR(" (homing cycle, bool)\r\n$26=")); print_uint8_base10(settings.homing_dir_mask);
printPgmString(PSTR(")\r\n$4=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
printPgmString(PSTR(" (step enable invert, bool)\r\n$5=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
printPgmString(PSTR(" (limit pins invert, bool)\r\n$6=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN));
printPgmString(PSTR(" (probe pin invert, bool)\r\n$10=")); print_uint8_base10(settings.status_report_mask);
printPgmString(PSTR(" (status report mask:")); print_uint8_base2(settings.status_report_mask);
printPgmString(PSTR(")\r\n$11=")); printFloat_SettingValue(settings.junction_deviation);
printPgmString(PSTR(" (junction deviation, mm)\r\n$12=")); printFloat_SettingValue(settings.arc_tolerance);
printPgmString(PSTR(" (arc tolerance, mm)\r\n$13=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
printPgmString(PSTR(" (report inches, bool)\r\n$14=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START));
printPgmString(PSTR(" (auto start, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
printPgmString(PSTR(" (soft limits, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
printPgmString(PSTR(" (hard limits, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
printPgmString(PSTR(" (homing cycle, bool)\r\n$23=")); print_uint8_base10(settings.homing_dir_mask);
printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask);
printPgmString(PSTR(")\r\n$27=")); printFloat_SettingValue(settings.homing_feed_rate);
printPgmString(PSTR(" (homing feed, mm/min)\r\n$28=")); printFloat_SettingValue(settings.homing_seek_rate);
printPgmString(PSTR(" (homing seek, mm/min)\r\n$29=")); print_uint8_base10(settings.homing_debounce_delay);
printPgmString(PSTR(" (homing debounce, msec)\r\n$30=")); printFloat_SettingValue(settings.homing_pulloff);
printPgmString(PSTR(")\r\n$24=")); printFloat_SettingValue(settings.homing_feed_rate);
printPgmString(PSTR(" (homing feed, mm/min)\r\n$25=")); printFloat_SettingValue(settings.homing_seek_rate);
printPgmString(PSTR(" (homing seek, mm/min)\r\n$26=")); print_uint8_base10(settings.homing_debounce_delay);
printPgmString(PSTR(" (homing debounce, msec)\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff);
printPgmString(PSTR(" (homing pull-off, mm)\r\n"));
// Print axis settings
uint8_t idx, set_idx;
uint8_t val = AXIS_SETTINGS_START_VAL;
for (set_idx=0; set_idx<AXIS_N_SETTINGS; set_idx++) {
for (idx=0; idx<N_AXIS; idx++) {
printPgmString(PSTR("$"));
print_uint8_base10(val+idx);
printPgmString(PSTR("="));
switch (set_idx) {
case 0: printFloat_SettingValue(settings.steps_per_mm[idx]); break;
case 1: printFloat_SettingValue(settings.max_rate[idx]); break;
case 2: printFloat_SettingValue(settings.acceleration[idx]/(60*60)); break;
case 3: printFloat_SettingValue(-settings.max_travel[idx]); break;
}
printPgmString(PSTR(" ("));
switch (idx) {
case X_AXIS: printPgmString(PSTR("x")); break;
case Y_AXIS: printPgmString(PSTR("y")); break;
case Z_AXIS: printPgmString(PSTR("z")); break;
}
switch (set_idx) {
case 0: printPgmString(PSTR(", step/mm")); break;
case 1: printPgmString(PSTR(" max rate, mm/min")); break;
case 2: printPgmString(PSTR(" accel, mm/sec^2")); break;
case 3: printPgmString(PSTR(" max travel, mm")); break;
}
printPgmString(PSTR(")\r\n"));
}
val += AXIS_SETTINGS_INCREMENT;
}
}
@ -354,37 +379,53 @@ void report_realtime_status()
}
// Report machine position
printPgmString(PSTR(",MPos:"));
for (i=0; i< N_AXIS; i++) {
print_position[i] = current_position[i]/settings.steps_per_mm[i];
printFloat_CoordValue(print_position[i]);
printPgmString(PSTR(","));
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_MACHINE_POSITION)) {
printPgmString(PSTR(",MPos:"));
for (i=0; i< N_AXIS; i++) {
print_position[i] = current_position[i]/settings.steps_per_mm[i];
printFloat_CoordValue(print_position[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
}
}
// Report work position
printPgmString(PSTR("WPos:"));
for (i=0; i< N_AXIS; i++) {
print_position[i] -= gc_state.coord_system[i]+gc_state.coord_offset[i];
if (i == TOOL_LENGTH_OFFSET_AXIS) { print_position[i] -= gc_state.tool_length_offset; }
printFloat_CoordValue(print_position[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_WORK_POSITION)) {
printPgmString(PSTR(",WPos:"));
for (i=0; i< N_AXIS; i++) {
print_position[i] -= gc_state.coord_system[i]+gc_state.coord_offset[i];
if (i == TOOL_LENGTH_OFFSET_AXIS) { print_position[i] -= gc_state.tool_length_offset; }
printFloat_CoordValue(print_position[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
}
}
// Returns the number of active blocks are in the planner buffer.
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_PLANNER_BUFFER)) {
printPgmString(PSTR(",Buf:"));
print_uint8_base10(plan_get_block_buffer_count());
}
// Report serial read buffer status
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_SERIAL_RX)) {
printPgmString(PSTR(",RX:"));
print_uint8_base10(serial_get_rx_buffer_count());
}
#ifdef USE_LINE_NUMBERS
// Report current line number
printPgmString(PSTR(",Ln:"));
int32_t ln=0;
plan_block_t * pb = plan_get_current_block();
if(pb != NULL) {
ln = pb->line_number;
}
printInteger(ln);
// Report current line number
printPgmString(PSTR(",Ln:"));
int32_t ln=0;
plan_block_t * pb = plan_get_current_block();
if(pb != NULL) {
ln = pb->line_number;
}
printInteger(ln);
#endif
#ifdef REPORT_REALTIME_RATE
// Report realtime rate
printPgmString(PSTR(",F:"));
printFloat_RateValue(st_get_realtime_rate());
// Report realtime rate
printPgmString(PSTR(",F:"));
printFloat_RateValue(st_get_realtime_rate());
#endif
printPgmString(PSTR(">\r\n"));

View File

@ -1,5 +0,0 @@
socat -d -d READLINE /dev/tty.usbmodem26221,nonblock=1,clocal=1
socat -d -d READLINE /dev/tty.usbmodem621,nonblock=1,clocal=1
socat -d -d READLINE /dev/tty.usbserial-A700e0GO,clocal=1,nonblock=1,cread=1,cs8,ixon=1,ixoff=1
socat -d -d READLINE /dev/tty.usbserial-A9007QcR,clocal=1,nonblock=1,cread=1,cs8,ixon=1,ixoff=1
#socat -d -d READLINE /dev/tty.FireFly-A964-SPP-1,clocal=1,nonblock=1,cread=1,cs8,ixon=1,ixoff=1

View File

@ -1,2 +0,0 @@
socat -d -d tcp4-listen:5001,fork /dev/tty.usbserial-A4001o6L,clocal=1,nonblock=1,cread=1,cs8,ixon=1,ixoff=1

View File

@ -1,2 +0,0 @@
#!/usr/bin/ruby
require 'script/stream'

View File

@ -1,50 +0,0 @@
require 'rubygems'
require 'optparse'
require 'serialport'
options_parser = OptionParser.new do |opts|
opts.banner = "Usage: stream [options] gcode-file"
opts.on('-v', '--verbose', 'Output more information') do
$verbose = true
end
opts.on('-p', '--prebuffer', 'Prebuffer commands') do
$prebuffer = true
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
options_parser.parse!
if ARGV.empty?
puts options_parser
exit
end
# SerialPort.open('/dev/tty.FireFly-A964-SPP-1', 115200) do |sp|
SerialPort.open('/dev/tty.usbserial-A700e0GO', 9600) do |sp|
sp.write("\r\n\r\n");
sleep 1
ARGV.each do |file|
puts "Processing file #{file}"
prebuffer = $prebuffer ? 20 : 0
File.readlines(file).each do |line|
next if line.strip == ''
puts line.strip
sp.write("#{line.strip}\r\n");
if prebuffer == 0
begin
result = sp.gets.strip
puts "Grbl >> #{result}" #unless result == 'ok'
end while !(result =~ /^ok|^error/)
else
prebuffer -= 1
end
end
end
puts "Done."
sleep 500
end

View File

@ -1,78 +0,0 @@
require 'pp'
def estimate_acceleration_distance(initial_rate, target_rate, acceleration)
(target_rate*target_rate-initial_rate*initial_rate)/(2*acceleration)
end
def intersection_distance(initial_rate, final_rate, acceleration, distance)
(2*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/(4*acceleration)
end
ACCELERATION_TICKS_PER_SECOND = 20
def trapezoid_params(step_event_count, nominal_rate, rate_delta, entry_factor, exit_factor)
initial_rate = (nominal_rate * entry_factor).round
final_rate = (nominal_rate * exit_factor).round
acceleration_per_minute = rate_delta*ACCELERATION_TICKS_PER_SECOND*60
accelerate_steps =
estimate_acceleration_distance(initial_rate, nominal_rate, acceleration_per_minute).round;
decelerate_steps =
estimate_acceleration_distance(nominal_rate, final_rate, -acceleration_per_minute).round;
# Calculate the size of Plateau of Nominal Rate.
plateau_steps = step_event_count-accelerate_steps-decelerate_steps;
# Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will
# have to use intersection_distance() to calculate when to abort acceleration and start braking
# in order to reach the final_rate exactly at the end of this block.
if (plateau_steps < 0)
accelerate_steps =
intersection_distance(initial_rate, final_rate, acceleration_per_minute, step_event_count).round
plateau_steps = 0;
end
accelerate_until = accelerate_steps;
decelerate_after = accelerate_steps+plateau_steps;
{:step_event_count => step_event_count,
:initial_rate => initial_rate,
:final_rate => final_rate,
:nominal_rate => nominal_rate,
:rate_delta => rate_delta,
:accelerate_until => accelerate_until,
:decelerate_after => decelerate_after}
end
def simulate_trapezoid(params)
result = {}
rate = params[:initial_rate]
step_event = 0.0
max_rate = 0
while(step_event < params[:step_event_count]) do
step_events_in_frame = rate/60.0/ACCELERATION_TICKS_PER_SECOND
step_event += step_events_in_frame
max_rate = rate if rate > max_rate
if (step_event < params[:accelerate_until])
rate += params[:rate_delta]
elsif (step_event > params[:decelerate_after])
if rate > params[:final_rate]
rate -= params[:rate_delta]
else
return :underflow_at => step_event, :final_rate => rate, :max_rate => max_rate
end
end
# puts "#{step_event} #{rate}"
end
{:final_rate => rate, :max_rate => max_rate}
end
(10..100).each do |rate|
(1..5).each do |steps|
params = trapezoid_params(steps*1000, rate*100, 10, 0.1, 0.1)
result = simulate_trapezoid(params)
# puts params.inspect
line = "#{steps*10} final: #{result[:final_rate]} == #{params[:final_rate]} peak: #{result[:max_rate]} == #{params[:nominal_rate]} d#{params[:nominal_rate]-result[:max_rate]} "
line << " (underflow at #{result[:underflow_at]})" if result[:underflow_at]
puts line
end
end

View File

@ -10,13 +10,37 @@ 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.
---------------------
The MIT License (MIT)
Copyright (c) 2012 Sungeun K. Jeon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------------------
"""
import serial
import time
# Open grbl serial port
s = serial.Serial('/dev/tty.usbmodem1811',9600)
s = serial.Serial('/dev/tty.usbmodem1811',115200)
# Open g-code file
f = open('grbl.gcode','r');

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python
"""\
Stream g-code to grbl controller
This script differs from the simple_stream.py script by
@ -9,9 +10,36 @@ 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
CHANGELOG:
- 20140714: Updated baud rate to 115200. Added a settings
write mode via simple streaming method. MIT-licensed.
Version: SKJ.20120110
TODO:
- Add runtime command capabilities
---------------------
The MIT License (MIT)
Copyright (c) 2012-2014 Sungeun K. Jeon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------------------
"""
import serial
@ -31,6 +59,8 @@ parser.add_argument('device_file',
help='serial device path')
parser.add_argument('-q','--quiet',action='store_true', default=False,
help='suppress output text')
parser.add_argument('-s','--settings',action='store_true', default=False,
help='settings write mode')
args = parser.parse_args()
# Periodic timer to query for status reports
@ -41,10 +71,12 @@ args = parser.parse_args()
# t.start()
# Initialize
s = serial.Serial(args.device_file,9600)
s = serial.Serial(args.device_file,115200)
f = args.gcode_file
verbose = True
if args.quiet : verbose = False
settings_mode = False
if args.settings : settings_mode = True
# Wake up grbl
print "Initializing grbl..."
@ -55,29 +87,46 @@ 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 = []
# 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
l_block = line.strip()
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
if out_temp.find('ok') < 0 and out_temp.find('error') < 0 :
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
if settings_mode:
# Send settings file via simple call-response streaming method. Settings must be streamed
# in this manner since the EEPROM accessing cycles shut-off the serial interrupt.
print "SETTINGS MODE: Streaming", args.gcode_file.name, " to ", args.device_file
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() # Strip all EOL characters for consistency
if verbose: print 'SND: ' + str(l_count) + ':' + l_block,
s.write(l_block + '\n') # Send g-code block to grbl
grbl_out = s.readline().strip() # Wait for grbl response with carriage return
if verbose: print 'REC:',grbl_out
else:
# Send g-code program via a more agressive streaming protocol that forces characters into
# Grbl's serial read buffer to ensure Grbl has immediate access to the next g-code command
# rather than wait for the call-response serial protocol to finish. This is done by careful
# counting of the number of characters sent by the streamer to Grbl and tracking Grbl's
# responses, such that we never overflow Grbl's serial read buffer.
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
l_block = line.strip()
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
if out_temp.find('ok') < 0 and out_temp.find('error') < 0 :
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] # Delete the block character count corresponding to the last 'ok'
if verbose: print "SND: " + str(l_count) + " : " + l_block,
s.write(l_block + '\n') # Send g-code 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"

View File

@ -29,29 +29,39 @@
#include "protocol.h"
uint8_t rx_buffer[RX_BUFFER_SIZE];
uint8_t rx_buffer_head = 0;
volatile uint8_t rx_buffer_tail = 0;
uint8_t serial_rx_buffer[RX_BUFFER_SIZE];
uint8_t serial_rx_buffer_head = 0;
volatile uint8_t serial_rx_buffer_tail = 0;
uint8_t tx_buffer[TX_BUFFER_SIZE];
uint8_t tx_buffer_head = 0;
volatile uint8_t tx_buffer_tail = 0;
uint8_t serial_tx_buffer[TX_BUFFER_SIZE];
uint8_t serial_tx_buffer_head = 0;
volatile uint8_t serial_tx_buffer_tail = 0;
#ifdef ENABLE_XONXOFF
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
// Returns the number of bytes used in the RX serial buffer.
uint8_t serial_get_rx_buffer_count()
{
uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); }
return (RX_BUFFER_SIZE - (rtail-serial_rx_buffer_head));
}
// Returns the number of bytes used in the TX serial buffer.
// NOTE: Not used except for debugging and ensuring no TX bottlenecks.
uint8_t serial_get_tx_buffer_count()
{
uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile
if (serial_tx_buffer_head >= ttail) { return(serial_tx_buffer_head-ttail); }
return (TX_BUFFER_SIZE - (ttail-serial_tx_buffer_head));
}
void serial_init()
{
// Set baud rate
@ -76,19 +86,21 @@ void serial_init()
}
// Writes one byte to the TX serial buffer. Called by main program.
// TODO: Check if we can speed this up for writing strings, rather than single bytes.
void serial_write(uint8_t data) {
// Calculate next head
uint8_t next_head = tx_buffer_head + 1;
uint8_t next_head = serial_tx_buffer_head + 1;
if (next_head == TX_BUFFER_SIZE) { next_head = 0; }
// Wait until there is space in the buffer
while (next_head == tx_buffer_tail) {
while (next_head == serial_tx_buffer_tail) {
if (sys.execute & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop.
}
// Store data and advance head
tx_buffer[tx_buffer_head] = data;
tx_buffer_head = next_head;
serial_tx_buffer[serial_tx_buffer_head] = data;
serial_tx_buffer_head = next_head;
// Enable Data Register Empty Interrupt to make sure tx-streaming is running
UCSR0B |= (1 << UDRIE0);
@ -98,7 +110,7 @@ void serial_write(uint8_t data) {
// Data Register Empty Interrupt handler
ISR(SERIAL_UDRE)
{
uint8_t tail = tx_buffer_tail; // Temporary tx_buffer_tail (to optimize for volatile)
uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
#ifdef ENABLE_XONXOFF
if (flow_ctrl == SEND_XOFF) {
@ -111,34 +123,35 @@ ISR(SERIAL_UDRE)
#endif
{
// Send a byte from the buffer
UDR0 = tx_buffer[tail];
UDR0 = serial_tx_buffer[tail];
// Update tail position
tail++;
if (tail == TX_BUFFER_SIZE) { tail = 0; }
tx_buffer_tail = tail;
serial_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); }
if (tail == serial_tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
}
// Fetches the first byte in the serial read buffer. Called by main program.
uint8_t serial_read()
{
uint8_t tail = rx_buffer_tail; // Temporary rx_buffer_tail (to optimize for volatile)
if (rx_buffer_head == tail) {
uint8_t tail = serial_rx_buffer_tail; // Temporary serial_rx_buffer_tail (to optimize for volatile)
if (serial_rx_buffer_head == tail) {
return SERIAL_NO_DATA;
} else {
uint8_t data = rx_buffer[tail];
uint8_t data = serial_rx_buffer[tail];
tail++;
if (tail == RX_BUFFER_SIZE) { tail = 0; }
rx_buffer_tail = tail;
serial_rx_buffer_tail = tail;
#ifdef ENABLE_XONXOFF
if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
if ((serial_get_serial_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
flow_ctrl = SEND_XON;
UCSR0B |= (1 << UDRIE0); // Force TX
}
@ -162,16 +175,16 @@ ISR(SERIAL_RX)
case CMD_FEED_HOLD: bit_true_atomic(sys.execute, EXEC_FEED_HOLD); break; // Set as true
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
default: // Write character to buffer
next_head = rx_buffer_head + 1;
next_head = serial_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;
if (next_head != serial_rx_buffer_tail) {
serial_rx_buffer[serial_rx_buffer_head] = data;
serial_rx_buffer_head = next_head;
#ifdef ENABLE_XONXOFF
if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
if ((serial_get_serial_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
flow_ctrl = SEND_XOFF;
UCSR0B |= (1 << UDRIE0); // Force TX
}
@ -185,7 +198,7 @@ ISR(SERIAL_RX)
void serial_reset_read_buffer()
{
rx_buffer_tail = rx_buffer_head;
serial_rx_buffer_tail = serial_rx_buffer_head;
#ifdef ENABLE_XONXOFF
flow_ctrl = XON_SENT;

View File

@ -48,11 +48,20 @@
void serial_init();
// Writes one byte to the TX serial buffer. Called by main program.
void serial_write(uint8_t data);
// Fetches the first byte in the serial read buffer. Called by main program.
uint8_t serial_read();
// Reset and empty data in read buffer. Used by e-stop and reset.
void serial_reset_read_buffer();
// Returns the number of bytes used in the RX serial buffer.
uint8_t serial_get_rx_buffer_count();
// Returns the number of bytes used in the TX serial buffer.
// NOTE: Not used except for debugging and ensuring no TX bottlenecks.
uint8_t serial_get_tx_buffer_count();
#endif

View File

@ -62,20 +62,19 @@ void write_global_settings()
// Method to reset Grbl global settings back to defaults.
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.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE;
settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE;
settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE;
settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION;
settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME;
settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK;
settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK;
settings.status_report_mask = DEFAULT_STATUS_REPORT_MASK;
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK;
settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE;
settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE;
settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY;
settings.homing_pulloff = DEFAULT_HOMING_PULLOFF;
settings.flags = 0;
if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; }
if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; }
@ -84,15 +83,20 @@ void settings_reset() {
if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_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_FEED_RATE;
settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE;
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.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.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE;
settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE;
settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE;
settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION;
settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
settings.max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL);
settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL);
settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL);
write_global_settings();
}
@ -158,69 +162,89 @@ uint8_t read_global_settings() {
// A helper method to set settings from command line
uint8_t settings_store_global_setting(int parameter, float value) {
uint8_t settings_store_global_setting(uint8_t parameter, float value) {
if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); }
switch(parameter) {
case 0: case 1: case 2:
settings.steps_per_mm[parameter] = value; break;
case 3: settings.max_rate[X_AXIS] = value; break;
case 4: settings.max_rate[Y_AXIS] = value; break;
case 5: settings.max_rate[Z_AXIS] = value; break;
case 6: settings.acceleration[X_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 7: settings.acceleration[Y_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 8: settings.acceleration[Z_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 9: settings.max_travel[X_AXIS] = -value; break; // Store as negative for grbl internal use.
case 10: settings.max_travel[Y_AXIS] = -value; break; // Store as negative for grbl internal use.
case 11: settings.max_travel[Z_AXIS] = -value; break; // Store as negative for grbl internal use.
case 12:
if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); }
settings.pulse_microseconds = round(value); break;
case 13: settings.step_invert_mask = trunc(value); break;
case 14: settings.dir_invert_mask = trunc(value); break;
case 15: settings.stepper_idle_lock_time = round(value); break;
case 16: settings.junction_deviation = fabs(value); break;
case 17: settings.arc_tolerance = value; break;
case 19:
if (value) { settings.flags |= BITFLAG_REPORT_INCHES; }
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
break;
case 20: // 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 21: // 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 22: // Reset to ensure change. Immediate re-init may cause problems.
if (value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; }
break;
case 23:
if (value) {
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); }
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
} else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; }
break;
case 24:
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 25:
if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; }
else {
settings.flags &= ~BITFLAG_HOMING_ENABLE;
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits.
if (parameter >= AXIS_SETTINGS_START_VAL) {
// Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines.
// NOTE: Ensure the setting index corresponds to the report.c settings printout.
parameter -= AXIS_SETTINGS_START_VAL;
uint8_t set_idx = 0;
while (set_idx < AXIS_N_SETTINGS) {
if (parameter < N_AXIS) {
// Valid axis setting found.
switch (set_idx) {
case 0: settings.steps_per_mm[parameter] = value; break;
case 1: settings.max_rate[parameter] = value; break;
case 2: settings.acceleration[parameter] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use.
}
break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call.
} else {
set_idx++;
// If axis index greater than N_AXIS or setting index greater than number of axis settings, error out.
if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) { return(STATUS_INVALID_STATEMENT); }
parameter -= AXIS_SETTINGS_INCREMENT;
}
break;
case 26: settings.homing_dir_mask = trunc(value); break;
case 27: settings.homing_feed_rate = value; break;
case 28: settings.homing_seek_rate = value; break;
case 29: settings.homing_debounce_delay = round(value); break;
case 30: settings.homing_pulloff = value; break;
default:
return(STATUS_INVALID_STATEMENT);
}
} else {
// Store non-axis Grbl settings
uint8_t int_value = trunc(value);
switch(parameter) {
case 0:
if (int_value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); }
settings.pulse_microseconds = int_value; break;
case 1: settings.stepper_idle_lock_time = int_value; break;
case 2: settings.step_invert_mask = int_value; break;
case 3: settings.dir_invert_mask = int_value; break;
case 4: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; }
break;
case 5: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; }
break;
case 6: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; }
else { settings.flags &= ~BITFLAG_INVERT_PROBE_PIN; }
break;
case 10: settings.status_report_mask = int_value;
case 11: settings.junction_deviation = value; break;
case 12: settings.arc_tolerance = value; break;
case 13:
if (int_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 (int_value) { settings.flags |= BITFLAG_AUTO_START; }
else { settings.flags &= ~BITFLAG_AUTO_START; }
break;
case 20:
if (int_value) {
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); }
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
} else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; }
break;
case 21:
if (int_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 22:
if (int_value) { settings.flags |= BITFLAG_HOMING_ENABLE; }
else {
settings.flags &= ~BITFLAG_HOMING_ENABLE;
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits.
}
break;
case 23: settings.homing_dir_mask = int_value; break;
case 24: settings.homing_feed_rate = value; break;
case 25: settings.homing_seek_rate = value; break;
case 26: settings.homing_debounce_delay = int_value; break;
case 27: settings.homing_pulloff = value; break;
default:
return(STATUS_INVALID_STATEMENT);
}
}
write_global_settings();
return(STATUS_OK);
@ -244,3 +268,30 @@ void settings_init() {
}
// NOTE: Startup lines are handled and called by main.c at the end of initialization.
}
// Returns step pin mask according to Grbl internal axis indexing.
uint8_t get_step_pin_mask(uint8_t axis_idx)
{
if ( axis_idx == X_AXIS ) { return((1<<X_STEP_BIT)); }
if ( axis_idx == Y_AXIS ) { return((1<<Y_STEP_BIT)); }
return((1<<Z_STEP_BIT));
}
// Returns direction pin mask according to Grbl internal axis indexing.
uint8_t get_direction_pin_mask(uint8_t axis_idx)
{
if ( axis_idx == X_AXIS ) { return((1<<X_DIRECTION_BIT)); }
if ( axis_idx == Y_AXIS ) { return((1<<Y_DIRECTION_BIT)); }
return((1<<Z_DIRECTION_BIT));
}
// Returns limit pin mask according to Grbl internal axis indexing.
uint8_t get_limit_pin_mask(uint8_t axis_idx)
{
if ( axis_idx == X_AXIS ) { return((1<<X_LIMIT_BIT)); }
if ( axis_idx == Y_AXIS ) { return((1<<Y_LIMIT_BIT)); }
return((1<<Z_LIMIT_BIT));
}

View File

@ -22,15 +22,13 @@
#ifndef settings_h
#define settings_h
#include "system.h"
#define GRBL_VERSION "0.9f"
#define GRBL_VERSION_BUILD "20140706"
#define GRBL_VERSION "0.9g"
#define GRBL_VERSION_BUILD "20140725"
// 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 8
#define SETTINGS_VERSION 9
// Define bit flag masks for the boolean settings in settings.flag.
#define BITFLAG_REPORT_INCHES bit(0)
@ -40,6 +38,13 @@
#define BITFLAG_HOMING_ENABLE bit(4)
#define BITFLAG_SOFT_LIMIT_ENABLE bit(5)
#define BITFLAG_INVERT_LIMIT_PINS bit(6)
#define BITFLAG_INVERT_PROBE_PIN bit(7)
// Define status reporting boolean enable bit flags in settings.status_report_mask
#define BITFLAG_RT_STATUS_MACHINE_POSITION bit(0)
#define BITFLAG_RT_STATUS_WORK_POSITION bit(1)
#define BITFLAG_RT_STATUS_PLANNER_BUFFER bit(2)
#define BITFLAG_RT_STATUS_SERIAL_RX bit(3)
// 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
@ -58,25 +63,35 @@
#define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2
// #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported)
// Define Grbl axis settings numbering scheme. Starts at START_VAL, every INCREMENT, over N_SETTINGS.
#define AXIS_N_SETTINGS 4
#define AXIS_SETTINGS_START_VAL 100 // NOTE: Reserving settings values >= 100 for axis settings. Up to 255.
#define AXIS_SETTINGS_INCREMENT 10 // Must be greater than the number of axis settings
// Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards)
typedef struct {
// Axis settings
float steps_per_mm[N_AXIS];
float max_rate[N_AXIS];
float acceleration[N_AXIS];
float max_travel[N_AXIS];
// Remaining Grbl settings
uint8_t pulse_microseconds;
uint8_t step_invert_mask;
uint8_t dir_invert_mask;
uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable.
uint8_t status_report_mask; // Mask to indicate desired report data.
float junction_deviation;
float arc_tolerance;
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 status_report_mask; // Mask to indicate desired report data.
} settings_t;
extern settings_t settings;
@ -84,7 +99,7 @@ extern settings_t settings;
void settings_init();
// A helper method to set new settings from command line
uint8_t settings_store_global_setting(int parameter, float value);
uint8_t settings_store_global_setting(uint8_t parameter, float value);
// Stores the protocol line variable as a startup line in EEPROM
void settings_store_startup_line(uint8_t n, char *line);
@ -92,8 +107,10 @@ 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);
// Stores build info user-defined string
void settings_store_build_info(char *line);
// Reads build info user-defined string
uint8_t settings_read_build_info(char *line);
// Writes selected coordinate data to EEPROM
@ -102,4 +119,14 @@ 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);
// Returns the step pin mask according to Grbl's internal axis numbering
uint8_t get_step_pin_mask(uint8_t i);
// Returns the direction pin mask according to Grbl's internal axis numbering
uint8_t get_direction_pin_mask(uint8_t i);
// Returns the limit pin mask according to Grbl's internal axis numbering
uint8_t get_limit_pin_mask(uint8_t i);
#endif

View File

@ -77,6 +77,7 @@ void spindle_run(uint8_t direction, float rpm)
}
#ifdef VARIABLE_SPINDLE
// TODO: Install the optional capability for frequency-based output for servos.
#define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM)
TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER);
TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x02; // set to 1/8 Prescaler

View File

@ -109,6 +109,10 @@ static volatile uint8_t segment_buffer_tail;
static uint8_t segment_buffer_head;
static uint8_t segment_next_head;
// Step and direction port invert masks.
static uint8_t step_port_invert_mask;
static uint8_t dir_port_invert_mask;
// Used to avoid ISR nesting of the "Stepper Driver Interrupt". Should never occur though.
static volatile uint8_t busy;
@ -189,8 +193,8 @@ void st_wake_up()
if (sys.state & (STATE_CYCLE | STATE_HOMING)){
// Initialize stepper output bits
st.dir_outbits = settings.dir_invert_mask;
st.step_outbits = settings.step_invert_mask;
st.dir_outbits = dir_port_invert_mask;
st.step_outbits = step_port_invert_mask;
// Initialize step pulse timing from settings. Here to ensure updating after re-writing.
#ifdef STEP_PULSE_DELAY
@ -330,7 +334,7 @@ ISR(TIMER1_COMPA_vect)
st.counter_z = st.counter_x;
}
st.dir_outbits = st.exec_block->direction_bits ^ settings.dir_invert_mask;
st.dir_outbits = st.exec_block->direction_bits ^ dir_port_invert_mask;
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level.
@ -399,7 +403,7 @@ ISR(TIMER1_COMPA_vect)
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
}
st.step_outbits ^= settings.step_invert_mask; // Apply step port invert mask
st.step_outbits ^= step_port_invert_mask; // Apply step port invert mask
busy = false;
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
}
@ -419,7 +423,7 @@ ISR(TIMER1_COMPA_vect)
ISR(TIMER0_OVF_vect)
{
// Reset stepping pins (leave the direction pins)
STEP_PORT = (STEP_PORT & ~STEP_MASK) | (settings.step_invert_mask & STEP_MASK);
STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK);
TCCR0B = 0; // Disable Timer0 to prevent re-entering this interrupt when it's not needed.
}
#ifdef STEP_PULSE_DELAY
@ -441,15 +445,28 @@ void st_reset()
// Initialize stepper driver idle state.
st_go_idle();
// Initialize stepper algorithm variables.
memset(&prep, 0, sizeof(prep));
memset(&st, 0, sizeof(st));
st.exec_segment = NULL;
pl_block = NULL; // Planner block pointer used by segment buffer
segment_buffer_tail = 0;
segment_buffer_head = 0; // empty = tail
segment_next_head = 1;
busy = false;
// Setup step and direction port invert masks.
uint8_t idx;
step_port_invert_mask = 0;
dir_port_invert_mask = 0;
for (idx=0; idx<N_AXIS; idx++) {
if (bit_istrue(settings.step_invert_mask,bit(idx))) { step_port_invert_mask |= get_step_pin_mask(idx); }
if (bit_istrue(settings.dir_invert_mask,bit(idx))) { dir_port_invert_mask |= get_direction_pin_mask(idx); }
}
// Initialize step and direction port pins.
STEP_PORT = (STEP_PORT & ~STEP_MASK) | step_port_invert_mask;
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | dir_port_invert_mask;
}
@ -458,10 +475,8 @@ void stepper_init()
{
// Configure step and direction interface pins
STEP_DDR |= STEP_MASK;
STEP_PORT = (STEP_PORT & ~STEP_MASK) | settings.step_invert_mask;
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
DIRECTION_DDR |= DIRECTION_MASK;
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | settings.dir_invert_mask;
// Configure Timer 1: Stepper Driver Interrupt
TCCR1B &= ~(1<<WGM13); // waveform generation = 0100 = CTC
@ -815,9 +830,6 @@ void st_prep_buffer()
}
}
// int32_t blength = segment_buffer_head - segment_buffer_tail;
// if (blength < 0) { blength += SEGMENT_BUFFER_SIZE; }
// printInteger(blength);
}
}
@ -827,13 +839,13 @@ void st_prep_buffer()
// in the segment buffer. It will always be behind by up to the number of segment blocks (-1)
// divided by the ACCELERATION TICKS PER SECOND in seconds.
#ifdef REPORT_REALTIME_RATE
float st_get_realtime_rate()
{
if (sys.state & (STATE_CYCLE | STATE_HOMING)){
return prep.current_speed;
}
return 0.0f;
}
float st_get_realtime_rate()
{
if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD)){
return prep.current_speed;
}
return 0.0f;
}
#endif
/*

View File

@ -193,7 +193,7 @@ uint8_t system_execute_line(char *line)
} else { // Store global setting.
if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); }
if(line[char_counter] != 0) { return(STATUS_INVALID_STATEMENT); }
return(settings_store_global_setting(parameter, value));
return(settings_store_global_setting((uint8_t)parameter, value));
}
}
}

4919
test/gcode/8x_gear_test.nc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ G0X0.000Y0.000S8000M3
G0X0.000Y-33.519Z6.000
G1Z-1.000
G1X0.327Y-33.521
F1000.0
F7000.0
X0.654Y-33.526
X0.980Y-33.534
X1.304Y-33.546

View File

@ -0,0 +1,31 @@
(Machine settings provided by @kikigey89)
$0=87.489 (x, step/mm)
$1=87.489 (y, step/mm)
$2=1280.000 (z, step/mm)
$3=1000.000 (x max rate, mm/min)
$4=1000.000 (y max rate, mm/min)
$5=500.000 (z max rate, mm/min)
$6=10.000 (x accel, mm/sec^2)
$7=10.000 (y accel, mm/sec^2)
$8=10.000 (z accel, mm/sec^2)
$9=211.000 (x max travel, mm)
$10=335.000 (y max travel, mm)
$11=70.000 (z max travel, mm)
$12=20 (step pulse, usec)
$13=160 (step port invert mask:10100000)
$14=160 (dir port invert mask:10100000)
$15=50 (step idle delay, msec)
$16=0.010 (junction deviation, mm)
$17=0.002 (arc tolerance, mm)
$19=0 (report inches, bool)
$20=1 (auto start, bool)
$21=0 (invert step enable, bool)
$22=0 (invert limit pins, bool)
$23=0 (soft limits, bool)
$24=0 (hard limits, bool)
$25=0 (homing cycle, bool)
$26=0 (homing dir invert mask:00000000)
$27=50.000 (homing feed, mm/min)
$28=500.000 (homing seek, mm/min)
$29=10 (homing debounce, msec)
$30=3.000 (homing pull-off, mm)