Grbl v1.0e huge beta release. Overrides and new reporting.

- Feature: Realtime feed, rapid, and spindle speed overrides. These
alter the running machine state within tens of milliseconds!
    - Feed override: 100%, +/-10%, +/-1% commands with values 1-200% of
programmed feed
    - Rapid override: 100%, 50%, 25% rapid rate commands
    - Spindle speed override: 100%, +/-10%, +/-1% commands with values
50-200% of programmed speed
    - Override values have configurable limits and increments in
config.h.
- Feature: Realtime toggle overrides for spindle stop, flood coolant,
and optionally mist coolant
    - Spindle stop: Enables and disables spindle during a feed hold.
Automatically restores last spindles state.
    - Flood and mist coolant: Immediately toggles coolant state until
next toggle or g-code coolant command.
- Feature: Jogging mode! Incremental and absolute modes supported.
    - Grbl accepts jogging-specific commands like $J=X100F50. An axis
word and feed rate are required. G20/21 and G90/G91 commands are
accepted.
    - Jog motions can be canceled at any time by a feed hold `!`
command. The buffer is automatically flushed. (No resetting required).
    - Jog motions do not alter the g-code parser state so GUIs don’t
have to track what they changed and correct it.
- Feature: Laser mode setting. Allows Grbl to execute continuous
motions with spindle speed and state changes.
- Feature: Significantly improved status reports. Overhauled to cram in
more meaningful data and still make it smaller on average.
    - All available data is now sent by default, but does not appear if
it doesn’t change or is not active.
    - Machine position(MPos) or work position(WPos) is reported but not
both at the same time. Instead, the work coordinate offsets (WCO)are
sent intermittently whenever it changes or refreshes after 10-30 status
reports. Position vectors are easily computed by WPos  = MPos - WCO.
    - All data has changed in some way. Details of changes are in the
markdown documents and wiki.
- Feature: 16 new realtime commands to control overrides. All in
extended-ASCII character space.
    - While they are not easily typeable and requires a GUI, they can’t
be accidentally triggered by some latent character in the g-code
program and have tons of room for expansion.
- Feature: New substates for HOLD and SAFETY DOOR. A `:x` is appended
to the state, where `x` is an integer and indicates a substate.
    - For example, each integer of a door state describes in what phase
the machine is in during parking. Substates are detailed in the
documentation.
- Feature: With the alarm codes, homing and probe alarms have been
expanded with more codes to provide more exact feedback on what caused
the alarm.
- Feature: New hard limit check upon power-up or reset. If detected, a
feedback message to check the limit switches sent immediately after the
welcome message.
    - May be disabled in config.h.

- OEM feature: Enable/disable `$RST=` individual commands based on
desired behavior in config.h.
- OEM feature: Configurable EEPROM wipe to prevent certain data from
being deleted during firmware upgrade to a new settings version or
`RST=*` command.
- OEM feature: Enable/disable the `$I=` build info write string with
external EEPROM write example sketch.
    - This prevents a user from altering the build info string in
EEPROM. This requires the vendor to write the string to EEPROM via
external means. An Arduino example sketch is provided to accomplish
this. This would be useful for contain product data that is
retrievable.

- Tweak: All feedback has been drastically trimmed to free up flash
space for the v1.0 release.
    - The `$` help message is just one string, listing available
commands.
    - The `$$` settings printout no longer includes descriptions. Only
the setting values. (Sorry it’s this or remove overrides!)
    - Grbl `error:` and `ALARM:` responses now only contain codes. No
descriptions. All codes are explained in documentation.
    - Grbl’s old feedback style may be restored via a config.h, but
keep in mind that it will likely not fit into the Arduino’s flash space.
- Tweak: Grbl now forces a buffer sync or stop motion whenever a g-code
command needs to update and write a value to EEPROM or changes the work
coordinate offset.
    - This addresses two old issues in all prior Grbl versions. First,
an EEPROM write requires interrupts to be disabled, including stepper
and serial comm. Steps can be lost and data can be corrupted. Second,
the work position may not be correlated to the actual machine position,
since machine position is derived from the actual current execution
state, while work position is based on the g-code parser offset state.
They are usually not in sync and the parser state is several motions
behind. This forced sync ensures work and machine positions are always
correct.
    - This behavior can be disabled through a config.h option, but it’s
not recommended to do so.
- Tweak: To make status reports standardized, users can no longer
change what is reported via status report mask, except for only
toggling machine or work positions.
    - All other data fields are included in the report and can only be
disabled through the config.h file. It’s not recommended to alter this,
because GUIs will be expecting this data to be present and may not be
compatible.
- Tweak: Homing cycle and parking motion no longer report a negative
line number in a status report. These will now not report a line number
at all.
- Tweak: New `[Restoring spindle]` message when restoring from a
spindle stop override. Provides feedback what Grbl is doing while the
spindle is powering up and a 4.0 second delay is enforced.
- Tweak: Override values are reset to 100% upon M2/30. This behavior
can be disabled in config.h
- Tweak: The planner buffer size has been reduced from 18 to 16 to free
up RAM for tracking and controlling overrides.
- Tweak: TX buffer size has been increased from 64 to 90 bytes to
improve status reporting and overall performance.
- Tweak: Removed the MOTION CANCEL state. It was redundant and didn’t
affect Grbl’s overall operation by doing so.
- Tweak: Grbl’s serial buffer increased by +1 internally, such that 128
bytes means 128, not 127 due to the ring buffer implementation. Long
overdue.
- Tweak: Altered sys.alarm variable to be set by alarm codes, rather
than bit flags. Simplified how it worked overall.
- Tweak: Planner buffer and serial RX buffer usage has been combined in
the status reports.
- Tweak: Pin state reporting has been refactored to report only the
pins “triggered” and nothing when not “triggered”.
- Tweak: Current machine rate or speed is now included in every report.
- Tweak: The work coordinate offset (WCO) and override states only need
to be refreshed intermittently or reported when they change. The
refresh rates may be altered for each in the config.h file with
different idle and busy rates to lessen Grbl’s load during a job.
- Tweak: For temporary compatibility to existing GUIs until they are
updated, an option to revert back to the old style status reports is
available in config.h, but not recommended for long term use.
- Tweak: Removed old limit pin state reporting option from config.h in
lieu of new status report that includes them.
- Tweak: Updated the defaults.h file to include laser mode, altered
status report mask, and fix an issue with a missing invert probe pin
default.

- Refactor: Changed how planner line data is generated and passed to
the planner and onto the step generator. By making it a struct
variable, this saved significant flash space.
- Refactor: Major re-factoring of the planner to incorporate override
values and allow for re-calculations fast enough to immediately take
effect during operation. No small feat.
- Refactor: Re-factored the step segment generator for re-computing new
override states.
- Refactor: Re-factored spindle_control.c to accommodate the spindle
speed overrides and laser mode.
- Refactor: Re-factored parts of the codebase for a new jogging mode.
Still under development though and slated to be part of the official
v1.0 release. Hang tight.
- Refactor: Created functions for computing a unit vector and value
limiting based on axis maximums to free up more flash.
- Refactor: The spindle PWM is now set directly inside of the stepper
ISR as it loads new step segments.
- Refactor: Moved machine travel checks out of soft limits function
into its own since jogging uses this too.
- Refactor: Removed coolant_stop() and combined with
coolant_set_state().
- Refactor: The serial RX ISR forks off extended ASCII values to
quickly assess the new override realtime commands.
- Refactor: Altered some names of the step control flags.
- Refactor: Improved efficiency of the serial RX get buffer count
function.
- Refactor: Saved significant flash by removing and combining print
functions. Namely the uint8 base10 and base2 functions.
- Refactor: Moved the probe state check in the main stepper ISR to
improve its efficiency.
- Refactor: Single character printPgmStrings() went converted to direct
serial_write() commands to save significant flash space.

- Documentation: Detailed Markdown documents on error codes, alarm
codes, messages, new real-time commands, new status reports, and how
jogging works. More to come later and will be posted on the Wiki as
well.
- Documentation: CSV files for quick importing of Grbl error and alarm
codes.

- Bug Fix: Applied v0.9 master fixes to CoreXY homing.
- Bug Fix: The print float function would cause Grbl to crash if a
value was 1e6 or greater. Increased the buffer by 3 bytes to help
prevent this in the future.
- Bug Fix: Build info and startup string EEPROM restoring was not
writing the checksum value.
- Bug Fix: Corrected an issue with safety door restoring the proper
spindle and coolant state. It worked before, but breaks with laser mode
that can continually change spindle state per planner block.
- Bug Fix: Move system position and probe position arrays out of the
system_t struct. Ran into some compiling errors that were hard to track
down as to why. Moving them out fixed it.
pull/1/head
chamnit 2016-09-21 19:08:24 -06:00
rodzic 0746a5a1d7
commit 12f48a008a
48 zmienionych plików z 3998 dodań i 2228 usunięć

20
COPYING
Wyświetl plik

@ -4,7 +4,7 @@ COPYRIGHT NOTICE FOR GRBL:
Grbl - Embedded CNC g-code interpreter and motion-controller
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Jens Geisler
@ -28,27 +28,27 @@ COPYRIGHT NOTICE(S) FOR WORK CONTAINED IN THIS SOFTWARE:
Copyright (c) 2008, Atmel Corporation All rights reserved.
Redistribution and use in source and binary forms, with or without
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of ATMEL may not be used to endorse or promote products derived
3. The name of ATMEL may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Wyświetl plik

@ -1,7 +1,7 @@
# Part of Grbl
#
# Copyright (c) 2009-2011 Simen Svale Skogsrud
# Copyright (c) 2012-2015 Sungeun K. Jeon
# Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
#
# Grbl is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -32,7 +32,7 @@ DEVICE ?= atmega328p
CLOCK = 16000000
PROGRAMMER ?= -c avrisp2 -P usb
SOURCE = main.c motion_control.c gcode.c spindle_control.c coolant_control.c serial.c \
protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c \
protocol.c stepper.c eeprom.c settings.c planner.c nuts_bolts.c limits.c jog.c\
print.c probe.c report.c system.c
BUILDDIR = build
SOURCEDIR = grbl
@ -42,7 +42,7 @@ FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m
# Tune the lines below only if you know what you are doing:
AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections -fdata-sections
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections
OBJECTS = $(addprefix $(BUILDDIR)/,$(notdir $(SOURCE:.c=.o)))

Wyświetl plik

@ -3,25 +3,25 @@
***
_**This is the development branch for Grbl v1.0's upcoming release. In general, the new features here are beta, so use with caution. If you'd like to help, please report any bugs or oddities that you find! Thanks!**_
_**This is the development branch for Grbl v1.0's upcoming release. Please keep in mind, the new features here are beta, so use with caution. If you'd like to help, please report any bugs or oddities that you find! Thanks!**_
***
Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. It will run on a vanilla Arduino (Duemillanove/Uno) as long as it sports an Atmega 328.
Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. This version of Grbl runs on an Arduino Uno.
The controller is written in highly optimized C utilizing every clever feature of the AVR-chips to achieve precise timing and asynchronous operation. It is able to maintain up to 30kHz of stable, jitter free control pulses.
It accepts standards-compliant g-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as, all other primary g-code commands. Macro functions, variables, and most canned cycles are not supported, but we think GUIs can do a much better job at translating them into straight g-code anyhow.
Grbl includes full acceleration management with look ahead. That means the controller will look up to 18 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering.
Grbl includes full acceleration management with look ahead. That means the controller will look up to 16 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering.
* [Licensing](https://github.com/grbl/grbl/wiki/Licensing): Grbl is free software, released under the GPLv3 license.
* [Licensing](https://github.com/gnea/grbl/wiki/Licensing): Grbl is free software, released under the GPLv3 license.
* 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!
* For more information and help, check out our **[Wiki pages!](https://github.com/gnea/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 Developer [_2011 - Current_]: Sungeun(Sonny) K. Jeon, Ph.D. (USA) aka @chamnit
* Lead Developer [_2011 - Current_]: Sungeun "Sonny" Jeon, Ph.D. (USA) aka @chamnit
* Lead Developer [_2009 - 2011_]: Simen Svale Skogsrud (Norway). aka The Originator/Creator/Pioneer/Father of Grbl.
* This work is built on the wonderful Grbl v0.6 firmware in 2011 written by Simen Svale Skogsrud (Norway).
***
@ -31,11 +31,9 @@ Grbl includes full acceleration management with look ahead. That means the contr
***
##Update Summary for v1.0c
##Update Summary for v1.0
- **IMPORTANT:** Your EEPROM will be wiped and restored with new settings. This is due to the addition of two new spindle speed '$' settings.
- Altered limit pin status reports from `Lim:000` to `Pin:000|0|0000`, where the `|` delimiters separate the new probe state and control pin states. Each new field may be disabled by the `$10` Grbl setting. NOTE: Commenting `REPORT_ALL_PIN_STATES` in config.h reverts to old `Lim:` reports, if needed.
- New safety door parking motion as a compile-option. Grbl will retract, disable the spindle/coolant, and park near Z max. When resumed, it will perform these task in reverse order and continue the program. Highly configurable. See config.h for details.
- New '$' Grbl settings for max and min spindle rpm. Allows for tweaking the PWM output to more closely match true spindle rpm. When max rpm is set to zero or less than min rpm, the PWM pin D11 will act like a simple enable on/off output.
@ -44,12 +42,14 @@ Grbl includes full acceleration management with look ahead. That means the contr
- **NOTE:** Arduino Mega2560 support has been moved to an active, official Grbl-Mega [project](http://www.github.com/gnea/grbl-Mega/). All new developments here and there will be synced when it makes sense to.
- Single file configuration for custom firmware.
- A few bug fixes and lots of refactoring to make the code more efficient and flexible.
-
```
```
List of Supported G-Codes in Grbl v0.9 Master:
- Non-Modal Commands: G4, G10L2, G10L20, G28, G30, G28.1, G30.1, G53, G92, G92.1
- Motion Modes: G0, G1, G2, G3, G38.2, G38.3, G38.4, G38.5, G80

Wyświetl plik

@ -0,0 +1,9 @@
1,Hard limit,Hard limit has been triggered. Machine position is likely lost due to sudden stop. Re-homing is highly recommended.
2,Soft limit,G-code motion target exceeds machine travel. Machine position safely retained. Alarm may be unlocked.
3,Abort during cycle,Reset while in motion. Grbl cannot guarantee position. Lost steps are likely. Re-homing is highly recommended.
4,Probe fail,If probe is not in the expected initial state before starting probe cycle when G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered.
5,Probe fail,If the probe fails to contact the workpiece within the programmed travel for G38.2 and G38.4.
6,Homing fail,If the active homing cycle was reset.
7,Homing fail,If the safety door was opened during homing cycle.
8,Homing fail,Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring.
9,Homing fail,Failed to find limit switch within travel. Defined as `1.5 * max_travel` on search and `5 * pulloff` on locate phases.
1 1 Hard limit Hard limit has been triggered. Machine position is likely lost due to sudden stop. Re-homing is highly recommended.
2 2 Soft limit G-code motion target exceeds machine travel. Machine position safely retained. Alarm may be unlocked.
3 3 Abort during cycle Reset while in motion. Grbl cannot guarantee position. Lost steps are likely. Re-homing is highly recommended.
4 4 Probe fail If probe is not in the expected initial state before starting probe cycle when G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered.
5 5 Probe fail If the probe fails to contact the workpiece within the programmed travel for G38.2 and G38.4.
6 6 Homing fail If the active homing cycle was reset.
7 7 Homing fail If the safety door was opened during homing cycle.
8 8 Homing fail Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring.
9 9 Homing fail Failed to find limit switch within travel. Defined as `1.5 * max_travel` on search and `5 * pulloff` on locate phases.

Wyświetl plik

@ -0,0 +1,34 @@
1,Expected command letter,G-code words consist of a letter and a value. Letter was not found.
2,Bad number format,Numeric value format is not valid or missing an expected value.
3,Invalid statement,Grbl '$' system command was not recognized or supported.
4,Value < 0`,Negative value received for an expected positive value.
5,Setting disabled,Homing cycle is not enabled via settings.
6,Value < 3 usec,Minimum step pulse time must be greater than 3usec.
7,EEPROM read fail. Using defaults,EEPROM read failed. Reset and restored to default values.
8,Not idle,Grbl '$' command cannot be used unless Grbl is IDLE. Ensures smooth operation during a job.
9,G-code lock,G-code locked out during alarm or jog state.
10,Homing not enabled,Soft limits cannot be enabled without homing also enabled.
11,Line overflow,Max characters per line exceeded. Line was not processed and executed.
12,Step rate > 30kHz,Grbl '$' setting value exceeds the maximum step rate supported.
13,Check Door,Safety door detected as opened and door state initiated.
14,Line length exceeded,Build info or startup line exceeded EEPROM line length limit.
15,Travel exceeded,Jog target exceeds machine travel. Command ignored.
16,Invalid jog command,Jog command with no '=' or contains prohibited g-code.
20,Unsupported command,Unsupported or invalid g-code command found in block.
21,Modal group violation,More than one g-code command from same modal group found in block.
22,Undefined feed rate,Feed rate has not yet been set or is undefined.
23,Invalid gcode ID:23,G-code command in block requires an integer value.
24,Invalid gcode ID:24,More than one g-code command that requires axis words found in block.
25,Invalid gcode ID:25,Repeated g-code word found in block.
26,Invalid gcode ID:26,No axis words found in block for g-code command or mode which requires them.
27,Invalid gcode ID:27,Line number value is invalid.
28,Invalid gcode ID:28,G-code command is missing a required value word.
29,Invalid gcode ID:29,Work coordinate system commanded not supported.
30,Invalid gcode ID:30,G53 only allowed during G0 and G1 motion modes.
31,Invalid gcode ID:31,Axis words found in block while no command uses them.
32,Invalid gcode ID:32,G2/3 arcs require at least one in-plane axis word.
33,Invalid gcode ID:33,Motion command target is invalid.
34,Invalid gcode ID:34,Arc radius value is invalid.
35,Invalid gcode ID:35,G2/3 arcs require at least one in-plane offset word.
36,Invalid gcode ID:36,Unused value words found in block.
37,Invalid gcode ID:37,G43.1 dynamic tool length offset assigned to wrong axis.
1 1 Expected command letter G-code words consist of a letter and a value. Letter was not found.
2 2 Bad number format Numeric value format is not valid or missing an expected value.
3 3 Invalid statement Grbl '$' system command was not recognized or supported.
4 4 Value < 0` Negative value received for an expected positive value.
5 5 Setting disabled Homing cycle is not enabled via settings.
6 6 Value < 3 usec Minimum step pulse time must be greater than 3usec.
7 7 EEPROM read fail. Using defaults EEPROM read failed. Reset and restored to default values.
8 8 Not idle Grbl '$' command cannot be used unless Grbl is IDLE. Ensures smooth operation during a job.
9 9 G-code lock G-code locked out during alarm or jog state.
10 10 Homing not enabled Soft limits cannot be enabled without homing also enabled.
11 11 Line overflow Max characters per line exceeded. Line was not processed and executed.
12 12 Step rate > 30kHz Grbl '$' setting value exceeds the maximum step rate supported.
13 13 Check Door Safety door detected as opened and door state initiated.
14 14 Line length exceeded Build info or startup line exceeded EEPROM line length limit.
15 15 Travel exceeded Jog target exceeds machine travel. Command ignored.
16 16 Invalid jog command Jog command with no '=' or contains prohibited g-code.
17 20 Unsupported command Unsupported or invalid g-code command found in block.
18 21 Modal group violation More than one g-code command from same modal group found in block.
19 22 Undefined feed rate Feed rate has not yet been set or is undefined.
20 23 Invalid gcode ID:23 G-code command in block requires an integer value.
21 24 Invalid gcode ID:24 More than one g-code command that requires axis words found in block.
22 25 Invalid gcode ID:25 Repeated g-code word found in block.
23 26 Invalid gcode ID:26 No axis words found in block for g-code command or mode which requires them.
24 27 Invalid gcode ID:27 Line number value is invalid.
25 28 Invalid gcode ID:28 G-code command is missing a required value word.
26 29 Invalid gcode ID:29 Work coordinate system commanded not supported.
27 30 Invalid gcode ID:30 G53 only allowed during G0 and G1 motion modes.
28 31 Invalid gcode ID:31 Axis words found in block while no command uses them.
29 32 Invalid gcode ID:32 G2/3 arcs require at least one in-plane axis word.
30 33 Invalid gcode ID:33 Motion command target is invalid.
31 34 Invalid gcode ID:34 Arc radius value is invalid.
32 35 Invalid gcode ID:35 G2/3 arcs require at least one in-plane offset word.
33 36 Invalid gcode ID:36 Unused value words found in block.
34 37 Invalid gcode ID:37 G43.1 dynamic tool length offset assigned to wrong axis.

Wyświetl plik

@ -0,0 +1,124 @@
## Meanings of Grbl messages and error/alarm codes
#### _'error:' Codes_
Format - `(v1.0)` `:` `(v0.9)` - `Description`
- `error:1` : `error: Expected command letter` - G-code words consist of a letter and a value. Letter was not found.
- `error:2` : `error: Bad number format` - Numeric value format is not valid or missing an expected value.
- `error:3` : `error: Invalid statement` - Grbl '$' system command was not recognized or supported
- `error:4` : `error: Value < 0` - Negative value received for an expected positive value.
- `error:5` : `error: Setting disabled` - Homing cycle is not enabled via settings.
- `error:6` : `error: Value < 3 usec` - Minimum step pulse time must be greater than 3usec
- `error:7` : `error: EEPROM read fail. Using defaults` - EEPROM read failed. Reset and restored to default values.
- `error:8` : `error: Not idle` - Grbl '$' command cannot be used unless Grbl is IDLE. Ensures smooth operation during a job.
- `error:9` : `error: G-code lock` - G-code locked out during alarm or jog state
- `error:10` : `error: Homing not enabled` - Soft limits cannot be enabled without homing also enabled.
- `error:11` : `error: Line overflow` - Max characters per line exceeded. Line was not processed and executed.
- `error:12` : `error: Step rate > 30kHz`* - Grbl '$' setting value exceeds the maximum step rate supported.
- `error:13` : `error: Check Door` - Safety door detected as opened and door state initiated.
- `error:14` : `error: Line length exceeded` - (Grbl-Mega Only) Build info or startup line exceeded EEPROM line length limit.
- `error:15` : `error: Travel exceeded` - Jog target exceeds machine travel. Command ignored.
- `error:16` : `error: Invalid jog command` - Jog command with no '=' or contains prohibited g-code.
- `error:20` : `error: Unsupported command` - Unsupported or invalid g-code command found in block.
- `error:21` : `error: Modal group violation` - More than one g-code command from same modal group found in block.
- `error:22` : `error: Undefined feed rate` - Feed rate has not yet been set or is undefined.
- `error:23` : `error: Invalid gcode ID:23` - G-code command in block requires an integer value.
- `error:24` : `error: Invalid gcode ID:24` - More than one g-code command that requires axis words found in block.
- `error:25` : `error: Invalid gcode ID:25` - Repeated g-code word found in block.
- `error:26` : `error: Invalid gcode ID:26` - No axis words found in block for g-code command or mode which requires them.
- `error:27` : `error: Invalid gcode ID:27` - Line number value is invalid
- `error:28` : `error: Invalid gcode ID:28` - G-code command is missing a required value word.
- `error:29` : `error: Invalid gcode ID:29` - Work coordinate system commanded not supported.
- `error:30` : `error: Invalid gcode ID:30` - G53 only allowed during G0 and G1 motion modes.
- `error:31` : `error: Invalid gcode ID:31` - Axis words found in block while no command uses them.
- `error:32` : `error: Invalid gcode ID:32` - G2/3 arcs require at least one in-plane axis word.
- `error:33` : `error: Invalid gcode ID:33` - Motion command target is invalid.
- `error:34` : `error: Invalid gcode ID:34` - Arc radius value is invalid.
- `error:35` : `error: Invalid gcode ID:35` - G2/3 arcs require at least one in-plane offset word.
- `error:36` : `error: Invalid gcode ID:36` - Unused value words found in block.
- `error:37` : `error: Invalid gcode ID:37` - G43.1 dynamic tool length offset assigned to wrong axis.
`*` indicates feedback enabled only by compile-time option.
-----
#### 'Alarm:' Codes
Format - `(v1.0)` `:` `(v0.9)` - `Description`
- `ALARM:1` : `ALARM: Hard limit` - Hard limit has been triggered. Machine position is likely lost due to sudden stop. Re-homing is highly recommended.
`
- `ALARM:2` : `ALARM: Soft limit` - G-code motion target exceeds machine travel. Machine position safely retained. Alarm may be unlocked.
- `ALARM:3` : `ALARM: Abort during cycle` - Reset while in motion. Grbl cannot guarantee position. Lost steps are likely. Re-homing is highly recommended.
- `ALARM:4` : `ALARM: Probe fail` - If probe is not in the expected initial state before starting probe cycle, where G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered.
- `ALARM:5` : `ALARM: Probe fail` - If the probe fails to contact the workpiece within the programmed travel for G38.2 and G38.4.
- `ALARM:6` : `ALARM: Homing fail` - If the active homing cycle was reset.
- `ALARM:7` : `ALARM: Homing fail` - If the safety door was opened during homing cycle.
- `ALARM:8` : `ALARM: Homing fail` - Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring.
- `ALARM:9` : `ALARM: Homing fail` - Failed to find limit switch within travel. Defined as `1.5 * max_travel` on search and `5 * pulloff` on locate phases.
-----
#### Message Descriptions
Format - `Message` - `Description`
- `[Reset to continue]` - Critical event message. Reset is required before Grbl accepts any other commands. This prevents ongoing command streaming and risking a motion before the alarm is acknowledged. Hard or soft limit errors will trigger this event.
- `[$H|$X to unlock]`- Alarm message at initialization. All g-code commands and some $ are blocked until unlocked via homing or $X.
- `[Caution: Unlocked]` - Alarm unlock $X acknowledgement.
- `[Enabled]` - Indicates Grbls check-mode is enabled.
- `[Disabled]` - Indicates Grbls check-mode is disabled. Grbl is automatically reset afterwards.
- `[Check Door]` - Safety door detected as open. This message appears either immediately upon a safety door ajar or if the safety is open when Grbl initializes after a power-up/reset.
- `[Check Limits]` - If Grbl detects a limit switch is triggered after power-up/reset and hard limits are enabled, this will appear as a courtesy message.
- `[Pgm End]` - M2/30 program end message to denote g-code modes have been restored to defaults according to the M2/30 g-code description.
- `[Restoring defaults]` - Acknowledgement message when restoring EEPROM defaults via a `$RST=` command.

Wyświetl plik

@ -0,0 +1,41 @@
## Grbl v1.0 Jogging
Executing a jog requires a specific command structure, as described below:
- The first three characters must be '$J=' to indicate the jog.
- The jog command follows immediate after the '=' and works like a normal G1 command.
- Feed rate is only interpreted in G94 units per minute. A prior G93 state is ignored during jog.
- Required words:
- XYZ: One or more axis words with target value.
- F - Feed rate value. NOTE: Each jog requires this value and is not treated as modal.
- Optional words: Jog executes based on current G20/G21 and G90/G91 g-code parser state. If one
of the following optional words is passed, that state is overridden for one command only.
- G20 or G21 - Inch and millimeter mode
- G90 or G91 - Absolute and incremental distances
- G53 - Move in machine coordinates
- All other g-codes, m-codes, and value words are not accepted in the jog command.
- Spaces and comments are allowed in the command. These are removed by the pre-parser.
- Example: G21 and G90 are active modal states prior to jogging. These are sequential commands.
- `$J=X10.0 Y-1.5` will move to X=10.0mm and Y=-1.5mm in work coordinate frame (WPos).
- `$J=G91 G20 X0.5` will move +0.5 inches (12.7mm) to X=22.7mm (WPos). Note that G91 and G20 are only applied to this jog command.
- `$J=G53 Y5.0` will move the machine to Y=5.0mm in the machine coordinate frame (MPos). If the work coordinate offset for the y-axis is 2.0mm, then Y is 3.0mm in (WPos).
Jog commands behave almost identically to normal g-code streaming. Every jog command will
return an 'ok' when the jogging motion has been parsed and is setup for execution. If a
command is not valid, Grbl will return an 'error:'. Multiple jogging commands may be
queued in sequence.
The main differences are:
- During a jog, Grbl will report a 'Jog' state while executing the jog.
- A jog command will only be accepted when Grbl is in either the 'Idle' or 'Jog' states.
- Jogging motions may not be mixed with g-code commands while executing, which will return
a lockout error, if attempted.
- All jogging motion(s) may be cancelled at anytime with a simple feed hold command. Grbl
will automatically flush Grbl's internal buffers of any queued jogging motions and return
to the 'Idle' state. No soft-reset required.
- IMPORTANT: Jogging does not alter the g-code parser state. Hence, no g-code modes need to
be explicitly managed, unlike previous ways of implementing jogs with commands like
'G91G1X1F100'. Since G91, G1, and F feed rates are modal and if they are not changed
back prior to resuming/starting a job, a job may not run how its was intended and result
in a crash.

Wyświetl plik

@ -0,0 +1,135 @@
## Grbl v1.0 Realtime commands
Realtime commands are single control characters that may be sent to Grbl to command and perform an action in real-time, regardless of what Grbl is doing at the time. These commands include a reset, feed hold, resume, status report query, and overrides (in v1.0).
A realtime command:
- Will execute within tens of milliseconds.
- Is a single character that may be sent to Grbl at any time.
- Does not require a line feed or carraige return after them.
- Is not considered a part of the streaming protocol.
- Will ignore multiple commands until it has executed the first received command.
- May be tied to an input pin and may be operated with a button or switch.
- Actions depends on state or what Grbl is doing. It may not do anything.
- Descriptions explain how they work and what to expect.
#### ASCII Realtime Command Descriptions
The normal ASCII realtime command characters used in Grbl v0.9 have been retained in Grbl v1.0 and are described below for completeness.
- `0x18` (ctrl-x) : Soft-Reset
- Immediately halts and resets Grbl.
- Accepts and executes this command at any time.
- If reset while in motion, Grbl will throw an alarm to indicate position may be lost from the motion halt.
- If reset while in not motion, position is retained and re-homing is not required.
- An input pin is available to connect a button or switch.
- `?` : Status Report Query
- Immediately generates and sends back runtime data with a status report.
- Accepts and executes this command at any time, except during a homing cycle and when critical alarm (hard/soft limit error) is thrown.
- `~` : Cycle Start / Resume
- Resumes a feed hold, a safety door/parking state when the door is closed, and the M0 program pause states.
- Command is otherwise ignored.
- If the parking compile-time option is enabled and the safety door state is ready to resume, Grbl will re-enable the spindle and coolant, move back into position, and then resume.
- An input pin is available to connect a button or switch.
- `!` : Feed Hold
- Places Grbl into a suspend or HOLD state. If in motion, the machine will decelerate to a stop and then be suspended.
- Command executes when Grbl is in an IDLE, RUN, or JOG state. It is otherwise ignored.
- By machine control definition, a feed hold does not disable the spindle or coolant. Only motion.
- An input pin is available to connect a button or switch.
#### Extended-ASCII Realtime Command Descriptions
Grbl v1.0 installed more than a dozen new realtime commands to control feed, rapid, and spindle overrides. To help prevent users from inadvertently altering overrides with a keystroke and allow for more commands later on, all of the new control characters have been moved to the extended ASCII character set. These are not readily type-able on a keyboard, but, depending on the OS, they may be entered using specific keystroke and code. GUI developers will need to be able to send extended ASCII characters, values `128 (0x80)` to `255 (0xFF)`, to Grbl to take advantage of these new features.
- `0x84` : Safety Door
- Although typically connected to an input pin to detect the opening of a safety door, this command allows a GUI to enact the safety door behavior with this command.
- Immediately suspends into a DOOR state and disables the spindle and coolant. If in motion, the machine will decelerate to a stop and then be suspended.
- If executed during homing, Grbl will instead halt motion and throw a homing alarm.
- If already in a suspend state or HOLD, the DOOR state supersedes it.
- If the parking compile-time option is enabled, Grbl will park the spindle to a specified location.
- Command executes when Grbl is in an IDLE, HOLD, RUN, HOMING, or JOG state. It is otherwise ignored.
- An input pin is available to connect a button or switch, if enabled with a compile-time option.
- Some builds of Grbl v0.9 used the `@` character for this command, but it was undocumented. Moved to extended-ASCII to prevent accidental commanding.
- Feed Overrides
- Immediately alters the feed override value. An active feed motion is altered within tens of milliseconds.
- Does not alter rapid rates, which include G0, G28, and G30, or jog motions.
- Feed override value can not be 1% or greater than 200%
- If feed override value does not change, the command is ignored.
- Feed override range and increments may be changed in config.h.
- The commands are:
- `0x90` : Set 100% of programmed rate.
- `0x91` : Increase 10%
- `0x92` : Decrease 10%
- `0x93` : Increase 1%
- `0x94` : Decrease 1%
- Rapid Overrides
- Immediately alters the rapid override value. An active rapid motion is altered within tens of milliseconds.
- Only effects rapid motions, which include G0, G28, and G30.
- If rapid override value does not change, the command is ignored.
- Rapid override set values may be changed in config.h.
- The commands are:
- `0x95` : Set to 100% full rapid rate.
- `0x96` : Set to 50% of rapid rate.
- `0x97` : Set to 25% of rapid rate.
- Spindle Speed Overrides
- Immediately alters the spindle speed override value. An active spindle speed is altered within tens of milliseconds.
- Override values may be changed at any time, regardless of if the spindle is enabled or disabled.
- Spindle override value can not be 50% or greater than 200%
- If spindle override value does not change, the command is ignored.
- Spindle override range and increments may be altered in config.h.
- The commands are:
- `0x99` : Set 100% of programmed spindle speed
- `0x9A` : Increase 10%
- `0x9B` : Decrease 10%
- `0x9C` : Increase 1%
- `0x9D` : Decrease 1%
- `0x9E` : Toggle Spindle Stop
- Toggles spindle enable or disable state immediately, but only while in the HOLD.
- The command is otherwise ignored, especially while in motion. This prevents accidental disabling during a job that can either destroy the part/machine or personal injury. Industrial machines handle the spindle stop override similarly.
- When motion restarts via cycle start, the last spindle state will be restored and wait 4.0 seconds (configurable) before resuming the tool path. This ensures the user doesn't forget to turn it back on.
- While disabled, spindle speed override values may still be altered and will be in effect once the spindle is re-enabled.
- If a safety door is opened, the DOOR state will supercede the spindle stop override, where it will manage the spindle re-energizing itself upon closing the door and resuming. The prior spindle stop override state is cleared and reset.
- `0xA0` : Toggle Flood Coolant
- Toggles flood coolant state and output pin until the next toggle or g-code command alters it.
- May be commanded at any time while in IDLE, RUN, or HOLD states. It is otherwise ignored.
- This override directly changes the coolant modal state in the g-code parser. Grbl will continue to operate normally like it received and executed an `M8` or `M9` g-code command.
- When `$G` g-code parser state is queried, the toggle override change will be reflected by an `M8` enabled or disabled with an `M9` or not appearing when `M7` is present.
- `0xA1` : Toggle Mist Coolant
- Enabled by `ENABLE_M7` compile-time option. Default is disabled.
- Toggles mist coolant state and output pin until the next toggle or g-code command alters it.
- May be commanded at any time while in IDLE, RUN, or HOLD states. It is otherwise ignored.
- This override directly changes the coolant modal state in the g-code parser. Grbl will continue to operate normally like it received and executed an `M7` or `M9` g-code command.
- When `$G` g-code parser state is queried, the toggle override change will be reflected by an `M7` enabled or disabled with an `M9` or not appearing when `M8` is present.

Wyświetl plik

@ -0,0 +1,279 @@
### _Grbl v1.0 Realtime Status Reports_ (Rev. 2)
--------
#### Summary of Changes from Grbl v0.9 Reports
- Intent of changes is to make parsing cleaner, reduce transmitting overhead without effecting overall Grbl performance, and add more feedback data, which includes three new override values and real-time velocity.
- Data fields are separated by `|` pipe delimiters, rather than `,` commas that were used to separate data values. This should help with parsing.
- The ability to mask and add/remove data fields from status reports via the `$10` status report mask setting has been disabled. Only selecting `MPos:` or `WPos:` coordinates is allowed.
- All available data is always sent to standardize the reports across all GUIs.
- For unique situations, data fields can be removed by config.h macros, but it is highly recommended to not alter these.
- `MPos:` OR `WPos:` are always included in a report, but not BOTH at the same time.
- This reduces transmit overhead tremendously by removing upwards to 40 characters.
- `WCO:0.000,10.000,2.500` A current work coordinate offset is now sent to easily convert between position vectors, where `WPos = MPos - WCO` for each axis.
- `WCO:` is included immediately whenever a `WCO:` value changes or intermittently after every **X** status reports as a refresh. Refresh rates can dynamically vary from 10 to 30 (configurable) reports depending on what Grbl is doing.
- `WCO:` is simply the sum of the work coordinate system, G92, and G43.1 tool length offsets.
- Basically, a GUI just needs to retain the last `WCO:` and apply the equation to get the other position vector.
- `WCO:` messages may only be disabled via a config.h compile-option, if a GUI wants to handle the work position calculations on its own to free up more transmit bandwidth.
- Be aware of the following issue regarding `WPos:`.
- In Grbl v0.9 and prior, there is an old outstanding bug where the `WPos:` work position reported may not correlate to what is executing, because `WPos:` is based on the g-code parser state, which can be several motions behind. Grbl v1.0 now forces the planner buffer to empty, sync, and stops motion whenever there is a command that alters the work coordinate offsets `G10,G43.1,G92,G54-59`. This is the simplest way to ensure `WPos:` is always correct. Fortunately, it's exceedingly rare that any of these commands are used need continuous motions through them.
- A compile-time option is available to disable the planner sync and forced stop, but, if used, it's up to the GUI to handle this position correlation issue.
- The `Hold` and `Door` states includes useful sub-state info via a `:` colon delimiter and an integer value. See descriptions for details.
- Limit and other input pin reports have significantly changed to reduce transmit overhead.
- The data type description is now just `Pn:`, rather than `Lim:000` or `Pin:000|0|0000`
- It does not appear if no inputs are detected as triggered.
- If an input is triggered, ```Pn:``` will be followed by a letter or set of letters of every triggered input pin. `XYZPDHRS` for the XYZ-axes limits, Probe, Door, Hold, soft-Reset, cycle Start pins, respectively.
- For example, a triggered Z-limit and probe pin would report `Pn:ZP`.
- Buffer data (planner and serial RX) reports have been tweaked and combined.
- `Bf:0,0`. The first value is planner blocks in use and the second is RX bytes in use.
- Override reports are intermittent since they don't change often once set.
- Overrides are included in every 10 or 20 status reports (configurable) depending on what Grbl is doing or, if an override value or toggle state changes, automatically in the next report.
- There are two override fields:
- `Ov:100,100,100` Organized as feed, rapid, and spindle speed overrides in percent.
- `T:SFM` with each letter `S`, `F`, and `M` are defined as spindle stop active, flood coolant toggled, and mist coolant toggled, respectively.
- Line numbers, when enabled in config.h, are omitted when:
- No line number is passed to Grbl in a block.
- Grbl is performing a system motion like homing, jogging, or parking.
- Grbl is executing g-code block that does not contain a motion, like `G20G54` or `G4P1` dwell. (NOTE: Looking to fixing this later.)
-------
#### Basic Characteristics
- Contains real-time data of Grbls state, position, and other data required independently of the stream.
- Categorized as a real-time message, where it is a separate message that should not be counted as part of the streaming protocol. It may appear at any given time.
- A status report is initiated by sending Grbl a '?' character.
- Like all real-time commands, the '?' character is intercepted and never enters the serial buffer. It's never a part of the stream and can be sent at any time.
- Grbl will generate and transmit a report within ~5-20 milliseconds.
- Every ? command sent by a GUI is not guaranteed with a response. The following are the current scenarios when Grbl may not immediately or ignore a status report request. _NOTE: These may change in the future and will be documented here._
- If two or more '?' queries are sent before the first report is generated, the additional queries are ignored.
- A soft-reset commanded clears the last status report query.
- When Grbl throws a critical alarm from a limit violation. A soft-reset is required to resume operation.
- During a homing cycle.
#### Message Construction:
- A message is a single line of ascii text, completed by a carriage return and line feed.
- `< >` Chevrons uniquely enclose reports to indicate message type.
- `|` Pipe delimiters separate data fields inside the report.
- The first data field is an exception to the following data field rules. See 'Machine State' description for details.
- All remaining data fields consist of a data type followed by a `:` colon delimiter and data values. `type:value(s)`
- Data values are given either as as one or more pre-defined character codes to indicate certain states/conditions or as numeric values, which are separated by a `,` comma delimiter when more than one is present. Numeric values are also in a pre-defined order and units of measure.
- The first (Machine State) and second (Current Position) data fields are always included in every report.
- Assume any following data field may or may not exist and can be in any order. The `$10` status report mask setting can alter what data is present and certain data fields can be reported intermittently (see descriptions for details.)
- The `$13` report inches settings alters the units of some data values. `$13=0` false indicates mm-mode, while `$13=1` true indicates inch-mode reporting. Keep note of this setting and which report values can be altered.
- _Data Field Descriptions:_
- **Machine State:**
- Valid states types: `Idle, Run, Hold, Jog, Alarm, Door, Check, Home`
- Sub-states may be included via `:` a colon delimiter and numeric code.
- Current sub-states are:
- `Hold:0` Hold complete. Ready to resume.
- `Hold:1` Hold in-progress. Reset will throw an alarm.
- `Door:0` Door closed. Ready to resume.
- `Door:1` Machine stopped. Door still ajar. Can't resume until closed.
- `Door:2` Door opened. Hold (or parking retract) in-progress. Reset will throw an alarm.
- `Door:3` Door closed and resuming. Restoring from park, if applicable. Reset will throw an alarm.
- This data field is always present as the first field.
- **Current Position:**
- Depending on `$10` status report mask settings, position may be sent as either:
- `MPos:0.000,-10.000,5.000` machine position or
- `WPos:-2.500,0.000,11.000` work position
- Three position values are given in the order of X, Y, and Z. A fourth position value may exist in later versions for the A-axis.
- `$13` report inches user setting effects these values and is given as either mm or inches.
- This data field is always present as the second field.
- **Work Coordinate Offset:**
- `WCO:0.000,1.551,5.664` is the current work coordinate offset of the g-code parser, which is the sum of the current work coordinate system, G92 offsets, and G43.1 tool length offset.
- Machine position and work position are related by this simple equation per axis: `WPos = MPos - WCO`
- Values are given in the order of the X,Y, and Z axes offsets. A fourth offset value may exist in later versions for the A-axis.
- `$13` report inches user setting effects these values and is given as either mm or inches.
- `WCO:` values don't change often during a job once set and only requires intermittent refreshing.
- This data field appears:
- In every 10 or 30 (configurable 1-255) status reports, depending on if Grbl is in a motion state or not.
- Immediately in the next report, if an offset value has changed.
- In the first report after a reset/power-cycle.
- This data field will not appear if:
- It is disabled in the config.h file. No `$` mask setting available.
- The refresh counter is in-between intermittent reports.
- **Buffer State:**
- `Bf:0,0`. The first value is planner blocks in use and the second is RX bytes in use.
- This data field will not appear if:
- It is disabled by the `$` status report mask setting.
- **Line Number:**
- `Ln:99999` indicates line 99999 is currently being executed. This differs from the `$G` line `N` value since the parser is usually queued few blocks behind execution.
- Compile-time option only because of memory requirements. However, if a GUI passes indicator line numbers onto Grbl, it's very useful to determine when Grbl is executing them.
- This data field will not appear if:
- It is disabled in the config.h file. No `$` mask setting available.
- The line number reporting not enabled in config.h. Different option to reporting data field.
- No line number or `N0` is passed with the g-code block.
- Grbl is homing, jogging, parking, or performing a system task/motion.
- There is no motion in the g-code block like a `G4P1` dwell. (May be fixed in later versions.)
- **Current Rate:**
- `F:1000.` indicates current actual feed rate (speed) of the executing motion. Depending on machine max rate settings and acceleration, this value may not be the programmed rate.
- Value units, either in mm/min or inches/min, is dependent on the `$` report inches user setting.
- As a operational note, reported rate is typically 30-50 msec behind actual position reported.
- This data field will not appear if:
- It is disabled in the config.h file. No `$` mask setting available.
- **Input Pin State:**
- `Pn:XYZPDHRS` indicates which input pins Grbl has detected as 'triggered'.
- Pin state is evaluated every time a status report is generated. All input pin inversions are appropriately applied to determine 'triggered' states.
- Each letter of `XYZPDHRS` denotes a particular 'triggered' input pin.
- `X Y Z` XYZ limit pins, respectively
- `P` the probe pin.
- `D H R S` the door, hold, soft-reset, and cycle-start pins, respectively.
- Example: `Pn:PZ` indicates the probe and z-limit pins are 'triggered'.
- Note: `A` may be added in later versions for an A-axis limit pin.
- Assume input pin letters are presented in no particular order.
- One or more 'triggered' pin letter(s) will always be present with a `Pn:` data field.
- This data field will not appear if:
- It is disabled in the config.h file. No `$` mask setting available.
- No input pins are detected as triggered.
- **Override Values:**
- `Ov:100,100,100` indicates current override values in percent of programmed values for feed, rapids, and spindle speed, respectively.
- Override values don't change often during a job once set and only requires intermittent refreshing. This data field appears:
- After 10 or 20 (configurable 1-255) status reports, depending on is in a motion state or not.
- If an override value has changed, this data field will appear immediately in the next report. However, if `WCO:` is present, this data field will be delayed one report.
- In the second report after a reset/power-cycle.
- This data field will not appear if:
- It is disabled in the config.h file. No `$` mask setting available.
- The override refresh counter is in-between intermittent reports.
- `WCO:` exists in current report during refresh. Automatically set to try again on next report.
- **Toggle Overrides:**
- `T:SFM` indicates a toggle override is in effect or has been commanded.
- Like the pin state field, each letter denotes a particular toggle override.
- `S` indicates the spindle stop toggle override is in effect. It will appear as long as the spindle stop override is active.
- `F` indicates the flood coolant toggle override was activated. It will only appear once after it has executed the coolant state change.
- `M` indicates the mist coolant toggle override was activated, if mist coolant is enabled via config.h. It will only appear once after it has executed the coolant state change.
- Assume toggle override letters are presented in no particular order.
- One or more active toggle override letter(s) will always be present with a `T:` data field.
- This data field appears:
- If a toggle override is active or has recently executed and only when the override values field is also present (see override value field rules).
- This data field will not appear if:
- If no toggle override is active or has been executed.
- It is disabled in the config.h file. No `$` mask setting available.
- If override refresh counter is in-between intermittent reports.
- `WCO:` exists in current report during refresh. Automatically set to try again on next report.

Wyświetl plik

@ -2,7 +2,7 @@
config.h - compile time configuration
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
// This file contains compile-time configurations for Grbl's internal system. For the most part,
// users will not need to directly modify these, but they are here for specific needs, i.e.
// performance tuning or adjusting to non-typical machines.
@ -38,19 +38,49 @@
#define CPU_MAP_ATMEGA328P // Arduino Uno CPU
// Serial baud rate
// #define BAUD_RATE 230400
#define BAUD_RATE 115200
// Define realtime command special characters. These characters are 'picked-off' directly from the
// serial read data stream and are not passed to the grbl line execution parser. Select characters
// that do not and must not exist in the streamed g-code program. ASCII control characters may be
// used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in
// that do not and must not exist in the streamed g-code program. ASCII control characters may be
// used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in
// g-code programs, maybe selected for interface programs.
// NOTE: If changed, manually update help message in report.c.
#define CMD_STATUS_REPORT '?'
#define CMD_FEED_HOLD '!'
#define CMD_CYCLE_START '~'
#define CMD_RESET 0x18 // ctrl-x.
#define CMD_SAFETY_DOOR '@'
#define CMD_STATUS_REPORT '?'
#define CMD_CYCLE_START '~'
#define CMD_FEED_HOLD '!'
// #define CMD_SAFETY_DOOR '@' // Moved to extended ASCII.
// NOTE: All override realtime commands must be in the extended ASCII character set, starting
// at character value 128 (0x80) and up to 255 (0xFF). If the normal set of realtime commands,
// such as status reports, feed hold, reset, and cycle start, are moved to the extended set
// space, serial.c's RX ISR will need to be modified to accomodate the change.
// #define CMD_RESET 0x80
// #define CMD_STATUS_REPORT 0x81
// #define CMD_CYCLE_START 0x82
// #define CMD_FEED_HOLD 0x83
#define CMD_SAFETY_DOOR 0x84
#define CMD_DEBUG_REPORT 0x85 // Only when DEBUG enabled, sends debug report in '{}' braces.
#define CMD_FEED_OVR_RESET 0x90 // Restores feed override value to 100%.
#define CMD_FEED_OVR_COARSE_PLUS 0x91
#define CMD_FEED_OVR_COARSE_MINUS 0x92
#define CMD_FEED_OVR_FINE_PLUS 0x93
#define CMD_FEED_OVR_FINE_MINUS 0x94
#define CMD_RAPID_OVR_RESET 0x95 // Restores rapid override value to 100%.
#define CMD_RAPID_OVR_MEDIUM 0x96
#define CMD_RAPID_OVR_LOW 0x97
// #define CMD_RAPID_OVR_EXTRA_LOW 0x98 // *NOT SUPPORTED*
#define CMD_SPINDLE_OVR_RESET 0x99 // Restores spindle override value to 100%.
#define CMD_SPINDLE_OVR_COARSE_PLUS 0x9A
#define CMD_SPINDLE_OVR_COARSE_MINUS 0x9B
#define CMD_SPINDLE_OVR_FINE_PLUS 0x9C
#define CMD_SPINDLE_OVR_FINE_MINUS 0x9D
#define CMD_SPINDLE_OVR_STOP 0x9E
#define CMD_COOLANT_FLOOD_OVR_TOGGLE 0xA0
#define CMD_COOLANT_MIST_OVR_TOGGLE 0xA1
// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
// the user to perform the homing cycle (or override the locks) before doing anything else. This is
@ -59,17 +89,17 @@
// Define the homing cycle patterns with bitmasks. The homing cycle first performs a search mode
// to quickly engage the limit switches, followed by a slower locate mode, and finished by a short
// pull-off motion to disengage the limit switches. The following HOMING_CYCLE_x defines are executed
// pull-off motion to disengage the limit switches. The following HOMING_CYCLE_x defines are executed
// in order starting with suffix 0 and completes the homing routine for the specified-axes only. If
// an axis is omitted from the defines, it will not home, nor will the system update its position.
// Meaning that this allows for users with non-standard cartesian machines, such as a lathe (x then z,
// with no y), to configure the homing cycle behavior to their needs.
// with no y), to configure the homing cycle behavior to their needs.
// NOTE: The homing cycle is designed to allow sharing of limit pins, if the axes are not in the same
// cycle, but this requires some pin settings changes in cpu_map.h file. For example, the default homing
// cycle can share the Z limit pin with either X or Y limit pins, since they are on different cycles.
// By sharing a pin, this frees up a precious IO pin for other purposes. In theory, all axes limit pins
// may be reduced to one pin, if all axes are homed with seperate cycles, or vice versa, all three axes
// on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits
// on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits
// will not be affected by pin sharing.
// NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y.
#define HOMING_CYCLE_0 (1<<Z_AXIS) // REQUIRED: First move Z to clear workspace.
@ -77,12 +107,12 @@
// #define HOMING_CYCLE_2 // OPTIONAL: Uncomment and add axes mask to enable
// Number of homing cycles performed after when the machine initially jogs to limit switches.
// This help in preventing overshoot and should improve repeatability. This value should be one or
// This help in preventing overshoot and should improve repeatability. This value should be one or
// greater.
#define N_HOMING_LOCATE_CYCLE 1 // 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
// 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.
@ -92,7 +122,7 @@
// parser state depending on user preferences.
#define N_STARTUP_LINE 2 // Integer (1-2)
// Number of floating decimal points printed by Grbl for certain value types. These settings are
// Number of floating decimal points printed by Grbl for certain value types. These settings are
// determined by realistic and commonly observed values in CNC machines. For example, position
// values cannot be less than 0.001mm or 0.0001in, because machines can not be physically more
// precise this. So, there is likely no need to change these, but you can if you need to here.
@ -106,29 +136,24 @@
// If your machine has two limits switches wired in parallel to one axis, you will need to enable
// this feature. Since the two switches are sharing a single pin, there is no way for Grbl to tell
// which one is enabled. This option only effects homing, where if a limit is engaged, Grbl will
// which one is enabled. This option only effects homing, where if a limit is engaged, Grbl will
// alarm out and force the user to manually disengage the limit switch. Otherwise, if you have one
// limit switch for each axis, don't enable this option. By keeping it disabled, you can perform a
// homing cycle while on the limit switch and not have to move the machine off of it.
// #define LIMITS_TWO_SWITCHES_ON_AXES
// Allows GRBL to track and report gcode line numbers. Enabling this means that the planning buffer
// goes from 18 or 16 to make room for the additional line number data in the plan_block_t struct
// goes from 16 to 15 to make room for the additional line number data in the plan_block_t struct
// #define USE_LINE_NUMBERS // Disabled by default. Uncomment to enable.
// Allows GRBL to report the real-time feed rate. Enabling this means that GRBL will be reporting more
// data with each status update.
// 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 4. 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.
// NOTE: The M8 flood coolant control pin on analog pin 3 will still be functional regardless.
// #define ENABLE_M7 // Disabled by default. Uncomment to enable.
// This option causes the feed hold input to act as a safety door switch. A safety door, when triggered,
@ -142,12 +167,12 @@
#define SAFETY_DOOR_SPINDLE_DELAY 4.0 // Float (seconds)
#define SAFETY_DOOR_COOLANT_DELAY 1.0 // Float (seconds)
// Enable CoreXY kinematics. Use ONLY with CoreXY machines.
// IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to
// Enable CoreXY kinematics. Use ONLY with CoreXY machines.
// IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to
// #define HOMING_CYCLE_0 (1<<X_AXIS) and #define HOMING_CYCLE_1 (1<<Y_AXIS)
// NOTE: This configuration option alters the motion of the X and Y axes to principle of operation
// defined at (http://corexy.com/theory.html). Motors are assumed to positioned and wired exactly as
// described, if not, motions may move in strange directions. Grbl assumes the CoreXY A and B motors
// described, if not, motions may move in strange directions. Grbl requires the CoreXY A and B motors
// have the same steps per mm internally.
// #define COREXY // Default disabled. Uncomment to enable.
@ -158,8 +183,8 @@
// #define INVERT_CONTROL_PIN_MASK CONTROL_MASK // Default disabled. Uncomment to disable.
// #define INVERT_CONTROL_PIN_MASK ((1<<CONTROL_SAFETY_DOOR_BIT)|(CONTROL_RESET_BIT)) // Default disabled.
// Inverts select limit pin states based on the following mask. This effects all limit pin functions,
// such as hard limits and homing. However, this is different from overall invert limits setting.
// Inverts select limit pin states based on the following mask. This effects all limit pin functions,
// such as hard limits and homing. However, this is different from overall invert limits setting.
// This build option will invert only the limit pins defined here, and then the invert limits setting
// will be applied to all of them. This is useful when a user has a mixed set of limit pins with both
// normally-open(NO) and normally-closed(NC) switches installed on their machine.
@ -168,52 +193,119 @@
// Inverts the spindle enable pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful
// for some pre-built electronic boards.
// NOTE: If VARIABLE_SPINDLE is enabled(default), this option has no effect as the PWM output and
// spindle enable are combined to one pin. If you need both this option and spindle speed PWM,
// NOTE: If VARIABLE_SPINDLE is enabled(default), this option has no effect as the PWM output and
// spindle enable are combined to one pin. If you need both this option and spindle speed PWM,
// uncomment the config option USE_SPINDLE_DIR_AS_ENABLE_PIN below.
// #define INVERT_SPINDLE_ENABLE_PIN // Default disabled. Uncomment to enable.
// Enable all pin states feedback in status reports. Configurable with Grbl settings to print only
// the desired data, which is presented as simple binary reading of each pin as (0 (low) or 1(high)).
// The fields are printed in a particular order and settings groups are separated by '|' characters.
// NOTE: This option is here for backward compatibility of the old style of pin state reports, i.e.
// `Lim:000`. This new `Pin:` report will be the standard going forward.
#define REPORT_ALL_PIN_STATES // Default enabled. Comment to disable.
// Inverts the selected coolant pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful
// for some pre-built electronic boards.
// #define INVERT_COOLANT_FLOOD_PIN // Default disabled. Uncomment to enable.
// #define INVERT_COOLANT_MIST_PIN // Default disabled. Note: Enable M7 mist coolant in config.h
// When Grbl powers-cycles or is hard reset with the Arduino reset button, Grbl boots up with no ALARM
// by default. This is to make it as simple as possible for new users to start using Grbl. When homing
// is enabled and a user has installed limit switches, Grbl will boot up in an ALARM state to indicate
// is enabled and a user has installed limit switches, Grbl will boot up in an ALARM state to indicate
// Grbl doesn't know its position and to force the user to home before proceeding. This option forces
// Grbl to always initialize into an ALARM state regardless of homing or not. This option is more for
// OEMs and LinuxCNC users that would like this power-cycle behavior.
// #define FORCE_INITIALIZATION_ALARM // Default disabled. Uncomment to enable.
// At power-up or a reset, Grbl will check the limit switch states to ensure they are not active
// before initialization. If it detects a problem and the hard limits setting is enabled, Grbl will
// simply message the user to check the limits and enter an alarm state, rather than idle. Grbl will
// not throw an alarm message.
#define CHECK_LIMITS_AT_INIT
// ---------------------------------------------------------------------------------------
// ADVANCED CONFIGURATION OPTIONS:
// Enables code for debugging purposes. Not for general use and always in constant flux.
// #define DEBUG // Uncomment to enable. Default disabled.
// Configure rapid, feed, and spindle override settings. These values define the max and min
// allowable override values and the coarse and fine increments per command received. Please
// note the allowable values in the descriptions following each define.
#define DEFAULT_FEED_OVERRIDE 100 // 100%. Don't change this value.
#define MAX_FEED_RATE_OVERRIDE 200 // Percent of programmed feed rate (100-255). Usually 120% or 200%
#define MIN_FEED_RATE_OVERRIDE 10 // Percent of programmed feed rate (1-100). Usually 50% or 1%
#define FEED_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%.
#define FEED_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%.
#define DEFAULT_RAPID_OVERRIDE 100 // 100%. Don't change this value.
#define RAPID_OVERRIDE_MEDIUM 50 // Percent of rapid (1-99). Usually 50%.
#define RAPID_OVERRIDE_LOW 25 // Percent of rapid (1-99). Usually 25%.
// #define RAPID_OVERRIDE_EXTRA_LOW 5 // *NOT SUPPORTED* Percent of rapid (1-99). Usually 5%.
#define DEFAULT_SPINDLE_SPEED_OVERRIDE 100 // 100%. Don't change this value.
#define MAX_SPINDLE_SPEED_OVERRIDE 200 // Percent of programmed spindle speed (100-255). Usually 200%.
#define MIN_SPINDLE_SPEED_OVERRIDE 50 // Percent of programmed spindle speed (1-100). Usually 50%.
#define SPINDLE_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%.
#define SPINDLE_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%.
// When a M2 or M30 program end command is executed, most g-code states are restored to their defaults.
// This compile-time option includes the restoring of the feed, rapid, and spindle speed override values
// to their default values at program end.
#define RESTORE_OVERRIDES_AFTER_PROGRAM_END // Default enabled. Comment to disable.
// Enables minimal reporting feedback mode for GUIs, where human-readable strings are not as important.
// This saves nearly 2KB of flash space and may allow enough space to install other/future features.
// GUIs will need to install a look-up table for the error-codes that Grbl sends back in their place.
// NOTE: This feature is new and experimental. Make sure the GUI you are using supports this mode.
// #define REPORT_GUI_MODE // Default disabled. Uncomment to enable.
#define REPORT_GUI_MODE // Default enabled. Comment to disable.
// The status report change for Grbl v1.0 and after also removed the ability to disable/enable data fields
// from the report. This caused issues for GUI developers, who've had to manage several scenarios and
// configurations. The increased efficiency of the new reporting style allows for all data fields to be
// sent without potential performance issues.
// NOTE: The options below are here only provide a way to disable certain data fields if a unique
// situation demands it, but be aware GUIs may depend on this data. If disabled, it may not be compatible.
#define REPORT_FIELD_BUFFER_STATE // Default enabled. Comment to disable.
#define REPORT_FIELD_PIN_STATE // Default enabled. Comment to disable.
#define REPORT_FIELD_CURRENT_RATE // Default enabled. Comment to disable.
#define REPORT_FIELD_WORK_COORD_OFFSET // Default enabled. Comment to disable.
#define REPORT_FIELD_OVERRIDES // Default enabled. Comment to disable.
#define REPORT_FIELD_LINE_NUMBERS // Default enabled. Comment to disable.
// Some status report data isn't necessary for realtime, only intermittently, because the values don't
// change often. The following macros configures how many times a status report needs to be called before
// the associated data is refreshed and included in the status report. However, if one of these value
// changes, Grbl will automatically include this data in the next status report, regardless of what the
// count is at the time. This helps reduce the communication overhead involved with high frequency reporting
// and agressive streaming. There is also a busy and an idle refresh count, which sets up Grbl to send
// refreshes more often when its not doing anything important. With a good GUI, this data doesn't need
// to be refreshed very often, on the order of a several seconds.
// NOTE: The refresh count cannot be set to zero and must be one or greater.
#define REPORT_OVR_REFRESH_BUSY_COUNT 20 // (1-255)
#define REPORT_OVR_REFRESH_IDLE_COUNT 10 // (1-255) Must be less than or equal to the busy count
#define REPORT_WCO_REFRESH_BUSY_COUNT 30 // (1-255)
#define REPORT_WCO_REFRESH_IDLE_COUNT 10 // (1-255) Must be less than or equal to the busy count
// COMPATIBILITY OPTIONS:
// Grbl v1.0 and later altered the formatting of the realtime status reports to make it more consistent
// for parsing with cleaner delimiters and optimized messages. To use Grbl v0.9-style status reporting,
// enable this compile option. This is generally useful if older GUIs require this formatting.
// #define USE_CLASSIC_REALTIME_REPORT
// #define REPORT_ALL_PIN_STATES // Default disabled. Comment to enable. NOTE: Compatible with old-style reports only.
// #define REPORT_REALTIME_RATE // Disabled by default. Uncomment to enable.
// The temporal resolution of the acceleration management subsystem. A higher number gives smoother
// acceleration, particularly noticeable on machines that run at very high feedrates, but may negatively
// impact performance. The correct value for this parameter is machine dependent, so it's advised to
// set this only as high as needed. Approximate successful values can widely range from 50 to 200 or more.
// NOTE: Changing this value also changes the execution time of a segment in the step segment buffer.
// NOTE: Changing this value also changes the execution time of a segment in the step segment buffer.
// When increasing this value, this stores less overall time in the segment buffer and vice versa. Make
// certain the step segment buffer is increased/decreased to account for these changes.
#define ACCELERATION_TICKS_PER_SECOND 100
#define ACCELERATION_TICKS_PER_SECOND 100
// Adaptive Multi-Axis Step Smoothing (AMASS) is an advanced feature that does what its name implies,
// Adaptive Multi-Axis Step Smoothing (AMASS) is an advanced feature that does what its name implies,
// smoothing the stepping of multi-axis motions. This feature smooths motion particularly at low step
// frequencies below 10kHz, where the aliasing between axes of multi-axis motions can cause audible
// frequencies below 10kHz, where the aliasing between axes of multi-axis motions can cause audible
// noise and shake your machine. At even lower step frequencies, AMASS adapts and provides even better
// step smoothing. See stepper.c for more details on the AMASS system works.
#define ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Default enabled. Comment to disable.
// Sets the maximum step rate allowed to be written as a Grbl setting. This option enables an error
// Sets the maximum step rate allowed to be written as a Grbl setting. This option enables an error
// check in the settings module to prevent settings values that will exceed this limitation. The maximum
// step rate is strictly limited by the CPU speed and will change if something other than an AVR running
// at 16MHz is used.
@ -221,15 +313,15 @@
// #define MAX_STEP_RATE_HZ 30000 // Hz
// By default, Grbl sets all input pins to normal-high operation with their internal pull-up resistors
// enabled. This simplifies the wiring for users by requiring only a switch connected to ground,
// enabled. This simplifies the wiring for users by requiring only a switch connected to ground,
// although its recommended that users take the extra step of wiring in low-pass filter to reduce
// electrical noise detected by the pin. If the user inverts the pin in Grbl settings, this just flips
// which high or low reading indicates an active signal. In normal operation, this means the user
// needs to connect a normal-open switch, but if inverted, this means the user should connect a
// normal-closed switch.
// The following options disable the internal pull-up resistors, sets the pins to a normal-low
// operation, and switches must be now connect to Vcc instead of ground. This also flips the meaning
// of the invert pin Grbl setting, where an inverted setting now means the user should connect a
// which high or low reading indicates an active signal. In normal operation, this means the user
// needs to connect a normal-open switch, but if inverted, this means the user should connect a
// normal-closed switch.
// The following options disable the internal pull-up resistors, sets the pins to a normal-low
// operation, and switches must be now connect to Vcc instead of ground. This also flips the meaning
// of the invert pin Grbl setting, where an inverted setting now means the user should connect a
// normal-open switch and vice versa.
// NOTE: All pins associated with the feature are disabled, i.e. XYZ limit pins, not individual axes.
// WARNING: When the pull-ups are disabled, this requires additional wiring with pull-down resistors!
@ -237,7 +329,7 @@
//#define DISABLE_PROBE_PIN_PULL_UP
//#define DISABLE_CONTROL_PIN_PULL_UP
// Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with
// Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with
// the selected axis with the tool oriented toward the negative direction. In other words, a positive
// tool length offset value is subtracted from the current location.
#define TOOL_LENGTH_OFFSET_AXIS Z_AXIS // Default z-axis. Valid values are X_AXIS, Y_AXIS, or Z_AXIS.
@ -251,24 +343,24 @@
// Used by variable spindle output only. This forces the PWM output to a minimum duty cycle when enabled.
// The PWM pin will still read 0V when the spindle is disabled. Most users will not need this option, but
// it may be useful in certain scenarios. This minimum PWM settings coincides with the spindle rpm minimum
// setting, like rpm max to max PWM. So the variable spindle pin will not output the voltage range between
// setting, like rpm max to max PWM. So the variable spindle pin will not output the voltage range between
// 0V for disabled and the voltage set by the minimum PWM for minimum rpm.
// NOTE: Compute duty cycle at the minimum PWM by this equation: (% duty cycle)=(SPINDLE_MINIMUM_PWM/256)*100
// #define SPINDLE_MINIMUM_PWM 5 // Default disabled. Uncomment to enable. Integer (0-255)
// By default on a 328p(Uno), Grbl combines the variable spindle PWM and the enable into one pin to help
// By default on a 328p(Uno), Grbl combines the variable spindle PWM and the enable into one pin to help
// preserve I/O pins. For certain setups, these may need to be separate pins. This configure option uses
// the spindle direction pin(D13) as a separate spindle enable pin along with spindle speed PWM on pin D11.
// NOTE: This configure option only works with VARIABLE_SPINDLE enabled and a 328p processor (Uno).
// the spindle direction pin(D13) as a separate spindle enable pin along with spindle speed PWM on pin D11.
// NOTE: This configure option only works with VARIABLE_SPINDLE enabled and a 328p processor (Uno).
// NOTE: With no direction pin, the spindle clockwise M4 g-code command will be removed. M3 and M5 still work.
// NOTE: BEWARE! The Arduino bootloader toggles the D13 pin when it powers up. If you flash Grbl with
// a programmer (you can use a spare Arduino as "Arduino as ISP". Search the web on how to wire this.),
// a programmer (you can use a spare Arduino as "Arduino as ISP". Search the web on how to wire this.),
// this D13 LED toggling should go away. We haven't tested this though. Please report how it goes!
// #define USE_SPINDLE_DIR_AS_ENABLE_PIN // Default disabled. Uncomment to enable.
// With this enabled, Grbl sends back an echo of the line it has received, which has been pre-parsed (spaces
// removed, capitalized letters, no comments) and is to be immediately executed by Grbl. Echoes will not be
// sent upon a line buffer overflow, but should for all normal lines sent to Grbl. For example, if a user
// removed, capitalized letters, no comments) and is to be immediately executed by Grbl. Echoes will not be
// sent upon a line buffer overflow, but should for all normal lines sent to Grbl. For example, if a user
// sendss the line 'g1 x1.032 y2.45 (test comment)', Grbl will echo back in the form '[echo: G1X1.032Y2.45]'.
// NOTE: Only use this for debugging purposes!! When echoing, this takes up valuable resources and can effect
// performance. If absolutely needed for normal operation, the serial write buffer should be greatly increased
@ -289,15 +381,15 @@
// machines, perhaps to 0.1mm/min, but your success may vary based on multiple factors.
#define MINIMUM_FEED_RATE 1.0 // (mm/min)
// Number of arc generation iterations by small angle approximation before exact arc trajectory
// correction with expensive sin() and cos() calcualtions. This parameter maybe decreased if there
// Number of arc generation iterations by small angle approximation before exact arc trajectory
// 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.
// bogged down by too many trig calculations.
#define N_ARC_CORRECTION 12 // Integer (1-255)
// The arc G2/3 g-code standard is problematic by definition. Radius-based arcs have horrible numerical
// errors when arc at semi-circles(pi) or full-circles(2*pi). Offset-based arcs are much more accurate
// but still have a problem when arcs are full-circles (2*pi). This define accounts for the floating
// The arc G2/3 g-code standard is problematic by definition. Radius-based arcs have horrible numerical
// errors when arc at semi-circles(pi) or full-circles(2*pi). Offset-based arcs are much more accurate
// but still have a problem when arcs are full-circles (2*pi). This define accounts for the floating
// point issues when offset-based arcs are commanded as full circles, but get interpreted as extremely
// small arcs with around machine epsilon (1.2e-7rad) due to numerical round-off and precision issues.
// This define value sets the machine epsilon cutoff to determine if the arc is a full-circle or not.
@ -307,16 +399,16 @@
// Time delay increments performed during a dwell. The default value is set at 50ms, which provides
// a maximum time delay of roughly 55 minutes, more than enough for most any application. Increasing
// this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of
// run-time command executions, like status reports, since these are performed between each dwell
// this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of
// run-time command executions, like status reports, since these are performed between each dwell
// time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays.
#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)
// Creates a delay between the direction pin setting and corresponding step pulse by creating
// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare)
// sets the direction pins, and does not immediately set the stepper pins, as it would in
// normal operation. The Timer2 compare fires next to set the stepper pins after the step
// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed
// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare)
// sets the direction pins, and does not immediately set the stepper pins, as it would in
// normal operation. The Timer2 compare fires next to set the stepper pins after the step
// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed
// by the step pulse time plus the step pulse delay. (Thanks langwadt for the idea!)
// NOTE: Uncomment to enable. The recommended delay must be > 3us, and, when added with the
// user-supplied step pulse time, the total time must not exceed 127us. Reported successful
@ -324,62 +416,65 @@
// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.
// The number of linear motions in the planner buffer to be planned at any give time. The vast
// majority of RAM that Grbl uses is based on this buffer size. Only increase if there is extra
// available RAM, like when re-compiling for a Mega or Sanguino. Or decrease if the Arduino
// begins to crash due to the lack of available RAM or if the CPU is having trouble keeping
// up with planning new incoming motions as they are executed.
// #define BLOCK_BUFFER_SIZE 18 // Uncomment to override default in planner.h.
// majority of RAM that Grbl uses is based on this buffer size. Only increase if there is extra
// available RAM, like when re-compiling for a Mega2560. Or decrease if the Arduino begins to
// crash due to the lack of available RAM or if the CPU is having trouble keeping up with planning
// new incoming motions as they are executed.
// #define BLOCK_BUFFER_SIZE 16 // Uncomment to override default in planner.h.
// Governs the size of the intermediary step segment buffer between the step execution algorithm
// and the planner blocks. Each segment is set of steps executed at a constant velocity over a
// fixed time defined by ACCELERATION_TICKS_PER_SECOND. They are computed such that the planner
// block velocity profile is traced exactly. The size of this buffer governs how much step
// execution lead time there is for other Grbl processes have to compute and do their thing
// block velocity profile is traced exactly. The size of this buffer governs how much step
// execution lead time there is for other Grbl processes have to compute and do their thing
// before having to come back and refill this buffer, currently at ~50msec of step moves.
// #define SEGMENT_BUFFER_SIZE 6 // Uncomment to override default in stepper.h.
// Line buffer size from the serial input stream to be executed. Also, governs the size of
// Line buffer size from the serial input stream to be executed. Also, governs the size of
// each of the startup blocks, as they are each stored as a string of this size. Make sure
// to account for the available EEPROM at the defined memory address in settings.h and for
// the number of desired startup blocks.
// 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
// 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 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
// interfaces will character count and track each block send to each block response. So,
// interfaces will character count and track each block send to each block response. So,
// 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
// NOTE: Grbl generates an average status report in about 0.5msec, but the serial TX stream at
// 115200 baud will take 5 msec to transmit a typical 55 character report. Worst case reports are
// around 90-100 characters. As long as the serial TX buffer doesn't get continually maxed, Grbl
// will continue operating efficiently. Size the TX buffer around the size of a worst-case report.
// #define RX_BUFFER_SIZE 128 // (1-254) Uncomment to override defaults in serial.h
// #define TX_BUFFER_SIZE 90 // (1-254)
// Toggles XON/XOFF software flow control for serial communications. Not officially supported
// due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos. The firmware
// on these chips do not support XON/XOFF flow control characters and the intermediate buffer
// in the chips cause latency and overflow problems with standard terminal programs. However,
// on these chips do not support XON/XOFF flow control characters and the intermediate buffer
// in the chips cause latency and overflow problems with standard terminal programs. However,
// using specifically-programmed UI's to manage this latency problem has been confirmed to work.
// As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard
// terminal programs since their firmware correctly manage these XON/XOFF characters. In any
// case, please report any successes to grbl administrators!
// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.
// A simple software debouncing feature for hard limit switches. When enabled, the interrupt
// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check
// the limit pin state after a delay of about 32msec. This can help with CNC machines with
// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with
// A simple software debouncing feature for hard limit switches. When enabled, the interrupt
// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check
// the limit pin state after a delay of about 32msec. This can help with CNC machines with
// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with
// electrical interference on the signal cables from external sources. It's recommended to first
// use shielded signal cables with their shielding connected to ground (old USB/computer cables
// use shielded signal cables with their shielding connected to ground (old USB/computer cables
// work well and are cheap to find) and wire in a low-pass circuit into each limit pin.
// #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable.
// Force Grbl to check the state of the hard limit switches when the processor detects a pin
// change inside the hard limit ISR routine. By default, Grbl will trigger the hard limits
// alarm upon any pin change, since bouncing switches can cause a state check like this to
// alarm upon any pin change, since bouncing switches can cause a state check like this to
// misread the pin. When hard limits are triggered, they should be 100% reliable, which is the
// reason that this option is disabled by default. Only if your system/electronics can guarantee
// that the switches don't bounce, we recommend enabling this option. This will help prevent
@ -388,7 +483,7 @@
// #define HARD_LIMIT_FORCE_STATE_CHECK // Default disabled. Uncomment to enable.
// Adjusts homing cycle search and locate scalars. These are the multipliers used by Grbl's
// homing cycle to ensure the limit switches are engaged and cleared through each phase of
// homing cycle to ensure the limit switches are engaged and cleared through each phase of
// the cycle. The search phase uses the axes max-travel setting times the SEARCH_SCALAR to
// determine distance to look for the limit switch. Once found, the locate phase begins and
// uses the homing pull-off distance setting times the LOCATE_SCALAR to pull-off and re-engage
@ -397,19 +492,62 @@
// #define HOMING_AXIS_SEARCH_SCALAR 1.5 // Uncomment to override defaults in limits.c.
// #define HOMING_AXIS_LOCATE_SCALAR 10.0 // Uncomment to override defaults in limits.c.
// Enable the '$RST=*', '$RST=$', and '$RST=#' eeprom restore commands. There are cases where
// these commands may be undesirable. Simply comment the desired macro to disable it.
// NOTE: See SETTINGS_RESTORE_ALL macro for customizing the `$RST=*` command.
#define ENABLE_RESTORE_EEPROM_WIPE_ALL // '$RST=*' Default enabled. Comment to disable.
#define ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS // '$RST=$' Default enabled. Comment to disable.
#define ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS // '$RST=#' Default enabled. Comment to disable.
// Defines the EEPROM data restored upon a settings version change and `$RST=*` command. Whenever the
// the settings or other EEPROM data structure changes between Grbl versions, Grbl will automatically
// wipe and restore the EEPROM. This macro controls what data is wiped and restored. This is useful
// particularily for OEMs that need to retain certain data. For example, the BUILD_INFO string can be
// written into the Arduino EEPROM via a seperate .INO sketch to contain product data. Altering this
// macro to not restore the build info EEPROM will ensure this data is retained after firmware upgrades.
// NOTE: Uncomment to override defaults in settings.h
// #define SETTINGS_RESTORE_ALL (SETTINGS_RESTORE_DEFAULTS | SETTINGS_RESTORE_PARAMETERS | SETTINGS_RESTORE_STARTUP_LINES | SETTINGS_RESTORE_BUILD_INFO)
// Enable the '$I=(string)' build info write command. If disabled, any existing build info data must
// be placed into EEPROM via external means with a valid checksum value. This macro option is useful
// to prevent this data from being over-written by a user, when used to store OEM product data.
// NOTE: See the included grblWrite_BuildInfo.ino example file to write this string seperately.
#define ENABLE_BUILD_INFO_WRITE_COMMAND // '$I=' Default enabled. Comment to disable.
// AVR processors require all interrupts to be disabled during an EEPROM write. This includes both
// the stepper ISRs and serial comm ISRs. In the event of a long EEPROM write, this ISR pause can
// cause active stepping to lose position and serial receive data to be lost. This configuration
// option forces the planner buffer to completely empty whenever the EEPROM is written to prevent
// any chance of lost steps.
// However, this doesn't prevent issues with lost serial RX data during an EEPROM write, especially
// if a GUI is premptively filling up the serial RX buffer simultaneously. It's highly advised for
// GUIs to flag these gcodes (G10,G28.1,G30.1) to always wait for an 'ok' after a block containing
// one of these commands before sending more data to eliminate this issue.
// NOTE: Most EEPROM write commands are implicitly blocked during a job (all '$' commands). However,
// coordinate set g-code commands (G10,G28/30.1) are not, since they are part of an active streaming
// job. At this time, this option only forces a planner buffer sync with these g-code commands.
#define FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE // Default enabled. Comment to disable.
// In Grbl v0.9 and prior, there is an old outstanding bug where the `WPos:` work position reported
// may not correlate to what is executing, because `WPos:` is based on the g-code parser state, which
// can be several motions behind. This option forces the planner buffer to empty, sync, and stop
// motion whenever there is a command that alters the work coordinate offsets `G10,G43.1,G92,G54-59`.
// This is the simplest way to ensure `WPos:` is always correct. Fortunately, it's exceedingly rare
// that any of these commands are used need continuous motions through them.
#define FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // Default enabled. Comment to disable.
// Enables and configures parking motion methods upon a safety door state. Primarily for OEMs
// that desire this feature for their integrated machines. At the moment, Grbl assumes that
// that desire this feature for their integrated machines. At the moment, Grbl assumes that
// the parking motion only involves one axis, although the parking implementation was written
// to be easily refactored for any number of motions on different axes by altering the parking
// source code. At this time, Grbl only supports parking one axis (typically the Z-axis) that
// to be easily refactored for any number of motions on different axes by altering the parking
// source code. At this time, Grbl only supports parking one axis (typically the Z-axis) that
// moves in the positive direction upon retracting and negative direction upon restoring position.
// The motion executes with a slow pull-out retraction motion, power-down, and a fast park.
// Restoring to the resume position follows these set motions in reverse: fast restore to
// The motion executes with a slow pull-out retraction motion, power-down, and a fast park.
// Restoring to the resume position follows these set motions in reverse: fast restore to
// pull-out position, power-up with a time-out, and plunge back to the original position at the
// slower pull-out rate.
// NOTE: Still a work-in-progress. Machine coordinates must be in all negative space and
// does not work with HOMING_FORCE_SET_ORIGIN enabled. Parking motion also moves only in
// NOTE: Still a work-in-progress. Machine coordinates must be in all negative space and
// does not work with HOMING_FORCE_SET_ORIGIN enabled. Parking motion also moves only in
// positive direction.
// #define PARKING_ENABLE // Default disabled. Uncomment to enable
@ -445,7 +583,7 @@
/* ---------------------------------------------------------------------------------------
OEM Single File Configuration Option
Instructions: Paste the cpu_map and default setting definitions below without an enclosing
#ifdef. Comment out the CPU_MAP_xxx and DEFAULT_xxx defines at the top of this file, and
the compiler will ignore the contents of defaults.h and cpu_map.h and use the definitions

Wyświetl plik

@ -2,7 +2,7 @@
coolant_control.c - coolant control methods
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,51 +16,58 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
*/
#include "grbl.h"
void coolant_init()
{
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT);
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); // Configure as output pin
#ifdef ENABLE_M7
COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT);
#endif
coolant_stop();
}
void coolant_stop()
{
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
#ifdef ENABLE_M7
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
#endif
coolant_set_state(COOLANT_DISABLE);
}
void coolant_set_state(uint8_t mode)
{
if (sys.abort) { return; } // Block during abort.
if (mode == COOLANT_FLOOD_ENABLE) {
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
#ifdef ENABLE_M7
} else if (mode == COOLANT_MIST_ENABLE) {
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
#endif
if (mode & COOLANT_FLOOD_ENABLE) {
#ifdef INVERT_COOLANT_FLOOD_PIN
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
#else
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
#endif
} else {
coolant_stop();
#ifdef INVERT_COOLANT_FLOOD_PIN
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
#else
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
#endif
}
#ifdef ENABLE_M7
if (mode & COOLANT_MIST_ENABLE) {
#ifdef INVERT_COOLANT_MIST_PIN
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
#else
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
#endif
} else {
#ifdef INVERT_COOLANT_MIST_PIN
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
#else
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
#endif
}
#endif
}
void coolant_run(uint8_t mode)
{
if (sys.state == STATE_CHECK_MODE) { return; }
protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
if (sys.abort) { return; } // Block during abort.
coolant_set_state(mode);
}

Wyświetl plik

@ -2,7 +2,7 @@
coolant_control.h - spindle control methods
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,12 +19,11 @@
*/
#ifndef coolant_control_h
#define coolant_control_h
#define coolant_control_h
void coolant_init();
void coolant_stop();
void coolant_set_state(uint8_t mode);
void coolant_run(uint8_t mode);
#endif
#endif

Wyświetl plik

@ -2,7 +2,7 @@
cpu_map.h - CPU and pin mapping configuration file
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -18,7 +18,7 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
/* The cpu_map.h files serve as a central pin mapping selection file for different
/* The cpu_map.h files serve as a central pin mapping selection file for different
processor types or alternative pin layouts. This version of Grbl officially supports
only the Arduino Mega328p. */
@ -55,28 +55,28 @@
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)
// Define homing/hard limit switch input pins and limit interrupt vectors.
// Define homing/hard limit switch input pins and limit interrupt vectors.
// NOTE: All limit bit pins must be on the same port, but not on a port with other input pins (CONTROL).
#define LIMIT_DDR DDRB
#define LIMIT_PIN PINB
#define LIMIT_PORT PORTB
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
#ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11.
#ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11.
#define Z_LIMIT_BIT 4 // Uno Digital Pin 12
#else
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
#endif
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
#define LIMIT_INT PCIE0 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT0_vect
#define LIMIT_INT_vect PCINT0_vect
#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
// Define spindle enable and spindle direction output pins.
#define SPINDLE_ENABLE_DDR DDRB
#define SPINDLE_ENABLE_PORT PORTB
// Z Limit pin and spindle PWM/enable pin swapped to access hardware PWM on Pin 11.
#ifdef VARIABLE_SPINDLE
#ifdef VARIABLE_SPINDLE
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
// If enabled, spindle direction pin now used as spindle enable, while PWM remains on D11.
#define SPINDLE_ENABLE_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
@ -91,18 +91,14 @@
#define SPINDLE_DIRECTION_PORT PORTB
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
#endif
// Define flood and mist coolant enable output pins.
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
// a later date if flash and memory space allows.
#define COOLANT_FLOOD_DDR DDRC
#define COOLANT_FLOOD_PORT PORTC
#define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3
#ifdef ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable.
#define COOLANT_MIST_DDR DDRC
#define COOLANT_MIST_PORT PORTC
#define COOLANT_MIST_BIT 4 // Uno Analog Pin 4
#endif
#define COOLANT_MIST_DDR DDRC
#define COOLANT_MIST_PORT PORTC
#define COOLANT_MIST_BIT 4 // Uno Analog Pin 3
// Define user-control controls (cycle start, reset, feed hold) input pins.
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
@ -118,7 +114,7 @@
#define CONTROL_PCMSK PCMSK1 // Pin change interrupt register
#define CONTROL_MASK ((1<<CONTROL_RESET_BIT)|(1<<CONTROL_FEED_HOLD_BIT)|(1<<CONTROL_CYCLE_START_BIT)|(1<<CONTROL_SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
// Define probe switch input pin.
#define PROBE_DDR DDRC
#define PROBE_PIN PINC
@ -126,28 +122,30 @@
#define PROBE_BIT 5 // Uno Analog Pin 5
#define PROBE_MASK (1<<PROBE_BIT)
// Start of PWM & Stepper Enabled Spindle
#ifdef VARIABLE_SPINDLE
// Advanced Configuration Below You should not need to touch these variables
#define SPINDLE_PWM_MAX_VALUE 255.0 // Don't change. 328p fast PWM mode fixes top value as 255.
#define SPINDLE_TCCRA_REGISTER TCCR2A
#define SPINDLE_TCCRB_REGISTER TCCR2B
#define SPINDLE_OCR_REGISTER OCR2A
#define SPINDLE_COMB_BIT COM2A1
// 1/8 Prescaler, 8-bit Fast PWM mode. Translates to about 7.8kHz PWM frequency.
#define SPINDLE_TCCRA_INIT_MASK ((1<<WGM20) | (1<<WGM21))
#define SPINDLE_TCCRB_INIT_MASK (1<<CS21)
// NOTE: On the 328p, these must be the same as the SPINDLE_ENABLE settings.
#define SPINDLE_PWM_DDR DDRB
#define SPINDLE_PWM_PORT PORTB
#define SPINDLE_PWM_BIT 3 // Uno Digital Pin 11
#endif // End of VARIABLE_SPINDLE
// Variable spindle configuration below. Do not change unless you know what you are doing.
// NOTE: Only used when variable spindle is enabled.
#define SPINDLE_PWM_MAX_VALUE 255.0 // Don't change. 328p fast PWM mode fixes top value as 255.
#define SPINDLE_PWM_OFF_VALUE 0
#define SPINDLE_TCCRA_REGISTER TCCR2A
#define SPINDLE_TCCRB_REGISTER TCCR2B
#define SPINDLE_OCR_REGISTER OCR2A
#define SPINDLE_COMB_BIT COM2A1
// Prescaled, 8-bit Fast PWM mode.
#define SPINDLE_TCCRA_INIT_MASK ((1<<WGM20) | (1<<WGM21)) // Configures fast PWM mode.
// #define SPINDLE_TCCRB_INIT_MASK (1<<CS21) // 1/8 prescaler -> 7.8kHz (Used in v0.9)
// #define SPINDLE_TCCRB_INIT_MASK ((1<<CS21) | (1<<CS20)) // 1/32 prescaler -> 1.96kHz
#define SPINDLE_TCCRB_INIT_MASK (1<<CS22) // 1/64 prescaler -> 0.98kHz
// NOTE: On the 328p, these must be the same as the SPINDLE_ENABLE settings.
#define SPINDLE_PWM_DDR DDRB
#define SPINDLE_PWM_PORT PORTB
#define SPINDLE_PWM_BIT 3 // Uno Digital Pin 11
#endif
/*
/*
#ifdef CPU_MAP_CUSTOM_PROC
// For a custom pin map or different processor, copy and edit one of the available cpu
// map files and modify it to your needs. Make sure the defined name is also changed in

Wyświetl plik

@ -2,7 +2,7 @@
defaults.h - defaults settings configuration file
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,7 +19,7 @@
*/
/* The defaults.h file serves as a central default settings selector for different machine
types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings
types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings
files listed here are supplied by users, so your results may vary. However, this should
give you a good starting point as you get to know your machine and tweak the settings for
your nefarious needs.
@ -47,7 +47,7 @@
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK 0
#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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.01 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
@ -55,6 +55,8 @@
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
@ -82,12 +84,12 @@
#define DEFAULT_Y_MAX_TRAVEL 125.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 170.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 2800.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))
#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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.01 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // true
@ -95,6 +97,8 @@
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 50.0 // mm/min
@ -128,22 +132,24 @@
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))
#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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_INVERT_ST_ENABLE 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 250.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#endif
#ifdef DEFAULTS_SHAPEOKO_2
@ -168,25 +174,27 @@
#define DEFAULT_Y_MAX_TRAVEL 290.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 100.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_INVERT_ST_ENABLE 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 250.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#endif
#ifdef DEFAULTS_SHAPEOKO_3
@ -220,15 +228,17 @@
#define DEFAULT_ARC_TOLERANCE 0.01 // mm
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_INVERT_ST_ENABLE 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 100.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 1000.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 25 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 5.0 // mm
#define DEFAULT_HOMING_PULLOFF 5.0 // mm
#endif
#ifdef DEFAULTS_X_CARVE_500MM
@ -253,25 +263,27 @@
#define DEFAULT_Y_MAX_TRAVEL 290.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 100.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Y_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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_INVERT_ST_ENABLE 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 3 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 750.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#endif
#ifdef DEFAULTS_X_CARVE_1000MM
@ -296,25 +308,27 @@
#define DEFAULT_Y_MAX_TRAVEL 790.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 100.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Y_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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_INVERT_ST_ENABLE 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 3 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 750.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#endif
#ifdef DEFAULTS_ZEN_TOOLWORKS_7x7
@ -337,19 +351,21 @@
#define DEFAULT_Y_MAX_TRAVEL 180.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 150.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS))
#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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_INVERT_ST_ENABLE 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
@ -374,12 +390,12 @@
#define DEFAULT_Y_MAX_TRAVEL 750.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 80.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK 0
#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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
@ -387,6 +403,8 @@
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
@ -416,7 +434,7 @@
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK 0
#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_STATUS_REPORT_MASK 255 // All enabled
#define DEFAULT_JUNCTION_DEVIATION 0.01 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
@ -424,6 +442,8 @@
#define DEFAULT_INVERT_LIMIT_PINS 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_INVERT_PROBE_PIN 0 // false
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min

Wyświetl plik

@ -0,0 +1,109 @@
/***********************************************************************
This sketch writes a `$I` build info string directly into Arduino EEPROM
To use:
- Just alter the "build_info_line" string to whatever you'd like. Then
compile and upload this sketch to your Arduino.
- If your Arduino is blinking slowly, your string has already been
written to your EEPROM and been verified by checksums! That's it!
- If you Arduino LED is blinking fast, something went wrong and the
checksums don't match. You can optionally connect to the Arduino via
the serial monitor, and the sketch will show what its doing.
NOTE: This sketch is provided as a tool template for OEMs who may need
to restrict users from altering their build info, so they can place
important product information here when enabling the restriction.
NOTE: When uploading Grbl to the Arduino with this sketch on it, make
sure you see the slow blink before you start the upload process. This
ensures you aren't flashing Grbl when it's in mid-write of the EEPROM.
Copyright (c) 2016 Sungeun K. Jeon for Gnea Research LLC
Released under the MIT-license. See license.txt for details.
***********************************************************************/
#include <avr/pgmspace.h>
#include <EEPROM.h>
#define SERIAL_BAUD_RATE 115200
#define LINE_LENGTH 80U // Grbl line length
#define BYTE_LOCATION 942U // Grbl build info EEPROM address.
// ----- CHANGE THIS LINE -----
char build_info_line[LINE_LENGTH] = "Testing123.";
// -----------------------------
uint8_t status = false;
int ledPin = 13; // LED connected to digital pin 13
void setup() {
Serial.begin(SERIAL_BAUD_RATE);
delay(500);
uint32_t address = BYTE_LOCATION;
uint32_t size = LINE_LENGTH;
char *write_pointer = (char*)build_info_line;
uint8_t write_checksum = 0;
for (; size>0; size--) {
write_checksum = (write_checksum << 1) || (write_checksum >> 7);
write_checksum += *write_pointer;
EEPROM.put(address++, *(write_pointer++));
}
EEPROM.put(address,write_checksum);
Serial.print(F("-> Writing line to EEPROM: '"));
Serial.print(build_info_line);
Serial.print(F("'\n\r-> Write checksum: "));
Serial.println(write_checksum,DEC);
size = LINE_LENGTH;
address = BYTE_LOCATION;
uint8_t data = 0;
char read_line[LINE_LENGTH];
char *read_pointer = (char*)read_line;
uint8_t read_checksum = 0;
uint8_t stored_checksum = 0;
for(; size > 0; size--) {
data = EEPROM.read(address++);
read_checksum = (read_checksum << 1) || (read_checksum >> 7);
read_checksum += data;
*(read_pointer++) = data;
}
stored_checksum = EEPROM.read(address);
Serial.print(F("<- Reading line from EEPROM: '"));
Serial.print(read_line);
Serial.print("'\n\r<- Read checksum: ");
Serial.println(read_checksum,DEC);
if ((read_checksum == write_checksum) && (read_checksum == stored_checksum)) {
status = true;
Serial.print(F("SUCCESS! All checksums match!\r\n"));
} else {
if (write_checksum != stored_checksum) {
Serial.println(F("ERROR! Write and stored EEPROM checksums don't match!"));
} else {
Serial.println(F("ERROR! Read and stored checksums don't match!"));
}
}
pinMode(ledPin, OUTPUT); // sets the digital pin as output
}
void loop() {
// Blink to let user know EEPROM write status.
// Slow blink is 'ok'. Fast blink is an 'error'.
digitalWrite(ledPin, HIGH); // sets the LED on
if (status) { delay(1500); } // Slow blink
else { delay(100); } // Rapid blink
digitalWrite(ledPin, LOW); // sets the LED off
if (status) { delay(1500); }
else { delay(100); }
}

Wyświetl plik

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Sungeun K. Jeon for Gnea Research LLC
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.

Plik diff jest za duży Load Diff

Wyświetl plik

@ -2,9 +2,9 @@
gcode.h - rs274/ngc parser.
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -23,10 +23,10 @@
#define gcode_h
// Define modal group internal numbers for checking multiple command violations and tracking the
// Define modal group internal numbers for checking multiple command violations and tracking the
// type of command that is called in the block. A modal group is a group of g-code commands that are
// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute
// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online,
// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online,
// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc).
// NOTE: Modal group define values must be sequential and starting from zero.
#define MODAL_GROUP_G0 0 // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal
@ -93,8 +93,8 @@
#define PROGRAM_FLOW_COMPLETED 2 // M2, M30
// Modal Group G5: Feed rate mode
#define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero)
#define FEED_RATE_MODE_INVERSE_TIME 1 // G93
#define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero)
#define FEED_RATE_MODE_INVERSE_TIME PL_COND_FLAG_INVERSE_TIME // G93 (NOTE: Uses planner condition bit flag)
// Modal Group G6: Units mode
#define UNITS_MODE_MM 0 // G21 (Default: Must be zero)
@ -108,13 +108,13 @@
// Modal Group M7: Spindle control
#define SPINDLE_DISABLE 0 // M5 (Default: Must be zero)
#define SPINDLE_ENABLE_CW 1 // M3
#define SPINDLE_ENABLE_CCW 2 // M4
#define SPINDLE_ENABLE_CW PL_COND_FLAG_SPINDLE_CW // M3 (NOTE: Uses planner condition bit flag)
#define SPINDLE_ENABLE_CCW PL_COND_FLAG_SPINDLE_CCW // M4 (NOTE: Uses planner condition bit flag)
// Modal Group M8: Coolant control
#define COOLANT_DISABLE 0 // M9 (Default: Must be zero)
#define COOLANT_MIST_ENABLE 1 // M7
#define COOLANT_FLOOD_ENABLE 2 // M8
#define COOLANT_FLOOD_ENABLE PL_COND_FLAG_COOLANT_FLOOD // M8 (NOTE: Uses planner condition bit flag)
#define COOLANT_MIST_ENABLE PL_COND_FLAG_COOLANT_MIST // M7 (NOTE: Uses planner condition bit flag)
// Modal Group G8: Tool length offset
#define TOOL_LENGTH_OFFSET_CANCEL 0 // G49 (Default: Must be zero)
@ -155,7 +155,7 @@ typedef struct {
uint8_t program_flow; // {M0,M1,M2,M30}
uint8_t coolant; // {M7,M8,M9}
uint8_t spindle; // {M3,M4,M5}
} gc_modal_t;
} gc_modal_t;
typedef struct {
float f; // Feed
@ -173,7 +173,7 @@ typedef struct {
typedef struct {
gc_modal_t modal;
float spindle_speed; // RPM
float feed_rate; // Millimeters/min
uint8_t tool; // Tracks tool number. NOT USED.
@ -181,24 +181,21 @@ typedef struct {
float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code
float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine
// position in mm. Loaded from EEPROM when called.
float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to
// machine zero in mm. Non-persistent. Cleared upon reset and boot.
float tool_length_offset; // Tracks tool length offset value when enabled.
float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine
// position in mm. Loaded from EEPROM when called.
float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to
// machine zero in mm. Non-persistent. Cleared upon reset and boot.
float tool_length_offset; // Tracks tool length offset value when enabled.
} parser_state_t;
extern parser_state_t gc_state;
typedef struct {
// uint16_t command_words; // NOTE: If this bitflag variable fills, G and M words can be separated.
// uint16_t value_words;
typedef struct {
uint8_t non_modal_command;
gc_modal_t modal;
gc_values_t values;
} parser_block_t;
extern parser_block_t gc_block;
// Initialize the parser
void gc_init();
@ -207,6 +204,6 @@ void gc_init();
uint8_t gc_execute_line(char *line);
// Set g-code parser position. Input in steps.
void gc_sync_position();
void gc_sync_position();
#endif

Wyświetl plik

@ -2,7 +2,7 @@
grbl.h - main Grbl include file
Part of Grbl
Copyright (c) 2015 Sungeun K. Jeon
Copyright (c) 2015-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -22,8 +22,8 @@
#define grbl_h
// Grbl versioning system
#define GRBL_VERSION "1.0c"
#define GRBL_VERSION_BUILD "20160330"
#define GRBL_VERSION "1.0e"
#define GRBL_VERSION_BUILD "20160921"
// Define standard libraries used by Grbl.
#include <avr/io.h>
@ -32,7 +32,7 @@
#include <avr/wdt.h>
#include <util/delay.h>
#include <math.h>
#include <inttypes.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
@ -45,6 +45,7 @@
#include "system.h"
#include "defaults.h"
#include "cpu_map.h"
#include "planner.h"
#include "coolant_control.h"
#include "eeprom.h"
#include "gcode.h"
@ -58,5 +59,6 @@
#include "serial.h"
#include "spindle_control.h"
#include "stepper.h"
#include "jog.h"
#endif

54
grbl/jog.c 100644
Wyświetl plik

@ -0,0 +1,54 @@
/*
jog.h - Jogging methods
Part of Grbl
Copyright (c) 2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "grbl.h"
// Sets up valid jog motion received from g-code parser, checks for soft-limits, and executes the jog.
uint8_t jog_execute(parser_block_t *gc_block)
{
// Initialize planner data struct for motion blocks.
// NOTE: Spindle and coolant are allowed to fully function with overrides during a jog.
plan_line_data_t plan_data;
plan_line_data_t *pl_data = &plan_data;
memset(pl_data,0,sizeof(plan_line_data_t)); // Zero pl_data struct
pl_data->feed_rate = gc_block->values.f;
pl_data->spindle_speed = gc_block->values.s; // Continue current spindle and coolant condition.
plan_data.condition = (PL_COND_FLAG_NO_FEED_OVERRIDE | gc_block->modal.spindle | gc_block->modal.coolant);
#ifdef USE_LINE_NUMBERS
pl_data->line_number = JOG_LINE_NUMBER;
#endif
if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) {
if (system_check_travel_limits(gc_block->values.xyz)) { return(STATUS_TRAVEL_EXCEEDED); }
}
// Valid jog command. Plan, set state, and execute.
mc_line(gc_block->values.xyz,pl_data);
if (sys.state == STATE_IDLE) {
if (plan_get_current_block() != NULL) { // Check if there is a block to execute.
sys.state = STATE_JOG;
st_prep_buffer();
st_wake_up(); // NOTE: Manual start. No state machine required.
}
}
return(STATUS_OK);
}

32
grbl/jog.h 100644
Wyświetl plik

@ -0,0 +1,32 @@
/*
jog.h - Jogging methods
Part of Grbl
Copyright (c) 2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef jog_h
#define jog_h
#include "gcode.h"
// System motion line numbers must be zero.
#define JOG_LINE_NUMBER 0
// Sets up valid jog motion received from g-code parser, checks for soft-limits, and executes the jog.
uint8_t jog_execute(parser_block_t *gc_block);
#endif

Wyświetl plik

@ -2,9 +2,9 @@
limits.c - code pertaining to limit-switches and performing the homing cycle
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "grbl.h"
@ -30,7 +30,7 @@
#define HOMING_AXIS_LOCATE_SCALAR 5.0 // Must be > 1 to ensure limit switch is cleared.
#endif
void limits_init()
void limits_init()
{
LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
@ -44,9 +44,9 @@ void limits_init()
LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
} else {
limits_disable();
limits_disable();
}
#ifdef ENABLE_SOFTWARE_DEBOUNCE
MCUSR &= ~(1<<WDRF);
WDTCSR |= (1<<WDCE) | (1<<WDE);
@ -63,7 +63,7 @@ void limits_disable()
}
// Returns limit state as a bit-wise uint8 variable. Each bit indicates an axis limit, where
// Returns limit state as a bit-wise uint8 variable. Each bit indicates an axis limit, where
// triggered is 1 and not triggered is 0. Invert mask is applied. Axes are defined by their
// number in bit position, i.e. Z_AXIS is (1<<2) or bit 2, and Y_AXIS is (1<<1) or bit 1.
uint8_t limits_get_state()
@ -74,7 +74,7 @@ uint8_t limits_get_state()
pin ^= INVERT_LIMIT_PIN_MASK;
#endif
if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { pin ^= LIMIT_MASK; }
if (pin) {
if (pin) {
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
if (pin & get_limit_pin_mask(idx)) { limit_state |= (1 << idx); }
@ -84,11 +84,11 @@ uint8_t limits_get_state()
}
// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing
// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing
// limit switch can cause a lot of problems, like false readings and multiple interrupt calls.
// If a switch is triggered at all, something bad has happened and treat it as such, regardless
// if a limit switch is being disengaged. It's impossible to reliably tell the state of a
// bouncing pin without a debouncing method. A simple software debouncing feature may be enabled
// if a limit switch is being disengaged. It's impossible to reliably tell the state of a
// bouncing pin without a debouncing method. A simple software debouncing feature may be enabled
// through the config.h file, where an extra timer delays the limit pin read by several milli-
// seconds to help with, not fix, bouncing switches.
// NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during
@ -96,72 +96,81 @@ uint8_t limits_get_state()
// 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.
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
// 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 (sys.state != STATE_ALARM) {
if (!(sys_rt_exec_alarm)) {
#ifdef HARD_LIMIT_FORCE_STATE_CHECK
// Check limit pin state.
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm_flag((EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate hard limit critical event
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm_flag((EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate hard limit critical event
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
#endif
}
}
}
}
#else // OPTIONAL: Software debounce limit pin routine.
// Upon limit pin change, enable watchdog timer to create a short delay.
// 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.
WDTCSR &= ~(1<<WDIE); // Disable watchdog timer.
if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
if (!(sys_rt_exec_alarm)) {
// Check limit pin state.
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm_flag((EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate hard limit critical event
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
}
}
}
}
#endif
// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after
// completing. Homing is a special motion case, which involves rapid uncontrolled stops to locate
// the trigger point of the limit switches. The rapid stops are handled by a system level axis lock
// mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically
// the trigger point of the limit switches. The rapid stops are handled by a system level axis lock
// mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically
// circumvent the processes for executing motions in normal operation.
// NOTE: Only the abort realtime command can interrupt this process.
// TODO: Move limit pin-specific calls to a general function for portability.
void limits_go_home(uint8_t cycle_mask)
void limits_go_home(uint8_t cycle_mask)
{
if (sys.abort) { return; } // Block if system reset has been issued.
// Initialize
// Initialize plan data struct for homing motion. Spindle and coolant are disabled.
plan_line_data_t plan_data;
plan_line_data_t *pl_data = &plan_data;
memset(pl_data,0,sizeof(plan_line_data_t));
pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
#ifdef USE_LINE_NUMBERS
pl_data->line_number = HOMING_CYCLE_LINE_NUMBER;
#endif
// Initialize variables used for homing computations.
uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1);
uint8_t step_pin[N_AXIS];
float target[N_AXIS];
float max_travel = 0.0;
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
for (idx=0; idx<N_AXIS; idx++) {
// Initialize step pin masks
step_pin[idx] = get_step_pin_mask(idx);
#ifdef COREXY
if ((idx==A_MOTOR)||(idx==B_MOTOR)) { step_pin[idx] = (get_step_pin_mask(X_AXIS)|get_step_pin_mask(Y_AXIS)); }
#ifdef COREXY
if ((idx==A_MOTOR)||(idx==B_MOTOR)) { step_pin[idx] = (get_step_pin_mask(X_AXIS)|get_step_pin_mask(Y_AXIS)); }
#endif
if (bit_istrue(cycle_mask,bit(idx))) {
if (bit_istrue(cycle_mask,bit(idx))) {
// Set target based on max_travel setting. Ensure homing switches engaged with search scalar.
// NOTE: settings.max_travel[] is stored as a negative value.
max_travel = max(max_travel,(-HOMING_AXIS_SEARCH_SCALAR)*settings.max_travel[idx]);
@ -175,7 +184,7 @@ void limits_go_home(uint8_t cycle_mask)
uint8_t limit_state, axislock, n_active_axis;
do {
system_convert_array_steps_to_mpos(target,sys.position);
system_convert_array_steps_to_mpos(target,sys_position);
// Initialize and declare variables needed for homing routine.
axislock = 0;
@ -184,16 +193,29 @@ void limits_go_home(uint8_t cycle_mask)
// Set target location for active axes and setup computation for homing rate.
if (bit_istrue(cycle_mask,bit(idx))) {
n_active_axis++;
sys.position[idx] = 0;
#ifdef COREXY
if (idx == X_AXIS) {
int32_t axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
sys_position[A_MOTOR] = axis_position;
sys_position[B_MOTOR] = -axis_position;
} else if (idx == Y_AXIS) {
int32_t axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
sys_position[A_MOTOR] = sys_position[B_MOTOR] = axis_position;
} else {
sys_position[Z_AXIS] = 0;
}
#else
sys_position[idx] = 0;
#endif
// Set target direction based on cycle mask and homing cycle approach state.
// NOTE: This happens to compile smaller than any other implementation tried.
if (bit_istrue(settings.homing_dir_mask,bit(idx))) {
if (approach) { target[idx] = -max_travel; }
else { target[idx] = max_travel; }
} else {
} else {
if (approach) { target[idx] = max_travel; }
else { target[idx] = -max_travel; }
}
}
// Apply axislock to the step port pins active in this cycle.
axislock |= step_pin[idx];
}
@ -202,15 +224,11 @@ void limits_go_home(uint8_t cycle_mask)
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
sys.homing_axis_lock = axislock;
plan_sync_position(); // Sync planner position to current machine position.
// Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
#ifdef USE_LINE_NUMBERS
plan_buffer_line(target, homing_rate, false, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan homing motion.
#else
plan_buffer_line(target, homing_rate, false, false); // Bypass mc_line(). Directly plan homing motion.
#endif
pl_data->feed_rate = homing_rate; // Set current homing rate.
plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion.
sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags.
st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
st_wake_up(); // Initiate motion
do {
@ -219,7 +237,14 @@ void limits_go_home(uint8_t cycle_mask)
limit_state = limits_get_state();
for (idx=0; idx<N_AXIS; idx++) {
if (axislock & step_pin[idx]) {
if (limit_state & (1 << idx)) { axislock &= ~(step_pin[idx]); }
if (limit_state & (1 << idx)) {
#ifdef COREXY
if (idx==Z_AXIS) { axislock &= ~(step_pin[Z_AXIS]); }
else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); }
#else
axislock &= ~(step_pin[idx]);
#endif
}
}
}
sys.homing_axis_lock = axislock;
@ -229,10 +254,16 @@ void limits_go_home(uint8_t cycle_mask)
// Exit routines: No time to run protocol_execute_realtime() in this loop.
if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) {
// Homing failure: Limit switches are still engaged after pull-off motion
if ( (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET)) || // Safety door or reset issued
(!approach && (limits_get_state() & cycle_mask)) || // Limit switch still engaged after pull-off motion
( approach && (sys_rt_exec_state & EXEC_CYCLE_STOP)) ) { // Limit switch not found during approach.
uint8_t rt_exec = sys_rt_exec_state;
// Homing failure condition: Reset issued during cycle.
if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
// Homing failure condition: Safety door was opened.
if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); }
// Homing failure condition: Limit switch still engaged after pull-off motion
if (!approach && (limits_get_state() & cycle_mask)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF); }
// Homing failure condition: Limit switch not found during approach.
if (approach && (rt_exec & EXEC_CYCLE_STOP)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); }
if (sys_rt_exec_alarm) {
mc_reset(); // Stop motors, if they are running.
protocol_execute_realtime();
return;
@ -240,39 +271,34 @@ void limits_go_home(uint8_t cycle_mask)
// Pull-off motion complete. Disable CYCLE_STOP from executing.
system_clear_exec_state_flag(EXEC_CYCLE_STOP);
break;
}
}
}
} while (STEP_MASK & axislock);
st_reset(); // Immediately force kill steppers and reset step segment buffer.
plan_reset(); // Reset planner buffer to zero planner current position and to clear previous motions.
delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate.
// Reverse direction and reset homing rate for locate cycle(s).
approach = !approach;
// After first cycle, homing enters locating phase. Shorten search to pull-off distance.
if (approach) {
max_travel = settings.homing_pulloff*HOMING_AXIS_LOCATE_SCALAR;
if (approach) {
max_travel = settings.homing_pulloff*HOMING_AXIS_LOCATE_SCALAR;
homing_rate = settings.homing_feed_rate;
} else {
max_travel = settings.homing_pulloff;
max_travel = settings.homing_pulloff;
homing_rate = settings.homing_seek_rate;
}
} while (n_cycle-- > 0);
// The active cycle axes should now be homed and machine limits have been located. By
// The active cycle axes should now be homed and machine limits have been located. By
// default, Grbl defines machine space as all negative, as do most CNCs. Since limit switches
// can be on either side of an axes, check and set axes machine zero appropriately. Also,
// set up pull-off maneuver from axes limit switches that have been homed. This provides
// some initial clearance off the switches and should also help prevent them from falsely
// triggering when hard limits are enabled or when more than one axes shares a limit pin.
#ifdef COREXY
int32_t off_axis_position = 0;
#endif
int32_t set_axis_position;
// Set machine positions for homed limit switches. Don't update non-homed axes.
for (idx=0; idx<N_AXIS; idx++) {
@ -280,74 +306,56 @@ void limits_go_home(uint8_t cycle_mask)
if (cycle_mask & bit(idx)) {
#ifdef HOMING_FORCE_SET_ORIGIN
set_axis_position = 0;
#else
#else
if ( bit_istrue(settings.homing_dir_mask,bit(idx)) ) {
set_axis_position = lround((settings.max_travel[idx]+settings.homing_pulloff)*settings.steps_per_mm[idx]);
} else {
set_axis_position = lround(-settings.homing_pulloff*settings.steps_per_mm[idx]);
}
#endif
#ifdef COREXY
if (idx==X_AXIS) {
off_axis_position = (sys.position[B_MOTOR] - sys.position[A_MOTOR])/2;
sys.position[A_MOTOR] = set_axis_position - off_axis_position;
sys.position[B_MOTOR] = set_axis_position + off_axis_position;
if (idx==X_AXIS) {
int32_t off_axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
sys_position[A_MOTOR] = set_axis_position + off_axis_position;
sys_position[B_MOTOR] = set_axis_position - off_axis_position;
} else if (idx==Y_AXIS) {
off_axis_position = (sys.position[A_MOTOR] + sys.position[B_MOTOR])/2;
sys.position[A_MOTOR] = off_axis_position - set_axis_position;
sys.position[B_MOTOR] = off_axis_position + set_axis_position;
int32_t off_axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
sys_position[A_MOTOR] = off_axis_position + set_axis_position;
sys_position[B_MOTOR] = off_axis_position - set_axis_position;
} else {
sys.position[idx] = set_axis_position;
}
#else
sys.position[idx] = set_axis_position;
sys_position[idx] = set_axis_position;
}
#else
sys_position[idx] = set_axis_position;
#endif
}
}
plan_sync_position(); // Sync planner position to homed machine position.
// sys.state = STATE_HOMING; // Ensure system state set as homing before returning.
sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
}
// Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed,
// the workspace volume is in all negative space, and the system is in normal operation.
// NOTE: Used by jogging to limit travel within soft-limit volume.
void limits_soft_check(float *target)
{
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
#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]) { sys.soft_limit = true; }
} else {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { sys.soft_limit = true; }
}
#else
// NOTE: max_travel is stored as negative
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { sys.soft_limit = true; }
#endif
if (sys.soft_limit) {
// 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.
if (sys.state == STATE_CYCLE) {
system_set_exec_state_flag(EXEC_FEED_HOLD);
do {
protocol_execute_realtime();
if (sys.abort) { return; }
} while ( sys.state != STATE_IDLE );
}
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
system_set_exec_alarm_flag((EXEC_ALARM_SOFT_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate soft limit critical event
protocol_execute_realtime(); // Execute to enter critical event loop and system abort
return;
if (system_check_travel_limits(target)) {
sys.soft_limit = true;
// 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.
if (sys.state == STATE_CYCLE) {
system_set_exec_state_flag(EXEC_FEED_HOLD);
do {
protocol_execute_realtime();
if (sys.abort) { return; }
} while ( sys.state != STATE_IDLE );
}
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
system_set_exec_alarm(EXEC_ALARM_SOFT_LIMIT); // Indicate soft limit critical event
protocol_execute_realtime(); // Execute to enter critical event loop and system abort
return;
}
}

Wyświetl plik

@ -2,9 +2,9 @@
limits.h - code pertaining to limit-switches and performing the homing cycle
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -20,7 +20,7 @@
*/
#ifndef limits_h
#define limits_h
#define limits_h
// Initialize the limits module
@ -38,4 +38,4 @@ void limits_go_home(uint8_t cycle_mask);
// Check for soft limit violations
void limits_soft_check(float *target);
#endif
#endif

Wyświetl plik

@ -1,10 +1,10 @@
/*
main.c - An embedded CNC Controller with rs274/ngc (g-code) support
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -23,7 +23,7 @@
// Declare system global variable structure
system_t sys;
system_t sys;
int main(void)
@ -33,7 +33,7 @@ int main(void)
settings_init(); // Load Grbl settings from EEPROM
stepper_init(); // Configure stepper pins and interrupt timers
system_init(); // Configure pinout pins and pin-change interrupt
memset(&sys, 0, sizeof(system_t)); // Clear all system variables
sys.abort = true; // Set abort to complete initialization
sei(); // Enable interrupts
@ -48,25 +48,25 @@ int main(void)
#ifdef HOMING_INIT_LOCK
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
#endif
// Force Grbl into an ALARM state upon a power-cycle or hard reset.
#ifdef FORCE_INITIALIZATION_ALARM
sys.state = STATE_ALARM;
#endif
// Grbl initialization loop upon power-up or a system abort. For the latter, all processes
// will return to this loop to be cleanly re-initialized.
for(;;) {
// TODO: Separate configure task that require interrupts to be disabled, especially upon
// a system abort and ensuring any active interrupts are cleanly reset.
// Reset Grbl primary systems.
serial_reset_read_buffer(); // Clear serial read buffer
gc_init(); // Set g-code parser to default state
spindle_init();
coolant_init();
limits_init();
limits_init();
probe_init();
plan_reset(); // Clear block buffer and planner variables
st_reset(); // Clear stepper subsystem variables.
@ -76,15 +76,27 @@ int main(void)
gc_sync_position();
// Reset system variables.
sys.abort = false;
sys.abort = sys.suspend = sys.soft_limit = false;
sys.step_control = STEP_CONTROL_NORMAL_OP;
sys.f_override = DEFAULT_FEED_OVERRIDE;
sys.r_override = DEFAULT_RAPID_OVERRIDE;
sys.spindle_speed_ovr = DEFAULT_SPINDLE_SPEED_OVERRIDE;
sys.toggle_ovr_mask = 0;
sys.report_wco_counter = REPORT_WCO_REFRESH_BUSY_COUNT; // Set to include in first report.
sys.report_ovr_counter = REPORT_OVR_REFRESH_BUSY_COUNT; // Set to include in first report.
sys_probe_state = 0;
sys_rt_exec_state = 0;
sys_rt_exec_alarm = 0;
sys.suspend = false;
sys.soft_limit = false;
sys_rt_exec_motion_override = 0;
sys_rt_exec_accessory_override = 0;
// Print welcome message. Indicates an initialization has occured at power-up or with a reset.
report_init_message();
// Start Grbl main loop. Processes program inputs and executes them.
protocol_main_loop();
}
return 0; /* Never reached */
}

Wyświetl plik

@ -2,10 +2,9 @@
motion_control.c - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -26,38 +25,37 @@
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
// (1 minute)/feed_rate time.
// NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line
// NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line
// segments, must pass through this routine before being passed to the planner. The seperation of
// mc_line and plan_buffer_line is done primarily to 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)
#else
void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate)
#endif
void mc_line(float *target, plan_line_data_t *pl_data)
{
// If enabled, check for soft limit violations. Placed here all line motions are picked up
// from everywhere in Grbl.
if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) { limits_soft_check(target); }
if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) {
// NOTE: Block jog state. Jogging is a special case and soft limits are handled independently.
if (sys.state != STATE_JOG) { limits_soft_check(target); }
}
// If in check gcode mode, prevent motion by blocking planner. Soft limits still work.
if (sys.state == STATE_CHECK_MODE) { return; }
// NOTE: Backlash compensation may be installed here. It will need direction info to track when
// to insert a backlash line motion(s) before the intended line motion and will require its own
// plan_check_full_buffer() and check for system abort loop. Also for position reporting
// plan_check_full_buffer() and check for system abort loop. Also for position reporting
// backlash steps will need to be also tracked, which will need to be kept at a system level.
// There are likely some other things that will need to be tracked as well. However, we feel
// that backlash compensation should NOT be handled by Grbl itself, because there are a myriad
// of ways to implement it and can be effective or ineffective for different CNC machines. This
// would be better handled by the interface as a post-processor task, where the original g-code
// is translated and inserts backlash motions that best suits the machine.
// is translated and inserts backlash motions that best suits the machine.
// NOTE: Perhaps as a middle-ground, all that needs to be sent is a flag or special command that
// indicates to Grbl what is a backlash compensation motion, so that Grbl executes the move but
// doesn't update the machine position values. Since the position values used by the g-code
// parser and planner are separate from the system machine positions, this is doable.
// If the buffer is full: good! That means we are well ahead of the robot.
// If the buffer is full: good! That means we are well ahead of the robot.
// Remain in this loop until there is room in the buffer.
do {
protocol_execute_realtime(); // Check for any run-time commands
@ -68,28 +66,19 @@
// Plan and queue motion into planner buffer
// uint8_t plan_status; // Not used in normal operation.
#ifdef USE_LINE_NUMBERS
plan_buffer_line(target, feed_rate, invert_feed_rate, false, line_number);
#else
plan_buffer_line(target, feed_rate, invert_feed_rate, false);
#endif
plan_buffer_line(target, pl_data);
}
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_X defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
// for vector transformation direction.
// The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance
// 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, uint8_t is_clockwise_arc, 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, uint8_t is_clockwise_arc)
#endif
void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius,
uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc)
{
float center_axis0 = position[axis_0] + offset[axis_0];
float center_axis1 = position[axis_1] + offset[axis_1];
@ -97,7 +86,7 @@
float r_axis1 = -offset[axis_1];
float rt_axis0 = target[axis_0] - center_axis0;
float rt_axis1 = target[axis_1] - center_axis1;
// CCW angle between position and target from circle center. Only one atan2() trig computation required.
float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
if (is_clockwise_arc) { // Correct atan2 output per direction
@ -112,13 +101,13 @@
// For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases.
uint16_t segments = floor(fabs(0.5*angular_travel*radius)/
sqrt(settings.arc_tolerance*(2*radius - settings.arc_tolerance)) );
if (segments) {
if (segments) {
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of
// all segments.
if (invert_feed_rate) { feed_rate *= segments; }
if (pl_data->condition & PL_COND_FLAG_INVERSE_TIME) { pl_data->feed_rate *= segments; }
float theta_per_segment = angular_travel/segments;
float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments;
@ -126,26 +115,26 @@
and phi is the angle of rotation. Solution approach by Jens Geisler.
r_T = [cos(phi) -sin(phi);
sin(phi) cos(phi] * r ;
For arc generation, the center of the circle is the axis of rotation and the radius vector is
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 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 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
(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
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
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
a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead.
This is important when there are successive arc motions.
a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead.
This is important when there are successive arc motions.
*/
// Computes: cos_T = 1 - theta_per_segment^2/2, sin_T = theta_per_segment - theta_per_segment^3/6) in ~52usec
float cos_T = 2.0 - theta_per_segment*theta_per_segment;
@ -157,16 +146,16 @@
float r_axisi;
uint16_t i;
uint8_t count = 0;
for (i = 1; i<segments; i++) { // Increment (segments-1).
if (count < N_ARC_CORRECTION) {
// Apply vector rotation matrix. ~40 usec
r_axisi = r_axis0*sin_T + r_axis1*cos_T;
r_axis0 = r_axis0*cos_T - r_axis1*sin_T;
r_axis1 = r_axisi;
count++;
} else {
} else {
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments. ~375 usec
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
cos_Ti = cos(i*theta_per_segment);
@ -175,33 +164,25 @@
r_axis1 = -offset[axis_0]*sin_Ti - offset[axis_1]*cos_Ti;
count = 0;
}
// Update arc_target location
position[axis_0] = center_axis0 + r_axis0;
position[axis_1] = center_axis1 + r_axis1;
position[axis_linear] += linear_per_segment;
#ifdef USE_LINE_NUMBERS
mc_line(position, feed_rate, invert_feed_rate, line_number);
#else
mc_line(position, feed_rate, invert_feed_rate);
#endif
mc_line(position, pl_data);
// Bail mid-circle on system abort. Runtime command check already performed by mc_line.
if (sys.abort) { return; }
}
}
// Ensure last segment arrives at target location.
#ifdef USE_LINE_NUMBERS
mc_line(target, feed_rate, invert_feed_rate, line_number);
#else
mc_line(target, feed_rate, invert_feed_rate);
#endif
mc_line(target, pl_data);
}
// Execute dwell in seconds.
void mc_dwell(float seconds)
void mc_dwell(float seconds)
{
if (sys.state == STATE_CHECK_MODE) { return; }
protocol_buffer_synchronize();
@ -217,19 +198,19 @@ void mc_homing_cycle()
// Check and abort homing cycle, if hard limits are already enabled. Helps prevent problems
// with machines with limits wired on both ends of travel to one limit pin.
// TODO: Move the pin-specific LIMIT_PIN call to limits.c as a function.
#ifdef LIMITS_TWO_SWITCHES_ON_AXES
if (limits_get_state()) {
#ifdef LIMITS_TWO_SWITCHES_ON_AXES
if (limits_get_state()) {
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
system_set_exec_alarm_flag((EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT));
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT);
return;
}
#endif
limits_disable(); // Disable hard limits pin change register for cycle duration
// -------------------------------------------------------------------------------------
// Perform homing routine. NOTE: Special motion case. Only system reset works.
// Search to engage all axes limit switches at faster homing seek rate.
limits_go_home(HOMING_CYCLE_0); // Homing cycle 0
#ifdef HOMING_CYCLE_1
@ -238,15 +219,16 @@ void mc_homing_cycle()
#ifdef HOMING_CYCLE_2
limits_go_home(HOMING_CYCLE_2); // Homing cycle 2
#endif
protocol_execute_realtime(); // Check for reset and set system abort.
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
// Homing cycle complete! Setup system for normal operation.
// -------------------------------------------------------------------------------------
// Gcode parser position was circumvented by the limits_go_home() routine, so sync position now.
// Sync gcode parser and planner positions to homed position.
gc_sync_position();
plan_sync_position();
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
limits_init();
@ -255,14 +237,8 @@ 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, uint8_t is_probe_away,
uint8_t is_no_error, int32_t line_number)
#else
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away,
uint8_t is_no_error)
#endif
{
void mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t is_probe_away, uint8_t is_no_error)
{
// TODO: Need to update this cycle so it obeys a non-auto cycle start.
if (sys.state == STATE_CHECK_MODE) { return; }
@ -270,41 +246,37 @@ void mc_homing_cycle()
protocol_buffer_synchronize();
// Initialize probing control variables
sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle.
sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle.
probe_configure_invert_mask(is_probe_away);
// After syncing, check if probe is already triggered. If so, halt and issue alarm.
// NOTE: This probe initialization error applies to all probing cycles.
if ( probe_get_state() ) { // Check probe pin state.
system_set_exec_alarm_flag(EXEC_ALARM_PROBE_FAIL);
system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_INITIAL);
protocol_execute_realtime();
}
if (sys.abort) { return; } // Return if system reset has been issued.
// 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);
#else
mc_line(target, feed_rate, invert_feed_rate);
#endif
mc_line(target, pl_data);
// Activate the probing state monitor in the stepper module.
sys_probe_state = PROBE_ACTIVE;
// Perform probing cycle. Wait here until probe is triggered or motion completes.
system_set_exec_state_flag(EXEC_CYCLE_START);
do {
protocol_execute_realtime();
protocol_execute_realtime();
if (sys.abort) { return; } // Check for system abort
} while (sys.state != STATE_IDLE);
// Probing cycle complete!
// Set state variables and error out, if the probe failed and cycle with error is enabled.
if (sys_probe_state == PROBE_ACTIVE) {
if (is_no_error) { memcpy(sys.probe_position, sys.position, sizeof(sys.position)); }
else { system_set_exec_alarm_flag(EXEC_ALARM_PROBE_FAIL); }
} else {
if (is_no_error) { memcpy(sys_probe_position, sys_position, sizeof(sys_position)); }
else { system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_CONTACT); }
} else {
sys.probe_succeeded = true; // Indicate to system the probing cycle completed successfully.
}
sys_probe_state = PROBE_OFF; // Ensure probe state monitor is disabled.
@ -312,13 +284,13 @@ void mc_homing_cycle()
if (sys.abort) { return; } // Check for system abort
// Reset the stepper and planner buffers to remove the remainder of the probe motion.
st_reset(); // Reest step segment buffer.
st_reset(); // Reset 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.
// TODO: Update the g-code parser code to not require this target calculation but uses a gc_sync_position() call.
// NOTE: The target[] variable updated here will be sent back and synced with the g-code parser.
system_convert_array_steps_to_mpos(target, sys.position);
system_convert_array_steps_to_mpos(target, sys_position);
#ifdef MESSAGE_PROBE_COORDINATES
// All done! Output the probe position as message.
@ -329,28 +301,25 @@ void mc_homing_cycle()
// Plans and executes the single special motion case for parking. Independent of main planner buffer.
// NOTE: Uses the always free planner ring buffer head to store motion parameters for execution.
void mc_parking_motion(float *parking_target, float feed_rate)
void mc_parking_motion(float *parking_target, plan_line_data_t *pl_data)
{
if (sys.abort) { return; } // Block during abort.
#ifdef USE_LINE_NUMBERS
uint8_t plan_status = plan_buffer_line(parking_target, feed_rate, false, true, PARKING_MOTION_LINE_NUMBER);
#else
uint8_t plan_status = plan_buffer_line(parking_target, feed_rate, false, true);
#endif
uint8_t plan_status = plan_buffer_line(parking_target, pl_data);
if (plan_status) {
bit_true(sys.step_control, STEP_CONTROL_EXECUTE_PARK);
bit_true(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION);
bit_false(sys.step_control, STEP_CONTROL_END_MOTION); // Allow parking motion to execute, if feed hold is active.
st_parking_setup_buffer(); // Setup step segment buffer for special parking motion case
st_prep_buffer();
st_wake_up();
st_wake_up();
do {
protocol_exec_rt_system();
if (sys.abort) { return; }
} while (sys.step_control & STEP_CONTROL_EXECUTE_PARK);
} while (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION);
st_parking_restore_buffer(); // Restore step segment buffer to normal run state.
} else {
bit_false(sys.step_control, STEP_CONTROL_EXECUTE_PARK);
bit_false(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION);
protocol_exec_rt_system();
}
@ -368,18 +337,18 @@ void mc_reset()
if (bit_isfalse(sys_rt_exec_state, EXEC_RESET)) {
system_set_exec_state_flag(EXEC_RESET);
// Kill spindle and coolant.
// Kill spindle and coolant.
spindle_stop();
coolant_stop();
coolant_set_state(COOLANT_DISABLE);
// Kill steppers only if in any motion state, i.e. cycle, actively holding, or homing.
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
// violated, by which, all bets are off.
if ((sys.state & (STATE_CYCLE | STATE_HOMING)) ||
(sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_PARK))) {
if (sys.state == STATE_HOMING) { system_set_exec_alarm_flag(EXEC_ALARM_HOMING_FAIL); }
else { system_set_exec_alarm_flag(EXEC_ALARM_ABORT_CYCLE); }
if ((sys.state & (STATE_CYCLE | STATE_HOMING | STATE_JOG)) ||
(sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION))) {
if (sys.state == STATE_HOMING) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
else { system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE); }
st_go_idle(); // Force kill steppers. Position has likely been lost.
}
}

Wyświetl plik

@ -2,9 +2,9 @@
motion_control.h - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -23,30 +23,22 @@
#define motion_control_h
#define HOMING_CYCLE_LINE_NUMBER -1
#define PARKING_MOTION_LINE_NUMBER -2
// System motion commands must have a line number of zero.
#define HOMING_CYCLE_LINE_NUMBER 0
#define PARKING_MOTION_LINE_NUMBER 0
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
// (1 minute)/feed_rate time.
#ifdef USE_LINE_NUMBERS
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);
#endif
void mc_line(float *target, plan_line_data_t *pl_data);
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, is_clockwise_arc boolean. Used
// for vector transformation direction.
#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, uint8_t is_clockwise_arc, 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, uint8_t is_clockwise_arc);
#endif
void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius,
uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc);
// Dwell for a specific number of seconds
void mc_dwell(float seconds);
@ -54,16 +46,10 @@ void mc_dwell(float seconds);
void mc_homing_cycle();
// Perform tool length probe cycle. Requires probe switch.
#ifdef USE_LINE_NUMBERS
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away,
uint8_t is_no_error, int32_t line_number);
#else
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away,
uint8_t is_no_error);
#endif
void mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t is_probe_away, uint8_t is_no_error);
// Plans and executes the single special motion case for parking. Independent of main planner buffer.
void mc_parking_motion(float *parking_target, float feed_rate);
void mc_parking_motion(float *parking_target, plan_line_data_t *pl_data);
// Performs system reset. If in motion state, kills all motion and sets system alarm.
void mc_reset();

Wyświetl plik

@ -2,7 +2,7 @@
nuts_bolts.c - Shared functions
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -30,16 +30,16 @@
// available conversion method examples, but has been highly optimized for Grbl. For known
// CNC applications, the typical decimal value is expected to be in the range of E0 to E-4.
// Scientific notation is officially not supported by g-code, and the 'E' character may
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
{
char *ptr = line + *char_counter;
unsigned char c;
// Grab first character and increment pointer. No spaces assumed in line.
c = *ptr++;
// Capture initial positive/minus character
bool isnegative = false;
if (c == '-') {
@ -48,7 +48,7 @@ uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
} else if (c == '+') {
c = *ptr++;
}
// Extract number into fast integer. Track decimal in terms of exponent value.
uint32_t intval = 0;
int8_t exp = 0;
@ -71,31 +71,31 @@ uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
}
c = *ptr++;
}
// Return if no digits have been read.
if (!ndigit) { return(false); };
// Convert integer into floating point.
float fval;
fval = (float)intval;
// Apply decimal. Should perform no more than two floating point multiplications for the
// expected range of E0 to E-4.
if (fval != 0) {
while (exp <= -2) {
fval *= 0.01;
fval *= 0.01;
exp += 2;
}
if (exp < 0) {
fval *= 0.1;
if (exp < 0) {
fval *= 0.1;
} else if (exp > 0) {
do {
fval *= 10.0;
} while (--exp > 0);
}
}
}
// Assign floating point value with correct sign.
// Assign floating point value with correct sign.
if (isnegative) {
*float_ptr = -fval;
} else {
@ -103,7 +103,7 @@ uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
}
*char_counter = ptr - line - 1; // Set char_counter to next statement
return(true);
}
@ -116,7 +116,7 @@ void delay_sec(float seconds, uint8_t mode)
if (sys.abort) { return; }
if (mode == DELAY_MODE_DWELL) {
protocol_execute_realtime();
} else { // DELAY_MODE_SAFETY_DOOR
} else { // DELAY_MODE_SYS_SUSPEND
// Execute rt_system() only to avoid nesting suspend loops.
protocol_exec_rt_system();
if (sys.suspend & SUSPEND_RESTART_RETRACT) { return; } // Bail, if safety door reopens.
@ -128,19 +128,19 @@ void delay_sec(float seconds, uint8_t mode)
// Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(),
// which only accepts constants in future compiler releases.
void delay_ms(uint16_t ms)
void delay_ms(uint16_t ms)
{
while ( ms-- ) { _delay_ms(1); }
}
// Delays variable defined microseconds. Compiler compatibility fix for _delay_us(),
// which only accepts constants in future compiler releases. Written to perform more
// which only accepts constants in future compiler releases. Written to perform more
// efficiently with larger delays, as the counter adds parasitic time in each iteration.
void delay_us(uint32_t us)
void delay_us(uint32_t us)
{
while (us) {
if (us < 10) {
if (us < 10) {
_delay_us(1);
us--;
} else if (us < 100) {
@ -159,3 +159,32 @@ void delay_us(uint32_t us)
// Simple hypotenuse computation function.
float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); }
float convert_delta_vector_to_unit_vector(float *vector)
{
uint8_t idx;
float magnitude = 0.0;
for (idx=0; idx<N_AXIS; idx++) {
if (vector[idx] != 0.0) {
magnitude += vector[idx]*vector[idx];
}
}
magnitude = sqrt(magnitude);
float inv_magnitude = 1.0/magnitude;
for (idx=0; idx<N_AXIS; idx++) { vector[idx] *= inv_magnitude; }
return(magnitude);
}
float limit_value_by_axis_maximum(float *max_value, float *unit_vec)
{
uint8_t idx;
float limit_value = SOME_LARGE_VALUE;
for (idx=0; idx<N_AXIS; idx++) {
if (unit_vec[idx] != 0) { // Avoid divide by zero.
limit_value = min(limit_value,fabs(max_value[idx]/unit_vec[idx]));
}
}
return(limit_value);
}

Wyświetl plik

@ -2,8 +2,8 @@
nuts_bolts.h - Header file for shared definitions, variables, and functions
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -25,9 +25,11 @@
#define false 0
#define true 1
#define SOME_LARGE_VALUE 1.0E+38
// Axis array index values. Must start with 0 and be continuous.
#define N_AXIS 3 // Number of axes
#define X_AXIS 0 // Axis indexing value.
#define X_AXIS 0 // Axis indexing value.
#define Y_AXIS 1
#define Z_AXIS 2
// #define A_AXIS 3
@ -45,7 +47,7 @@
#define TICKS_PER_MICROSECOND (F_CPU/1000000)
#define DELAY_MODE_DWELL 0
#define DELAY_MODE_SAFETY_DOOR 1
#define DELAY_MODE_SYS_SUSPEND 1
// Useful macros
#define clear_vector(a) memset(a, 0, sizeof(a))
@ -55,14 +57,14 @@
#define min(a,b) (((a) < (b)) ? (a) : (b))
// Bit field and masking macros
#define bit(n) (1 << n)
#define bit(n) (1 << n)
#define bit_true(x,mask) (x) |= (mask)
#define bit_false(x,mask) (x) &= ~(mask)
#define bit_istrue(x,mask) ((x & mask) != 0)
#define bit_isfalse(x,mask) ((x & mask) == 0)
// Read a floating point value from a string. Line points to the input buffer, char_counter
// is the indexer pointing to the current character of the line, while float_ptr is
// Read a floating point value from a string. Line points to the input buffer, char_counter
// is the indexer pointing to the current character of the line, while float_ptr is
// a pointer to the result variable. Returns true when it succeeds
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr);
@ -78,4 +80,7 @@ void delay_us(uint32_t us);
// Computes hypotenuse, avoiding avr-gcc's bloated version and the extra error checking.
float hypot_f(float x, float y);
float convert_delta_vector_to_unit_vector(float *vector);
float limit_value_by_axis_maximum(float *max_value, float *unit_vec);
#endif

Wyświetl plik

@ -2,10 +2,10 @@
planner.c - buffers movement commands and manages the acceleration profile plan
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Jens Geisler
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Jens Geisler
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -22,8 +22,6 @@
#include "grbl.h"
#define SOME_LARGE_VALUE 1.0E+38 // Used by rapids and acceleration maximization calculations. Just needs
// to be larger than any feasible (mm/min)^2 or mm/sec^2 value.
static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
static uint8_t block_buffer_tail; // Index of the block to process now
@ -37,13 +35,13 @@ typedef struct {
// from g-code position for movements requiring multiple line motions,
// i.e. arcs, canned cycles, and backlash compensation.
float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment
float previous_nominal_speed_sqr; // Nominal speed of previous path line segment
float previous_nominal_speed; // Nominal speed of previous path line segment
} planner_t;
static planner_t pl;
// Returns the index of the next block in the ring buffer. Also called by stepper segment buffer.
uint8_t plan_next_block_index(uint8_t block_index)
uint8_t plan_next_block_index(uint8_t block_index)
{
block_index++;
if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
@ -52,7 +50,7 @@ uint8_t plan_next_block_index(uint8_t block_index)
// Returns the index of the previous block in the ring buffer
static uint8_t plan_prev_block_index(uint8_t block_index)
static uint8_t plan_prev_block_index(uint8_t block_index)
{
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
block_index--;
@ -60,64 +58,64 @@ static uint8_t plan_prev_block_index(uint8_t block_index)
}
/* PLANNER SPEED DEFINITION
/* PLANNER SPEED DEFINITION
+--------+ <- current->nominal_speed
/ \
current->entry_speed -> + \
/ \
current->entry_speed -> + \
| + <- next->entry_speed (aka exit speed)
+-------------+
time -->
+-------------+
time -->
Recalculates the motion plan according to the following basic guidelines:
1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
(i.e. current->entry_speed) such that:
a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
neighboring blocks.
b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
with a maximum allowable deceleration over the block travel distance.
c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
2. Go over every block in chronological (forward) order and dial down junction speed values if
2. Go over every block in chronological (forward) order and dial down junction speed values if
a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
acceleration over the block travel distance.
When these stages are complete, the planner will have maximized the velocity profiles throughout the all
of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
are possible. If a new block is added to the buffer, the plan is recomputed according to the said
are possible. If a new block is added to the buffer, the plan is recomputed according to the said
guidelines for a new optimal plan.
To increase computational efficiency of these guidelines, a set of planner block pointers have been
created to indicate stop-compute points for when the planner guidelines cannot logically make any further
changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
point) are all accelerating, they are all optimal and can not be altered by a new block added to the
planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
recomputed as stated in the general guidelines.
Planner buffer index mapping:
- block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
- block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
- block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
the buffer is full or empty. As described for standard ring buffers, this block is always empty.
- next_buffer_head: Points to next planner buffer block after the buffer head block. When equal to the
- next_buffer_head: Points to next planner buffer block after the buffer head block. When equal to the
buffer tail, this indicates the buffer is full.
- block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
planner buffer that don't change with the addition of a new block, as describe above. In addition,
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
planner buffer that don't change with the addition of a new block, as describe above. In addition,
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
this requirement when encountered by the plan_discard_current_block() routine during a cycle.
NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't
enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then
enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then
decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this happens and
becomes an annoyance, there are a few simple solutions: (1) Maximize the machine acceleration. The planner
will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line
will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line
motion(s) distance per block to a desired tolerance. The more combined distance the planner has to use,
the faster it can go. (3) Maximize the planner buffer size. This also will increase the combined distance
for the planner to compute over. It also increases the number of computations the planner has to perform
@ -125,14 +123,14 @@ static uint8_t plan_prev_block_index(uint8_t block_index)
ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more.
*/
static void planner_recalculate()
{
static void planner_recalculate()
{
// Initialize block index to the last block in the planner buffer.
uint8_t block_index = plan_prev_block_index(block_buffer_head);
// Bail. Can't do anything with one only one plan-able block.
if (block_index == block_buffer_planned) { return; }
// Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
// block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
// NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
@ -142,19 +140,19 @@ static void planner_recalculate()
// Calculate maximum entry speed for last block in buffer, where the exit speed is always zero.
current->entry_speed_sqr = min( current->max_entry_speed_sqr, 2*current->acceleration*current->millimeters);
block_index = plan_prev_block_index(block_index);
if (block_index == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete.
// Check if the first block is the tail. If so, notify stepper to update its current parameters.
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
} else { // Three or more plan-able blocks
while (block_index != block_buffer_planned) {
while (block_index != block_buffer_planned) {
next = current;
current = &block_buffer[block_index];
block_index = plan_prev_block_index(block_index);
// Check if next block is the tail block(=planned block). If so, update current stepper parameters.
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
// Compute maximum entry speed decelerating over the current block from its exit speed.
if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
@ -166,16 +164,16 @@ static void planner_recalculate()
}
}
}
}
}
// Forward Pass: Forward plan the acceleration curve from the planned pointer onward.
// Also scans for optimal plan breakpoints and appropriately updates the planned pointer.
next = &block_buffer[block_buffer_planned]; // Begin at buffer planned pointer
block_index = plan_next_block_index(block_buffer_planned);
block_index = plan_next_block_index(block_buffer_planned);
while (block_index != block_buffer_head) {
current = next;
next = &block_buffer[block_index];
// Any acceleration detected in the forward pass automatically moves the optimal planned
// pointer forward, since everything before this is all optimal. In other words, nothing
// can improve the plan from the buffer tail to the planned pointer by logic.
@ -187,20 +185,26 @@ static void planner_recalculate()
block_buffer_planned = block_index; // Set optimal plan pointer.
}
}
// Any block set at its maximum entry speed also creates an optimal plan up to this
// point in the buffer. When the plan is bracketed by either the beginning of the
// buffer and a maximum entry speed or two maximum entry speeds, every block in between
// cannot logically be further improved. Hence, we don't have to recompute them anymore.
if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; }
block_index = plan_next_block_index( block_index );
}
}
}
void plan_reset()
void plan_reset()
{
memset(&pl, 0, sizeof(planner_t)); // Clear planner struct
plan_reset_buffer();
}
void plan_reset_buffer()
{
block_buffer_tail = 0;
block_buffer_head = 0; // Empty = tail
next_buffer_head = 1; // plan_next_block_index(block_buffer_head)
@ -208,7 +212,7 @@ void plan_reset()
}
void plan_discard_current_block()
void plan_discard_current_block()
{
if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer.
uint8_t block_index = plan_next_block_index( block_buffer_tail );
@ -219,24 +223,26 @@ void plan_discard_current_block()
}
plan_block_t *plan_get_parking_block()
// Returns address of planner buffer block used by system motions. Called by segment generator.
plan_block_t *plan_get_system_motion_block()
{
return(&block_buffer[block_buffer_head]);
}
plan_block_t *plan_get_current_block()
// Returns address of first planner block, if available. Called by various main program functions.
plan_block_t *plan_get_current_block()
{
if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
return(&block_buffer[block_buffer_tail]);
}
float plan_get_exec_block_exit_speed()
float plan_get_exec_block_exit_speed_sqr()
{
uint8_t block_index = plan_next_block_index(block_buffer_tail);
if (block_index == block_buffer_head) { return( 0.0 ); }
return( sqrt( block_buffer[block_index].entry_speed_sqr ) );
return( block_buffer[block_index].entry_speed_sqr );
}
@ -248,47 +254,89 @@ uint8_t plan_check_full_buffer()
}
// Computes and returns block nominal speed based on running condition and override values.
// NOTE: All system motion commands, such as homing/parking, are not subject to overrides.
float plan_compute_profile_nominal_speed(plan_block_t *block)
{
float nominal_speed;
if (block->condition & PL_COND_FLAG_RAPID_MOTION) {
nominal_speed = block->rapid_rate;
nominal_speed *= (0.01*sys.r_override);
} else {
nominal_speed = block->programmed_rate;
if (!(block->condition & PL_COND_FLAG_NO_FEED_OVERRIDE)) { nominal_speed *= (0.01*sys.f_override); }
if (nominal_speed > block->rapid_rate) { nominal_speed = block->rapid_rate; }
}
if (nominal_speed > MINIMUM_FEED_RATE) { return(nominal_speed); }
return(MINIMUM_FEED_RATE);
}
// Computes and updates the max entry speed (sqr) of the block, based on the minimum of the junction's
// previous and current nominal speeds and max junction speed.
static void plan_compute_profile_parameters(plan_block_t *block, float nominal_speed, float prev_nominal_speed)
{
// Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds.
if (nominal_speed > prev_nominal_speed) { block->max_entry_speed_sqr = prev_nominal_speed*prev_nominal_speed; }
else { block->max_entry_speed_sqr = nominal_speed*nominal_speed; }
if (block->max_entry_speed_sqr > block->max_junction_speed_sqr) { block->max_entry_speed_sqr = block->max_junction_speed_sqr; }
}
// Re-calculates buffered motions profile parameters upon a motion-based override change.
void plan_update_velocity_profile_parameters()
{
uint8_t block_index = block_buffer_tail;
plan_block_t *block;
float nominal_speed;
float prev_nominal_speed = SOME_LARGE_VALUE; // Set high for first block nominal speed calculation.
while (block_index != block_buffer_head) {
block = &block_buffer[block_index];
nominal_speed = plan_compute_profile_nominal_speed(block);
plan_compute_profile_parameters(block, nominal_speed, prev_nominal_speed);
prev_nominal_speed = nominal_speed;
block_index = plan_next_block_index(block_index);
}
pl.previous_nominal_speed = prev_nominal_speed; // Update prev nominal speed for next incoming block.
}
/* Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
All position data passed to the planner must be in terms of machine position to keep the planner
All position data passed to the planner must be in terms of machine position to keep the planner
independent of any coordinate system changes and offsets, which are handled by the g-code parser.
NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control.
In other words, the buffer head is never equal to the buffer tail. Also the feed rate input value
is used in three ways: as a normal feed rate if invert_feed_rate is false, as inverse time if
invert_feed_rate is true, or as seek/rapids rate if the feed_rate value is negative (and
invert_feed_rate always false).
The is_parking_motion boolean tells the planner to plan a motion in the always unused block buffer
invert_feed_rate always false).
The system motion condition tells the planner to plan a motion in the always unused block buffer
head. It avoids changing the planner state and preserves the buffer to ensure subsequent gcode
motions are still planned correctly, while the stepper module only points to the block buffer head
to execute the parking motion. */
#ifdef USE_LINE_NUMBERS
uint8_t plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_parking_motion, int32_t line_number)
#else
uint8_t plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_parking_motion)
#endif
motions are still planned correctly, while the stepper module only points to the block buffer head
to execute the special system motion. */
uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
{
// Prepare and initialize new block
// Prepare and initialize new block. Copy relevant pl_data for block execution.
plan_block_t *block = &block_buffer[block_buffer_head];
block->step_event_count = 0;
block->millimeters = 0;
block->direction_bits = 0;
block->acceleration = SOME_LARGE_VALUE; // Scaled down to maximum acceleration later
memset(block,0,sizeof(plan_block_t)); // Zero all block values.
block->condition = pl_data->condition;
#ifdef VARIABLE_SPINDLE
block->spindle_speed = pl_data->spindle_speed;
#endif
#ifdef USE_LINE_NUMBERS
block->line_number = line_number;
block->line_number = pl_data->line_number;
#endif
// Compute and store initial move distance data.
// TODO: After this for-loop, we don't touch the stepper algorithm data. Might be a good idea
// to try to keep these types of things completely separate from the planner for portability.
int32_t target_steps[N_AXIS], position_steps[N_AXIS];
float unit_vec[N_AXIS], delta_mm;
uint8_t idx;
// Copy position data based on type of motion being planned.
if (is_parking_motion) { memcpy(position_steps, sys.position, sizeof(sys.position)); }
if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) { memcpy(position_steps, sys_position, sizeof(sys_position)); }
else { memcpy(position_steps, pl.position, sizeof(pl.position)); }
#ifdef COREXY
target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]);
target_steps[B_MOTOR] = lround(target[B_MOTOR]*settings.steps_per_mm[B_MOTOR]);
@ -307,9 +355,9 @@ uint8_t plan_check_full_buffer()
}
block->step_event_count = max(block->step_event_count, block->steps[idx]);
if (idx == A_MOTOR) {
delta_mm = ((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS]))/settings.steps_per_mm[idx];
delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] + target_steps[Y_AXIS]-position_steps[Y_AXIS])/settings.steps_per_mm[idx];
} else if (idx == B_MOTOR) {
delta_mm = ((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS]))/settings.steps_per_mm[idx];
delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] - target_steps[Y_AXIS]+position_steps[Y_AXIS])/settings.steps_per_mm[idx];
} else {
delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
}
@ -319,116 +367,97 @@ uint8_t plan_check_full_buffer()
block->step_event_count = max(block->step_event_count, block->steps[idx]);
delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
#endif
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_pin_mask(idx); }
// Incrementally compute total move distance by Euclidean norm. First add square of each term.
block->millimeters += delta_mm*delta_mm;
}
block->millimeters = sqrt(block->millimeters); // Complete millimeters calculation with sqrt()
// Bail if this is a zero-length block. Highly unlikely to occur.
if (block->step_event_count == 0) { return(PLAN_EMPTY_BLOCK); }
// Adjust feed_rate value to mm/min depending on type of rate input (normal, inverse time, or rapids)
// TODO: Need to distinguish a rapids vs feed move for overrides. Some flag of some sort.
if (feed_rate < 0) { feed_rate = SOME_LARGE_VALUE; } // Scaled down to absolute max/rapids rate later
else if (invert_feed_rate) { feed_rate *= block->millimeters; }
if (feed_rate < MINIMUM_FEED_RATE) { feed_rate = MINIMUM_FEED_RATE; } // Prevents step generation round-off condition.
unit_vec[idx] = delta_mm; // Store unit vector numerator
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
// down such that no individual axes maximum values are exceeded with respect to the line direction.
// Set direction bits. Bit enabled always means direction is negative.
if (delta_mm < 0.0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
}
// Bail if this is a zero-length block. Highly unlikely to occur.
if (block->step_event_count == 0) { return(PLAN_EMPTY_BLOCK); }
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
// down such that no individual axes maximum values are exceeded with respect to the line direction.
// NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes,
// if they are also orthogonal/independent. Operates on the absolute value of the unit vector.
float inverse_unit_vec_value;
float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple float divides
float junction_cos_theta = 0.0;
for (idx=0; idx<N_AXIS; idx++) {
if (unit_vec[idx] != 0) { // Avoid divide by zero.
unit_vec[idx] *= inverse_millimeters; // Complete unit vector calculation
inverse_unit_vec_value = fabs(1.0/unit_vec[idx]); // Inverse to remove multiple float divides.
block->millimeters = convert_delta_vector_to_unit_vector(unit_vec);
block->acceleration = limit_value_by_axis_maximum(settings.acceleration, unit_vec);
block->rapid_rate = limit_value_by_axis_maximum(settings.max_rate, unit_vec);
// Check and limit feed rate against max individual axis velocities and accelerations
feed_rate = min(feed_rate,settings.max_rate[idx]*inverse_unit_vec_value);
block->acceleration = min(block->acceleration,settings.acceleration[idx]*inverse_unit_vec_value);
// Incrementally compute cosine of angle between previous and current path. Cos(theta) of the junction
// between the current move and the previous move is simply the dot product of the two unit vectors,
// where prev_unit_vec is negative. Used later to compute maximum junction speed.
junction_cos_theta -= pl.previous_unit_vec[idx] * unit_vec[idx];
}
}
// Store programmed rate.
block->programmed_rate = pl_data->feed_rate;
if (block->condition & PL_COND_FLAG_INVERSE_TIME) { block->programmed_rate *= block->millimeters; }
// TODO: Need to check this method handling zero junction speeds when starting from rest.
if ((block_buffer_head == block_buffer_tail) || is_parking_motion) {
if ((block_buffer_head == block_buffer_tail) || (block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
// Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later.
// If parking motion, the parking block always is assumed to start from rest and end at a complete stop.
// If system motion, the system motion block always is assumed to start from rest and end at a complete stop.
block->entry_speed_sqr = 0.0;
block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity.
} else {
/*
Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
Let a circle be tangent to both previous and current path line segments, where the junction
deviation is defined as the distance from the junction to the closest edge of the circle,
colinear with the circle center. The circular segment joining the two paths represents the
path of centripetal acceleration. Solve for max velocity based on max acceleration about the
radius of the circle, defined indirectly by junction deviation. This may be also viewed as
path width or max_jerk in the previous Grbl version. This approach does not actually deviate
from path, but used as a robust way to compute cornering speeds, as it takes into account the
nonlinearities of both the junction angle and junction velocity.
// Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
// Let a circle be tangent to both previous and current path line segments, where the junction
// deviation is defined as the distance from the junction to the closest edge of the circle,
// colinear with the circle center. The circular segment joining the two paths represents the
// path of centripetal acceleration. Solve for max velocity based on max acceleration about the
// radius of the circle, defined indirectly by junction deviation. This may be also viewed as
// path width or max_jerk in the previous Grbl version. This approach does not actually deviate
// from path, but used as a robust way to compute cornering speeds, as it takes into account the
// nonlinearities of both the junction angle and junction velocity.
//
// NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
// mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
// stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
// is exactly the same. Instead of motioning all the way to junction point, the machine will
// just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
// a continuous mode path, but ARM-based microcontrollers most certainly do.
//
// NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
// changed dynamically during operation nor can the line move geometry. This must be kept in
// memory in the event of a feedrate override changing the nominal speeds of blocks, which can
// change the overall maximum entry speed conditions of all blocks.
float junction_unit_vec[N_AXIS];
float junction_cos_theta = 0.0;
for (idx=0; idx<N_AXIS; idx++) {
junction_cos_theta -= pl.previous_unit_vec[idx]*unit_vec[idx];
junction_unit_vec[idx] = unit_vec[idx]-pl.previous_unit_vec[idx];
}
NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
is exactly the same. Instead of motioning all the way to junction point, the machine will
just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
a continuous mode path, but ARM-based microcontrollers most certainly do.
NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
changed dynamically during operation nor can the line move geometry. This must be kept in
memory in the event of a feedrate override changing the nominal speeds of blocks, which can
change the overall maximum entry speed conditions of all blocks.
*/
// NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
if (junction_cos_theta > 0.999999) {
// For a 0 degree acute junction, just set minimum junction speed.
// For a 0 degree acute junction, just set minimum junction speed.
block->max_junction_speed_sqr = MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED;
} else {
junction_cos_theta = max(junction_cos_theta,-0.999999); // Check for numerical round-off to avoid divide by zero.
float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive.
// TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the
// two junctions. However, this shouldn't be a significant problem except in extreme circumstances.
block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
(block->acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
if (junction_cos_theta < -0.999999) {
// Junction is a straight line or 180 degrees. Junction speed is infinite.
block->max_junction_speed_sqr = SOME_LARGE_VALUE;
} else {
convert_delta_vector_to_unit_vector(junction_unit_vec);
float junction_acceleration = limit_value_by_axis_maximum(settings.acceleration, junction_unit_vec);
float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive.
block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
(junction_acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
}
}
}
// Store block nominal speed
block->nominal_speed_sqr = feed_rate*feed_rate; // (mm/min). Always > 0
// Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds.
block->max_entry_speed_sqr = min(block->max_junction_speed_sqr,
min(block->nominal_speed_sqr,pl.previous_nominal_speed_sqr));
// Block parking motion from updating this data to ensure next g-code motion is computed correctly.
if (!is_parking_motion) {
// Update previous path unit_vector and nominal speed (squared)
// Block system motion from updating this data to ensure next g-code motion is computed correctly.
if (!(block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
float nominal_speed = plan_compute_profile_nominal_speed(block);
plan_compute_profile_parameters(block, nominal_speed, pl.previous_nominal_speed);
pl.previous_nominal_speed = nominal_speed;
// Update previous path unit_vector and planner position.
memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[]
pl.previous_nominal_speed_sqr = block->nominal_speed_sqr;
// Update planner position
memcpy(pl.position, target_steps, sizeof(target_steps)); // pl.position[] = target_steps[]
// New block is all set. Update buffer head and next buffer head indices.
block_buffer_head = next_buffer_head;
block_buffer_head = next_buffer_head;
next_buffer_head = plan_next_block_index(block_buffer_head);
// Finish up by recalculating the plan with the new block.
planner_recalculate();
}
@ -440,19 +469,19 @@ uint8_t plan_check_full_buffer()
void plan_sync_position()
{
// TODO: For motor configurations not in the same coordinate frame as the machine position,
// this function needs to be updated to accomodate the difference.
// this function needs to be updated to accomodate the difference.
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
#ifdef COREXY
if (idx==A_MOTOR) {
pl.position[idx] = (sys.position[A_MOTOR] + sys.position[B_MOTOR])/2;
} else if (idx==B_MOTOR) {
pl.position[idx] = (sys.position[A_MOTOR] - sys.position[B_MOTOR])/2;
if (idx==X_AXIS) {
pl.position[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
} else if (idx==Y_AXIS) {
pl.position[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
} else {
pl.position[idx] = sys.position[idx];
pl.position[idx] = sys_position[idx];
}
#else
pl.position[idx] = sys.position[idx];
pl.position[idx] = sys_position[idx];
#endif
}
}
@ -473,5 +502,5 @@ void plan_cycle_reinitialize()
// Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer.
st_update_plan_block_parameters();
block_buffer_planned = block_buffer_tail;
planner_recalculate();
planner_recalculate();
}

Wyświetl plik

@ -2,7 +2,7 @@
planner.h - buffers movement commands and manages the acceleration profile plan
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -26,59 +26,89 @@
// The number of linear motions that can be in the plan at any give time
#ifndef BLOCK_BUFFER_SIZE
#ifdef USE_LINE_NUMBERS
#define BLOCK_BUFFER_SIZE 16
#define BLOCK_BUFFER_SIZE 15
#else
#define BLOCK_BUFFER_SIZE 18
#define BLOCK_BUFFER_SIZE 16
#endif
#endif
// Returned status message from planner.
#define PLAN_OK true
#define PLAN_EMPTY_BLOCK false
// Define planner data condition flags. Used to denote running conditions of a block.
#define PL_COND_FLAG_RAPID_MOTION bit(0)
#define PL_COND_FLAG_SYSTEM_MOTION bit(1) // Single motion. Circumvents planner state. Used by home/park.
#define PL_COND_FLAG_NO_FEED_OVERRIDE bit(2) // Motion does not honor feed override.
#define PL_COND_FLAG_INVERSE_TIME bit(3)
#define PL_COND_FLAG_SPINDLE_CW bit(4)
#define PL_COND_FLAG_SPINDLE_CCW bit(5)
#define PL_COND_FLAG_COOLANT_FLOOD bit(6)
#define PL_COND_FLAG_COOLANT_MIST bit(7)
// This struct stores a linear movement of a g-code block motion with its critical "nominal" values
// are as specified in the source g-code.
// are as specified in the source g-code.
typedef struct {
// Fields used by the bresenham algorithm for tracing the line
// NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values.
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
uint32_t steps[N_AXIS]; // Step count along each axis
uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block.
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
// Block condition data to ensure correct execution depending on states and overrides.
uint8_t condition; // Block bitflag variable defining block run conditions. Copied from pl_line_data.
#ifdef USE_LINE_NUMBERS
int32_t line_number; // Block line number for real-time reporting. Copied from pl_line_data.
#endif
// Fields used by the motion planner to manage acceleration. Some of these values may be updated
// by the stepper module during execution of special motion cases for replanning purposes.
float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2
float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and
// neighboring nominal speeds with overrides in (mm/min)^2
float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2
float nominal_speed_sqr; // Axis-limit adjusted nominal speed for this block in (mm/min)^2
float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2)
float millimeters; // The remaining distance for this block to be executed in (mm)
// uint8_t max_override; // Maximum override value based on axis speed limits
float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2
float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and
// neighboring nominal speeds with overrides in (mm/min)^2
float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2). Does not change.
float millimeters; // The remaining distance for this block to be executed in (mm).
// NOTE: This value may be altered by stepper algorithm during execution.
#ifdef USE_LINE_NUMBERS
int32_t line_number;
// Stored rate limiting data used by planner when changes occur.
float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2
float rapid_rate; // Axis-limit adjusted maximum rate for this block direction in (mm/min)
float programmed_rate; // Programmed rate of this block (mm/min).
#ifdef VARIABLE_SPINDLE
// Stored spindle speed data used by spindle overrides and resuming methods.
float spindle_speed; // Block spindle speed. Copied from pl_line_data.
#endif
} plan_block_t;
// Initialize and reset the motion plan subsystem
void plan_reset();
// Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
// Planner data prototype. Must be used when passing new motions to the planner.
typedef struct {
float feed_rate; // Desired feed rate for line motion. Value is ignored, if rapid motion.
float spindle_speed; // Desired spindle speed through line motion.
uint8_t condition; // Bitflag variable to indicate planner conditions. See defines above.
#ifdef USE_LINE_NUMBERS
int32_t line_number; // Desired line number to report when executing.
#endif
} plan_line_data_t;
// Initialize and reset the motion plan subsystem
void plan_reset(); // Reset all
void plan_reset_buffer(); // Reset buffer only.
// Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
// in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
// rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
#ifdef USE_LINE_NUMBERS
uint8_t plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_parking_motion, int32_t line_number);
#else
uint8_t plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_parking_motion);
#endif
uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data);
// Called when the current block is no longer needed. Discards the block and makes the memory
// availible for new blocks.
void plan_discard_current_block();
// Gets the planner block for the parking special motion case. Parking uses the always available buffer head.
plan_block_t *plan_get_parking_block();
// Gets the planner block for the special system motion cases. (Parking/Homing)
plan_block_t *plan_get_system_motion_block();
// Gets the current block. Returns NULL if buffer empty
plan_block_t *plan_get_current_block();
@ -87,7 +117,13 @@ plan_block_t *plan_get_current_block();
uint8_t plan_next_block_index(uint8_t block_index);
// Called by step segment buffer when computing executing block velocity profile.
float plan_get_exec_block_exit_speed();
float plan_get_exec_block_exit_speed_sqr();
// Called by main program during planner calculations and step segment buffer during initialization.
float plan_compute_profile_nominal_speed(plan_block_t *block);
// Re-calculates buffered motions profile parameters upon a motion-based override change.
void plan_update_velocity_profile_parameters();
// Reset the planner position vector (in steps)
void plan_sync_position();
@ -101,4 +137,7 @@ 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();
void plan_get_planner_mpos(float *target);
#endif

Wyświetl plik

@ -2,7 +2,7 @@
print.c - Functions for formatting output strings
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -39,20 +39,20 @@ void printPgmString(const char *s)
// void printIntegerInBase(unsigned long n, unsigned long base)
// {
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
// {
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
// unsigned long i = 0;
//
//
// if (n == 0) {
// serial_write('0');
// return;
// }
//
// }
//
// while (n > 0) {
// buf[i++] = n % base;
// n /= base;
// }
//
//
// for (; i > 0; i--)
// serial_write(buf[i - 1] < 10 ?
// '0' + buf[i - 1] :
@ -60,15 +60,33 @@ void printPgmString(const char *s)
// }
// Prints an uint8 variable with base and number of desired digits.
void print_unsigned_int8(uint8_t n, uint8_t base, uint8_t digits)
{
// Prints an uint8 variable in base 10.
void print_uint8_base10(uint8_t n)
{
uint8_t digit_a = 0;
uint8_t digit_b = 0;
if (n >= 100) { // 100-255
digit_a = '0' + n % 10;
n /= 10;
}
if (n >= 10) { // 10-99
digit_b = '0' + n % 10;
n /= 10;
}
serial_write('0' + n);
if (digit_b) { serial_write(digit_b); }
if (digit_a) { serial_write(digit_a); }
}
// Prints an uint8 variable in base 2 with desired number of desired digits.
void print_uint8_base2_ndigit(uint8_t n, uint8_t digits) {
unsigned char buf[digits];
uint8_t i = 0;
for (; i < digits; i++) {
buf[i] = n % base ;
n /= base;
buf[i] = n % 2 ;
n /= 2;
}
for (; i > 0; i--)
@ -76,38 +94,21 @@ void print_unsigned_int8(uint8_t n, uint8_t base, uint8_t digits)
}
// Prints an uint8 variable in base 2.
void print_uint8_base2(uint8_t n) {
print_unsigned_int8(n,2,8);
}
// Prints an uint8 variable in base 10.
void print_uint8_base10(uint8_t n)
{
uint8_t digits;
if (n < 10) { digits = 1; }
else if (n < 100) { digits = 2; }
else { digits = 3; }
print_unsigned_int8(n,10,digits);
}
void print_uint32_base10(uint32_t n)
{
{
if (n == 0) {
serial_write('0');
return;
}
}
unsigned char buf[10];
uint8_t i = 0;
unsigned char buf[10];
uint8_t i = 0;
while (n > 0) {
buf[i++] = n % 10;
n /= 10;
}
for (; i > 0; i--)
serial_write('0' + buf[i-1]);
}
@ -127,7 +128,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.
// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up
// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up
// techniques are actually just slightly slower. Found this out the hard way.
void printFloat(float n, uint8_t decimal_places)
{
@ -143,25 +144,25 @@ void printFloat(float n, uint8_t decimal_places)
}
if (decimals) { n *= 10; }
n += 0.5; // Add rounding factor. Ensures carryover through entire value.
// Generate digits backwards and store in string.
unsigned char buf[10];
unsigned char buf[13];
uint8_t i = 0;
uint32_t a = (long)n;
uint32_t a = (long)n;
buf[decimal_places] = '.'; // Place decimal point, even if decimal places are zero.
while(a > 0) {
if (i == decimal_places) { i++; } // Skip decimal point location
buf[i++] = (a % 10) + '0'; // Get digit
a /= 10;
}
while (i < decimal_places) {
while (i < decimal_places) {
buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1)
}
if (i == decimal_places) { // Fill in leading zero, if needed.
i++;
buf[i++] = '0';
}
buf[i++] = '0';
}
// Print the generated string.
for (; i > 0; i--)
serial_write(buf[i-1]);
@ -173,15 +174,15 @@ void printFloat(float n, uint8_t decimal_places)
// - CoordValue: Handles all position or coordinate values in inches or mm reporting.
// - RateValue: Handles feed rate and current velocity in inches or mm reporting.
// - SettingValue: Handles all floating point settings values (always in mm.)
void printFloat_CoordValue(float n) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
void printFloat_CoordValue(float n) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
printFloat(n*INCH_PER_MM,N_DECIMAL_COORDVALUE_INCH);
} else {
printFloat(n,N_DECIMAL_COORDVALUE_MM);
}
}
void printFloat_RateValue(float n) {
void printFloat_RateValue(float n) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
printFloat(n*INCH_PER_MM,N_DECIMAL_RATEVALUE_INCH);
} else {
@ -193,13 +194,13 @@ void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); }
void printFloat_RPMValue(float n) { printFloat(n,N_DECIMAL_RPMVALUE); }
// Debug tool to print free memory in bytes at the called point.
// Debug tool to print free memory in bytes at the called point.
// NOTE: Keep commented unless using. Part of this function always gets compiled in.
// void printFreeMemory()
// {
// extern int __heap_start, *__brkval;
// extern int __heap_start, *__brkval;
// uint16_t free; // Up to 64k values.
// free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
// free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
// printInteger((int32_t)free);
// printString(" ");
// }

Wyświetl plik

@ -2,7 +2,7 @@
print.h - Functions for formatting output strings
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -31,18 +31,15 @@ void printInteger(long n);
void print_uint32_base10(uint32_t n);
// Prints uint8 variable with base and number of desired digits.
void print_unsigned_int8(uint8_t n, uint8_t base, uint8_t digits);
// Prints an uint8 variable in base 2.
void print_uint8_base2(uint8_t n);
// Prints an uint8 variable in base 10.
void print_uint8_base10(uint8_t n);
// Prints an uint8 variable in base 2 with desired number of desired digits.
void print_uint8_base2_ndigit(uint8_t n, uint8_t digits);
void printFloat(float n, uint8_t decimal_places);
// Floating value printing handlers for special variables types used in Grbl.
// Floating value printing handlers for special variables types used in Grbl.
// - CoordValue: Handles all position or coordinate values in inches or mm reporting.
// - RateValue: Handles feed rate and current velocity in inches or mm reporting.
// - SettingValue: Handles all floating point settings values (always in mm.)
@ -55,4 +52,4 @@ void printFloat_RPMValue(float n);
// Debug tool to print free memory in bytes at the called point. Not used otherwise.
void printFreeMemory();
#endif
#endif

Wyświetl plik

@ -2,7 +2,7 @@
probe.c - code pertaining to probing methods
Part of Grbl
Copyright (c) 2014-2015 Sungeun K. Jeon
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "grbl.h"
@ -26,7 +26,7 @@ uint8_t probe_invert_mask;
// Probe pin initialization routine.
void probe_init()
void probe_init()
{
PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins
#ifdef DISABLE_PROBE_PIN_PULL_UP
@ -34,13 +34,13 @@ void probe_init()
#else
PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation.
#endif
probe_configure_invert_mask(false); // Initialize invert mask. Re-updated during use.
probe_configure_invert_mask(false); // Initialize invert mask.
}
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
void probe_configure_invert_mask(uint8_t is_probe_away)
{
probe_invert_mask = 0; // Initialize as zero.
@ -58,11 +58,9 @@ uint8_t probe_get_state() { return((PROBE_PIN & PROBE_MASK) ^ probe_invert_mask)
// NOTE: This function must be extremely efficient as to not bog down the stepper ISR.
void probe_state_monitor()
{
if (sys_probe_state == PROBE_ACTIVE) {
if (probe_get_state()) {
sys_probe_state = PROBE_OFF;
memcpy(sys.probe_position, sys.position, sizeof(sys.position));
bit_true(sys_rt_exec_state, EXEC_MOTION_CANCEL);
}
if (probe_get_state()) {
sys_probe_state = PROBE_OFF;
memcpy(sys_probe_position, sys_position, sizeof(sys_position));
bit_true(sys_rt_exec_state, EXEC_MOTION_CANCEL);
}
}

Wyświetl plik

@ -2,7 +2,7 @@
probe.h - code pertaining to probing methods
Part of Grbl
Copyright (c) 2014-2015 Sungeun K. Jeon
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,20 +17,20 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef probe_h
#define probe_h
// Values that define the probing state machine.
#ifndef probe_h
#define probe_h
// Values that define the probing state machine.
#define PROBE_OFF 0 // Probing disabled or not in use. (Must be zero.)
#define PROBE_ACTIVE 1 // Actively watching the input pin.
// Probe pin initialization routine.
void probe_init();
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
void probe_configure_invert_mask(uint8_t is_probe_away);
// Returns probe pin state. Triggered = true. Called by gcode parser and probe state monitor.

Wyświetl plik

@ -1,8 +1,8 @@
/*
protocol.c - controls Grbl execution protocol and procedures
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -32,35 +32,39 @@ static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static void protocol_exec_rt_suspend();
/*
/*
GRBL PRIMARY LOOP:
*/
void protocol_main_loop()
{
// ------------------------------------------------------------
// Complete initialization procedures upon a power-up or reset.
// ------------------------------------------------------------
// Print welcome message
report_init_message();
// Perform some machine checks to make sure everything is good to go.
#ifdef CHECK_LIMITS_AT_INIT
if (bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)) {
if (limits_get_state()) {
sys.state = STATE_ALARM; // Ensure alarm state is active.
report_feedback_message(MESSAGE_CHECK_LIMITS);
}
}
#endif
// Check for and report alarm state after a reset, error, or an initial power up.
if (sys.state == STATE_ALARM) {
report_feedback_message(MESSAGE_ALARM_LOCK);
report_feedback_message(MESSAGE_ALARM_LOCK);
} else {
// All systems go! But first check for safety door.
// Check if the safety door is open.
sys.state = STATE_IDLE;
if (system_check_safety_door_ajar()) {
bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state.
}
}
// All systems go!
system_execute_startup(line); // Execute startup script.
}
// ---------------------------------------------------------------------------------
// Primary loop! Upon a system abort, this exits back to main() to reset the system.
// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// Primary loop! Upon a system abort, this exits back to main() to reset the system.
// This is also where Grbl idles while waiting for something to do.
// ---------------------------------------------------------------------------------
uint8_t line_flags = 0;
uint8_t char_counter = 0;
uint8_t c;
@ -68,19 +72,11 @@ void protocol_main_loop()
// Process one line of incoming serial data, as the data becomes available. Performs an
// initial filtering by removing spaces and comments and capitalizing all letters.
// NOTE: While comment, spaces, and block delete(if supported) handling should technically
// be done in the g-code parser, doing it here helps compress the incoming data into Grbl's
// line buffer, which is limited in size. The g-code standard actually states a line can't
// exceed 256 characters, but the Arduino Uno does not have the memory space for this.
// With a better processor, it would be very easy to pull this initial parsing out as a
// seperate task to be shared by the g-code parser and Grbl's system commands.
while((c = serial_read()) != SERIAL_NO_DATA) {
if ((c == '\n') || (c == '\r')) { // End of line reached
protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to calling function upon system abort
if (sys.abort) { return; } // Bail to calling function upon system abort
line[char_counter] = 0; // Set string termination character.
#ifdef REPORT_ECHO_LINE_RECEIVED
@ -90,27 +86,27 @@ void protocol_main_loop()
// Direct and execute one line of formatted input, and report status of execution.
if (line_flags & LINE_FLAG_OVERFLOW) {
// Report line overflow error.
report_status_message(STATUS_OVERFLOW);
report_status_message(STATUS_OVERFLOW);
} else if (line[0] == 0) {
// Empty or comment line. For syncing purposes.
report_status_message(STATUS_OK);
} else if (line[0] == '$') {
// Grbl '$' system command
report_status_message(system_execute_line(line));
} else if (sys.state == STATE_ALARM) {
// Everything else is gcode. Block if in alarm mode.
report_status_message(STATUS_ALARM_LOCK);
} else if (sys.state & (STATE_ALARM | STATE_JOG)) {
// Everything else is gcode. Block if in alarm or jog mode.
report_status_message(STATUS_SYSTEM_GC_LOCK);
} else {
// Parse and execute g-code block.
report_status_message(gc_execute_line(line));
report_status_message(gc_execute_line(line));
}
// Reset tracking data for next line.
line_flags = 0;
char_counter = 0;
} else {
if (line_flags) {
// Throw away all (except EOL) comment characters and overflow characters.
if (c == ')') {
@ -118,9 +114,9 @@ void protocol_main_loop()
if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) { line_flags &= ~(LINE_FLAG_COMMENT_PARENTHESES); }
}
} else {
if (c <= ' ') {
// Throw away whitepace and control characters
} else if (c == '/') {
if (c <= ' ') {
// Throw away whitepace and control characters
} else if (c == '/') {
// Block delete NOT SUPPORTED. Ignore character.
// NOTE: If supported, would simply need to check the system if block delete is enabled.
} else if (c == '(') {
@ -132,11 +128,11 @@ void protocol_main_loop()
} else if (c == ';') {
// NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST.
line_flags |= LINE_FLAG_COMMENT_SEMICOLON;
// TODO: Install '%' feature
// TODO: Install '%' feature
// } else if (c == '%') {
// Program start-end percent sign NOT SUPPORTED.
// NOTE: This maybe installed to tell Grbl when a program is running vs manual input,
// where, during a program, the system auto-cycle start will continue to execute
// where, during a program, the system auto-cycle start will continue to execute
// 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)) {
@ -148,20 +144,20 @@ void protocol_main_loop()
line[char_counter++] = c;
}
}
}
}
// If there are no more characters in the serial read buffer to be processed and executed,
// this indicates that g-code streaming has either filled the planner buffer or has
// this indicates that g-code streaming has either filled the planner buffer or has
// completed. In either case, auto-cycle start, if enabled, any queued moves.
protocol_auto_cycle_start();
protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to main() program loop to reset system.
}
return; /* Never reached */
}
@ -179,28 +175,28 @@ void protocol_buffer_synchronize()
}
// Auto-cycle start has two purposes: 1. Resumes a plan_synchronize() call from a function that
// requires the planner buffer to empty (spindle enable, dwell, etc.) 2. As a user setting that
// automatically begins the cycle when a user enters a valid motion command manually. This is
// intended as a beginners feature to help new users to understand g-code. It can be disabled
// as a beginner tool, but (1.) still operates. If disabled, the operation of cycle start is
// manually issuing a cycle start command whenever the user is ready and there is a valid motion
// command in the planner queue.
// NOTE: This function is called from the main loop, buffer sync, and mc_line() only and executes
// when one of these conditions exist respectively: There are no more blocks sent (i.e. streaming
// is finished, single commands), a command that needs to wait for the motions in the buffer to
// Auto-cycle start triggers when there is a motion ready to execute and if the main program is not
// actively parsing commands.
// NOTE: This function is called from the main loop, buffer sync, and mc_line() only and executes
// when one of these conditions exist respectively: There are no more blocks sent (i.e. streaming
// is finished, single commands), a command that needs to wait for the motions in the buffer to
// execute calls a buffer sync, or the planner buffer is full and ready to go.
void protocol_auto_cycle_start() { system_set_exec_state_flag(EXEC_CYCLE_START); }
void protocol_auto_cycle_start()
{
if (plan_get_current_block() != NULL) { // Check if there are any blocks in the buffer.
system_set_exec_state_flag(EXEC_CYCLE_START); // If so, execute them!
}
}
// This function is the general interface to Grbl's real-time command execution system. It is called
// from various check points in the main program, primarily where there may be a while loop waiting
// for a buffer to clear space or any point where the execution time from the last check point may
// be more than a fraction of a second. This is a way to execute realtime commands asynchronously
// from various check points in the main program, primarily where there may be a while loop waiting
// for a buffer to clear space or any point where the execution time from the last check point may
// be more than a fraction of a second. This is a way to execute realtime commands asynchronously
// (aka multitasking) with grbl's g-code parsing and planning functions. This function also serves
// as an interface for the interrupts to set the system realtime flags, where only the main program
// handles them, removing the need to define more computationally-expensive volatile variables. This
// also provides a controlled way to execute certain tasks without having two or more instances of
// also provides a controlled way to execute certain tasks without having two or more instances of
// the same task, such as the planner recalculating the buffer upon a feedhold or overrides.
// NOTE: The sys_rt_exec_state variable flags are set by any process, step or serial interrupts, pinouts,
// limit switches, or the main program.
@ -211,8 +207,8 @@ void protocol_execute_realtime()
}
// Executes run-time commands, when required. This function primarily operates as Grbl's state
// machine and controls the various real-time features Grbl has to offer.
// Executes run-time commands, when required. This function primarily operates as Grbl's state
// machine and controls the various real-time features Grbl has to offer.
// NOTE: Do not alter this unless you know exactly what you are doing!
void protocol_exec_rt_system()
{
@ -223,82 +219,65 @@ void protocol_exec_rt_system()
// the source of the error to the user. If critical, Grbl disables by entering an infinite
// loop until system reset/abort.
sys.state = STATE_ALARM; // Set system alarm state
if (rt_exec & EXEC_ALARM_HARD_LIMIT) {
report_alarm_message(ALARM_HARD_LIMIT_ERROR);
} else if (rt_exec & EXEC_ALARM_SOFT_LIMIT) {
report_alarm_message(ALARM_SOFT_LIMIT_ERROR);
} else if (rt_exec & EXEC_ALARM_ABORT_CYCLE) {
report_alarm_message(ALARM_ABORT_CYCLE);
} else if (rt_exec & EXEC_ALARM_PROBE_FAIL) {
report_alarm_message(ALARM_PROBE_FAIL);
} else if (rt_exec & EXEC_ALARM_HOMING_FAIL) {
report_alarm_message(ALARM_HOMING_FAIL);
}
report_alarm_message(rt_exec);
// Halt everything upon a critical event flag. Currently hard and soft limits flag this.
if (rt_exec & EXEC_CRITICAL_EVENT) {
if ((rt_exec == EXEC_ALARM_HARD_LIMIT) || (rt_exec == EXEC_ALARM_HARD_LIMIT)) {
report_feedback_message(MESSAGE_CRITICAL_EVENT);
system_clear_exec_state_flag(EXEC_RESET); // Disable any existing reset
do {
// Block everything, except reset and status reports, until user issues reset or power
// cycles. Hard limits typically occur while unattended or not paying attention. Gives
do {
// Block everything, except reset and status reports, until user issues reset or power
// cycles. Hard limits typically occur while unattended or not paying attention. Gives
// the user and a GUI time to do what is needed before resetting, like killing the
// incoming stream. The same could be said about soft limits. While the position is not
// lost, streaming could cause a serious crash if it continues afterwards.
// TODO: Allow status reports during a critical alarm. Still need to think about implications of this.
// if (sys_rt_exec_state & EXEC_STATUS_REPORT) {
// report_realtime_status();
// system_clear_exec_state_flag(EXEC_STATUS_REPORT);
// }
// incoming stream. The same could be said about soft limits. While the position is not
// lost, continued streaming could cause a serious crash if by chance it gets executed.
} while (bit_isfalse(sys_rt_exec_state,EXEC_RESET));
}
system_clear_exec_alarm_flag(0xFF); // Clear all alarm flags
}
rt_exec = sys_rt_exec_state; // Copy volatile sys_rt_exec_state.
if (rt_exec) {
// Execute system abort.
// Execute system abort.
if (rt_exec & EXEC_RESET) {
sys.abort = true; // Only place this is set true.
return; // Nothing else to do but exit.
}
// Execute and serial print status
if (rt_exec & EXEC_STATUS_REPORT) {
if (rt_exec & EXEC_STATUS_REPORT) {
report_realtime_status();
system_clear_exec_state_flag(EXEC_STATUS_REPORT);
}
// NOTE: The math involved to calculate the hold should be low enough for most, if not all,
// operational scenarios. Once hold is initiated, the system enters a suspend state to block
// all main program processes until either reset or resumed.
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) {
// TODO: CHECK MODE? How to handle this? Likely nothing, since it only works when IDLE and then resets Grbl.
// State check for allowable states for hold methods.
if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_MOTION_CANCEL | STATE_HOLD | STATE_SAFETY_DOOR))) {
// If in CYCLE state, all hold states immediately initiate a motion HOLD.
if (sys.state == STATE_CYCLE) {
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = STEP_CONTROL_EXECUTE_HOLD; // Initiate suspend state with active flag.
// NOTE: Once hold is initiated, the system immediately enters a suspend state to block all
// main program processes until either reset or resumed. This ensures a hold completes safely.
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) {
// State check for allowable states for hold methods.
if ((sys.state == STATE_IDLE) ||
(sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_SAFETY_DOOR | STATE_JOG))) {
// If in CYCLE or JOG states, immediately initiate a motion HOLD.
if (sys.state & (STATE_CYCLE | STATE_JOG)) {
if (!(sys.suspend & (SUSPEND_MOTION_CANCEL | SUSPEND_JOG_CANCEL))) { // Block, if already holding.
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = STEP_CONTROL_EXECUTE_HOLD; // Initiate suspend state with active flag.
if (sys.state == STATE_JOG) { sys.suspend |= SUSPEND_JOG_CANCEL; } // Jog cancelled upon any hold event.
}
}
// If IDLE, Grbl is not in motion. Simply indicate suspend state and hold is complete.
if (sys.state == STATE_IDLE) {
if (sys.state == STATE_IDLE) {
sys.suspend = SUSPEND_HOLD_COMPLETE;
sys.step_control = STEP_CONTROL_END_MOTION;
sys.step_control = STEP_CONTROL_END_MOTION;
}
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
// to halt and cancel the remainder of the motion.
if (rt_exec & EXEC_MOTION_CANCEL) {
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
// to hold the CYCLE. If so, only flag that motion cancel is complete.
if (sys.state == STATE_CYCLE) { sys.state = STATE_MOTION_CANCEL; }
// NOTE: Ensures the motion cancel is handled correctly if it is active during a HOLD or DOOR state.
// NOTE: State is still STATE_CYCLE.
sys.suspend |= SUSPEND_MOTION_CANCEL; // Indicate motion cancel when resuming.
}
@ -306,66 +285,67 @@ void protocol_exec_rt_system()
if (rt_exec & EXEC_FEED_HOLD) {
// Block SAFETY_DOOR state from prematurely changing back to HOLD, which should only
// occur if the safety door switch closes.
if (sys.state != STATE_SAFETY_DOOR) { sys.state = STATE_HOLD; }
if (!(sys.state & (STATE_SAFETY_DOOR | STATE_JOG))) { sys.state = STATE_HOLD; }
}
// Execute a safety door stop with a feed hold and disable spindle/coolant.
// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
// devices (spindle/coolant), and blocks resuming until switch is re-engaged.
if (rt_exec & EXEC_SAFETY_DOOR) {
report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR);
// Check if the safety re-opened during a restore parking motion only. Ignore if
// already retracting or parked.
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
if (sys.suspend & SUSPEND_INITIATE_RESTORE) { // Actively restoring
#ifdef PARKING_ENABLE
// Set hold and reset appropriate control flags to restart parking sequence.
if (sys.step_control & STEP_CONTROL_EXECUTE_PARK) {
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_PARK);
sys.suspend &= ~(SUSPEND_HOLD_COMPLETE);
} // else NO_MOTION is active.
#endif
sys.suspend &= ~(SUSPEND_RETRACT_COMPLETE | SUSPEND_INITIATE_RESTORE | SUSPEND_RESTORE_COMPLETE);
sys.suspend |= SUSPEND_RESTART_RETRACT;
report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR);
// If jogging, block safety door methods until jog cancel is complete. Just flag that it happened.
if (!(sys.suspend & SUSPEND_JOG_CANCEL)) {
// Check if the safety re-opened during a restore parking motion only. Ignore if
// already retracting or parked.
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
if (sys.suspend & SUSPEND_INITIATE_RESTORE) { // Actively restoring
#ifdef PARKING_ENABLE
// Set hold and reset appropriate control flags to restart parking sequence.
if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) {
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION);
sys.suspend &= ~(SUSPEND_HOLD_COMPLETE);
} // else NO_MOTION is active.
#endif
sys.suspend &= ~(SUSPEND_RETRACT_COMPLETE | SUSPEND_INITIATE_RESTORE | SUSPEND_RESTORE_COMPLETE);
sys.suspend |= SUSPEND_RESTART_RETRACT;
}
}
sys.state = STATE_SAFETY_DOOR;
}
// NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking motions
// are executed if the door switch closes and the state returns to HOLD.
sys.suspend |= SUSPEND_SAFETY_DOOR_AJAR;
sys.state = STATE_SAFETY_DOOR;
// are executed if the door switch closes and the state returns to HOLD.
sys.suspend |= SUSPEND_SAFETY_DOOR_AJAR;
}
}
system_clear_exec_state_flag((EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR));
system_clear_exec_state_flag((EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR));
}
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
if (rt_exec & EXEC_CYCLE_START) {
// Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
// Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {
if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {
// Resume door state when parking motion has retracted and door has been closed.
if ((sys.state == STATE_SAFETY_DOOR) && !(sys.suspend & SUSPEND_SAFETY_DOOR_AJAR)) {
if (sys.suspend & SUSPEND_RESTORE_COMPLETE) {
sys.state = STATE_IDLE; // Set to IDLE to immediately resume the cycle.
} else if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
// Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
// NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
// the retraction execution is complete, which implies the initial feed hold is not active. To
// restore normal operation, the restore procedures must be initiated by the following flag. Once,
// they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
sys.suspend |= SUSPEND_INITIATE_RESTORE;
}
}
// Cycle start only when IDLE or when a hold is complete and ready to resume.
// NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed.
if ((sys.state == STATE_IDLE) || ((sys.state & (STATE_HOLD | STATE_MOTION_CANCEL)) && (sys.suspend & SUSPEND_HOLD_COMPLETE))) {
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
if bit_isfalse(sys.suspend,SUSPEND_RESTORE_COMPLETE) {
// Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
// NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
// the retraction execution is complete, which implies the initial feed hold is not active. To
// restore normal operation, the restore procedures must be initiated by the following flag. Once,
// they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
sys.suspend |= SUSPEND_INITIATE_RESTORE;
} else {
bit_false(sys.suspend,SUSPEND_SAFETY_DOOR_AJAR);
}
}
}
if (!(sys.suspend & SUSPEND_SAFETY_DOOR_AJAR)) {
if ((sys.state == STATE_IDLE) || ((sys.state & STATE_HOLD) && (sys.suspend & SUSPEND_HOLD_COMPLETE))) {
if (sys.state == STATE_HOLD && (sys.toggle_ovr_mask & TOGGLE_OVR_STOP_ACTIVE_MASK)) {
sys.toggle_ovr_mask |= TOGGLE_OVR_STOP_RESTORE_CYCLE; // Set to restore in suspend routine and cycle start after.
} else {
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
sys.step_control = STEP_CONTROL_NORMAL_OP; // Restore step control to normal operation
if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {
@ -379,36 +359,183 @@ void protocol_exec_rt_system()
}
}
}
}
}
system_clear_exec_state_flag(EXEC_CYCLE_START);
}
// if (rt_exec & EXEC_CYCLE_START) {
// // Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
// // Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
// if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {
// // Cycle start only when IDLE or when a hold is complete and ready to resume.
// // NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed.
// if ((sys.state == STATE_IDLE) || ((sys.state & STATE_HOLD) && (sys.suspend & SUSPEND_HOLD_COMPLETE))) {
// if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
// if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
// if bit_isfalse(sys.suspend,SUSPEND_RESTORE_COMPLETE) {
// // Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
// // NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
// // the retraction execution is complete, which implies the initial feed hold is not active. To
// // restore normal operation, the restore procedures must be initiated by the following flag. Once,
// // they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
// sys.suspend |= SUSPEND_INITIATE_RESTORE;
// } else {
// bit_false(sys.suspend,SUSPEND_SAFETY_DOOR_AJAR);
// }
// }
// }
// if (!(sys.suspend & SUSPEND_SAFETY_DOOR_AJAR)) {
// // Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
// sys.step_control = STEP_CONTROL_NORMAL_OP; // Restore step control to normal operation
// if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {
// sys.suspend = SUSPEND_DISABLE; // Break suspend state.
// sys.state = STATE_CYCLE;
// st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
// st_wake_up();
// } else { // Otherwise, do nothing. Set and resume IDLE state.
// sys.suspend = SUSPEND_DISABLE; // Break suspend state.
// sys.state = STATE_IDLE;
// }
// }
// }
// }
// system_clear_exec_state_flag(EXEC_CYCLE_START);
// }
if (rt_exec & EXEC_CYCLE_STOP) {
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
// realtime command execution in the main program, ensuring that the planner re-plans safely.
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
if ((sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) && !(sys.soft_limit)) {
if ((sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) && !(sys.soft_limit) && !(sys.suspend & SUSPEND_JOG_CANCEL)) {
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
// has issued a resume command or reset.
plan_cycle_reinitialize();
if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { sys.suspend |= SUSPEND_HOLD_COMPLETE; }
bit_false(sys.step_control,(STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_PARK));
} else { // Motion is complete. Includes CYCLE, HOMING, and MOTION_CANCEL states.
sys.suspend = SUSPEND_DISABLE;
sys.state = STATE_IDLE;
}
if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { sys.suspend |= SUSPEND_HOLD_COMPLETE; }
bit_false(sys.step_control,(STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION));
} else {
// Motion complete. Includes CYCLE/JOG/HOMING states and jog cancel/motion cancel/soft limit events.
// NOTE: Motion and jog cancel both immediately return to idle after the hold completes.
if (sys.suspend & SUSPEND_JOG_CANCEL) { // For jog cancel, flush buffers and sync positions.
sys.step_control = STEP_CONTROL_NORMAL_OP;
plan_reset();
st_reset();
gc_sync_position();
plan_sync_position();
}
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) { // Only occurs when safety door opens during jog.
sys.suspend &= ~(SUSPEND_JOG_CANCEL);
sys.suspend |= SUSPEND_HOLD_COMPLETE;
sys.state = STATE_SAFETY_DOOR;
} else {
sys.suspend = SUSPEND_DISABLE;
sys.state = STATE_IDLE;
}
}
system_clear_exec_state_flag(EXEC_CYCLE_STOP);
}
}
// Overrides flag byte (sys.override) and execution should be installed here, since they
// are realtime and require a direct and controlled interface to the main stepper program.
// Execute overrides.
rt_exec = sys_rt_exec_motion_override; // Copy volatile sys_rt_exec_motion_override
if (rt_exec) {
system_clear_exec_motion_overrides(); // Clear all motion override flags.
uint8_t new_f_override = sys.f_override;
if (rt_exec & EXEC_FEED_OVR_RESET) { new_f_override = DEFAULT_FEED_OVERRIDE; }
if (rt_exec & EXEC_FEED_OVR_COARSE_PLUS) { new_f_override += FEED_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_COARSE_MINUS) { new_f_override -= FEED_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_FINE_PLUS) { new_f_override += FEED_OVERRIDE_FINE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_FINE_MINUS) { new_f_override -= FEED_OVERRIDE_FINE_INCREMENT; }
new_f_override = min(new_f_override,MAX_FEED_RATE_OVERRIDE);
new_f_override = max(new_f_override,MIN_FEED_RATE_OVERRIDE);
uint8_t new_r_override = sys.r_override;
if (rt_exec & EXEC_RAPID_OVR_RESET) { new_r_override = DEFAULT_RAPID_OVERRIDE; }
if (rt_exec & EXEC_RAPID_OVR_MEDIUM) { new_r_override = RAPID_OVERRIDE_MEDIUM; }
if (rt_exec & EXEC_RAPID_OVR_LOW) { new_r_override = RAPID_OVERRIDE_LOW; }
if ((new_f_override != sys.f_override) || (new_r_override != sys.r_override)) {
sys.f_override = new_f_override;
sys.r_override = new_r_override;
sys.report_ovr_counter = REPORT_OVR_REFRESH_BUSY_COUNT; // Set to report change immediately
plan_update_velocity_profile_parameters();
plan_cycle_reinitialize();
}
}
rt_exec = sys_rt_exec_accessory_override;
if (rt_exec) {
system_clear_exec_accessory_overrides(); // Clear all accessory override flags.
// NOTE: Unlike motion overrides, spindle overrides do not require a planner reinitialization.
uint8_t last_s_override = sys.spindle_speed_ovr;
if (rt_exec & EXEC_SPINDLE_OVR_RESET) { last_s_override = DEFAULT_SPINDLE_SPEED_OVERRIDE; }
if (rt_exec & EXEC_SPINDLE_OVR_COARSE_PLUS) { last_s_override += SPINDLE_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_COARSE_MINUS) { last_s_override -= SPINDLE_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_FINE_PLUS) { last_s_override += SPINDLE_OVERRIDE_FINE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_FINE_MINUS) { last_s_override -= SPINDLE_OVERRIDE_FINE_INCREMENT; }
last_s_override = min(last_s_override,MAX_SPINDLE_SPEED_OVERRIDE);
last_s_override = max(last_s_override,MIN_SPINDLE_SPEED_OVERRIDE);
if (last_s_override != sys.spindle_speed_ovr) {
sys.spindle_speed_ovr = last_s_override;
sys.report_ovr_counter = REPORT_OVR_REFRESH_BUSY_COUNT; // Set to report change immediately
}
uint8_t last_toggle_ovr_mask = sys.toggle_ovr_mask;
if (rt_exec & EXEC_SPINDLE_OVR_STOP) {
// Toggle allowed only while in HOLD state.
if (sys.state == STATE_HOLD) {
if (!(last_toggle_ovr_mask & TOGGLE_OVR_STOP_ACTIVE_MASK)) { last_toggle_ovr_mask |= TOGGLE_OVR_STOP_INITIATE; }
else if (last_toggle_ovr_mask & TOGGLE_OVR_STOP_ENABLED) { last_toggle_ovr_mask |= TOGGLE_OVR_STOP_RESTORE; }
}
}
// NOTE: Since coolant state always performs a planner sync whenever it changes, g-code parser
// state can be implicitly determine current run state at the beginning of the planner.
if (rt_exec & (EXEC_COOLANT_FLOOD_OVR_TOGGLE | EXEC_COOLANT_MIST_OVR_TOGGLE)) {
if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOLD))) {
uint8_t coolant_state = gc_state.modal.coolant;
#ifdef ENABLE_M7
if (rt_exec & EXEC_COOLANT_MIST_OVR_TOGGLE) {
if (coolant_state & COOLANT_MIST_ENABLE) { bit_false(coolant_state,COOLANT_MIST_ENABLE); }
else { coolant_state |= COOLANT_MIST_ENABLE; }
last_toggle_ovr_mask |= TOGGLE_OVR_MIST_COOLANT;
}
if (rt_exec & EXEC_COOLANT_FLOOD_OVR_TOGGLE) {
if (coolant_state & COOLANT_FLOOD_ENABLE) { bit_false(coolant_state,COOLANT_FLOOD_ENABLE); }
else { coolant_state |= COOLANT_FLOOD_ENABLE; }
last_toggle_ovr_mask |= TOGGLE_OVR_FLOOD_COOLANT;
}
#else
if (coolant_state & COOLANT_FLOOD_ENABLE) { bit_false(coolant_state,COOLANT_FLOOD_ENABLE); }
else { coolant_state |= COOLANT_FLOOD_ENABLE; }
last_toggle_ovr_mask |= TOGGLE_OVR_FLOOD_COOLANT;
#endif
coolant_set_state(coolant_state);
gc_state.modal.coolant = coolant_state;
}
}
if (last_toggle_ovr_mask != sys.toggle_ovr_mask) {
sys.toggle_ovr_mask = last_toggle_ovr_mask;
sys.report_ovr_counter = REPORT_OVR_REFRESH_BUSY_COUNT; // Set to report change immediately
}
}
#ifdef DEBUG
if (sys_rt_exec_debug) {
report_realtime_debug();
sys_rt_exec_debug = 0;
}
#endif
// Reload step segment buffer
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR | STATE_HOMING)) {
st_prep_buffer();
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_SAFETY_DOOR | STATE_HOMING | STATE_JOG)) {
st_prep_buffer();
}
}
@ -418,7 +545,7 @@ void protocol_exec_rt_system()
// The system will enter this loop, create local variables for suspend tasks, and return to
// whatever function that invoked the suspend, such that Grbl resumes normal operation.
// This function is written in a way to promote custom parking motions. Simply use this as a
// template
// template
static void protocol_exec_rt_suspend()
{
#ifdef PARKING_ENABLE
@ -426,131 +553,195 @@ static void protocol_exec_rt_suspend()
float restore_target[N_AXIS];
float parking_target[N_AXIS];
float retract_waypoint = PARKING_PULLOUT_INCREMENT;
plan_line_data_t plan_data;
plan_line_data_t *pl_data = &plan_data;
memset(pl_data,0,sizeof(plan_line_data_t));
pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
#ifdef USE_LINE_NUMBERS
pl_data->line_number = PARKING_MOTION_LINE_NUMBER;
#endif
#endif
plan_block_t *block = plan_get_current_block();
uint8_t restore_condition;
#ifdef VARIABLE_SPINDLE
float restore_spindle_speed;
if (block == NULL) {
restore_condition = (gc_state.modal.spindle | gc_state.modal.coolant);
restore_spindle_speed = gc_state.spindle_speed;
} else {
restore_condition = block->condition;
restore_spindle_speed = block->spindle_speed;
}
#else
float restore_spindle_speed = 0.0; // Without variable spindle, this value is unused.
if (block == NULL) { restore_condition = (gc_state.modal.spindle | gc_state.modal.coolant); }
else { restore_condition = block->condition; }
#endif
while (sys.suspend) {
if (sys.abort) { return; }
// Safety door manager. Handles de/re-energizing, switch state checks, and parking motions.
if ((sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) && (sys.suspend & SUSPEND_HOLD_COMPLETE)) {
// Handles retraction motions and de-energizing.
if (bit_isfalse(sys.suspend,SUSPEND_RETRACT_COMPLETE)) {
#ifndef PARKING_ENABLE
spindle_stop(); // De-energize
coolant_stop(); // De-energize
#else
// Get current position and store restore location and spindle retract waypoint.
system_convert_array_steps_to_mpos(parking_target,sys.position);
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
memcpy(restore_target,parking_target,sizeof(parking_target));
retract_waypoint += restore_target[PARKING_AXIS];
retract_waypoint = min(retract_waypoint,PARKING_TARGET);
}
// Block until initial hold is complete and the machine has stopped motion.
if (sys.suspend & SUSPEND_HOLD_COMPLETE) {
// Safety door manager. Handles de/re-energizing, switch state checks, and parking motions.
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
// Handles retraction motions and de-energizing.
if (bit_isfalse(sys.suspend,SUSPEND_RETRACT_COMPLETE)) {
// Ensure any prior spindle stop override is disabled at start of safety door routine.
bit_false(sys.toggle_ovr_mask,TOGGLE_OVR_STOP_ACTIVE_MASK);
#ifndef PARKING_ENABLE
// Execute slow pull-out parking retract motion. Parking requires homing enabled and
// the current location not exceeding the parking target location.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
if ((bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) &&
(parking_target[PARKING_AXIS] < PARKING_TARGET)) {
// Retract spindle by pullout distance. Ensure retraction motion moves away from
// the workpiece and waypoint motion doesn't exceed the parking target location.
if (parking_target[PARKING_AXIS] < retract_waypoint) {
parking_target[PARKING_AXIS] = retract_waypoint;
mc_parking_motion(parking_target, PARKING_PULLOUT_RATE);
}
spindle_stop(); // De-energize
coolant_stop(); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
// Execute fast parking retract motion to parking target location.
if (parking_target[PARKING_AXIS] < PARKING_TARGET) {
parking_target[PARKING_AXIS] = PARKING_TARGET;
mc_parking_motion(parking_target, PARKING_RATE);
#else
// Get current position and store restore location and spindle retract waypoint.
system_convert_array_steps_to_mpos(parking_target,sys_position);
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
memcpy(restore_target,parking_target,sizeof(parking_target));
retract_waypoint += restore_target[PARKING_AXIS];
retract_waypoint = min(retract_waypoint,PARKING_TARGET);
}
} else {
// Parking motion not possible. Just disable the spindle and coolant.
spindle_stop(); // De-energize
coolant_stop(); // De-energize
}
#endif
sys.suspend &= ~(SUSPEND_RESTART_RETRACT);
sys.suspend |= SUSPEND_RETRACT_COMPLETE;
} else {
// Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to resume.
// NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume.
if (sys.state == STATE_SAFETY_DOOR) {
if (!(system_check_safety_door_ajar())) {
sys.state = STATE_HOLD; // Update to HOLD state to indicate door is closed and ready to resume.
}
}
// Handles parking restore and safety door resume.
if (sys.suspend & SUSPEND_INITIATE_RESTORE) {
#ifdef PARKING_ENABLE
// Execute fast restore motion to the pull-out position. Parking requires homing enabled.
// Execute slow pull-out parking retract motion. Parking requires homing enabled and
// the current location not exceeding the parking target location.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Check to ensure the motion doesn't move below pull-out position.
if (parking_target[PARKING_AXIS] <= PARKING_TARGET) {
if ((bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) &&
(parking_target[PARKING_AXIS] < PARKING_TARGET)) {
// Retract spindle by pullout distance. Ensure retraction motion moves away from
// the workpiece and waypoint motion doesn't exceed the parking target location.
if (parking_target[PARKING_AXIS] < retract_waypoint) {
parking_target[PARKING_AXIS] = retract_waypoint;
mc_parking_motion(parking_target, PARKING_RATE);
pl_data->feed_rate = PARKING_PULLOUT_RATE;
mc_parking_motion(parking_target, pl_data);
}
spindle_stop(); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
// Execute fast parking retract motion to parking target location.
if (parking_target[PARKING_AXIS] < PARKING_TARGET) {
parking_target[PARKING_AXIS] = PARKING_TARGET;
pl_data->feed_rate = PARKING_RATE;
mc_parking_motion(parking_target, pl_data);
}
} else {
// Parking motion not possible. Just disable the spindle and coolant.
spindle_stop(); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
}
#endif
// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SAFETY_DOOR);
sys.suspend &= ~(SUSPEND_RESTART_RETRACT);
sys.suspend |= SUSPEND_RETRACT_COMPLETE;
} else {
// Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to resume.
// NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume.
if (sys.state == STATE_SAFETY_DOOR) {
if (!(system_check_safety_door_ajar())) {
sys.suspend &= ~(SUSPEND_SAFETY_DOOR_AJAR); // Reset door ajar flag to denote ready to resume.
}
}
if (gc_state.modal.coolant != COOLANT_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
coolant_set_state(gc_state.modal.coolant);
delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SAFETY_DOOR);
}
}
#ifdef PARKING_ENABLE
// Execute slow plunge motion from pull-out position to resume position.
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Handles parking restore and safety door resume.
if (sys.suspend & SUSPEND_INITIATE_RESTORE) {
#ifdef PARKING_ENABLE
// Execute fast restore motion to the pull-out position. Parking requires homing enabled.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Check to ensure the motion doesn't move below pull-out position.
if (parking_target[PARKING_AXIS] <= PARKING_TARGET) {
parking_target[PARKING_AXIS] = retract_waypoint;
pl_data->feed_rate = PARKING_RATE;
mc_parking_motion(parking_target, pl_data);
}
}
#endif
// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
// Regardless if the retract parking motion was a valid/safe motion or not, the
// restore parking motion should logically be valid, either by returning to the
// original position through valid machine space or by not moving at all.
mc_parking_motion(restore_target, PARKING_PULLOUT_RATE);
}
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SYS_SUSPEND);
}
}
if (gc_state.modal.coolant != COOLANT_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
coolant_set_state((restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_FLOOD)));
delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SYS_SUSPEND);
}
}
#ifdef PARKING_ENABLE
// Execute slow plunge motion from pull-out position to resume position.
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
// Regardless if the retract parking motion was a valid/safe motion or not, the
// restore parking motion should logically be valid, either by returning to the
// original position through valid machine space or by not moving at all.
pl_data->feed_rate = PARKING_PULLOUT_RATE;
mc_parking_motion(parking_target, pl_data);
}
}
#endif
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
sys.suspend |= SUSPEND_RESTORE_COMPLETE;
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
}
#endif
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
sys.suspend |= SUSPEND_RESTORE_COMPLETE;
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
}
}
} else {
// Feed hold manager. Controls spindle stop override states.
// NOTE: Hold ensured as completed by condition check at the beginning of suspend routine.
if (sys.toggle_ovr_mask & TOGGLE_OVR_STOP_INITIATE) { // Handles beginning of spindle stop
bit_false(sys.toggle_ovr_mask,TOGGLE_OVR_STOP_ACTIVE_MASK); // Clear stop override state
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
spindle_stop(); // De-energize
sys.toggle_ovr_mask |= TOGGLE_OVR_STOP_ENABLED; // Set stop override state to enabled, if de-energized.
}
} else if (sys.toggle_ovr_mask & (TOGGLE_OVR_STOP_RESTORE | TOGGLE_OVR_STOP_RESTORE_CYCLE)) { // Handles restoring of spindle state
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
report_feedback_message(MESSAGE_SPINDLE_RESTORE);
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SYS_SUSPEND);
}
if (sys.toggle_ovr_mask & TOGGLE_OVR_STOP_RESTORE_CYCLE) {
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
}
bit_false(sys.toggle_ovr_mask,TOGGLE_OVR_STOP_ACTIVE_MASK); // Clear stop override state
}
}
}
protocol_exec_rt_system();
}
}

Wyświetl plik

@ -2,7 +2,7 @@
protocol.h - controls Grbl execution protocol and procedures
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -26,7 +26,7 @@
// NOTE: Not a problem except for extreme cases, but the line buffer size can be too small
// and g-code blocks can get truncated. Officially, the g-code standards support up to 256
// characters. In future versions, this will be increased, when we know how much extra
// memory space we can invest into here or we re-write the g-code parser not to have this
// 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 80
@ -40,15 +40,6 @@ void protocol_main_loop();
void protocol_execute_realtime();
void protocol_exec_rt_system();
// Notify the stepper subsystem to start executing the g-code program in buffer.
// void protocol_cycle_start();
// Reinitializes the buffer after a feed hold for a resume.
// void protocol_cycle_reinitialize();
// Initiates a feed hold of the running program
// void protocol_feed_hold();
// Executes the auto cycle feature, if enabled.
void protocol_auto_cycle_start();

Wyświetl plik

@ -2,7 +2,7 @@
report.c - reporting and messaging methods
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -18,11 +18,11 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
/*
This file functions as the primary feedback interface for Grbl. Any outgoing data, such
/*
This file functions as the primary feedback interface for Grbl. Any outgoing data, such
as the protocol status messages, feedback messages, and status reports, are stored here.
For the most part, these functions primarily are called from protocol.c methods. If a
different style feedback is desired (i.e. JSON), then a user can change these following
For the most part, these functions primarily are called from protocol.c methods. If a
different style feedback is desired (i.e. JSON), then a user can change these following
methods to accomodate their needs.
*/
@ -30,86 +30,99 @@
// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
// For every incoming line, this method responds with an 'ok' for a successful command or an
// 'error:' to indicate some error event with the line or some critical system error during
// For every incoming line, this method responds with an 'ok' for a successful command or an
// 'error:' to indicate some error event with the line or some critical system error during
// operation. Errors events can originate from the g-code parser, settings module, or asynchronously
// from a critical error, such as a triggered hard limit. Interface should always monitor for these
// responses.
// NOTE: In silent mode, all error codes are greater than zero.
// TODO: Install silent mode to return only numeric values, primarily for GUIs.
void report_status_message(uint8_t status_code)
void report_status_message(uint8_t status_code)
{
if (status_code == 0) { // STATUS_OK
printPgmString(PSTR("ok\r\n"));
} else {
printPgmString(PSTR("error: "));
#ifdef REPORT_GUI_MODE
print_uint8_base10(status_code);
#else
switch(status_code) {
case STATUS_EXPECTED_COMMAND_LETTER:
printPgmString(PSTR("Expected command letter")); break;
case STATUS_BAD_NUMBER_FORMAT:
printPgmString(PSTR("Bad number format")); break;
case STATUS_INVALID_STATEMENT:
printPgmString(PSTR("Invalid statement")); break;
case STATUS_NEGATIVE_VALUE:
printPgmString(PSTR("Value < 0")); break;
case STATUS_SETTING_DISABLED:
printPgmString(PSTR("Setting disabled")); break;
case STATUS_SETTING_STEP_PULSE_MIN:
printPgmString(PSTR("Value < 3 usec")); break;
case STATUS_SETTING_READ_FAIL:
printPgmString(PSTR("EEPROM read fail. Using defaults")); break;
case STATUS_IDLE_ERROR:
printPgmString(PSTR("Not idle")); break;
case STATUS_ALARM_LOCK:
printPgmString(PSTR("Alarm lock")); break;
case STATUS_SOFT_LIMIT_ERROR:
printPgmString(PSTR("Homing not enabled")); break;
case STATUS_OVERFLOW:
printPgmString(PSTR("Line overflow")); break;
#ifdef MAX_STEP_RATE_HZ
case STATUS_MAX_STEP_RATE_EXCEEDED:
printPgmString(PSTR("Step rate > 30kHz")); break;
#endif
case STATUS_CHECK_DOOR:
printPgmString(PSTR("Check Door")); break;
// Common g-code parser errors.
case STATUS_GCODE_MODAL_GROUP_VIOLATION:
printPgmString(PSTR("Modal group violation")); break;
case STATUS_GCODE_UNSUPPORTED_COMMAND:
printPgmString(PSTR("Unsupported command")); break;
case STATUS_GCODE_UNDEFINED_FEED_RATE:
printPgmString(PSTR("Undefined feed rate")); break;
default:
// Remaining g-code parser errors with error codes
printPgmString(PSTR("Invalid gcode ID:"));
print_uint8_base10(status_code); // Print error code for user reference
}
#endif
printPgmString(PSTR("\r\n"));
switch(status_code) {
case STATUS_OK: // STATUS_OK
printPgmString(PSTR("ok\r\n")); break;
default:
#ifdef REPORT_GUI_MODE
printPgmString(PSTR("error:"));
print_uint8_base10(status_code);
#else
printPgmString(PSTR("error: "));
switch(status_code) {
case STATUS_EXPECTED_COMMAND_LETTER:
printPgmString(PSTR("Expected command letter")); break;
case STATUS_BAD_NUMBER_FORMAT:
printPgmString(PSTR("Bad number format")); break;
case STATUS_INVALID_STATEMENT:
printPgmString(PSTR("Invalid statement")); break;
case STATUS_NEGATIVE_VALUE:
printPgmString(PSTR("Value < 0")); break;
case STATUS_SETTING_DISABLED:
printPgmString(PSTR("Setting disabled")); break;
case STATUS_SETTING_STEP_PULSE_MIN:
printPgmString(PSTR("Value < 3 usec")); break;
case STATUS_SETTING_READ_FAIL:
printPgmString(PSTR("EEPROM read fail. Using defaults")); break;
case STATUS_IDLE_ERROR:
printPgmString(PSTR("Not idle")); break;
case STATUS_SYSTEM_GC_LOCK:
printPgmString(PSTR("G-code lock")); break;
case STATUS_SOFT_LIMIT_ERROR:
printPgmString(PSTR("Homing not enabled")); break;
case STATUS_OVERFLOW:
printPgmString(PSTR("Line overflow")); break;
#ifdef MAX_STEP_RATE_HZ
case STATUS_MAX_STEP_RATE_EXCEEDED:
printPgmString(PSTR("Step rate > 30kHz")); break;
#endif
case STATUS_CHECK_DOOR:
printPgmString(PSTR("Check Door")); break;
// case STATUS_LINE_LENGTH_EXCEEDED: // Supported on Grbl-Mega only.
// printPgmString(PSTR("Line length exceeded")); break;
case STATUS_TRAVEL_EXCEEDED:
printPgmString(PSTR("Travel exceeded")); break;
case STATUS_INVALID_JOG_COMMAND:
printPgmString(PSTR("Invalid jog command")); break;
// Common g-code parser errors.
case STATUS_GCODE_UNSUPPORTED_COMMAND:
printPgmString(PSTR("Unsupported command")); break;
case STATUS_GCODE_MODAL_GROUP_VIOLATION:
printPgmString(PSTR("Modal group violation")); break;
case STATUS_GCODE_UNDEFINED_FEED_RATE:
printPgmString(PSTR("Undefined feed rate")); break;
default:
// Remaining g-code parser errors with error codes
printPgmString(PSTR("Invalid gcode ID:"));
print_uint8_base10(status_code); // Print error code for user reference
}
#endif
printPgmString(PSTR("\r\n"));
}
}
// Prints alarm messages.
void report_alarm_message(int8_t alarm_code)
{
printPgmString(PSTR("ALARM: "));
#ifdef REPORT_GUI_MODE
printPgmString(PSTR("ALARM:"));
print_uint8_base10(alarm_code);
#else
printPgmString(PSTR("ALARM: "));
switch (alarm_code) {
case ALARM_HARD_LIMIT_ERROR:
printPgmString(PSTR("Hard limit")); break;
case ALARM_HARD_LIMIT_ERROR:
printPgmString(PSTR("Hard limit")); break;
case ALARM_SOFT_LIMIT_ERROR:
printPgmString(PSTR("Soft limit")); break;
case ALARM_ABORT_CYCLE:
printPgmString(PSTR("Abort during cycle")); break;
case ALARM_PROBE_FAIL:
printPgmString(PSTR("Probe fail")); break;
case ALARM_HOMING_FAIL:
printPgmString(PSTR("Homing fail")); break;
printPgmString(PSTR("Soft limit")); break;
case ALARM_ABORT_CYCLE:
printPgmString(PSTR("Abort during cycle")); break;
case ALARM_PROBE_FAIL_INITIAL:
case ALARM_PROBE_FAIL_CONTACT:
printPgmString(PSTR("Probe fail")); break;
case ALARM_HOMING_FAIL_RESET:
case ALARM_HOMING_FAIL_DOOR:
case ALARM_HOMING_FAIL_PULLOFF:
case ALARM_HOMING_FAIL_APPROACH:
printPgmString(PSTR("Homing fail")); break;
}
#endif
printPgmString(PSTR("\r\n"));
@ -124,24 +137,27 @@ void report_alarm_message(int8_t alarm_code)
// TODO: Install silence feedback messages option in settings
void report_feedback_message(uint8_t message_code)
{
printPgmString(PSTR("["));
switch(message_code) {
case MESSAGE_CRITICAL_EVENT:
printPgmString(PSTR("Reset to continue")); break;
printPgmString(PSTR("[Reset to continue")); break;
case MESSAGE_ALARM_LOCK:
printPgmString(PSTR("'$H'|'$X' to unlock")); break;
printPgmString(PSTR("['$H'|'$X' to unlock")); break;
case MESSAGE_ALARM_UNLOCK:
printPgmString(PSTR("Caution: Unlocked")); break;
printPgmString(PSTR("[Caution: Unlocked")); break;
case MESSAGE_ENABLED:
printPgmString(PSTR("Enabled")); break;
printPgmString(PSTR("[Enabled")); break;
case MESSAGE_DISABLED:
printPgmString(PSTR("Disabled")); break;
printPgmString(PSTR("[Disabled")); break;
case MESSAGE_SAFETY_DOOR_AJAR:
printPgmString(PSTR("Check Door")); break;
printPgmString(PSTR("[Check Door")); break;
case MESSAGE_CHECK_LIMITS:
printPgmString(PSTR("[Check Limits")); break;
case MESSAGE_PROGRAM_END:
printPgmString(PSTR("Pgm End")); break;
printPgmString(PSTR("[Pgm End")); break;
case MESSAGE_RESTORE_DEFAULTS:
printPgmString(PSTR("Restoring defaults")); break;
printPgmString(PSTR("[Restoring defaults")); break;
case MESSAGE_SPINDLE_RESTORE:
printPgmString(PSTR("[Restoring spindle")); break;
}
printPgmString(PSTR("]\r\n"));
}
@ -155,7 +171,9 @@ void report_init_message()
// Grbl help message
void report_grbl_help() {
#ifndef REPORT_GUI_MODE
#ifdef REPORT_GUI_MODE
printPgmString(PSTR("[$$ $# $G $I $N $x=val $Nx=line $J=line $C $X $H ~ ! ? ctrl-x]\r\n"));
#else
printPgmString(PSTR("$$ (view Grbl settings)\r\n"
"$# (view # parameters)\r\n"
"$G (view parser state)\r\n"
@ -163,6 +181,7 @@ void report_grbl_help() {
"$N (view startup blocks)\r\n"
"$x=value (save Grbl setting)\r\n"
"$Nx=line (save startup block)\r\n"
"$J=line (jog)\r\n"
"$C (check gcode mode)\r\n"
"$X (kill alarm lock)\r\n"
"$H (run homing cycle)\r\n"
@ -181,8 +200,8 @@ void report_grbl_settings() {
#ifdef REPORT_GUI_MODE
printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds);
printPgmString(PSTR("\r\n$1=")); print_uint8_base10(settings.stepper_idle_lock_time);
printPgmString(PSTR("\r\n$2=")); print_uint8_base10(settings.step_invert_mask);
printPgmString(PSTR("\r\n$3=")); print_uint8_base10(settings.dir_invert_mask);
printPgmString(PSTR("\r\n$2=")); print_uint8_base10(settings.step_invert_mask);
printPgmString(PSTR("\r\n$3=")); print_uint8_base10(settings.dir_invert_mask);
printPgmString(PSTR("\r\n$4=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
printPgmString(PSTR("\r\n$5=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
printPgmString(PSTR("\r\n$6=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN));
@ -200,44 +219,49 @@ void report_grbl_settings() {
printPgmString(PSTR("\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff);
printPgmString(PSTR("\r\n$30=")); printFloat_RPMValue(settings.rpm_max);
printPgmString(PSTR("\r\n$31=")); printFloat_RPMValue(settings.rpm_min);
printPgmString(PSTR("\r\n"));
#else
#ifdef VARIABLE_SPINDLE
printPgmString(PSTR("\r\n$32=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_LASER_MODE));
#else
printPgmString(PSTR("\r\n$32=0\r\n"));
#endif
#else
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$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$4=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
printPgmString(PSTR(" (step idle delay, msec)\r\n$2=")); print_uint8_base10(settings.step_invert_mask);
printPgmString(PSTR(" (step port invert mask)\r\n$3=")); print_uint8_base10(settings.dir_invert_mask);
printPgmString(PSTR(" (dir port invert mask)\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(" (status report mask)\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$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$24=")); printFloat_SettingValue(settings.homing_feed_rate);
printPgmString(PSTR(" (homing dir invert mask\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$30=")); printFloat_RPMValue(settings.rpm_max);
printPgmString(PSTR(" (rpm max)\r\n$31=")); printFloat_RPMValue(settings.rpm_min);
printPgmString(PSTR(" (rpm min)\r\n"));
#ifdef VARIABLE_SPINDLE
printPgmString(PSTR(" (rpm min)\r\n$32=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_LASER_MODE));
printPgmString(PSTR(" (laser mode, bool)\r\n"));
#else
printPgmString(PSTR(" (rpm min)\r\n$32=0 (laser mode, bool)\r\n"));
#endif
#endif
// 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("$"));
serial_write('$');
print_uint8_base10(val+idx);
printPgmString(PSTR("="));
serial_write('=');
switch (set_idx) {
case 0: printFloat_SettingValue(settings.steps_per_mm[idx]); break;
case 1: printFloat_SettingValue(settings.max_rate[idx]); break;
@ -247,7 +271,8 @@ void report_grbl_settings() {
#ifdef REPORT_GUI_MODE
printPgmString(PSTR("\r\n"));
#else
printPgmString(PSTR(" ("));
serial_write(' ');
serial_write('(');
switch (idx) {
case X_AXIS: printPgmString(PSTR("x")); break;
case Y_AXIS: printPgmString(PSTR("y")); break;
@ -258,31 +283,31 @@ void report_grbl_settings() {
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"));
#endif
}
val += AXIS_SETTINGS_INCREMENT;
}
}
}
// Prints current probe parameters. Upon a probe command, these parameters are updated upon a
// successful probe or upon a failed probe with the G38.3 without errors command (if supported).
// successful probe or upon a failed probe with the G38.3 without errors command (if supported).
// These values are retained until Grbl is power-cycled, whereby they will be re-zeroed.
void report_probe_parameters()
{
uint8_t i;
float print_position[N_AXIS];
// Report in terms of machine position.
printPgmString(PSTR("[PRB:"));
for (i=0; i< N_AXIS; i++) {
print_position[i] = system_convert_axis_steps_to_mpos(sys.probe_position,i);
print_position[i] = system_convert_axis_steps_to_mpos(sys_probe_position,i);
printFloat_CoordValue(print_position[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
if (i < (N_AXIS-1)) { serial_write(','); }
}
printPgmString(PSTR(":"));
serial_write(':');
print_uint8_base10(sys.probe_succeeded);
printPgmString(PSTR("]\r\n"));
}
@ -293,30 +318,30 @@ void report_ngc_parameters()
{
float coord_data[N_AXIS];
uint8_t coord_select, i;
for (coord_select = 0; coord_select <= SETTING_INDEX_NCOORD; coord_select++) {
if (!(settings_read_coord_data(coord_select,coord_data))) {
report_status_message(STATUS_SETTING_READ_FAIL);
for (coord_select = 0; coord_select <= SETTING_INDEX_NCOORD; coord_select++) {
if (!(settings_read_coord_data(coord_select,coord_data))) {
report_status_message(STATUS_SETTING_READ_FAIL);
return;
}
}
printPgmString(PSTR("[G"));
switch (coord_select) {
case 6: printPgmString(PSTR("28")); break;
case 7: printPgmString(PSTR("30")); break;
default: print_uint8_base10(coord_select+54); break; // G54-G59
}
printPgmString(PSTR(":"));
}
serial_write(':');
for (i=0; i<N_AXIS; i++) {
printFloat_CoordValue(coord_data[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
if (i < (N_AXIS-1)) { serial_write(','); }
else { printPgmString(PSTR("]\r\n")); }
}
}
}
printPgmString(PSTR("[G92:")); // Print G92,G92.1 which are not persistent in memory
for (i=0; i<N_AXIS; i++) {
printFloat_CoordValue(gc_state.coord_offset[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
if (i < (N_AXIS-1)) { serial_write(','); }
else { printPgmString(PSTR("]\r\n")); }
}
}
printPgmString(PSTR("[TLO:")); // Print tool length offset value
printFloat_CoordValue(gc_state.tool_length_offset);
printPgmString(PSTR("]\r\n"));
@ -327,37 +352,35 @@ void report_ngc_parameters()
// Print current gcode parser mode state
void report_gcode_modes()
{
printPgmString(PSTR("["));
switch (gc_state.modal.motion) {
case MOTION_MODE_SEEK : printPgmString(PSTR("G0")); break;
case MOTION_MODE_LINEAR : printPgmString(PSTR("G1")); break;
case MOTION_MODE_CW_ARC : printPgmString(PSTR("G2")); break;
case MOTION_MODE_CCW_ARC : printPgmString(PSTR("G3")); break;
case MOTION_MODE_NONE : printPgmString(PSTR("G80")); break;
default:
printPgmString(PSTR("G38."));
case MOTION_MODE_SEEK : printPgmString(PSTR("[G0")); break;
case MOTION_MODE_LINEAR : printPgmString(PSTR("[G1")); break;
case MOTION_MODE_CW_ARC : printPgmString(PSTR("[G2")); break;
case MOTION_MODE_CCW_ARC : printPgmString(PSTR("[G3")); break;
case MOTION_MODE_NONE : printPgmString(PSTR("[G80")); break;
default:
printPgmString(PSTR("[G38."));
print_uint8_base10(gc_state.modal.motion - (MOTION_MODE_PROBE_TOWARD-2));
}
printPgmString(PSTR(" G"));
print_uint8_base10(gc_state.modal.coord_select+54);
switch (gc_state.modal.plane_select) {
case PLANE_SELECT_XY : printPgmString(PSTR(" G17")); break;
case PLANE_SELECT_ZX : printPgmString(PSTR(" G18")); break;
case PLANE_SELECT_YZ : printPgmString(PSTR(" G19")); break;
}
if (gc_state.modal.units == UNITS_MODE_MM) { printPgmString(PSTR(" G21")); }
else { printPgmString(PSTR(" G20")); }
if (gc_state.modal.distance == DISTANCE_MODE_ABSOLUTE) { printPgmString(PSTR(" G90")); }
else { printPgmString(PSTR(" G91")); }
if (gc_state.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { printPgmString(PSTR(" G93")); }
else { printPgmString(PSTR(" G94")); }
switch (gc_state.modal.program_flow) {
case PROGRAM_FLOW_RUNNING : printPgmString(PSTR(" M0")); break;
case PROGRAM_FLOW_PAUSED : printPgmString(PSTR(" M1")); break;
@ -369,21 +392,23 @@ void report_gcode_modes()
case SPINDLE_ENABLE_CCW : printPgmString(PSTR(" M4")); break;
case SPINDLE_DISABLE : printPgmString(PSTR(" M5")); break;
}
switch (gc_state.modal.coolant) {
case COOLANT_DISABLE : printPgmString(PSTR(" M9")); break;
case COOLANT_FLOOD_ENABLE : printPgmString(PSTR(" M8")); break;
#ifdef ENABLE_M7
case COOLANT_MIST_ENABLE : printPgmString(PSTR(" M7")); break;
#endif
}
#ifdef ENABLE_M7
if (gc_state.modal.coolant) { // Note: Multiple coolant states may be active at the same time.
if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_MIST) { printPgmString(PSTR(" M7")); }
if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_FLOOD) { printPgmString(PSTR(" M8")); }
} else { printPgmString(PSTR(" M9")); }
#else
if (gc_state.modal.coolant) { printPgmString(PSTR(" M8")); }
else { printPgmString(PSTR(" M9")); }
#endif
printPgmString(PSTR(" T"));
print_uint8_base10(gc_state.tool);
printPgmString(PSTR(" F"));
printFloat_RateValue(gc_state.feed_rate);
#ifdef VARIABLE_SPINDLE
printPgmString(PSTR(" S"));
printFloat_RPMValue(gc_state.spindle_speed);
@ -395,8 +420,10 @@ void report_gcode_modes()
// Prints specified startup line
void report_startup_line(uint8_t n, char *line)
{
printPgmString(PSTR("$N")); print_uint8_base10(n);
printPgmString(PSTR("=")); printString(line);
printPgmString(PSTR("$N"));
print_uint8_base10(n);
serial_write('=');
printString(line);
printPgmString(PSTR("\r\n"));
}
@ -419,112 +446,308 @@ void report_echo_line_received(char *line)
}
// Prints real-time data. This function grabs a real-time snapshot of the stepper subprogram
// Prints real-time data. This function grabs a real-time snapshot of the stepper subprogram
// and the actual location of the CNC machine. Users may change the following function to their
// specific needs, but the desired real-time data report must be as short as possible. This is
// requires as it minimizes the computational overhead and allows grbl to keep running smoothly,
// requires as it minimizes the computational overhead and allows grbl to keep running smoothly,
// especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz).
void report_realtime_status()
{
// **Under construction** Bare-bones status report. Provides real-time machine position relative to
// the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). Eventually
// to be added are distance to go on block, processed block id, and feed rate. Also a settings bitmask
// for a user to select the desired real-time data.
uint8_t idx;
int32_t current_position[N_AXIS]; // Copy current state of the system position variable
memcpy(current_position,sys.position,sizeof(sys.position));
float print_position[N_AXIS];
// Report current machine state
switch (sys.state) {
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
case STATE_MOTION_CANCEL: // Report run state.
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
case STATE_HOLD: printPgmString(PSTR("<Hold")); break;
case STATE_HOMING: printPgmString(PSTR("<Home")); break;
case STATE_ALARM: printPgmString(PSTR("<Alarm")); break;
case STATE_CHECK_MODE: printPgmString(PSTR("<Check")); break;
case STATE_SAFETY_DOOR: printPgmString(PSTR("<Door")); break;
}
// If reporting a position, convert the current step count (current_position) to millimeters.
if (bit_istrue(settings.status_report_mask,(BITFLAG_RT_STATUS_MACHINE_POSITION | BITFLAG_RT_STATUS_WORK_POSITION))) {
system_convert_array_steps_to_mpos(print_position,current_position);
}
// Report machine position
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_MACHINE_POSITION)) {
printPgmString(PSTR(",MPos:"));
for (idx=0; idx< N_AXIS; idx++) {
printFloat_CoordValue(print_position[idx]);
if (idx < (N_AXIS-1)) { printPgmString(PSTR(",")); }
}
}
// Report work position
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_WORK_POSITION)) {
printPgmString(PSTR(",WPos:"));
for (idx=0; idx< N_AXIS; idx++) {
// Apply work coordinate offsets and tool length offset to current position.
print_position[idx] -= gc_state.coord_system[idx]+gc_state.coord_offset[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { print_position[idx] -= gc_state.tool_length_offset; }
printFloat_CoordValue(print_position[idx]);
if (idx < (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());
}
#ifdef USE_CLASSIC_REALTIME_REPORT
// 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);
#endif
#ifdef REPORT_REALTIME_RATE
// Report realtime rate
printPgmString(PSTR(",F:"));
printFloat_RateValue(st_get_realtime_rate());
#endif
#ifdef REPORT_ALL_PIN_STATES
if (bit_istrue(settings.status_report_mask,
( BITFLAG_RT_STATUS_LIMIT_PINS| BITFLAG_RT_STATUS_PROBE_PIN | BITFLAG_RT_STATUS_CONTROL_PINS ))) {
printPgmString(PSTR(",Pin:"));
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_LIMIT_PINS)) {
print_unsigned_int8(limits_get_state(),2,N_AXIS);
}
printPgmString(PSTR("|"));
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_PROBE_PIN)) {
if (probe_get_state()) { printPgmString(PSTR("1")); }
else { printPgmString(PSTR("0")); }
}
printPgmString(PSTR("|"));
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_CONTROL_PINS)) {
print_unsigned_int8(system_control_get_state(),2,N_CONTROL_PIN);
uint8_t idx;
int32_t current_position[N_AXIS]; // Copy current state of the system position variable
memcpy(current_position,sys_position,sizeof(sys_position));
float print_position[N_AXIS];
// Report current machine state
switch (sys.state) {
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
case STATE_HOLD:
if (!(sys.suspend & SUSPEND_JOG_CANCEL)) {
printPgmString(PSTR("<Hold"));
break;
} // Continues to print jog state during jog cancel.
case STATE_JOG: printPgmString(PSTR("<Jog")); break;
case STATE_HOMING: printPgmString(PSTR("<Home")); break;
case STATE_ALARM: printPgmString(PSTR("<Alarm")); break;
case STATE_CHECK_MODE: printPgmString(PSTR("<Check")); break;
case STATE_SAFETY_DOOR:
if (!(sys.suspend & SUSPEND_RETRACT_COMPLETE)) {
printPgmString(PSTR("<Door"));
} else {
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) { printPgmString(PSTR("<Door")); }
else { printPgmString(PSTR("<Hold")); }
}
break;
}
// If reporting a position, convert the current step count (current_position) to millimeters.
if (bit_istrue(settings.status_report_mask,(BITFLAG_RT_STATUS_MACHINE_POSITION | BITFLAG_RT_STATUS_WORK_POSITION))) {
system_convert_array_steps_to_mpos(print_position,current_position);
}
// Report machine position
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_MACHINE_POSITION)) {
printPgmString(PSTR(",MPos:"));
for (idx=0; idx< N_AXIS; idx++) {
printFloat_CoordValue(print_position[idx]);
if (idx < (N_AXIS-1)) { serial_write(','); }
}
}
// Report work position
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_WORK_POSITION)) {
printPgmString(PSTR(",WPos:"));
for (idx=0; idx< N_AXIS; idx++) {
// Apply work coordinate offsets and tool length offset to current position.
print_position[idx] -= gc_state.coord_system[idx]+gc_state.coord_offset[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { print_position[idx] -= gc_state.tool_length_offset; }
printFloat_CoordValue(print_position[idx]);
if (idx < (N_AXIS-1)) { serial_write(','); }
}
}
// 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);
#endif
#ifdef REPORT_REALTIME_RATE
// Report realtime rate
printPgmString(PSTR(",F:"));
printFloat_RateValue(st_get_realtime_rate());
#endif
#ifdef REPORT_ALL_PIN_STATES
if (bit_istrue(settings.status_report_mask,
( BITFLAG_RT_STATUS_LIMIT_PINS| BITFLAG_RT_STATUS_PROBE_PIN | BITFLAG_RT_STATUS_CONTROL_PINS ))) {
printPgmString(PSTR(",Pin:"));
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_LIMIT_PINS)) {
print_uint8_base2_ndigit(limits_get_state(),N_AXIS);
}
printPgmString(PSTR("|"));
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_PROBE_PIN)) {
if (probe_get_state()) { printPgmString(PSTR("1")); }
else { printPgmString(PSTR("0")); }
}
printPgmString(PSTR("|"));
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_CONTROL_PINS)) {
print_uint8_base2_ndigit(system_control_get_state(),N_CONTROL_PIN);
}
}
#else
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_LIMIT_PINS)) {
printPgmString(PSTR(",Lim:"));
print_uint8_base2_ndigit(limits_get_state(),N_AXIS);
}
#endif
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_OVERRIDES)) {
printPgmString(PSTR(",Ov:"));
print_uint8_base10(sys.f_override);
serial_write(',');
print_uint8_base10(sys.r_override);
serial_write(',');
print_uint8_base10(sys.spindle_speed_ovr);
if (sys.toggle_ovr_mask) {
printPgmString(PSTR("|T:"));
if (sys.toggle_ovr_mask & TOGGLE_OVR_STOP_ACTIVE_MASK) { serial_write('S'); }
if (sys.toggle_ovr_mask & TOGGLE_OVR_FLOOD_COOLANT) { serial_write('F'); }
#ifdef ENABLE_M7
if (sys.toggle_ovr_mask & TOGGLE_OVR_MIST_COOLANT) { serial_write('M'); }
#endif
bit_false(sys.toggle_ovr_mask, (TOGGLE_OVR_FLOOD_COOLANT|TOGGLE_OVR_FLOOD_COOLANT));
}
}
printPgmString(PSTR(">\r\n"));
#else
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_LIMIT_PINS)) {
printPgmString(PSTR(",Lim:"));
print_unsigned_int8(limits_get_state(),2,N_AXIS);
uint8_t idx;
int32_t current_position[N_AXIS]; // Copy current state of the system position variable
memcpy(current_position,sys_position,sizeof(sys_position));
float print_position[N_AXIS];
system_convert_array_steps_to_mpos(print_position,current_position);
// Report current machine state and sub-states
switch (sys.state) {
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
case STATE_HOLD:
if (!(sys.suspend & SUSPEND_JOG_CANCEL)) {
printPgmString(PSTR("<Hold:"));
if (sys.suspend & SUSPEND_HOLD_COMPLETE) { serial_write('0'); } // Ready to resume
else { serial_write('1'); } // Actively holding
break;
} // Continues to print jog state during jog cancel.
case STATE_JOG: printPgmString(PSTR("<Jog")); break;
case STATE_HOMING: printPgmString(PSTR("<Home")); break;
case STATE_ALARM: printPgmString(PSTR("<Alarm")); break;
case STATE_CHECK_MODE: printPgmString(PSTR("<Check")); break;
case STATE_SAFETY_DOOR:
printPgmString(PSTR("<Door:"));
if (sys.suspend & SUSPEND_INITIATE_RESTORE) {
serial_write('3'); // Restoring
} else {
if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
serial_write('1'); // Door ajar
} else {
serial_write('0');
} // Door closed and ready to resume
} else {
serial_write('2'); // Retracting
}
}
break;
// case STATE_SLEEP: printPgmString(PSTR("<Sleep:")); // [Grbl-Mega Only]
// if (sys.suspend & SUSPEND_RETRACT_COMPLETE) { printPgmString(PSTR("0")); } // Parked
// else { printPgmString(PSTR("1")); } // Actively holding and retracting
// break;
}
// Report machine position
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE)) {
printPgmString(PSTR("|MPos:"));
} else {
// Report work position
printPgmString(PSTR("|WPos:"));
for (idx=0; idx< N_AXIS; idx++) {
// Apply work coordinate offsets and tool length offset to current position.
print_position[idx] -= gc_state.coord_system[idx]+gc_state.coord_offset[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { print_position[idx] -= gc_state.tool_length_offset; }
}
}
for (idx=0; idx< N_AXIS; idx++) {
printFloat_CoordValue(print_position[idx]);
if (idx < (N_AXIS-1)) { serial_write(','); }
}
// Returns planner and serial read buffer states.
#ifdef REPORT_FIELD_BUFFER_STATE
printPgmString(PSTR("|Bf:"));
print_uint8_base10(plan_get_block_buffer_count());
serial_write(',');
print_uint8_base10(serial_get_rx_buffer_count());
#endif
#ifdef USE_LINE_NUMBERS
#ifdef REPORT_FIELD_LINE_NUMBERS
// Report current line number
plan_block_t * cur_block = plan_get_current_block();
if (cur_block != NULL) {
uint32_t ln = cur_block->line_number;
if (ln > 0) {
printPgmString(PSTR("|Ln:"));
printInteger(ln);
}
}
#endif
#endif
// Report realtime rate
#ifdef REPORT_FIELD_CURRENT_RATE
printPgmString(PSTR("|F:"));
printFloat_RateValue(st_get_realtime_rate());
#endif
#ifdef REPORT_FIELD_PIN_STATE
uint8_t lim_pin_state = limits_get_state();
uint8_t ctrl_pin_state = system_control_get_state();
uint8_t prb_pin_state = probe_get_state();
if (lim_pin_state | ctrl_pin_state | prb_pin_state) {
printPgmString(PSTR("|Pn:"));
if (prb_pin_state) { serial_write('P'); }
if (lim_pin_state) {
if (bit_istrue(lim_pin_state,bit(X_AXIS))) { serial_write('X'); }
if (bit_istrue(lim_pin_state,bit(Y_AXIS))) { serial_write('Y'); }
if (bit_istrue(lim_pin_state,bit(Z_AXIS))) { serial_write('Z'); }
}
if (ctrl_pin_state) {
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_SAFETY_DOOR)) { serial_write('D'); }
#endif
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_RESET)) { serial_write('R'); }
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_FEED_HOLD)) { serial_write('H'); }
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_CYCLE_START)) { serial_write('S'); }
}
}
#endif
#ifdef REPORT_FIELD_WORK_COORD_OFFSET
if (sys.report_wco_counter++ >= REPORT_WCO_REFRESH_BUSY_COUNT) {
if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
sys.report_wco_counter = 1; // Reset counter for slow refresh
} else { sys.report_wco_counter = (REPORT_WCO_REFRESH_BUSY_COUNT-REPORT_WCO_REFRESH_IDLE_COUNT+1); }
if (sys.report_ovr_counter >= REPORT_OVR_REFRESH_BUSY_COUNT) {
sys.report_ovr_counter = (REPORT_OVR_REFRESH_BUSY_COUNT-1); // Set override on next report.
}
printPgmString(PSTR("|WCO:"));
float axis_offset;
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
axis_offset = gc_state.coord_system[idx]+gc_state.coord_offset[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { axis_offset += gc_state.tool_length_offset; }
printFloat_CoordValue(axis_offset);
if (idx < (N_AXIS-1)) { serial_write(','); }
}
}
#endif
#ifdef REPORT_FIELD_OVERRIDES
if (sys.report_ovr_counter++ >= REPORT_OVR_REFRESH_BUSY_COUNT) {
if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
sys.report_ovr_counter = 1; // Reset counter for slow refresh
} else { sys.report_ovr_counter = (REPORT_OVR_REFRESH_BUSY_COUNT-REPORT_OVR_REFRESH_IDLE_COUNT+1); }
printPgmString(PSTR("|Ov:"));
print_uint8_base10(sys.f_override);
serial_write(',');
print_uint8_base10(sys.r_override);
serial_write(',');
print_uint8_base10(sys.spindle_speed_ovr);
if (sys.toggle_ovr_mask) {
printPgmString(PSTR("|T:"));
if (sys.toggle_ovr_mask & TOGGLE_OVR_STOP_ACTIVE_MASK) { serial_write('S'); }
if (sys.toggle_ovr_mask & TOGGLE_OVR_FLOOD_COOLANT) { serial_write('F'); }
#ifdef ENABLE_M7
if (sys.toggle_ovr_mask & TOGGLE_OVR_MIST_COOLANT) { serial_write('M'); }
#endif
bit_false(sys.toggle_ovr_mask, (TOGGLE_OVR_FLOOD_COOLANT|TOGGLE_OVR_FLOOD_COOLANT));
}
}
#endif
printPgmString(PSTR(">\r\n"));
#endif
printPgmString(PSTR(">\r\n"));
}
#ifdef DEBUG
void report_realtime_debug()
{
}
#endif

Wyświetl plik

@ -2,7 +2,7 @@
report.h - reporting and messaging methods
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,7 +20,7 @@
#ifndef report_h
#define report_h
// Define Grbl status codes.
// Define Grbl status codes. Valid values (0-255)
#define STATUS_OK 0
#define STATUS_EXPECTED_COMMAND_LETTER 1
#define STATUS_BAD_NUMBER_FORMAT 2
@ -30,11 +30,14 @@
#define STATUS_SETTING_STEP_PULSE_MIN 6
#define STATUS_SETTING_READ_FAIL 7
#define STATUS_IDLE_ERROR 8
#define STATUS_ALARM_LOCK 9
#define STATUS_SYSTEM_GC_LOCK 9
#define STATUS_SOFT_LIMIT_ERROR 10
#define STATUS_OVERFLOW 11
#define STATUS_MAX_STEP_RATE_EXCEEDED 12
#define STATUS_CHECK_DOOR 13
#define STATUS_LINE_LENGTH_EXCEEDED 14
#define STATUS_TRAVEL_EXCEEDED 15
#define STATUS_INVALID_JOG_COMMAND 16
#define STATUS_GCODE_UNSUPPORTED_COMMAND 20
#define STATUS_GCODE_MODAL_GROUP_VIOLATION 21
@ -55,22 +58,28 @@
#define STATUS_GCODE_UNUSED_WORDS 36
#define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 37
// Define Grbl alarm codes.
#define ALARM_HARD_LIMIT_ERROR 1
#define ALARM_SOFT_LIMIT_ERROR 2
#define ALARM_ABORT_CYCLE 3
#define ALARM_PROBE_FAIL 4
#define ALARM_HOMING_FAIL 5
// Define Grbl alarm codes. Valid values (1-255). 0 is reserved.
#define ALARM_HARD_LIMIT_ERROR EXEC_ALARM_HARD_LIMIT
#define ALARM_SOFT_LIMIT_ERROR EXEC_ALARM_SOFT_LIMIT
#define ALARM_ABORT_CYCLE EXEC_ALARM_ABORT_CYCLE
#define ALARM_PROBE_FAIL_INITIAL EXEC_ALARM_PROBE_FAIL_INITIAL
#define ALARM_PROBE_FAIL_CONTACT EXEC_ALARM_PROBE_FAIL_CONTACT
#define ALARM_HOMING_FAIL_RESET EXEC_ALARM_HOMING_FAIL_RESET
#define ALARM_HOMING_FAIL_DOOR EXEC_ALARM_HOMING_FAIL_DOOR
#define ALARM_HOMING_FAIL_PULLOFF EXEC_ALARM_HOMING_FAIL_PULLOFF
#define ALARM_HOMING_FAIL_APPROACH EXEC_ALARM_HOMING_FAIL_APPROACH
// Define Grbl feedback message codes.
// Define Grbl feedback message codes. Valid values (0-255).
#define MESSAGE_CRITICAL_EVENT 1
#define MESSAGE_ALARM_LOCK 2
#define MESSAGE_ALARM_UNLOCK 3
#define MESSAGE_ENABLED 4
#define MESSAGE_DISABLED 5
#define MESSAGE_SAFETY_DOOR_AJAR 6
#define MESSAGE_PROGRAM_END 7
#define MESSAGE_RESTORE_DEFAULTS 8
#define MESSAGE_CHECK_LIMITS 7
#define MESSAGE_PROGRAM_END 8
#define MESSAGE_RESTORE_DEFAULTS 9
#define MESSAGE_SPINDLE_RESTORE 10
// Prints system status messages.
void report_status_message(uint8_t status_code);
@ -111,4 +120,8 @@ void report_startup_line(uint8_t n, char *line);
// Prints build info and user info
void report_build_info(char *line);
#ifdef DEBUG
void report_realtime_debug();
#endif
#endif

Wyświetl plik

@ -2,7 +2,7 @@
serial.c - Low level functions for sending and recieving bytes via the serial port
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -21,12 +21,14 @@
#include "grbl.h"
#define RX_RING_BUFFER (RX_BUFFER_SIZE+1)
#define TX_RING_BUFFER (TX_BUFFER_SIZE+1)
uint8_t serial_rx_buffer[RX_BUFFER_SIZE];
uint8_t serial_rx_buffer[RX_RING_BUFFER];
uint8_t serial_rx_buffer_head = 0;
volatile uint8_t serial_rx_buffer_tail = 0;
uint8_t serial_tx_buffer[TX_BUFFER_SIZE];
uint8_t serial_tx_buffer[TX_RING_BUFFER];
uint8_t serial_tx_buffer_head = 0;
volatile uint8_t serial_tx_buffer_tail = 0;
@ -34,14 +36,14 @@ volatile uint8_t serial_tx_buffer_tail = 0;
#ifdef ENABLE_XONXOFF
volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
#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));
uint8_t diff = serial_rx_buffer_head-serial_rx_buffer_tail;
if (diff >= 0) { return(diff); }
return (RX_RING_BUFFER + diff);
}
@ -51,7 +53,7 @@ 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));
return (TX_RING_BUFFER - (ttail-serial_tx_buffer_head));
}
@ -67,14 +69,10 @@ void serial_init()
#endif
UBRR0H = UBRR0_value >> 8;
UBRR0L = UBRR0_value;
// enable rx and tx
UCSR0B |= 1<<RXEN0;
UCSR0B |= 1<<TXEN0;
// enable interrupt on complete reception of a byte
UCSR0B |= 1<<RXCIE0;
// enable rx, tx, and interrupt on complete reception of a byte
UCSR0B |= (1<<RXEN0 | 1<<TXEN0 | 1<<RXCIE0);
// defaults to 8-bit, no parity, 1 stop bit
}
@ -84,20 +82,20 @@ void serial_init()
void serial_write(uint8_t data) {
// Calculate next head
uint8_t next_head = serial_tx_buffer_head + 1;
if (next_head == TX_BUFFER_SIZE) { next_head = 0; }
if (next_head == TX_RING_BUFFER) { next_head = 0; }
// Wait until there is space in the buffer
while (next_head == serial_tx_buffer_tail) {
// TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
while (next_head == serial_tx_buffer_tail) {
// TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
if (sys_rt_exec_state & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop.
}
// Store data and advance 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);
UCSR0B |= (1 << UDRIE0);
}
@ -105,27 +103,27 @@ void serial_write(uint8_t data) {
ISR(SERIAL_UDRE)
{
uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
#ifdef ENABLE_XONXOFF
if (flow_ctrl == SEND_XOFF) {
UDR0 = XOFF_CHAR;
flow_ctrl = XOFF_SENT;
} else if (flow_ctrl == SEND_XON) {
UDR0 = XON_CHAR;
flow_ctrl = XON_SENT;
if (flow_ctrl == SEND_XOFF) {
UDR0 = XOFF_CHAR;
flow_ctrl = XOFF_SENT;
} else if (flow_ctrl == SEND_XON) {
UDR0 = XON_CHAR;
flow_ctrl = XON_SENT;
} else
#endif
{
// Send a byte from the buffer
{
// Send a byte from the buffer
UDR0 = serial_tx_buffer[tail];
// Update tail position
tail++;
if (tail == TX_BUFFER_SIZE) { tail = 0; }
if (tail == TX_RING_BUFFER) { tail = 0; }
serial_tx_buffer_tail = tail;
}
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
if (tail == serial_tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
}
@ -139,18 +137,18 @@ uint8_t serial_read()
return SERIAL_NO_DATA;
} else {
uint8_t data = serial_rx_buffer[tail];
tail++;
if (tail == RX_BUFFER_SIZE) { tail = 0; }
if (tail == RX_RING_BUFFER) { tail = 0; }
serial_rx_buffer_tail = tail;
#ifdef ENABLE_XONXOFF
if ((serial_get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
if ((serial_get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
flow_ctrl = SEND_XON;
UCSR0B |= (1 << UDRIE0); // Force TX
}
#endif
return data;
}
}
@ -160,38 +158,64 @@ ISR(SERIAL_RX)
{
uint8_t data = UDR0;
uint8_t next_head;
// Pick off realtime command characters directly from the serial stream. These characters are
// not passed into the buffer, but these set system state flag bits for realtime execution.
// not passed into the main buffer, but these set system state flag bits for realtime execution.
switch (data) {
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
case CMD_STATUS_REPORT: system_set_exec_state_flag(EXEC_STATUS_REPORT); break; // Set as true
case CMD_CYCLE_START: system_set_exec_state_flag(EXEC_CYCLE_START); break; // Set as true
case CMD_FEED_HOLD: system_set_exec_state_flag(EXEC_FEED_HOLD); break; // Set as true
case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
default: // Write character to buffer
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 != serial_rx_buffer_tail) {
serial_rx_buffer[serial_rx_buffer_head] = data;
serial_rx_buffer_head = next_head;
#ifdef ENABLE_XONXOFF
if ((serial_get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
flow_ctrl = SEND_XOFF;
UCSR0B |= (1 << UDRIE0); // Force TX
}
#endif
default :
if (data > 0x7F) { // Real-time control characters are extended ACSII only.
switch(data) {
case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true
#ifdef DEBUG
case CMD_DEBUG_REPORT: {uint8_t sreg = SREG; cli(); bit_true(sys_rt_exec_debug,EXEC_DEBUG_REPORT); SREG = sreg;} break;
#endif
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
case CMD_FEED_OVR_COARSE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_MINUS); break;
case CMD_FEED_OVR_FINE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_PLUS); break;
case CMD_FEED_OVR_FINE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_MINUS); break;
case CMD_RAPID_OVR_RESET: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_RESET); break;
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
case CMD_SPINDLE_OVR_COARSE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS); break;
case CMD_SPINDLE_OVR_COARSE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS); break;
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
case CMD_SPINDLE_OVR_FINE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS); break;
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
case CMD_COOLANT_FLOOD_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE); break;
#ifdef ENABLE_M7
case CMD_COOLANT_MIST_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE); break;
#endif
}
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
} else { // Write character to buffer
next_head = serial_rx_buffer_head + 1;
if (next_head == RX_RING_BUFFER) { next_head = 0; }
// Write data to buffer unless it is full.
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 ((serial_get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
flow_ctrl = SEND_XOFF;
UCSR0B |= (1 << UDRIE0); // Force TX
}
#endif
}
}
//TODO: else alarm on overflow?
}
}
void serial_reset_read_buffer()
void serial_reset_read_buffer()
{
serial_rx_buffer_tail = serial_rx_buffer_head;

Wyświetl plik

@ -2,7 +2,7 @@
serial.c - Low level functions for sending and recieving bytes via the serial port
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -27,7 +27,11 @@
#define RX_BUFFER_SIZE 128
#endif
#ifndef TX_BUFFER_SIZE
#define TX_BUFFER_SIZE 64
#ifdef USE_LINE_NUMBERS
#define TX_BUFFER_SIZE 100
#else
#define TX_BUFFER_SIZE 90
#endif
#endif
#define SERIAL_NO_DATA 0xff

Wyświetl plik

@ -1,8 +1,8 @@
/*
settings.c - eeprom configuration handling
settings.c - eeprom configuration handling
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -27,95 +27,113 @@ settings_t settings;
// Method to store startup lines into EEPROM
void settings_store_startup_line(uint8_t n, char *line)
{
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
// TODO: Alter the startup line parsing to prevent motions from being executed before this call.
// Implement it like the jog parsing.
protocol_buffer_synchronize(); // A startup line may contain a motion and be executing.
#endif
uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK;
memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE);
}
// Method to store build info into EEPROM
// NOTE: This function can only be called in IDLE state.
void settings_store_build_info(char *line)
{
// Build info can only be stored when state is IDLE.
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(char*)line, LINE_BUFFER_SIZE);
}
// Method to store coord data parameters into EEPROM
void settings_write_coord_data(uint8_t coord_select, float *coord_data)
{
{
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
protocol_buffer_synchronize();
#endif
uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS;
memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS);
}
}
// Method to store Grbl global settings struct and version number into EEPROM
void write_global_settings()
// NOTE: This function can only be called in IDLE state.
void write_global_settings()
{
eeprom_put_char(0, SETTINGS_VERSION);
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t));
}
// Method to restore EEPROM-saved Grbl global settings back to defaults.
void settings_restore(uint8_t restore_flag) {
// Method to restore EEPROM-saved Grbl global settings back to defaults.
void settings_restore(uint8_t restore_flag) {
if (restore_flag & SETTINGS_RESTORE_DEFAULTS) {
settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;
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.rpm_max = DEFAULT_SPINDLE_RPM_MAX;
settings.rpm_min = DEFAULT_SPINDLE_RPM_MIN;
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.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;
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.flags = 0;
if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; }
if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
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.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);
settings.rpm_max = DEFAULT_SPINDLE_RPM_MAX;
settings.rpm_min = DEFAULT_SPINDLE_RPM_MIN;
write_global_settings();
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_LASER_MODE) { settings.flags |= BITFLAG_LASER_MODE; }
if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; }
if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; }
if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; }
if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
if (DEFAULT_INVERT_PROBE_PIN) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; }
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();
}
if (restore_flag & SETTINGS_RESTORE_PARAMETERS) {
uint8_t idx;
float coord_data[N_AXIS];
memset(&coord_data, 0, sizeof(coord_data));
for (idx=0; idx <= SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); }
uint8_t idx;
float coord_data[N_AXIS];
memset(&coord_data, 0, sizeof(coord_data));
for (idx=0; idx <= SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); }
}
if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) {
#if N_STARTUP_LINE > 0
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK, 0);
#endif
#if N_STARTUP_LINE > 1
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+1), 0);
#endif
#if N_STARTUP_LINE > 0
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK, 0);
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+1, 0); // Checksum
#endif
#if N_STARTUP_LINE > 1
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+1), 0);
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+2), 0); // Checksum
#endif
}
if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) {
eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0);
eeprom_put_char(EEPROM_ADDR_BUILD_INFO+1 , 0); // Checksum
}
if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) { eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0); }
}
@ -152,12 +170,12 @@ uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS;
if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) {
// Reset with default zero vector
clear_vector_float(coord_data);
clear_vector_float(coord_data);
settings_write_coord_data(coord_select,coord_data);
return(false);
}
return(true);
}
}
// Reads Grbl global settings struct from EEPROM.
@ -170,7 +188,7 @@ uint8_t read_global_settings() {
return(false);
}
} else {
return(false);
return(false);
}
return(true);
}
@ -178,7 +196,7 @@ uint8_t read_global_settings() {
// A helper method to set settings from command line
uint8_t settings_store_global_setting(uint8_t parameter, float value) {
if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); }
if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); }
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.
@ -215,16 +233,16 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
// Store non-axis Grbl settings
uint8_t int_value = trunc(value);
switch(parameter) {
case 0:
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;
case 2:
settings.step_invert_mask = int_value;
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
break;
case 3:
settings.dir_invert_mask = int_value;
case 3:
settings.dir_invert_mask = int_value;
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
break;
case 4: // Reset to ensure change. Immediate re-init may cause problems.
@ -238,6 +256,7 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
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; }
probe_configure_invert_mask(false);
break;
case 10: settings.status_report_mask = int_value; break;
case 11: settings.junction_deviation = value; break;
@ -247,9 +266,9 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
break;
case 20:
if (int_value) {
if (int_value) {
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); }
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
} else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; }
break;
case 21:
@ -259,8 +278,8 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
break;
case 22:
if (int_value) { settings.flags |= BITFLAG_HOMING_ENABLE; }
else {
settings.flags &= ~BITFLAG_HOMING_ENABLE;
else {
settings.flags &= ~BITFLAG_HOMING_ENABLE;
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits.
}
break;
@ -271,7 +290,15 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
case 27: settings.homing_pulloff = value; break;
case 30: settings.rpm_max = value; break;
case 31: settings.rpm_min = value; break;
default:
case 32:
#ifdef VARIABLE_SPINDLE
if (int_value) { settings.flags |= BITFLAG_LASER_MODE; }
else { settings.flags &= ~BITFLAG_LASER_MODE; }
#else
return(STATUS_SETTING_DISABLED);
#endif
break;
default:
return(STATUS_INVALID_STATEMENT);
}
}
@ -287,18 +314,6 @@ void settings_init() {
settings_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data.
report_grbl_settings();
}
// NOTE: Checking paramater data, startup lines, and build info string should be done here,
// but it seems fairly redundant. Each of these can be manually checked and reset or restored.
// Check all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing.
// float coord_data[N_AXIS];
// uint8_t i;
// for (i=0; i<=SETTING_INDEX_NCOORD; i++) {
// if (!settings_read_coord_data(i, coord_data)) {
// report_status_message(STATUS_SETTING_READ_FAIL);
// }
// }
// NOTE: Startup lines are checked and executed by protocol_main_loop at the end of initialization.
}

Wyświetl plik

@ -1,10 +1,10 @@
/*
settings.h - eeprom configuration handling
settings.h - eeprom configuration handling
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -31,7 +31,7 @@
// Define bit flag masks for the boolean settings in settings.flag.
#define BITFLAG_REPORT_INCHES bit(0)
// #define BITFLAG_AUTO_START bit(1) // Obsolete. Don't alter to keep back compatibility.
#define BITFLAG_LASER_MODE bit(1)
#define BITFLAG_INVERT_ST_ENABLE bit(2)
#define BITFLAG_HARD_LIMIT_ENABLE bit(3)
#define BITFLAG_HOMING_ENABLE bit(4)
@ -40,24 +40,31 @@
#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 BITFLAG_RT_STATUS_LIMIT_PINS bit(4)
#define BITFLAG_RT_STATUS_PROBE_PIN bit(5)
#define BITFLAG_RT_STATUS_CONTROL_PINS bit(6)
#ifdef USE_CLASSIC_REALTIME_REPORT
#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 BITFLAG_RT_STATUS_LIMIT_PINS bit(4)
#define BITFLAG_RT_STATUS_PROBE_PIN bit(5)
#define BITFLAG_RT_STATUS_CONTROL_PINS bit(6)
#define BITFLAG_RT_STATUS_OVERRIDES bit(7)
#else
#define BITFLAG_RT_STATUS_POSITION_TYPE bit(0)
#endif
// Define settings restore bitflags.
#define SETTINGS_RESTORE_ALL 0xFF // All bitflags
#define SETTINGS_RESTORE_DEFAULTS bit(0)
#define SETTINGS_RESTORE_PARAMETERS bit(1)
#define SETTINGS_RESTORE_STARTUP_LINES bit(2)
#define SETTINGS_RESTORE_BUILD_INFO bit(3)
#ifndef SETTINGS_RESTORE_ALL
#define SETTINGS_RESTORE_ALL 0xFF // All bitflags
#endif
// Define EEPROM memory address location values for Grbl settings and parameters
// NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and
// the startup script. The lower half contains the global settings and space for future
// the startup script. The lower half contains the global settings and space for future
// developments.
#define EEPROM_ADDR_GLOBAL 1U
#define EEPROM_ADDR_PARAMETERS 512U
@ -93,10 +100,10 @@ typedef struct {
uint8_t status_report_mask; // Mask to indicate desired report data.
float junction_deviation;
float arc_tolerance;
float rpm_max;
float rpm_min;
uint8_t flags; // Contains default boolean settings
uint8_t homing_dir_mask;

Wyświetl plik

@ -2,7 +2,7 @@
spindle_control.c - spindle control methods
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -22,8 +22,20 @@
#include "grbl.h"
#ifdef SPINDLE_MINIMUM_PWM
#define SPINDLE_PWM_MIN_VALUE SPINDLE_MINIMUM_PWM
#else
#define SPINDLE_PWM_MIN_VALUE 0
#endif
#define SPINDLE_PWM_RANGE (SPINDLE_PWM_MAX_VALUE-SPINDLE_PWM_MIN_VALUE)
#ifdef VARIABLE_SPINDLE
static float pwm_gradient; // Precalulated value to speed up rpm to PWM conversions.
#endif
void spindle_init()
{
{
#ifdef VARIABLE_SPINDLE
// Configure variable spindle PWM and enable pin, if requried. On the Uno, PWM and enable are
@ -33,28 +45,31 @@ void spindle_init()
SPINDLE_TCCRB_REGISTER = SPINDLE_TCCRB_INIT_MASK;
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
#else
#else
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin.
#endif
#else
pwm_gradient = SPINDLE_PWM_RANGE/(settings.rpm_max-settings.rpm_min);
#else
// Configure no variable spindle and only enable pin.
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin.
#endif
spindle_stop();
}
void spindle_stop()
// Stop and start spindle routines. Called by all spindle routines and stepper ISR.
inline void spindle_stop()
{
// On the Uno, spindle enable and PWM are shared. Other CPUs have seperate enable pin.
#ifdef VARIABLE_SPINDLE
SPINDLE_TCCRA_REGISTER &= ~(1<<SPINDLE_COMB_BIT); // Disable PWM. Output voltage is zero.
#if defined(CPU_MAP_ATMEGA2560) || defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); // Set pin to high
#else
@ -67,15 +82,54 @@ void spindle_stop()
#else
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low
#endif
#endif
#endif
}
void spindle_set_state(uint8_t state, float rpm)
#ifdef VARIABLE_SPINDLE
inline void spindle_set_speed(uint8_t pwm_value)
{
if (pwm_value == SPINDLE_PWM_OFF_VALUE) {
spindle_stop();
} else {
SPINDLE_OCR_REGISTER = pwm_value; // Set PWM output level.
SPINDLE_TCCRA_REGISTER |= (1<<SPINDLE_COMB_BIT); // Ensure PWM output is enabled.
#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
#else
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif
#endif
}
}
uint8_t spindle_compute_pwm_value(float rpm) // 328p PWM register is 8-bit.
{
// Calculate PWM register value based on rpm max/min settings and programmed rpm.
if ((settings.rpm_min >= settings.rpm_max) || (rpm > settings.rpm_max)) {
// No PWM range possible. Set simple on/off spindle control pin state.
return(SPINDLE_PWM_MAX_VALUE);
} else if (rpm < settings.rpm_min) {
if (rpm == 0.0) { return(SPINDLE_PWM_OFF_VALUE); }
else { return(SPINDLE_PWM_MIN_VALUE); }
} else {
return(floor( (rpm-settings.rpm_min)*pwm_gradient + (SPINDLE_PWM_MIN_VALUE+0.5)));
}
}
#endif
// Immediately sets spindle running state with direction and spindle rpm via PWM, if enabled.
// Called by spindle_run() after sync and parking motion/spindle stop override during restore.
void spindle_set_state(uint8_t state, uint8_t pwm_value)
{
if (sys.abort) { return; } // Block during abort.
// Halt or set spindle direction and rpm.
// Halt or set spindle direction and rpm.
if (state == SPINDLE_DISABLE) {
spindle_stop();
@ -92,59 +146,32 @@ void spindle_set_state(uint8_t state, float rpm)
#ifdef VARIABLE_SPINDLE
// TODO: Install the optional capability for frequency-based output for servos.
uint8_t current_pwm; // 328p PWM register is 8-bit.
spindle_set_speed(pwm_value);
// Calculate PWM register value based on rpm max/min settings and programmed rpm.
if (rpm <= 0.0) { spindle_stop(); } // RPM should never be negative, but check anyway.
else {
if (settings.rpm_max <= settings.rpm_min) {
// No PWM range possible. Set simple on/off spindle control pin state.
current_pwm = SPINDLE_PWM_MAX_VALUE;
} else {
if (rpm > settings.rpm_max) { rpm = settings.rpm_max; }
if (rpm < settings.rpm_min) { rpm = settings.rpm_min; }
#ifdef SPINDLE_MINIMUM_PWM
float pwm_gradient = (SPINDLE_PWM_MAX_VALUE-SPINDLE_MINIMUM_PWM)/(settings.rpm_max-settings.rpm_min);
current_pwm = floor( (rpm-settings.rpm_min)*pwm_gradient + (SPINDLE_MINIMUM_PWM+0.5));
#else
float pwm_gradient = (SPINDLE_PWM_MAX_VALUE)/(settings.rpm_max-settings.rpm_min);
current_pwm = floor( (rpm-settings.rpm_min)*pwm_gradient + 0.5);
#endif
}
SPINDLE_OCR_REGISTER = current_pwm; // Set PWM output level.
SPINDLE_TCCRA_REGISTER |= (1<<SPINDLE_COMB_BIT); // Ensure PWM output is enabled.
// On the Uno, spindle enable and PWM are shared, unless otherwise specified.
#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
#else
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif
#endif
}
#else
// NOTE: Without variable spindle, the enable bit should just turn on or off, regardless
// if the spindle speed value is zero, as its ignored anyhow.
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
#else
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif
// NOTE: Without variable spindle, the enable bit should just turn on or off, regardless
// if the spindle speed value is zero, as its ignored anyhow.
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
#else
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif
#endif
}
}
// Called by g-code parser when setting spindle state and requires a buffer sync.
void spindle_run(uint8_t state, float rpm)
{
if (sys.state == STATE_CHECK_MODE) { return; }
protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed.
spindle_set_state(state, rpm);
protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed.
#ifdef VARIABLE_SPINDLE
spindle_set_state(state, spindle_compute_pwm_value(rpm));
#else
spindle_set_state(state,0); // Send null pwm value. Not used.
#endif
}

Wyświetl plik

@ -2,7 +2,7 @@
spindle_control.h - spindle control methods
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -17,21 +17,27 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
*/
#ifndef spindle_control_h
#define spindle_control_h
#define spindle_control_h
// Initializes spindle pins and hardware PWM, if enabled.
void spindle_init();
// Sets spindle direction and spindle rpm via PWM, if enabled.
// Called by g-code parser when setting spindle state and requires a buffer sync.
void spindle_run(uint8_t direction, float rpm);
void spindle_set_state(uint8_t state, float rpm);
// Immediately sets spindle running state with direction and spindle rpm via PWM, if enabled.
// Called by spindle_run() after sync and parking motion/spindle stop override during restore.
void spindle_set_state(uint8_t state, uint8_t pwm_value);
// Stop and start spindle routines. Called by all spindle routines and stepper ISR.
inline void spindle_stop();
inline void spindle_set_speed(uint8_t pwm_value); // Variable spindle only.
uint8_t spindle_compute_pwm_value(float rpm); // 328p PWM register is 8-bit. Variable spindle only.
// Kills spindle.
void spindle_stop();
#endif

Wyświetl plik

@ -2,9 +2,9 @@
stepper.c - stepper motor driver: executes motion plans using stepper motors
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@ -23,21 +23,23 @@
// Some useful constants.
#define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) // min/segment
#define REQ_MM_INCREMENT_SCALAR 1.25
#define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) // min/segment
#define REQ_MM_INCREMENT_SCALAR 1.25
#define RAMP_ACCEL 0
#define RAMP_CRUISE 1
#define RAMP_DECEL 2
#define RAMP_DECEL_OVERRIDE 3
#define PREP_FLAG_RECALCULATE bit(0)
#define PREP_FLAG_HOLD_PARTIAL_BLOCK bit(1)
#define PREP_FLAG_PARKING bit(2)
#define PREP_FLAG_DECEL_OVERRIDE bit(3)
// Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level
// frequency bin starts at 0Hz and ends at its cutoff frequency. The next lower level frequency bin
// starts at the next higher cutoff frequency, and so on. The cutoff frequencies for each level must
// be considered carefully against how much it over-drives the stepper ISR, the accuracy of the 16-bit
// timer, and the CPU overhead. Level 0 (no AMASS, normal operation) frequency bin starts at the
// timer, and the CPU overhead. Level 0 (no AMASS, normal operation) frequency bin starts at the
// Level 1 cutoff frequency and up to as fast as the CPU allows (over 30kHz in limited testing).
// NOTE: AMASS cutoff frequency multiplied by ISR overdrive factor must not exceed maximum step frequency.
// NOTE: Current settings are set to overdrive the ISR to no more than 16kHz, balancing CPU overhead
@ -49,22 +51,25 @@
#define AMASS_LEVEL3 (F_CPU/2000) // Over-drives ISR (x8)
// Stores the planner block Bresenham algorithm execution data for the segments in the segment
// Stores the planner block Bresenham algorithm execution data for the segments in the segment
// buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will
// never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1).
// NOTE: This data is copied from the prepped planner blocks so that the planner blocks may be
// discarded when entirely consumed and completed by the segment buffer. Also, AMASS alters this
// data for its own use.
typedef struct {
// data for its own use.
typedef struct {
uint8_t direction_bits;
#ifdef VARIABLE_SPINDLE
uint8_t spindle_pwm;
#endif
uint32_t steps[N_AXIS];
uint32_t step_event_count;
} st_block_t;
static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1];
// Primary stepper segment ring buffer. Contains small, short line segments for the stepper
// Primary stepper segment ring buffer. Contains small, short line segments for the stepper
// algorithm to execute, which are "checked-out" incrementally from the first block in the
// planner buffer. Once "checked-out", the steps in the segments buffer cannot be modified by
// planner buffer. Once "checked-out", the steps in the segments buffer cannot be modified by
// the planner, where the remaining planner block steps still can.
typedef struct {
uint16_t n_step; // Number of step events to be executed for this segment
@ -82,12 +87,12 @@ static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
typedef struct {
// Used by the bresenham line algorithm
uint32_t counter_x, // Counter variables for the bresenham line tracer
counter_y,
counter_y,
counter_z;
#ifdef STEP_PULSE_DELAY
uint8_t step_bits; // Stores out_bits output to complete the step pulse delay
#endif
uint8_t execute_step; // Flags step execution for each interrupt.
uint8_t step_pulse_time; // Step pulse reset time after step rise
uint8_t step_outbits; // The next stepping-bits to be output
@ -96,7 +101,7 @@ typedef struct {
uint32_t steps[N_AXIS];
#endif
uint16_t step_count; // Steps remaining in line segment motion
uint16_t step_count; // Steps remaining in line segment motion
uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block.
st_block_t *exec_block; // Pointer to the block data for the segment being executed
segment_t *exec_segment; // Pointer to the segment being executed
@ -108,24 +113,24 @@ 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.
// 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;
static volatile uint8_t busy;
// Pointers for the step segment being prepped from the planner buffer. Accessed only by the
// main program. Pointers may be planning segments or planner blocks ahead of what being executed.
static plan_block_t *pl_block; // Pointer to the planner block being prepped
static st_block_t *st_prep_block; // Pointer to the stepper block data being prepped
static st_block_t *st_prep_block; // Pointer to the stepper block data being prepped
// Segment preparation data struct. Contains all the necessary information to compute new segments
// based on the current executing planner block.
typedef struct {
uint8_t st_block_index; // Index of stepper common data block being prepped
uint8_t recalculate_flag;
float dt_remainder;
float steps_remaining;
float step_per_mm;
@ -150,7 +155,7 @@ typedef struct {
static st_prep_t prep;
/* BLOCK VELOCITY PROFILE DEFINITION
/* BLOCK VELOCITY PROFILE DEFINITION
__________________________
/| |\ _________________ ^
/ | | \ /| |\ |
@ -161,72 +166,70 @@ static st_prep_t prep;
| BLOCK 1 ^ BLOCK 2 | d
|
time -----> EXAMPLE: Block 2 entry speed is at max junction velocity
The planner block buffer is planned assuming constant acceleration velocity profiles and are
continuously joined at block junctions as shown above. However, the planner only actively computes
the block entry speeds for an optimal velocity plan, but does not compute the block internal
velocity profiles. These velocity profiles are computed ad-hoc as they are executed by the
velocity profiles. These velocity profiles are computed ad-hoc as they are executed by the
stepper algorithm and consists of only 7 possible types of profiles: cruise-only, cruise-
deceleration, acceleration-cruise, acceleration-only, deceleration-only, full-trapezoid, and
deceleration, acceleration-cruise, acceleration-only, deceleration-only, full-trapezoid, and
triangle(no cruise).
maximum_speed (< nominal_speed) -> +
+--------+ <- maximum_speed (= nominal_speed) /|\
/ \ / | \
maximum_speed (< nominal_speed) -> +
+--------+ <- maximum_speed (= nominal_speed) /|\
/ \ / | \
current_speed -> + \ / | + <- exit_speed
| + <- exit_speed / | |
+-------------+ current_speed -> +----+--+
time --> ^ ^ ^ ^
| | | |
| + <- exit_speed / | |
+-------------+ current_speed -> +----+--+
time --> ^ ^ ^ ^
| | | |
decelerate_after(in mm) decelerate_after(in mm)
^ ^ ^ ^
| | | |
accelerate_until(in mm) accelerate_until(in mm)
The step segment buffer computes the executing block velocity profile and tracks the critical
parameters for the stepper algorithm to accurately trace the profile. These critical parameters
parameters for the stepper algorithm to accurately trace the profile. These critical parameters
are shown and defined in the above illustration.
*/
// Stepper state initialization. Cycle should only start if the st.cycle_start flag is
// enabled. Startup init and limits call this function but shouldn't start the cycle.
void st_wake_up()
void st_wake_up()
{
// Enable stepper drivers.
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT); }
else { STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT); }
// if (sys.state & (STATE_CYCLE | STATE_HOMING)){
// Initialize stepper output bits
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
// Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope.
st.step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
// Set delay between direction pin write and step command.
OCR0A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
#else // Normal operation
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
#endif
// Initialize stepper output bits
st.dir_outbits = dir_port_invert_mask;
st.step_outbits = step_port_invert_mask;
// Enable Stepper Driver Interrupt
TIMSK1 |= (1<<OCIE1A);
// }
// Initialize step pulse timing from settings. Here to ensure updating after re-writing.
#ifdef STEP_PULSE_DELAY
// Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope.
st.step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
// Set delay between direction pin write and step command.
OCR0A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
#else // Normal operation
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
#endif
// Enable Stepper Driver Interrupt
TIMSK1 |= (1<<OCIE1A);
}
// Stepper shutdown
void st_go_idle()
void st_go_idle()
{
// Disable Stepper Driver Interrupt. Allow Stepper Port Reset Interrupt to finish, if active.
TIMSK1 &= ~(1<<OCIE1A); // Disable Timer1 interrupt
TCCR1B = (TCCR1B & ~((1<<CS12) | (1<<CS11))) | (1<<CS10); // Reset clock to no prescaling.
busy = false;
// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
bool pin_state = false; // Keep enabled.
if (((settings.stepper_idle_lock_time != 0xff) || sys_rt_exec_alarm) && sys.state != STATE_HOMING) {
@ -246,54 +249,54 @@ void st_go_idle()
Unlike the popular DDA algorithm, the Bresenham algorithm is not susceptible to numerical
round-off errors and only requires fast integer counters, meaning low computational overhead
and maximizing the Arduino's capabilities. However, the downside of the Bresenham algorithm
is, for certain multi-axis motions, the non-dominant axes may suffer from un-smooth step
pulse trains, or aliasing, which can lead to strange audible noises or shaking. This is
particularly noticeable or may cause motion issues at low step frequencies (0-5kHz), but
is, for certain multi-axis motions, the non-dominant axes may suffer from un-smooth step
pulse trains, or aliasing, which can lead to strange audible noises or shaking. This is
particularly noticeable or may cause motion issues at low step frequencies (0-5kHz), but
is usually not a physical problem at higher frequencies, although audible.
To improve Bresenham multi-axis performance, Grbl uses what we call an Adaptive Multi-Axis
Step Smoothing (AMASS) algorithm, which does what the name implies. At lower step frequencies,
AMASS artificially increases the Bresenham resolution without effecting the algorithm's
AMASS artificially increases the Bresenham resolution without effecting the algorithm's
innate exactness. AMASS adapts its resolution levels automatically depending on the step
frequency to be executed, meaning that for even lower step frequencies the step smoothing
frequency to be executed, meaning that for even lower step frequencies the step smoothing
level increases. Algorithmically, AMASS is acheived by a simple bit-shifting of the Bresenham
step count for each AMASS level. For example, for a Level 1 step smoothing, we bit shift
the Bresenham step event count, effectively multiplying it by 2, while the axis step counts
step count for each AMASS level. For example, for a Level 1 step smoothing, we bit shift
the Bresenham step event count, effectively multiplying it by 2, while the axis step counts
remain the same, and then double the stepper ISR frequency. In effect, we are allowing the
non-dominant Bresenham axes step in the intermediate ISR tick, while the dominant axis is
non-dominant Bresenham axes step in the intermediate ISR tick, while the dominant axis is
stepping every two ISR ticks, rather than every ISR tick in the traditional sense. At AMASS
Level 2, we simply bit-shift again, so the non-dominant Bresenham axes can step within any
of the four ISR ticks, the dominant axis steps every four ISR ticks, and quadruple the
stepper ISR frequency. And so on. This, in effect, virtually eliminates multi-axis aliasing
issues with the Bresenham algorithm and does not significantly alter Grbl's performance, but
Level 2, we simply bit-shift again, so the non-dominant Bresenham axes can step within any
of the four ISR ticks, the dominant axis steps every four ISR ticks, and quadruple the
stepper ISR frequency. And so on. This, in effect, virtually eliminates multi-axis aliasing
issues with the Bresenham algorithm and does not significantly alter Grbl's performance, but
in fact, more efficiently utilizes unused CPU cycles overall throughout all configurations.
AMASS retains the Bresenham algorithm exactness by requiring that it always executes a full
Bresenham step, regardless of AMASS Level. Meaning that for an AMASS Level 2, all four
intermediate steps must be completed such that baseline Bresenham (Level 0) count is always
retained. Similarly, AMASS Level 3 means all eight intermediate steps must be executed.
Bresenham step, regardless of AMASS Level. Meaning that for an AMASS Level 2, all four
intermediate steps must be completed such that baseline Bresenham (Level 0) count is always
retained. Similarly, AMASS Level 3 means all eight intermediate steps must be executed.
Although the AMASS Levels are in reality arbitrary, where the baseline Bresenham counts can
be multiplied by any integer value, multiplication by powers of two are simply used to ease
CPU overhead with bitshift integer operations.
be multiplied by any integer value, multiplication by powers of two are simply used to ease
CPU overhead with bitshift integer operations.
This interrupt is simple and dumb by design. All the computational heavy-lifting, as in
determining accelerations, is performed elsewhere. This interrupt pops pre-computed segments,
defined as constant velocity over n number of steps, from the step segment buffer and then
executes them by pulsing the stepper pins appropriately via the Bresenham algorithm. This
defined as constant velocity over n number of steps, from the step segment buffer and then
executes them by pulsing the stepper pins appropriately via the Bresenham algorithm. This
ISR is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port
after each pulse. The bresenham line tracer algorithm controls all stepper outputs
simultaneously with these two interrupts.
NOTE: This interrupt must be as efficient as possible and complete before the next ISR tick,
which for Grbl must be less than 33.3usec (@30kHz ISR rate). Oscilloscope measured time in
NOTE: This interrupt must be as efficient as possible and complete before the next ISR tick,
which for Grbl must be less than 33.3usec (@30kHz ISR rate). Oscilloscope measured time in
ISR is 5usec typical and 25usec maximum, well below requirement.
NOTE: This ISR expects at least one step to be executed per segment.
*/
// TODO: Replace direct updating of the int32 position counters in the ISR somehow. Perhaps use smaller
// int8 variables and update position counters only when a segment completes. This can get complicated
// int8 variables and update position counters only when a segment completes. This can get complicated
// with probing and homing cycles that require true real-time positions.
ISR(TIMER1_COMPA_vect)
{
{
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
// Set the direction pins a couple of nanoseconds before we step the steppers
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK);
@ -302,7 +305,7 @@ ISR(TIMER1_COMPA_vect)
st.step_bits = (STEP_PORT & ~STEP_MASK) | st.step_outbits; // Store out_bits to prevent overwriting.
#else // Normal operation
STEP_PORT = (STEP_PORT & ~STEP_MASK) | st.step_outbits;
#endif
#endif
// Enable step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after
// exactly settings.pulse_microseconds microseconds, independent of the main Timer1 prescaler.
@ -310,9 +313,9 @@ ISR(TIMER1_COMPA_vect)
TCCR0B = (1<<CS01); // Begin Timer0. Full speed, 1/8 prescaler
busy = true;
sei(); // Re-enable interrupts to allow Stepper Port Reset Interrupt to fire on-time.
sei(); // Re-enable interrupts to allow Stepper Port Reset Interrupt to fire on-time.
// NOTE: The remaining code in this ISR will finish before returning to main program.
// If there is no step segment, attempt to pop one from the stepper buffer
if (st.exec_segment == NULL) {
// Anything in the buffer? If so, load and initialize next step segment.
@ -333,11 +336,11 @@ ISR(TIMER1_COMPA_vect)
if ( st.exec_block_index != st.exec_segment->st_block_index ) {
st.exec_block_index = st.exec_segment->st_block_index;
st.exec_block = &st_block_buffer[st.exec_block_index];
// Initialize Bresenham line and distance counters
st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
}
st.dir_outbits = st.exec_block->direction_bits ^ dir_port_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.
@ -345,68 +348,73 @@ ISR(TIMER1_COMPA_vect)
st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level;
st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level;
#endif
#ifdef VARIABLE_SPINDLE
// Set real-time spindle output as segment is loaded, just prior to the first step.
spindle_set_speed(st.exec_block->spindle_pwm);
#endif
} else {
// Segment buffer empty. Shutdown.
st_go_idle();
system_set_exec_state_flag(EXEC_CYCLE_STOP); // Flag main program for cycle end
return; // Nothing to do but exit.
}
}
}
// Check probing state.
probe_state_monitor();
if (sys_probe_state == PROBE_ACTIVE) { probe_state_monitor(); }
// Reset step out bits.
st.step_outbits = 0;
st.step_outbits = 0;
// Execute step displacement profile by Bresenham line algorithm
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_x += st.steps[X_AXIS];
#else
st.counter_x += st.exec_block->steps[X_AXIS];
#endif
#endif
if (st.counter_x > st.exec_block->step_event_count) {
st.step_outbits |= (1<<X_STEP_BIT);
st.counter_x -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
else { sys.position[X_AXIS]++; }
if (st.exec_block->direction_bits & (1<<X_DIRECTION_BIT)) { sys_position[X_AXIS]--; }
else { sys_position[X_AXIS]++; }
}
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_y += st.steps[Y_AXIS];
#else
st.counter_y += st.exec_block->steps[Y_AXIS];
#endif
#endif
if (st.counter_y > st.exec_block->step_event_count) {
st.step_outbits |= (1<<Y_STEP_BIT);
st.counter_y -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
else { sys.position[Y_AXIS]++; }
if (st.exec_block->direction_bits & (1<<Y_DIRECTION_BIT)) { sys_position[Y_AXIS]--; }
else { sys_position[Y_AXIS]++; }
}
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_z += st.steps[Z_AXIS];
#else
st.counter_z += st.exec_block->steps[Z_AXIS];
#endif
#endif
if (st.counter_z > st.exec_block->step_event_count) {
st.step_outbits |= (1<<Z_STEP_BIT);
st.counter_z -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
else { sys.position[Z_AXIS]++; }
}
if (st.exec_block->direction_bits & (1<<Z_DIRECTION_BIT)) { sys_position[Z_AXIS]--; }
else { sys_position[Z_AXIS]++; }
}
// During a homing cycle, lock out and prevent desired axes from moving.
if (sys.state == STATE_HOMING) { st.step_outbits &= sys.homing_axis_lock; }
if (sys.state == STATE_HOMING) { st.step_outbits &= sys.homing_axis_lock; }
st.step_count--; // Decrement step events count
st.step_count--; // Decrement step events count
if (st.step_count == 0) {
// Segment is complete. Discard current segment and advance segment indexing.
st.exec_segment = NULL;
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
}
st.step_outbits ^= step_port_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
}
@ -417,17 +425,17 @@ ISR(TIMER1_COMPA_vect)
finish, if Timer1 is disabled after completing a move.
NOTE: Interrupt collisions between the serial and stepper interrupts can cause delays by
a few microseconds, if they execute right before one another. Not a big deal, but can
cause issues at high step rates if another high frequency asynchronous interrupt is
cause issues at high step rates if another high frequency asynchronous interrupt is
added to Grbl.
*/
// This interrupt is enabled by ISR_TIMER1_COMPAREA when it sets the motor port bits to execute
// a step. This ISR resets the motor port after a short period (settings.pulse_microseconds)
// a step. This ISR resets the motor port after a short period (settings.pulse_microseconds)
// completing one step cycle.
ISR(TIMER0_OVF_vect)
{
// Reset stepping pins (leave the direction pins)
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.
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
// This interrupt is used only when STEP_PULSE_DELAY is enabled. Here, the step pulse is
@ -435,8 +443,8 @@ ISR(TIMER0_OVF_vect)
// will then trigger after the appropriate settings.pulse_microseconds, as in normal operation.
// The new timing between direction, step pulse, and step complete events are setup in the
// st_wake_up() routine.
ISR(TIMER0_COMPA_vect)
{
ISR(TIMER0_COMPA_vect)
{
STEP_PORT = st.step_bits; // Begin step pulse.
}
#endif
@ -444,7 +452,7 @@ ISR(TIMER0_OVF_vect)
// Generates the step and direction port invert masks used in the Stepper Interrupt Driver.
void st_generate_step_dir_invert_masks()
{
{
uint8_t idx;
step_port_invert_mask = 0;
dir_port_invert_mask = 0;
@ -460,7 +468,7 @@ void st_reset()
{
// Initialize stepper driver idle state.
st_go_idle();
// Initialize stepper algorithm variables.
memset(&prep, 0, sizeof(st_prep_t));
memset(&st, 0, sizeof(stepper_t));
@ -470,9 +478,9 @@ void st_reset()
segment_buffer_head = 0; // empty = tail
segment_next_head = 1;
busy = false;
st_generate_step_dir_invert_masks();
// 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;
@ -490,11 +498,11 @@ void stepper_init()
// Configure Timer 1: Stepper Driver Interrupt
TCCR1B &= ~(1<<WGM13); // waveform generation = 0100 = CTC
TCCR1B |= (1<<WGM12);
TCCR1A &= ~((1<<WGM11) | (1<<WGM10));
TCCR1A &= ~((1<<WGM11) | (1<<WGM10));
TCCR1A &= ~((1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0)); // Disconnect OC1 output
// TCCR1B = (TCCR1B & ~((1<<CS12) | (1<<CS11))) | (1<<CS10); // Set in st_go_idle().
// TIMSK1 &= ~(1<<OCIE1A); // Set in st_go_idle().
// Configure Timer 0: Stepper Port Reset Interrupt
TIMSK0 &= ~((1<<OCIE0B) | (1<<OCIE0A) | (1<<TOIE0)); // Disconnect OC0 outputs and OVF interrupt.
TCCR0A = 0; // Normal operation
@ -504,11 +512,11 @@ void stepper_init()
TIMSK0 |= (1<<OCIE0A); // Enable Timer0 Compare Match A interrupt
#endif
}
// Called by planner_recalculate() when the executing block is updated by the new plan.
void st_update_plan_block_parameters()
{
{
if (pl_block != NULL) { // Ignore if at start of a new block.
prep.recalculate_flag |= PREP_FLAG_RECALCULATE;
pl_block->entry_speed_sqr = prep.current_speed*prep.current_speed; // Update entry speed.
@ -545,10 +553,9 @@ static uint8_t st_next_block_index(uint8_t block_index)
// Restores the step segment buffer to the normal run state after a parking motion.
// NOTE: This function does not compile if parking is disabled.
void st_parking_restore_buffer()
{
// Restore step execution data and flags of partially completed block, if necessary.
{
// Restore step execution data and flags of partially completed block, if necessary.
if (prep.recalculate_flag & PREP_FLAG_HOLD_PARTIAL_BLOCK) {
st_prep_block = &st_block_buffer[prep.last_st_block_index];
prep.st_block_index = prep.last_st_block_index;
@ -565,7 +572,7 @@ static uint8_t st_next_block_index(uint8_t block_index)
#endif
/* Prepares step segment buffer. Continuously called from main program.
/* Prepares step segment buffer. Continuously called from main program.
The segment buffer is an intermediary buffer interface between the execution of steps
by the stepper algorithm and the velocity profiles generated by the planner. The stepper
@ -574,7 +581,7 @@ static uint8_t st_next_block_index(uint8_t block_index)
step execution and planning optimization processes atomic and protected from each other.
The number of steps "checked-out" from the planner buffer and the number of segments in
the segment buffer is sized and computed such that no operation in the main program takes
longer than the time it takes the stepper algorithm to empty it before refilling it.
longer than the time it takes the stepper algorithm to empty it before refilling it.
Currently, the segment buffer conservatively holds roughly up to 40-50 msec of steps.
NOTE: Computation units are in steps, millimeters, and minutes.
*/
@ -588,42 +595,29 @@ void st_prep_buffer()
// Determine if we need to load a new planner block or if the block needs to be recomputed.
if (pl_block == NULL) {
#ifdef PARKING_ENABLE
// Query planner for a queued block
if (sys.step_control & STEP_CONTROL_EXECUTE_PARK) { pl_block = plan_get_parking_block(); }
else { pl_block = plan_get_current_block(); }
if (pl_block == NULL) { return; } // No planner blocks. Exit.
// Query planner for a queued block
if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) { pl_block = plan_get_system_motion_block(); }
else { pl_block = plan_get_current_block(); }
if (pl_block == NULL) { return; } // No planner blocks. Exit.
// Check if we need to only recompute the velocity profile or load a new block.
if (prep.recalculate_flag & PREP_FLAG_RECALCULATE) {
// Check if we need to only recompute the velocity profile or load a new block.
if (prep.recalculate_flag & PREP_FLAG_RECALCULATE) {
#ifdef PARKING_ENABLE
if (prep.recalculate_flag & PREP_FLAG_PARKING) { prep.recalculate_flag &= ~(PREP_FLAG_RECALCULATE); }
else { prep.recalculate_flag = false; }
} else {
#else
// Query planner for a queued block
pl_block = plan_get_current_block();
if (pl_block == NULL) { return; } // No planner blocks. Exit.
// Check if we need to only recompute the velocity profile or load a new block.
if (prep.recalculate_flag & PREP_FLAG_RECALCULATE) {
#else
prep.recalculate_flag = false;
} else {
#endif
#endif
} else {
// Load the Bresenham stepping data for the block.
prep.st_block_index = st_next_block_index(prep.st_block_index);
// Prepare and copy Bresenham algorithm segment data from the new planner block, so that
// when the segment buffer completes the planner block, it may be discarded when the
// segment buffer finishes the prepped block, but the stepper ISR is still executing it.
// when the segment buffer completes the planner block, it may be discarded when the
// segment buffer finishes the prepped block, but the stepper ISR is still executing it.
st_prep_block = &st_block_buffer[prep.st_block_index];
st_prep_block->direction_bits = pl_block->direction_bits;
uint8_t idx;
@ -631,32 +625,33 @@ void st_prep_buffer()
for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps[idx] = pl_block->steps[idx]; }
st_prep_block->step_event_count = pl_block->step_event_count;
#else
// With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS
// With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS
// level, such that we never divide beyond the original data anywhere in the algorithm.
// If the original data is divided, we can lose a step from integer roundoff.
for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps[idx] = pl_block->steps[idx] << MAX_AMASS_LEVEL; }
st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
#endif
// Initialize segment buffer data for generating the segments.
prep.steps_remaining = (float)pl_block->step_event_count;
prep.step_per_mm = prep.steps_remaining/pl_block->millimeters;
prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm;
prep.dt_remainder = 0.0; // Reset for new segment block
if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) {
if ((sys.step_control & STEP_CONTROL_EXECUTE_HOLD) || (prep.recalculate_flag & PREP_FLAG_DECEL_OVERRIDE)) {
// New block loaded mid-hold. Override planner block entry speed to enforce deceleration.
prep.current_speed = prep.exit_speed;
prep.current_speed = prep.exit_speed;
pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed;
} else {
prep.current_speed = sqrt(pl_block->entry_speed_sqr);
prep.recalculate_flag &= ~(PREP_FLAG_DECEL_OVERRIDE);
} else {
prep.current_speed = sqrt(pl_block->entry_speed_sqr);
}
}
/* ---------------------------------------------------------------------------------
/* ---------------------------------------------------------------------------------
Compute the velocity profile of a new planner block based on its entry and exit
speeds, or recompute the profile of a partially-completed planner block if the
planner has updated it. For a commanded forced-deceleration, such as from a feed
speeds, or recompute the profile of a partially-completed planner block if the
planner has updated it. For a commanded forced-deceleration, such as from a feed
hold, override the planner velocities and decelerate to the target exit speed.
*/
prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block.
@ -677,47 +672,77 @@ void st_prep_buffer()
} else { // [Normal Operation]
// Compute or recompute velocity profile parameters of the prepped planner block.
prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp.
prep.accelerate_until = pl_block->millimeters;
prep.accelerate_until = pl_block->millimeters;
#ifdef PARKING_ENABLE
if (sys.step_control & STEP_CONTROL_EXECUTE_PARK) { prep.exit_speed = 0.0; }
else { prep.exit_speed = plan_get_exec_block_exit_speed(); }
#else
prep.exit_speed = plan_get_exec_block_exit_speed();
#endif
float exit_speed_sqr;
float nominal_speed;
if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) {
prep.exit_speed = exit_speed_sqr = 0.0; // Enforce stop at end of system motion.
} else {
exit_speed_sqr = plan_get_exec_block_exit_speed_sqr();
prep.exit_speed = sqrt(exit_speed_sqr);
}
float exit_speed_sqr = prep.exit_speed*prep.exit_speed;
nominal_speed = plan_compute_profile_nominal_speed(pl_block);
float nominal_speed_sqr = nominal_speed*nominal_speed;
float intersect_distance =
0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr));
if (intersect_distance > 0.0) {
if (pl_block->entry_speed_sqr > nominal_speed_sqr) { // Only occurs during override reductions.
prep.accelerate_until = pl_block->millimeters - inv_2_accel*(pl_block->entry_speed_sqr-nominal_speed_sqr);
if (prep.accelerate_until <= 0.0) { // Deceleration-only.
prep.ramp_type = RAMP_DECEL;
// prep.decelerate_after = pl_block->millimeters;
// prep.maximum_speed = prep.current_speed;
// Compute override block exit speed since it doesn't match the planner exit speed.
prep.exit_speed = sqrt(pl_block->entry_speed_sqr - 2*pl_block->acceleration*pl_block->millimeters);
prep.recalculate_flag |= PREP_FLAG_DECEL_OVERRIDE; // Flag to load next block as deceleration override.
// TODO: Determine correct handling of parameters in deceleration-only.
// Can be tricky since entry speed will be current speed, as in feed holds.
// Also, look into near-zero speed handling issues with this.
} else {
// Decelerate to cruise or cruise-decelerate types. Guaranteed to intersect updated plan.
prep.decelerate_after = inv_2_accel*(nominal_speed_sqr-exit_speed_sqr);
prep.maximum_speed = nominal_speed;
prep.ramp_type = RAMP_DECEL_OVERRIDE;
}
} else if (intersect_distance > 0.0) {
if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types
// NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0.
prep.decelerate_after = inv_2_accel*(pl_block->nominal_speed_sqr-exit_speed_sqr);
prep.decelerate_after = inv_2_accel*(nominal_speed_sqr-exit_speed_sqr);
if (prep.decelerate_after < intersect_distance) { // Trapezoid type
prep.maximum_speed = sqrt(pl_block->nominal_speed_sqr);
if (pl_block->entry_speed_sqr == pl_block->nominal_speed_sqr) {
prep.maximum_speed = nominal_speed;
if (pl_block->entry_speed_sqr == nominal_speed_sqr) {
// Cruise-deceleration or cruise-only type.
prep.ramp_type = RAMP_CRUISE;
} else {
// Full-trapezoid or acceleration-cruise types
prep.accelerate_until -= inv_2_accel*(pl_block->nominal_speed_sqr-pl_block->entry_speed_sqr);
prep.accelerate_until -= inv_2_accel*(nominal_speed_sqr-pl_block->entry_speed_sqr);
}
} else { // Triangle type
prep.accelerate_until = intersect_distance;
prep.decelerate_after = intersect_distance;
prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr);
}
}
} else { // Deceleration-only type
prep.ramp_type = RAMP_DECEL;
// prep.decelerate_after = pl_block->millimeters;
// prep.maximum_speed = prep.current_speed;
prep.ramp_type = RAMP_DECEL;
// prep.decelerate_after = pl_block->millimeters;
// prep.maximum_speed = prep.current_speed;
}
} else { // Acceleration-only type
prep.accelerate_until = 0.0;
// prep.decelerate_after = 0.0;
prep.maximum_speed = prep.exit_speed;
}
}
}
#ifdef VARIABLE_SPINDLE
st_prep_block->spindle_pwm = spindle_compute_pwm_value((0.01*sys.spindle_speed_ovr)*pl_block->spindle_speed);
#endif
}
// Initialize new segment
@ -728,30 +753,44 @@ void st_prep_buffer()
/*------------------------------------------------------------------------------------
Compute the average velocity of this new segment by determining the total distance
traveled over the segment time DT_SEGMENT. The following code first attempts to create
a full segment based on the current ramp conditions. If the segment time is incomplete
traveled over the segment time DT_SEGMENT. The following code first attempts to create
a full segment based on the current ramp conditions. If the segment time is incomplete
when terminating at a ramp state change, the code will continue to loop through the
progressing ramp states to fill the remaining segment execution time. However, if
an incomplete segment terminates at the end of the velocity profile, the segment is
progressing ramp states to fill the remaining segment execution time. However, if
an incomplete segment terminates at the end of the velocity profile, the segment is
considered completed despite having a truncated execution time less than DT_SEGMENT.
The velocity profile is always assumed to progress through the ramp sequence:
acceleration ramp, cruising state, and deceleration ramp. Each ramp's travel distance
may range from zero to the length of the block. Velocity profiles can end either at
the end of planner block (typical) or mid-block at the end of a forced deceleration,
may range from zero to the length of the block. Velocity profiles can end either at
the end of planner block (typical) or mid-block at the end of a forced deceleration,
such as from a feed hold.
*/
float dt_max = DT_SEGMENT; // Maximum segment time
float dt = 0.0; // Initialize segment time
float time_var = dt_max; // Time worker variable
float mm_var; // mm-Distance worker variable
float speed_var; // Speed worker variable
float speed_var; // Speed worker variable
float mm_remaining = pl_block->millimeters; // New segment distance from end of block.
float minimum_mm = mm_remaining-prep.req_mm_increment; // Guarantee at least one step.
if (minimum_mm < 0.0) { minimum_mm = 0.0; }
do {
switch (prep.ramp_type) {
case RAMP_ACCEL:
case RAMP_DECEL_OVERRIDE:
speed_var = pl_block->acceleration*time_var;
mm_var = time_var*(prep.current_speed - 0.5*speed_var);
mm_remaining -= mm_var;
if ((mm_remaining < prep.accelerate_until) || (mm_var <= 0)) {
// Cruise or cruise-deceleration types only for deceleration override.
mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB
time_var = 2.0*(pl_block->millimeters-mm_remaining)/(prep.current_speed+prep.maximum_speed);
prep.ramp_type = RAMP_CRUISE;
prep.current_speed = prep.maximum_speed;
} else { // Mid-deceleration override ramp.
prep.current_speed -= speed_var;
}
break;
case RAMP_ACCEL:
// NOTE: Acceleration ramp only computes during first do-while loop.
speed_var = pl_block->acceleration*time_var;
mm_remaining -= time_var*(prep.current_speed + 0.5*speed_var);
@ -762,23 +801,23 @@ void st_prep_buffer()
if (mm_remaining == prep.decelerate_after) { prep.ramp_type = RAMP_DECEL; }
else { prep.ramp_type = RAMP_CRUISE; }
prep.current_speed = prep.maximum_speed;
} else { // Acceleration only.
} else { // Acceleration only.
prep.current_speed += speed_var;
}
break;
case RAMP_CRUISE:
case RAMP_CRUISE:
// NOTE: mm_var used to retain the last mm_remaining for incomplete segment time_var calculations.
// NOTE: If maximum_speed*time_var value is too low, round-off can cause mm_var to not change. To
// NOTE: If maximum_speed*time_var value is too low, round-off can cause mm_var to not change. To
// prevent this, simply enforce a minimum speed threshold in the planner.
mm_var = mm_remaining - prep.maximum_speed*time_var;
if (mm_var < prep.decelerate_after) { // End of cruise.
if (mm_var < prep.decelerate_after) { // End of cruise.
// Cruise-deceleration junction or end of block.
time_var = (mm_remaining - prep.decelerate_after)/prep.maximum_speed;
mm_remaining = prep.decelerate_after; // NOTE: 0.0 at EOB
prep.ramp_type = RAMP_DECEL;
} else { // Cruising only.
mm_remaining = mm_var;
}
} else { // Cruising only.
mm_remaining = mm_var;
}
break;
default: // case RAMP_DECEL:
// NOTE: mm_var used as a misc worker variable to prevent errors when near zero speed.
@ -794,7 +833,7 @@ void st_prep_buffer()
}
// Otherwise, at end of block or end of forced-deceleration.
time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed);
mm_remaining = prep.mm_complete;
mm_remaining = prep.mm_complete;
prep.current_speed = prep.exit_speed;
}
dt += time_var; // Add computed ramp time to total segment time.
@ -805,19 +844,19 @@ void st_prep_buffer()
// through distance calculations until minimum_mm or mm_complete.
dt_max += DT_SEGMENT;
time_var = dt_max - dt;
} else {
} else {
break; // **Complete** Exit loop. Segment execution time maxed.
}
}
} while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete.
/* -----------------------------------------------------------------------------------
Compute segment step rate, steps to execute, and apply necessary rate corrections.
NOTE: Steps are computed by direct scalar conversion of the millimeter distance
NOTE: Steps are computed by direct scalar conversion of the millimeter distance
remaining in the block, rather than incrementally tallying the steps executed per
segment. This helps in removing floating point round-off issues of several additions.
However, since floats have only 7.2 significant digits, long moves with extremely
segment. This helps in removing floating point round-off issues of several additions.
However, since floats have only 7.2 significant digits, long moves with extremely
high step counts can exceed the precision of floats, which can lead to lost steps.
Fortunately, this scenario is highly unlikely and unrealistic in CNC machines
supported by Grbl (i.e. exceeding 10 meters axis travel at 200 step/mm).
@ -826,48 +865,48 @@ void st_prep_buffer()
float n_steps_remaining = ceil(step_dist_remaining); // Round-up current steps remaining
float last_n_steps_remaining = ceil(prep.steps_remaining); // Round-up last steps remaining
prep_segment->n_step = last_n_steps_remaining-n_steps_remaining; // Compute number of steps to execute.
// Bail if we are at the end of a feed hold and don't have a step to execute.
if (prep_segment->n_step == 0) {
if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) {
// Less than one step to decelerate to zero speed, but already very close. AMASS
// Less than one step to decelerate to zero speed, but already very close. AMASS
// requires full steps to execute. So, just bail.
bit_true(sys.step_control,STEP_CONTROL_END_MOTION);
#ifdef PARKING_ENABLE
if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) { prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK; }
if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) { prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK; }
#endif
return; // Segment not generated, but current step data still retained.
}
}
// Compute segment step rate. Since steps are integers and mm distances traveled are not,
// the end of every segment can have a partial step of varying magnitudes that are not
// the end of every segment can have a partial step of varying magnitudes that are not
// executed, because the stepper ISR requires whole steps due to the AMASS algorithm. To
// compensate, we track the time to execute the previous segment's partial step and simply
// apply it with the partial step distance to the current segment, so that it minutely
// adjusts the whole segment rate to keep step output exact. These rate adjustments are
// adjusts the whole segment rate to keep step output exact. These rate adjustments are
// typically very small and do not adversely effect performance, but ensures that Grbl
// outputs the exact acceleration and velocity profiles as computed by the planner.
dt += prep.dt_remainder; // Apply previous segment partial step execute time
float inv_rate = dt/(last_n_steps_remaining - step_dist_remaining); // Compute adjusted step rate inverse
// Compute CPU cycles per step for the prepped segment.
uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step)
uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step)
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// Compute step timing and multi-axis smoothing level.
// NOTE: AMASS overdrives the timer with each level, so only one prescalar is required.
if (cycles < AMASS_LEVEL1) { prep_segment->amass_level = 0; }
else {
if (cycles < AMASS_LEVEL2) { prep_segment->amass_level = 1; }
else if (cycles < AMASS_LEVEL3) { prep_segment->amass_level = 2; }
else { prep_segment->amass_level = 3; }
cycles >>= prep_segment->amass_level;
else { prep_segment->amass_level = 3; }
cycles >>= prep_segment->amass_level;
prep_segment->n_step <<= prep_segment->amass_level;
}
if (cycles < (1UL << 16)) { prep_segment->cycles_per_tick = cycles; } // < 65536 (4.1ms @ 16MHz)
else { prep_segment->cycles_per_tick = 0xffff; } // Just set the slowest speed possible.
#else
#else
// Compute step timing and timer prescalar for normal step generation.
if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
prep_segment->prescaler = 1; // prescaler: 0
@ -875,7 +914,7 @@ void st_prep_buffer()
} else if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
prep_segment->prescaler = 2; // prescaler: 8
prep_segment->cycles_per_tick = cycles >> 3;
} else {
} else {
prep_segment->prescaler = 3; // prescaler: 64
if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
prep_segment->cycles_per_tick = cycles >> 6;
@ -890,16 +929,16 @@ void st_prep_buffer()
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
// Update the appropriate planner and segment data.
pl_block->millimeters = mm_remaining;
pl_block->millimeters = mm_remaining;
prep.steps_remaining = n_steps_remaining;
prep.dt_remainder = (n_steps_remaining - step_dist_remaining)*inv_rate;
// Check for exit conditions and flag to load next planner block.
if (mm_remaining == prep.mm_complete) {
if (mm_remaining == prep.mm_complete) {
// End of planner block or forced-termination. No more distance to be executed.
if (mm_remaining > 0.0) { // At end of forced-termination.
// Reset prep parameters for resuming and then bail. Allow the stepper ISR to complete
// the segment queue, where realtime protocol will set new state upon receiving the
// the segment queue, where realtime protocol will set new state upon receiving the
// cycle stop flag from the ISR. Prep_segment is blocked until then.
bit_true(sys.step_control,STEP_CONTROL_END_MOTION);
#ifdef PARKING_ENABLE
@ -908,31 +947,27 @@ void st_prep_buffer()
return; // Bail!
} else { // End of planner block
// The planner block is complete. All steps are set to be executed in the segment buffer.
#ifdef PARKING_ENABLE
if (sys.step_control & STEP_CONTROL_EXECUTE_PARK) {
bit_true(sys.step_control,STEP_CONTROL_END_MOTION);
return;
}
#endif
if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) {
bit_true(sys.step_control,STEP_CONTROL_END_MOTION);
return;
}
pl_block = NULL; // Set pointer to indicate check and load next planner block.
plan_discard_current_block();
}
}
}
}
}
}
// Called by realtime status reporting to fetch the current speed being executed. This value
// however is not exactly the current speed, but the speed computed in the last step segment
// 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 | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR)){
return prep.current_speed;
}
return 0.0f;
}
#endif
// divided by the ACCELERATION TICKS PER SECOND in seconds.
float st_get_realtime_rate()
{
if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)){
return prep.current_speed;
}
return 0.0f;
}

Wyświetl plik

@ -2,7 +2,7 @@
stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl is free software: you can redistribute it and/or modify
@ -20,7 +20,7 @@
*/
#ifndef stepper_h
#define stepper_h
#define stepper_h
#ifndef SEGMENT_BUFFER_SIZE
#define SEGMENT_BUFFER_SIZE 6
@ -38,7 +38,7 @@ void st_go_idle();
// Generate the step and direction port invert masks.
void st_generate_step_dir_invert_masks();
// Reset the stepper subsystem variables
// Reset the stepper subsystem variables
void st_reset();
// Changes the run state of the step segment buffer to execute the special parking motion.
@ -46,7 +46,7 @@ void st_parking_setup_buffer();
// Restores the step segment buffer to the normal run state after a parking motion.
void st_parking_restore_buffer();
// Reloads step segment buffer. Called continuously by realtime execution system.
void st_prep_buffer();
@ -54,8 +54,6 @@ void st_prep_buffer();
void st_update_plan_block_parameters();
// Called by realtime status reporting if realtime rate reporting is enabled in config.h.
#ifdef REPORT_REALTIME_RATE
float st_get_realtime_rate();
#endif
#endif

Wyświetl plik

@ -2,7 +2,7 @@
system.c - Handles system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2015 Sungeun K. Jeon
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -34,7 +34,7 @@ void system_init()
}
// Returns control pin state as a uint8 bitfield. Each bit indicates the input pin state, where
// Returns control pin state as a uint8 bitfield. Each bit indicates the input pin state, where
// triggered is 1 and not triggered is 0. Invert mask is applied. Bitfield organization is
// defined by the CONTROL_PIN_INDEX in the header file.
uint8_t system_control_get_state()
@ -57,25 +57,25 @@ uint8_t system_control_get_state()
// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets
// only the realtime command execute variable to have the main program execute these when
// only the realtime command execute variable to have the main program execute these when
// its ready. This works exactly like the character-based realtime commands when picked off
// directly from the incoming serial data stream.
ISR(CONTROL_INT_vect)
ISR(CONTROL_INT_vect)
{
uint8_t pin = system_control_get_state();
if (pin) {
if (pin) {
if (bit_istrue(pin,CONTROL_PIN_INDEX_RESET)) {
mc_reset();
} else if (bit_istrue(pin,CONTROL_PIN_INDEX_CYCLE_START)) {
bit_true(sys_rt_exec_state, EXEC_CYCLE_START);
#ifndef ENABLE_SAFETY_DOOR_INPUT_PIN
} else if (bit_istrue(pin,CONTROL_PIN_INDEX_FEED_HOLD)) {
bit_true(sys_rt_exec_state, EXEC_FEED_HOLD);
bit_true(sys_rt_exec_state, EXEC_FEED_HOLD);
#else
} else if (bit_istrue(pin,CONTROL_PIN_INDEX_SAFETY_DOOR)) {
bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR);
#endif
}
}
}
}
@ -92,7 +92,7 @@ uint8_t system_check_safety_door_ajar()
// Executes user startup script, if stored.
void system_execute_startup(char *line)
void system_execute_startup(char *line)
{
uint8_t n;
for (n=0; n < N_STARTUP_LINE; n++) {
@ -103,29 +103,35 @@ void system_execute_startup(char *line)
printString(line); // Echo startup line to indicate execution.
report_status_message(gc_execute_line(line));
}
}
}
}
}
}
// Directs and executes one line of formatted input from protocol_process. While mostly
// incoming streaming g-code blocks, this also executes Grbl internal commands, such as
// incoming streaming g-code blocks, this also executes Grbl internal commands, such as
// settings, initiating the homing cycle, and toggling switch states. This differs from
// the realtime command module by being susceptible to when Grbl is ready to execute the
// the realtime command module by being susceptible to when Grbl is ready to execute the
// next line during a cycle, so for switches like block delete, the switch only effects
// the lines that are processed afterward, not necessarily real-time during a cycle,
// the lines that are processed afterward, not necessarily real-time during a cycle,
// since there are motions already stored in the buffer. However, this 'lag' should not
// be an issue, since these commands are not typically used during a cycle.
uint8_t system_execute_line(char *line)
{
uint8_t char_counter = 1;
uint8_t system_execute_line(char *line)
{
uint8_t char_counter = 1;
uint8_t helper_var = 0; // Helper variable
float parameter, value;
switch( line[char_counter] ) {
case 0 : report_grbl_help(); break;
case 'J' : // Jogging
// Execute only if in IDLE or JOG states.
if (sys.state != STATE_IDLE && sys.state != STATE_JOG) { return(STATUS_IDLE_ERROR); }
if(line[2] != '=') { return(STATUS_INVALID_STATEMENT); }
return(gc_execute_line(line)); // NOTE: $J= is ignored inside g-code parser and used to detect jog motions.
break;
case '$': case 'G': case 'C': case 'X':
if ( line[(char_counter+1)] != 0 ) { return(STATUS_INVALID_STATEMENT); }
switch( line[char_counter] ) {
if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); }
switch( line[1] ) {
case '$' : // Prints Grbl settings
if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print.
else { report_grbl_settings(); }
@ -133,86 +139,82 @@ uint8_t system_execute_line(char *line)
case 'G' : // Prints gcode parser state
// TODO: Move this to realtime commands for GUIs to request this data during suspend-state.
report_gcode_modes();
break;
break;
case 'C' : // Set check g-code mode [IDLE/CHECK]
// Perform reset when toggling off. Check g-code mode should only work if Grbl
// is idle and ready, regardless of alarm locks. This is mainly to keep things
// simple and consistent.
if ( sys.state == STATE_CHECK_MODE ) {
mc_reset();
if ( sys.state == STATE_CHECK_MODE ) {
mc_reset();
report_feedback_message(MESSAGE_DISABLED);
} else {
if (sys.state) { return(STATUS_IDLE_ERROR); } // Requires no alarm mode.
sys.state = STATE_CHECK_MODE;
report_feedback_message(MESSAGE_ENABLED);
}
break;
break;
case 'X' : // Disable alarm lock [ALARM]
if (sys.state == STATE_ALARM) {
if (sys.state == STATE_ALARM) {
// Block if safety door is ajar.
if (system_check_safety_door_ajar()) { return(STATUS_CHECK_DOOR); }
report_feedback_message(MESSAGE_ALARM_UNLOCK);
sys.state = STATE_IDLE;
// Don't run startup script. Prevents stored moves in startup from causing accidents.
} // Otherwise, no effect.
break;
// case 'J' : break; // Jogging methods
// TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be
// susceptible to other realtime commands except for e-stop. The jogging function is intended to
// be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped
// steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would
// start motion and toggle off would initiate a deceleration to stop. One could 'feather' the
// motion by repeatedly toggling to slow the motion to the desired location. Location data would
// need to be updated real-time and supplied to the user through status queries.
// More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are
// handled by the planner. It would be possible for the jog subprogram to insert blocks into the
// block buffer without having the planner plan them. It would need to manage de/ac-celerations
// on its own carefully. This approach could be effective and possibly size/memory efficient.
break;
}
break;
default :
default :
// Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing)
if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); }
switch( line[char_counter] ) {
switch( line[1] ) {
case '#' : // Print Grbl NGC parameters
if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); }
else { report_ngc_parameters(); }
break;
break;
case 'H' : // Perform homing cycle [IDLE/ALARM]
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Block if safety door is ajar.
if (system_check_safety_door_ajar()) { return(STATUS_CHECK_DOOR); }
sys.state = STATE_HOMING; // Set system state variable
mc_homing_cycle();
mc_homing_cycle();
if (!sys.abort) { // Execute startup scripts after successful homing.
sys.state = STATE_IDLE; // Set to IDLE when complete.
st_go_idle(); // Set steppers to the settings idle state before returning.
system_execute_startup(line);
system_execute_startup(line);
}
} else { return(STATUS_SETTING_DISABLED); }
break;
case 'I' : // Print or store build info. [IDLE/ALARM]
if ( line[++char_counter] == 0 ) {
if ( line[++char_counter] == 0 ) {
settings_read_build_info(line);
report_build_info(line);
} else { // Store startup line [IDLE/ALARM]
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
helper_var = char_counter; // Set helper variable as counter to start of user info line.
do {
line[char_counter-helper_var] = line[char_counter];
} while (line[char_counter++] != 0);
settings_store_build_info(line);
#ifdef ENABLE_BUILD_INFO_WRITE_COMMAND
} else { // Store startup line [IDLE/ALARM]
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
helper_var = char_counter; // Set helper variable as counter to start of user info line.
do {
line[char_counter-helper_var] = line[char_counter];
} while (line[char_counter++] != 0);
settings_store_build_info(line);
#endif
}
break;
break;
case 'R' : // Restore defaults [IDLE/ALARM]
if (line[++char_counter] != 'S') { return(STATUS_INVALID_STATEMENT); }
if (line[++char_counter] != 'T') { return(STATUS_INVALID_STATEMENT); }
if (line[++char_counter] != '=') { return(STATUS_INVALID_STATEMENT); }
if (line[char_counter+2] != 0) { return(STATUS_INVALID_STATEMENT); }
switch (line[++char_counter]) {
case '$': settings_restore(SETTINGS_RESTORE_DEFAULTS); break;
case '#': settings_restore(SETTINGS_RESTORE_PARAMETERS); break;
case '*': settings_restore(SETTINGS_RESTORE_ALL); break;
if (line[2] != 'S') { return(STATUS_INVALID_STATEMENT); }
if (line[3] != 'T') { return(STATUS_INVALID_STATEMENT); }
if (line[4] != '=') { return(STATUS_INVALID_STATEMENT); }
if (line[6] != 0) { return(STATUS_INVALID_STATEMENT); }
switch (line[5]) {
#ifdef ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS
case '$': settings_restore(SETTINGS_RESTORE_DEFAULTS); break;
#endif
#ifdef ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS
case '#': settings_restore(SETTINGS_RESTORE_PARAMETERS); break;
#endif
#ifdef ENABLE_RESTORE_EEPROM_WIPE_ALL
case '*': settings_restore(SETTINGS_RESTORE_ALL); break;
#endif
default: return(STATUS_INVALID_STATEMENT);
}
report_feedback_message(MESSAGE_RESTORE_DEFAULTS);
@ -230,7 +232,7 @@ uint8_t system_execute_line(char *line)
break;
} else { // Store startup line [IDLE Only] Prevents motion during ALARM.
if (sys.state != STATE_IDLE) { return(STATUS_IDLE_ERROR); } // Store only when idle.
helper_var = true; // Set helper_var to flag storing method.
helper_var = true; // Set helper_var to flag storing method.
// No break. Continues into default: to read remaining command characters.
}
default : // Storing setting methods [IDLE/ALARM]
@ -245,7 +247,7 @@ uint8_t system_execute_line(char *line)
// Execute gcode block to ensure block is valid.
helper_var = gc_execute_line(line); // Set helper_var to returned status code.
if (helper_var) { return(helper_var); }
else {
else {
helper_var = trunc(parameter); // Set helper_var to int value of parameter
settings_store_startup_line(helper_var,line);
}
@ -254,12 +256,22 @@ uint8_t system_execute_line(char *line)
if((line[char_counter] != 0) || (parameter > 255)) { return(STATUS_INVALID_STATEMENT); }
return(settings_store_global_setting((uint8_t)parameter, value));
}
}
}
}
return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
}
void system_flag_wco_change()
{
#ifdef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE
protocol_buffer_synchronize();
#endif
sys.report_wco_counter = REPORT_WCO_REFRESH_BUSY_COUNT;
}
// Returns machine position of axis 'idx'. Must be sent a 'step' array.
// NOTE: If motor steps and machine position are not in the same coordinate frame, this function
// serves as a central place to compute the transformation.
@ -267,10 +279,10 @@ float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx)
{
float pos;
#ifdef COREXY
if (idx==A_MOTOR) {
pos = 0.5*((steps[A_MOTOR] + steps[B_MOTOR])/settings.steps_per_mm[idx]);
} else if (idx==B_MOTOR) {
pos = 0.5*((steps[A_MOTOR] - steps[B_MOTOR])/settings.steps_per_mm[idx]);
if (idx==X_AXIS) {
pos = (float)system_convert_corexy_to_x_axis_steps(steps) / settings.steps_per_mm[idx];
} else if (idx==Y_AXIS) {
pos = (float)system_convert_corexy_to_y_axis_steps(steps) / settings.steps_per_mm[idx];
} else {
pos = steps[idx]/settings.steps_per_mm[idx];
}
@ -291,31 +303,94 @@ void system_convert_array_steps_to_mpos(float *position, int32_t *steps)
}
// CoreXY calculation only. Returns x or y-axis "steps" based on CoreXY motor steps.
#ifdef COREXY
int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps)
{
return( (steps[A_MOTOR] + steps[B_MOTOR])/2 );
}
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps)
{
return( (steps[A_MOTOR] - steps[B_MOTOR])/2 );
}
#endif
// Checks and reports if target array exceeds machine travel limits.
uint8_t system_check_travel_limits(float *target)
{
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
#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]) { return(true); }
} else {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); }
}
#else
// NOTE: max_travel is stored as negative
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); }
#endif
}
return(false);
}
// Special handlers for setting and clearing Grbl's real-time execution flags.
void system_set_exec_state_flag(uint8_t mask) {
uint8_t sreg = SREG;
cli();
uint8_t sreg = SREG;
cli();
sys_rt_exec_state |= (mask);
SREG = sreg;
}
void system_clear_exec_state_flag(uint8_t mask) {
uint8_t sreg = SREG;
cli();
uint8_t sreg = SREG;
cli();
sys_rt_exec_state &= ~(mask);
SREG = sreg;
}
void system_set_exec_alarm_flag(uint8_t mask) {
uint8_t sreg = SREG;
cli();
sys_rt_exec_alarm |= (mask);
void system_set_exec_alarm(uint8_t code) {
uint8_t sreg = SREG;
cli();
sys_rt_exec_alarm = code;
SREG = sreg;
}
void system_clear_exec_alarm_flag(uint8_t mask) {
uint8_t sreg = SREG;
cli();
uint8_t sreg = SREG;
cli();
sys_rt_exec_alarm &= ~(mask);
SREG = sreg;
}
void system_set_exec_motion_override_flag(uint8_t mask) {
uint8_t sreg = SREG;
cli();
sys_rt_exec_motion_override |= (mask);
SREG = sreg;
}
void system_set_exec_accessory_override_flag(uint8_t mask) {
uint8_t sreg = SREG;
cli();
sys_rt_exec_accessory_override |= (mask);
SREG = sreg;
}
void system_clear_exec_motion_overrides() {
uint8_t sreg = SREG;
cli();
sys_rt_exec_motion_override = 0;
SREG = sreg;
}
void system_clear_exec_accessory_overrides() {
uint8_t sreg = SREG;
cli();
sys_rt_exec_accessory_override = 0;
SREG = sreg;
}

Wyświetl plik

@ -2,7 +2,7 @@
system.h - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2015 Sungeun K. Jeon
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -23,10 +23,10 @@
#include "grbl.h"
// Define system executor bit map. Used internally by realtime protocol as realtime command flags,
// Define system executor bit map. Used internally by realtime protocol as realtime command flags,
// which notifies the main program to execute the specified realtime command asynchronously.
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
// flags are always false, so the realtime protocol only needs to check for a non-zero value to
// flags are always false, so the realtime protocol only needs to check for a non-zero value to
// know when there is a realtime command to execute.
#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001
#define EXEC_CYCLE_START bit(1) // bitmask 00000010
@ -36,16 +36,37 @@
#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000
#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000
// Alarm executor bit map.
// NOTE: EXEC_CRITICAL_EVENT is an optional flag that must be set with an alarm flag. When enabled,
// this halts Grbl into an infinite loop until the user aknowledges the problem and issues a soft-
// reset command. For example, a hard limit event needs this type of halt and aknowledgement.
#define EXEC_CRITICAL_EVENT bit(0) // bitmask 00000001 (SPECIAL FLAG. See NOTE:)
#define EXEC_ALARM_HARD_LIMIT bit(1) // bitmask 00000010
#define EXEC_ALARM_SOFT_LIMIT bit(2) // bitmask 00000100
#define EXEC_ALARM_ABORT_CYCLE bit(3) // bitmask 00001000
#define EXEC_ALARM_PROBE_FAIL bit(4) // bitmask 00010000
#define EXEC_ALARM_HOMING_FAIL bit(5) // bitmask 00100000
// Alarm executor codes. Valid values (1-255). Zero is reserved.
#define EXEC_ALARM_HARD_LIMIT 1
#define EXEC_ALARM_SOFT_LIMIT 2
#define EXEC_ALARM_ABORT_CYCLE 3
#define EXEC_ALARM_PROBE_FAIL_INITIAL 4
#define EXEC_ALARM_PROBE_FAIL_CONTACT 5
#define EXEC_ALARM_HOMING_FAIL_RESET 6
#define EXEC_ALARM_HOMING_FAIL_DOOR 7
#define EXEC_ALARM_HOMING_FAIL_PULLOFF 8
#define EXEC_ALARM_HOMING_FAIL_APPROACH 9
// Override bit maps. Realtime bitflags to control feed, rapid, spindle, and coolant overrides.
// Spindle/coolant and feed/rapids are separated into two controlling flag variables.
#define EXEC_FEED_OVR_RESET bit(0)
#define EXEC_FEED_OVR_COARSE_PLUS bit(1)
#define EXEC_FEED_OVR_COARSE_MINUS bit(2)
#define EXEC_FEED_OVR_FINE_PLUS bit(3)
#define EXEC_FEED_OVR_FINE_MINUS bit(4)
#define EXEC_RAPID_OVR_RESET bit(5)
#define EXEC_RAPID_OVR_MEDIUM bit(6)
#define EXEC_RAPID_OVR_LOW bit(7)
// #define EXEC_RAPID_OVR_EXTRA_LOW bit(*) // *NOT SUPPORTED*
#define EXEC_SPINDLE_OVR_RESET bit(0)
#define EXEC_SPINDLE_OVR_COARSE_PLUS bit(1)
#define EXEC_SPINDLE_OVR_COARSE_MINUS bit(2)
#define EXEC_SPINDLE_OVR_FINE_PLUS bit(3)
#define EXEC_SPINDLE_OVR_FINE_MINUS bit(4)
#define EXEC_SPINDLE_OVR_STOP bit(5)
#define EXEC_COOLANT_FLOOD_OVR_TOGGLE bit(6)
#define EXEC_COOLANT_MIST_OVR_TOGGLE bit(7)
// Define system state bit map. The state variable primarily tracks the individual functions
// of Grbl to manage each without overlapping. It is also used as a messaging flag for
@ -56,8 +77,9 @@
#define STATE_HOMING bit(2) // Performing homing cycle
#define STATE_CYCLE bit(3) // Cycle is running or motions are being executed.
#define STATE_HOLD bit(4) // Active feed hold
#define STATE_SAFETY_DOOR bit(5) // Safety door is ajar. Feed holds and de-energizes system.
#define STATE_MOTION_CANCEL bit(6) // Motion cancel by feed hold and return to idle.
#define STATE_JOG bit(5) // Jogging mode.
#define STATE_SAFETY_DOOR bit(6) // Safety door is ajar. Feed holds and de-energizes system.
// #define STATE_SLEEP bit(7) // Sleep state. [Grbl-Mega Only]
// Define system suspend flags. Used in various ways to manage suspend states and procedures.
#define SUSPEND_DISABLE 0 // Must be zero.
@ -66,15 +88,15 @@
#define SUSPEND_RETRACT_COMPLETE bit(2) // (Safety door only) Indicates retraction and de-energizing is complete.
#define SUSPEND_INITIATE_RESTORE bit(3) // (Safety door only) Flag to initiate resume procedures from a cycle start.
#define SUSPEND_RESTORE_COMPLETE bit(4) // (Safety door only) Indicates ready to resume normal operation.
#define SUSPEND_SAFETY_DOOR_AJAR bit(5) // Indicates suspend was initiated by a safety door state.
#define SUSPEND_SAFETY_DOOR_AJAR bit(5) // Tracks safety door state for resuming.
#define SUSPEND_MOTION_CANCEL bit(6) // Indicates a canceled resume motion. Currently used by probing routine.
#define SUSPEND_JOG_CANCEL bit(7) // Indicates a jog cancel in process and to reset buffers when complete.
// Define step segment generator state flags.
#define STEP_CONTROL_NORMAL_OP 0
// #define STEP_CONTROL_RECOMPUTE_ACTIVE_BLOCK bit(0)
#define STEP_CONTROL_END_MOTION bit(1)
#define STEP_CONTROL_EXECUTE_HOLD bit(2)
#define STEP_CONTROL_EXECUTE_PARK bit(3)
#define STEP_CONTROL_NORMAL_OP 0
#define STEP_CONTROL_END_MOTION bit(0)
#define STEP_CONTROL_EXECUTE_HOLD bit(1)
#define STEP_CONTROL_EXECUTE_SYS_MOTION bit(2)
// Define control pin index for Grbl internal use. Pin maps may change, but these values don't.
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
@ -90,28 +112,49 @@
#define CONTROL_PIN_INDEX_CYCLE_START bit(2)
#endif
// Define toggle override control states.
#define TOGGLE_OVR_STOP_ENABLED bit(0)
#define TOGGLE_OVR_STOP_INITIATE bit(1)
#define TOGGLE_OVR_STOP_RESTORE bit(2)
#define TOGGLE_OVR_STOP_RESTORE_CYCLE bit(3)
#define TOGGLE_OVR_FLOOD_COOLANT bit(4)
#define TOGGLE_OVR_MIST_COOLANT bit(5)
#define TOGGLE_OVR_STOP_ACTIVE_MASK (TOGGLE_OVR_STOP_ENABLED|TOGGLE_OVR_STOP_INITIATE|TOGGLE_OVR_STOP_RESTORE|TOGGLE_OVR_STOP_RESTORE_CYCLE)
// NOTE: Mask is used to determine if spindle stop is active or disabled.
// Define global system variables
typedef struct {
uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
uint8_t state; // Tracks the current state of Grbl.
uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door.
uint8_t soft_limit; // Tracks soft limit errors for the state machine (Boolean)
uint8_t step_control;
int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
// NOTE: This may need to be a volatile variable, if problems arise.
int32_t probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
uint8_t probe_succeeded; // Tracks if last probing cycle was successful.
uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR.
uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
uint8_t state; // Tracks the current system state of Grbl.
uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door.
uint8_t soft_limit; // Tracks soft limit errors for the state machine. (boolean)
uint8_t step_control; // Governs the step segment generator depending on system state.
uint8_t probe_succeeded; // Tracks if last probing cycle was successful.
uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR.
uint8_t f_override; // Feed rate override value in percent
uint8_t r_override; // Rapids override value in percent
uint8_t spindle_speed_ovr; // Spindle speed value in percent
uint8_t toggle_ovr_mask; // Tracks toggle override states
uint8_t report_ovr_counter; // Tracks when to add override data to status reports.
uint8_t report_wco_counter; // Tracks when to add work coordinate offset data to status reports.
} system_t;
extern system_t sys;
volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
// NOTE: These position variables may need to be declared as volatiles, if problems arise.
int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
#ifdef DEBUG
#define EXEC_DEBUG_REPORT bit(0)
volatile uint8_t sys_rt_exec_debug;
#endif
// Initialize the serial protocol
void system_init();
@ -128,17 +171,33 @@ uint8_t system_execute_line(char *line);
// Execute the startup script lines stored in EEPROM upon initialization
void system_execute_startup(char *line);
void system_flag_wco_change();
// Returns machine position of axis 'idx'. Must be sent a 'step' array.
float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx);
// Updates a machine 'position' array based on the 'step' array sent.
void system_convert_array_steps_to_mpos(float *position, int32_t *steps);
// CoreXY calculation only. Returns x or y-axis "steps" based on CoreXY motor steps.
#ifdef COREXY
int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps);
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps);
#endif
// Checks and reports if target array exceeds machine travel limits.
uint8_t system_check_travel_limits(float *target);
// Special handlers for setting and clearing Grbl's real-time execution flags.
void system_set_exec_state_flag(uint8_t mask);
void system_clear_exec_state_flag(uint8_t mask);
void system_set_exec_alarm_flag(uint8_t mask);
void system_set_exec_alarm(uint8_t code);
void system_clear_exec_alarm_flag(uint8_t mask);
void system_set_exec_motion_override_flag(uint8_t mask);
void system_set_exec_accessory_override_flag(uint8_t mask);
void system_clear_exec_motion_overrides();
void system_clear_exec_accessory_overrides();
#endif