#include "grbl.h"
Go to the source code of this file.
Macros | |
#define | MAX_LINE_NUMBER 10000000 |
NOTE: Max line number is defined by the g-code standard to be 99999. More... | |
#define | MAX_TOOL_NUMBER 255 |
Limited by max unsigned 8-bit value. More... | |
#define | AXIS_COMMAND_NONE 0 |
#define | AXIS_COMMAND_NON_MODAL 1 |
#define | AXIS_COMMAND_MOTION_MODE 2 |
#define | AXIS_COMMAND_TOOL_LENGTH_OFFSET 3 |
*Undefined but required More... | |
#define | FAIL(status) return(status); |
Functions | |
void | gc_init () |
Initialize the parser. More... | |
void | gc_sync_position () |
Sets g-code parser position in mm. Input in steps. Called by the system abort and hard. More... | |
uint8_t | gc_execute_line (char *line) |
Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase. More... | |
if (axis_words) | |
STEP 3: Error-check all commands and values passed in this block. This step ensures all of the commands are valid for execution and follows the NIST standard as closely as possible. If an error is found, all commands and values in this block are dumped and will not update the active system g-code modes. If the block is ok, the active system g-code modes will be updated based on the commands of this block, and signal for it to be executed. More... | |
if (bit_istrue(value_words, bit(WORD_N))) | |
if (gc_parser_flags &GC_PARSER_JOG_MOTION) | |
NOTE: Single-meaning value word. Set at end of error-checking. More... | |
if (gc_block.modal.units==UNITS_MODE_INCHES) | |
if (axis_command==AXIS_COMMAND_TOOL_LENGTH_OFFSET) | |
NOTE: Since cutter radius compensation is never enabled, these G40 errors don't apply. Grbl supports G40. More... | |
memcpy (block_coord_system, gc_state.coord_system, sizeof(gc_state.coord_system)) | |
if (bit_istrue(command_words, bit(MODAL_GROUP_G12))) | |
switch (gc_block.non_modal_command) | |
NOTE: We need to separate the non-modal commands that are axis word-using (G10/G28/G30/G92), as these. More... | |
if (value_words) | |
[Unused words] More... | |
memset (pl_data, 0, sizeof(plan_line_data_t)) | |
Zero pl_data struct. More... | |
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) | |
if (gc_state.modal.feed_rate) | |
Set condition flag for planner use. More... | |
if ((gc_state.spindle_speed!=gc_block.values.s)||bit_istrue(gc_parser_flags, GC_PARSER_LASER_FORCE_SYNC)) | |
if (bit_isfalse(gc_parser_flags, GC_PARSER_LASER_DISABLE)) | |
NOTE: Pass zero spindle speed for all restricted laser motions. More... | |
if (gc_state.modal.spindle!=gc_block.modal.spindle) | |
if (gc_state.modal.coolant!=gc_block.modal.coolant) | |
if (gc_block.non_modal_command==NON_MODAL_DWELL) | |
if (gc_state.modal.coord_select!=gc_block.modal.coord_select) | |
if (gc_state.modal.motion!=MOTION_MODE_NONE) | |
return (STATUS_OK) | |
Variables | |
parser_state_t | gc_state |
Declare gc extern struct. More... | |
parser_block_t | gc_block |
else | |
Remove single-meaning value words. More... | |
float | block_coord_system [N_AXIS] |
plan_line_data_t | plan_data |
STEP 4: EXECUTE!! Assumes that all error-checking has been completed and no failure modes exist. We just need to update the state and execute the block according to the order-of-execution. More... | |
plan_line_data_t * | pl_data = &plan_data |
gc_state | line_number = gc_block.values.n |
NOTE: If no line number is present, the value is zero. More... | |
gc_state modal | feed_rate = gc_block.modal.feed_rate |
Always copy this value. See feed rate error-checking. More... | |
gc_state | tool = gc_block.values.t |
pl_data | condition = gc_state.modal.spindle |
Set condition flag for planner use. More... | |
gc_state modal | plane_select = gc_block.modal.plane_select |
gc_state modal | units = gc_block.modal.units |
gc_state modal | distance = gc_block.modal.distance |
NOTE: Always default. More... | |
gc_state modal | motion = gc_block.modal.motion |
NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes. More... | |
gc_state modal | program_flow = gc_block.modal.program_flow |
#define AXIS_COMMAND_TOOL_LENGTH_OFFSET 3 |
#define MAX_LINE_NUMBER 10000000 |
#define MAX_TOOL_NUMBER 255 |
uint8_t gc_execute_line | ( | char * | line | ) |
Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase.
Execute one block of rs275/ngc/g-code.
STEP 1: Initialize parser block struct and copy current g-code state modes. The parser updates these modes and commands as the block line is parser and will only be used and executed after successful error-checking. The parser block struct also contains a block values struct, word tracking variables, and a non-modal commands tracker for the new block. This struct contains all of the necessary information to execute the block.
< Initialize the parser block struct.
< Copy current modes
< Tracks G10 P coordinate selection for execution
< XYZ tracking
< IJK tracking
< Tracks G and M command words. Also used for modal group violations.
< Tracks value words.
<
NOTE: $J=
already parsed when passed to this function.
< Initialize default line number reported during jog.
STEP 2: Import all g-code words in the block line. A g-code word is a letter followed by a number, which can either be a 'G'/'M' command or sets/assigns a command value. Also, perform initial error-checks for command word modal group violations, for any repeated words, and for negative values set for the value words F, N, P, T, and S.
< Bit-value for assigning tracking variables
< Loop until no more g-code words in line.
NOTE: Mantissa is multiplied by 100 to catch non-integer command values. This is more
< Compute mantissa for Gxx.x commands.
NOTE: Rounding must be used to catch small floating point errors.
'G' and 'M' Command Words: Parse commands and check for modal group violations.
/ NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20
< Ignore G28.1, G30.1, and G92.1
< Set to zero to indicate valid non-integer G command.
< [Unsupported G38.x command]
< Set to zero to indicate valid non-integer G command.
< Set to zero to indicate valid non-integer G command.
NOTE: Not required since cutter radius compensation is always disabled. Only here
NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed,
< G49
< G43.1
< Set to zero to indicate valid non-integer G command.
NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.)
< Shift to array indexing.
< [Unsupported G command]
NOTE: Variable 'word_bit' is always assigned, if the command is valid.
< Program pause
< Optional stop not supported. Ignore.
< Program end and reset
< [Unsupported M command]
NOTE: Variable 'word_bit' is always assigned, if the command is valid.
NOTE: All remaining letters assign values.
Non-Command Words: This initial parsing phase only checks for repeats of the remaining legal g-code words and stores their value. Error-checking is performed later since some words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands.
NOTE: For certain commands, P value must be an integer, but none of these commands are supported.
NOTE: Variable 'word_bit' is always assigned, if the non-command letter is valid.
NOTE: Negative value check is done here simply for code-efficiency.
< Flag to indicate parameter assigned.
void gc_sync_position | ( | ) |
if | ( | axis_words | ) |
STEP 3: Error-check all commands and values passed in this block. This step ensures all of the commands are valid for execution and follows the NIST standard as closely as possible. If an error is found, all commands and values in this block are dumped and will not update the active system g-code modes. If the block is ok, the active system g-code modes will be updated based on the commands of this block, and signal for it to be executed.
Also, we have to pre-convert all of the values passed based on the modes set by the parsed block. There are a number of error-checks that require target information that can only be accurately calculated if we convert these values in conjunction with the error-checking. This relegates the next execution step as only updating the system g-code modes and performing the programmed actions in order. The execution step should not require any conversion calculations and would only require minimal checks necessary to execute.
/ NOTE: At this point, the g-code block has been parsed and the block line can be freed.
/ NOTE: It's also possible, at some future point, to break up STEP 2, to allow piece-wise parsing of the block on a per-word basis, rather than the entire block. This could remove the need for maintaining a large string variable for the entire block and free up some memory. To do this, this would simply need to retain all of the data in STEP 1, such as the new block data struct, the modal group and value bitflag tracking variables, and axis array indices compatible variables. This data contains all of the information necessary to error-check the new g-code block when the EOL character is received. However, this would break Grbl's startup lines in how it currently works and would require some refactoring to make it compatible.
if | ( | bit_istrue(value_words, bit(WORD_N)) | ) |
if | ( | gc_parser_flags & | GC_PARSER_JOG_MOTION | ) |
NOTE: Single-meaning value word. Set at end of error-checking.
NOTE: G-code parser state is not updated, except the position to ensure sequential jog.
NOTE: Single-meaning value words are removed all at once at the end of error-checking, because NOTE: For jogging, ignore prior feed rate mode. Enforce G94 and check for required F word.
NOTE: Feed rate word and axis word checks have already been performed in STEP 3.
if | ( | gc_block.modal. | units = = UNITS_MODE_INCHES | ) |
if | ( | axis_command | = = AXIS_COMMAND_TOOL_LENGTH_OFFSET | ) |
NOTE: Since cutter radius compensation is never enabled, these G40 errors don't apply. Grbl supports G40.
NOTE: Not needed since always disabled.
Remove axis words.
NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid
NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms
< Indicates called in block.
< No break intentional.
NOTE: Both radius and offsets are required for arc tracing and are pre-computed with the error-checking.
< Delta x between current position and target
< Delta y between current position and target
< Arc Radius Mode
We need to calculate the center of the circle that has the designated radius and passes through both the current position and the target position. This method calculates the following set of equations where [x,y] is the vector from current to target position, d == magnitude of that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point [i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc.
d^2 == x^2 + y^2 h^2 == r^2 - (d/2)^2 i == x/2 - y/d*h j == y/2 + x/d*h
O <- [i,j] - | r - | - | - | h - | [0,0] -> C -----------------+--------------- T <- [x,y] | <------ d/2 ---->|
C - Current position T - Target position O - center of circle that pass through both C and T d - distance from C to T r - designated radius h - distance from center of CT to O
Expanding the equations:
d -> sqrt(x^2 + y^2) h -> sqrt(4 * r^2 - x^2 - y^2)/2 i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
Which can be written:
i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
Which we for size and speed reasons optimize to:
h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2) i = (x - (y * h_x2_div_d))/2 j = (y + (x * h_x2_div_d))/2
< == -(h * 2 / d)
The counter clockwise circle lies to the left of the target direction. When offset is positive, the left hand circle will be generated - when it is negative the right hand circle is generated.
T <-- Target position ^ Clockwise circles with this center | Clockwise circles with this center will have will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing! \ | /
center of arc when h_x2_div_d is positive -> x <–— | –—> x <- center of arc when h_x2_div_d is negative | |
C <– Current position
< Finished with r. Set to positive for mc_arc
< Arc Center Format Offset Mode
< Axes indices are consistent, so loop may be used to save flash space.
< Delta x between circle center and target
< Delta y between circle center and target
< No break intentional.
NOTE: Probe check moved to probe cycle. Instead of returning
< Indicates a change.
< G49
if | ( | bit_istrue(command_words, bit(MODAL_GROUP_G12)) | ) |
if | ( | bit_istrue(settings.flags, BITFLAG_LASER_MODE) | ) |
if | ( | gc_state.modal. | feed_rate | ) |
Set condition flag for planner use.
< Sync and finish all remaining buffered motions before moving on.
< Use feed hold for program pause.
< Execute suspend.
< == PROGRAM_FLOW_COMPLETED
< G54
< Set to refresh immediately just in case something altered.
< Reset program flow.
if | ( | (gc_state.spindle_speed!=gc_block.values.s)||bit_istrue(gc_parser_flags, GC_PARSER_LASER_FORCE_SYNC) | ) |
if | ( | bit_isfalse(gc_parser_flags, GC_PARSER_LASER_DISABLE) | ) |
if | ( | gc_state.modal.spindle! | = gc_block.modal.spindle | ) |
if | ( | gc_state.modal.coolant! | = gc_block.modal.coolant | ) |
if | ( | gc_block. | non_modal_command = = NON_MODAL_DWELL | ) |
if | ( | gc_state.modal.coord_select! | = gc_block.modal.coord_select | ) |
if | ( | gc_state.modal.motion! | = MOTION_MODE_NONE | ) |
memcpy | ( | block_coord_system | , |
gc_state. | coord_system, | ||
sizeof(gc_state.coord_system) | |||
) |
memset | ( | pl_data | , |
0 | , | ||
sizeof(plan_line_data_t) | |||
) |
Zero pl_data struct.
switch | ( | gc_block. | non_modal_command | ) |
NOTE: We need to separate the non-modal commands that are axis word-using (G10/G28/G30/G92), as these.
< [No axis words]
< Convert p value to int.
NOTE: Store parameter data in IJK values. By rule, they are not in use with this command.
< Axes indices are consistent, so loop may be used.
< Axes indices are consistent, so loop may be used.
NOTE: Tool offsets may be appended to these conversions when/if this feature is added.
< TLO block any axis command.
< Axes indices are consistent, so loop may be used to save flash space.
< No axis word in block. Keep same axis position.
NOTE: G53 is never active with G28/30 since they are in the same modal group.
< Incremental mode
< G28
< G30
NOTE: Store parameter data in IJK values. By rule, they are not in use with this command.
< == NON_MODAL_GO_HOME_1
< Set to none if no intermediate motion.
< G28.1
< G30.1
NOTE: If axis words are passed here, they are interpreted as an implicit motion mode.
NOTE: If axis words are passed here, they are interpreted as an implicit motion mode.
NOTE: All explicit axis word commands are in this modal group. So no implicit check necessary.
< [G53 G0/1 not active]
< Set rapid motion condition flag.
< Disable G92 offsets by zeroing offset vector.
float block_coord_system[N_AXIS] |
pl_data condition = gc_state.modal.spindle |
gc_state modal distance = gc_block.modal.distance |
pl_data feed_rate = gc_block.modal.feed_rate |
parser_block_t gc_block |
parser_state_t gc_state |
pl_data line_number = gc_block.values.n |
gc_state modal motion = gc_block.modal.motion |
plan_line_data_t* pl_data = &plan_data |
plan_line_data_t plan_data |